Обработчик прерывания и SLEEP

anarch
Offline
Зарегистрирован: 10.09.2017

Делаю брелок сигнализатор. В проэкте использую ATtiny13.

На стороне АРДУИНО использую библиотеку VirtualWire.

Из-за того что готовая библиотека занимает много места и не вмещаеться в памяти тини13 написал кусок который отправляет пакет.


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/crc16.h>
#include <util/delay.h>
#include <string.h>
#include <avr/sleep.h>

#define _code "R01"
//#define _code "R02"
//#define _code "R03"
//#define _code "R04"

#define Input 1
#define Led 2
#define RF_vcc 3

#define TXPORT PORTB    // Имя порта для передачи
#define TXDDR DDRB    // Регистр направления порта на передачу
#define TXD 0     // Номер бита порта для использования на передачу

#define VW_HEADER_LEN 8 //Длина хидера
#define VW_MAX_MESSAGE_LEN 6 //Максимальная длина сообщения

/*
  Ниже задаются константы, определяющие скорость передачи данных (бодрейт)
  расчет BAUD_DIV осуществляется следующим образом:
  BAUD_DIV = (CPU_CLOCK / DIV) / BAUD_RATE
  где CPU_CLOCK - тактовая частота контроллера, BAUD_RATE - желаемая скорость UART,
  а DIV - значение делителя частоты таймера, задающееся регистром TCCR0B.
  Например, тактовая частота 9.6 МГц, делитель на 8, скорость порта 9600 бод:
  BAUD_DIV = (9 600 000 / 8) / 9600 = 125 (0x7D).
*/

//#define T_DIV   0x01  // DIV = 1
//#define T_DIV   0x02  // DIV = 8
#define T_DIV   0x03  // DIV = 64
#define BAUD_DIV  0x4B  //Скорость 2000 бод
//#define BAUD_DIV  0x7D  // Скорость = 9600 бод

uint8_t symbols[16] =
{
  0xd,  0xe,  0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c,
  0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x32, 0x34
};

uint8_t vw_tx_buf[(VW_MAX_MESSAGE_LEN * 2) + VW_HEADER_LEN]
  = {0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x38, 0x2c};

volatile uint16_t txbyte;
volatile uint8_t txbitcount;
volatile byte alarm_flag = false;

const char *msg = _code;

void tx_send();
void RF_send(uint8_t* buf, uint8_t len);

ISR(TIM0_COMPA_vect)
{
  TXPORT = (TXPORT & ~(1 << TXD)) | ((txbyte & 0x01) << TXD);
  txbyte >>= 1;
  if (txbitcount > 0)     // Если идет передача (счетик бит больше нуля),
  {
    txbitcount--;     // то уменьшаем его на единицу.
  }
}

ISR(INT0_vect)
{
  alarm_flag = true;
}

int main(void)
{
  pinMode(Input, INPUT_PULLUP);
  pinMode(Led, OUTPUT);
  pinMode(RF_vcc, OUTPUT);
  digitalWrite(Led, LOW);
  digitalWrite(RF_vcc, LOW);
  GIMSK |= (1 << INT0);
  MCUCR |= (1 << ISC01) | (0 << ISC00);
  txbyte = 0xFFFF;    // Значение буфера на передачу - все единицы
  txbitcount = 0x00;    // Значение счетчика преедаваемых бит - ноль (ничего пока не передаем)

  TXDDR |= (1 << TXD);    // Задаем направление порта на передачу как выход
  OCR0A = BAUD_DIV;   // Задаем значение регистра OCR0A в соответствии с бодрейтом
  TIMSK0 |= (1 << OCIE0A);  // Разрешаем прерывание TIM0_COMPA
  TCCR0A |= (1 << WGM01);   // Режим таймера CTC (очистка TCNT0 по достижению OCR0A)
  TCCR0B |= T_DIV;    // Задаем скорость счета таймера в соответствии с делителем
  sei();        // Разрешаем прерывания глобально
  while (1)
  {
    if (alarm_flag == true) {
      digitalWrite(Led, HIGH);
      digitalWrite(RF_vcc, HIGH);
      _delay_ms(1);
      RF_send((uint8_t *)msg, strlen(msg));
      _delay_ms(1);
      digitalWrite(RF_vcc, LOW);
      while (digitalRead(Input) == LOW);
      digitalWrite(Led, LOW);
      alarm_flag = false;
    }
  }
  return (0);
}

void tx_send()
{
  uint8_t i;
  for (i = 0; i < strlen((char *)vw_tx_buf); i++) {
    while (txbitcount);   // Ждем пока закончится передача предыдущего байта
    txbyte = vw_tx_buf[i];
    txbitcount = 0x06;    // Задаем счетчик байт равным 10
  }
}

void RF_send(uint8_t* buf, uint8_t len)
{
  uint8_t i;
  uint16_t crc = 0xffff;
  uint8_t *p = vw_tx_buf + VW_HEADER_LEN; // start of the message area
  uint8_t count = len + 3; // Added byte count and FCS to get total number of bytes

  // Encode the message length
  crc = _crc_ccitt_update(crc, count);
  *p++ = symbols[count >> 4];
  *p++ = symbols[count & 0xf];

  // Encode the message into 6 bit symbols. Each byte is converted into
  // 2 6-bit symbols, high nybble first, low nybble second
  for (i = 0; i < len; i++)
  {
    crc = _crc_ccitt_update(crc, buf[i]);
    *p++ = symbols[buf[i] >> 4];
    *p++ = symbols[buf[i] & 0xf];
  }

  // Append the fcs, 16 bits before encoding (4 6-bit symbols after encoding)
  // Caution: VW expects the _ones_complement_ of the CCITT CRC-16 as the FCS
  // VW sends FCS as low byte then hi byte
  crc = ~crc;
  *p++ = symbols[(crc >> 4)  & 0xf];
  *p++ = symbols[crc & 0xf];
  *p++ = symbols[(crc >> 12) & 0xf];
  *p++ = symbols[(crc >> 8)  & 0xf];

  tx_send();
}

В данный момент программа работает, но есть несколько вопросов.

1. Почему в обработчике прерывания выполнить такой код? Приходиться выставлять флаг и через него в основном цикле через проверку выполнять этот код.

ISR(INT0_vect)
{
      digitalWrite(Led, HIGH);
      digitalWrite(RF_vcc, HIGH);
      _delay_ms(1);
      RF_send((uint8_t *)msg, strlen(msg));
      _delay_ms(1);
      digitalWrite(RF_vcc, LOW);
      while (digitalRead(Input) == LOW);
      digitalWrite(Led, LOW);
}

2.Каким оброзом заставить процессор уходить в сон для снижения энергопотреления? Питаться устройство будет от батарейки.

3.Почему используюя attachInterrupt в обработчике не устанавливаеться флаг или теряеться после завершения?

pinMode(0,INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(0), blink, CHANGE);

void blink(){
    flag = true;
}

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

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

1. Цикл while (насколько я понимаю, ожидание готовности устройства),

2. Вызовы delay().

3. Вызов RF_send (насколько я понимаю, команда на передачу медленному устройству).

4. Заменить вызовы digitalWrite на прямую работу с портами (впрочем, если Вы выполните предыдущие 3 пункта, их просто можно убрать).

 

По 3 вопросу: вероятно, Вы забыли описать flag как volatile.

anarch
Offline
Зарегистрирован: 10.09.2017

Описан как volatile.

Заменить digitalWrite можно, но не уверен что пренисет результата.

Вот на этой функци и происходит зависание. Зажигаеться светодиод, подаеться питание на пин и все дальше выполнение программы завершаеться. До digitalWrite(Led, LOW); программа не доходит.

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

В обработчике устанавливаю флаг.

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

anarch пишет:

вынеся все из обработчика программа начала работать.

 

Я за нее рад.

anarch
Offline
Зарегистрирован: 10.09.2017

Понял свою ошибку по поводу прерывания. Но осталься вопрос по ардуиновскому attachInterrupt упорно не хочет работать.

Ни как не мого заставить уснуть камень. Попробовал напрямую через регистры, ток не падает. Что я делаю не так.


  MCUCR |= (0 << SM0) | (1 << SM1);
  MCUCR |= (1 << SE);

  sei();        
  while (1)
  {
    if (alarm_flag == true) {
      digitalWrite(Led, HIGH);
      digitalWrite(RF_vcc, HIGH);
      _delay_ms(1);
      RF_send((uint8_t *)msg, strlen(msg));
      _delay_ms(1);
      digitalWrite(RF_vcc, LOW);
      while (digitalRead(Input) == LOW);
      digitalWrite(Led, LOW);
      alarm_flag = false;
    }
    asm ("sleep");
  }
 

 

anarch
Offline
Зарегистрирован: 10.09.2017
ADCSRA &= ~(1 << ADEN);
  MCUCR |= (1 << SM1);
  MCUCR &= ~(1 << SM0);
  MCUCR |= (1 << SE);

  // Разрешаем прерывания глобально
  while (1)
  {

    if (flag == true) {
      digitalWrite(Led, HIGH);
      digitalWrite(RF_vcc, HIGH);
      _delay_ms(1);
      RF_send((uint8_t *)msg, strlen(msg));
      _delay_ms(1);
      digitalWrite(RF_vcc, LOW);
      while (digitalRead(Input) == LOW);
      digitalWrite(Led, LOW);
      flag = false;
      asm("sleep");
    }


  }

Вот таким образом уходит в сон но просыпаться ни в какую :(