LINUX.ORG.RU

Поморгать светодиодом Arduino с прерываниями.

 , ,


0

1

Что-то у меня ум за разум заходит. Не удаётся написать тривиальную программу для Arduino UNO. Хочу чтобы светодиод (который на ноге digital 13) моргал.

Алгоритм такой. Есть таймер, который каждые N миллисекунд вызывает прерывание, где добавляет 1 к переменной-счётчику. В основном цикле крутится проверка значения этой переменной. Если значение достигло некоторой величины, то меняем уровень сигнала на digital 13, и обнуляем переменную-счётчик. Вроде бы всё просто, но не работает. ЧЯДНТ?

Да, Arduino, конечно, offtopic на linux'овом сайте. Но надеюсь мне кто-нибудь поможет.

Вот код:

#include <avr/interrupt.h>

int status; // Глобальная переменная в которой лежит состояние светодиода, горит-не_горит.
int tick_counter; // Переменная-счётчик вызовов прерывания.

void init (void) {
  cli(); // Запрещаем все прерывания на время инициализации.

  DDRB |= (1<<PB5);  // Set D13 as output pin.
  //PORTB |= (1<<PB5); // Set D13 level high.

  status = 0;
  tick_counter = 0;

  OCR0A = 65535;
  TIMSK0 |= (1<<TOIE0); // Включаем прерывание по достижению Timer/Counter0 значения OCR0A.
  TCCR0A |= (1<<WGM01) | (1<<WGM00); // Работаем в режиме FastPWM.
  TCCR0B |= (1<<WGM02) | (1<<CS02) | (1<<CS00); // Делитель 1/1024. С таким делителем и OCR0A прерывание будет вызываться примерно каждые 4 мс.

  sei(); // Закончили инициализацию, разрешаем все прерывания.
}

void run() {
  while(1) {

    if (tick_counter >= 5) {
      if (status == 1) {
	PORTB &= ~(1<<PB5);
	status = 0;
      }
      else {
	PORTB |= (1<<PB5);
	status = 1;
      }
      tick_counter = 0;
    }
  }
}

ISR(TIMER0_OVF_vect) {//Обработка прерывания по переполнению Timer/Counter0
  tick_counter++;
}

int main(void) {
  init();
  run();

  return 0;
}

★★★★★

Линукс тут при чём?

anonymous ()

Не всматривался в регистры, т.к. не использую AVR. Вопрос: а не мигает ли оно слишком часто, что ты не замечаешь?

exst ★★★ ()

Работаем в режиме FastPWM

Не мешает ли оно вызову прерывания? Обработчик вообще вызывается?

exst ★★★ ()
Ответ на: комментарий от exst

Вызывается.

Не мешает ли оно вызову прерывания? Обработчик вообще вызывается?

Не мешает, вызывается. Код приведённый ниже вызывает очень частое, но видимое глазом мерцание светодиода. То есть прерывание вызывается, и переменная status вполне доступна из этого прерывания.

Можно ещё проверить поставив в init() зажигание светодиода, а в прерывании его гашение, или наоборот, и тогда светодиод не будет гореть, а если закомментировать гашение светодиода в обработчике прерывания, то будет гореть.

Мне непонятно почему не работает код с tick_counter. Эта переменная объявлена таким же образом и с ней не делается ничего необычного. Ум за разум у меня заходит от того что я писал куда более сложные программы для Arduino, а сейчас не могу помигать светодиодом. То что режим FastPWM в данном случае не играет большого значения, этот ШИМ ни на какой pin не выведен, для этого нужно поднимать ещё кое-какие разряды в соответствующих регистрах, но таймер-счётчик Timer/Counter0 запущен, и прерывание по его переполнению или достижению значения OCR0A включено.

#include <avr/interrupt.h>

int status;
int tick_counter;

void init (void) {
  cli();

  DDRB |= (1<<PB5);  // Set D13 as output pin.
  //PORTB |= (1<<PB5); // Set D13 level high.

  status = 0;
  tick_counter = 0;

  OCR0A = 65535;
  TIMSK0 |= (1<<TOIE0); // Enable interrupt on overflow.
  TCCR0A |= (1<<WGM01) | (1<<WGM00);
  TCCR0B |= (1<<WGM02) | (1<<CS02) | (1<<CS00); //Set prescaler 1/1024.

  sei();
}

void run() {
  while(1) {
  }
}

ISR(TIMER0_OVF_vect) {///<Обработка прерывания по переполнению Timer/Counter0
      if (status == 1) {
	PORTB &= ~(1<<PB5); // Set D13 level low.
	status = 0;
      }
      else {
	PORTB |= (1<<PB5); // Set D13 level high.
	status = 1;
      }
}

int main(void) {
  init();
  run();

  return 0;
}
Camel ★★★★★ ()
Ответ на: комментарий от exst

Не часто.

Нет, не мигает часто. Код выше мигает с периодом где-то 8 мс. tick_counter >= 5 должно заставить мигать реже, но не слишком редко.

Camel ★★★★★ ()

Не вдавался особо, но 5 х 4мс = 20мс, не слишком ли это часто?

anonymous ()

Переменные, к которым идёт обращение из прерывания, должны быть объявлены как volatile, чтобы компилятор не пытался их оптимизировать (он не в курсе, что во время работы run() переменная будет изменяться, считает, что она постоянно равна нулю, и оптимизирует, соответственно).

prischeyadro ★★★☆☆ ()
Ответ на: Вызывается. от Camel
TIMSK0 = _BV(OCIE1A);

ISR(TIMER0_COMPA_vect) {
        PORTB ^= _BV(PB5);
}

Потом доки почитаю, поточнее про PWM расскажу.

beastie ★★★★★ ()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от nanoolinux

Как раз нормальная практика для event-based. CPU ведь надо что-то делать, пока оно interrupt ждёт. (Можно ещё в sleep отправить, но это следующий уровень.)

beastie ★★★★★ ()
Ответ на: комментарий от beastie

Можно ещё в sleep отправить, но это следующий уровень

я как бы об этом и говорил.

CPU ведь надо что-то делать, пока оно interrupt ждёт

я подозреваю, ты совершенно не в теме. всё что надо делать когда ждёшь прерывание - ждать прерывания, внезапно. и делать это лучше в слипмоде, т.к. конкретно атмега потребляет офигенно мало в этом режиме, что существенно экономит батарейку. Время пробуждения 4 такта, или что-то в этом духе, могу ошибаться. Т.е. латентность где-то в приделах 50 наносекунд при 20 МГц кварце.

Там, где батарейки или нужны - атмега тоже не нужен. Так что дизайн с busyloop на атмеге по определению ущербен. Не можешь сделать со слипом - лучше вообще не делай.

nanoolinux ★★★★ ()
Ответ на: комментарий от nanoolinux

Иногда и 50usec слишком много и на батарейку бывает пофиг. Человек только начал. Не злопыхай. Сначала busyloop, потом sleep.

beastie ★★★★★ ()

Что можно почитать на тему Си под ардуино?

Как-то гуглил, так ничего вменяемого не нашел.

unt1tled ★★★★ ()
Ответ на: комментарий от beastie

Не дурной Arduino.

Но это не дурной ardunio, а кошерный avr-libc.

Я вам по секрету скажу, что тоже пишу не на дурном Arduino, а на кошерном C/avr-libc. Для меня Arduino это плата с микроконтроллером ATMega328.

Camel ★★★★★ ()
Ответ на: комментарий от prischeyadro

volatile

Переменные, к которым идёт обращение из прерывания, должны быть объявлены как volatile, чтобы компилятор не пытался их оптимизировать (он не в курсе, что во время работы run() переменная будет изменяться, считает, что она постоянно равна нулю, и оптимизирует, соответственно).

Благодарю. Я знал, что для решения проблемы мне нехватает всего нескольких, но очень важных букв.

Camel ★★★★★ ()
Ответ на: Не дурной Arduino. от Camel

Тогда всё ляпота! Потыкай палочкой мой репозиторий, думаю у меня не самый плохой стиль написания. Там 3 проекта: bootloader-firmware, соответствующий bootloader-client (работает через UART/USB), и сам тестовый проект мигания лампочками (главный файл dmx.c). kernel.c — soft-realtime-preemtive-multitasking-kernel в 200 строк. Думаю разберёшся, если будет интерессно.

beastie ★★★★★ ()
Ответ на: комментарий от nanoolinux

sleep

всё что надо делать когда ждёшь прерывание - ждать прерывания, внезапно. и делать это лучше в слипмоде, т.к. конкретно атмега потребляет офигенно мало в этом режиме, что существенно экономит батарейку. Время пробуждения 4 такта, или что-то в этом духе, могу ошибаться. Т.е. латентность где-то в приделах 50 наносекунд при 20 МГц кварце.

Спасибо за наводку.

Там, где батарейки или нужны - атмега тоже не нужен. Так что дизайн с busyloop на атмеге по определению ущербен. Не можешь сделать со слипом - лучше вообще не делай.

Вы излишне категоричны.

Camel ★★★★★ ()
Ответ на: комментарий от beastie

Заработало.

volatile для глабальных нонсенс, gcc и так глобальные не оптимизирует.

Нонсенс не нонсенс, а с volatile заработало.

Camel ★★★★★ ()
Ответ на: комментарий от prischeyadro

Это похоже, что шутки оптимизации. Ладно, согласен, для ISR не помешает.

beastie ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.