OWSlave на прерывании.
- Войдите на сайт для отправки комментариев
Чт, 30/06/2022 - 14:05
Всем здасьте!
объясните что не так с кодом (в симуляторе работает идеально):
//cpp файл //для теста PIN 2 #define TCNT_REG TCNT2 //register of timer-counter #define TIMER_INT ISR(TIMER2_COMPA_vect) //the timer interrupt service routine #define EN_TIMER {TCNT_REG=0;TIMSK2|= (1 << OCIE2A); TIFR2|=(1<<TOV2);} //enable timer interrupt #define DIS_TIMER (TIMSK2 &=~(1<<OCIE2A))// disable timer interrupt #define OW_PINN (1<<PIND2) #define SET_LOW {DDRD |= (1 << DDD2);PORTD &= ~(1 << PORTD2);} //set 1-Wire line to low #define ENTR_LINE {DDRD &= ~ (1 << DDD2);} //set 1-Wire pin as input #define READ_HIGH ((PIND & OW_PINN)==OW_PINN)//считать высокий сигнал с линии #define READ_LOW (!READ_HIGH)//считать низкий сигнал с линии #define EN_INT {EIMSK|=(1<<INT0);EIFR|=(1<<INTF0);} //enable interrupt #define DIS_INT EIMSK&=~(1<<INT0); //disable interrupt #define SET_RISING EICRA=(1<<ISC01)|(1<<ISC00); //set interrupt at rising edge #define SET_FALLING {EICRA &= ~(1 << ISC00);EICRA|=(1<<ISC01);} //set interrupt at falling edge #define CHKCHK_INT_EN_INT_EN (EIMSK&(1<<INT0))==(1<<INT0) //test if interrupt enabled #define CHK_FLLNG_EN (!(EICRA&(1<<ISC00))==(1<<ISC00)) #define OW_START 0 #define OW_RESET 1 #define OW_COMMAND 2 #define OW_WRITE 0xCD #define OW_SEARCH_ROM 0xF0 #define OW_READ_ROM 0x33 #define OW_MATCH_ROM 0x55 #define OW_SKIP_ROM 0xCC ISR (INT0_vect){ uint8_t r; if (CHK_FLLNG_EN) {EN_TIMER;} switch (mode) { case OW_RESET: SET_FALLING; presence();//посылаем PRESENCE mode = OW_COMMAND; bitmask=1;rsv_byte=0; return; case OW_COMMAND: delayMicroseconds(15); r = READ_HIGH; if(r & 1) rsv_byte |= bitmask; bitmask = (bitmask << 1); if(bitmask) {return;} DIS_INT; bitmask=1;actbit=0;wmode = 0; instance-> CallbackCommand_(rsv_byte); EN_INT; switch (rsv_byte) { case 0xF0: //search rom DIS_INT; for (int i=0; i<8; i++) { for (bitmask = 0x01; bitmask; bitmask <<= 1) { actbit = (bitmask & rom_[i])?1:0; wmode = !actbit; while (READ_LOW);//ждем 1 while (READ_HIGH);//ждем 0 if(!actbit){ SET_LOW; delayMicroseconds(20); ENTR_LINE; } while (READ_LOW);//ждем 1 while (READ_HIGH);//ждем 0 if(!wmode){ SET_LOW; delayMicroseconds(20); ENTR_LINE; } while (READ_LOW);//ждем 1 while (READ_HIGH);//ждем 0 delayMicroseconds(15); r = READ_HIGH; if (r!=actbit){ mode=0;EN_INT; return; } } } ;mode=0;EN_INT return; } default: return; } } //the timer interrupt service routine for ATmega328P and Attiny X5 TIMER_INT { DIS_TIMER; mode=OW_RESET; SET_RISING; } OWSLAVE::OWSLAVE(){ TCCR2A = 0; TCCR2B = 0; TCNT2 = 0; TIMSK2 = 0; OCR2A=90; TCCR2A |=(1<<WGM21); TCCR2B |=(1<<CS22) | (0<<CS21) | (0<<CS20);//clkT2S/64 (From prescaler) DIS_TIMER; } bool presence(){ while (READ_LOW); #if defined (__AVR_ATmega328P__) delayMicroseconds(30); SET_LOW; del(120); #elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) del(20); SET_LOW; del(100); #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) del(15); SET_LOW; del(100); #endif ENTR_LINE; while (READ_LOW);//return 0; } как только его переносишь на ардуино - не работает, но если добавить две строчки в OW_COMMAND:
case OW_COMMAND: while (!READ_HIGH);//ждем высокий уровень на входе while (READ_HIGH);// ждем низкий уровень на входе delayMicroseconds(15); r = READ_HIGH; .... .... ....
то работает на ардуино, но в симуляторе нет (видно что идут пропуски при принятии ROM команды от мастера (F0).
в оригинальной OneWireSlave есть функция waitTimeSlot() которая практически делает тоже самое что и мои две строчки, и без нее вместо F0 принимается E1.
вот и не пойму что не так, чисто по логике: сработало прерывание, ждем 15 -20 мкСек и читаем, а в реальной ардуино надо пропускать таймслоты.
Что-то я не понимаю, либо я дурак, либо.... Вообщем, прошу помощи!
объясните что не так с кодом
Код как минимум не аккуратный.
Что сразу бросается в глаза.
007
#define EN_TIMER {TCNT_REG=0;TIMSK2|= (1 << OCIE2A); TIFR2|=(1<<TOV2);} //enable timer interrupt
сначала надо сбросить флаг, потом разрешить прерывания,
зачем используется (1<<TOV2); ?
Для сброса конкретного флага, надо просто писать в его место 1. Если делать ИЛИ, то сбросятся все остальные флаги. Если использовать маску нужного флага, то ИЛИ лишнее.
аналогично
016
#define EN_INT {EIMSK|=(1<<INT0);EIFR|=(1<<INTF0);} //enable interrupt
дело вообще не в таймере, можно вообще отказаться от него, путем перехвата RISING, суть вопроса в ошибке тайм-слота, но за правку по таймеру и прерыванию спасибо!
ошибка была здесь:
перед посылкой presence(); необходимо было запретить аппаратное прерывание:
Тема закрыта!
перед посылкой presence(); необходимо было запретить аппаратное прерывание:
Если вы про
то там все прерывания уже запрещены т.к. выполняется обработчик прерывания. Вероятнее влияет сброс флага прерывания при последующем "включении прерывания" EN_INT.
однако же после запрета внешнего прерывания все заработало. Мистика однако!)))
Блин ну этот код, просто вынос мозга :))) Циклы в прерывании это песня. Глюки обеспечены. То как вы написали обработку в прерывании, делает так, что прерывание не имеет смысла.
а что не так с циклами? если не использовать цикл для записи бита, то гарантирован пропуск времени считывания (для onewire важны тайминги). я пробовал без цикла, мастер прерывал search rom из-за того что ведомый не успевал вовремя передать бит. а в цикле успевает. кроме того, пока ведомый не обработает команды мастера, другие какие-то действия ведомого контроллера не имеют значения, поэтому как долго ведомый обрабатывает прерывание не имеет смысла.
Писал когда то. Не так всё просто. Транспортный, сетевой, уровень связи... И очень шустро нужно реагировать... Но, всё реально.