Serial и прерывания
- Войдите на сайт для отправки комментариев
Пт, 17/06/2016 - 14:21
Коллеги, подскажите решение.
Мне нужно управлять железкой в реальном времени с шагом времени 100 мкс. Нет проблем - использую прерывания по таймеру.
Но :( Одновременно мне нужно обмениваться данными с компьютером. Самое простое и очевидное - Serial
Однако прерывания и последовательный порт не работают :( Serial.write могу, а вот Serial.available Serial.read - нифига :(
Как быть?
Показать неработающий код, для начала :)
Если я делаю так:
void setup() { TIMSK2 &= ~(1<<TOIE2); TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); TCCR2B &= ~((1<<WGM22) | (1<<CS22)); TCCR2B |= ((1<<CS21) | (1<<CS20)); ASSR &= ~(1<<AS2); TIMSK2 |= (1<<TOIE2); Serial.begin(9600); }То далее
Serial.print("ssf"); - работает
А вот
Serial.available() всегда возвращает 0
Serial.available() всегда возвращает 0
ок а, Serial.read(), что возвращает?
false
Я нашёл упоминание, что если повиснуть на прерываниях таймера, то Serial использовать низя :(
false
Я нашёл упоминание, что если повиснуть на прерываниях таймера, то Serial использовать низя :(
Без ума низзя, а с умом можно.
Кто согласен поделиться умом?
Кто согласен поделиться умом?
Так вроде DIYMan в посте №1 выразил такое желание и попросил Вас показать код. А Вы что сделали? Показали маленький огрызочек и опять стали руками разводить "а если так, а если эдак". А как человеку Вам помочь, если он не видит ни Вашей ISR, ни того, как именно Вы работаете с сериалом, ни функции loop? Хрустальный шар у него сейчас на техобслуживании, а без него Ваш код ему узнать неоткуда. Так что, если Вам нужна помощь, делайте. что Вам говорят. А если Вы боитесь публиковать код из опасений за свою интеллектуальную собственноть, так и решайте проблемы этой собственности самостоятельно.
Там нечего показывать :) Ну вот весь код для тестирования:
#include <avr/version.h> #include <avr/io.h> #include <avr/interrupt.h> volatile unsigned int tcnt2 = 206; //32 такта делителя * (256-206)=1600 тактов. То есть каждые 0,1 мс. void setup() { TIMSK2 &= ~(1<<TOIE2); TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); TCCR2B &= ~((1<<WGM22) | (1<<CS22)); TCCR2B |= ((1<<CS21) | (1<<CS20)); ASSR &= ~(1<<AS2); TIMSK2 |= (1<<TOIE2); Serial.begin(9600); } void loop(){ while(Serial.available()>0){ Serial.write(Serial.read()); } Serial.print('0'); } //************* прерывание ************* ISR(TIMER2_OVF_vect) { TCNT2 = tcnt2; }Если что-то пытаться ему отправлять, то видно, что контроллер мигает светодиодом, но эха нет.
Если сделать так:
void loop(){ Serial.print(Serial.available(), DEC); Serial.print(Serial.read()); }Постоянно печатает 0-10-10-1... ну понимаете? Serial.available() всегда возвращает 0, Serial.read() всегда возвращает -1
Версия IDE?
1.23
Только, при чём тут версия IDE???? :)
Речь идёт о том, что аппаратный последовательный порт не желает работать, если я перенастраиваю прерывания.
TCNT2 = tcnt2; // вот здесь засада
суть в том, что Вы изменили время тика и а на него завязана скорость передачи UART
перевестте свой тик на другой таймер (который отвечает за ШИМ) и все наладится....
Ага... а примером не одарите?
1.23
Только, при чём тут версия IDE???? :)
Речь идёт о том, что аппаратный последовательный порт не желает работать, если я перенастраиваю прерывания.
Версия IDE при том, что если потребуется смотреть код Serial'а то надо знать, где смотреть.
Это Вам кажется. что Вы знаете о чём идёт речь. Если бы Вы и впрямь это знали, Вы бы сейчас здесь не задавали этот вопрос.
Какая у Вас Ардуина? В смысле контроллер какой? (На всякий случай, если Вам вопрос опять покахется пустым - есть большая разница между atmega328 2560
Ага... а примером не одарите?
нет, я на ардуинке таймеры никогда не программировал... просто понимаю принцепы по сколько делал подобное под DOS, только там все сложнее... а я Вам самое простое решение предложил...
а вообще поищите, здесь были классы для "многопоточности", там конечно сложно но там еще более правильное решение, расписывать его здесь не буду...
суть в том, что Вы изменили время тика и а на него завязана скорость передачи UART
На второй таймер??? Не могли бы Вы указать страницу даташита? Или другой источник?
ну и добавлю про версии IDE, там есть существенные различия в реализации прерываний, например раньше прерывания прерывали сами себя, сейчас вроде сделали вместо этого отдельную очередь....
Arduino nano 328
суть в том, что Вы изменили время тика и а на него завязана скорость передачи UART
На второй таймер??? Не могли бы Вы указать страницу даташита? Или другой источник?
всегда думал, что у меги несколько таймеров... если это не так, тогда нужно организовывать второй счетчик кратный делителю, о чем и написано выше (про многопоточность)
Хорошо. Nano у меня есть. Правда такой версии IDE как у Вас нету. Сегодня вечером я попробую запустить Ваш скетч и посмотрю на него. Всё там должно работать, я постоянно пользусь таймерами и Serial'ом - проблемы были только с мегой и там они тоже решабельны.
Спасибо
esisl,
это первая серия моего ответа. у меня Nano, 328, IDE - 1.6.5
#define OVERFLOW_INTERVAL 206 void TimerInit(void) { PRR &= ~bit(PRTIM2); // Убедимся, что таймер 2 не отключен TCCR2A = 0; // Установим Normal режим TCCR2B = bit(CS20) | bit(CS21); // Установим делитель частоты 32 TCNT2 = OVERFLOW_INTERVAL; // Установим счётчик TIMSK2 |= bit(TOIE2); // Разрешим прерывание по переполнению TIFR2 = 1; // Очистим прерывание } static volatile unsigned long cnt = 0; ISR(TIMER2_OVF_vect) { cnt ++; TCNT2 = OVERFLOW_INTERVAL; } void setup() { Serial.begin(115200); Serial.println("Fun begins!"); TimerInit(); } void loop() { if (Serial.available()) { char c = Serial.read(); if (isalpha(c)) { Serial.print(c); Serial.print('='); Serial.println(cnt); } } }Псмотрите, я использую тот же, что и Вы, таймер (2), тот же делитель (32) и то же количество тиков (206).
Как видите, в loop она проверяет нет ли чего в Serial'е, и, если есть, читает символ. Если символ буква, то она печатает её, затем знак равенства, затем значение переменной cnt.
Переменная же cnt просто увеличивается на 1 всякий раз при переполнении таймера.
Результат вполне ожидаемый:
Ну, я вводил t, o, p и т.п., а она в ответ печатад. cnt каждый раз успевала подрасти пока я там вводить собирался.
Всё адекватно?
Теперь запустите сами и убедитесь. что оно также работает и у Вас и никаких, как Вы думали "аппаратный последовательный порт не желает работать" нет в и в помине. Работает за милую душу, если нормально всё сделать.
Сейчас я внимательно посмотрю на Ваш код и будет вторая серия.
esisl, вы таймер программируете через #$$@. Зачем обнулять счётный регистр в прерывании? У таймера есть режим сброс по совпадению, записываете в регистр сравнения нужное число тактов, и всё. Вот то-же самое, но по-человечески:
void setup() { Serial.begin(9600); TCCR2A= 1<<WGM21; //CTC mode TCCR2B= 1<<CS21 ; // divider=8 OCR2A=199; // TIMSK2= 1<<OCIE2A; } void loop(){ while(Serial.available()>0){ Serial.write(Serial.read()); } Serial.print('0'); } ISR(TIMER2_COMPA_vect) { // судя попадает каждые 100мкс//// }На ввод данных реагирует.
Погодите, dimax, там смешнее всё. Бог с ней с Normal mode. Может человеку надо иногда менять длительность "переполнения", тогда он всё нормально делает.
Не подсказывайте, я хочу, что он сам дошёл почему у него не работает.
Вторая серия.
Теперь я взял ВАШУ инициализацию таймера (явно избыточную) вставил вместо своей. Всё остальное оставил как у меня было.
#define OVERFLOW_INTERVAL 206 void TimerInit(void) { TIMSK2 &= ~(1<<TOIE2); TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); TCCR2B &= ~((1<<WGM22) | (1<<CS22)); TCCR2B |= ((1<<CS21) | (1<<CS20)); ASSR &= ~(1<<AS2); TIMSK2 |= (1<<TOIE2); } static volatile unsigned long cnt = 0; ISR(TIMER2_OVF_vect) { cnt ++; TCNT2 = OVERFLOW_INTERVAL; } void setup() { TimerInit(); Serial.begin(115200); Serial.println("Fun begins!"); } void loop() { if (Serial.available()) { char c = Serial.read(); if (isalpha(c)) { Serial.print(c); Serial.print('='); Serial.println(cnt); } } }и опять ведь нормально работает!
Кстати, я не понял почему Вы при инициализации TCNT2 не проинициализировали? Первое прерывание у Вас когда попало наступит, ну да Бог с ним - работает ведь! Попробуйте!
Эээ... Потому, что я сбрасываю "лишние" биты?
P.S. На самом деле, потому, что это копипаста. Но я пытаюсь разобраться :-P
Третья серия.
Может дело в loop? Может там у Вас что не так? А может в том, что у меня скорость другая? Или в том, я сначала "Fun begins" печатаю?
Ладно, делаю ВСЁ как у Вас
#define OVERFLOW_INTERVAL 206 void TimerInit(void) { TIMSK2 &= ~(1<<TOIE2); TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); TCCR2B &= ~((1<<WGM22) | (1<<CS22)); TCCR2B |= ((1<<CS21) | (1<<CS20)); ASSR &= ~(1<<AS2); TIMSK2 |= (1<<TOIE2); } ISR(TIMER2_OVF_vect) { TCNT2 = OVERFLOW_INTERVAL; } void setup() { TimerInit(); Serial.begin(9600); } void loop() { while(Serial.available()>0){ Serial.write(Serial.read()); } }Ну, что в окне терминала ввожу 1234<ENTER>qwerty<ENTER>zxcvb<ENTER> и что вижу? Так то и вижу
Попробуйте у Вас также?
И последняя серия.
Беру в точности Ваш скетч из поста №8.
Только убираю в нём дурацкую печать 0 при каждом проходе loop чтобы не засирала терминал
#include <avr/version.h> #include <avr/io.h> #include <avr/interrupt.h> volatile unsigned int tcnt2 = 206; //32 такта делителя * (256-206)=1600 тактов. То есть каждые 0,1 мс. void setup() { TIMSK2 &= ~(1<<TOIE2); TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); TCCR2B &= ~((1<<WGM22) | (1<<CS22)); TCCR2B |= ((1<<CS21) | (1<<CS20)); ASSR &= ~(1<<AS2); TIMSK2 |= (1<<TOIE2); Serial.begin(9600); } void loop(){ while(Serial.available()>0){ Serial.write(Serial.read()); } //Serial.print('0'); } //************* прерывание ************* ISR(TIMER2_OVF_vect) { TCNT2 = tcnt2; }Убедитесь - это в чистом виде Ваш скетч. Одна строка закомментирована.
И .... он работает совершенно адекватно. Ввожу 1234<ENTER>qwerty<ENTER>zxcvb<ENTER> и получаю тоже, что и впрошлый раз! Запустите!
А теперь подумайте в чём у Вас проблема (не в той строке, что закомментировал - она не виновата).
Подумайте и скажите.
Сори, я это уже вечером попробую...
Попробуйте и скажите, что получится.
Т.е. не один из моих примеров Вы пока ещё не пробовали?
Да. Вот этот пример http://arduino.ru/forum/programmirovanie/serial-i-preryvaniya#comment-20... заработал.
Спасибо! :)
Так это ж Ваш пример, который вы мне выложили! Сравните!
Вы поняли в чём была проблема-то?
Нет. У Вас биты иначе выставляются.
Нет. У Вас биты иначе выставляются.
Евгений привёл РОВНО ваш скетч, только с одной закомментированной строчкой.
Нет. У Вас биты иначе выставляются.
Нет, все биты такие же. Это ВАШ скетч котрый я перенёс тупой копипастой в IDE и закомментировал одну строку. Но с той строкой тоже работает, только она все загаживает на экране и не видно ни хрена, так что дело не в ней. А вот в чём дело, Вам бы очень полезно разобраться.
В общем всем спасибо. Магия. Всё заработало, почему - не знаю.
Можно закрывать.
Магия. Всё заработало, почему - не знаю. Можно закрывать.
Жаль, что Вы не захотели разобраться. Значит, ждём следующей такой же темы.
А у меня не работает Serial.
Я использую библиотеку timer-api.h для установки прерывания по таймеру.
Библиотек решил юзать из-за простоты назначения таймеров (https://habr.com/ru/post/337430/)
Использую timer_init_ISR_500KHz(TIMER_DEFAULT);
Serial.write - работает
Serial.available и Serial.read - не работает.
#include"timer-api.h" void setup() { Serial.begin(9600); timer_init_ISR_500KHz(TIMER_DEFAULT); ////// pinMode(13, OUTPUT); } void loop() { while(Serial.available()>0){ Serial.write(Serial.read()); } } void timer_handle_interrupts(int timer) { digitalWrite(13, !digitalRead(13)); }При включенной строке timer_init_ISR_500KHz(TIMER_DEFAULT); не работает Serial.
При закомментированной строке Serial работает, но, соответственно, не работает функция прерывания по таймеру.
А как же ему бедному работать, если Вы ему работать не даёте. Прерывание работает дольше , чем Вы его вызываете.