|
|
  |
Временные интервалы |
|
|
|
Jun 24 2010, 22:35
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Цитата(xemul @ Jun 22 2010, 15:18)  Завести таймер на 5 мкс, в его прерывании делать только Код PORTC = tmpC; Flag_5us = 1; В основном цикле Код uint24_t ch1_cnt, ..., ch8_cnt; // uint24_t придумать, для счётчиков при 0.1 Гц и 5 мкс нужен 21 бит // с uint32_t будут лишние считания if(Flag_5us) { if(!--ch1_cnt) { tmpC ^= 1; ch1_cnt = (tmpC & 1)? ch1_high_time: ch1_low_time; } if(!--ch2_cnt) { tmpC ^= 2; ch2_cnt = (tmpC & 2)? ch2_high_time: ch2_low_time; } ... if(!--ch8_cnt) { tmpC ^= 128; ch8_cnt = (tmpC & 128)? ch8_high_time: ch8_low_time; } Flag_5us = 0; } Но в 5 мкс (100 тактов на 20 МГц) if(Flag_5us) {...} всё равно не уложится, так что урезайте осетра по разрешению. Тогда уж лучше весь if(Flag_5us) {...} вставить в прерывание, по крайней мере, флаг не придётся проверять. Но прерывание для данной задачи - зло, поэтому лучше делать без прерываний вообще, не будет потерь времени на сохранение контекста в прерывании. Конструкция if(!--ch1_cnt) ... выглядит компактно, но только на бумаге, здесь 8 раз вычитается то, что в предыдущем решении таймер1 делает аппаратно. По моим грубым прикидкам, если писать на асме, то можно уложиться в максимальный джиттер 8 мкс.
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Jun 25 2010, 05:57
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(singlskv @ Jun 25 2010, 01:04)  Кстати, задачка на самом деле очень интересная, и вполне решаемая... Не забываем что Ton+Toff >= 100 тиков для каждого канал и такта 5мкс, а всего смен значения порта за 100тиков не более 16 Вот и давайте посчитаем: сколько отводиться на смену выходного сигнала? Сто делим на 16 = 6 (четыре такта отдадим на смену Ton/Toff  ). Не все команды выполняются за один такт. Следовательно, нужно определить прошедшее время удержания сигнала, сравнить с некой константой, при необходимости изменить выходной сигнал и проделать некие действия по новому отсчету времени, и всё это улолжить максимум в шесть команд (точнее в несколько команд, которые выполняются за шесть тактов). Сможите? Пусть даже на ассемблере.
|
|
|
|
|
Jun 25 2010, 07:08
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Палыч @ Jun 25 2010, 09:57)  Вот и давайте посчитаем: сколько отводиться на смену выходного сигнала? Сто делим на 16 = 6 (четыре такта отдадим на смену Ton/Toff  ). Не все команды выполняются за один такт. Следовательно, нужно определить прошедшее время удержания сигнала, сравнить с некой константой, при необходимости изменить выходной сигнал и проделать некие действия по новому отсчету времени, и всё это улолжить максимум в шесть команд (точнее в несколько команд, которые выполняются за шесть тактов). Сможите? Пусть даже на ассемблере. Вы чего-то совсем не то насчитали, 16смен значения порта будет в самом худшем случае за 100*5мкс=500мкс (если на всех выходах нужно 2КГц и никакие фронты не совпадают) Цитата(=GM= @ Jun 25 2010, 02:35)  Тогда уж лучше весь if(Flag_5us) {...} вставить в прерывание, по крайней мере, флаг не придётся проверять. Почти так, только не совсем Цитата Но прерывание для данной задачи - зло, поэтому лучше делать без прерываний вообще, не будет потерь времени на сохранение контекста в прерывании. Прерывания позволят освободить основной цикл проги для приема обновленных значений Ton/Toff извне, например по уарту или с кнопочек. Большую часть сохранения контекста можно делать после вывода значения в порт перед вычислениями(это конечно только на АСМ). Видимо Вы не до конца поняли суть предлагаемого алгоритма. За 500мкс сменить значение порта нужно не более 16 раз. Если эти 16смен забуферировать и приделать к ним счетчики сколько времени не менять значение на порту, то у нас будет куча времени на рассчеты следующих значений. Вопрос только в правильной организации рассчетов в прерывании и одновременный вывод из буфера уже сохраненных. Так вот здесь очень красиво можно вписать вложенные прерывания, причем прерывание то будет одно, только оно может вызываться из уже работающего того же самого обработчика.
|
|
|
|
|
Jun 25 2010, 07:18
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(singlskv @ Jun 25 2010, 11:08)  Видимо Вы не до конца поняли суть предлагаемого алгоритма. За 500мкс сменить значение порта нужно не более 16 раз. Вы видимо, тоже не до конца. Эти смены не равномерно распределены по интервалу, а могут вплотную друг за другом идти, либо вообще накладываться. Вот в эти моменты и возникает джиттер. И требование в 5мкс накладывает очень жёсткие требования...
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Jun 25 2010, 07:24
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(MrYuran @ Jun 25 2010, 11:18)  Вы видимо, тоже не до конца. Эти смены не равномерно распределены по интервалу, а могут вплотную друг за другом идти, либо вообще накладываться. Вот в эти моменты и возникает джиттер. И требование в 5мкс накладывает очень жёсткие требования... Как раз для случаев когда они идут с интервалом в 5мкс и вводится буферирование, и вывод в порт при смене значения всегда из буфера в самом начале прерывания, а после этого дальнейший рассчет нового значения на освободившееся место в буфере.
|
|
|
|
|
Jun 25 2010, 09:12
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Цитата(MrYuran @ Jun 25 2010, 05:07)  -В общем, если отбросить чисто академические изыски, не имеющие практического значения, нужно поставить снаружи ЦПЛД за 30р и упаковать туда 8-канальный таймер-счётчик. -Либо применить готовые программируемые интервальные таймеры -Либо заменить контроллер на более подходящий для таких целей На самом деле в CPLD нужно ставить шестнадцать 24-битных счётчиков ПЛЮС 16 регистров хранения длительностей полупериодов ПЛЮС схема управления ПЛЮС интерфейс связи с МК...придётся осетра за 30 рубчиков урезать :-). Ещё вариант - поставить четыре 8-ногие тиньки, у которых есть два аппаратных ШИМА...Но можно и чисто программно, те же 6-ногие АТмега10 подойдут. У меня получается где-то 18 тактов на обработку одного канала, т.е. для софтового генератора джиттер не превосходит 1 мкс/канал.
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Jun 25 2010, 10:07
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(=GM= @ Jun 25 2010, 02:35)  Тогда уж лучше весь if(Flag_5us) {...} вставить в прерывание, по крайней мере, флаг не придётся проверять. Но прерывание для данной задачи - зло, поэтому лучше делать без прерываний вообще, не будет потерь времени на сохранение контекста в прерывании. Я затруднюсь сказать, что лучше, не видя остальной части загадки. Потери на общение с волатильными счетчиками могут оказаться больше и непредсказуемей. Цитата Конструкция if(!--ch1_cnt) ... выглядит компактно, но только на бумаге, здесь 8 раз вычитается то, что в предыдущем решении таймер1 делает аппаратно. Я предложил "рыбу" решения на С - так короче. имхо, исходно было понятно, что отдавать оптимизацию компилятору в этой задаче ... неразумно. Извините, не понял, какое из предыдущих решений Вы имели в виду.
|
|
|
|
|
Jun 25 2010, 12:46
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(=GM= @ Jun 25 2010, 15:30)  Из поста #26.
1) Ну смотрите, объясняю конструкцию if(!--ch1_cnt) на пальцах. Надо загрузить 4-х байтный счётчик из памяти в регистры, вычесть 1 и сохранить обратно в памяти. И так 8 раз! За объяснение спасибо. Я об этом смутно догадывался, Вы подтвердили мои опасения.  В посте #26 в любой ветке выполняется 3 бинарные (!) операции с uint32_t (две из них - с загрузкой и сохранением). И так 8 раз! Изначально (мной) было сказано, что достаточно 3 байтов на счётчик, с соответствующими умолчательными предположениями о реализации и оптимизации. Декремент 3-х (или 4-х) байтового числа с проверкой на 0, имхо, не сложнее if(sysTime>endTime[i]), которая выполняется через вычитание двух uint32_t и проверку знака результата. Цитата 2) В прерывании вы выставляете флаг5мкс, а в фоне ждёте его. Если поместить тело if в прерывание, то ждать флага не надо, наступило прерывание - выполняем тело if. Всяко быстрее будет. Вряд ли - только на сохранении/восстановлении регистровой пары в прерывании будет потеряно столько же. В качестве же бесплатного довеска - некоторое количество volatile uint32_t с сопутствующим оверхедом. Ну и скорость в данном месте как-то сбоку - на джиттер не влияет, а если у контроллера не хватит скорострельности, то абсолютно фиолетово, где не хватит - в прерывании или в фоне.
|
|
|
|
|
Jun 25 2010, 15:56
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Немного повергло в шок, как компилятор WinAVR транслирует вашу конструкцию Код //HEMUL VERSION if(!--endTime0) 19c: 8d 81 ldd r24, Y+5; 0x05 19e: 9e 81 ldd r25, Y+6; 0x06 1a0: af 81 ldd r26, Y+7; 0x07 1a2: b8 85 ldd r27, Y+8; 0x08 1a4: 01 97 sbiw r24, 0x01; 1 1a6: a1 09 sbc r26, r1 1a8: b1 09 sbc r27, r1 1aa: 8d 83 std Y+5, r24; 0x05 1ac: 9e 83 std Y+6, r25; 0x06 1ae: af 83 std Y+7, r26; 0x07 1b0: b8 87 std Y+8, r27; 0x08 1b2: 8d 81 ldd r24, Y+5; 0x05 1b4: 9e 81 ldd r25, Y+6; 0x06 1b6: af 81 ldd r26, Y+7; 0x07 1b8: b8 85 ldd r27, Y+8; 0x08 1ba: 00 97 sbiw r24, 0x00; 0 1bc: a1 05 cpc r26, r1 1be: b1 05 cpc r27, r1 1c0: 81 f4 brne .+32 ; 0x1e2 <main+0x166> { PINC =_BV(0); 1c2: 73 bb out 0x13, r23; 19 endTime0 =(PORTC & _BV(0))? tLow0 : tHigh0; 1c4: a8 9b sbis 0x15, 0; 21 1c6: 05 c0 rjmp .+10 ; 0x1d2 <main+0x156> 1c8: 8d 85 ldd r24, Y+13; 0x0d 1ca: 9e 85 ldd r25, Y+14; 0x0e 1cc: af 85 ldd r26, Y+15; 0x0f 1ce: b8 89 ldd r27, Y+16; 0x10 1d0: 04 c0 rjmp .+8 ; 0x1da <main+0x15e> 1d2: 89 89 ldd r24, Y+17; 0x11 1d4: 9a 89 ldd r25, Y+18; 0x12 1d6: ab 89 ldd r26, Y+19; 0x13 1d8: bc 89 ldd r27, Y+20; 0x14 1da: 8d 83 std Y+5, r24; 0x05 1dc: 9e 83 std Y+6, r25; 0x06 1de: af 83 std Y+7, r26; 0x07 1e0: b8 87 std Y+8, r27; 0x08 } Но по зрелому размышлению понял, что лучше и не сделаешь. Странно, но вот так: if(!endTime0--) получается фрагмент на 4 такта быстрее.
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|