Прерывания по таймеру

MarioM
Offline
Зарегистрирован: 18.04.2011

Всем привет. Суть в следующем - нужно 3-5 раз в секунду считывать состояние портов ввода вывода в предназначенные для этого переменные и затем выводить их состояние на экран. Все это уже реализовано но без прерываний. Имеем плату расширения портов ввода вывода и LCD шилд. Все это подключено по I2C. Решил сделать считывание портов по прерыванию таймера. Использовать решил библиотеку http://robocraft.ru/blog/arduino/614.html. Так же поключены библиотеки <MCP23xxx.h> <LiquidCrystal_MCP23x17.h>

Функция считывания состояния портов вызываемая по прерыванию
void scanIOport()
{
  portI = mcp23_in.digitalRead16();
  portO = mcp23_out.digitalRead16();
}

Так вот суть проблемы в том что контроллер виснет сразу после запуска. Висяк вызван именно методом .digitalRead16(); Если вместо считывания присваивать переменным рандомные значения то все работает как часы. И дело похоже не в частоте опроса портов, так как ни 1 раз в 2 секунды ни 2 раза в секунду опрашивания не происходит. Имеем просто светящийся оранжевый экран. Программа не выполняет ни единой строчки из цикла loop(); пробовал и без использования библиотеки TimerOne - аналогично. пожоже конфликт с I2C?

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

MarioM, о том как , и в какое прерывание вы это засовывате -нужно самому догадываться?

MarioM
Offline
Зарегистрирован: 18.04.2011

Написал же что использую библиотеку TimerOne ( ссылку дал)

вот как инициализируется объект в этой библиотеке

void TimerOne::initialize(long microseconds)
{
  TCCR1A = 0;                 // clear control register A 
  TCCR1B = _BV(WGM13);        // set mode 8: phase and frequency correct pwm, stop the timer
  setPeriod(microseconds);
}


void TimerOne::setPeriod(long microseconds)		// AR modified for atomic access
{
  
  long cycles = (F_CPU / 2000000) * microseconds;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  if(cycles < RESOLUTION)              clockSelectBits = _BV(CS10);              // no prescale, full xtal
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11);              // prescale by /8
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10);  // prescale by /64
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12);              // prescale by /256
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10);  // prescale by /1024
  else        cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10);  // request was out of bounds, set as maximum
  
  oldSREG = SREG;				
  cli();							// Disable interrupts for 16 bit register access
  ICR1 = pwmPeriod = cycles;                                          // ICR1 is TOP in p & f correct pwm mode
  SREG = oldSREG;
  
  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
  TCCR1B |= clockSelectBits;                                          // reset clock select register, and starts the clock
}

вот в какое прерывание засунута моя функция :

ISR(TIMER1_OVF_vect)          // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
  Timer1.isrCallback();
}

вот как происходит привязывание

void TimerOne::attachInterrupt(void (*isr)(), long microseconds)
{
  if(microseconds > 0) setPeriod(microseconds);
  isrCallback = isr;                                       // register the user's callback with the real ISR
  TIMSK1 = _BV(TOIE1);                                     // sets the timer overflow interrupt enable bit
	// might be running with interrupts disabled (eg inside an ISR), so don't touch the global state
//  sei();
  resume();												
}

а если в кратце то пробовал и c TIMER1 и TIMER2 без использования библиотеки TimerOne. но с прерываниями я еще на Вы и подумал, что делаю что-то не так, поэтому и подключил библиотеку TimerOne, но результат аналогичный. Вообще позволяет ли Ардуина работать с I2C по прерыванию?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

MarioM, могу только высказать предположение. Где-то видимо используется прерывание, а запуск некоего кода, использующего прерывания из-под другого прерывания по-умолчанию запрещён. Попробуйте разрешить, может прокатит. Лучше пробывать без timerOne . Опция  используется так:  ISR(XXX_vect, ISR_NOBLOCK)

Datak
Offline
Зарегистрирован: 09.10.2014

MarioM, могу наврать, но по-моему функция по указателю не так вызывается.



Timer1.isrCallback();

Как правильно - не помню, спишите откуда-нибудь. Я сам всегда списываю. :)

-----

Upd: Вот так попробуйте:

(*Timer1.isrCallback)( );

 

alex_r61
Offline
Зарегистрирован: 20.06.2012

Для примера, PORTC5 переключается раз в секунду а PORTC4 раз в max_tick секунд.


#define TOGGLE(x,y) (x ^= (1<<y))

#define max_tick 5
unsigned char tick=0;
unsigned char flags=0;
#define sec1  0
#define Nsec  1

void timer1_init(void)
{
  TCCR1A = 0x00;
  TCCR1B = 0x04;
  TCCR1C = 0x00;
  TCNT1H = 0x0B;
  TCNT1L = 0xDC;
  TIMSK1 = 0x01;
}
//=======================================================================
ISR(TIMER1_OVF_vect)
{
  //TIMER1 has overflowed
  TCNT1H = 0x0B;    //reload counter high value
  TCNT1L = 0xDC;    //reload counter low value
 
  bitSet(flags, sec1); 
  tick++;
  if(tick >= max_tick)
  {
    tick = 0;
    bitSet(flags, Nsec);
  }
}
//=======================================================================

void setup(){
  timer1_init();
  DDRC = 0x30;
}

void loop(){
  if(bitRead(flags, sec1)){
    bitClear(flags, sec1);
    // выполняется раз в секунду
    TOGGLE(PORTC, 5);
  }
  else if(bitRead(flags, Nsec)){
    bitClear(flags, Nsec);
    // выполняется раз в max_tick секунд
    TOGGLE(PORTC, 4);
  }
}
MarioM
Offline
Зарегистрирован: 18.04.2011

dimax, спасибо заработало но не без глюков. Буду дальше копать.