(там уже на 32 элемента поменял вместо 100 - ничего не изменилось)
и где тут чтение из массива?
Как Вы делаете, абсолютно непонятн6о, т.к. код неполный.
Но настораживает несколько моментов.
- Вы отключаете прерывания каждый раз при прохождении цикла. Включаете ли обратно - неизвестно. Если “да”, понятно, откуда “нестабильность”, если “нет” - вообще непонятно, зачем Вы это делаете.
- любые условные операторы в данном случае - возможный источник нестабильности. А если, к тому же, только с одной веткой - очевидный источник нестабильности.
- в отрывке вообще отсутствует какой-либо код, обеспечивающий синхронизацию, поэтому вообще о какой-либо стабильности говорить не приходится.
В 17-й строке. Сейчас всё закомментировано, чтобы отключить и после этого импульсы стали стабильными.
Тут еще много что настораживает.
Например, заполнение массива внутри луп. Он, что при каждом проходе заново заполняется?
Или переходы по меткам ВНУТРЬ области с запрещенными прерываниями. ЭТО вообще бред, на мой взгляд.
В таком коде могут быть ляпы на каждой строчке
вы, простите, здоровы?
Показываете код, где нужное место закомментировано?
И как нужно догадаться, что именно надо раскомментировать или убрать, чтобы получить нужный эффект?
Вы бы еще часть кода бумажкой прикрыли и спросили на форуме “Подскажите, что у меня не работает в прикрытом коде?”
Прерывания включаю в конце цикла. Мне их тут посоветовали отключать, чтобы задержка на “nop’ах” была стабильной. Потому что точность micros’ов - 4 мкс, что тоже не вариант. Вот полный код.
// Set up potentiometer pin
#define POT_PIN A3
// Set up clock out pin
#define CLC_OUT 9 // PORTB первый бит PB1
// Set up random out pin (выход данных)
#define RANDOM_OUT 10 // PORTB второй бит PB2
// Set up MFM out pin (выход МЧМ)
#define MFM_OUT 11 // PORTB третий бит PB3
byte RND = 0; // текущий бит данных
byte RND_OLD = 0; // предыдущий бит данных
unsigned int POT = 10;
// unsigned int m=20; // считал, что 20 мкс - это 160 тактов при частоте 8 МГц, но получилось 50 (?)
// unsigned int POT_PLUS = POT + m; // добавка для выравнивания меандра
unsigned int l=0;
// unsigned int arr[32]; // массив случайных чисел
// int i=0;
// int j=0;
void setup() {
// Set up pins as input
pinMode(POT_PIN, INPUT);
pinMode(A1, INPUT);
// Set up pins as outputs
pinMode(CLC_OUT, OUTPUT);
pinMode(RANDOM_OUT, OUTPUT);
pinMode(MFM_OUT, OUTPUT);
// для пинов 9 и 10 (таймер1) - такие установки ни на что не влияют
// TCCR1A = 0b00000001; // 8bit
// TCCR1B = 0b00001001; // x1 fast pwm - 62.5 кГц - для UNO и 125 кГц - для LGT8F328P
}
void loop() {
// for(i = 0; i < 32; i++){ // в массиве будет 32 элементов
// arr[i] = random(65536); // заполняем массив случайными числами
// }
noInterrupts();
label:
PORTB |= (1 << 1); // фронт CLC
//RND = bitRead(arr[i], j); // чтение битов массива - 1 мкс, почти спокойно
// digitalWrite(RANDOM_OUT, RND); // вывод данных на 10-й пин - около 3 мкс, сильный джиттер
if (RND < 1) PORTB &= ~(1 << 2); // установить 0 в бите 2 (10-й пин)
else PORTB |= (1 << 2); // установить 1 в бите 2 (10-й пин)
if (RND < 1 && RND_OLD < 1) PORTB = PORTB^0b00001000; // в начале бита данных, если второй 0 подряд
// j=j+1; //
// if(j > 15) { // последовательный перебор
// j = 0; // битов массива
// i=i+1; // (быстрее, чем random (2))
// } //
// if(i > 31) i = 0; //
POT = analogRead(POT_PIN); // чтение POT - 23-24 мкс
for (l = 0; l < POT; l++) { // задержка, определяемая потенциометром
asm ("nop");
}
PORTB &= ~(1 << 1); // спад CLC
if (RND > 0) PORTB = PORTB^0b00001000; // в середине бита, т.е. CLC должен быть меандром
RND_OLD = RND;
for (l = 0; l < POT; l++) { // длительность nop зависит от типа переменной
asm ("nop");
}
RND = analogRead(A1)&0b1; // тоже 23-24 мкс, поэтому в конце, чтобы по фронту без задержек
goto label;
interrupts();
} // закрыли loop
Сейчас с AnalogRead импульсы стабильные.
Пример простенького генератора шума.
Всего 16 разрядов, поэтому последовательность короткая - 65535, что при частоте дискретизации 40 кГц дает чуть более полутора секунд. Дальше - повторяется, но на слух неотличимо от генератора белого шума.
#include <TimerOne.h>
#define RESOLUTION 25 // разрешение по времени, т.е. период дискретизации
void setup() {
pinMode(13, OUTPUT);
Serial.begin(115200);
long t0 = micros();
for(int i = 0; i < 1000; i++) {
LFSR_Ga_16();
}
long t1 = micros();
Serial.println(t1-t0); // здесь мы печатаем в нс время выполнения нашего генератора шума
for(int i = 0; i < 1000; i++) {
tick();
}
long t2 = micros();
Serial.println(t2-t1); // а здесь - всего прерывания
Timer1.initialize(RESOLUTION); // настраиваем таймер на 25 мкс
Timer1.attachInterrupt(tick); // и сообщаем ему, что вызывать с этим периодом
}
void loop() {}
void tick() { // обработчик прерывания
if(LFSR_Ga_16()) {
PORTB |= 1 << 5;
} else {
PORTB &= ~(1 << 5);
}
}
inline int LFSR_Ga_16(void) { // наш генератор шума
// для полинома 16,15,13,4 => 0,1,3,12 => 0x100b
static unsigned long S = 0x00000001;
if (S & 1) {
S = ((S ^ 0x100b) >> 1) | 0x8000;
return 1;
} else {
S >>= 1;
return 0;
}
}
Тут, конечно, можно немного соптимизировать, если перенести работу с портами сразу в генератор и именно его вызывать в качестве прерывания, но теряется универсальность.
Спасибо, само по себе интересно, сохраню. Но тут опять микросы, у которых от рождения нестабильность 4 мкс …
Не нестабильность, а дискретность.
И используются они исключительно, чтобы узнать время выполнения процедур. Т.е. это предварительные измерения, которые дальше ни на что не влияют.
А период, как запрошено, 25 мкс (нацело на 4 не делится).
А как подпрограмму этот полином можно вызывать? Чтобы без прерываний, со сдвигом при каждом вызове.
В общем, инструкцию понял ![]()
- Берём однобайтовую переменную GEN (биты 0-7) и присваиваем ей любое значение, отличное от 0.
-
- Получаем бит XOR из битов 5 и 6 по логике исключающего или.
-
- Сдвигаем биты в GEN влево.
-
- Записываем XOR в нулевой бит GENы.
- Goto 1.
Можно в самой программе. Там будет довольно длинная последовательность. Если 16 бит, то точки подключения будут другие.
Заработало
Стабильность нормальная и длительность полупериода всего 2 мкс. А другой полупериод минимум 24мкс - там потенциометр читается, надо выравнивать, ну ладно.
int GEN=650; // любое ненулевое значение (все нули - запрещённая комбинация)
bool XOR=1;
label:
XOR=bitRead(GEN, 14)^bitRead(GEN, 13);
GEN=GEN<<1;
bitWrite(GEN, 0, XOR);
RND=bitRead(GEN, 15);
// RND = analogRead(A1)&0b1; // 23-24 мкс
PORTB |= (1 << 1); // фронт CLC
if (RND < 1) PORTB &= ~(1 << 2); // установить 0 в бите 2 (10-й пин)
else PORTB |= (1 << 2); // установить 1 в бите 2 (10-й пин)
Спасибо всем, кто помог!
Спасибо тут выражается выставлением Решения на тот пост что помог !
Вставилось криво, а редактировать нельзя.
Тут многие чем-то помогали, но нет какого-то конкретного поста, на который можно указать, как на однозначное решение.
Вот окончательный вариант:
// Получение последовательности псевдослучайных данных с регулируемой внешним потенциометром
// стабильной частотой (без джиттера) от 900 Гц до 20 кГц.
// Set up potentiometer pin
#define POT_PIN A3
// Set up clock out pin
#define CLC_OUT 9 // PORTB первый бит PB1
// Set up random out pin (выход данных)
#define RANDOM_OUT 10 // PORTB второй бит PB2
byte RND = 0; // текущий бит данных
int GEN=650;
bool XOR=1;
unsigned int POT = 10;
// unsigned int m=30; // считал, что 20 мкс - это 160 тактов при частоте 8 МГц, но получилось 30 (?)
// unsigned int POT_PLUS = POT + m; // добавка для выравнивания меандра
unsigned int l=0;
void setup() {
// Set up pins as input
pinMode(POT_PIN, INPUT);
// Set up pins as outputs
pinMode(CLC_OUT, OUTPUT);
pinMode(RANDOM_OUT, OUTPUT);
}
void loop() {
noInterrupts();
label: // до метки заполнялся массив случайных данных, пришлось убрать
XOR=bitRead(GEN, 14)^bitRead(GEN, 13); //
GEN=GEN<<1; // псевдослучайная последовательность на переменной GEN (аналог LFSR)
bitWrite(GEN, 0, XOR); // более стабильная, чем random (фронты CLC не дрожат)
RND=bitRead(GEN, 15); // "случайные" биты данных
// RND = analogRead(A1)&0b1; // 23-24 мкс, тоже без джиттера, но нужен шум на А1
PORTB |= (1 << 1); // фронт CLC
// Вывод данных
// digitalWrite(RANDOM_OUT, RND); // вывод данных на 10-й пин - около 3 мкс, сильный джиттер
if (RND < 1) PORTB &= ~(1 << 2); // установить 0 в бите 2 (10-й пин), всего за пару микросекунд
else PORTB |= (1 << 2); // установить 1 в бите 2 (10-й пин)
POT = analogRead(POT_PIN); // "чтение" потенциометра - 23-24 мкс
POT=POT/4*3; // уменьшил, чтобы приблизить мин. частоту к 1 кГц
for (l = 0; l < POT; l++) { // задержка, определяемая потенциометром
asm ("nop");
}
PORTB &= ~(1 << 1); // спад CLC
for (l = 0; l < POT+30; l++) { // добавка +30, чтобы приблизить CLC к меандру (не обязательно)
asm ("nop");
}
goto label;
interrupts();
} // закрыли loop
И то … Добавка к POT добавляет примерно 20 мкс и для этого потребовалось всего +30 “nop’ов” … Кто-нибудь может это объяснить?
Напоминаю, что у меня китайский клон LGT8F328P, там 4 таймера, а не 3, больше частота и т.д., так что могут быть всякие накладки.
Без добавки +30 отрицательный полупериод CLC короткий и частота больше 30 кГц. Просто данным всё равно, насколько симметричный CLC.
Можно читать POT после спада CLC и тогда положительный полупериод будет коротким.
“Это” объясняется тем, что вы не знаете, как “это” работает )).
А сколько по вашему длится “пустой цикл”, без “nop”? Нисколько?
А ведь есть ещё оптимизатор, который много чего может сделать на своё усмотрение…
Мне это нужно как небольшая часть макета, поэтому работает и ладно. Я писал о том, что нет какого-то однозначного решения, который любой пользователь может использовать без оглядки, потому что это назвали решением.
Вот это правильно.
Просто, чтобы разобраться, придётся серьёзно углубиться в тему, в целом подучиться. Если это надо лишь для одного проекта - не вижу смысла
Ну да, я и это через год забуду ) Просто тут написали, что надо указать на какой-то пост, как на “решение”, а такого поста нет.