Освоение Атмеги или кипит наш разум

Pavel_E

Тов. Штопор, мерси за алгоритм! Вроде вкурил что и как, пора в железо воплощать.

sht0p0r:

1 разрешить прерывание внутри преывания результаттом может стать stack overflow

В одной умной книжке прочитал, что, это типа невозможно и прерывания выполняются по очереди.

В остальном - всем спасибо за активность, впереди выходные, как раз поэксперементирую. А пока пойду вторую атмегу куплю на всяк случай.

Я еще подумал, может плюнуть на это дело и тупо мерить усредненное конденсатором напряжение выхода приемника? Но эта мысль вредная, пока отгоняю.

sht0p0r

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

последняя мысль очень вредная. имхо
книжка товарища Шпака “порограмирование на С авр и пик” как то так называется. вам поможет освоить это б…ство.

valera_o

если будете покупать, купите мегу88, для внешних прерываний можно использовать практически все ноги портов, памяти столько же, опять же мега8 снята с производства.
Книга “Микроконтроллеры AVR семейства Mega” А.В.Евстифеев 2007
тоже хороша для освоения устройсва контроллеров.

Boroda
Pavel_E:

или Вариант 2:
Использовать ICP1. Установить прерывание по изменению состояния ноги и при срабатывании оного читать из регистра ICR1 сколько там натикало. И никаких таймеров запускать не надо.

Надо. В регистр ICP записывается значение из РАБОТАЮЩЕГО tcnt1 именно в
момент прихода сигнала на ногу. Таймер при этом не останавливается. Когда ICP будет прочитан в прерывании или основном теле, вам не надо будет задумываться о том сколько тиков прошло с момента прихода фронта до момента чтения регистра таймера.

PigTail
valera_o:

… опять же мега8 снята с производства.

И где это такие сведения? 😉 www.atmel.com/dyn/products/devices.asp?Status=Matu…® 8-Bit RISC
Если топикстартеру ща еще подсунуть 88 с разницей в регистрах после 8, то он точно закипит 😃

Pavel_E

м-да…
😵
надо выспаться. может уложится по полочкам и будет просто 😃

Слушайте, как вы женам объясняете, чем занимаетесь, а?

Aleksey_Gorelikov

ну объясните ей, что вот такая мелкая микросхемка - ни что иное, как полноценный компутер, что такая штука может являться брелком к авто сигнализации, самой сигнализацией, что авто, что домашней, “мозгами” стиральной машины, пультом от телека, часами с будильником и т.д. и т.п. Что все зависит от того, какую программу для нее написать. Глядишь, попросит мк с акселерометром в утюг встроить, чтоб отключался сам, если его на любимых трусах забыли и дырку не прожег. 😃

Pavel_E

Новости с фронтов.
Убит вечер пятницы, вся суббота и воскресенье. Поставленная задача решена на 70%. Умею управлять сервой и выводить произвольную информацию на четырехразрядный светодиодный индикатор. Осталось научиться расшифровывать сигнал с приемника, но вроде уже понятно как.

Не хватило ножек. Мигрировал на 40ногую атмегу16. Зверь.

Заодно вопрос-восклицание. 8-и битный таймер не может достойно управлять сервой в режиме PWM. Расчет простой - максимум на период 20мс (50Гц) приходится 256 отсчетов таймера. Т.е. 12,8 отсчета на одну миллисекунду. Нам нужен регулируемый импульс длительностью в диапазоне от 0,8 до 2,2 мс. Т.е. в лучшем случае - 18 отсчетов. Если рабочий угол сервы 180градусов, то получается можем задавать не точнее 10 градусов! Короче, в сад или в 16 бит. А 16 бит жалко на такое дело транжирить…

msv

Для декодирования по алгоритму sht0p0r можно и 8-разрядный таймер программно наростить и брать с него timestamp. А 16-разрядный использовать для ШИМА.
А что собственно за девайс изобретаете?
Если это декодер, никак не врублюсь как аппаратным PWM можно раздать импульсы на 8 каналов…

Pavel_E
sht0p0r:

я немного не так сделал
1 прерыванийе ставим на фронт
ждем
обработчик
1.1 берем timestamp с таймера 1 (он 16 бит)
1.2 проверяем что за прерывание пришло фронт или спад
1.3 пишем timestamp в timestamp_R или в timestamp_F (соответственно)
1.4 переключаем прерывание на определение фронта или спада (в зависсимости от того что пришло)
1.5 если пришел спад то поднимаем флаг(готово время для рассчета ширины импульса)
2 в основной программе
while(1){
2.1 если поднят флаг то рассчитываем ширину импулса (с учетом длинны счета таймера и переполнения, если таковое было между фронтом и спадом )
все можно наслаждаться.
2.2 если поднят флагг…
2.3 если поднят флаггг…
2.4 если поднят флаггггг…
2.n придушить бешеную собаку!!!
}

А вот вопрос. Под “поднятием флага” понимается просто запись значения в какую-нибудь переменную для обработки программой? А как оно будет работать, если программа сложная и быстродействия не хватит, чтобы успевать между импульсами (20мс)?. Т.е. может быть, что флаг поднят, но значение по фронту уже перезаписалось другим импульсом, не успев быть учтенным в программе. И тут она начнет его учитывать…

msv:

Для декодирования по алгоритму sht0p0r можно и 8-разрядный таймер программно наростить и брать с него timestamp. А 16-разрядный использовать для ШИМА.

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

Альтернативная мысль: “наростить программно” ШИМ на 8-битном таймере. Т.е. в цикл таймера загнать время импульса, а скважность считать по количеству циклов, используя для хранения количества переменные в программе. По переполнению таймера проверять сколько циклов натикало и если хватит, обнуляться и пускать сигнал заново.

А можно и не парить мозг, а оставить этот режим для того, для чего он и предназначен. Т.е. генерировать ШИМ для управления, например, коллекторным двигателем.

msv:

А что собственно за девайс изобретаете?

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

Еще хочу сделать вольтметр и амперметр на базе атмеги с цифровой индикацией.

Еще хочу брать NMEA с GPS, углы с гироскопов, высоту с бародатчиков и обороты с винта. И фигачить все это по радиоканалу в цифровом виде на землю. На земле принимать и выдавать на ноутбук в красивой графической форме в виде авиационных приборов.

И чтобы если стемнело, то включалась поисковая фотовспышка хотя бы раз в минуту.

Еще много чего хочу 😃 Вопрос, в какой момент это надоест или когда снова начнется работа…

sht0p0r

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

Pavel_E:

А вот вопрос. Под “поднятием флага”…

да

Pavel_E:

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

просто не обработаете управляющий импульс.
добавте проверку флага в прерывании int(0,1) если не сброшен то return из прерывания.
ног там более чем достаточно что-то вы включаете не рационально. повесте вашу схемку, думаю ее можно опримизировать.
что мешает гнерировать 16 битным таймером pwm и паралельно брать со счетчика значения?
что мешает програмно, по прерыванию генерировать pwm на нескольких ногах процессора?
включаете прерывание по переполнению таймера, в преравании поднимаете флаг, в основной программе генерируете импульс(ы) задонной длительности с периудом переполнений таймера.во время генерации импульса можно с пользой для дела использовать время процессора (например вывести на индикатор, что нибудь).
активней пользуйтесь аппаратной частью процессора тогда и программа будет обрабатываться быстро.

все уже сделано до вас 😃 www.dmd.es/osd.htm

msv

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

что мешает програмно, по прерыванию генерировать pwm на нескольких ногах процессора?
включаете прерывание по переполнению таймера, в преравании поднимаете флаг, в основной программе генерируете импульс(ы) задонной длительности с периудом переполнений таймера.во время генерации импульса можно с пользой для дела использовать время процессора

Мне показалось, что Pavel_E делает декодер, и поэтому не понял, как это можно решить (по многочисленным советам) именно на чисто аппаратном pwm.
Хотя с вышеописанным алгоритмом тоже непонятка. Ведь есть 8-каналов (8 ног) с разным коэфф. заполнения и одинаковым периодом. Время от установки флагов до их обработки непредсказуемое, а значит качественный шим уже не получится.
Вообщем-то у меня вопросов то нет, что-нибудь наваяю, но вероятно и программные задержки ( с запрещенными в эти моменты прерываниями) использовать прийдется.
А в принципе (для абстрактной задачи) с советами по возможности для решения реал-тайм задач использовать прерывания и аппаратные возможности, а в фоновом цикле выполнять фоновые задачи, конечно трудно не согласиться… 😃

sht0p0r

например так :
по флагу поднимаемому таймером 50 раз в секунду
int width[8]; значения pwm для 8 каналов.
в основном теле пишем
{
start=TCNT1;
PORTD=0xFF;
{сюда напихать чегонибудь периодического на ~700us}
while((TCNT1+start)<MaxPWM)
{
if ((width[0]+start)<TCNT1) PORTD_Bit0=0; // это грубо на самом деле чуть сложнее, но не на много
if ((width[1]+start)<TCNT1) PORTD_Bit1=0;

if ((width[7]+start)<TCNT1) PORTD_Bit7=0;
// или вот гдето так
for (i =0 ;i<8;i++) if ((width[i]+start)<TCNT1) PORTD&(~(1<<i));
}
}

Pavel_E

Параллельно вопрос. Классический сигнал цифровых машинок - 50Гц, ширина импульса 1…2мс. Как воспримут машинки сигнал с частотой 45…55Гц? Но той же длительностью импульса?

sht0p0r

нормально воспримут. у меня с приемника spektrum AR7000 примерно 47 идет да еще и частота плавает… полный ужас короче, а машинки жрут и ничего 😃

токо 50 это для аналоговых, цифровые и 100 слопают.
импульс 1540 ± 760( если память не изменяет).

msv

Код нормальный, хотя можно оптимизировать. А если учитывать переполнения, то будет и более-менее рабочий. 😃 Правда он вроде звучит как ответ на мою реплику “…не понял, как это можно решить именно на чисто аппаратном pwm”, а отношение ни к аппаратному pwm, ни даже к прерываниям не имеет…

Pavel_E

Помаленьку продвигаемся. Вчера заставил 8-битный таймер генерировать 54Гц и управлять машинкой регулируемыми импульсами длиной от 0 до 2048мкс с шагом 8мкс. Это повысило реальную точность позиционирования реальной машинки в 12 раз по сравнению с режимом PWM. Уже достаточно, но с минимальными усложнениями текста точность еще можно повысить раза в 1,5 а частоту подобрать ровно 50Гц.

Алогритм следующий:
Нам нужно отмерить 2 интервала - один регулируемый (1…2мс, лучше 1,8…2,2мс) и один можно нерегулируемый - 20мс +/-. Я сделал это так:
Используется 8-ми битный таймер и 2 прерывания - по срабатыванию компаратора (TIM2_COMP) и по переполнению (TIM2_OVF). При этом, для повышения точности установки первого интервала (время импульса) нужно максимальную его продолжительность подвести как можно ближе к максимальному значению таймера (255).
Первый интервал задается включением таймера с режимом PWM и отсекается прерыванием по окончанию импульса заданной продолжительности. Продолжительность задается значением регистра OCR2. По срабатыванию прерывания режим PWM отключается.
Второй интервал задается тупо количеством циклов таймера. Каждый цикл отнимает единичку от переменной и когда она становится “0”, снова включает режим PWM и снова накидывает количество циклов до нужного значения. Это значение считаем арифметически и подбираем под нужную частоту импульсов.

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

Выкладываю листинг. Прошу покритиковать.

unsigned char tim2_countdown=8; //задаем количество циклов таймера для установки частоты импульсов.
// 1000000Гц/8/256/50Гц=9,76… Ближайшее: 10 - один на первый цикл, остается 9.

interrupt [TIM2_COMP] void timer2_compare(void) //прерывание по окончанию импульса (режим PWM)
{
TCCR2=0b01001010; // отключаем режим PWM
PORTC.1=0; // выключаем стороннюю ногу.
}

interrupt [TIM2_OVF] void timer2_ovf(void) //прерывание по переполнению timer2
{
if (tim2_countdown!=0) //смотрим сколько циклов осталось до окончания периода
tim2_countdown=tim2_countdown-1;
else
{
TCCR2=0b01101010; //снова включили режим PWM
OCR2=stop_signal; //установили отсечку импульса PWM
tim2_countdown=8; //заново установили количество циклов
PORTC.1=1; // включили стороннюю ногу. На ней начался сигнал.
}
}

void main(void)
{
int stop_signal=188; //переменная длинны импульса, начальное значение соотв. 1500мкс

PORTC=0x00; //включаем порт C на выход, сигнал на все ноги
DDRC=0xFF;

TCCR2=0b01101010; // включить таймер, режим PWM
TCNT2=0x00; //начальное значение таймера
OCR2=stop_signal; //начальное значение длины импульса
TIMSK=0b11000000; // включены прерывания по окончанию импульса PWM и переполнению таймера

#asm(“sei”) //какой-то рудимент, оно тут нужно?

while (1)
{
for(stop_signal=110;stop_signal<=255;stop_signal=stop_signal+1) //меняем длину импульса от 888мкс ((110+1)*8) до 2048мкс ((255+1*8)
{
led(stop_signal); //выводим на светодиодный индикатор значение
delay_ms(50); // чтобы машинка успевала отрабатывать
}
}
}

Вешаем машинку на PC0 - шевелится почти что в полном диапазоне. Задача управления машинкой решена.

sht0p0r

регистр TCNT доступен на чтение и запись, считать до 255 не обязательно.

TCCR2|=(1<<COM20); // это установит в 1 COM20 знать конкретный бит необязательно они уже проименованы.

TCCR2&=(~(1<<COM20)) // это сбросит в 0 COM20 не затрагивая остальные биты регистра
ненадо заботится о том чтобы сохранить/знать текущее состояние
когда проверяешь “0” то достаточно if (! tim2_countdown)

Pavel_E

Спасибо за коммент, учту. Эти вещи мне в принципе знакомы, но специально пока делаю текст наиболее наглядным. Чтобы мозг не отвлекался на расшифровку собственного кода.

sht0p0r:

регистр TCNT доступен на чтение и запись, считать до 255 не обязательно.

Это я в курсе, но в моем алгоритме писать в TCNT2 вроде не зачем. Ну есть один момент - для того, чтобы точно вывести частоту в 50Гц можно один цикл сократить, задав ему начало счтета не с 0, а с какой-то величины. Но это уже на этапе причесывания кода.

Тут есть один косяк в программе, который я вчера понять не успел. А именно, для вывода сигнала я вынуждено использую ногу PC0 (регистр PORTC.0). Хотя работа одного цикла таймера в PWM режиме должна автоматом поднимать и опускать ногу OC2(PD7). А она это делает только один раз. Или где-то ошибка, или PWM не желает запускаться. Хотя компаратор запускается и работает… Как разберусь, можно будет еще 2 операции убрать из прерываний - установка и уборка состояня ноги PC0.

sht0p0r
Pavel_E:

#asm(“sei”) //какой-то рудимент, оно тут нужно?

это асемблерная вставка в с достаточно sei();// разрешение прераваний
cli(); соответственно запрет
DDR регистр на выход
правильно поставить WGM
больше ничего ненадо.

Pavel_E

Всё, поставленные задачи выполнены и перевыполнены. Всем участникам спасибо за помощь в понимании этого безобразия! Особенно тов. Штопору - за деятельное участие и вне рамок данного форума.

Итак, МК умеет:

  • брать сигнал от приемника по двум каналам;
  • расшифровывать и переводить в значение длительности импульсов в мкс;
  • обрабатывать (в моем случае - нормировать в диапазон 1000…2000мкс);
  • усреднять значение по одному из каналов и выводить на четырехразрядный светодиодный цифровой индикатор;
  • генерировать сигнал на двух каналах.

Как обещано, выкладываю исходник на С для конструктивной критики.
Тов. MSV, будешь код контроля акков прикручивать?

servo_control.rar