Serial и прерывания

esisl
Offline
Зарегистрирован: 21.05.2015

Коллеги, подскажите решение.

Мне нужно управлять железкой в реальном времени с шагом времени 100 мкс. Нет проблем - использую прерывания по таймеру.

Но :( Одновременно мне нужно обмениваться данными с компьютером. Самое простое и очевидное - Serial

Однако прерывания и последовательный порт не работают :( Serial.write могу, а вот Serial.available Serial.read - нифига :(

Как быть?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Показать неработающий код, для начала :)

esisl
Offline
Зарегистрирован: 21.05.2015

Если я делаю так:

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

 

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

esisl пишет:

Serial.available() всегда возвращает 0

ок а, Serial.read(), что возвращает?

esisl
Offline
Зарегистрирован: 21.05.2015

false

Я нашёл упоминание, что если повиснуть на прерываниях таймера, то Serial использовать низя :(

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

esisl пишет:

false

Я нашёл упоминание, что если повиснуть на прерываниях таймера, то Serial использовать низя :(

Без ума низзя, а с умом можно.

esisl
Offline
Зарегистрирован: 21.05.2015

Кто согласен поделиться умом?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

esisl пишет:

Кто согласен поделиться умом?

Так вроде DIYMan в посте №1 выразил такое желание и попросил Вас показать код. А Вы что сделали? Показали маленький огрызочек и опять стали руками разводить "а если так, а если эдак". А как человеку Вам помочь, если он не видит ни Вашей ISR, ни того, как именно Вы работаете с сериалом, ни функции loop? Хрустальный шар у него сейчас на техобслуживании, а без него Ваш код ему узнать неоткуда. Так что, если Вам нужна помощь, делайте. что Вам говорят. А если Вы боитесь публиковать код из опасений за свою интеллектуальную собственноть, так и решайте проблемы этой собственности самостоятельно.

esisl
Offline
Зарегистрирован: 21.05.2015

Там нечего показывать :) Ну вот весь код для тестирования:


#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;
}

Этот код непрерывно печатает в терминале 0

Если что-то пытаться ему отправлять, то видно, что контроллер мигает светодиодом, но эха нет.
Если сделать так:

void loop(){
Serial.print(Serial.available(), DEC);
Serial.print(Serial.read());
}

Постоянно печатает 0-10-10-1... ну понимаете? Serial.available() всегда возвращает 0, Serial.read() всегда возвращает -1

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Версия IDE?

esisl
Offline
Зарегистрирован: 21.05.2015

1.23

Только, при чём тут версия IDE???? :)

Речь идёт о том, что аппаратный последовательный порт не желает работать, если я перенастраиваю прерывания.

vde69
Offline
Зарегистрирован: 10.01.2016

TCNT2 = tcnt2; // вот здесь засада

суть в том, что Вы изменили время тика и а на него завязана скорость передачи UART

перевестте свой тик на другой таймер (который отвечает за ШИМ) и все наладится....

esisl
Offline
Зарегистрирован: 21.05.2015

Ага... а примером не одарите?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

esisl пишет:

1.23

Только, при чём тут версия IDE???? :)

Речь идёт о том, что аппаратный последовательный порт не желает работать, если я перенастраиваю прерывания.

Версия IDE при том, что если потребуется смотреть код Serial'а то надо знать, где смотреть.

Это Вам кажется. что Вы знаете о чём идёт речь. Если бы Вы и впрямь это знали, Вы бы сейчас здесь не задавали этот вопрос.

Какая у Вас Ардуина? В смысле контроллер какой? (На всякий случай, если Вам вопрос опять покахется пустым - есть большая разница между atmega328 2560

vde69
Offline
Зарегистрирован: 10.01.2016

esisl пишет:

Ага... а примером не одарите?

нет, я на ардуинке таймеры никогда не программировал... просто понимаю принцепы по сколько делал подобное под DOS, только там все сложнее... а я Вам самое простое решение предложил...

 

а вообще поищите, здесь были классы для "многопоточности", там конечно сложно но там еще более правильное решение, расписывать его здесь не буду...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vde69 пишет:

суть в том, что Вы изменили время тика и а на него завязана скорость передачи UART

На второй таймер??? Не могли бы Вы указать страницу даташита? Или другой источник?

vde69
Offline
Зарегистрирован: 10.01.2016

ну и добавлю про версии IDE, там есть существенные различия в реализации прерываний, например раньше прерывания прерывали сами себя, сейчас вроде сделали вместо этого отдельную очередь....

esisl
Offline
Зарегистрирован: 21.05.2015

Arduino nano 328

vde69
Offline
Зарегистрирован: 10.01.2016

ЕвгенийП пишет:

vde69 пишет:

суть в том, что Вы изменили время тика и а на него завязана скорость передачи UART

На второй таймер??? Не могли бы Вы указать страницу даташита? Или другой источник?

всегда думал, что у меги несколько таймеров... если это не так, тогда нужно организовывать второй счетчик кратный делителю, о чем и написано выше (про многопоточность)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Хорошо. Nano у меня есть. Правда такой версии IDE как у Вас нету. Сегодня вечером я попробую запустить Ваш скетч и посмотрю на него. Всё там должно работать, я постоянно пользусь таймерами и Serial'ом - проблемы были только с мегой и там они тоже решабельны.

 

esisl
Offline
Зарегистрирован: 21.05.2015

Спасибо

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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 всякий раз при переполнении таймера.

Результат вполне ожидаемый:

Fun begins!
t=108372
o=149236
p=174922
d=268747

Ну, я вводил t, o, p и т.п., а она в ответ печатад. cnt каждый раз успевала подрасти пока я там вводить собирался.

Всё адекватно?

Теперь запустите сами и убедитесь. что оно также работает и у Вас и никаких, как Вы думали "аппаратный последовательный порт не желает работать" нет в и в помине. Работает за милую душу, если нормально всё сделать.

Сейчас я внимательно посмотрю на Ваш код и будет вторая серия.

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

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мкс////
}

На ввод данных реагирует.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Погодите, dimax, там смешнее всё. Бог с ней с Normal mode. Может человеку надо иногда менять длительность "переполнения", тогда он всё нормально делает.

Не подсказывайте, я хочу, что он сам дошёл почему у него не работает.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вторая серия.

Теперь я взял ВАШУ инициализацию таймера (явно избыточную) вставил вместо своей. Всё остальное оставил как у меня было.

#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 не проинициализировали? Первое прерывание у Вас когда попало наступит, ну да Бог с ним - работает ведь! Попробуйте!

esisl
Offline
Зарегистрирован: 21.05.2015

Эээ... Потому, что я сбрасываю "лишние" биты?

P.S. На самом деле, потому, что это копипаста. Но я пытаюсь разобраться :-P

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Третья серия.

Может дело в 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> и что вижу? Так то и вижу

1234
qwerty
zxcvb

Попробуйте у Вас также?

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

И последняя серия.

Беру в точности Ваш скетч из поста №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> и получаю тоже, что и впрошлый раз! Запустите!

А теперь подумайте в чём у Вас проблема (не в той строке, что закомментировал - она не виновата).

Подумайте и скажите.

esisl
Offline
Зарегистрирован: 21.05.2015

Сори, я это уже вечером попробую...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Попробуйте и скажите, что получится.

Т.е. не один из моих примеров Вы пока ещё не пробовали?

esisl
Offline
Зарегистрирован: 21.05.2015

Да. Вот этот пример http://arduino.ru/forum/programmirovanie/serial-i-preryvaniya#comment-20... заработал.

Спасибо! :)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так это ж Ваш пример, который вы мне выложили! Сравните!

Вы поняли в чём была проблема-то?

esisl
Offline
Зарегистрирован: 21.05.2015

Нет. У Вас биты иначе выставляются.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

esisl пишет:

Нет. У Вас биты иначе выставляются.

Евгений привёл РОВНО ваш скетч, только с одной закомментированной строчкой.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

esisl пишет:

Нет. У Вас биты иначе выставляются.

Нет, все биты такие же. Это ВАШ скетч котрый я перенёс тупой копипастой в IDE и закомментировал одну строку. Но с той строкой тоже работает, только она все загаживает на экране и не видно ни хрена, так что дело не в ней. А вот в чём дело, Вам бы очень полезно разобраться.

esisl
Offline
Зарегистрирован: 21.05.2015

В общем всем спасибо. Магия. Всё заработало, почему - не знаю.
Можно закрывать.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

esisl пишет:

Магия. Всё заработало, почему - не знаю. Можно закрывать.

Жаль, что Вы не захотели разобраться. Значит, ждём следующей такой же темы.

ardukov
Offline
Зарегистрирован: 25.02.2020

А у меня не работает Serial.

Я использую библиотеку timer-api.h для установки прерывания по таймеру.

Библиотек решил юзать из-за простоты назначения таймеров (https://habr.com/ru/post/337430/)

Использую timer_init_ISR_500KHz(TIMER_DEFAULT);

Serial.write - работает

Serial.available и Serial.read - не работает.

 

ardukov
Offline
Зарегистрирован: 25.02.2020
#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)); 
}

 

ardukov
Offline
Зарегистрирован: 25.02.2020

При включенной строке timer_init_ISR_500KHz(TIMER_DEFAULT); не работает  Serial.

При закомментированной строке  Serial работает, но, соответственно, не работает функция прерывания по таймеру.

nik182
Онлайн
Зарегистрирован: 04.05.2015

А как же ему бедному работать, если Вы ему работать не даёте. Прерывание работает дольше , чем Вы его вызываете.