13.08.2018, 20:46 | #1 |
Moderator
Регистрация: 20.07.2014
Адрес: МСК
Сообщений: 991
Вес репутации: 1031 |
Кольцевой буфер в прерываниях (AVR atmega328)
Мучая энкодер, понял, что вывод в Serial тормозит атмегу, что приводить к потере косяков энкодера.
Решил в прерываниях писать в FIFO, а в цикле выводить в Serial. За пару часов родилось и скомпилировалось это: КодКод:
#define set_bit(reg,value) reg |= (_BV(value)) #define unset_bit(reg,value) reg &= ~(_BV(value)) #define BUF_SIZE 256 //размер буфера обязательно равен степени двойки! #define BUF_MASK (BUF_SIZE-1) uint8_t idxIN, idxOUT; struct { uint8_t ReadPortD; //тут храним состояние порта D Пины с 0-7 на ардуино UNO unsigned long timer; }buff [BUF_SIZE] ; ISR(PCINT2_vect){ // сработало прерывание digitalWrite(13, HIGH); // включает светодиод unset_bit (PCICR,PCIE2); //отключаем прерывание до отработки прерывания, во избежании повторных срабатываний buff[idxIN++] = {PIND, millis()} ;//записываем состояние порта D idxIN &= BUF_MASK; set_bit (PCICR,PCIE2); //включаем прерывание. Вроде не забыл digitalWrite(13, LOW); // включает светодиод } void setup() { pinMode(5,INPUT_PULLUP); // Clock Подключаем к INT1, нельзя переназначать pinMode(6, INPUT_PULLUP); // второй вывод энкодера pinMode(7, INPUT_PULLUP); // кнопка энкодера pinMode(13, OUTPUT); // кнопка энкодера Serial.begin(9600); while (!Serial); set_bit (PCICR,PCIE2);//Включаем прерывание PCINT16-23 (PortD) PCIE2, регистр PCICR принимает значение 0b00000100; PCMSK2 |= (_BV(PCINT23)| _BV(PCINT22) | _BV(PCINT21)); //Указываем, что надо реагировать только на пины 5,7 // для порта D соответствуют pcint 16-23 Serial.println(PCMSK2,BIN); sei();//включаем прерывания Serial.println("ready"); } void loop() { if (idxIN != idxOUT) { // если сюда что-то записано, значит был поворот или нажатие кнопки, прерывания отключены. Надо бы не забыть их включить. Serial.print(idxIN); Serial.print(":"); Serial.print(idxOUT); Serial.print(":"); Serial.print(buff[idxOUT].timer); Serial.print(":"); Serial.println(buff[idxOUT++].ReadPortD,BIN); idxOUT &= BUF_MASK; } } [свернуть] Насколько он правильно работает - не знаю. Возможно в нем есть ошибки. Но в выводе есть странности: 11:4016:11100101 12:4016:10100101 13:4016:10100101 14:4016:10100101 15:4016:10100101 16:4016:11100101 В моем представлении в течении 4016 миллисекунды сначало произошло падение напряжения на 6 пине (с 11 на 12 срабатывание), потом 3 непонятных события (регистр PIND без изменений), и восстановление напряжения. А есть что-то быстрее чем millis()? Чтобы увидеть насколько разнесены по времени срабатывания. Последний раз редактировалось Tohin; 16.08.2018 в 21:11. |
13.08.2018, 21:04 | #2 | |
Administrator
Регистрация: 12.04.2010
Адрес: Москва
Сообщений: 9,618
Вес репутации: 9823 |
Цитата:
|
|
14.08.2018, 11:04 | #3 |
Senior Member
Регистрация: 02.04.2012
Адрес: Питер
Сообщений: 1,125
Вес репутации: 1311 |
Буфер слопал 60% ОЗУ.
__________________
istarik.ru |
16.08.2018, 21:11 | #4 |
Moderator
Регистрация: 20.07.2014
Адрес: МСК
Сообщений: 991
Вес репутации: 1031 |
Мне для тестов и 100% не жалко это ж не для устройства, это ж что-то вроде примитивного осцилографа для понимания косяков работы энкодера.
Хотя если есть реализация более правильная - с удовольствием посмотрю! Micros() тоже не помог: 5 1 6365 11000101 14 2 6365 11000101 14 3 6365 11000101 87 4 6365 11000101 254 5 6365 11100101 Если я правильно трактую рост idxIN в течении одной микросекунды произошло более 200 прерываний. Последний раз редактировалось Tohin; 16.08.2018 в 21:14. |
16.08.2018, 21:28 | #5 | |
Administrator
Регистрация: 12.04.2010
Адрес: Москва
Сообщений: 9,618
Вес репутации: 9823 |
Конденсаторы стоят? Я вроде рекомендовал
Цитата:
|
|
16.08.2018, 21:52 | #6 |
Moderator
Регистрация: 20.07.2014
Адрес: МСК
Сообщений: 991
Вес репутации: 1031 |
Я понимаю что это дребезг. Только что напаял 0.09 uF.
Стало лучше. Но есть и ложные срабатывания (5 последних значений одинаковые) 219 202 25528 11000101 219 203 25533 10000101 219 204 25533 10100111 219 205 25533 10000111 219 206 25533 10000111 219 207 25533 10000111 219 208 25533 10000111 219 209 25534 10000111 А что если в самом прерывании сравнивать текущее значение с предыдущим и отбрасывать идентичные? кажется так: if ((PIND>>5)^(last_PIND>>5)) Последний раз редактировалось Tohin; 21.12.2018 в 21:54. |
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1) | |
|
|