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

Probelzaelo

А для чего используешь такое “хитрое” переназначение вектора прерывания да еще и внутри каждого обработчика?
detachInterrupt/attachInterrupt; Как то не айс, может как то попытать вроде такого?:

int Int0Pin=2;
void setup()
{


attachInterrupt(0, AllPulse, CHANGE);
}

Void AllPulse()
{
TimeInt = timerCount;
timerCount=0;

IF( digitalRead(Int0Pin) > 0)
{
impUP()
}
else
{
impDown()
}
}

void impUp() //---------- Функция обработки положительного импульса
{
upImpuls = timeInt ;
if ( upImpuls > 43 && upImpuls < 51) { Error = 2 ; } else { Error = 0 ; delay(50) ; chanel = 0 ; }
}

void impDown() // Функция обработки отрицательного импульса
{
int ppm = timeInt;
if( ppm > 400 ) { chanel = 0 ; } else { chanel ++ ; }
ppmImpuls[chanel] = ppm ;
}

Mastar

Напишите окончательный скетчь для получения значений каждого канала. Ваш вариант с функцией IF( digitalRead(Int0Pin) > 0) не устраивает самой функцией, которая только условно стабильна. Попробуйте мой и сравните со своим, в жизни, но не на эмуляторах.

Probelzaelo
Mastar:

Напишите окончательный скетчь для получения значений каждого канала. Ваш вариант с функцией IF( digitalRead(Int0Pin) > 0) не устраивает самой функцией, которая только условно стабильна. Попробуйте мой и сравните со своим, в жизни, но не на эмуляторах.

Не знаю что нестабильного в чтении состояния, пока еще ни разу жаловаться не приходилось. единственно внутри IF чтение мне самому не очень нравится, надежнее бы промежуточную переменную использовать, но как правило и так работает…
Пробовать буду, но немного поздже 😉

Mastar

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

У меня одновременно с чтением PPM идет сбор данных с GPS, управление сервами и много-много всего другого где есть и работа с 2 портами и micros() и delayMicroseconds().

Для справочки по прерыванию

Замечание по использованию

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данный передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.

Полное описание arduino.ru/Reference/AttachInterrupt

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

Вот данные на ком-порту
CH0-пауза между пакетами
I-импульс между каналами
CH1-CH8 - соответственно сами каналы.

CH0=1035 I=46 CH1=101 I=46 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=48 CH7=94 I=47 CH8=104 I=47
CH0=1035 I=48 CH1=101 I=47 CH2=98 I=47 CH3=106 I=47 CH4=101 I=46 CH5=48 I=46 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=48 CH3=106 I=47 CH4=101 I=47 CH5=48 I=47 CH6=101 I=47 CH7=94 I=46 CH8=104 I=47
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=48 CH7=94 I=48 CH8=104 I=48
CH0=1035 I=48 CH1=101 I=47 CH2=98 I=47 CH3=106 I=46 CH4=101 I=47 CH5=48 I=47 CH6=101 I=47 CH7=94 I=47 CH8=104 I=47
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=47 CH3=106 I=48 CH4=101 I=48 CH5=48 I=47 CH6=101 I=47 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=47 CH1=101 I=47 CH2=98 I=47 CH3=106 I=47 CH4=101 I=47 CH5=48 I=47 CH6=101 I=48 CH7=94 I=48 CH8=104 I=47
CH0=1035 I=47 CH1=101 I=47 CH2=98 I=47 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=48 CH3=106 I=48 CH4=101 I=47 CH5=48 I=47 CH6=101 I=46 CH7=94 I=47 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=48 CH7=94 I=47 CH8=104 I=47
CH0=1035 I=47 CH1=101 I=47 CH2=98 I=46 CH3=106 I=47 CH4=101 I=46 CH5=48 I=46 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=48 CH3=106 I=48 CH4=101 I=47 CH5=48 I=47 CH6=101 I=47 CH7=94 I=47 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=47 CH7=94 I=47 CH8=104 I=48
CH0=1035 I=47 CH1=101 I=47 CH2=98 I=47 CH3=106 I=46 CH4=101 I=47 CH5=48 I=47 CH6=101 I=47 CH7=94 I=47 CH8=104 I=47
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=47 CH3=106 I=47 CH4=101 I=48 CH5=48 I=47 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=48 CH7=94 I=48 CH8=104 I=47
CH0=1035 I=48 CH1=101 I=47 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=48 CH3=106 I=48 CH4=101 I=47 CH5=48 I=46 CH6=101 I=46 CH7=94 I=47 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=47 CH7=94 I=48 CH8=104 I=47
CH0=1035 I=47 CH1=101 I=46 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=48 CH3=106 I=47 CH4=101 I=47 CH5=48 I=47 CH6=101 I=47 CH7=94 I=47 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=48 CH6=101 I=48 CH7=94 I=47 CH8=104 I=48
CH0=1035 I=47 CH1=101 I=47 CH2=98 I=47 CH3=106 I=47 CH4=101 I=46 CH5=48 I=46 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=48 CH2=98 I=48 CH3=106 I=47 CH4=101 I=48 CH5=48 I=47 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=46 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=48 CH6=101 I=48 CH7=94 I=48 CH8=104 I=47
CH0=1035 I=48 CH1=101 I=47 CH2=98 I=46 CH3=106 I=46 CH4=101 I=46 CH5=48 I=46 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46
CH0=1035 I=46 CH1=101 I=48 CH2=98 I=48 CH3=106 I=47 CH4=101 I=47 CH5=48 I=46 CH6=101 I=46 CH7=94 I=46 CH8=104 I=46

Probelzaelo
Mastar:

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

Это еще почему? в обработчик прерывания будем попадать кратковременно при получении каждого перепада на входе. При этом получаем длительность последнего импульса/паузы.
Во время нахождения в обработчике прерывания запрещены (условно, скидывается флаг EI, (бит 7 SREG)) ибо так устроен проц. по выходу - комманде reti прерывание снова разрешается (установится EI) в посте #136 в конце есть 2 ссылочки на не плохую русскую доку.
внутри обработчика функции micros() и delayMicroseconds() не работают и не нужны. Но “посещение” прерывания никак не влияет на их работу в основном цикле…

Mastar:

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются.

Согласен, если из обработчика прерывания вызывать delay() то результат может оказаться не предсказуемым, а millis(), если она почему то заинтересует, вернет значение которое было на момент входа в прерывание … внутри обработчика в принципе и не следует задаваться какими то временными интервалами, оттуда вообще надо уходить как можно быстрее, чтобы не тратить чужое время… Полагаю что все обработки переменных и анализ можно и нужно делать в основном цикле программы… тогда ни что не будет мешать принять данные и от других портов и устройств… у процессора “сил” достаточно много чтобы со всеми справиться, да еще и “перекурить” )

Mastar

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

Bare

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

Mastar
Bare:

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

Естественно, но проблема только самыми последними выпусками, а в старых наверно везде возможно. PCM-приемники конечно не подходит.

А программку конечно интересно.

Probelzaelo

Хороший проектик, зачетно-позитивный… Давно уже хочется реализовать что то очень подобное …

Bare
Mastar:

А программку конечно интересно.

Вот последняя версия. Только я её проверить не успел, т.к. ардуино отдал на время другу, но компилируется. Как будет время, добавлю больше комментариев и всяких опций.

rc_pwm-111213a.zip

Mastar
Probelzaelo:

Хороший проектик, зачетно-позитивный… Давно уже хочется реализовать что то очень подобное …

Спасибо, но только я похоже замахнулся на длительный проект, попробую за зиму осилить.

Bare:

Вот последняя версия.

Спасибо, ознакомлюсь.

Bare

Заметил одну особенность, если в программе использовать постоянно Serial.print (например в loop, как у меня), то сервы начинают дрожать.

Tester500

Есть еще одна засада. Если мозг приемника слегка съедет, и на одном из каналов который мониторится контроллером появится сигнал с частотой ~ 1 MГц, контроллер все время будет в прерываниях, на остальное ему сил не хватит. Блокировки канала от неверного сигнала (времянка меньше 0.5 мс) нету.

Probelzaelo
Tester500:

Есть еще одна засада. Если мозг приемника слегка съедет, и на одном из каналов который мониторится контроллером появится сигнал с частотой ~ 1 MГц, контроллер все время будет в прерываниях, на остальное ему сил не хватит. Блокировки канала от неверного сигнала (времянка меньше 0.5 мс) нету.

Не понятно, откуда там возникает такой “интересный” сигнал? это продуманная фича от производителя или глюк какого то реального приемника? и на сколько часто происходит такой “сьезд мозга”? шибко это похоже на очень не штатную ситуацию…
впрочем PPM обычно не снимается “с одного из каналов”.

ilyxascrat

я вот такую конструкцию накатал в виде библиотеки - кладем в libraries, пишем в коде:

#include <PPMint.h>
...
PPMint ppm;
...
void setup(){....
ppm.setup();
...}

void loop(){...
 <используем по назначению> ppm.realRaw[<номер канал>]
...}

Мне удобно, может еще кому полезно будет www.nest.org.ru/…/ardiuno-библиотека-ppmint/

Использую для подключения пульта Futaba T7C по Bluetooth (к ящику сим подключаю).

Есть также библиотека для аналогичной обработки PWM - отдельно с каждого канала приемыша R6017HS - что не важно - но показать пока не могу - работает через ж…у, и не оформлено пригодно для использования - кое-какая муть с использованием более 2-х пинов с прерываниями, но чисто технически все уже понятно.

25 days later
Nesenin

делаю дискретный регулятор хода коллекторных двигателей на ATmega88PA. пишу на asm. в работе с микроконтроллерами новичок. в качестве примера взял исходники для контроллера квадракоптера. вот они
накопипастил кода для 3х каналов. написал код обработки длительности импулься для включение 2х ножек для 1 канала. долго боролся с случайным блужданием контроллера по строкам кода, с цикличным прохождением прерывания по reset. после чего добился стабильного свечения светодиодов без видимых мерцаний во время включения\работы. вот код (не без мусора)

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

;обзываем некоторые из 16 РОН (регистров общего назначения): R16…R31


.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

;.dseg    ; Сегмент ОЗУ

;Line:	.byte 2   ;константа в ОЗУ sram!

.cseg    ;обозначает начало программного сегмента (запись во флэшь)
;.ESEG		; Сегмент EEPROM
.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				;rising or falling?
	rjmp rx1m1


	lds RxChannel1StartL, tcnt1l		;rising, store the start value
	lds RxChannel1StartH, tcnt1h


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

flagon:

	lds RxChannel1StartL, tcnt1l		;rising, store the start value
	lds RxChannel1StartH, tcnt1h

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

rx1m1:
    lds RxChannel1L, tcnt1l			;falling, calculate the pulse length
	lds RxChannel1H, tcnt1h

	sub RxChannel1L, RxChannel1StartL
	sbc RxChannel1H, RxChannel1StartH

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


RxChannel2:

	in tisp, sreg

	sbis pind,2				;rising or falling?
	rjmp rx2m1


	lds RxChannel2StartL, tcnt1l		;rising, store the start value
	lds RxChannel2StartH, tcnt1h


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

	reti


rx2m1:
    lds RxChannel2L, tcnt1l			;falling, calculate the pulse length
	lds RxChannel2H, tcnt1h

	sub RxChannel2L, RxChannel2StartL
	sbc RxChannel2H, RxChannel2StartH


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

	reti

RxChannel3:


	in tisp, sreg

	sbis pind,3				;rising or falling?
	rjmp rx3m1


	lds RxChannel3StartL, tcnt1l		;rising, store the start value
	lds RxChannel3StartH, tcnt1h


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

	reti


rx3m1:
    lds RxChannel3L, tcnt1l			;falling, calculate the pulse length
	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,0b00000000 ;все вырубить
		  out PortD,Temp
		   	;        76543210
         ;ldi Temp,0b00001110  ;настройка портов в режиме чтения
	     ;out PORTD,Temp       ;установили подтягивающий резистор однеркой.
		                    ;нулем поставили 3 состояние с высоким сопротивлением.

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

	   ;       76543210
	ldi Temp,0b00000101	;PB7, PD1  разрешение прерыв  по pcint
	sts pcicr,Temp
		;      76543210
	ldi Temp,0b00000010	;PD1       пок какой ноге pcint
	sts pcmsk2,Temp

	;          76543210
	ldi Temp,0b00000101	;PD2, PD3  разрешение прерыв по int
	sts eicra,Temp
	;       76543210
	ldi Temp,0b00000011	;PD2, PD3      запись что и по read & write
	out eimsk,Temp		;STS? OUT?  Come on!


;---- таймер 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:

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

                cp RxChannel1L,xl
	            cpc RxChannel1H,xh
				BRSH da

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

				cp RxChannel1L,xl
	            cpc RxChannel1H,xh
				BRSH net
			;sei
pinout:

				cbi PortD,6
				cbi PortD,7

timeflagon:

		rcall Delay ;вызов задержки

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

 da:
     sbi PortD,6
    rjmp timeflagon

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

				cp RxChannel1L,xl
	            cpc RxChannel1H,xh
				BRSH pinout
	sbi PortD,7
	rjmp timeflagon
;****************************************************
;Задержка
;****************************************************
Delay:
          ldi Temp,0     ;присвоение 10тичной константы
          ldi Temp1,0

Doop:
          dec Temp
          brne Doop

         ; dec Temp2
         ; brne Doop

		      ret
  

работает, но полагаю могут быть ошибки. что можно\нужно пофиксить\улучшить?

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 года выпуска - так вобще аналоговый сигнал! Там кол-во бит = бесконечность! 😃