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, суть вопроса в ошибке тайм-слота, но за правку по таймеру и прерыванию спасибо!
ошибка была здесь:
SET_FALLING; presence();//посылаем PRESENCE mode = OW_COMMAND; bitmask=1;rsv_byte=0; return;перед посылкой presence(); необходимо было запретить аппаратное прерывание:
case OW_RESET: SET_FALLING; DIS_INT; presence();//посылаем PRESENCE EN_INT; mode = OW_COMMAND; bitmask=1;rsv_byte=0; return;Тема закрыта!
перед посылкой presence(); необходимо было запретить аппаратное прерывание:
case OW_RESET: SET_FALLING; DIS_INT; presence();//посылаем PRESENCEЕсли вы про
то там все прерывания уже запрещены т.к. выполняется обработчик прерывания. Вероятнее влияет сброс флага прерывания при последующем "включении прерывания" EN_INT.
однако же после запрета внешнего прерывания все заработало. Мистика однако!)))
Блин ну этот код, просто вынос мозга :))) Циклы в прерывании это песня. Глюки обеспечены. То как вы написали обработку в прерывании, делает так, что прерывание не имеет смысла.
а что не так с циклами? если не использовать цикл для записи бита, то гарантирован пропуск времени считывания (для onewire важны тайминги). я пробовал без цикла, мастер прерывал search rom из-за того что ведомый не успевал вовремя передать бит. а в цикле успевает. кроме того, пока ведомый не обработает команды мастера, другие какие-то действия ведомого контроллера не имеют значения, поэтому как долго ведомый обрабатывает прерывание не имеет смысла.
Писал когда то. Не так всё просто. Транспортный, сетевой, уровень связи... И очень шустро нужно реагировать... Но, всё реально.