1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-24 00:35:39 +03:00
betaflight/src/drv_pwm.c
timecop@gmail.com f2a931d248 OLIMEXINO - Initial support for the OLIMEXINO board.
PWM and ADC configuration is the same as the naze32 for now.  Requires an accelerometer is connected via I2C2.
I2C signals SDA2 and SCL2 are on the UEXT header (Pins 6 and 5 respectively)
USART signals TX and RX are on the CON3/CON4 headers (TX = D7, RX = D8)
Onboard LEDs (LED1/LED2) working.
Buzzer support removed for now as it conflicted with PWM map and LEDs.
mma845x support disabled as it conflicted with an LED.
Relocated some NAZE specific comments.

git-svn-id: https://afrodevices.googlecode.com/svn/trunk/baseflight@302 7c89a4a9-59b9-e629-4cfe-3a2d53b20e61
2013-04-07 13:18:37 +00:00

513 lines
16 KiB
C
Executable file

#include "board.h"
#define PULSE_1MS (1000) // 1ms pulse width
/* FreeFlight/Naze32 timer layout
TIM2_CH1 RC1 PWM1
TIM2_CH2 RC2 PWM2
TIM2_CH3 RC3/UA2_TX PWM3
TIM2_CH4 RC4/UA2_RX PWM4
TIM3_CH1 RC5 PWM5
TIM3_CH2 RC6 PWM6
TIM3_CH3 RC7 PWM7
TIM3_CH4 RC8 PWM8
TIM1_CH1 PWM1 PWM9
TIM1_CH4 PWM2 PWM10
TIM4_CH1 PWM3 PWM11
TIM4_CH2 PWM4 PWM12
TIM4_CH3 PWM5 PWM13
TIM4_CH4 PWM6 PWM14
// RX1 TIM2_CH1 PA0 [also PPM] [also used for throttle calibration]
// RX2 TIM2_CH2 PA1
// RX3 TIM2_CH3 PA2 [also UART2_TX]
// RX4 TIM2_CH4 PA3 [also UART2_RX]
// RX5 TIM3_CH1 PA6 [also ADC_IN6]
// RX6 TIM3_CH2 PA7 [also ADC_IN7]
// RX7 TIM3_CH3 PB0 [also ADC_IN8]
// RX8 TIM3_CH4 PB1 [also ADC_IN9]
// Outputs
// PWM1 TIM1_CH1 PA8
// PWM2 TIM1_CH4 PA11
// PWM3 TIM4_CH1 PB6? [also I2C1_SCL]
// PWM4 TIM4_CH2 PB7 [also I2C1_SDA]
// PWM5 TIM4_CH3 PB8
// PWM6 TIM4_CH4 PB9
Groups that allow running different period (ex 50Hz servos + 400Hz throttle + etc):
TIM2 4 channels
TIM3 4 channels
TIM1 2 channels
TIM4 4 channels
Configuration maps:
1) multirotor PPM input
PWM1 used for PPM
PWM5..8 used for motors
PWM9..10 used for servo or else motors
PWM11..14 used for motors
2) multirotor PPM input with more servos
PWM1 used for PPM
PWM5..8 used for motors
PWM9..10 used for servo or else motors
PWM11..14 used for servos
2) multirotor PWM input
PWM1..8 used for input
PWM9..10 used for servo or else motors
PWM11..14 used for motors
3) airplane / flying wing w/PWM
PWM1..8 used for input
PWM9 used for motor throttle +PWM10 for 2nd motor
PWM11.14 used for servos
4) airplane / flying wing with PPM
PWM1 used for PPM
PWM5..8 used for servos
PWM9 used for motor throttle +PWM10 for 2nd motor
PWM11.14 used for servos
*/
typedef void pwmCallbackPtr(uint8_t port, uint16_t capture);
static pwmHardware_t timerHardware[] = {
{ TIM2, GPIOA, GPIO_Pin_0, TIM_Channel_1, TIM2_IRQn, 0, }, // PWM1
{ TIM2, GPIOA, GPIO_Pin_1, TIM_Channel_2, TIM2_IRQn, 0, }, // PWM2
{ TIM2, GPIOA, GPIO_Pin_2, TIM_Channel_3, TIM2_IRQn, 0, }, // PWM3
{ TIM2, GPIOA, GPIO_Pin_3, TIM_Channel_4, TIM2_IRQn, 0, }, // PWM4
{ TIM3, GPIOA, GPIO_Pin_6, TIM_Channel_1, TIM3_IRQn, 0, }, // PWM5
{ TIM3, GPIOA, GPIO_Pin_7, TIM_Channel_2, TIM3_IRQn, 0, }, // PWM6
{ TIM3, GPIOB, GPIO_Pin_0, TIM_Channel_3, TIM3_IRQn, 0, }, // PWM7
{ TIM3, GPIOB, GPIO_Pin_1, TIM_Channel_4, TIM3_IRQn, 0, }, // PWM8
{ TIM1, GPIOA, GPIO_Pin_8, TIM_Channel_1, TIM1_CC_IRQn, 1, }, // PWM9
{ TIM1, GPIOA, GPIO_Pin_11, TIM_Channel_4, TIM1_CC_IRQn, 1, }, // PWM10
{ TIM4, GPIOB, GPIO_Pin_6, TIM_Channel_1, TIM4_IRQn, 0, }, // PWM11
{ TIM4, GPIOB, GPIO_Pin_7, TIM_Channel_2, TIM4_IRQn, 0, }, // PWM12
{ TIM4, GPIOB, GPIO_Pin_8, TIM_Channel_3, TIM4_IRQn, 0, }, // PWM13
{ TIM4, GPIOB, GPIO_Pin_9, TIM_Channel_4, TIM4_IRQn, 0, }, // PWM14
};
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[] = {
PWM1 | TYPE_IP, // PPM input
PWM9 | TYPE_M, // Swap to servo if needed
PWM10 | TYPE_M, // Swap to servo if needed
PWM11 | TYPE_M,
PWM12 | TYPE_M,
PWM13 | TYPE_M,
PWM14 | TYPE_M,
PWM5 | TYPE_M, // Swap to servo if needed
PWM6 | TYPE_M, // Swap to servo if needed
PWM7 | TYPE_M, // Swap to servo if needed
PWM8 | TYPE_M, // Swap to servo if needed
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, // motor #1 or servo #1 (swap to servo if needed)
PWM10 | TYPE_M, // motor #2 or servo #2 (swap to servo if needed)
PWM11 | TYPE_M, // motor #1 or #3
PWM12 | TYPE_M,
PWM13 | TYPE_M,
PWM14 | TYPE_M, // motor #4 or #6
0xFF
};
static const uint8_t airPPM[] = {
PWM1 | TYPE_IP, // PPM input
PWM9 | TYPE_M, // motor #1
PWM10 | TYPE_M, // motor #2
PWM11 | TYPE_S, // servo #1
PWM12 | TYPE_S,
PWM13 | TYPE_S,
PWM14 | TYPE_S, // servo #4
PWM5 | TYPE_S, // servo #5
PWM6 | TYPE_S,
PWM7 | TYPE_S,
PWM8 | TYPE_S, // servo #8
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_S, // servo #1
PWM12 | TYPE_S,
PWM13 | TYPE_S,
PWM14 | TYPE_S, // servo #4
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_IPD;
else
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_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 only on TIM1
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)
{
uint8_t port;
if (TIM_GetITStatus(TIM1, TIM_IT_CC1) == SET) {
port = PWM9;
TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);
pwmPorts[port].callback(port, TIM_GetCapture1(TIM1));
} else if (TIM_GetITStatus(TIM1, TIM_IT_CC4) == SET) {
port = PWM10;
TIM_ClearITPendingBit(TIM1, TIM_IT_CC4);
pwmPorts[port].callback(port, TIM_GetCapture4(TIM1));
}
}
static void pwmTIMxHandler(TIM_TypeDef *tim, uint8_t portBase)
{
int8_t port;
// Generic CC handler for TIM2,3,4
if (TIM_GetITStatus(tim, TIM_IT_CC1) == SET) {
port = portBase + 0;
TIM_ClearITPendingBit(tim, TIM_IT_CC1);
pwmPorts[port].callback(port, TIM_GetCapture1(tim));
} else if (TIM_GetITStatus(tim, TIM_IT_CC2) == SET) {
port = portBase + 1;
TIM_ClearITPendingBit(tim, TIM_IT_CC2);
pwmPorts[port].callback(port, TIM_GetCapture2(tim));
} else if (TIM_GetITStatus(tim, TIM_IT_CC3) == SET) {
port = portBase + 2;
TIM_ClearITPendingBit(tim, TIM_IT_CC3);
pwmPorts[port].callback(port, TIM_GetCapture3(tim));
} else if (TIM_GetITStatus(tim, TIM_IT_CC4) == SET) {
port = portBase + 3;
TIM_ClearITPendingBit(tim, TIM_IT_CC4);
pwmPorts[port].callback(port, TIM_GetCapture4(tim));
}
}
void TIM2_IRQHandler(void)
{
pwmTIMxHandler(TIM2, PWM1); // PWM1..4
}
void TIM3_IRQHandler(void)
{
pwmTIMxHandler(TIM3, PWM5); // PWM5..8
}
void TIM4_IRQHandler(void)
{
pwmTIMxHandler(TIM4, PWM11); // PWM11..14
}
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 http://www.rcgroups.com/forums/showpost.php?p=21996147&postcount=3960 "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;
#ifdef OLIMEXINO
// PWM2 is connected to LED2 on the board and cannot be connected.
if (port == PWM2)
continue;
#endif
// skip UART ports for GPS
if (init->useUART && (port == PWM3 || port == PWM4))
continue;
// skip ADC for powerMeter if configured
if (init->adcChannel && (init->adcChannel == port))
continue;
// hacks to allow current functionality
if (mask & (TYPE_IP | TYPE_IW) && !init->enableInput)
mask = 0;
if (init->useServos && !init->airplane) {
// remap PWM9+10 as servos (but not in airplane mode LOL)
if (port == PWM9 || port == PWM10)
mask = TYPE_S;
}
if (init->extraServos && !init->airplane) {
// remap PWM5..8 as servos when used in extended servo mode
if (port >= PWM5 && port <= PWM8)
mask = TYPE_S;
}
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];
}