OWSlave на прерывании.

Soyer
Offline
Зарегистрирован: 25.06.2022

Всем здасьте!

объясните что не так с кодом (в симуляторе работает идеально):

//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 мкСек и читаем, а в реальной ардуино надо пропускать таймслоты.

Что-то я не понимаю, либо я дурак, либо.... Вообщем, прошу помощи!

Upper
Offline
Зарегистрирован: 23.06.2020

Soyer пишет:

объясните что не так с кодом

Код как минимум не аккуратный.

Что сразу бросается в глаза.

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

 

Soyer
Offline
Зарегистрирован: 25.06.2022

дело вообще не в таймере, можно вообще отказаться от него, путем перехвата RISING, суть вопроса в ошибке тайм-слота, но за правку по таймеру и прерыванию спасибо!

Soyer
Offline
Зарегистрирован: 25.06.2022

ошибка была здесь:

   case OW_RESET:
     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;

Тема закрыта!

Upper
Offline
Зарегистрирован: 23.06.2020

Soyer пишет:

перед посылкой presence(); необходимо было запретить аппаратное прерывание:

case OW_RESET:

     SET_FALLING;
     DIS_INT;
     presence();//посылаем PRESENCE

Если вы про

  038             case OW_RESET:

то там все прерывания уже запрещены т.к. выполняется обработчик прерывания. Вероятнее влияет сброс флага прерывания при последующем "включении прерывания" EN_INT.

Soyer
Offline
Зарегистрирован: 25.06.2022

однако же после запрета внешнего прерывания все заработало. Мистика однако!)))

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Блин ну этот код, просто вынос мозга :))) Циклы в прерывании это песня. Глюки обеспечены. То как вы написали обработку в прерывании, делает так, что прерывание не имеет смысла.

Soyer
Offline
Зарегистрирован: 25.06.2022

а что не так с циклами? если не использовать цикл для записи бита, то гарантирован пропуск времени считывания (для onewire важны тайминги). я пробовал без цикла, мастер прерывал search rom из-за того что ведомый не успевал вовремя передать бит. а в цикле успевает. кроме того, пока ведомый не обработает команды мастера, другие какие-то действия ведомого контроллера не имеют значения, поэтому как долго ведомый обрабатывает прерывание не имеет смысла.

Green
Offline
Зарегистрирован: 01.10.2015

Писал когда то. Не так всё просто. Транспортный, сетевой, уровень связи... И очень шустро нужно реагировать... Но, всё реально.