Как принять сигнал с приемника в AVR (PWM|Digital)

KGB

я понимаю что от РРМ никуда не дется… ну или от канального импульса… чтобы вогнать это в авр!!!
а почему нельзя пойти совсем по другому варианту… взять приёмник с sbus шиной от футабы… и считывать сразу в коде!!! это 100% точности!!
а вот преборазовывая длину импульса в код никто не задумывается о фазовых искажениях… при срабатывании по прерыванию теряем несколько тактов…
а потом по второму фронту!! …

Probelzaelo
Nesenin:

что можно\нужно пофиксить\улучшить?

Код мягко говоря не самый читаемый, в плане комментов, 😉 наверное первым делом нужно принципиально и подальше уйти от использования функции типа “Delay”, во всяком случае избегать использования такого рода циклов, у тебя таймеры бездельничают, таймер 1 даже инициализируется, но как то ни пе при делах, используй его чтоли для задания времени. мало ли что может произойти пока цпу занят этим совсем неполезным действом …
очень доходчиво о таймерах на AT eugenemcu.ru/publ/5-1-0-48

KGB:

преборазовывая длину импульса в код никто не задумывается о фазовых искажениях… при срабатывании по прерыванию теряем несколько тактов…

Ну давай посчитаем, из расчета “В PPM-кодере каждый канал задается импульсом, длительностью от 700 до 2200 мкс. Границы могут немного варьироваться, в зависимости от конкретной модели аппаратуры. Среднему положению рулевой машинки соответствует импульс длительностью около 1500 мкс” 1мкс это ни много ни мало, а целых 4-20 тактов(мГц) для среднего контроллера. учитывая что в диаппазоне 2200-700=1500 закодировано от 256 но не более 1024 позиций, получаем что у нас в зависимости от выбранного CPU и точности пульта приходится от 6 (1024поз/4МГц) и до 200 (256поз/20мгц) тактов на каждую единичку позиции, что позволяет не задумываться об проблеме, но в любом случае можно результат сделать более точным просто добавляя к “измеренному времени” константу, среднее время вхождения в прерывания.
Но для стиков пульта уж очень малокритично выглядит более точная чем 256 оцифровка позиции, ведь потенциометр слишком грубый инструмент для задания позиции…

Aleksey_Gorelikov

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

KGB
Probelzaelo:

…закодировано от 256 но не более 1024 позиций,

у футабы в сбусе… идёт инфа в 11 бит!!! тоесть 2048!!! сам видел!!!

Aleksey_Gorelikov

Ну к словам придераться не обязательно. У спектрума, к примеру - тоже 11 бит протокол есть. Причем еще и 11мс пакеты повторяются. Суть это не сильно меняет. Кстати, а в трехканальном новопропе 88 года выпуска - так вобще аналоговый сигнал! Там кол-во бит = бесконечность! 😃

Nesenin
Probelzaelo:

таймер 1 даже инициализируется, но как то ни пе при делах

не. он используется для подсчета длительности импульсов ШИМ. комментарии заменил.
вот подчистил мусор. добавил код для остальных каналов.


.device         ATmega88PA
.include "C:\Program Files\Atmel\AVR Studio 5.0\avrassembler\include\m88PAdef.inc"

.def Temp=r16
.def Temp1=r17
.def Temp2=r18
.def Temp3=r19
.def Temp4=r20
.def Temp5=r21

.def	RxChannel1StartL	=r0
.def	RxChannel1StartH	=r1

.def	RxChannel2StartL	=r2
.def	RxChannel2StartH	=r3

.def	RxChannel3StartL	=r4
.def	RxChannel3StartH	=r5

.def	RxChannel1L		=r8
.def	RxChannel1H		=r9

.def	RxChannel2L		=r10
.def	RxChannel2H		=r11

.def	RxChannel3L		=r12
.def	RxChannel3H		=r13

.def	tisp=r14


.cseg    ;обозначает начало программного сегмента

.org 0 ;задает начальный адрес. В данном случае он = 0

	rjmp reset
	rjmp RxChannel2   ;INT0   PD2
	rjmp RxChannel3   ;INT1   PD3
	rjmp unused
	rjmp unused
	rjmp RxChannel1   ;PCINT17  PD1
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused
	rjmp unused

unused:	reti

;************************
;прерывания
;************************

RxChannel1:
	in tisp, sreg

	sbis pind,1				;проверка состояния ножки
	rjmp rx1m1


	lds RxChannel1StartL, tcnt1l		;запись времени начала импульса
	lds RxChannel1StartH, tcnt1h


	out sreg,tisp
	reti


rx1m1:
    lds RxChannel1L, tcnt1l			;запись времени конца импулса
	lds RxChannel1H, tcnt1h

	sub RxChannel1L, RxChannel1StartL  ;подсчет длительности
	sbc RxChannel1H, RxChannel1StartH

	out sreg,tisp
	reti


RxChannel2:

	in tisp, sreg

	sbis pind,2				;проверка состояния ножки
	rjmp rx2m1


	lds RxChannel2StartL, tcnt1l		;запись времени начала импульса
	lds RxChannel2StartH, tcnt1h


	out sreg,tisp				;выход сразу из прерывания

	reti


rx2m1:
    lds RxChannel2L, tcnt1l			;запись времени конца импулса
	lds RxChannel2H, tcnt1h

	sub RxChannel2L, RxChannel2StartL    ;подсчет длительности
	sbc RxChannel2H, RxChannel2StartH


	out sreg,tisp

	reti

RxChannel3:


	in tisp, sreg

	sbis pind,3				;проверка состояния ножки
	rjmp rx3m1


	lds RxChannel3StartL, tcnt1l		;запись времени начала импульса
	lds RxChannel3StartH, tcnt1h


	out sreg,tisp

	reti


rx3m1:
    lds RxChannel3L, tcnt1l			;запись времени конца импулса
	lds RxChannel3H, tcnt1h

	sub RxChannel3L, RxChannel3StartL   ;подсчет длительности
	sbc RxChannel3H, RxChannel3StartH

	out sreg,tisp

	reti

;****************************************************
; ИНИЦИАЛИЗАЦИЯ
;****************************************************

Reset:
          ldi Temp1,0   ;задержка запуска после сброса против всякой фигни
          ldi Temp2,0
		  ldi Temp3,10

    l_res:
          dec Temp1
          brne l_res

          dec Temp2
          brne l_res

		  dec Temp3
          brne l_res


			ldi Temp,Low(ramend)	; Инициализация стека
	        out spl,Temp
			ldi Temp,High(ramend)
	        out sph,Temp

;--- setup IO ---
         	;        76543210
		  ldi Temp,0b11111111   ;настройка портов
		  out DDRB,Temp          ;порт Б настроили на вывод
		    ;        76543210
		  ldi Temp,0b00000000 ;все вырубить
		  out PortB,Temp
         	;        76543210
		  ldi Temp,0b11110001  ;настройка портов
		  out DDRD,Temp        ;1,2,3 ногу порта Д настроили на ввод
		   	;        76543210
         ldi Temp,0b00001110  ;настройка портов в режиме чтения
	     out PORTD,Temp       ;установили подтягивающий резистор однеркой.
		                    ;нулем поставили 3 состояние с высоким сопротивлением.

;--- Setup pin change interrupt on PD1, PD2, PD3, PB4
			 ;установка разрешенных прерываний по ногам

	   ;       76543210
	ldi Temp,0b00000101
	sts pcicr,Temp
		;      76543210
	ldi Temp,0b00000010
	sts pcmsk2,Temp

	;          76543210
	ldi Temp,0b00000101
	sts eicra,Temp
	;       76543210
	ldi Temp,0b00000011
	out eimsk,Temp


;---- таймер 1 будет на 1MHz (8MHz/8) ----
		;      76543210
	ldi Temp,0b00000010
	sts tccr1b,Temp

;****************************************************
;прочие начальные установки
;****************************************************
Load:

	ldi xl,low(1500)		;загружаем константы
	ldi xh,high(1500)

	mov RxChannel1L,xl
	mov RxChannel1H,xh
	mov RxChannel2L,xl
	mov RxChannel2H,xh
	mov RxChannel3L,xl
	mov RxChannel3H,xh

sei ;разрешить глобальные прерывания


;****************************************************
; ОСНОВНОЙ ЦИКЛ
;****************************************************

Inf:

				ldi xl,low(1700)		;загружаем константы
	            ldi xh,high(1700)

                cp RxChannel1L,xl
	            cpc RxChannel1H,xh
				BRSH da1

				ldi xl,low(820)		;загружаем константы
	            ldi xh,high(820)

				cp RxChannel1L,xl
	            cpc RxChannel1H,xh
				BRSH net1
pinout1:
				cbi PortB,1
				cbi PortB,2

timeflagon1:
 				ldi xl,low(1700)		;загружаем константы
	            ldi xh,high(1700)

                cp RxChannel2L,xl
	            cpc RxChannel2H,xh     ;проверка
				BRSH da2

				ldi xl,low(820)		;загружаем константы
	            ldi xh,high(820)

				cp RxChannel2L,xl		;проверка
	            cpc RxChannel2H,xh
				BRSH net2

 pinout2:
                cbi PortD,7
				cbi PortB,0

timeflagon2:
				ldi xl,low(1700)		;загружаем константы
	            ldi xh,high(1700)

                cp RxChannel3L,xl			;проверка
	            cpc RxChannel3H,xh
				BRSH da3

				ldi xl,low(820)		;загружаем константы
	            ldi xh,high(820)

				cp RxChannel3L,xl			;проверка
	            cpc RxChannel3H,xh
				BRSH net3

pinout3:
                cbi PortD,5
				cbi PortD,6
timeflagon3:
		;rcall Delay ;вызов задержки

 rjmp Inf              ;бесконечный цикл
 ;****************************************************

 da1:
     sbi PortB,1
    rjmp timeflagon1

net1:
                ldi xl,low(1350)		;загружаем константы
	            ldi xh,high(1350)

				cp RxChannel1L,xl		;проверка
	            cpc RxChannel1H,xh
				BRSH pinout1
	sbi PortB,2
	rjmp timeflagon1


 da2:
     sbi PortB,0
    rjmp timeflagon2

net2:
                ldi xl,low(1350)		;загружаем константы
	            ldi xh,high(1350)

				cp RxChannel2L,xl		;проверка
	            cpc RxChannel2H,xh
				BRSH pinout2
	sbi PortD,7
	rjmp timeflagon2

 da3:
     sbi PortD,5
    rjmp timeflagon3

net3:
                ldi xl,low(1350)		;загружаем константы
	            ldi xh,high(1350)

				cp RxChannel3L,xl		;проверка
	            cpc RxChannel3H,xh
				BRSH pinout3
	sbi PortD,6
	rjmp timeflagon3

все работает. входы ШИМ PD1,2,3. По прерываниям идет запись значений таймера и подсчет длины импульса. в основном коде идет анализ и вкл\выкл соответствующих ножек (PD5-7,PB0-2).

KGB

lds RxChannel2StartL, tcnt1l ;запись времени начала импульса
lds RxChannel2StartH, tcnt1h

lds RxChannel2L, tcnt1l ;запись времени конца импулса
lds RxChannel2H, tcnt1h

sub RxChannel2L, RxChannel2StartL ;подсчет длительности
sbc RxChannel2H, RxChannel2StartH

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

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

а ещё вопрос! этот вариант будет работать на приёмниках где канальные импульсы начинаются все вместе…??

Probelzaelo
KGB:

у футабы в сбусе… идёт инфа в 11 бит!!! тоесть 2048!!! сам видел!!!

есть и 3072 те аж 12 бит(почему не 4096 не знаю), а кому нужны эти 11-12 бит? если даже 256 уровней различать на стиках и то избыточно для такой задачи как актИвное ручное управление, мягко говоря не менее 3 из этих бит можно смело выбросить, тем более что там у резисторов в стиках “шум” больше в разы чем точность установленного туда же АЦП, все эти “навороты” и “крутизна” лишь для того чтобы ну хоть как то оправдать утроенную цену для игрушки и “выпятить” ее по сравнению с конкурентами, не более того…
Вот такое вот у меня есть ИМХО…

Nesenin:

он используется для подсчета длительности импульсов ШИМ

действительно используется, пропустил - не заметил …

KGB:

а ещё вопрос! этот вариант будет работать на приёмниках где канальные импульсы начинаются все вместе…??

Если начинаются все вместе то по идее работать будет, но лишь по “случайному” стечению обстоятельств.

KGB:

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

Так и есть. гдето 15 раз в секунду вроде должен … 1М/65536=15.2587890625 самый верный способ уйти от этой ошибки это завести длинную переменную в которой считать время от старта 😉 и сделать еще и обработчик прерывания таймера с инкрементом системного времени.
Второй вариант обнулять таймерпри старте импульса… тогда он до конца посчитать не должОн успеть в течении всей командной посылки… а если успеет то это уже повод для аудита, скорее всего сигнал потерялся.

Boroda
KGB:

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

Результат будет точно такой, как и без перехода через ноль. Это основы двоичной арифметики. К примеру, в 8-битном формате запись числа -236 ничем не отличается от записи числа 20.

KGB:

а ещё вопрос! этот вариант будет работать на приёмниках где канальные импульсы начинаются все вместе…??

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

Probelzaelo
Boroda:

К примеру, в 8-битном формате запись числа -236 ничем не отличается от записи числа 20.

они еще как отличаются, а именно тем что в одном случае в 8 бит это диаппазон -127…+127 а в другом 0…255. в случае с таймером чтобы результат был один и тот же нужно таки своевременно отследить переполнение счетчика.

Boroda:

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

Встает естественный вопрос, как же все таки возникают канальные импульсы на выходах приемника. не далее как в в #83 посте этой же ветки было совсем если не обратное то слегка противоречащее утверждение…
кто то имеет реальную временную диаграмму показывающую реальную картину кан.импульсов хотя бы для какой то одной модели их доступной кучи приемников?

Пост #82

Probelzaelo:

Надеюсь что каналы ШИМят все же по очереди, и в том же порядке как это было принято из входящего PPM…

Пост #83

msv:

Не надейтесь… Последовательно шимы появляются пожалуй только в простейших декодерах на сдвиговом регистре…

Кто же видел правду?

Вот обнаружил, кратенько, но доходчиво и с картинками описаны разные вариации генерации PWM сигналов на выходах мультиканальноого приемника и даже с алгоритмами. Я так понимаю что все эти варианты вполне живут в современных конструкциях. Хотя бы из тех соображений, что каждый из производителей пытается отличаться от других принятыми решениями, ради собственной “уникальности”

KGB
Boroda:

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

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

я вот просто тоже работал в аналогичной теме…над sbus шиной… поэтому и посмотрел осцилом как там что идёт!
и у меня тоже возникла одна проблема которую я не решил!!!
а проблема такая!!! вот есть у нас 16каналов по 11бит и 2 канала по 8 бит… тоесть в числовом виде!!!
как из этого сделать 18 каналов пвм импульсов??? получается что нужен процик ч тактовой в мгц 100-150… либо специализированая МС…
вот на логике это я могу сделать… но будет куча корпусов 😦
а програмно смысл такой…что просто бежит счётчик…и на один его такт… сравнивается с канальным значением… тоесть это надо сделать 18 раз…
а так как проц 8 бит. а оперируем в 16… то код увеличивается ещё вдвое!!! кароче просто не успеваем…
на меге8 с 16мгц можно успеть 1-2 канала всего 😦
а вот если канальные импульсы сдвигать один за одним… то проблеммы появятся уже с их периодом!!! 😦

Probelzaelo
KGB:

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

По логике такой вариант сам просится, алгоритм достаточно простой и быстрый выходит. сброс таймера в ноль и на все выходы подать активное напряжение, одновременно записью в регистр по 8 бит, после этого в цифровой компаратор таймера поместить длину самого короткого импульса, по срабатыванию снять активный бит и вписать следующее значение… и так пока не отработают все каналы… потом додерживается синхро пауза и все сначала…

KGB:

а так как проц 8 бит. а оперируем в 16… то код увеличивается ещё вдвое!!!

так длинные каналы вроде как передаются в серии не целиком, а через раз “по половине”, а 8-ми битные влезают целиком в каждую серию…

msv
Probelzaelo:

алгоритм достаточно простой и быстрый выходит.

Да, я сделал именно так. Сортировка по времени массива структуры (маска сброса (канал), время канала), установка уровня по всем каналам, сброс таймера и вперед… в глухом цикле сравнение с очередным каналом и его сброс. Только прерывания должны быть на это время запрещены, что требует синхронизации с остальными процессами (получение значений итп.).

Boroda
Probelzaelo:

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

Нет, не нужно. Пусть 8-битный таймер меряет импульс шириной 20 тиков. Без переполнения 30-10=20, с переполнением 10-246=-236 Оба результата в 8-битном двоичном формате будут выглядеть как 10100

Probelzaelo:

кто то имеет реальную временную диаграмму

Уже был пост #110 стараниями AndyBig. Вы хотите ещё таких-же картинок? Других мне пока получить не удалось.

KGB:

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

Большое спасибо за информацию! FASST никогда в руках не держал, надо срочно проверить эту не очень приятную новость!

msv:

Да, я сделал именно так.

Сергей, как боретесь с джиттером? Ведь чем больше каналов, и чем “одинаковее” их длительности, тем больше вероятность конфликта по времени спада импульсов?

KGB
Boroda:

Большое спасибо за информацию! FASST никогда в руках не держал, надо срочно проверить эту не очень приятную новость!

а что её проверять то??? вот например приёмник 6014…
тоесть 14 каналов… период импульсов 14мс… вот… а если все 14к выстроить один за одним по фазе… и при максимальной длительности импульса получим
14Х2=28мс… тоесть время в два раза больше времени периода 😦

Boroda:

Сергей, как боретесь с джиттером? Ведь чем больше каналов, и чем “одинаковее” их длительности, тем больше вероятность конфликта по времени спада импульсов?

согласен! когда время не равно а очень близко и по цыклам …лажа может получится… не вариант…

Probelzaelo:

так длинные каналы вроде как передаются в серии не целиком, а через раз “по половине”, а 8-ми битные влезают целиком в каждую серию…

я говорю не про передаются а про обработку!!
да и передаются не по половине а… 8+3_5+6_2+8+1 итд…

Probelzaelo
KGB:

да и передаются не по половине а… 8+3_5+6_2+8+1 итд…

я имел ввиду половина каналов сейчас, вторая потом )) чередуются, четные /нечетные.

KGB
Probelzaelo:

я имел ввиду половина каналов сейчас, вторая потом )) чередуются, четные /нечетные.

это где так каналы передаются???

пс
выше я описывал передачу по сбусу

Probelzaelo
Boroda:

Уже был пост #110 стараниями AndyBig. Вы хотите ещё таких-же картинок?

Спасибо, глянул, странно что я тот пост раньше пропустил, вроде стараюсь следить за темой.

Пришла в голову мысль. (поздравлять не надо, поздно уже)
А какая нам собственно разница, одновременно ли выходят канальные импульсы или поочередно? ведь если их выгонять одновременно, тогда приемнику надо каждый из них обработать, вычислить длину, сохранить, потом ее отработать на соответствующем канальном выходе, для этого придется задействовать таймер, но ведь во всем этом нет ни какого смысла, если только приемник не ужасно умный и сам знает что хочет. Но это уже, считай что это уже целый робот/автопилот с ручной корректировкой… Что то мне подсказывает что ни один приемник вообще не вычисляет эти длительности импульсов из приходящей серии. А тупо “сортирует” их по номеру и выставляют в канал один в один, точно так же как это бы сделал и сдвиговый регистр. В таком случае и погрешность в длину практически не будет внесена. Просто у импульса появятся красивые фронт и спад а сам импульс сдвинется по времени на несколько тактов CPU - на время реакции процессора, а она достаточно быстрая и главное одинакова в обоих случаях и при установке и при сбросе канала… зачем приемнику что то улучшать в сигнале или разбираться в нем?

тогда перефразирую свой вопрос

Probelzaelo:

Кто же видел правду?

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

KGB

вот… это …жолтая линия - 1й канал поданый в синхровнизацию
а зелёная это 2й канал…345678 выглядят также!!!
это приёмник Futaba R6208SB !
также работают 4 6 7 8 и 14 каналов приёмники от футабы! 😃

msv
KGB:

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

Проц atmega88, тактовая 15мгц (от rfm22b), прога тупо на си (наверняка можно оптимизировать на асме). Измерял для 4-х каналов с одинаковым временем. Разница по длине импульса между 1-м и 4-м каналом <20мкс. Причем ошибка статическая, никакого життера нет.


//-----------------------------------------------------
#define PPM_MIN_CH    1500   // 0.8ms*1875
#define PPM_MAX_CH    4125   // 2.2ms*1875
void OutPWM(void)
{
u_long l;
u_char i, ff;
union { u_int i; u_char ch[2]; } cnt;
typedef struct { u_char mask; u_int val; } tagDataPWM;
tagDataPWM DataPWM[6];
u_char NumChPWM;
tagDataPWM dp;
const u_char mask[]={ 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe };

for(i=0; i<6; i++)
  { // scale
  DataPWM[i].mask=mask[i];
  l=gMainData.ChVal[i]; l*=(PPM_MAX_CH-PPM_MIN_CH); DataPWM[i].val=(u_int)(l/256);
  DataPWM[i].val+=PPM_MIN_CH;
  }
ff=1;
while(ff)
  { // sorting
  ff=0;
  for(i=0; i<(6-1); i++)
    if(DataPWM[i].val>DataPWM[i+1].val)
      {  dp=DataPWM[i]; DataPWM[i]=DataPWM[i+1]; DataPWM[i+1]=dp;  ff=1; }
  }
NumChPWM=0;
#asm("cli")
PORTC=0x3f;
TCNT1H=0x00;
TCNT1L=0x00;
while(NumChPWM<6)
  { // work
  [0]=TCNT1L; [1]=TCNT1H;
  if(cnt.i>=DataPWM[NumChPWM].val)
    {
    PORTC&=DataPWM[NumChPWM].mask;
    NumChPWM++;
    }
  }
#asm("sei")
}
//-----------------------------------------------------

Написал на скорую руку для оценки работоспособности, но понравилось как работает… и пока нет желания дальнейшей оптимизации…

KGB

я на си не понимаю… тока асм 😦