Обсуждаем контроллеры компании Atmel.
Ответить

Параллельные задачи в ATmega8

Ср авг 21, 2019 08:52:13

Здравствуйте, уважаемые форумчане.
Прошу прощения, если эта тема уже рассматривалась и я просто не нашел ее на форуме (если это так, скиньте ссылку).

Задача: реализовать на ATmega8 одновременную работу диода (мигает с частотой 100 Гц) и отправки пакета сигналов по NEC-протоколу раз в 1 секунду. Запрещено использовать прерывания.

Вопрос: как?

Комментарий к задаче: я понимаю, что настоящего параллельного выполнения на ATmega8 не добиться, можно лишь имитировать параллелизм при помощи быстрого опроса флагов. В том-то и проблема, что совсем не получается придумать код для этого опроса.

Комментарий к работе уже готовой программы: пакет данных по NEC-протоколу передается в отдельной функции. Временные промежутки (560 мкс, 1,12 мкс, 2,25 мкс, 9 мс, 4,5 мс, 1 с) отсчитываются при помощи счетчика флага прерывания по совпадению (OCF2): флаг установился, счетчик увеличился на 1, флаг сбросился и так по циклу. Диодом мигать нужно тоже при помощи этого флага.

Код работающей программы:
Код:
#define F_CPU 2000000UL

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

#define PND4            (1<<PD4)
#define PND3            (1<<PD3)
#define PND2            (1<<PD2)
#define PND1            (1<<PD1)
#define PND0            (1<<PD0)

#define OUT            PB1               //порт вывода данных
#define ERROR                     111               //код ошибки
#define LED                 PB0               //порт, отвечающий за мигание диодом

#define but_0_50_4_9()      !(PIND & PND4)      //кнопка, отвечающая за числа 0, 4, 9, 50
#define but_10_60_3_8()    !(PIND & PND3)      //кнопка, отвечающая за числа 3, 8, 10, 60
#define but_20_70_2_7()    !(PIND & PND2)      //кнопка, отвечающая за числа 2, 7, 20, 70
#define but_30_80_1_6()    !(PIND & PND1)      //кнопка, отвечающая за числа 1, 6, 30, 90
#define but_40_90_0_5()    !(PIND & PND0)      //кнопка, отвечающая за числа 0, 5, 40, 90

#define but_from_0_to_40   0x20               //настраиваем на выход кнопку, покдлюченную к PC5
#define but_from_50_to_90 0x10               //настраиваем на выход кнопку, покдлюченную к PC4
#define but_from_5_to_9     0x08               //настраиваем на выход кнопку, покдлюченную к PC3
#define but_from_0_to_4     0x04               //настраиваем на выход кнопку, покдлюченную к PC2

uint8_t flag = 0;

void timer1_init(void)                          //таймер, настраивающий несущую 36 кГц
{
   TCCR1B |= (1 << WGM12)|(1 << CS10);
   TCCR1A |= (1 << COM1A0);
   OCR1A   = 27;
   TCNT1   = 0;
}

void timer2_init(void)                          //таймер, отсчитывающий отрезки по 560 мкс
{
   TCCR2 = (1 << CS21);
   OCR2  = 140;                           
   TCNT2 = 0;
}

void port_init(void)            //инициализация портов для опроса кнопок и вывода сигнала
{
   DDRB   |= (1 << OUT)|(1 << LED);
   PORTB  |= (1 << OUT);
   DDRD    = 0b00000000;
   PORTD   = 0b00011111;
}

void timer(void)               //счетчик, отсчитывающий отрезки по 560 мкс
{
   if (TIFR & (1 << OCF2))
   {
      TIFR |= (1 << OCF2);
      TCNT2 = 0;
      if (flag) flag--;
   }
}

void on(void)               //функция, выдающая импульсы на выходе (формирует стартовый сигнал, сигнал единицы и сигнал нуля)
{
   TCCR1A |= (1 << COM1A0);
   TCCR1A &= ~(1 << COM1A1);
}

void off(void)               //функция, выдающая паузу на выходе (формирует стартовый сигнал, сигнал единицы и сигнал нуля)
{
   TCCR1A &= ~(1 << COM1A0);
   TCCR1A |= (1 << COM1A1);
}

void start_func(void)            //формирование стартовой функции
{
   flag = 17;
   on();
   while (flag) timer();
   
   flag = 8;
   off();
   while (flag) timer();
}

void one_func(void)            //формирование сигнала единицы
{
   flag = 1;
   on();
   while (flag) timer();
   
   flag = 3;
   off();
   while (flag) timer();
}

void null_func(void)            //формирование сигнала нуля
{
   flag = 1;
   on();
   while (flag) timer();
   
   flag = 1;
   off();
   while (flag) timer();
}

void signal_out(char command)      //вывод сигнала
{
   start_func();                                //отправляем стартовый сигнал
   for(char i=0; i<8; i++)                    //посылаем отдельно каждый бит
   {
      timer();
      if (command & (1<<i)) one_func();
      else                  null_func();    //если i-ый бит равен 1, отправляем 1, если 0, отправляем 0
   }
      
   for(char i=0; i<8; i++)                   //посылаем отдельно каждый бит
   {
      timer();
      if (command & (1<<i)) null_func();
      else                  one_func();   //если i-ый бит равен 1, отправляем 1, если 0, отправляем 0
   }
   one_func();
}

uint16_t button_handler (void)      //обработка нажатых кнопок
{
   uint16_t command1 = ERROR;         //значение кнопок, отвечающих за десятки, в состоянии по умолчанию
   uint16_t command2 = ERROR;              //значение кнопок, отвечающих за единицы, в состоянии по умолчанию
   uint16_t command  = 0;
   
   DDRC = but_from_0_to_40;
   _delay_us(1.1);
   if (but_0_50_4_9())   command1=0;
   if (but_10_60_3_8())   command1=10;
   if (but_20_70_2_7())   command1=20;
   if (but_30_80_1_6())   command1=30;
   if (but_40_90_0_5())   command1=40;
   
   DDRC = but_from_50_to_90;
   _delay_us(1.1);
   if (but_0_50_4_9())   command1=50;
   if (but_10_60_3_8())   command1=60;
   if (but_20_70_2_7())   command1=70;
   if (but_30_80_1_6())   command1=80;
   if (but_40_90_0_5())   command1=90;
   
   
   DDRC = but_from_5_to_9;
   _delay_us(1.1);
   if (but_0_50_4_9())   command2=9;
   if (but_10_60_3_8())   command2=8;
   if (but_20_70_2_7())   command2=7;
   if (but_30_80_1_6())   command2=6;
   if (but_40_90_0_5())   command2=5;
      
   DDRC = but_from_0_to_4;
   _delay_us(1.1);
   if (but_0_50_4_9())   command2=4;
   if (but_10_60_3_8())   command2=3;
   if (but_20_70_2_7())   command2=2;
   if (but_30_80_1_6())   command2=1;
   if (but_40_90_0_5())   command2=0;
   
   command=command1+command2;      //вычисление номера этажа
   if (command >= 100)   command=ERROR;   //условие выдачи ошибки
   
   return command;
}

int main(void)
{
   port_init();
   timer1_init();
   timer2_init();
   sei();
    while (1)
   {   
      signal_out(button_handler());      
   }
}

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 09:18:43

и чо? что это вообще должно означать?
имхо, все это бред.
для IR дистанционного управления допускаются достаточно большие отступления в длительности посылок (точно не скажу, но процентов до 5-10, пожалуй), что позволяет использовать прерывания. и в итоге задача из проблемы превращается в пшик.

Добавлено after 46 seconds:
я уж молчу про отправку битов "вручную", а не циклом по команде... это за гранью разумного, имхо.

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 09:39:09

это за гранью разумного, имхо.
Изобретения собственных велосипедов неизбежны, как и падения бутербродов маслом вниз :twisted:

dzzzzzzzИзображение
RTFM

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 10:06:07

и чо? что это вообще должно означать?
имхо, все это бред.


согласитесь, вопрос был немного другим: как реализовать задачу параллельной работы (псевдопараллельной) в сложившихся условиях без использования прерывания

я учусь в освоении AVR, не все идеально, но я и не претендую на звание "гуру м/к"

если ваших способностей не хватает, чтобы помочь в решении задачи, так может и не стоит стучать по клавиатуре и набирать такие бессмысленные сообщения?

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 10:23:05

dzzzzzzz писал(а):вопрос был немного другим
вопрос был другим, это факт. но от того, какой вопрос вы ставите, зависит результат. если бы люди хотели достать Луну с неба для изучения, они до сих пор бы не знали о том, что она летает вокруг Земли. люди поставили другие вопросы - и нашли решения без необходимости доставать Луну.

советую и вам вместо взращивания гонора сосредоточиться на правильном анализе проблемы и поиске наиболее простого её решения.

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 10:23:48

Есть такая штука - ДИСПЕТЧЕР ЗАДАЧ.
:wink:
Только в АВРке не использовать аппаратную начинку
для создания псевдопараллельности в простейших проектах....
Ежли только это не для учебного изврата...
:dont_know:

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 10:37:44

советую и вам вместо взращивания гонора сосредоточиться на правильном анализе проблемы и поиске наиболее простого её решения.


советую вам подбирать выражения при общении с людьми, даже если это происходит на форуме
стандартные правила приличия никто не отменял

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 11:17:56

IF (обоснованно объяснишь необходимость отказа от прерываний) {встану; буду аплодировать; помогу сделать;} else {делай по уму - на прерываниях;};

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 11:46:40

dzzzzzzz, прерывания в данном случае нужны, а вот обработчик прерывания может быть примитивен. Мультизадачность реализуется тупейшим способом. Выполняется остановка процессора (команда SLEEP), а после нее проверяется, по какому прерыванию проснулись. В зависимости от этого вызывается соответствующая функция, после нее проверяем наличии необработанных событий (произошедших за это время прерываний). Если таковые есть - команда SLEEP пропускается. Если нет - засыпаем снова.
В более сложном случае реализуем кольцевой буфер, сообщения в который добавляются из обработчиков прерываний, а считываются и удаляются сообщения из него в том же бесконечном цикле с SLEEP.
Я, обычно, оперирую сообщениями длиной от 1 до 4 байт. Длина определяется старшими двумя битами кода сообщения в первом байте. Младшие шесть бит рассматриваются как индекс в массиве указателей на функцию обработчика события. Остальные байты, если они есть, передаются вызываемой функции через параметры (uint8_t, uint16_t или uint16_t плюс uint8_t)

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 12:13:49

2 ПростоНуб :facepalm: :facepalm: :facepalm:

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 12:31:45

ARV, а каков Ваш метод реализации мультизадачности на восьмибитных МК? Реализовывать полноценную RTOS, захламляя стек данными прерванных задач и обеспечивая реентерабельность всех функций? Или Ваше решение оффтопик - на вопрос о реализации мультизадачности ответить, что она не нужна ТС?

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 12:36:08

да тот самый, который всегда и был: фоновые задачи по прерываниям, остальное тупо в главном цикле.
сомневаюсь, что до уровня реализации TCP/IP своими силами или иных сетевых протоколов, требуются на самом деле "мультизадачные" методы из "большого" программирования.

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 12:44:59

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

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 13:07:09

ARV, сложность реализации такая же, а вот минусов больше. В моем случае обработчик прерывания простейший. Например, получив прерывание по порту он формирует сообщение состоящее только из содержимого порта, вызвавшиго прерывание и таймстемпа с таймера, определяющего время возникновения этого прерывания. Вам же приходится решать проблему вложенных прерываний, поедающих стек, которая меня не беспокоит из-за малого времени на обработку прерываний. Или же запрещать прерывания, теряя точность измерения промежутков времени и понижая исправляющую способность при приеме сигналов.

Например, мой подход для HC-SR04 реально обеспечил точность даже выше заявленной - получилось +-5мм на расстояниях до 1 метра. А версия сына в основном цикле из-за возникающих длинных прерываний гуляла на порядок больше - +-50мм.

Добавлено after 7 minutes 53 seconds:
BOB51, проблема та же, что и в C - расход стека на сохранение контекста прерванных задач. Не забывайте, что так как я фиксирую в сообщениях время возникновения события, то даже если очередной обработчик длился более, чем несколько событий от того же IR приемника, я ничего не потеряю. Просто декодирую посылку чуть позже - таймстэмпы то есть )

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 13:07:27

я поздравляю вас, что вы сумели превзойти своего сына в программировании :)
но я всегда сам придерживался и другим рекомендовал придерживаться принципа "чем проще, тем лучше". задача ТС - мигание светодиода 100 Гц - это период 5 мс. Протокол NEC не содержит интервалов длиннее этих 5 мс (если не считать стартовый импульс 9 мс).
в итоге передачу NEC-кода можно делать в главном цикле даже при помощи _delay_us, а в это время по прерыванию таймера мигать светодиодом. будет ли ваш вариант проще? сомневаюсь.
если паранойя замучает, можно в главном цикле ждать, пока таймер "отмигнет", чтобы посылать очередной бит NEC-команды строго тогда, когда он не будет прерван обработчиком - и все равно это будет крайне просто.

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 13:18:03

ARV, Ваш вариант - отказаться от мультизадачности, то бишь оффтопик )

Ну и Ваш вариант не универсален. Например, на той же ATtiny13A только один таймер и именно его нужно будет использовать для модуляции IR. Для 100Гц уже таймера не останется )

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 13:35:06

мультизадачность - это "одновременное" исполнение нескольких задач. поскольку в моем примере одна задача - мигание выполняется одновременно со второй - отправкой кода, все строго в рамках поставленной задачи. только с прерываниями.
вероятно ТС хотел кооперативную мультизадачность :) но я ему сразу сказал - это бред.
ПростоНуб писал(а):Ну и Ваш вариант не универсален. Например, на той же ATtiny13A только один таймер и именно его нужно будет использовать для модуляции IR. Для 100Гц уже таймера не останется
мда... это вам кто-то подсказал, или вы сами додумались? ;) вот здесь https://simple-devices.ru/prj/9-electr/ ... te-control есть исходник передатчика RC5 - там немного другие принципы, но модуляция и т.п. все то же самое. и как раз attiny13 :))) и, вы не поверите! - там IR-посылка формируется вместе с модуляцией на тупых _delay_us! :))) так шта...

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 13:42:12

ARV, Вы забыли о мигании в 100 Гц. Когда задержки в 18мкс формируются программно, прерывание от таймера раз в 10мс приведет к сбою посылки модулируемого сигнала. А вся посылка длится существенно дольше 10мс.
Иными словами, приведенный Вами в качестве примера код, совершенно не работоспособен при разрешенных прерываниях )

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 13:59:23

ПростоНуб писал(а):приведенный Вами в качестве примера код, совершенно не работоспособен при разрешенных прерываниях
хотите поспорить? ;) я могу доказать.
Код:
ISR(TIMER0_OVF_vect){
   PINB |= 1;
}
как по-вашему, сколько тактов займет обработчик этого прерывания для мигания светодиодом? не трудитесь, вот листинг:
Код:
ISR(TIMER0_OVF_vect){
   PINB |= 1;
  32:   b0 9a          sbi   0x16, 0   ; 22
}
  34:   18 95          reti
то есть сам обработчик 2 такта, плюс 4 на вход-выход, итого 6 тактов. при тактировании от 8 МГц это будет аж 750 наносекунд. вы на самом деле считаете, что при такой погрешности IR-посылки она не будет корректно принята приемником? ;)

Re: Параллельные задачи в ATmega8

Ср авг 21, 2019 14:10:29

А что это за "мигание" в 100ГЦ???
Чей глаз его узрит-то?
:)))
Там и просто включить можно, и "мигающий" поставить...
Нафига мозг по дурному морочить?
:wink:
Ответить