Как принять сигнал с приемника в AVR (PWM|Digital)
Хочу принять на ардуину канал с приемника. Как различать разные положения крутилки?
Его в ШИМ (PWM) ногу или в обычную цифровую? или в аналоговую? как вообще ходит сигнал из приемника к перефирийным устройствам?
(Arduino mini= 2,5 грамм, Апаратура TH9b FlySky, приемник обычный 8-ми канальный)
Илья, обычно я принимаю сигнал с приемника в AVR так - подается он на вход с обработкой внешнего прерывания, это значит, когда меняется фронт сигнала ШИМ от приемнка с возростающего - на спадающий и наооборот, авр генерирует прерывание. Дальше при помощи таймера мы подсчитываем длину импульса ШИМ-сигнала и пишем соответсвующий код реакции…
вообщем все просто, если Вы хорошо знакомы с программированием контроллеров…
Также различаются разные положения крутилки - это разные длины ШИМ импульса, которые меряются выше указанным мной способом…
Я так делал свою электронику для серв, кил свичи, реверсы, замедлители и др. устройства, управляемые ШИМом…
Ежели без sum ppm, то банально: начинаем при появлении высокого уровня, останавливаемся при исчезновении высокого уровня.
Время между этими двумя событиями и есть сигнал приемника.
Высчитывать это можно и таймерами, и прерываниями, и банальным millis() в основном цикле
Его в ШИМ (PWM)
Обязательно!
Иначе- замучаетесь ложняки вылавливать.
Обязательно!
Иначе- замучаетесь ложняки вылавливать.
Если бы ув. blade разбирался в контроллерах он бы знал, что Arduino mini нога PWM обозначает только то, что с неё удобно получать ШИМ, читаем описание: “Pins 3, 5, 6, 9, 10, and 11 can provide PWM output; for details see the analogWrite() function.”
Но упомянутая нога, может быть и входом для ШИМ, также нога №2 может быть входом для ШИМ…
осталось написать соответсвующий код для этого контроллера…
Если бы ikar разбирался в контроллерах он бы знал
Если бы Вы разбирались в чём нибудь, кроме болтологии, то знали бы что, например в Атмега 8 (я на них делал автопилот, поэтому все тонкости их схемотехники мне известны) есть две ноги, заточенные на приём PWM сигнала- а именно 5 и 6 (в дип 28 версии) и две- заточенные на выдачу его же (но уже обработанного-15,16).
Не вдаваясь в изучение Ат368 или 1280, почему то склонен считать, что организация портов на них-аналогичная.
И именно затем, чтобы человек не муд…чался, разбираясь- почему длительность ШИМ сигнала на входе- определяется то так, то эдак, а на выходе- импульс трясётся, что вызывает рычание серв- я ему и посоветовал найти входы/выходы аппаратно заточенные на обработку PWM.
Если бы Вы разбирались в чём нибудь, кроме болтологии, то знали бы что, например в Атмега 8 (я на них делал автопилот, поэтому все тонкости их схемотехники мне известны) есть две ноги, заточенные на приём PWM сигнала- а именно 5 и 6 (в дип 28 версии) и две- заточенные на выдачу его же (но уже обработанного-15,16).
Не вдаваясь в изучение Ат368 или 1280, почему то склонен считать, что организация портов на них-аналогичная.
И именно затем, чтобы человек не муд…чался, разбираясь- почему длительность ШИМ сигнала на входе- определяется то так, то эдак, а на выходе- импульс трясётся, что вызывает рычание серв- я ему и посоветовал найти входы/выходы аппаратно заточенные на обработку PWM.
Ну, во-первых, я согласен с тем, что я немного резковато выразился насчет Вашей личности, это потому что Вы в соседней теме меня раздражали,…
А по существу вот, что скажу - пин 5 атмеги8 (кстате у Arduino mini используется чип атмега 168, который идентичен по разпиновке атмеге8, ну это Вы и так знаете), так вот этот пин №5 в дип версии, мало чем отличается, например, от пина №4 - это просто братья-близнецы, кроме порта ввода-вывода, они еще имеют функцию подключения источников внешнего прерывания… Пин6 - отличается от выше указанных, тем, что, цитирую из даташита “XCK (USART External Clock Input/Output) T0 (Timer/Counter 0 External Counter Input)” тоесть внешнее тактирование интерфейса УСАПП, или внешнее тактирование таймера/счетчика0…
Короче говоря, в этом контроллере нет специально заточенных ног на ШИМ-вход, на выход есть(!), но на вход - самые удобные ноги - это уже названные мною, те самые братья-близнецы, потому что они дают возможность более легкую программу написать, для обработки ШИМа… И по большему счету, при достаточном владении языком программирования, можно написать код, который обработает, адекватно ШИМ, если даже его будете подавать на другие ноги (понятное дело - кроме служебных, питание и т.д.).
А тяслось у Вас, только потому, что, видимо Вы забыли поставить так называемую подтяжку, или была криво написана прога.
Я делал проекты на разных кристаллах, в том числе на меге8 - ничего у меня не тряслость и работало четко…
Если Вы не согласны с тем, что я изложил тут, ув. blade, то пожалуйсто ткните в меня местом из даташита на указанный Вами мега8, где сказано, что названные Вами ножки (5 и 6) “заточены под ШИМ”. Буду Вам признателен.
ничего у меня не тряслость и работало четко
Извините, что вмешиваюсь: а у Вас “не тряслось” в “Протеусе” или Вы сервы подключали?
Вставлю и я пятачок 😃. Сразу оговорюсь, что в теме микроконтроллеров я недавно, поэтому тягаццо с мэтрами по части знания даташитов не буду. Но модельной электроникой я занимаюсь четверть века, поэтому представление о том, что должно получиться в результате, у меня присутствует.
В общем, у меня делается так:
- Обработка импульса ведется в прямом цикле, без таймеров и прерываний (для новичка это важно, помогает избежать гимора на этапе знакомства с железом).
- Первый-второй импульсы после включения пропускаются (они могут быть битыми и привести к неверной отработке устройства).
- Разрядность при обработке должна быть выше разрешающей способности сервы (у меня импульсу 1,5 мс соответствует 2000-2500 тиков), это избавит от дерганья сервы.
- Сам импульс должен сразу проверяться на пригодность к дальнейшей обработке (у меня это диапазон 0,8-2,2 мс).
- Если на выходе после обработки должен получаться дискретный сигнал, используется “защелка” с гистерезисом в 2-3 тика, это исключит ложные срабатывания вблизи точки переключения.
- Если на выходе после обработки должен получаться ШИМ, то “защелка” не нужна.
- Если на выходе должен быть какой-то результирующий сервоимпульс, то правила его формирования должны соответствовать п.п. 1 и 3., тактировать выходные импульсы лучше входными, обработку вести во время синхропаузы.
- Для обеспечения максимальной совместимости по уровням сигнала, импульс желательно обрабатывать на входе аналогового компаратора, но это не обязательно.
- Схема обязательно макетируется, только это гарантирует ее нормальную последующую работу на рабочей плате.
Данные советы на системах с ИИ не проверялись, но вполне пригодны в 90% случаев для простых модельных устройств типа сервореверсера, подкала, коллекторного регулятора оборотов и т.д.
Извините, что вмешиваюсь: а у Вас “не тряслось” в “Протеусе” или Вы сервы подключали?
Я протеусом не пользуюсь вообще, как при написании прог так и при проектировании и сборки железа. Прошивки проверяю на макете или на конструкторе или непосредственно на железе…
- Обработка импульса ведется в прямом цикле, без таймеров и прерываний (для новичка это важно, помогает избежать гимора на этапе знакомства с железом).
Вот именно это и достигается применением аппаратно-заточенных под ШИМ входов/выходов.
Иначе, даже выглядящие вполне прилично на осциллографе канальные импульсы, вызывают тряску серв 😦
Вот именно это и достигается применением аппаратно-заточенных под ШИМ входов/выходов.
Иначе, даже выглядящие вполне прилично на осциллографе канальные импульсы, вызывают тряску серв 😦
выглядящие вполне прилично на осциллографе импульсы, не могут вызывать тряску серв, повторяю - эта проблема решается правильно написаным софтом и установкой “подтяжек”…
И опять же, нету у контроллера аппаратно-заточеннх под ШИМ входов (не путать с выходами!)…
Ув. blade, разъясните мне пожалуйсто, что такое по Вашему “аппаратно-заточенный под ШИМ вход” и что за чудо схемотехника стоит за этим входом, что он носит гордое название “аппаратно-заточенный”?..
Вот, просто, любопытно, сколько занимаюсь контроллерами с детальным изучением даташитов и впервые вижу такое определение, просветите пожалуйсто)
ICP
ICP
ну извините меня, ICP в атмега8 это - 14 нога, а не названные выше, и она обозначает вход триггера захвата фронта таймера-счетчика 1. Им можно мерять импульсы, не спорю (правда там есть свои нюансы), но например в том же ардуино используется для этих целей нога с альтернативной функциеей - внешнее прерывание по изменению фронта, а почему? потому, что так проще код написать - через внешнее прерывание, и что освобождает процессор от всяких циклов с задержками для других задач…
У меня был такой проект, где в меге8 - был и сервотестер, и замерщик ШИМ-сигнала, для активаций оборудования, и слежения за линией питания, и вся телеметрия эта, передавалась по последовательной шине другому контроллеру. Все происходило в одно и тоже время, прошивка заняла 95% места в кристалле…
Окей, из приемника выходит PPM. мерять его по изменению фронта. это уже более-мение понятно.
Подскажите, чем вы эмулируете PPM в протеусе? там есть генираторы П импульсов - но как тестить? запустил с одной величиной - потом остановил - запустил с другой?
а тут же еще надо последовательность _П_I–I_ как получить вход, для отладки в протеусе?
Просто читая таймер по прерыванию имеем нестабильность на время входа в прерывание. Это как мин 4 такта проца, как макс. время окончания подпрограммы другого прерывания или просто по каким то причинам запрещены прерывания (запись в EEPROM итп). C ICR все железно, достаточно смотреть флаги и читать значения не реже времени строба или по тому же прерыванию. Для протеуса в качестве генератора PPM использовал Easy HDL, там пишется простейший луп с необходимыми длительностями. Для отладки вполне достаточно.
Просто читая таймер по прерыванию имеем нестабильность на время входа в прерывание. Это как мин 4 такта проца, как макс. время окончания подпрограммы другого прерывания или просто по каким то причинам запрещены прерывания (запись в EEPROM итп). C ICR все железно, достаточно смотреть флаги и читать значения не реже времени строба или по тому же прерыванию. Для протеуса в качестве генератора PPM использовал Easy HDL, там пишется простейший луп с необходимыми длительностями. Для отладки вполне достаточно.
Вас не затруднит примерчик такого скрипта опубликовать?
Да там все просто, что-то типа:
LBL_START:
REM CH1 -------------
OUT=0
SLEEP FOR 0.0004
OUT=1
SLEEP FOR 0.0011
REM CH2 -------------
OUT=0
SLEEP FOR 0.0004
OUT=1
SLEEP FOR 0.0011
REM CH3 -------------
OUT=0
SLEEP FOR 0.0004
OUT=1
SLEEP FOR 0.0011
REM CH4 -------------
OUT=0
SLEEP FOR 0.0004
OUT=1
SLEEP FOR 0.0011
REM CH5 -------------
OUT=0
SLEEP FOR 0.0004
OUT=1
SLEEP FOR 0.0011
REM CH6 -------------
OUT=0
SLEEP FOR 0.0004
OUT=1
SLEEP FOR 0.0004
REM SYN -------------
OUT=0
SLEEP FOR 0.0004
OUT=1
SLEEP FOR 0.0026
GOTO LBL_START
Ээээ, то есть в каждый канал с приемника поступает вся пачка состояний каналов???
что-то я не понял…
или же только один канал выходит?
то есть
-
-
- 4,8-6 вольт с бека
- PPM посылка соответсвующая данному каналу, длинною в 2500мс из которой в первые 400 мс синхропауза, потом длина отклонения стика пропорциональна длине сигнала. но не больше 2100мс
я правильно понял?
Все разобрался, всем спасибо за разъяснения.
Теперь с помощью ардуины у меня есть БАНО.
4 режима:
- режим готовности, все выключено.
- режим стоянки, работают только стояночные огни (габориты).
- режим взлета и посадки, включается такси фонарь (передние фары) и стробы.
- полетный режим, работают габариты и строба. такси фонарь выключен.
код:
////////////////////////////////////////////////////////////////////
// BANO Arduino mini pro. 2,5 gramm. Channal for managment.
// Illia Golovatskyi mailto:
////////////////////////////////////////////////////////////////////
int ledTAXI = 2; // LED connected to digital pin 2 (4) Front of the plane.
int ledWHITE = 4; // LED connected to digital pin 4 (6) On the ruder.
int ledRED = 7; // LED connected to digital pin 7 (13) On the left console
int ledGREEN = 8; // LED connected to digital pin 8 (14) On the right console
int ledFLY = 10; // LED connected to PWM pin 10 (16) Strobe.
int pinInput = 3; // Input managment channel pin 3 (5)
volatile int Recv; // store RC signal pulse length
int Norm; // mapped value to be between 0-100
volatile long CH1PulseStartTicks;
int CH1Ready;
volatile int ParkFire = false;
volatile int TaxiFire = false;
volatile int FlyFire = false;
unsigned long FlyStartTicks;
unsigned long FlyTicks;
int BlinkFire = false;
int FadeLow = 25;
int FadeHigh = 255;
////////////////////////////////////////////////////////////////////
void test () {
digitalWrite(ledRED, HIGH); // set the LED on
digitalWrite(ledGREEN, HIGH); // set the LED on
digitalWrite(ledWHITE, HIGH); // set the LED on
digitalWrite(ledTAXI, HIGH); // set the LED on
analogWrite(ledFLY, 255); // set the LED on
delay(3000); // wait for a second
digitalWrite(ledRED, LOW); // set the LED off
digitalWrite(ledGREEN, LOW); // set the LED off
digitalWrite(ledWHITE, LOW); // set the LED off
digitalWrite(ledTAXI, LOW); // set the LED off
analogWrite(ledFLY, 0); // set the LED off
}
////////////////////////////////////////////////////////////////////
void setup() {
// nothing happens in setup
pinMode(ledRED, OUTPUT);
pinMode(ledGREEN, OUTPUT);
pinMode(ledWHITE, OUTPUT);
pinMode(ledTAXI, OUTPUT);
pinMode(pinInput, INPUT); //PPM inputs from RC receiver
attachInterrupt(1, ch1, CHANGE); // catch interrupt 1 (digital pin 3) going HIGH and send to rc1()
test();
}
////////////////////////////////////////////////////////////////////
void ch1()
{
if (digitalRead( pinInput ) == HIGH)// did the pin change to high or low?
{
CH1PulseStartTicks = micros();// store the current micros() value
}
else
{
// Pin transitioned low, calculate the duration of the pulse
Recv = micros() - CH1PulseStartTicks; // may glitch during timer wrap-around
// Set flag for main loop to process the pulse
CH1Ready = true;
}
}
////////////////////////////////////////////////////////////////////
void ParkLed(){
if (ParkFire) {
digitalWrite(ledRED, LOW); // set the LED off
digitalWrite(ledGREEN, LOW); // set the LED off
digitalWrite(ledWHITE, LOW); // set the LED off
ParkFire = false;
} else {
digitalWrite(ledRED, HIGH); // set the LED on
digitalWrite(ledGREEN, HIGH); // set the LED on
digitalWrite(ledWHITE, HIGH); // set the LED on
ParkFire = true;
}
}
////////////////////////////////////////////////////////////////////
void TaxiLed(){
if (TaxiFire) {
digitalWrite(ledTAXI, LOW); // set the LED off
TaxiFire = false;
} else {
digitalWrite(ledTAXI, HIGH); // set the LED on
TaxiFire = true;
}
}
////////////////////////////////////////////////////////////////////
void FlyLed(){
if (FlyFire) {
digitalWrite(ledFLY, LOW); // set the LED off
FlyFire = false;
} else {
digitalWrite(ledFLY, HIGH); // set the LED on
FlyFire = true;
FlyStartTicks = millis();
}
}
////////////////////////////////////////////////////////////////////
void loop(){
if (CH1Ready) {
CH1Ready = false; // reset input flag
// constrain and map the pulse length
Norm = map(constrain(Recv, 1000, 2000), 1000, 2000, 0, 100);
// Update LED
if (Norm < 10) { // All LED is off.
if (ParkFire == true) {ParkLed(); }
if (TaxiFire == true) {TaxiLed(); }
if (FlyFire == true) {FlyLed(); }
digitalWrite(ledTAXI, LOW); // set the LED off
} else if (Norm < 25) { // Park light is on. (Red on left console, Green on right console and White on ruder)
if (ParkFire == false) {ParkLed();}
if (TaxiFire == true) {TaxiLed(); }
if (FlyFire == true) {FlyLed(); }
} else if (Norm < 50) { // Landing mode: Park light + taxi + strobe LED is on.
if (ParkFire == false) {ParkLed();}
if (TaxiFire == false) {TaxiLed(); }
if (FlyFire == false) {FlyLed(); }
} else if (Norm < 75) { // Fly mode: Park light is on, strobe is blinking
if (ParkFire == false) {ParkLed();}
if (TaxiFire == true) {TaxiLed(); }
if (FlyFire == false) {FlyLed();}
}
}
if (FlyFire == true) {
FlyTicks = millis() - FlyStartTicks;
if (BlinkFire) {
if (FlyTicks >100) {
analogWrite(ledFLY,FadeLow);
FlyStartTicks = millis();
BlinkFire=false;
}
} else {
if (FlyTicks >500) {
analogWrite(ledFLY,FadeHigh);
FlyStartTicks = millis();
BlinkFire=true;
}
}
}
////////////////////////////////////////////////////////////////////
} // End of loop;
////////////////////////////////////////////////////////////////////
Прошу прощения, я понял что нужен генератор группового PPM, а не канального декодированного PWM. Для Вашей задачи особой точности не требуется и код вполне работоспособен… но контроль за переполнением CH1PulseStartTicks я бы все-таки сделал… 😃