Создание собственной системы стабилизации

SergDoc

Вот только думаю, для хобби, полёт по точкам -архинужная вещь, вышел в чисто поле - отпустил коптер погулять и сиди медитируй, позагоать можно, а он выгулялся и назад прилелел - романтика 😃 -получается принцип как в рыбалке из анекдота - наливай да пей 😃 я, если что, за правильную рыбалку - без спиртного…

mataor
SergDoc:

p.p.s нарыл проект виртуального com- порта(не из примеров), непомню где и ешё не ковырялся, но по всей видимости подойдёт как замена программатора, с данным ПО должно места хватить для уживания с основной прошивкой (интегрирования в прошивку), посмотрю что за бяка отпишусь…

не знаю правильно ли я понял вас, у себя на хмеге серии U уже вовсю пользуюсь программированием + работой ГУИ через аппаратный ЮСБ. Пришлось поковырятся 2 дня, зато удовольствия и удобства - дофига!

По полету по точкам - не сделано в вие скорее всего именно из-за непродуманной передачи координат, так как как уже и было сказано выше - все что нужно - т.е. полет по точке (в виде возврата домой сейчас) в коде давно уже есть - я просто сейчас жду новую платку с флешкой и для ЖПС-а как раз тоже.

SergDoc

я пока до УСБ не добрался, во первых платы ещё нет, во вторых ещё датчики присобачить надо по SPI а как-то без самой платы интереса нет, пока только ПО (имеющееся) “пересадил” на F4, очень много изменений по периферии…

Razek

Полет по точкам имеет смысл с железобетонным удержанием высоты, такая фича только у комрада Маховика есть, а в оригинальном вие вроде она не так хорошо работает.

Probelzaelo
mataor:

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

Я помню что гдето видел в мануалах, когда плата ВсеВ Одном была еще с 328 процессором. Там была возможность повесить GPS только через I2C и иметь 16 точек WP0…WP15 из которых нулевая это HOME те точка возврата по RTH, либо чуть позже “научились” вешать Serial GPS, но тогда приходилось принести в жертву все точки кроме WP0, из за банального недостатка памяти у чипа. Сейчас на “больших” платах памяти достаточно, но пока видимо руки до организации WP не дошли. Либо это сознательно не делается ради сохранения совместимости с предыдущими моделями плат, которых очень много. В любом случае Wii имеет возможность летать по точкам, и содержит это в исходниках, но нужно адаптировать конфигурацию до аппаратных возможностей более мощной платы.

Probelzaelo
Razek:

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

Не понятно откуда такое условие, поясни плиз, как именно связано удержание высоты с полетом по точкам?

Razek

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

SergDoc

Дошли руки до ШИМов, начало:

static pwmHardware_t timerHardware[] = {
    { TIM2, GPIOA, GPIO_Pin_15,TIM_Channel_1, TIM2_IRQn, 0, },          	// PWM1
    { TIM2, GPIOB, GPIO_Pin_3, TIM_Channel_2, TIM2_IRQn, 0, },          	// PWM2
    { TIM3, GPIOB, GPIO_Pin_4, TIM_Channel_1, TIM3_IRQn, 0, },          	// PWM3
    { TIM3, GPIOB, GPIO_Pin_5, TIM_Channel_2, TIM3_IRQn, 0, },          	// PWM4
    { TIM4, GPIOB, GPIO_Pin_6, TIM_Channel_1, TIM4_IRQn, 0, },          	// PWM5
    { TIM4, GPIOB, GPIO_Pin_7, TIM_Channel_2, TIM4_IRQn, 0, },          	// PWM6
    { TIM4, GPIOB, GPIO_Pin_8, TIM_Channel_3, TIM4_IRQn, 0, },  	              // PWM7
    { TIM4, GPIOB, GPIO_Pin_9, TIM_Channel_4, TIM4_IRQn, 0, },		// PWM8
    { TIM1, GPIOE, GPIO_Pin_14,TIM_Channel_4, TIM1_CC_IRQn, 1, },                // PWM9
    { TIM1, GPIOE, GPIO_Pin_13,TIM_Channel_3, TIM1_CC_IRQn, 1, },       	// PWM10
    { TIM1, GPIOE, GPIO_Pin_11,TIM_Channel_2, TIM1_CC_IRQn, 1, },      	// PWM11
    { TIM1, GPIOE, GPIO_Pin_9, TIM_Channel_1, TIM1_CC_IRQn, 1, },                 // PWM12
    { TIM5, GPIOA, GPIO_Pin_3, TIM_Channel_4, TIM5_IRQn, 0, },          	// PWM13
    { TIM5, GPIOA, GPIO_Pin_2, TIM_Channel_3, TIM5_IRQn, 0, },          	// PWM14
    { TIM5, GPIOA, GPIO_Pin_1, TIM_Channel_2, TIM5_IRQn, 0, },          	// PWM15
    { TIM5, GPIOA, GPIO_Pin_0, TIM_Channel_1, TIM5_IRQn, 0, },       		// PWM16
    { TIM8, GPIOC, GPIO_Pin_6, TIM_Channel_1, TIM8_CC_IRQn, 1, },                 // PWM17
    { TIM8, GPIOC, GPIO_Pin_7, TIM_Channel_2, TIM8_CC_IRQn, 1, },                 // PWM18
    { TIM8, GPIOC, GPIO_Pin_8, TIM_Channel_3, TIM8_CC_IRQn, 1, },                 // PWM19
    { TIM8, GPIOC, GPIO_Pin_9, TIM_Channel_4, TIM8_CC_IRQn, 1, },                 // PWM20
};
Probelzaelo
Razek:

Удержание высоты и хорошая работа магнитометра это основополагающие функции для полета по точкам,

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

Razek
Probelzaelo:

“споткнувшись” об грунт, правильно?

Об грунт конечно это уже совсем тяжелый случай, но в целом да.

mahowik
Razek:

Полет по точкам имеет смысл с железобетонным удержанием высоты, такая фича только у комрада Маховика есть, а в оригинальном вие вроде она не так хорошо работает

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

DVE:

А разве Wii уже поддерживает полет по точкам?

уже есть… точка задается через андроид Ezio (EZ-GUI) прогу, посредством пальцо тыка, см. видео ниже…

SergDoc:

Вот только думаю, для хобби, полёт по точкам -архинужная вещь, вышел в чисто поле - отпустил коптер погулять и сиди медитируй, позагоать можно, а он выгулялся и назад прилелел - романтика

тогда уж лучше функцию follow me:

  • first implementation of MSP_SET_WP
    with the help of Ezio app (EZ-GUI), we can now control the multi with a smartphone: set a new position on a map / follow me / follow heading
    see Multiwii EZ-GUI specific topic: www.multiwii.com/forum/viewtopic.php?f=8&t=2034
    some video about this functionality:
SergDoc

Война с ШИМ часть вторая:
так как у меня всё через ж… , ну в общем с ног на голову поставлено

static void pwmGPIOConfig(GPIO_TypeDef *gpio, uint32_t pin, uint8_t input)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = pin;
    if (input)
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    else{
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    }
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(gpio, &GPIO_InitStructure);
}

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

SergDoc
mahowik:

уже облетал?
калман одномерный там? чет не верю я в это чудо чудное, что прям так сразу лучше полетит, чем более простые ИМУ-шки…
с калманом, уже не по наслышке, подбор коэф-в еще тот секаС…

мелкоплата на нём у меня, правда у неё секас с акселем, но летает… кальман да одномерный…

есть там один секрет 😃 комплиментарный фильтр никто не отменял 😃 цикл жрёт в два раза больше (4000), но на много лучше себя ведёт чем Таймкопа (~2000)…

mahowik
SergDoc:

кальман да одномерный…

а что характеризует n-мерность калмана? кол-во сенсоров на входе? тогда что он фильтрует 1D?

Пытаюсь вот вникнуть понемногу, но пока туговато заходит:
habrahabr.ru/post/166693/
habrahabr.ru/post/140274/

SergDoc

ось - одноосевой, каждая ось отдельно 😦

SergDoc

Допилил ШИМ вроде, но за мной как всегда проверять надо:


#include "board.h"

#define PULSE_1MS       (1000) // 1ms pulse width

typedef void pwmCallbackPtr(uint8_t port, uint16_t capture);

static pwmHardware_t timerHardware[] = {
    { TIM2, GPIOA, GPIO_Pin_15,TIM_Channel_1, TIM2_IRQn, 0, },          	// PWM1
    { TIM2, GPIOB, GPIO_Pin_3, TIM_Channel_2, TIM2_IRQn, 0, },          	// PWM2
    { TIM3, GPIOB, GPIO_Pin_4, TIM_Channel_1, TIM3_IRQn, 0, },          	// PWM3
    { TIM3, GPIOB, GPIO_Pin_5, TIM_Channel_2, TIM3_IRQn, 0, },          	// PWM4
    { TIM4, GPIOB, GPIO_Pin_6, TIM_Channel_1, TIM4_IRQn, 0, },          	// PWM5
    { TIM4, GPIOB, GPIO_Pin_7, TIM_Channel_2, TIM4_IRQn, 0, },          	// PWM6
    { TIM4, GPIOB, GPIO_Pin_8, TIM_Channel_3, TIM4_IRQn, 0, },  			// PWM7
    { TIM4, GPIOB, GPIO_Pin_9, TIM_Channel_4, TIM4_IRQn, 0, },				// PWM8
    { TIM1, GPIOE, GPIO_Pin_14,TIM_Channel_4, TIM1_CC_IRQn, 1, },           // PWM9
    { TIM1, GPIOE, GPIO_Pin_13,TIM_Channel_3, TIM1_CC_IRQn, 1, },       	// PWM10
    { TIM1, GPIOE, GPIO_Pin_11,TIM_Channel_2, TIM1_CC_IRQn, 1, },      		// PWM11
    { TIM1, GPIOE, GPIO_Pin_9, TIM_Channel_1, TIM1_CC_IRQn, 1, },           // PWM12
    { TIM5, GPIOA, GPIO_Pin_3, TIM_Channel_4, TIM5_IRQn, 0, },          	// PWM13
    { TIM5, GPIOA, GPIO_Pin_2, TIM_Channel_3, TIM5_IRQn, 0, },          	// PWM14
    { TIM5, GPIOA, GPIO_Pin_1, TIM_Channel_2, TIM5_IRQn, 0, },          	// PWM15
    { TIM5, GPIOA, GPIO_Pin_0, TIM_Channel_1, TIM5_IRQn, 0, },       		// PWM16
    { TIM8, GPIOC, GPIO_Pin_6, TIM_Channel_1, TIM8_CC_IRQn, 1, },           // PWM17
    { TIM8, GPIOC, GPIO_Pin_7, TIM_Channel_2, TIM8_CC_IRQn, 1, },           // PWM18
    { TIM8, GPIOC, GPIO_Pin_8, TIM_Channel_3, TIM8_CC_IRQn, 1, },           // PWM19
    { TIM8, GPIOC, GPIO_Pin_9, TIM_Channel_4, TIM8_CC_IRQn, 1, },           // PWM20
};

typedef struct {
    pwmCallbackPtr *callback;
    volatile uint16_t *ccr;
    uint16_t period;

    // for input only
    uint8_t channel;
    uint8_t state;
    uint16_t rise;
    uint16_t fall;
    uint16_t capture;
} pwmPortData_t;

enum {
    TYPE_IP = 0x10,
    TYPE_IW = 0x20,
    TYPE_M = 0x40,
    TYPE_S = 0x80
};

static pwmPortData_t pwmPorts[MAX_PORTS];
static uint16_t captures[MAX_INPUTS];
static pwmPortData_t *motors[MAX_MOTORS];
static pwmPortData_t *servos[MAX_SERVOS];
static uint8_t numMotors = 0;
static uint8_t numServos = 0;
static uint8_t  numInputs = 0;
// external vars (ugh)
extern int16_t failsafeCnt;

static const uint8_t multiPPM[] = {
    PWM6  | TYPE_IP,     // PPM input
    PWM9  | TYPE_M,
    PWM10 | TYPE_M,
    PWM11 | TYPE_M,
    PWM12 | TYPE_M,
    PWM13 | TYPE_M,
    PWM14 | TYPE_M,
    PWM15 | TYPE_M,
    PWM16 | TYPE_M,
    PWM17 | TYPE_S,
    PWM18 | TYPE_S,
    PWM19 | TYPE_S,
    PWM20 | TYPE_S,
    0xFF
};

static const uint8_t multiPWM[] = {
    PWM1 | TYPE_IW,     // input #1
    PWM2 | TYPE_IW,
    PWM3 | TYPE_IW,
    PWM4 | TYPE_IW,
    PWM5 | TYPE_IW,
    PWM6 | TYPE_IW,
    PWM7 | TYPE_IW,
    PWM8 | TYPE_IW,     // input #8
    PWM9 | TYPE_M,
    PWM10 | TYPE_M,
    PWM11 | TYPE_M,
    PWM12 | TYPE_M,
    PWM13 | TYPE_M,
    PWM14 | TYPE_M,
    PWM15 | TYPE_M,
    PWM16 | TYPE_M,
    PWM17 | TYPE_S,
    PWM18 | TYPE_S,
    PWM19 | TYPE_S,
    PWM20 | TYPE_S,
    0xFF
};

static const uint8_t airPPM[] = {
    PWM6 | TYPE_IP,     // PPM input
    PWM9 | TYPE_M,      // motor #1
    PWM10 | TYPE_M,     // motor #2
    PWM11 | TYPE_M,
    PWM12 | TYPE_M,
    PWM13 | TYPE_S,     // servo #1
    PWM14 | TYPE_S,
    PWM15 | TYPE_S,
    PWM16 | TYPE_S,
    PWM17 | TYPE_S,
    PWM18 | TYPE_S,
    PWM19 | TYPE_S,
    PWM20 | TYPE_S,
    0xFF
};

static const uint8_t airPWM[] = {
    PWM1 | TYPE_IW,     // input #1
    PWM2 | TYPE_IW,
    PWM3 | TYPE_IW,
    PWM4 | TYPE_IW,
    PWM5 | TYPE_IW,
    PWM6 | TYPE_IW,
    PWM7 | TYPE_IW,
    PWM8 | TYPE_IW,     // input #8
    PWM9 | TYPE_M,      // motor #1
    PWM10 | TYPE_M,     // motor #2
    PWM11 | TYPE_M,
    PWM12 | TYPE_M,
    PWM13 | TYPE_S,     // servo #1
    PWM14 | TYPE_S,
    PWM15 | TYPE_S,
    PWM16 | TYPE_S,
    PWM17 | TYPE_S,
    PWM18 | TYPE_S,
    PWM19 | TYPE_S,
    PWM20 | TYPE_S,
    0xFF
};

static const uint8_t *hardwareMaps[] = {
    multiPWM,
    multiPPM,
    airPWM,
    airPPM,
};

static void pwmTimeBase(TIM_TypeDef *tim, uint32_t period)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.TIM_Period = period - 1;
    TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 1000000) - 1; // all timers run at 1MHz
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(tim, &TIM_TimeBaseStructure);
}

static void pwmNVICConfig(uint8_t irq)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel = irq;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

static void pwmOCConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t value)
{
    TIM_OCInitTypeDef  TIM_OCInitStructure;

    TIM_OCStructInit(&TIM_OCInitStructure);
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
    TIM_OCInitStructure.TIM_Pulse = value;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;

    switch (channel) {
        case TIM_Channel_1:
            TIM_OC1Init(tim, &TIM_OCInitStructure);
            TIM_OC1PreloadConfig(tim, TIM_OCPreload_Enable);
            break;
        case TIM_Channel_2:
            TIM_OC2Init(tim, &TIM_OCInitStructure);
            TIM_OC2PreloadConfig(tim, TIM_OCPreload_Enable);
            break;
        case TIM_Channel_3:
            TIM_OC3Init(tim, &TIM_OCInitStructure);
            TIM_OC3PreloadConfig(tim, TIM_OCPreload_Enable);
            break;
        case TIM_Channel_4:
            TIM_OC4Init(tim, &TIM_OCInitStructure);
            TIM_OC4PreloadConfig(tim, TIM_OCPreload_Enable);
            break;
    }
}

static void pwmICConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t polarity)
{
    TIM_ICInitTypeDef  TIM_ICInitStructure;

    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_Channel = channel;
    TIM_ICInitStructure.TIM_ICPolarity = polarity;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0x0;

    TIM_ICInit(tim, &TIM_ICInitStructure);
}

static void pwmGPIOConfig(GPIO_TypeDef *gpio, uint32_t pin, uint8_t input)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = pin;
    if (input)
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    else{
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    }
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(gpio, &GPIO_InitStructure);
}

static pwmPortData_t *pwmOutConfig(uint8_t port, uint16_t period, uint16_t value)
{
    pwmPortData_t *p = &pwmPorts[port];
    pwmTimeBase(timerHardware[port].tim, period);
    pwmGPIOConfig(timerHardware[port].gpio, timerHardware[port].pin, 0);
    pwmOCConfig(timerHardware[port].tim, timerHardware[port].channel, value);
    // Needed  TIM1 & TIM8
    if (timerHardware[port].outputEnable)
        TIM_CtrlPWMOutputs(timerHardware[port].tim, ENABLE);
    TIM_Cmd(timerHardware[port].tim, ENABLE);

    switch (timerHardware[port].channel) {
        case TIM_Channel_1:
            p->ccr = &timerHardware[port].tim->CCR1;
            break;
        case TIM_Channel_2:
            p->ccr = &timerHardware[port].tim->CCR2;
            break;
        case TIM_Channel_3:
            p->ccr = &timerHardware[port].tim->CCR3;
            break;
        case TIM_Channel_4:
            p->ccr = &timerHardware[port].tim->CCR4;
            break;
    }
    return p;
}

static pwmPortData_t *pwmInConfig(uint8_t port, pwmCallbackPtr callback, uint8_t channel)
{
    pwmPortData_t *p = &pwmPorts[port];
    pwmTimeBase(timerHardware[port].tim, 0xFFFF);
    pwmGPIOConfig(timerHardware[port].gpio, timerHardware[port].pin, 1);
    pwmICConfig(timerHardware[port].tim, timerHardware[port].channel, TIM_ICPolarity_Rising);
    TIM_Cmd(timerHardware[port].tim, ENABLE);
    pwmNVICConfig(timerHardware[port].irq);
    // set callback before configuring interrupts
    p->callback = callback;
    p->channel = channel;

    switch (timerHardware[port].channel) {
        case TIM_Channel_1:
            TIM_ITConfig(timerHardware[port].tim, TIM_IT_CC1, ENABLE);
            break;
        case TIM_Channel_2:
            TIM_ITConfig(timerHardware[port].tim, TIM_IT_CC2, ENABLE);
            break;
        case TIM_Channel_3:
            TIM_ITConfig(timerHardware[port].tim, TIM_IT_CC3, ENABLE);
            break;
        case TIM_Channel_4:
            TIM_ITConfig(timerHardware[port].tim, TIM_IT_CC4, ENABLE);
            break;
    }
    return p;
}

void TIM1_CC_IRQHandler(void)		//TIM1
{
    uint8_t port;

    if (TIM_GetITStatus(TIM1, TIM_IT_CC1) == SET) {
        port = PWM12;
        TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);
        pwmPorts[port].callback(port, TIM_GetCapture1(TIM1));
    } else if (TIM_GetITStatus(TIM1, TIM_IT_CC2) == SET) {
        port = PWM11;
        TIM_ClearITPendingBit(TIM1, TIM_IT_CC2);
        pwmPorts[port].callback(port, TIM_GetCapture4(TIM1));
    } else if (TIM_GetITStatus(TIM1, TIM_IT_CC3) == SET) {
        port = PWM10;
        TIM_ClearITPendingBit(TIM1, TIM_IT_CC3);
        pwmPorts[port].callback(port, TIM_GetCapture4(TIM1));
    } else if (TIM_GetITStatus(TIM1, TIM_IT_CC4) == SET) {
        port = PWM9;
        TIM_ClearITPendingBit(TIM1, TIM_IT_CC4);
        pwmPorts[port].callback(port, TIM_GetCapture4(TIM1));
    }
}
void TIM8_CC_IRQHandler(void)		//TIM8
{
    uint8_t port;

    if (TIM_GetITStatus(TIM8, TIM_IT_CC1) == SET) {
        port = PWM17;
        TIM_ClearITPendingBit(TIM8, TIM_IT_CC1);
        pwmPorts[port].callback(port, TIM_GetCapture1(TIM8));
    } else if (TIM_GetITStatus(TIM8, TIM_IT_CC2) == SET) {
        port = PWM18;
        TIM_ClearITPendingBit(TIM8, TIM_IT_CC2);
        pwmPorts[port].callback(port, TIM_GetCapture4(TIM8));
    } else if (TIM_GetITStatus(TIM8, TIM_IT_CC3) == SET) {
        port = PWM19;
        TIM_ClearITPendingBit(TIM8, TIM_IT_CC3);
        pwmPorts[port].callback(port, TIM_GetCapture4(TIM8));
    } else if (TIM_GetITStatus(TIM8, TIM_IT_CC4) == SET) {
        port = PWM20;
        TIM_ClearITPendingBit(TIM8, TIM_IT_CC4);
        pwmPorts[port].callback(port, TIM_GetCapture4(TIM8));
    }
}

 void TIM2_IRQHandler(void)		//TIM2
{
    int8_t port;


    if (TIM_GetITStatus(TIM2, TIM_IT_CC1) == SET) {
        port = PWM1;
        TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
        pwmPorts[port].callback(port, TIM_GetCapture1(TIM2));
    } else if (TIM_GetITStatus(TIM2, TIM_IT_CC2) == SET) {
        port = PWM2;
        TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
        pwmPorts[port].callback(port, TIM_GetCapture2(TIM2));
    }
}


 void TIM3_IRQHandler(void)		//TIM3
{
    int8_t port;


    if (TIM_GetITStatus(TIM3, TIM_IT_CC1) == SET) {
        port = PWM3;
        TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
        pwmPorts[port].callback(port, TIM_GetCapture1(TIM3));
    } else if (TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET) {
        port = PWM4;
        TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
        pwmPorts[port].callback(port, TIM_GetCapture2(TIM3));
    }
}
 void TIM4_IRQHandler(void)		//TIM4
{
    int8_t port;


    if (TIM_GetITStatus(TIM4, TIM_IT_CC1) == SET) {
        port = PWM5;
        TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);
        pwmPorts[port].callback(port, TIM_GetCapture1(TIM4));
    } else if (TIM_GetITStatus(TIM4, TIM_IT_CC2) == SET) {
        port = PWM6;
        TIM_ClearITPendingBit(TIM4, TIM_IT_CC2);
        pwmPorts[port].callback(port, TIM_GetCapture2(TIM4));
    } else if (TIM_GetITStatus(TIM4, TIM_IT_CC3) == SET) {
        port = PWM7;
        TIM_ClearITPendingBit(TIM4, TIM_IT_CC3);
        pwmPorts[port].callback(port, TIM_GetCapture3(TIM4));
    } else if (TIM_GetITStatus(TIM4, TIM_IT_CC4) == SET) {
        port = PWM8;
        TIM_ClearITPendingBit(TIM4, TIM_IT_CC4);
        pwmPorts[port].callback(port, TIM_GetCapture4(TIM4));
    }
}

 void TIM5_IRQHandler(void)		//TIM5
{
    int8_t port;


    if (TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET) {
        port = PWM16;
        TIM_ClearITPendingBit(TIM5, TIM_IT_CC1);
        pwmPorts[port].callback(port, TIM_GetCapture1(TIM5));
    } else if (TIM_GetITStatus(TIM5, TIM_IT_CC2) == SET) {
        port = PWM15;
        TIM_ClearITPendingBit(TIM5, TIM_IT_CC2);
        pwmPorts[port].callback(port, TIM_GetCapture2(TIM5));
    } else if (TIM_GetITStatus(TIM5, TIM_IT_CC3) == SET) {
        port = PWM14;
        TIM_ClearITPendingBit(TIM5, TIM_IT_CC3);
        pwmPorts[port].callback(port, TIM_GetCapture3(TIM5));
    } else if (TIM_GetITStatus(TIM5, TIM_IT_CC4) == SET) {
        port = PWM13;
        TIM_ClearITPendingBit(TIM5, TIM_IT_CC4);
        pwmPorts[port].callback(port, TIM_GetCapture4(TIM5));
    }
}
static void ppmCallback(uint8_t port, uint16_t capture)
{
    uint16_t diff;
    static uint16_t now;
    static uint16_t last = 0;
    static uint8_t chan = 0;

    last = now;
    now = capture;
    diff = now - last;

    if (diff > 2700) { // Per  "So, if you use 2.5ms or higher as being the reset for the PPM stream start, you will be fine. I use 2.7ms just to be safe."
        chan = 0;
    } else {
        if (diff > 750 && diff < 2250 && chan < 8) {   // 750 to 2250 ms is our 'valid' channel range
            captures[chan] = diff;
        }
        chan++;
        failsafeCnt = 0;
    }
}

static void pwmCallback(uint8_t port, uint16_t capture)
{
    if (pwmPorts[port].state == 0) {
        pwmPorts[port].rise = capture;
        pwmPorts[port].state = 1;
        pwmICConfig(timerHardware[port].tim, timerHardware[port].channel, TIM_ICPolarity_Falling);
    } else {
        pwmPorts[port].fall = capture;
        // compute capture
        pwmPorts[port].capture = pwmPorts[port].fall - pwmPorts[port].rise;
        captures[pwmPorts[port].channel] = pwmPorts[port].capture;
        // switch state
        pwmPorts[port].state = 0;
        pwmICConfig(timerHardware[port].tim, timerHardware[port].channel, TIM_ICPolarity_Rising);
        // reset failsafe
        failsafeCnt = 0;
    }
}

bool pwmInit(drv_pwm_config_t *init)
{
    int i = 0;
    const uint8_t *setup;

    // this is pretty hacky shit, but it will do for now. array of 4 config maps, [ multiPWM multiPPM airPWM airPPM ]
    if (init->airplane)
        i = 2; // switch to air hardware config
    if (init->usePPM)
        i++; // next index is for PPM

    setup = hardwareMaps[i];

    for (i = 0; i < MAX_PORTS; i++) {
        uint8_t port = setup[i] & 0x0F;
        uint8_t mask = setup[i] & 0xF0;

        if (setup[i] == 0xFF) // terminator
            break;
        if (mask & TYPE_IP) {
            pwmInConfig(port, ppmCallback, 0);
            numInputs = 8;
        } else if (mask & TYPE_IW) {
            pwmInConfig(port, pwmCallback, numInputs);
            numInputs++;
        } else if (mask & TYPE_M) {
            motors[numMotors++] = pwmOutConfig(port, 1000000 / init->motorPwmRate, PULSE_1MS);
        } else if (mask & TYPE_S) {
            servos[numServos++] = pwmOutConfig(port, 1000000 / init->servoPwmRate, PULSE_1MS);
        }
    }

    return false;
}

void pwmWriteMotor(uint8_t index, uint16_t value)
{
    if (index < numMotors)
        *motors[index]->ccr = value;
}

void pwmWriteServo(uint8_t index, uint16_t value)
{
    if (index < numServos)
        *servos[index]->ccr = value;
}

uint16_t pwmRead(uint8_t channel)
{
    return captures[channel];
}
Razek
mahowik:

тогда что он фильтрует 1D?

Я так сильно не капал калман в афрофлайте, но похоже это вариации на тему Альфа-Бета фильтра. Фильтровать он фильтрует хорошо, но данные не фузионит.
И основная проблема это как узнать характеристику шума сигнала который нужно фильтровать, для корректной настройки калмана.

Вот нашел семпл калмана для атмеги sites.google.com/…/kalman_filter_by_Jordi.txt