Создание собственной системы стабилизации
А как это Вы так один канал настроили, что он оба фронта ловит,
если не секрет…?
А как это Вы так один канал настроили, что он оба фронта ловит,
если не секрет…?
Не секрет конечно.
вот настройка канала:
Timer* timer = Timer::getInstance();
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge; // ловим оба перехода
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // данные напрямую со входа
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // делитель отключен
TIM_ICInitStructure.TIM_ICFilter = 0x0; // фильтр отключен
timer->TIM_ICInit(TIMx, &TIM_ICInitStructure);
Ловлю переход из 0 в 1, запоминаю значение счетного таймера в переменную oldData, ловлю переход из 1 в 0, запоминаю значение из счетного таймера в newData.
Таймер тикает с частотой 1 Мг.
Так как частота ШИМ стандартно 50Гц, значит период будет 0,2 с, и мне его считать не надо. Таким образом получаю скважность ШИМ.
Снимая показания с пульта, получаю разницу между newData и oldData от 1000мкс до 2000мкс (минимальное - максимальное значение газа). 1500мкс - центральное положение.
В будущем если нужен будет захват на другой частоте, учесть коэффициент не проблема.
Раз в 0,4 сек. получаю “слейку” 2-х периодов. Вот ее мне и надо убрать…
Собственно сейчас подключил к мотору, вроде бы эти “склейки” не мешают.
Хотелось бы конечно от них избавится на всякий случай, кто знает когда регуль рассинхрон словит…
В будущем если нужен будет захват на другой частоте, учесть коэффициент не проблема.
так получается система не универсальная. под каждый приемник настраивать надо
так как сейчас ковыряюсь с AeroQad32
радио PWM
/*
Copyright (c) 2011 ala42. All rights reserved.
STM32 receiver class by ala42 using time input capture
for use with AeroQuad software and Maple library
V 1.0 Oct 15 2011
V 1.1 Jan 22 2012 class free version for AeroQuad 3.0 compatibility
Define the pin numbers used for the receiver in receiverPin[]
Timer and timer channels are accessed using the Maple PIN_MAP array.
Make sure libmaple and this receiver class are compiled using the
same structure alignment mode. When in doubt, change the stm32_pin_info
declaration in wirish_types.h to align the size to a multiple of 4 byte
by adding a filler byte at the end of the structure declaration.
*/
#ifndef _AEROQUAD_RECEIVER_STM32_H_
#define _AEROQUAD_RECEIVER_STM32_H_
#if defined(AeroQuadSTM32)
#include "Receiver.h"
#include "wirish.h"
//#define STM32_TIMER_DEBUG // enable debug messages
///////////////////////////////////////////////////////////////////////////////
// configuration part starts here
// definition of pins used for PWM receiver input
/*
ROLL 0 3
PITCH 1 1
YAW 2 0
THROTTLE 3 2
MODE 4 4
AUX 5 6
AUX2 6 5
AUX3 7 7
*/
static byte ReceiverChannelMap[] = {0, 1, 2, 3, 4, 5, 6, 7}; // default mapping
///////////////////////////////////////////////////////////////////////////////
// implementation part starts here.
// forward declaration, array is defined at the end of this file
extern voidFuncPtr PWM_in_handler[];
typedef struct {
timer_dev *TimerDev;
timer_gen_reg_map *TimerRegs;
__io uint32 *Timer_ccr;
int Low;
int High;
uint16 HighTime;
uint16 RiseTime;
uint16 LastChange;
int Channel;
int TimerChannel;
int PolarityMask;
int Valid;
int Debug;
} tFrqData;
#define FRQInputs 8
volatile tFrqData FrqData[FRQInputs];
void FrqInit(int aChannel, int aDefault, volatile tFrqData *f, timer_dev *aTimer, int aTimerChannel) {
aTimerChannel--; // transform timer channel numbering from 1-4 to 0-3
f->Channel = aChannel;
f->Valid = false;
f->TimerDev = aTimer;
timer_gen_reg_map *timer = aTimer->regs.gen;
f->TimerRegs = timer;
f->Timer_ccr = &timer->CCR1 + aTimerChannel;
f->Debug = false;
f->HighTime = aDefault;
f->TimerChannel = aTimerChannel;
int TimerEnable = (1 << (4*aTimerChannel));
f->PolarityMask = TimerEnable << 1;
uint32 clock_speed = rcc_dev_timer_clk_speed(f->TimerDev->clk_id);
timer->PSC = (clock_speed/1000000)-1;
timer->ARR = 0xffff;
timer->CR1 = 0;
timer->DIER &= ~(1);
timer->CCER &= ~TimerEnable; // Disable timer
timer->CCER &= ~(f->PolarityMask);
volatile uint32 *mr;
if(aTimerChannel < 2) {
mr = &(timer->CCMR1);
}
else {
mr = &(timer->CCMR2);
}
*mr &= ~(0xFF << (8*(aTimerChannel&1))); // prescaler 1
*mr |= 0x61 << (8*(aTimerChannel&1)); // 0x61 -> 6=filter, 1=inputs 1,2,3,4
timer->CCER |= TimerEnable; // Enable
timer->CR1 = 1;
}
void InitFrqMeasurement() {
for(int rcLine = 0; rcLine < (int)(sizeof(receiverPin) / sizeof(receiverPin[0])); rcLine++) {
int pin = receiverPin[rcLine];
timer_dev *timer_num = PIN_MAP[pin].timer_device;
if(timer_num != NULL) {
gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, GPIO_AF_INPUT_PD);
FrqInit(rcLine, 1500, &FrqData[rcLine], timer_num, PIN_MAP[pin].timer_channel);
timer_attach_interrupt(timer_num, PIN_MAP[pin].timer_channel, PWM_in_handler[rcLine]);
}
}
}
void PWMInvertPolarity(volatile tFrqData *f) {
f->TimerRegs->CCER ^= f->PolarityMask; // invert polarity
}
void FrqChange(volatile tFrqData *f) {
timer_gen_reg_map *timer = f->TimerRegs;
uint16_t c = *(f->Timer_ccr);
bool rising = (timer->CCER & f->PolarityMask) == 0;
if(f->Valid) {
if(rising) {
f->RiseTime = c;
}
else {
uint16_t highTime = c - f->RiseTime;
if(highTime > 900 && highTime < 2100) {
f->HighTime = highTime;
}
else {
f->Valid = false;
}
}
}
else if(rising) {
// rising edge, store start time
f->RiseTime = c;
f->Valid = true;
}
PWMInvertPolarity(f);
}
// hide the class details from the interrupt handler
void IrqChangeValue(int chan) {
FrqChange(&FrqData[chan]);
}
///////////////////////////////////////////////////////////////////////////////
// definition of interrupt handler functions, one for each channel
void PWM_in_0() { IrqChangeValue(0); }
void PWM_in_1() { IrqChangeValue(1); }
void PWM_in_2() { IrqChangeValue(2); }
void PWM_in_3() { IrqChangeValue(3); }
void PWM_in_4() { IrqChangeValue(4); }
void PWM_in_5() { IrqChangeValue(5); }
void PWM_in_6() { IrqChangeValue(6); }
void PWM_in_7() { IrqChangeValue(7); }
voidFuncPtr PWM_in_handler[] = { PWM_in_0, PWM_in_1, PWM_in_2, PWM_in_3, PWM_in_4, PWM_in_5, PWM_in_6, PWM_in_7 };
///////////////////////////////////////////////////////////////////////////////
// interface part starts here
void initializeReceiver(int nbChannel = 8) {
initializeReceiverParam(nbChannel);
InitFrqMeasurement();
}
int getRawChannelValue(const byte channel) {
int chan = ReceiverChannelMap[channel];
if(chan < (int)sizeof(receiverPin)) {
volatile tFrqData *f = &FrqData[chan];
uint16_t PulsLength = f->HighTime;
return PulsLength;
} else {
return 1500;
}
}
void setChannelValue(byte channel,int value) {
}
#endif
#endif
радио PPM
/*
Copyright (c) 2012 kha. All rights reserved.
STM32 PPM receiver by kha based on
STM32 receiver class by ala42 using time input capture
for use with AeroQuad software and Maple library
V 1.0 Jun 14 2012
Define the pin numbers used for the receiver in receiverPinPPM
Timer and timer channels are accessed using the Maple PIN_MAP array.
Make sure libmaple and this receiver class are compiled using the
same structure alignment mode. When in doubt, change the stm32_pin_info
declaration in wirish_types.h to align the size to a multiple of 4 byte
by adding a filler byte at the end of the structure declaration.
*/
#ifndef _AEROQUAD_RECEIVER_STM32PPM_H_
#define _AEROQUAD_RECEIVER_STM32PPM_H_
#if defined(AeroQuadSTM32)
#include "Receiver.h"
#include "wirish.h"
#include "Receiver_PPM_common.h"
static byte ReceiverChannelMap[PPM_CHANNELS] = {SERIAL_SUM_PPM};
uint16 rawChannelValue[PPM_CHANNELS] = {1500,1500,1500,1500,1500,1500,1500,1500,1500,1500};
byte currentChannel;
///////////////////////////////////////////////////////////////////////////////
// implementation part starts here.
typedef struct {
timer_dev *TimerDev;
timer_gen_reg_map *TimerRegs;
__io uint32 *Timer_ccr;
uint16 RiseTime;
int TimerChannel;
int PolarityMask;
} tFrqData;
volatile tFrqData FrqData;
void FrqInit(int aDefault, timer_dev *aTimer, int aTimerChannel)
{
aTimerChannel--; // transform timer channel numbering from 1-4 to 0-3
FrqData.TimerDev = aTimer;
timer_gen_reg_map *timer = aTimer->regs.gen;
FrqData.TimerRegs = timer;
FrqData.Timer_ccr = &timer->CCR1 + aTimerChannel;
FrqData.TimerChannel = aTimerChannel;
int TimerEnable = (1 << (4*aTimerChannel));
FrqData.PolarityMask = TimerEnable << 1;
uint32 clock_speed = rcc_dev_timer_clk_speed(FrqData.TimerDev->clk_id);
timer->PSC = (clock_speed/1000000)-1;
timer->ARR = 0xffff;
timer->CR1 = 0;
timer->DIER &= ~(1);
timer->CCER &= ~TimerEnable; // Disable timer
timer->CCER &= ~(FrqData.PolarityMask);
volatile uint32 *mr;
if(aTimerChannel < 2) {
mr = &(timer->CCMR1);
}
else {
mr = &(timer->CCMR2);
}
*mr &= ~(0xFF << (8*(aTimerChannel&1))); // prescaler 1
*mr |= 0x61 << (8*(aTimerChannel&1)); // 0x61 -> 6=filter, 1=inputs 1,2,3,4
timer->CCER |= TimerEnable; // Enable
timer->CR1 = 1;
}
void FrqChange()
{
uint16_t c = *(FrqData.Timer_ccr);
uint16_t diffTime = c - FrqData.RiseTime;
if ((diffTime > 900) && (diffTime < 2100)) {
if (currentChannel < PPM_CHANNELS) {
rawChannelValue[currentChannel] = diffTime;
currentChannel++;
}
}
else if (diffTime > 2500) {
currentChannel = 0;
}
else {
// glitch; stop and wait next round
currentChannel = PPM_CHANNELS;
}
FrqData.RiseTime = c;
}
void InitFrqMeasurement()
{
int pin = receiverPinPPM;
timer_dev *timer_num = PIN_MAP[pin].timer_device;
currentChannel=8;
if(timer_num != NULL) {
gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, GPIO_AF_INPUT_PD);
FrqInit(1500, timer_num, PIN_MAP[pin].timer_channel);
timer_attach_interrupt(timer_num, PIN_MAP[pin].timer_channel, FrqChange);
}
}
///////////////////////////////////////////////////////////////////////////////
// interface part starts here
void initializeReceiver(int nbChannel = 8) {
initializeReceiverParam(nbChannel);
InitFrqMeasurement();
}
int getRawChannelValue(const byte channel) {
return rawChannelValue[ReceiverChannelMap[channel]];
}
void setChannelValue(byte channel,int value) {
}
#endif
#endif
так получается система не универсальная. под каждый приемник настраивать надо
Ну в вообщем-то да, вся настройка выглядит вот так:
int main(void)
{
STM32* mk = STM32::getInstance();
Clock* clc = Clock::getInstance();
PWM* pwm = PWM::getInctance();
// Инициализируем железо
mk->Init();
// Настраиваем системный таймер
clc->Setup();
pwm->Setup(PB7, TIM4, CH_2, INPUT, 50); // приемник
pwm->Setup(PA0, TIM2, CH_1, OUTPUT, 50); // мотор
while(1)
{
pwm->Write(TIM2, CH_1, pwm->Read(TIM4, CH_2)); // передаю на мотор сигнал с приемника
}
}
Это полный текст главного цикла, где с одного канала ( с приемника) получаю ШИМ и передаю на другой ( на мотор).
Цифра 50 - это и есть частота ШИМ.
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;
Видимо у 407-го (не смотрел) есть настройка на оба фронта, а у меня F303 только “или”-“или”…
Вы посмотрите, - есть ли аппаратная возможность сброса счетчика по первому фронту (в моем кажется есть, сейчас как раз копаюсь), тогда все проблемы отпадут…
Видимо у 407-го (не смотрел) есть настройка на оба фронта, а у меня F303 только “или”-“или”…
Вы посмотрите, - есть ли аппаратная возможность сброса счетчика по первому фронту (в моем кажется есть, сейчас как раз копаюсь), тогда все проблемы отпадут…
А сбрасывать ведь нельзя…
Если к этому таймеру подключены еще 3 канала ШИМ ?
Вы ведь сбросите счетчик таймера для все каналов он же единый…
И если на одном получите правильное значение, то для всех остальных будет “мусор”
Я бы тогда конечно сбрасывал по второму фронту и проблем бы с переполнением не было.
А сбрасывать ведь нельзя…
.
Согласен…, затупил , к тому же “разул глаза” благодаря Вам, и на моем можно по обеим фронтам настроить…, будем думать…
Однако имейте в виду (проверено) что в реальном времени импульсы всех каналов разнесены и не пересекаются (они из PPM вырезаны), так что насчет сброса еще подумать надо…
Согласен…, затупил , к тому же “разул глаза” благодаря Вам, и на моем можно по обеим фронтам настроить…, будем думать…
Однако имейте в виду (проверено) что в реальном времени импульсы всех каналов разнесены и не пересекаются (они из PPM вырезаны), так что насчет сброса еще подумать надо…
Впринципе, можно проверить, подключить к лог. анализатору 4 канала и посмотреть временые диаграмы.
Ну все равно риск некоторый есть мне кажется, чуть задержался в прерываниях, пришел импульс с другого канала и все, пиши пропало.
чуть задержался в прерываниях, пришел импульс с другого канала и все, пиши пропало.
на то они и прерывания чтобы в них не задерживаться
и все, пиши пропало.
Вот и я всю голову изломал, даже всерьез подумываю слепить преобразователь на простеньком Avr-e в PPM и тогда получим: 1 нога STM под приемник ! ПДП на чтение всех каналов ! никаких прерываний !
на то они и прерывания чтобы в них не задерживаться
Ну имелось в виду не само прерывание как таковое, а функция его обработки.
Пока обработываешь, пришло другое прерывание, значение счетчика изменилось, а мы его сбросили… Потому сбрасывать его не желательно. ИМХО.
Вообщем, проверять это все дело надо.
Вот и я всю голову изломал, даже всерьез подумываю слепить преобразователь на простеньком Avr-e в PPM и тогда получим: 1 нога STM под приемник ! ПДП на чтение всех каналов ! никаких прерываний !
Вот уж не знаю, стоит ли шкурка выделки…
По мне так лучше как сейчас, 1 канал таймера - 1 канал приемника.При таких настройках моторы вчера гонял весь вечер, проблем не было.
Однако имейте в виду (проверено) что в реальном времени импульсы всех каналов разнесены и не пересекаются
Увы, не во всех приемниках так. В некоторых они как раз одновременно идут
Вот пример картинки с сайта Эксперта
Вот-вот, тем более даже если взять 1-ю картинку, то по ней спад 1 канала совпадает с фронтом 2-го. Даже в этом случае расчитывать на сброс счетчика уже опасно, пока считаешь первый канал, со второго пришло прерывание. И не факт что сбросить счетчик успеешь до прихода прерывания со второго канала.
А если учесть что на 1 таймере весит аж 4 канала управления, игра в рулетку какая-то получается.
Увы, не во всех приемниках так.
Странно… не знал, ведь в радиосигнале информация не может быть параллельна, как же это они так “измудрились” к одному фронту привязать все каналы, хотя … пути господни…
Получается, что все алгоритмы летят к “… бабушке”.
И не факт что сбросить счетчик успеешь до прихода прерывания со второго канала.
действительно, что делать если прерывание следующего канала сработает сразу с завершением фронта предыдущего? как успеть записать значение шима???
Странно… не знал, ведь в радиосигнале информация не может быть параллельна, как же это они так “измудрились” к одному фронту привязать все каналы, хотя … пути господни…
Это же цифровой сигнал, приходит пакет, приемник его декодирует и выставляет значения каналов, к примеру.
Это же цифровой сигнал.
У меня futaba PCM (типа “цифра”) но импульсы всеж разнесены причем с “зазором”, как и в реале…
Вот rcopen.com/forum/f123/topic264377/49 простая ловилка PWM на tiny 2313 сам сейчас с такой летаю, как пишет автор, там ссылка есть на первоисточник, работает с любыми выходами приёмника…
I have used it without problems with my mikrokopter. It works without skipping frames on my Corona CR8D type 2 receiver (all servo channels output simultaneously). It should work fine on type 1 receivers also (servo channels output sequentially), but I don’t own any so I can’t test it.
действительно, что делать если прерывание следующего канала сработает сразу с завершением фронта предыдущего? как успеть записать значение шима???
Если сделать как у меня, то ничего страшного в этом не будет, я же не сбрасываю значение счетчика. А вот если сбрасывать, тогда да, будут проблемы.
У меня futaba PCM (типа “цифра”) но импульсы всеж разнесены причем с “зазором”, как и в реале…
А зазор какой, сколько есть времени на обработку?
А зазор какой, сколько есть времени на обработку?
0.3 мили/сек
простая ловилка PWM на tiny
Вот, а я еще хочу скажем в RS232 преобразовать (не проблема) тогда прям в USART заливай 16 байт (8х2), ПДП, и вперед…