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

SergDoc

у YS-X4 тоже внешний АЦП и у AutoQuad и Мультипилота есть два ИМУ различных на аналоговых датчиках, на всем известных IDG и ISZ и на дорогиж гирах и у всех внешний АЦП…

Sir_Alex
rual:

Еще говорят у МПУ есть режим трансляции компаса из собственной и2ц во внутренние регистры - был бы отличный вариант совсем без и2ц.

Есть в исходниках multiwii, можно подсмотреть, правда там и сам MPU работает через I2C…

SergDoc

Ковыряюсь в AeroQuad32 никак не могу понять, AeroQuad - ардуино, но 32 не могу разобраться, поддерживаемые платы f1 f4 DiscoveryF4 и FF - та которую Таймкоп в назе32 первой версии превратил, код довольно аккуратный, но блин под что? попробовать в само собранную среду на eclipse запихнуть ?

oleg70
rual:

все данные ПДП разложит в ОЗУ

Кстати… (сейчас подошел к датчикам) стоит ли мне связываться с ПДП ? По моему должно быстрее работать, т.к. отвлекать проц. совсем не придется (но опыта НЕТ и поэтому сомневаюсь… грабли, какие ?).
Т.е. прерывание от гиро " толкает" ПДП ? или SPI ?
Как выстроить алгоритм по Вашему?

Razek
SergDoc:

Ковыряюсь в AeroQuad32 никак не могу понять, AeroQuad - ардуино

Она кросплатформенная правда это кросплатформенность на дно их тянет

SergDoc
Razek:

Она кросплатформенная

ага, надо ардуино 1 и выше, maple ide в пролёте, а потом спрашивает - а где ваши STM?, а где бутлоадер в проце? а есть там только на ф103, ну и т.д. буду ковырять дальше…

Razek

Либы там маполвские переписанные под F407. Все идет в дистре исходников. Компилер CodeSourcery ARM EABI (2011.09-69) новый криво компилирует, бутлоадер, да смотря чем заливать прошивки собираешься, могу скинуть оригинальный бутлоадер. Да кстати все что не 407 и не ардуино 100% поддержки нет.

SergDoc

надо всё-таки под eclipse (ide с поддержкой мапловских библиотек) перетянуть - будет счастье…

Razek

Гробага будет скорей всего, надо эклипс настроить на CodeSourcery компилер

rual
oleg70:

Т.е. прерывание от гиро " толкает" ПДП ? или SPI ? Как выстроить алгоритм по Вашему?

Уменя так: готовность ДУС-вн.прерывание-запуск ПДП-прерывание окнчания ПДП-расчет ДУС

Razek:

правда это кросплатформенность на дно их тянет

Ну эт смотря как писать, ну и не скрешивать бульдога с носорогом (АВР и АРМ). У меня, допустим, достаточно 2 файла заменить для перехода между СТМами, и 3й файл если менять датчики.

Alexsis1109

Подскажите, кто знает наверняка, в Atmega128 есть таймер с функцией захвата? Я смотрел в pdf, там есть много функций и есть что-то подобное на захват. Но оно ли это?

oleg70
Alexsis1109:

и есть что-то подобное на захват

Захват у таймеров 1 и 3 , вход называется “input capture”

Razek
rual:

Ну эт смотря как писать, ну и не скрешивать бульдога с носорогом

Да как не пиши, а все равно написать код который будет работать и на АВР и на АРМ требует большего времени чем просто на что-то одно да и за универсальность всегда приходится платить или функционалом или производительностью.

SergDoc

короче да не получилось, ну что ж буду сам собирать проект… когдато ж надо начинать учится 😃

RaJa
SergDoc:

а по поводу с чего всё начиналось: один человек мне пообещал опупенный алгоритм, а как только я начал делать что-то, утёк в кусты, вот и мучаюсь 😃

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

Alexsis1109
oleg70:

Захват у таймеров 1 и 3 , вход называется “input capture”

спасибо. т.е. их два. а для считываня шимов с каналов достаточно только одного таймера? его просто по событиям переключать, сначало из 0 в 1 , потом из 1 в 0?

Sir_Alex
Alexsis1109:

а для считываня шимов с каналов достаточно только одного таймера? его просто по событиям переключать, сначало из 0 в 1 , потом из 1 в 0?

Я уже писал вам, одного ICP(Input Capture Pin) хватит для обработки PPM(CPPM) сигнала. Для этого достаточно ловить только Rising Edge (из 0 в 1). Если вам надо каждый канал по отдельности обрабатывать, то разумеется вам надо столько ICP пинов, сколько у вас каналов и в этом случае надо его переключать из 0 в 1 и 1 в 0.

oleg70
Alexsis1109:

спасибо. т.е. их два.

Если Вы хотите сделать “систему стабилизации для моделей” то аппаратных ресурсов чтения ШИМ Вам не хватит (на таймерах AVR по захвату),
я Вам выше постом изложил приблизительный алгоритм чтения по прерыванию на “ноге” где счетчик используется в обычном режиме измерения длительности, его надо по положительному фронту импульса запустить и по отрицательному фронту остановить…

rual:

Уменя так: готовность ДУС-вн.прерывание-запуск ПДП-прерывание окнчания ПДП-расчет ДУС …

В Вашем проекте нашел функции по ДЕинициализации петли ДУСа, каково их применение ?
И еще, я так понял, что для чтения акселя и магнетометра ПДП не применяется (?).

Alexsis1109
Sir_Alex:

Если вам надо каждый канал по отдельности обрабатывать, то разумеется вам надо столько ICP пинов, сколько у вас каналов и в этом случае надо его переключать из 0 в 1 и 1 в 0.

вот на примере приемника Futaba 7C, по идее выход - РРМ сигнал, но опрашивать же нужно каждый канал в отдельности?

DVE

Именно поэтому в первых контроллерах так любили приемники Спектрум - у них весь PPM идет по одному проводу, что-то типа такого.
api.ning.com/…/RC_Receiver_Timing_Diagram1.jpg

Для Футабы есть приемники FsSky, которые можно переключить в режим PPM sum.

Geniok

Всем добрый вечер.
камень stm32f407.
Возник такой вопрос, переписал захват ШИМ так, чтобы использовать только 1 канал таймера, вместо двух, как советует даташит.
Вроде бы все хорошо, данные захватываются верно, но примерно раз 1 сек. идет сбой. как я понимаю, происходит это в тот момент, когда происходит переполнение счетчика таймера. Его обрабатываю следующим образом:
// newData - значение счетчика в момент спада
// oldData - значение счетчика в момент фронта

if(newData > oldData)
{
// считаем разницу тиков между подъемом и спадом
capture = newData - oldData;
}
else
{
// считаем разницу тиков между подъмом и спадом с учетом что между этими 2-мя событиями было переполнение счетчика таймера
capture = (UINT16_MAX - oldData) + newData;
}

Такое условие не помогает. Кто как обрабатывает данную ситуацию ?

oleg70

А как это Вы так один канал настроили, что он оба фронта ловит,
если не секрет…?

Geniok
oleg70:

А как это Вы так один канал настроили, что он оба фронта ловит,
если не секрет…?

Не секрет конечно.

вот настройка канала:

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-х периодов. Вот ее мне и надо убрать…

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

Alexsis1109
Geniok:

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

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

SergDoc

так как сейчас ковыряюсь с 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