mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-01 09:24:10 +01:00
Make PWM/PPM and Servo drivers play nicely together
PWM and PPM can now coexist in the same load and be selected at boot time via the hwsettings UAVObject. This is basically a complete restructuring of the way the drivers interact with the TIM peripheral in the STM32. As a side effect, the PWM and PPM drivers are now ready to support multiple instances of each. This also provides the first step toward being able to reassign some of the PWM input pins to be servo output pins. Still more work required, but this is a good start.
This commit is contained in:
parent
b3c43da90a
commit
2f86e4dd4f
@ -194,6 +194,7 @@ SRC += $(PIOSSTM32F10X)/pios_gpio.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_exti.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_rtc.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_wdg.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_tim.c
|
||||
|
||||
|
||||
# PIOS USB related files (seperated to make code maintenance more easy)
|
||||
|
@ -48,7 +48,7 @@
|
||||
/* Supported receiver interfaces */
|
||||
#define PIOS_INCLUDE_SPEKTRUM
|
||||
#define PIOS_INCLUDE_SBUS
|
||||
//#define PIOS_INCLUDE_PPM
|
||||
#define PIOS_INCLUDE_PPM
|
||||
#define PIOS_INCLUDE_PWM
|
||||
#define PIOS_INCLUDE_GCSRCVR
|
||||
|
||||
|
@ -196,6 +196,220 @@ void PIOS_ADC_handler() {
|
||||
PIOS_ADC_DMA_Handler();
|
||||
}
|
||||
|
||||
#include "pios_tim_priv.h"
|
||||
|
||||
static const TIM_TimeBaseInitTypeDef tim_1_2_3_4_time_base = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1,
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = ((1000000 / PIOS_SERVO_UPDATE_HZ) - 1),
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
};
|
||||
|
||||
static const struct pios_tim_clock_cfg tim_1_cfg = {
|
||||
.timer = TIM1,
|
||||
.time_base_init = &tim_1_2_3_4_time_base,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannel = TIM1_CC_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pios_tim_clock_cfg tim_2_cfg = {
|
||||
.timer = TIM2,
|
||||
.time_base_init = &tim_1_2_3_4_time_base,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannel = TIM2_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pios_tim_clock_cfg tim_3_cfg = {
|
||||
.timer = TIM3,
|
||||
.time_base_init = &tim_1_2_3_4_time_base,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannel = TIM3_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pios_tim_clock_cfg tim_4_cfg = {
|
||||
.timer = TIM4,
|
||||
.time_base_init = &tim_1_2_3_4_time_base,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannel = TIM4_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pios_tim_channel pios_tim_rcvrport_all_channels[] = {
|
||||
{
|
||||
.timer = TIM4,
|
||||
.timer_chan = TIM_Channel_1,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_6,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.timer_chan = TIM_Channel_2,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_5,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
.remap = GPIO_PartialRemap_TIM3,
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.timer_chan = TIM_Channel_3,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_0,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.timer_chan = TIM_Channel_4,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_1,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM2,
|
||||
.timer_chan = TIM_Channel_1,
|
||||
.pin = {
|
||||
.gpio = GPIOA,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_0,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM2,
|
||||
.timer_chan = TIM_Channel_2,
|
||||
.pin = {
|
||||
.gpio = GPIOA,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_1,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pios_tim_channel pios_tim_servoport_all_pins[] = {
|
||||
{
|
||||
.timer = TIM4,
|
||||
.timer_chan = TIM_Channel_4,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_9,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM4,
|
||||
.timer_chan = TIM_Channel_3,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_8,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM4,
|
||||
.timer_chan = TIM_Channel_2,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_7,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM1,
|
||||
.timer_chan = TIM_Channel_1,
|
||||
.pin = {
|
||||
.gpio = GPIOA,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_8,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.timer_chan = TIM_Channel_1,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_4,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
.remap = GPIO_PartialRemap_TIM3,
|
||||
},
|
||||
{
|
||||
.timer = TIM2,
|
||||
.timer_chan = TIM_Channel_3,
|
||||
.pin = {
|
||||
.gpio = GPIOA,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_2,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#if defined(PIOS_INCLUDE_USART)
|
||||
|
||||
#include "pios_usart_priv.h"
|
||||
@ -565,121 +779,40 @@ void PIOS_RTC_IRQ_Handler (void)
|
||||
* Servo outputs
|
||||
*/
|
||||
#include <pios_servo_priv.h>
|
||||
static const struct pios_servo_channel pios_servo_channels[] = {
|
||||
{
|
||||
.timer = TIM4,
|
||||
.port = GPIOB,
|
||||
.channel = TIM_Channel_4,
|
||||
.pin = GPIO_Pin_9,
|
||||
},
|
||||
{
|
||||
.timer = TIM4,
|
||||
.port = GPIOB,
|
||||
.channel = TIM_Channel_3,
|
||||
.pin = GPIO_Pin_8,
|
||||
},
|
||||
{
|
||||
.timer = TIM4,
|
||||
.port = GPIOB,
|
||||
.channel = TIM_Channel_2,
|
||||
.pin = GPIO_Pin_7,
|
||||
},
|
||||
{
|
||||
.timer = TIM1,
|
||||
.port = GPIOA,
|
||||
.channel = TIM_Channel_1,
|
||||
.pin = GPIO_Pin_8,
|
||||
},
|
||||
{ /* needs to remap to alternative function */
|
||||
.timer = TIM3,
|
||||
.port = GPIOB,
|
||||
.channel = TIM_Channel_1,
|
||||
.pin = GPIO_Pin_4,
|
||||
},
|
||||
{
|
||||
.timer = TIM2,
|
||||
.port = GPIOA,
|
||||
.channel = TIM_Channel_3,
|
||||
.pin = GPIO_Pin_2,
|
||||
},
|
||||
};
|
||||
|
||||
const struct pios_servo_cfg pios_servo_cfg = {
|
||||
.tim_base_init = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1,
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = ((1000000 / PIOS_SERVO_UPDATE_HZ) - 1),
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
},
|
||||
.tim_oc_init = {
|
||||
.TIM_OCMode = TIM_OCMode_PWM1,
|
||||
.TIM_OutputState = TIM_OutputState_Enable,
|
||||
.TIM_OutputNState = TIM_OutputNState_Disable,
|
||||
.TIM_Pulse = PIOS_SERVOS_INITIAL_POSITION,
|
||||
.TIM_Pulse = PIOS_SERVOS_INITIAL_POSITION,
|
||||
.TIM_OCPolarity = TIM_OCPolarity_High,
|
||||
.TIM_OCNPolarity = TIM_OCPolarity_High,
|
||||
.TIM_OCIdleState = TIM_OCIdleState_Reset,
|
||||
.TIM_OCNIdleState = TIM_OCNIdleState_Reset,
|
||||
},
|
||||
.gpio_init = {
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
.remap = GPIO_PartialRemap_TIM3,
|
||||
.channels = pios_servo_channels,
|
||||
.num_channels = NELEMENTS(pios_servo_channels),
|
||||
.channels = pios_tim_servoport_all_pins,
|
||||
.num_channels = NELEMENTS(pios_tim_servoport_all_pins),
|
||||
};
|
||||
|
||||
#if defined(PIOS_INCLUDE_PWM) && defined(PIOS_INCLUDE_PPM)
|
||||
#error Cannot define both PIOS_INCLUDE_PWM and PIOS_INCLUDE_PPM at the same time (yet)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* PPM Inputs
|
||||
*/
|
||||
#if defined(PIOS_INCLUDE_PPM)
|
||||
#include <pios_ppm_priv.h>
|
||||
|
||||
void TIM4_IRQHandler();
|
||||
void TIM4_IRQHandler() __attribute__ ((alias ("PIOS_TIM4_irq_handler")));
|
||||
const struct pios_ppm_cfg pios_ppm_cfg = {
|
||||
.tim_base_init = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1, /* For 1 uS accuracy */
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = 0xFFFF, /* shared timer, make sure init correctly in outputs */
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
},
|
||||
.tim_ic_init = {
|
||||
.TIM_Channel = TIM_Channel_1,
|
||||
.TIM_ICPolarity = TIM_ICPolarity_Rising,
|
||||
.TIM_ICSelection = TIM_ICSelection_DirectTI,
|
||||
.TIM_ICPrescaler = TIM_ICPSC_DIV1,
|
||||
.TIM_ICFilter = 0x0,
|
||||
},
|
||||
.gpio_init = {
|
||||
.GPIO_Pin = GPIO_Pin_6,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
.remap = 0,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
.timer = TIM4,
|
||||
.port = GPIOB,
|
||||
.ccr = TIM_IT_CC1,
|
||||
/* Use only the first channel for ppm */
|
||||
.channels = &pios_tim_rcvrport_all_channels[0],
|
||||
.num_channels = 1,
|
||||
};
|
||||
|
||||
void PIOS_TIM4_irq_handler()
|
||||
{
|
||||
PIOS_PPM_irq_handler();
|
||||
}
|
||||
#endif /* PIOS_INCLUDE_PPM */
|
||||
|
||||
/*
|
||||
@ -688,98 +821,16 @@ void PIOS_TIM4_irq_handler()
|
||||
#if defined(PIOS_INCLUDE_PWM)
|
||||
#include <pios_pwm_priv.h>
|
||||
|
||||
static const struct pios_pwm_channel pios_pwm_channels[] = {
|
||||
{
|
||||
.timer = TIM4,
|
||||
.port = GPIOB,
|
||||
.ccr = TIM_IT_CC1,
|
||||
.channel = TIM_Channel_1,
|
||||
.pin = GPIO_Pin_6,
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.port = GPIOB,
|
||||
.ccr = TIM_IT_CC2,
|
||||
.channel = TIM_Channel_2,
|
||||
.pin = GPIO_Pin_5,
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.port = GPIOB,
|
||||
.ccr = TIM_IT_CC3,
|
||||
.channel = TIM_Channel_3,
|
||||
.pin = GPIO_Pin_0
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.port = GPIOB,
|
||||
.ccr = TIM_IT_CC4,
|
||||
.channel = TIM_Channel_4,
|
||||
.pin = GPIO_Pin_1,
|
||||
},
|
||||
{
|
||||
.timer = TIM2,
|
||||
.port = GPIOA,
|
||||
.ccr = TIM_IT_CC1,
|
||||
.channel = TIM_Channel_1,
|
||||
.pin = GPIO_Pin_0,
|
||||
},
|
||||
{
|
||||
.timer = TIM2,
|
||||
.port = GPIOA,
|
||||
.ccr = TIM_IT_CC2,
|
||||
.channel = TIM_Channel_2,
|
||||
.pin = GPIO_Pin_1,
|
||||
},
|
||||
};
|
||||
|
||||
void TIM2_IRQHandler();
|
||||
void TIM3_IRQHandler();
|
||||
void TIM4_IRQHandler();
|
||||
void TIM2_IRQHandler() __attribute__ ((alias ("PIOS_TIM2_irq_handler")));
|
||||
void TIM3_IRQHandler() __attribute__ ((alias ("PIOS_TIM3_irq_handler")));
|
||||
void TIM4_IRQHandler() __attribute__ ((alias ("PIOS_TIM4_irq_handler")));
|
||||
const struct pios_pwm_cfg pios_pwm_cfg = {
|
||||
.tim_base_init = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1,
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = 0xFFFF,
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
},
|
||||
.tim_ic_init = {
|
||||
.TIM_ICPolarity = TIM_ICPolarity_Rising,
|
||||
.TIM_ICSelection = TIM_ICSelection_DirectTI,
|
||||
.TIM_ICPrescaler = TIM_ICPSC_DIV1,
|
||||
.TIM_ICFilter = 0x0,
|
||||
.TIM_ICFilter = 0x0,
|
||||
},
|
||||
.gpio_init = {
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
.remap = 0,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
.channels = pios_pwm_channels,
|
||||
.num_channels = NELEMENTS(pios_pwm_channels),
|
||||
.channels = pios_tim_rcvrport_all_channels,
|
||||
.num_channels = NELEMENTS(pios_tim_rcvrport_all_channels),
|
||||
};
|
||||
void PIOS_TIM2_irq_handler()
|
||||
{
|
||||
PIOS_PWM_irq_handler(TIM2);
|
||||
}
|
||||
void PIOS_TIM3_irq_handler()
|
||||
{
|
||||
PIOS_PWM_irq_handler(TIM3);
|
||||
}
|
||||
void PIOS_TIM4_irq_handler()
|
||||
{
|
||||
PIOS_PWM_irq_handler(TIM4);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PIOS_INCLUDE_I2C)
|
||||
@ -931,6 +982,12 @@ void PIOS_Board_Init(void) {
|
||||
/* Initialize the task monitor library */
|
||||
TaskMonitorInitialize();
|
||||
|
||||
/* Set up pulse timers */
|
||||
PIOS_TIM_InitClock(&tim_1_cfg);
|
||||
PIOS_TIM_InitClock(&tim_2_cfg);
|
||||
PIOS_TIM_InitClock(&tim_3_cfg);
|
||||
PIOS_TIM_InitClock(&tim_4_cfg);
|
||||
|
||||
/* Configure the main IO port */
|
||||
uint8_t hwsettings_DSMxBind;
|
||||
HwSettingsDSMxBindGet(&hwsettings_DSMxBind);
|
||||
@ -1114,22 +1171,30 @@ void PIOS_Board_Init(void) {
|
||||
break;
|
||||
case HWSETTINGS_RCVRPORT_PWM:
|
||||
#if defined(PIOS_INCLUDE_PWM)
|
||||
PIOS_PWM_Init();
|
||||
uint32_t pios_pwm_rcvr_id;
|
||||
if (PIOS_RCVR_Init(&pios_pwm_rcvr_id, &pios_pwm_rcvr_driver, 0)) {
|
||||
PIOS_Assert(0);
|
||||
{
|
||||
uint32_t pios_pwm_id;
|
||||
PIOS_PWM_Init(&pios_pwm_id, &pios_pwm_cfg);
|
||||
|
||||
uint32_t pios_pwm_rcvr_id;
|
||||
if (PIOS_RCVR_Init(&pios_pwm_rcvr_id, &pios_pwm_rcvr_driver, pios_pwm_id)) {
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
pios_rcvr_group_map[MANUALCONTROLSETTINGS_CHANNELGROUPS_PWM] = pios_pwm_rcvr_id;
|
||||
}
|
||||
pios_rcvr_group_map[MANUALCONTROLSETTINGS_CHANNELGROUPS_PWM] = pios_pwm_rcvr_id;
|
||||
#endif /* PIOS_INCLUDE_PWM */
|
||||
break;
|
||||
case HWSETTINGS_RCVRPORT_PPM:
|
||||
#if defined(PIOS_INCLUDE_PPM)
|
||||
PIOS_PPM_Init();
|
||||
uint32_t pios_ppm_rcvr_id;
|
||||
if (PIOS_RCVR_Init(&pios_ppm_rcvr_id, &pios_ppm_rcvr_driver, 0)) {
|
||||
PIOS_Assert(0);
|
||||
{
|
||||
uint32_t pios_ppm_id;
|
||||
PIOS_PPM_Init(&pios_ppm_id, &pios_ppm_cfg);
|
||||
|
||||
uint32_t pios_ppm_rcvr_id;
|
||||
if (PIOS_RCVR_Init(&pios_ppm_rcvr_id, &pios_ppm_rcvr_driver, pios_ppm_id)) {
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
pios_rcvr_group_map[MANUALCONTROLSETTINGS_CHANNELGROUPS_PPM] = pios_ppm_rcvr_id;
|
||||
}
|
||||
pios_rcvr_group_map[MANUALCONTROLSETTINGS_CHANNELGROUPS_PPM] = pios_ppm_rcvr_id;
|
||||
#endif /* PIOS_INCLUDE_PPM */
|
||||
break;
|
||||
}
|
||||
@ -1139,14 +1204,19 @@ void PIOS_Board_Init(void) {
|
||||
PIOS_GCSRCVR_Init();
|
||||
uint32_t pios_gcsrcvr_rcvr_id;
|
||||
if (PIOS_RCVR_Init(&pios_gcsrcvr_rcvr_id, &pios_gcsrcvr_rcvr_driver, 0)) {
|
||||
PIOS_Assert(0);
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
pios_rcvr_group_map[MANUALCONTROLSETTINGS_CHANNELGROUPS_GCS] = pios_gcsrcvr_rcvr_id;
|
||||
#endif /* PIOS_INCLUDE_GCSRCVR */
|
||||
|
||||
/* Remap AFIO pin */
|
||||
GPIO_PinRemapConfig( GPIO_Remap_SWJ_NoJTRST, ENABLE);
|
||||
PIOS_Servo_Init();
|
||||
|
||||
#ifndef PIOS_DEBUG_ENABLE_DEBUG_PINS
|
||||
PIOS_Servo_Init(&pios_servo_cfg);
|
||||
#else
|
||||
PIOS_DEBUG_Init(&pios_tim_servo_all_channels, NELEMENTS(pios_tim_servo_all_channels));
|
||||
#endif /* PIOS_DEBUG_ENABLE_DEBUG_PINS */
|
||||
|
||||
PIOS_ADC_Init();
|
||||
PIOS_GPIO_Init();
|
||||
|
@ -24,6 +24,7 @@
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "pios.h"
|
||||
#include "ahrs_spi_comm.h"
|
||||
#include "pios_debug.h"
|
||||
|
||||
|
@ -164,6 +164,7 @@ SRC += $(PIOSSTM32F10X)/pios_ppm.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_pwm.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_spektrum.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_sbus.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_tim.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_debug.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_gpio.c
|
||||
SRC += $(PIOSSTM32F10X)/pios_exti.c
|
||||
|
@ -47,7 +47,7 @@
|
||||
#define PIOS_INCLUDE_SPEKTRUM
|
||||
//#define PIOS_INCLUDE_SBUS
|
||||
#define PIOS_INCLUDE_PWM
|
||||
//#define PIOS_INCLUDE_PPM
|
||||
#define PIOS_INCLUDE_PPM
|
||||
|
||||
#define PIOS_INCLUDE_TELEMETRY_RF
|
||||
|
||||
|
@ -302,6 +302,89 @@ void PIOS_ADC_handler() {
|
||||
PIOS_ADC_DMA_Handler();
|
||||
}
|
||||
|
||||
#include "pios_tim_priv.h"
|
||||
|
||||
static const TIM_TimeBaseInitTypeDef tim_4_8_time_base = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1,
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = ((1000000 / PIOS_SERVO_UPDATE_HZ) - 1),
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
};
|
||||
|
||||
static const struct pios_tim_clock_cfg tim_4_cfg = {
|
||||
.timer = TIM4,
|
||||
.time_base_init = &tim_4_8_time_base,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannel = TIM4_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pios_tim_clock_cfg tim_8_cfg = {
|
||||
.timer = TIM8,
|
||||
.time_base_init = &tim_4_8_time_base,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannel = TIM8_CC_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const TIM_TimeBaseInitTypeDef tim_1_3_5_time_base = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1,
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = 0xFFFF,
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
};
|
||||
|
||||
static const struct pios_tim_clock_cfg tim_1_cfg = {
|
||||
.timer = TIM1,
|
||||
.time_base_init = &tim_1_3_5_time_base,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannel = TIM1_CC_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pios_tim_clock_cfg tim_3_cfg = {
|
||||
.timer = TIM3,
|
||||
.time_base_init = &tim_1_3_5_time_base,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannel = TIM3_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pios_tim_clock_cfg tim_5_cfg = {
|
||||
.timer = TIM5,
|
||||
.time_base_init = &tim_1_3_5_time_base,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannel = TIM5_IRQn,
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#if defined(PIOS_INCLUDE_USART)
|
||||
|
||||
#include "pios_usart_priv.h"
|
||||
@ -536,65 +619,106 @@ static const struct pios_spektrum_cfg pios_spektrum_cfg = {
|
||||
* Pios servo configuration structures
|
||||
*/
|
||||
#include <pios_servo_priv.h>
|
||||
static const struct pios_servo_channel pios_servo_channels[] = {
|
||||
static const struct pios_tim_channel pios_tim_servoport_all_pins[] = {
|
||||
{
|
||||
.timer = TIM4,
|
||||
.port = GPIOB,
|
||||
.channel = TIM_Channel_1,
|
||||
.pin = GPIO_Pin_6,
|
||||
.timer_chan = TIM_Channel_1,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_6,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM4,
|
||||
.port = GPIOB,
|
||||
.channel = TIM_Channel_2,
|
||||
.pin = GPIO_Pin_7,
|
||||
.timer_chan = TIM_Channel_2,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_7,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM4,
|
||||
.port = GPIOB,
|
||||
.channel = TIM_Channel_3,
|
||||
.pin = GPIO_Pin_8,
|
||||
.timer_chan = TIM_Channel_3,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_8,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM4,
|
||||
.port = GPIOB,
|
||||
.channel = TIM_Channel_4,
|
||||
.pin = GPIO_Pin_9,
|
||||
.timer_chan = TIM_Channel_4,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_9,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM8,
|
||||
.port = GPIOC,
|
||||
.channel = TIM_Channel_1,
|
||||
.pin = GPIO_Pin_6,
|
||||
.timer_chan = TIM_Channel_1,
|
||||
.pin = {
|
||||
.gpio = GPIOC,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_6,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM8,
|
||||
.port = GPIOC,
|
||||
.channel = TIM_Channel_2,
|
||||
.pin = GPIO_Pin_7,
|
||||
.timer_chan = TIM_Channel_2,
|
||||
.pin = {
|
||||
.gpio = GPIOC,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_7,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM8,
|
||||
.port = GPIOC,
|
||||
.channel = TIM_Channel_3,
|
||||
.pin = GPIO_Pin_8,
|
||||
.timer_chan = TIM_Channel_3,
|
||||
.pin = {
|
||||
.gpio = GPIOC,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_8,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM8,
|
||||
.port = GPIOC,
|
||||
.channel = TIM_Channel_4,
|
||||
.pin = GPIO_Pin_9,
|
||||
.timer_chan = TIM_Channel_4,
|
||||
.pin = {
|
||||
.gpio = GPIOC,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_9,
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const struct pios_servo_cfg pios_servo_cfg = {
|
||||
.tim_base_init = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1,
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = ((1000000 / PIOS_SERVO_UPDATE_HZ) - 1),
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
},
|
||||
.tim_oc_init = {
|
||||
.TIM_OCMode = TIM_OCMode_PWM1,
|
||||
.TIM_OutputState = TIM_OutputState_Enable,
|
||||
@ -605,13 +729,8 @@ const struct pios_servo_cfg pios_servo_cfg = {
|
||||
.TIM_OCIdleState = TIM_OCIdleState_Reset,
|
||||
.TIM_OCNIdleState = TIM_OCNIdleState_Reset,
|
||||
},
|
||||
.gpio_init = {
|
||||
.GPIO_Mode = GPIO_Mode_AF_PP,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
.remap = 0,
|
||||
.channels = pios_servo_channels,
|
||||
.num_channels = NELEMENTS(pios_servo_channels),
|
||||
.channels = pios_tim_servoport_all_pins,
|
||||
.num_channels = NELEMENTS(pios_tim_servoport_all_pins),
|
||||
};
|
||||
|
||||
|
||||
@ -620,112 +739,117 @@ const struct pios_servo_cfg pios_servo_cfg = {
|
||||
*/
|
||||
#if defined(PIOS_INCLUDE_PWM)
|
||||
#include <pios_pwm_priv.h>
|
||||
static const struct pios_pwm_channel pios_pwm_channels[] = {
|
||||
static const struct pios_tim_channel pios_tim_rcvrport_all_channels[] = {
|
||||
{
|
||||
.timer = TIM1,
|
||||
.port = GPIOA,
|
||||
.ccr = TIM_IT_CC2,
|
||||
.channel = TIM_Channel_2,
|
||||
.pin = GPIO_Pin_9,
|
||||
},
|
||||
.timer_chan = TIM_Channel_2,
|
||||
.pin = {
|
||||
.gpio = GPIOA,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_9,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM1,
|
||||
.port = GPIOA,
|
||||
.ccr = TIM_IT_CC3,
|
||||
.channel = TIM_Channel_3,
|
||||
.pin = GPIO_Pin_10,
|
||||
.timer_chan = TIM_Channel_3,
|
||||
.pin = {
|
||||
.gpio = GPIOA,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_10,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM5,
|
||||
.port = GPIOA,
|
||||
.ccr = TIM_IT_CC1,
|
||||
.channel = TIM_Channel_1,
|
||||
.pin = GPIO_Pin_0
|
||||
.timer_chan = TIM_Channel_1,
|
||||
.pin = {
|
||||
.gpio = GPIOA,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_0,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM1,
|
||||
.port = GPIOA,
|
||||
.ccr = TIM_IT_CC1,
|
||||
.channel = TIM_Channel_1,
|
||||
.pin = GPIO_Pin_8,
|
||||
.timer_chan = TIM_Channel_1,
|
||||
.pin = {
|
||||
.gpio = GPIOA,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_8,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.port = GPIOB,
|
||||
.ccr = TIM_IT_CC4,
|
||||
.channel = TIM_Channel_4,
|
||||
.pin = GPIO_Pin_1,
|
||||
.timer_chan = TIM_Channel_4,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_1,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.port = GPIOB,
|
||||
.ccr = TIM_IT_CC3,
|
||||
.channel = TIM_Channel_3,
|
||||
.pin = GPIO_Pin_0,
|
||||
.timer_chan = TIM_Channel_3,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_0,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.port = GPIOB,
|
||||
.ccr = TIM_IT_CC1,
|
||||
.channel = TIM_Channel_1,
|
||||
.pin = GPIO_Pin_4,
|
||||
.timer_chan = TIM_Channel_1,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_4,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
.remap = GPIO_PartialRemap_TIM3,
|
||||
},
|
||||
{
|
||||
.timer = TIM3,
|
||||
.port = GPIOB,
|
||||
.ccr = TIM_IT_CC2,
|
||||
.channel = TIM_Channel_2,
|
||||
.pin = GPIO_Pin_5,
|
||||
.timer_chan = TIM_Channel_2,
|
||||
.pin = {
|
||||
.gpio = GPIOB,
|
||||
.init = {
|
||||
.GPIO_Pin = GPIO_Pin_5,
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
},
|
||||
.remap = GPIO_PartialRemap_TIM3,
|
||||
},
|
||||
};
|
||||
|
||||
void TIM1_CC_IRQHandler();
|
||||
void TIM3_IRQHandler();
|
||||
void TIM5_IRQHandler();
|
||||
void TIM1_CC_IRQHandler() __attribute__ ((alias ("PIOS_TIM1_CC_irq_handler")));
|
||||
void TIM3_IRQHandler() __attribute__ ((alias ("PIOS_TIM3_irq_handler")));
|
||||
void TIM5_IRQHandler() __attribute__ ((alias ("PIOS_TIM5_irq_handler")));
|
||||
const struct pios_pwm_cfg pios_pwm_cfg = {
|
||||
.tim_base_init = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1,
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = 0xFFFF,
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
},
|
||||
.tim_ic_init = {
|
||||
.TIM_ICPolarity = TIM_ICPolarity_Rising,
|
||||
.TIM_ICSelection = TIM_ICSelection_DirectTI,
|
||||
.TIM_ICPrescaler = TIM_ICPSC_DIV1,
|
||||
.TIM_ICFilter = 0x0,
|
||||
},
|
||||
.gpio_init = {
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
},
|
||||
.remap = GPIO_PartialRemap_TIM3,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
.channels = pios_pwm_channels,
|
||||
.num_channels = NELEMENTS(pios_pwm_channels),
|
||||
.channels = pios_tim_rcvrport_all_channels,
|
||||
.num_channels = NELEMENTS(pios_tim_rcvrport_all_channels),
|
||||
};
|
||||
void PIOS_TIM1_CC_irq_handler()
|
||||
{
|
||||
PIOS_PWM_irq_handler(TIM1);
|
||||
}
|
||||
void PIOS_TIM3_irq_handler()
|
||||
{
|
||||
PIOS_PWM_irq_handler(TIM3);
|
||||
}
|
||||
void PIOS_TIM5_irq_handler()
|
||||
{
|
||||
PIOS_PWM_irq_handler(TIM5);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -733,42 +857,7 @@ void PIOS_TIM5_irq_handler()
|
||||
*/
|
||||
#if defined(PIOS_INCLUDE_PPM)
|
||||
#include <pios_ppm_priv.h>
|
||||
void TIM6_IRQHandler();
|
||||
void TIM6_IRQHandler() __attribute__ ((alias ("PIOS_TIM6_irq_handler")));
|
||||
static const struct pios_ppmsv_cfg pios_ppmsv_cfg = {
|
||||
.tim_base_init = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1, /* For 1 uS accuracy */
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = ((1000000 / 25) - 1), /* 25 Hz */
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
},
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
},
|
||||
},
|
||||
.timer = TIM6,
|
||||
.ccr = TIM_IT_Update,
|
||||
};
|
||||
|
||||
void PIOS_TIM6_irq_handler(void)
|
||||
{
|
||||
PIOS_PPMSV_irq_handler();
|
||||
}
|
||||
|
||||
void TIM1_CC_IRQHandler();
|
||||
void TIM1_CC_IRQHandler() __attribute__ ((alias ("PIOS_TIM1_CC_irq_handler")));
|
||||
static const struct pios_ppm_cfg pios_ppm_cfg = {
|
||||
.tim_base_init = {
|
||||
.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1, /* For 1 uS accuracy */
|
||||
.TIM_ClockDivision = TIM_CKD_DIV1,
|
||||
.TIM_CounterMode = TIM_CounterMode_Up,
|
||||
.TIM_Period = 0xFFFF,
|
||||
.TIM_RepetitionCounter = 0x0000,
|
||||
},
|
||||
.tim_ic_init = {
|
||||
.TIM_ICPolarity = TIM_ICPolarity_Rising,
|
||||
.TIM_ICSelection = TIM_ICSelection_DirectTI,
|
||||
@ -776,30 +865,11 @@ static const struct pios_ppm_cfg pios_ppm_cfg = {
|
||||
.TIM_ICFilter = 0x0,
|
||||
.TIM_Channel = TIM_Channel_2,
|
||||
},
|
||||
.gpio_init = {
|
||||
.GPIO_Mode = GPIO_Mode_IPD,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
.GPIO_Pin = GPIO_Pin_9,
|
||||
},
|
||||
.remap = 0,
|
||||
.irq = {
|
||||
.init = {
|
||||
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_MID,
|
||||
.NVIC_IRQChannelSubPriority = 0,
|
||||
.NVIC_IRQChannelCmd = ENABLE,
|
||||
.NVIC_IRQChannel = TIM1_CC_IRQn,
|
||||
},
|
||||
},
|
||||
.timer = TIM1,
|
||||
.port = GPIOA,
|
||||
.ccr = TIM_IT_CC2,
|
||||
/* Use only the first channel for ppm */
|
||||
.channels = &pios_tim_rcvrport_all_channels[0],
|
||||
.num_channels = 1,
|
||||
};
|
||||
|
||||
void PIOS_TIM1_CC_irq_handler(void)
|
||||
{
|
||||
PIOS_PPM_irq_handler();
|
||||
}
|
||||
|
||||
#endif //PPM
|
||||
|
||||
#if defined(PIOS_INCLUDE_I2C)
|
||||
@ -1008,8 +1078,9 @@ void PIOS_Board_Init(void) {
|
||||
/* Remap AFIO pin */
|
||||
//GPIO_PinRemapConfig( GPIO_Remap_SWJ_NoJTRST, ENABLE);
|
||||
|
||||
/* Debug services */
|
||||
PIOS_DEBUG_Init();
|
||||
#ifdef PIOS_DEBUG_ENABLE_DEBUG_PINS
|
||||
PIOS_DEBUG_Init(&pios_tim_servo_all_channels, NELEMENTS(pios_tim_servo_all_channels));
|
||||
#endif /* PIOS_DEBUG_ENABLE_DEBUG_PINS */
|
||||
|
||||
/* Delay system */
|
||||
PIOS_DELAY_Init();
|
||||
@ -1040,6 +1111,14 @@ void PIOS_Board_Init(void) {
|
||||
/* Initialize the task monitor library */
|
||||
TaskMonitorInitialize();
|
||||
|
||||
/* Set up pulse timers */
|
||||
PIOS_TIM_InitClock(&tim_1_cfg);
|
||||
PIOS_TIM_InitClock(&tim_3_cfg);
|
||||
PIOS_TIM_InitClock(&tim_5_cfg);
|
||||
|
||||
PIOS_TIM_InitClock(&tim_4_cfg);
|
||||
PIOS_TIM_InitClock(&tim_8_cfg);
|
||||
|
||||
/* Prepare the AHRS Comms upper layer protocol */
|
||||
AhrsInitComms();
|
||||
|
||||
@ -1107,7 +1186,10 @@ void PIOS_Board_Init(void) {
|
||||
break;
|
||||
}
|
||||
|
||||
PIOS_Servo_Init();
|
||||
#ifndef PIOS_DEBUG_ENABLE_DEBUG_PINS
|
||||
PIOS_Servo_Init(&pios_servo_cfg);
|
||||
#endif /* PIOS_DEBUG_ENABLE_DEBUG_PINS */
|
||||
|
||||
PIOS_ADC_Init();
|
||||
PIOS_GPIO_Init();
|
||||
|
||||
@ -1153,22 +1235,30 @@ void PIOS_Board_Init(void) {
|
||||
break;
|
||||
case HWSETTINGS_RCVRPORT_PWM:
|
||||
#if defined(PIOS_INCLUDE_PWM)
|
||||
PIOS_PWM_Init();
|
||||
uint32_t pios_pwm_rcvr_id;
|
||||
if (PIOS_RCVR_Init(&pios_pwm_rcvr_id, &pios_pwm_rcvr_driver, 0)) {
|
||||
PIOS_Assert(0);
|
||||
{
|
||||
uint32_t pios_pwm_id;
|
||||
PIOS_PWM_Init(&pios_pwm_id, &pios_pwm_cfg);
|
||||
|
||||
uint32_t pios_pwm_rcvr_id;
|
||||
if (PIOS_RCVR_Init(&pios_pwm_rcvr_id, &pios_pwm_rcvr_driver, pios_pwm_id)) {
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
pios_rcvr_group_map[MANUALCONTROLSETTINGS_CHANNELGROUPS_PWM] = pios_pwm_rcvr_id;
|
||||
}
|
||||
pios_rcvr_group_map[MANUALCONTROLSETTINGS_CHANNELGROUPS_PWM] = pios_pwm_rcvr_id;
|
||||
#endif /* PIOS_INCLUDE_PWM */
|
||||
break;
|
||||
case HWSETTINGS_RCVRPORT_PPM:
|
||||
#if defined(PIOS_INCLUDE_PPM)
|
||||
PIOS_PPM_Init();
|
||||
uint32_t pios_ppm_rcvr_id;
|
||||
if (PIOS_RCVR_Init(&pios_ppm_rcvr_id, &pios_ppm_rcvr_driver, 0)) {
|
||||
PIOS_Assert(0);
|
||||
{
|
||||
uint32_t pios_ppm_id;
|
||||
PIOS_PPM_Init(&pios_ppm_id, &pios_ppm_cfg);
|
||||
|
||||
uint32_t pios_ppm_rcvr_id;
|
||||
if (PIOS_RCVR_Init(&pios_ppm_rcvr_id, &pios_ppm_rcvr_driver, pios_ppm_id)) {
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
pios_rcvr_group_map[MANUALCONTROLSETTINGS_CHANNELGROUPS_PPM] = pios_ppm_rcvr_id;
|
||||
}
|
||||
pios_rcvr_group_map[MANUALCONTROLSETTINGS_CHANNELGROUPS_PPM] = pios_ppm_rcvr_id;
|
||||
#endif /* PIOS_INCLUDE_PPM */
|
||||
break;
|
||||
}
|
||||
|
@ -212,11 +212,13 @@ extern uint32_t pios_com_telem_usb_id;
|
||||
//-------------------------
|
||||
// Receiver PPM input
|
||||
//-------------------------
|
||||
#define PIOS_PPM_MAX_DEVS 1
|
||||
#define PIOS_PPM_NUM_INPUTS 12
|
||||
|
||||
//-------------------------
|
||||
// Receiver PWM input
|
||||
//-------------------------
|
||||
#define PIOS_PWM_MAX_DEVS 1
|
||||
#define PIOS_PWM_NUM_INPUTS 6
|
||||
|
||||
//-------------------------
|
||||
@ -231,6 +233,11 @@ extern uint32_t pios_com_telem_usb_id;
|
||||
#define PIOS_SERVO_UPDATE_HZ 50
|
||||
#define PIOS_SERVOS_INITIAL_POSITION 0 /* dont want to start motors, have no pulse till settings loaded */
|
||||
|
||||
//--------------------------
|
||||
// Timer controller settings
|
||||
//--------------------------
|
||||
#define PIOS_TIM_MAX_DEVS 3
|
||||
|
||||
//-------------------------
|
||||
// GPIO
|
||||
//-------------------------
|
||||
|
@ -185,11 +185,13 @@ extern uint32_t pios_com_aux_id;
|
||||
//-------------------------
|
||||
// Receiver PPM input
|
||||
//-------------------------
|
||||
#define PIOS_PPM_MAX_DEVS 1
|
||||
#define PIOS_PPM_NUM_INPUTS 12
|
||||
|
||||
//-------------------------
|
||||
// Receiver PWM input
|
||||
//-------------------------
|
||||
#define PIOS_PWM_MAX_DEVS 1
|
||||
#define PIOS_PWM_NUM_INPUTS 8
|
||||
|
||||
//-------------------------
|
||||
@ -204,6 +206,11 @@ extern uint32_t pios_com_aux_id;
|
||||
#define PIOS_SERVO_UPDATE_HZ 50
|
||||
#define PIOS_SERVOS_INITIAL_POSITION 0 /* dont want to start motors, have no pulse till settings loaded */
|
||||
|
||||
//--------------------------
|
||||
// Timer controller settings
|
||||
//--------------------------
|
||||
#define PIOS_TIM_MAX_DEVS 3
|
||||
|
||||
//-------------------------
|
||||
// ADC
|
||||
// PIOS_ADC_PinGet(0) = Temperature Sensor (On-board)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This is the size of the stack for all FreeRTOS IRQs */
|
||||
_irq_stack_size = 0x180;
|
||||
_irq_stack_size = 0x1A0;
|
||||
/* This is the size of the stack for early init: life span is until scheduler starts */
|
||||
_init_stack_size = 0x100;
|
||||
|
||||
|
@ -34,39 +34,41 @@
|
||||
// Global variables
|
||||
const char *PIOS_DEBUG_AssertMsg = "ASSERT FAILED";
|
||||
|
||||
#include <pios_servo_priv.h>
|
||||
extern const struct pios_servo_channel pios_servo_channels[];
|
||||
#define PIOS_SERVO_GPIO_PORT_1TO4 pios_servo_channels[0].port
|
||||
#define PIOS_SERVO_GPIO_PORT_5TO8 pios_servo_channels[4].port
|
||||
#define PIOS_SERVO_GPIO_PIN_1 pios_servo_channels[0].pin
|
||||
#define PIOS_SERVO_GPIO_PIN_2 pios_servo_channels[1].pin
|
||||
#define PIOS_SERVO_GPIO_PIN_3 pios_servo_channels[2].pin
|
||||
#define PIOS_SERVO_GPIO_PIN_4 pios_servo_channels[3].pin
|
||||
#define PIOS_SERVO_GPIO_PIN_5 pios_servo_channels[4].pin
|
||||
#define PIOS_SERVO_GPIO_PIN_6 pios_servo_channels[5].pin
|
||||
#define PIOS_SERVO_GPIO_PIN_7 pios_servo_channels[6].pin
|
||||
#define PIOS_SERVO_GPIO_PIN_8 pios_servo_channels[7].pin
|
||||
/* Private Function Prototypes */
|
||||
#ifdef PIOS_ENABLE_DEBUG_PINS
|
||||
static const struct pios_tim_channel * debug_channels;
|
||||
static uint8_t debug_num_channels;
|
||||
#endif /* PIOS_ENABLE_DEBUG_PINS */
|
||||
|
||||
/**
|
||||
* Initialise Debug-features
|
||||
*/
|
||||
void PIOS_DEBUG_Init(void)
|
||||
void PIOS_DEBUG_Init(const struct pios_tim_channel * channels, uint8_t num_channels)
|
||||
{
|
||||
#ifdef PIOS_ENABLE_DEBUG_PINS
|
||||
// Initialise Servo pins as standard output pins
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
GPIO_StructInit(&GPIO_InitStructure);
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStructure.GPIO_Pin = PIOS_SERVO_GPIO_PIN_1 | PIOS_SERVO_GPIO_PIN_2 | PIOS_SERVO_GPIO_PIN_3 | PIOS_SERVO_GPIO_PIN_4;
|
||||
GPIO_Init(PIOS_SERVO_GPIO_PORT_1TO4, &GPIO_InitStructure);
|
||||
GPIO_InitStructure.GPIO_Pin = PIOS_SERVO_GPIO_PIN_5 | PIOS_SERVO_GPIO_PIN_6 | PIOS_SERVO_GPIO_PIN_7 | PIOS_SERVO_GPIO_PIN_8;
|
||||
GPIO_Init(PIOS_SERVO_GPIO_PORT_5TO8, &GPIO_InitStructure);
|
||||
PIOS_Assert(channels);
|
||||
PIOS_Assert(num_channels);
|
||||
|
||||
// Drive all pins low
|
||||
PIOS_SERVO_GPIO_PORT_1TO4->BRR = PIOS_SERVO_GPIO_PIN_1 | PIOS_SERVO_GPIO_PIN_2 | PIOS_SERVO_GPIO_PIN_3 | PIOS_SERVO_GPIO_PIN_4;
|
||||
PIOS_SERVO_GPIO_PORT_5TO8->BRR = PIOS_SERVO_GPIO_PIN_5 | PIOS_SERVO_GPIO_PIN_6 | PIOS_SERVO_GPIO_PIN_7 | PIOS_SERVO_GPIO_PIN_8;
|
||||
/* Store away the GPIOs we've been given */
|
||||
debug_channels = channels;
|
||||
debug_num_channels = num_channels;
|
||||
|
||||
/* Configure the GPIOs we've been given */
|
||||
for (uint8_t i = 0; i < num_channels; i++) {
|
||||
const struct pios_tim_channel * chan = &channels[i];
|
||||
|
||||
// Initialise pins as standard output pins
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
GPIO_StructInit(&GPIO_InitStructure);
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStructure.GPIO_Pin = chan->init->GPIO_Pin;
|
||||
|
||||
/* Initialize the GPIO */
|
||||
GPIO_Init(chan->init->port, &GPIO_InitStructure);
|
||||
|
||||
/* Set the pin low */
|
||||
GPIO_WriteBit(chan->init->port, chan->init->GPIO_Pin, Bit_RESET);
|
||||
}
|
||||
#endif // PIOS_ENABLE_DEBUG_PINS
|
||||
}
|
||||
|
||||
@ -74,14 +76,17 @@ void PIOS_DEBUG_Init(void)
|
||||
* Set debug-pin high
|
||||
* \param pin 0 for S1 output
|
||||
*/
|
||||
void PIOS_DEBUG_PinHigh(uint8_t Pin)
|
||||
void PIOS_DEBUG_PinHigh(uint8_t pin)
|
||||
{
|
||||
#ifdef PIOS_ENABLE_DEBUG_PINS
|
||||
if (Pin < 4) {
|
||||
PIOS_SERVO_GPIO_PORT_1TO4->BSRR = (PIOS_SERVO_GPIO_PIN_1 << Pin);
|
||||
} else if (Pin <= 7) {
|
||||
PIOS_SERVO_GPIO_PORT_5TO8->BSRR = (PIOS_SERVO_GPIO_PIN_5 << (Pin - 4));
|
||||
if (!debug_channels || pin >= debug_num_channels) {
|
||||
return;
|
||||
}
|
||||
|
||||
const struct pios_tim_channel * chan = &debug_channels[pin];
|
||||
|
||||
GPIO_WriteBit(chan->init->port, chan->init->GPIO_Pin, Bit_Set);
|
||||
|
||||
#endif // PIOS_ENABLE_DEBUG_PINS
|
||||
}
|
||||
|
||||
@ -89,14 +94,17 @@ void PIOS_DEBUG_PinHigh(uint8_t Pin)
|
||||
* Set debug-pin low
|
||||
* \param pin 0 for S1 output
|
||||
*/
|
||||
void PIOS_DEBUG_PinLow(uint8_t Pin)
|
||||
void PIOS_DEBUG_PinLow(uint8_t pin)
|
||||
{
|
||||
#ifdef PIOS_ENABLE_DEBUG_PINS
|
||||
if (Pin < 4) {
|
||||
PIOS_SERVO_GPIO_PORT_1TO4->BRR = (PIOS_SERVO_GPIO_PIN_1 << Pin);
|
||||
} else if (Pin <= 7) {
|
||||
PIOS_SERVO_GPIO_PORT_5TO8->BRR = (PIOS_SERVO_GPIO_PIN_5 << (Pin - 4));
|
||||
if (!debug_channels || pin >= debug_num_channels) {
|
||||
return;
|
||||
}
|
||||
|
||||
const struct pios_tim_channel * chan = &debug_channels[pin];
|
||||
|
||||
GPIO_WriteBit(chan->init->port, chan->init->GPIO_Pin, Bit_RESET);
|
||||
|
||||
#endif // PIOS_ENABLE_DEBUG_PINS
|
||||
}
|
||||
|
||||
@ -104,11 +112,22 @@ void PIOS_DEBUG_PinLow(uint8_t Pin)
|
||||
void PIOS_DEBUG_PinValue8Bit(uint8_t value)
|
||||
{
|
||||
#ifdef PIOS_ENABLE_DEBUG_PINS
|
||||
if (!debug_channels) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t bsrr_l = ( ((~value)&0x0F)<<(16+6) ) | ((value & 0x0F)<<6);
|
||||
uint32_t bsrr_h = ( ((~value)&0xF0)<<(16+6-4) ) | ((value & 0xF0)<<(6-4));
|
||||
|
||||
PIOS_IRQ_Disable();
|
||||
PIOS_SERVO_GPIO_PORT_1TO4->BSRR = bsrr_l;
|
||||
PIOS_SERVO_GPIO_PORT_5TO8->BSRR = bsrr_h;
|
||||
|
||||
/*
|
||||
* This is sketchy since it assumes a particular ordering
|
||||
* and bitwise layout of the channels provided to the debug code.
|
||||
*/
|
||||
debug_channels[0].init.port->BSRR = bsrr_l;
|
||||
debug_channels[4].init.port->BSRR = bsrr_h;
|
||||
|
||||
PIOS_IRQ_Enable();
|
||||
#endif // PIOS_ENABLE_DEBUG_PINS
|
||||
}
|
||||
@ -116,8 +135,16 @@ void PIOS_DEBUG_PinValue8Bit(uint8_t value)
|
||||
void PIOS_DEBUG_PinValue4BitL(uint8_t value)
|
||||
{
|
||||
#ifdef PIOS_ENABLE_DEBUG_PINS
|
||||
if (!debug_channels) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is sketchy since it assumes a particular ordering
|
||||
* and bitwise layout of the channels provided to the debug code.
|
||||
*/
|
||||
uint32_t bsrr_l = ((~(value & 0x0F)<<(16+6))) | ((value & 0x0F)<<6);
|
||||
PIOS_SERVO_GPIO_PORT_1TO4->BSRR = bsrr_l;
|
||||
debug_channels[0].init.port->BSRR = bsrr_l;
|
||||
#endif // PIOS_ENABLE_DEBUG_PINS
|
||||
}
|
||||
|
||||
|
@ -51,104 +51,136 @@ const struct pios_rcvr_driver pios_ppm_rcvr_driver = {
|
||||
|
||||
/* Local Variables */
|
||||
static TIM_ICInitTypeDef TIM_ICInitStructure;
|
||||
static uint8_t PulseIndex;
|
||||
static uint32_t PreviousTime;
|
||||
static uint32_t CurrentTime;
|
||||
static uint32_t DeltaTime;
|
||||
static uint32_t CaptureValue[PIOS_PPM_IN_MAX_NUM_CHANNELS];
|
||||
static uint32_t CaptureValueNewFrame[PIOS_PPM_IN_MAX_NUM_CHANNELS];
|
||||
static uint32_t LargeCounter;
|
||||
static int8_t NumChannels;
|
||||
static int8_t NumChannelsPrevFrame;
|
||||
static uint8_t NumChannelCounter;
|
||||
|
||||
static uint8_t supv_timer = 0;
|
||||
static bool Tracking;
|
||||
static bool Fresh;
|
||||
|
||||
static void PIOS_PPM_Supervisor(uint32_t ppm_id);
|
||||
|
||||
void PIOS_PPM_Init(void)
|
||||
enum pios_ppm_dev_magic {
|
||||
PIOS_PPM_DEV_MAGIC = 0xee014d8b,
|
||||
};
|
||||
|
||||
struct pios_ppm_dev {
|
||||
enum pios_ppm_dev_magic magic;
|
||||
const struct pios_ppm_cfg * cfg;
|
||||
|
||||
uint8_t PulseIndex;
|
||||
uint32_t PreviousTime;
|
||||
uint32_t CurrentTime;
|
||||
uint32_t DeltaTime;
|
||||
uint32_t CaptureValue[PIOS_PPM_IN_MAX_NUM_CHANNELS];
|
||||
uint32_t CaptureValueNewFrame[PIOS_PPM_IN_MAX_NUM_CHANNELS];
|
||||
uint32_t LargeCounter;
|
||||
int8_t NumChannels;
|
||||
int8_t NumChannelsPrevFrame;
|
||||
uint8_t NumChannelCounter;
|
||||
|
||||
uint8_t supv_timer;
|
||||
bool Tracking;
|
||||
bool Fresh;
|
||||
};
|
||||
|
||||
static bool PIOS_PPM_validate(struct pios_ppm_dev * ppm_dev)
|
||||
{
|
||||
/* Flush counter variables */
|
||||
int32_t i;
|
||||
return (ppm_dev->magic == PIOS_PPM_DEV_MAGIC);
|
||||
}
|
||||
|
||||
PulseIndex = 0;
|
||||
PreviousTime = 0;
|
||||
CurrentTime = 0;
|
||||
DeltaTime = 0;
|
||||
LargeCounter = 0;
|
||||
NumChannels = -1;
|
||||
NumChannelsPrevFrame = -1;
|
||||
NumChannelCounter = 0;
|
||||
Tracking = FALSE;
|
||||
Fresh = FALSE;
|
||||
#if defined(PIOS_INCLUDE_FREERTOS) && 0
|
||||
static struct pios_ppm_dev * PIOS_PPM_alloc(void)
|
||||
{
|
||||
struct pios_ppm_dev * ppm_dev;
|
||||
|
||||
for (i = 0; i < PIOS_PPM_IN_MAX_NUM_CHANNELS; i++) {
|
||||
CaptureValue[i] = 0;
|
||||
CaptureValueNewFrame[i] = 0;
|
||||
ppm_dev = (struct pios_ppm_dev *)malloc(sizeof(*ppm_dev));
|
||||
if (!ppm_dev) return(NULL);
|
||||
|
||||
ppm_dev->magic = PIOS_PPM_DEV_MAGIC;
|
||||
return(ppm_dev);
|
||||
}
|
||||
#else
|
||||
static struct pios_ppm_dev pios_ppm_devs[PIOS_PPM_MAX_DEVS];
|
||||
static uint8_t pios_ppm_num_devs;
|
||||
static struct pios_ppm_dev * PIOS_PPM_alloc(void)
|
||||
{
|
||||
struct pios_ppm_dev * ppm_dev;
|
||||
|
||||
if (pios_ppm_num_devs >= PIOS_PPM_MAX_DEVS) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
NVIC_InitTypeDef NVIC_InitStructure = pios_ppm_cfg.irq.init;
|
||||
ppm_dev = &pios_ppm_devs[pios_ppm_num_devs++];
|
||||
ppm_dev->magic = PIOS_PPM_DEV_MAGIC;
|
||||
|
||||
/* Enable appropriate clock to timer module */
|
||||
switch((int32_t) pios_ppm_cfg.timer) {
|
||||
case (int32_t)TIM1:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM2:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM3:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM4:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
|
||||
break;
|
||||
#ifdef STM32F10X_HD
|
||||
case (int32_t)TIM5:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM6:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM7:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM8:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM8_CC_IRQn;
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
|
||||
break;
|
||||
return (ppm_dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void PIOS_PPM_tim_overflow_cb (uint32_t id, uint32_t context, uint8_t channel, uint16_t count);
|
||||
static void PIOS_PPM_tim_edge_cb (uint32_t id, uint32_t context, uint8_t channel, uint16_t count);
|
||||
const static struct pios_tim_callbacks tim_callbacks = {
|
||||
.overflow = PIOS_PPM_tim_overflow_cb,
|
||||
.edge = PIOS_PPM_tim_edge_cb,
|
||||
};
|
||||
|
||||
extern int32_t PIOS_PPM_Init(uint32_t * ppm_id, const struct pios_ppm_cfg * cfg)
|
||||
{
|
||||
PIOS_DEBUG_Assert(ppm_id);
|
||||
PIOS_DEBUG_Assert(cfg);
|
||||
|
||||
struct pios_ppm_dev * ppm_dev;
|
||||
|
||||
ppm_dev = (struct pios_ppm_dev *) PIOS_PPM_alloc();
|
||||
if (!ppm_dev) goto out_fail;
|
||||
|
||||
/* Bind the configuration to the device instance */
|
||||
ppm_dev->cfg = cfg;
|
||||
|
||||
/* Set up the state variables */
|
||||
ppm_dev->PulseIndex = 0;
|
||||
ppm_dev->PreviousTime = 0;
|
||||
ppm_dev->CurrentTime = 0;
|
||||
ppm_dev->DeltaTime = 0;
|
||||
ppm_dev->LargeCounter = 0;
|
||||
ppm_dev->NumChannels = -1;
|
||||
ppm_dev->NumChannelsPrevFrame = -1;
|
||||
ppm_dev->NumChannelCounter = 0;
|
||||
ppm_dev->Tracking = FALSE;
|
||||
ppm_dev->Fresh = FALSE;
|
||||
|
||||
for (uint8_t i = 0; i < PIOS_PPM_IN_MAX_NUM_CHANNELS; i++) {
|
||||
/* Flush counter variables */
|
||||
ppm_dev->CaptureValue[i] = 0;
|
||||
ppm_dev->CaptureValueNewFrame[i] = 0;
|
||||
|
||||
}
|
||||
/* Enable timer interrupts */
|
||||
NVIC_Init(&NVIC_InitStructure);
|
||||
|
||||
/* Configure input pins */
|
||||
GPIO_InitTypeDef GPIO_InitStructure = pios_ppm_cfg.gpio_init;
|
||||
GPIO_Init(pios_ppm_cfg.port, &GPIO_InitStructure);
|
||||
uint32_t tim_id;
|
||||
if (PIOS_TIM_InitChannels(&tim_id, cfg->channels, cfg->num_channels, &tim_callbacks, (uint32_t)ppm_dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Configure timer for input capture */
|
||||
TIM_ICInitStructure = pios_ppm_cfg.tim_ic_init;
|
||||
TIM_ICInit(pios_ppm_cfg.timer, &TIM_ICInitStructure);
|
||||
/* Configure the channels to be in capture/compare mode */
|
||||
for (uint8_t i = 0; i < cfg->num_channels; i++) {
|
||||
const struct pios_tim_channel * chan = &cfg->channels[i];
|
||||
|
||||
/* Configure timer clocks */
|
||||
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = pios_ppm_cfg.tim_base_init;
|
||||
TIM_InternalClockConfig(pios_ppm_cfg.timer);
|
||||
TIM_TimeBaseInit(pios_ppm_cfg.timer, &TIM_TimeBaseStructure);
|
||||
/* Configure timer for input capture */
|
||||
TIM_ICInitTypeDef TIM_ICInitStructure = cfg->tim_ic_init;
|
||||
TIM_ICInitStructure.TIM_Channel = chan->timer_chan;
|
||||
TIM_ICInit(chan->timer, &TIM_ICInitStructure);
|
||||
|
||||
/* Enable the Capture Compare Interrupt Request */
|
||||
TIM_ITConfig(pios_ppm_cfg.timer, pios_ppm_cfg.ccr | TIM_IT_Update, ENABLE);
|
||||
|
||||
/* Enable timers */
|
||||
TIM_Cmd(pios_ppm_cfg.timer, ENABLE);
|
||||
/* Enable the Capture Compare Interrupt Request */
|
||||
switch (chan->timer_chan) {
|
||||
case TIM_Channel_1:
|
||||
TIM_ITConfig(chan->timer, TIM_IT_CC1 | TIM_IT_Update, ENABLE);
|
||||
break;
|
||||
case TIM_Channel_2:
|
||||
TIM_ITConfig(chan->timer, TIM_IT_CC2 | TIM_IT_Update, ENABLE);
|
||||
break;
|
||||
case TIM_Channel_3:
|
||||
TIM_ITConfig(chan->timer, TIM_IT_CC3 | TIM_IT_Update, ENABLE);
|
||||
break;
|
||||
case TIM_Channel_4:
|
||||
TIM_ITConfig(chan->timer, TIM_IT_CC4 | TIM_IT_Update, ENABLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup local variable which stays in this scope */
|
||||
/* Doing this here and using a local variable saves doing it in the ISR */
|
||||
@ -156,9 +188,16 @@ void PIOS_PPM_Init(void)
|
||||
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
|
||||
TIM_ICInitStructure.TIM_ICFilter = 0x0;
|
||||
|
||||
if (!PIOS_RTC_RegisterTickCallback(PIOS_PPM_Supervisor, 0)) {
|
||||
if (!PIOS_RTC_RegisterTickCallback(PIOS_PPM_Supervisor, (uint32_t)ppm_dev)) {
|
||||
PIOS_DEBUG_Assert(0);
|
||||
}
|
||||
|
||||
*ppm_id = (uint32_t)ppm_dev;
|
||||
|
||||
return(0);
|
||||
|
||||
out_fail:
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,142 +208,146 @@ void PIOS_PPM_Init(void)
|
||||
*/
|
||||
static int32_t PIOS_PPM_Get(uint32_t rcvr_id, uint8_t channel)
|
||||
{
|
||||
/* Return error if channel not available */
|
||||
if (channel >= PIOS_PPM_IN_MAX_NUM_CHANNELS) {
|
||||
struct pios_ppm_dev * ppm_dev = (struct pios_ppm_dev *)rcvr_id;
|
||||
|
||||
if (!PIOS_PPM_validate(ppm_dev)) {
|
||||
/* Invalid device specified */
|
||||
return -1;
|
||||
}
|
||||
return CaptureValue[channel];
|
||||
|
||||
if (channel >= PIOS_PPM_IN_MAX_NUM_CHANNELS) {
|
||||
/* Channel out of range */
|
||||
return -1;
|
||||
}
|
||||
return ppm_dev->CaptureValue[channel];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle TIMx global interrupt request
|
||||
* Some work and testing still needed, need to detect start of frame and decode pulses
|
||||
*
|
||||
*/
|
||||
void PIOS_PPM_irq_handler(void)
|
||||
static void PIOS_PPM_tim_overflow_cb (uint32_t tim_id, uint32_t context, uint8_t channel, uint16_t count)
|
||||
{
|
||||
/* Timer Overflow Interrupt
|
||||
* The time between timer overflows must be greater than the PPM
|
||||
* frame period. If a full frame has not decoded in the between timer
|
||||
* overflows then capture values should be cleared.
|
||||
*/
|
||||
struct pios_ppm_dev * ppm_dev = (struct pios_ppm_dev *)context;
|
||||
|
||||
if (TIM_GetITStatus(pios_ppm_cfg.timer, TIM_IT_Update) == SET) {
|
||||
/* Clear TIMx overflow interrupt pending bit */
|
||||
TIM_ClearITPendingBit(pios_ppm_cfg.timer, TIM_IT_Update);
|
||||
|
||||
/* If sharing a timer with a servo output the ARR register will
|
||||
be set according to the PWM period. When timer reaches the
|
||||
ARR value a timer overflow interrupt will fire. We use the
|
||||
interrupt accumulate a 32-bit timer. */
|
||||
LargeCounter = LargeCounter + pios_ppm_cfg.timer->ARR;
|
||||
if (!PIOS_PPM_validate(ppm_dev)) {
|
||||
/* Invalid device specified */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Signal edge interrupt */
|
||||
if (TIM_GetITStatus(pios_ppm_cfg.timer, pios_ppm_cfg.ccr) == SET) {
|
||||
PreviousTime = CurrentTime;
|
||||
ppm_dev->LargeCounter += count;
|
||||
|
||||
switch((int32_t) pios_ppm_cfg.ccr) {
|
||||
case (int32_t)TIM_IT_CC1:
|
||||
CurrentTime = TIM_GetCapture1(pios_ppm_cfg.timer);
|
||||
break;
|
||||
case (int32_t)TIM_IT_CC2:
|
||||
CurrentTime = TIM_GetCapture2(pios_ppm_cfg.timer);
|
||||
break;
|
||||
case (int32_t)TIM_IT_CC3:
|
||||
CurrentTime = TIM_GetCapture3(pios_ppm_cfg.timer);
|
||||
break;
|
||||
case (int32_t)TIM_IT_CC4:
|
||||
CurrentTime = TIM_GetCapture4(pios_ppm_cfg.timer);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void PIOS_PPM_tim_edge_cb (uint32_t tim_id, uint32_t context, uint8_t chan_idx, uint16_t count)
|
||||
{
|
||||
/* Recover our device context */
|
||||
struct pios_ppm_dev * ppm_dev = (struct pios_ppm_dev *)context;
|
||||
|
||||
if (!PIOS_PPM_validate(ppm_dev)) {
|
||||
/* Invalid device specified */
|
||||
return;
|
||||
}
|
||||
|
||||
if (chan_idx >= ppm_dev->cfg->num_channels) {
|
||||
/* Channel out of range */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Shift the last measurement out */
|
||||
ppm_dev->PreviousTime = ppm_dev->CurrentTime;
|
||||
|
||||
/* Grab the new count */
|
||||
ppm_dev->CurrentTime = count;
|
||||
|
||||
/* Convert to 32-bit timer result */
|
||||
ppm_dev->CurrentTime += ppm_dev->LargeCounter;
|
||||
|
||||
/* Capture computation */
|
||||
ppm_dev->DeltaTime = ppm_dev->CurrentTime - ppm_dev->PreviousTime;
|
||||
|
||||
ppm_dev->PreviousTime = ppm_dev->CurrentTime;
|
||||
|
||||
/* Sync pulse detection */
|
||||
if (ppm_dev->DeltaTime > PIOS_PPM_IN_MIN_SYNC_PULSE_US) {
|
||||
if (ppm_dev->PulseIndex == ppm_dev->NumChannelsPrevFrame
|
||||
&& ppm_dev->PulseIndex >= PIOS_PPM_IN_MIN_NUM_CHANNELS
|
||||
&& ppm_dev->PulseIndex <= PIOS_PPM_IN_MAX_NUM_CHANNELS)
|
||||
{
|
||||
/* If we see n simultaneous frames of the same
|
||||
number of channels we save it as our frame size */
|
||||
if (ppm_dev->NumChannelCounter < PIOS_PPM_STABLE_CHANNEL_COUNT)
|
||||
ppm_dev->NumChannelCounter++;
|
||||
else
|
||||
ppm_dev->NumChannels = ppm_dev->PulseIndex;
|
||||
} else {
|
||||
ppm_dev->NumChannelCounter = 0;
|
||||
}
|
||||
|
||||
/* Clear TIMx Capture compare interrupt pending bit */
|
||||
TIM_ClearITPendingBit(pios_ppm_cfg.timer, pios_ppm_cfg.ccr);
|
||||
|
||||
/* Convert to 32-bit timer result */
|
||||
CurrentTime = CurrentTime + LargeCounter;
|
||||
|
||||
/* Capture computation */
|
||||
DeltaTime = CurrentTime - PreviousTime;
|
||||
|
||||
PreviousTime = CurrentTime;
|
||||
|
||||
/* Sync pulse detection */
|
||||
if (DeltaTime > PIOS_PPM_IN_MIN_SYNC_PULSE_US) {
|
||||
if (PulseIndex == NumChannelsPrevFrame
|
||||
&& PulseIndex >= PIOS_PPM_IN_MIN_NUM_CHANNELS
|
||||
&& PulseIndex <= PIOS_PPM_IN_MAX_NUM_CHANNELS)
|
||||
{
|
||||
/* If we see n simultaneous frames of the same
|
||||
number of channels we save it as our frame size */
|
||||
if (NumChannelCounter < PIOS_PPM_STABLE_CHANNEL_COUNT)
|
||||
NumChannelCounter++;
|
||||
else
|
||||
NumChannels = PulseIndex;
|
||||
} else {
|
||||
NumChannelCounter = 0;
|
||||
/* Check if the last frame was well formed */
|
||||
if (ppm_dev->PulseIndex == ppm_dev->NumChannels && ppm_dev->Tracking) {
|
||||
/* The last frame was well formed */
|
||||
for (uint32_t i = 0; i < ppm_dev->NumChannels; i++) {
|
||||
ppm_dev->CaptureValue[i] = ppm_dev->CaptureValueNewFrame[i];
|
||||
}
|
||||
|
||||
/* Check if the last frame was well formed */
|
||||
if (PulseIndex == NumChannels && Tracking) {
|
||||
/* The last frame was well formed */
|
||||
for (uint32_t i = 0; i < NumChannels; i++) {
|
||||
CaptureValue[i] = CaptureValueNewFrame[i];
|
||||
}
|
||||
for (uint32_t i = NumChannels;
|
||||
i < PIOS_PPM_IN_MAX_NUM_CHANNELS; i++) {
|
||||
CaptureValue[i] = PIOS_PPM_INPUT_INVALID;
|
||||
}
|
||||
for (uint32_t i = ppm_dev->NumChannels;
|
||||
i < PIOS_PPM_IN_MAX_NUM_CHANNELS; i++) {
|
||||
ppm_dev->CaptureValue[i] = PIOS_PPM_INPUT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
Fresh = TRUE;
|
||||
Tracking = TRUE;
|
||||
NumChannelsPrevFrame = PulseIndex;
|
||||
PulseIndex = 0;
|
||||
ppm_dev->Fresh = TRUE;
|
||||
ppm_dev->Tracking = TRUE;
|
||||
ppm_dev->NumChannelsPrevFrame = ppm_dev->PulseIndex;
|
||||
ppm_dev->PulseIndex = 0;
|
||||
|
||||
/* We rely on the supervisor to set CaptureValue to invalid
|
||||
if no valid frame is found otherwise we ride over it */
|
||||
/* We rely on the supervisor to set CaptureValue to invalid
|
||||
if no valid frame is found otherwise we ride over it */
|
||||
|
||||
} else if (Tracking) {
|
||||
/* Valid pulse duration 0.75 to 2.5 ms*/
|
||||
if (DeltaTime > PIOS_PPM_IN_MIN_CHANNEL_PULSE_US
|
||||
&& DeltaTime < PIOS_PPM_IN_MAX_CHANNEL_PULSE_US
|
||||
&& PulseIndex < PIOS_PPM_IN_MAX_NUM_CHANNELS) {
|
||||
|
||||
CaptureValueNewFrame[PulseIndex] = DeltaTime;
|
||||
PulseIndex++;
|
||||
} else {
|
||||
/* Not a valid pulse duration */
|
||||
Tracking = FALSE;
|
||||
for (uint32_t i = 0; i < PIOS_PPM_IN_MAX_NUM_CHANNELS ; i++) {
|
||||
CaptureValueNewFrame[i] = PIOS_PPM_INPUT_INVALID;
|
||||
}
|
||||
} else if (ppm_dev->Tracking) {
|
||||
/* Valid pulse duration 0.75 to 2.5 ms*/
|
||||
if (ppm_dev->DeltaTime > PIOS_PPM_IN_MIN_CHANNEL_PULSE_US
|
||||
&& ppm_dev->DeltaTime < PIOS_PPM_IN_MAX_CHANNEL_PULSE_US
|
||||
&& ppm_dev->PulseIndex < PIOS_PPM_IN_MAX_NUM_CHANNELS) {
|
||||
|
||||
ppm_dev->CaptureValueNewFrame[ppm_dev->PulseIndex] = ppm_dev->DeltaTime;
|
||||
ppm_dev->PulseIndex++;
|
||||
} else {
|
||||
/* Not a valid pulse duration */
|
||||
ppm_dev->Tracking = FALSE;
|
||||
for (uint32_t i = 0; i < PIOS_PPM_IN_MAX_NUM_CHANNELS ; i++) {
|
||||
ppm_dev->CaptureValueNewFrame[i] = PIOS_PPM_INPUT_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PIOS_PPM_Supervisor(uint32_t ppm_id) {
|
||||
/* Recover our device context */
|
||||
struct pios_ppm_dev * ppm_dev = (struct pios_ppm_dev *)ppm_id;
|
||||
|
||||
if (!PIOS_PPM_validate(ppm_dev)) {
|
||||
/* Invalid device specified */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* RTC runs at 625Hz so divide down the base rate so
|
||||
* that this loop runs at 25Hz.
|
||||
*/
|
||||
if(++supv_timer < 25) {
|
||||
if(++(ppm_dev->supv_timer) < 25) {
|
||||
return;
|
||||
}
|
||||
supv_timer = 0;
|
||||
ppm_dev->supv_timer = 0;
|
||||
|
||||
if (!Fresh) {
|
||||
Tracking = FALSE;
|
||||
if (!ppm_dev->Fresh) {
|
||||
ppm_dev->Tracking = FALSE;
|
||||
|
||||
for (int32_t i = 0; i < PIOS_PPM_IN_MAX_NUM_CHANNELS ; i++) {
|
||||
CaptureValue[i] = PIOS_PPM_INPUT_INVALID;
|
||||
CaptureValueNewFrame[i] = PIOS_PPM_INPUT_INVALID;
|
||||
ppm_dev->CaptureValue[i] = PIOS_PPM_INPUT_INVALID;
|
||||
ppm_dev->CaptureValueNewFrame[i] = PIOS_PPM_INPUT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
Fresh = FALSE;
|
||||
ppm_dev->Fresh = FALSE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -42,96 +42,124 @@ const struct pios_rcvr_driver pios_pwm_rcvr_driver = {
|
||||
};
|
||||
|
||||
/* Local Variables */
|
||||
static uint8_t CaptureState[PIOS_PWM_NUM_INPUTS];
|
||||
static uint16_t RiseValue[PIOS_PWM_NUM_INPUTS];
|
||||
static uint16_t FallValue[PIOS_PWM_NUM_INPUTS];
|
||||
static uint32_t CaptureValue[PIOS_PWM_NUM_INPUTS];
|
||||
|
||||
static uint32_t CapCounter[PIOS_PWM_NUM_INPUTS];
|
||||
enum pios_pwm_dev_magic {
|
||||
PIOS_PWM_DEV_MAGIC = 0xab30293c,
|
||||
};
|
||||
|
||||
struct pios_pwm_dev {
|
||||
enum pios_pwm_dev_magic magic;
|
||||
const struct pios_pwm_cfg * cfg;
|
||||
|
||||
uint8_t CaptureState[PIOS_PWM_NUM_INPUTS];
|
||||
uint16_t RiseValue[PIOS_PWM_NUM_INPUTS];
|
||||
uint16_t FallValue[PIOS_PWM_NUM_INPUTS];
|
||||
uint32_t CaptureValue[PIOS_PWM_NUM_INPUTS];
|
||||
uint32_t CapCounter[PIOS_PWM_NUM_INPUTS];
|
||||
};
|
||||
|
||||
static bool PIOS_PWM_validate(struct pios_pwm_dev * pwm_dev)
|
||||
{
|
||||
return (pwm_dev->magic == PIOS_PWM_DEV_MAGIC);
|
||||
}
|
||||
|
||||
#if defined(PIOS_INCLUDE_FREERTOS) && 0
|
||||
static struct pios_pwm_dev * PIOS_PWM_alloc(void)
|
||||
{
|
||||
struct pios_pwm_dev * pwm_dev;
|
||||
|
||||
pwm_dev = (struct pios_pwm_dev *)malloc(sizeof(*pwm_dev));
|
||||
if (!pwm_dev) return(NULL);
|
||||
|
||||
pwm_dev->magic = PIOS_PWM_DEV_MAGIC;
|
||||
return(pwm_dev);
|
||||
}
|
||||
#else
|
||||
static struct pios_pwm_dev pios_pwm_devs[PIOS_PWM_MAX_DEVS];
|
||||
static uint8_t pios_pwm_num_devs;
|
||||
static struct pios_pwm_dev * PIOS_PWM_alloc(void)
|
||||
{
|
||||
struct pios_pwm_dev * pwm_dev;
|
||||
|
||||
if (pios_pwm_num_devs >= PIOS_PWM_MAX_DEVS) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
pwm_dev = &pios_pwm_devs[pios_pwm_num_devs++];
|
||||
pwm_dev->magic = PIOS_PWM_DEV_MAGIC;
|
||||
|
||||
return (pwm_dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void PIOS_PWM_tim_overflow_cb (uint32_t id, uint32_t context, uint8_t channel, uint16_t count);
|
||||
static void PIOS_PWM_tim_edge_cb (uint32_t id, uint32_t context, uint8_t channel, uint16_t count);
|
||||
const static struct pios_tim_callbacks tim_callbacks = {
|
||||
.overflow = PIOS_PWM_tim_overflow_cb,
|
||||
.edge = PIOS_PWM_tim_edge_cb,
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialises all the pins
|
||||
*/
|
||||
void PIOS_PWM_Init(void)
|
||||
int32_t PIOS_PWM_Init(uint32_t * pwm_id, const struct pios_pwm_cfg * cfg)
|
||||
{
|
||||
for (uint8_t i = 0; i < pios_pwm_cfg.num_channels; i++) {
|
||||
/* Flush counter variables */
|
||||
CaptureState[i] = 0;
|
||||
RiseValue[i] = 0;
|
||||
FallValue[i] = 0;
|
||||
CaptureValue[i] = 0;
|
||||
|
||||
NVIC_InitTypeDef NVIC_InitStructure = pios_pwm_cfg.irq.init;
|
||||
GPIO_InitTypeDef GPIO_InitStructure = pios_pwm_cfg.gpio_init;
|
||||
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = pios_pwm_cfg.tim_base_init;
|
||||
TIM_ICInitTypeDef TIM_ICInitStructure = pios_pwm_cfg.tim_ic_init;
|
||||
|
||||
struct pios_pwm_channel channel = pios_pwm_cfg.channels[i];
|
||||
|
||||
/* Enable appropriate clock to timer module */
|
||||
switch((int32_t) channel.timer) {
|
||||
case (int32_t)TIM1:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM2:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM3:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM4:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
|
||||
break;
|
||||
#ifdef STM32F10X_HD
|
||||
|
||||
case (int32_t)TIM5:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM6:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM7:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM8:
|
||||
NVIC_InitStructure.NVIC_IRQChannel = TIM8_CC_IRQn;
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
NVIC_Init(&NVIC_InitStructure);
|
||||
PIOS_DEBUG_Assert(pwm_id);
|
||||
PIOS_DEBUG_Assert(cfg);
|
||||
|
||||
struct pios_pwm_dev * pwm_dev;
|
||||
|
||||
pwm_dev = (struct pios_pwm_dev *) PIOS_PWM_alloc();
|
||||
if (!pwm_dev) goto out_fail;
|
||||
|
||||
/* Bind the configuration to the device instance */
|
||||
pwm_dev->cfg = cfg;
|
||||
|
||||
for (uint8_t i = 0; i < PIOS_PWM_NUM_INPUTS; i++) {
|
||||
/* Flush counter variables */
|
||||
pwm_dev->CaptureState[i] = 0;
|
||||
pwm_dev->RiseValue[i] = 0;
|
||||
pwm_dev->FallValue[i] = 0;
|
||||
pwm_dev->CaptureValue[i] = 0;
|
||||
}
|
||||
|
||||
uint32_t tim_id;
|
||||
if (PIOS_TIM_InitChannels(&tim_id, cfg->channels, cfg->num_channels, &tim_callbacks, (uint32_t)pwm_dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Configure the channels to be in capture/compare mode */
|
||||
for (uint8_t i = 0; i < cfg->num_channels; i++) {
|
||||
const struct pios_tim_channel * chan = &cfg->channels[i];
|
||||
|
||||
/* Enable GPIO */
|
||||
GPIO_InitStructure.GPIO_Pin = channel.pin;
|
||||
GPIO_Init(channel.port, &GPIO_InitStructure);
|
||||
|
||||
/* Configure timer for input capture */
|
||||
TIM_ICInitStructure.TIM_Channel = channel.channel;
|
||||
TIM_ICInit(channel.timer, &TIM_ICInitStructure);
|
||||
|
||||
/* Configure timer clocks */
|
||||
TIM_InternalClockConfig(channel.timer);
|
||||
if(channel.timer->PSC != ((PIOS_MASTER_CLOCK / 1000000) - 1))
|
||||
TIM_TimeBaseInit(channel.timer, &TIM_TimeBaseStructure);
|
||||
TIM_ICInitTypeDef TIM_ICInitStructure = cfg->tim_ic_init;
|
||||
TIM_ICInitStructure.TIM_Channel = chan->timer_chan;
|
||||
TIM_ICInit(chan->timer, &TIM_ICInitStructure);
|
||||
|
||||
/* Enable the Capture Compare Interrupt Request */
|
||||
TIM_ITConfig(channel.timer, channel.ccr, ENABLE);
|
||||
|
||||
/* Enable timers */
|
||||
TIM_Cmd(channel.timer, ENABLE);
|
||||
switch (chan->timer_chan) {
|
||||
case TIM_Channel_1:
|
||||
TIM_ITConfig(chan->timer, TIM_IT_CC1, ENABLE);
|
||||
break;
|
||||
case TIM_Channel_2:
|
||||
TIM_ITConfig(chan->timer, TIM_IT_CC2, ENABLE);
|
||||
break;
|
||||
case TIM_Channel_3:
|
||||
TIM_ITConfig(chan->timer, TIM_IT_CC3, ENABLE);
|
||||
break;
|
||||
case TIM_Channel_4:
|
||||
TIM_ITConfig(chan->timer, TIM_IT_CC4, ENABLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(pios_pwm_cfg.remap) {
|
||||
/* Warning, I don't think this will work for multiple remaps at once */
|
||||
GPIO_PinRemapConfig(pios_pwm_cfg.remap, ENABLE);
|
||||
}
|
||||
*pwm_id = (uint32_t) pwm_dev;
|
||||
|
||||
return (0);
|
||||
|
||||
out_fail:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,75 +170,86 @@ void PIOS_PWM_Init(void)
|
||||
*/
|
||||
static int32_t PIOS_PWM_Get(uint32_t rcvr_id, uint8_t channel)
|
||||
{
|
||||
/* Return error if channel not available */
|
||||
if (channel >= pios_pwm_cfg.num_channels) {
|
||||
struct pios_pwm_dev * pwm_dev = (struct pios_pwm_dev *)rcvr_id;
|
||||
|
||||
if (!PIOS_PWM_validate(pwm_dev)) {
|
||||
/* Invalid device specified */
|
||||
return -1;
|
||||
}
|
||||
return CaptureValue[channel];
|
||||
|
||||
if (channel >= PIOS_PWM_NUM_INPUTS) {
|
||||
/* Channel out of range */
|
||||
return -1;
|
||||
}
|
||||
return pwm_dev->CaptureValue[channel];
|
||||
}
|
||||
|
||||
void PIOS_PWM_irq_handler(TIM_TypeDef * timer)
|
||||
static void PIOS_PWM_tim_overflow_cb (uint32_t tim_id, uint32_t context, uint8_t channel, uint16_t count)
|
||||
{
|
||||
uint16_t val = 0;
|
||||
for(uint8_t i = 0; i < pios_pwm_cfg.num_channels; i++) {
|
||||
struct pios_pwm_channel channel = pios_pwm_cfg.channels[i];
|
||||
if ((channel.timer == timer) && (TIM_GetITStatus(channel.timer, channel.ccr) == SET)) {
|
||||
|
||||
TIM_ClearITPendingBit(channel.timer, channel.ccr);
|
||||
|
||||
switch(channel.channel) {
|
||||
case TIM_Channel_1:
|
||||
val = TIM_GetCapture1(channel.timer);
|
||||
break;
|
||||
case TIM_Channel_2:
|
||||
val = TIM_GetCapture2(channel.timer);
|
||||
break;
|
||||
case TIM_Channel_3:
|
||||
val = TIM_GetCapture3(channel.timer);
|
||||
break;
|
||||
case TIM_Channel_4:
|
||||
val = TIM_GetCapture4(channel.timer);
|
||||
break;
|
||||
}
|
||||
|
||||
if (CaptureState[i] == 0) {
|
||||
RiseValue[i] = val;
|
||||
} else {
|
||||
FallValue[i] = val;
|
||||
}
|
||||
|
||||
// flip state machine and capture value here
|
||||
/* Simple rise or fall state machine */
|
||||
TIM_ICInitTypeDef TIM_ICInitStructure = pios_pwm_cfg.tim_ic_init;
|
||||
if (CaptureState[i] == 0) {
|
||||
/* Switch states */
|
||||
CaptureState[i] = 1;
|
||||
|
||||
/* Switch polarity of input capture */
|
||||
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
|
||||
TIM_ICInitStructure.TIM_Channel = channel.channel;
|
||||
TIM_ICInit(channel.timer, &TIM_ICInitStructure);
|
||||
} else {
|
||||
/* Capture computation */
|
||||
if (FallValue[i] > RiseValue[i]) {
|
||||
CaptureValue[i] = (FallValue[i] - RiseValue[i]);
|
||||
} else {
|
||||
CaptureValue[i] = ((channel.timer->ARR - RiseValue[i]) + FallValue[i]);
|
||||
}
|
||||
|
||||
/* Switch states */
|
||||
CaptureState[i] = 0;
|
||||
|
||||
/* Increase supervisor counter */
|
||||
CapCounter[i]++;
|
||||
|
||||
/* Switch polarity of input capture */
|
||||
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
|
||||
TIM_ICInitStructure.TIM_Channel = channel.channel;
|
||||
TIM_ICInit(channel.timer, &TIM_ICInitStructure);
|
||||
}
|
||||
}
|
||||
struct pios_pwm_dev * pwm_dev = (struct pios_pwm_dev *)context;
|
||||
|
||||
if (!PIOS_PWM_validate(pwm_dev)) {
|
||||
/* Invalid device specified */
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void PIOS_PWM_tim_edge_cb (uint32_t tim_id, uint32_t context, uint8_t chan_idx, uint16_t count)
|
||||
{
|
||||
/* Recover our device context */
|
||||
struct pios_pwm_dev * pwm_dev = (struct pios_pwm_dev *)context;
|
||||
|
||||
if (!PIOS_PWM_validate(pwm_dev)) {
|
||||
/* Invalid device specified */
|
||||
return;
|
||||
}
|
||||
|
||||
if (chan_idx >= pwm_dev->cfg->num_channels) {
|
||||
/* Channel out of range */
|
||||
return;
|
||||
}
|
||||
|
||||
const struct pios_tim_channel * chan = &pwm_dev->cfg->channels[chan_idx];
|
||||
|
||||
if (pwm_dev->CaptureState[chan_idx] == 0) {
|
||||
pwm_dev->RiseValue[chan_idx] = count;
|
||||
} else {
|
||||
pwm_dev->FallValue[chan_idx] = count;
|
||||
}
|
||||
|
||||
// flip state machine and capture value here
|
||||
/* Simple rise or fall state machine */
|
||||
TIM_ICInitTypeDef TIM_ICInitStructure = pwm_dev->cfg->tim_ic_init;
|
||||
if (pwm_dev->CaptureState[chan_idx] == 0) {
|
||||
/* Switch states */
|
||||
pwm_dev->CaptureState[chan_idx] = 1;
|
||||
|
||||
/* Switch polarity of input capture */
|
||||
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
|
||||
TIM_ICInitStructure.TIM_Channel = chan->timer_chan;
|
||||
TIM_ICInit(chan->timer, &TIM_ICInitStructure);
|
||||
} else {
|
||||
/* Capture computation */
|
||||
if (pwm_dev->FallValue[chan_idx] > pwm_dev->RiseValue[chan_idx]) {
|
||||
pwm_dev->CaptureValue[chan_idx] = (pwm_dev->FallValue[chan_idx] - pwm_dev->RiseValue[chan_idx]);
|
||||
} else {
|
||||
pwm_dev->CaptureValue[chan_idx] = ((chan->timer->ARR - pwm_dev->RiseValue[chan_idx]) + pwm_dev->FallValue[chan_idx]);
|
||||
}
|
||||
|
||||
/* Switch states */
|
||||
pwm_dev->CaptureState[chan_idx] = 0;
|
||||
|
||||
/* Increase supervisor counter */
|
||||
pwm_dev->CapCounter[chan_idx]++;
|
||||
|
||||
/* Switch polarity of input capture */
|
||||
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
|
||||
TIM_ICInitStructure.TIM_Channel = chan->timer_chan;
|
||||
TIM_ICInit(chan->timer, &TIM_ICInitStructure);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -31,97 +31,55 @@
|
||||
/* Project Includes */
|
||||
#include "pios.h"
|
||||
#include "pios_servo_priv.h"
|
||||
#include "pios_tim_priv.h"
|
||||
|
||||
/* Private Function Prototypes */
|
||||
|
||||
static const struct pios_servo_cfg * servo_cfg;
|
||||
|
||||
/**
|
||||
* Initialise Servos
|
||||
*/
|
||||
void PIOS_Servo_Init(void)
|
||||
int32_t PIOS_Servo_Init(const struct pios_servo_cfg * cfg)
|
||||
{
|
||||
#ifndef PIOS_ENABLE_DEBUG_PINS
|
||||
#if defined(PIOS_INCLUDE_SERVO)
|
||||
uint32_t tim_id;
|
||||
if (PIOS_TIM_InitChannels(&tim_id, cfg->channels, cfg->num_channels, NULL, 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Store away the requested configuration */
|
||||
servo_cfg = cfg;
|
||||
|
||||
for (uint8_t i = 0; i < pios_servo_cfg.num_channels; i++) {
|
||||
GPIO_InitTypeDef GPIO_InitStructure = pios_servo_cfg.gpio_init;
|
||||
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = pios_servo_cfg.tim_base_init;
|
||||
TIM_OCInitTypeDef TIM_OCInitStructure = pios_servo_cfg.tim_oc_init;
|
||||
|
||||
struct pios_servo_channel channel = pios_servo_cfg.channels[i];
|
||||
|
||||
/* Enable appropriate clock to timer module */
|
||||
switch((int32_t) channel.timer) {
|
||||
case (int32_t)TIM1:
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM2:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM3:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM4:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM5:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM6:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM7:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
|
||||
break;
|
||||
case (int32_t)TIM8:
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable GPIO */
|
||||
GPIO_InitStructure.GPIO_Pin = channel.pin;
|
||||
GPIO_Init(channel.port, &GPIO_InitStructure);
|
||||
|
||||
/* Enable time base */
|
||||
TIM_TimeBaseInit(channel.timer, &TIM_TimeBaseStructure);
|
||||
|
||||
channel.timer->PSC = (PIOS_MASTER_CLOCK / 1000000) - 1;
|
||||
/* Configure the channels to be in output compare mode */
|
||||
for (uint8_t i = 0; i < cfg->num_channels; i++) {
|
||||
const struct pios_tim_channel * chan = &cfg->channels[i];
|
||||
|
||||
/* Set up for output compare function */
|
||||
switch(channel.channel) {
|
||||
switch(chan->timer_chan) {
|
||||
case TIM_Channel_1:
|
||||
TIM_OC1Init(channel.timer, &TIM_OCInitStructure);
|
||||
TIM_OC1PreloadConfig(channel.timer, TIM_OCPreload_Enable);
|
||||
TIM_OC1Init(chan->timer, &cfg->tim_oc_init);
|
||||
TIM_OC1PreloadConfig(chan->timer, TIM_OCPreload_Enable);
|
||||
break;
|
||||
case TIM_Channel_2:
|
||||
TIM_OC2Init(channel.timer, &TIM_OCInitStructure);
|
||||
TIM_OC2PreloadConfig(channel.timer, TIM_OCPreload_Enable);
|
||||
TIM_OC2Init(chan->timer, &cfg->tim_oc_init);
|
||||
TIM_OC2PreloadConfig(chan->timer, TIM_OCPreload_Enable);
|
||||
break;
|
||||
case TIM_Channel_3:
|
||||
TIM_OC3Init(channel.timer, &TIM_OCInitStructure);
|
||||
TIM_OC3PreloadConfig(channel.timer, TIM_OCPreload_Enable);
|
||||
TIM_OC3Init(chan->timer, &cfg->tim_oc_init);
|
||||
TIM_OC3PreloadConfig(chan->timer, TIM_OCPreload_Enable);
|
||||
break;
|
||||
case TIM_Channel_4:
|
||||
TIM_OC4Init(channel.timer, &TIM_OCInitStructure);
|
||||
TIM_OC4PreloadConfig(channel.timer, TIM_OCPreload_Enable);
|
||||
TIM_OC4Init(chan->timer, &cfg->tim_oc_init);
|
||||
TIM_OC4PreloadConfig(chan->timer, TIM_OCPreload_Enable);
|
||||
break;
|
||||
}
|
||||
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
|
||||
TIM_ARRPreloadConfig(channel.timer, ENABLE);
|
||||
TIM_CtrlPWMOutputs(channel.timer, ENABLE);
|
||||
TIM_Cmd(channel.timer, ENABLE);
|
||||
|
||||
TIM_ARRPreloadConfig(chan->timer, ENABLE);
|
||||
TIM_CtrlPWMOutputs(chan->timer, ENABLE);
|
||||
TIM_Cmd(chan->timer, ENABLE);
|
||||
}
|
||||
|
||||
if(pios_servo_cfg.remap) {
|
||||
/* Warning, I don't think this will work for multiple remaps at once */
|
||||
GPIO_PinRemapConfig(pios_servo_cfg.remap, ENABLE);
|
||||
}
|
||||
|
||||
|
||||
#endif // PIOS_INCLUDE_SERVO
|
||||
#endif // PIOS_ENABLE_DEBUG_PINS
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,31 +89,31 @@ void PIOS_Servo_Init(void)
|
||||
*/
|
||||
void PIOS_Servo_SetHz(uint16_t * speeds, uint8_t banks)
|
||||
{
|
||||
#ifndef PIOS_ENABLE_DEBUG_PINS
|
||||
#if defined(PIOS_INCLUDE_SERVO)
|
||||
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = pios_servo_cfg.tim_base_init;
|
||||
if (!servo_cfg) {
|
||||
return;
|
||||
}
|
||||
|
||||
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = servo_cfg->tim_base_init;
|
||||
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
|
||||
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
|
||||
TIM_TimeBaseStructure.TIM_Prescaler = (PIOS_MASTER_CLOCK / 1000000) - 1;
|
||||
|
||||
uint8_t set = 0;
|
||||
|
||||
for(uint8_t i = 0; (i < pios_servo_cfg.num_channels) && (set < banks); i++) {
|
||||
for(uint8_t i = 0; (i < servo_cfg->num_channels) && (set < banks); i++) {
|
||||
bool new = true;
|
||||
struct pios_servo_channel channel = pios_servo_cfg.channels[i];
|
||||
const struct pios_tim_channel * chan = &servo_cfg->channels[i];
|
||||
|
||||
/* See if any previous channels use that same timer */
|
||||
for(uint8_t j = 0; (j < i) && new; j++)
|
||||
new &= channel.timer != pios_servo_cfg.channels[j].timer;
|
||||
new &= chan->timer != servo_cfg->channels[j].timer;
|
||||
|
||||
if(new) {
|
||||
TIM_TimeBaseStructure.TIM_Period = ((1000000 / speeds[set]) - 1);
|
||||
TIM_TimeBaseInit(channel.timer, &TIM_TimeBaseStructure);
|
||||
TIM_TimeBaseInit(chan->timer, &TIM_TimeBaseStructure);
|
||||
set++;
|
||||
}
|
||||
}
|
||||
#endif // PIOS_INCLUDE_SERVO
|
||||
#endif // PIOS_ENABLE_DEBUG_PINS
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,29 +121,27 @@ void PIOS_Servo_SetHz(uint16_t * speeds, uint8_t banks)
|
||||
* \param[in] Servo Servo number (0-7)
|
||||
* \param[in] Position Servo position in microseconds
|
||||
*/
|
||||
void PIOS_Servo_Set(uint8_t Servo, uint16_t Position)
|
||||
void PIOS_Servo_Set(uint8_t servo, uint16_t position)
|
||||
{
|
||||
#ifndef PIOS_ENABLE_DEBUG_PINS
|
||||
#if defined(PIOS_INCLUDE_SERVO)
|
||||
/* Make sure servo exists */
|
||||
if (Servo < pios_servo_cfg.num_channels && Servo >= 0) {
|
||||
/* Update the position */
|
||||
|
||||
switch(pios_servo_cfg.channels[Servo].channel) {
|
||||
case TIM_Channel_1:
|
||||
TIM_SetCompare1(pios_servo_cfg.channels[Servo].timer, Position);
|
||||
break;
|
||||
case TIM_Channel_2:
|
||||
TIM_SetCompare2(pios_servo_cfg.channels[Servo].timer, Position);
|
||||
break;
|
||||
case TIM_Channel_3:
|
||||
TIM_SetCompare3(pios_servo_cfg.channels[Servo].timer, Position);
|
||||
break;
|
||||
case TIM_Channel_4:
|
||||
TIM_SetCompare4(pios_servo_cfg.channels[Servo].timer, Position);
|
||||
break;
|
||||
}
|
||||
if (!servo_cfg || servo >= servo_cfg->num_channels) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the position */
|
||||
const struct pios_tim_channel * chan = &servo_cfg->channels[servo];
|
||||
switch(chan->timer_chan) {
|
||||
case TIM_Channel_1:
|
||||
TIM_SetCompare1(chan->timer, position);
|
||||
break;
|
||||
case TIM_Channel_2:
|
||||
TIM_SetCompare2(chan->timer, position);
|
||||
break;
|
||||
case TIM_Channel_3:
|
||||
TIM_SetCompare3(chan->timer, position);
|
||||
break;
|
||||
case TIM_Channel_4:
|
||||
TIM_SetCompare4(chan->timer, position);
|
||||
break;
|
||||
}
|
||||
#endif // PIOS_INCLUDE_SERVO
|
||||
#endif // PIOS_ENABLE_DEBUG_PINS
|
||||
}
|
||||
|
391
flight/PiOS/STM32F10x/pios_tim.c
Normal file
391
flight/PiOS/STM32F10x/pios_tim.c
Normal file
@ -0,0 +1,391 @@
|
||||
#include "pios.h"
|
||||
|
||||
#include "pios_tim.h"
|
||||
#include "pios_tim_priv.h"
|
||||
|
||||
enum pios_tim_dev_magic {
|
||||
PIOS_TIM_DEV_MAGIC = 0x87654098,
|
||||
};
|
||||
|
||||
struct pios_tim_dev {
|
||||
enum pios_tim_dev_magic magic;
|
||||
|
||||
const struct pios_tim_channel * channels;
|
||||
uint8_t num_channels;
|
||||
|
||||
const struct pios_tim_callbacks * callbacks;
|
||||
uint32_t context;
|
||||
};
|
||||
|
||||
#if 0
|
||||
static bool PIOS_TIM_validate(struct pios_tim_dev * tim_dev)
|
||||
{
|
||||
return (tim_dev->magic == PIOS_TIM_DEV_MAGIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PIOS_INCLUDE_FREERTOS) && 0
|
||||
static struct pios_tim_dev * PIOS_TIM_alloc(void)
|
||||
{
|
||||
struct pios_tim_dev * tim_dev;
|
||||
|
||||
tim_dev = (struct pios_tim_dev *)malloc(sizeof(*tim_dev));
|
||||
if (!tim_dev) return(NULL);
|
||||
|
||||
tim_dev->magic = PIOS_TIM_DEV_MAGIC;
|
||||
return(tim_dev);
|
||||
}
|
||||
#else
|
||||
static struct pios_tim_dev pios_tim_devs[PIOS_TIM_MAX_DEVS];
|
||||
static uint8_t pios_tim_num_devs;
|
||||
static struct pios_tim_dev * PIOS_TIM_alloc(void)
|
||||
{
|
||||
struct pios_tim_dev * tim_dev;
|
||||
|
||||
if (pios_tim_num_devs >= PIOS_TIM_MAX_DEVS) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
tim_dev = &pios_tim_devs[pios_tim_num_devs++];
|
||||
tim_dev->magic = PIOS_TIM_DEV_MAGIC;
|
||||
|
||||
return (tim_dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PIOS_TIM_InitClock(const struct pios_tim_clock_cfg * cfg)
|
||||
{
|
||||
PIOS_DEBUG_Assert(cfg);
|
||||
|
||||
/* Enable appropriate clock to timer module */
|
||||
switch((uint32_t) cfg->timer) {
|
||||
case (uint32_t)TIM1:
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
|
||||
break;
|
||||
case (uint32_t)TIM2:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
||||
break;
|
||||
case (uint32_t)TIM3:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
|
||||
break;
|
||||
case (uint32_t)TIM4:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
|
||||
break;
|
||||
#ifdef STM32F10X_HD
|
||||
case (uint32_t)TIM5:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
|
||||
break;
|
||||
case (uint32_t)TIM6:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
|
||||
break;
|
||||
case (uint32_t)TIM7:
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
|
||||
break;
|
||||
case (uint32_t)TIM8:
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Configure the dividers for this timer */
|
||||
TIM_TimeBaseInit(cfg->timer, cfg->time_base_init);
|
||||
|
||||
/* Configure internal timer clocks */
|
||||
TIM_InternalClockConfig(cfg->timer);
|
||||
|
||||
/* Enable timers */
|
||||
TIM_Cmd(cfg->timer, ENABLE);
|
||||
|
||||
/* Enable Interrupts */
|
||||
NVIC_Init(&cfg->irq.init);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t PIOS_TIM_InitChannels(uint32_t * tim_id, const struct pios_tim_channel * channels, uint8_t num_channels, const struct pios_tim_callbacks * callbacks, uint32_t context)
|
||||
{
|
||||
PIOS_Assert(channels);
|
||||
PIOS_Assert(num_channels);
|
||||
|
||||
struct pios_tim_dev * tim_dev;
|
||||
tim_dev = (struct pios_tim_dev *) PIOS_TIM_alloc();
|
||||
if (!tim_dev) goto out_fail;
|
||||
|
||||
/* Bind the configuration to the device instance */
|
||||
tim_dev->channels = channels;
|
||||
tim_dev->num_channels = num_channels;
|
||||
tim_dev->callbacks = callbacks;
|
||||
tim_dev->context = context;
|
||||
|
||||
/* Configure the pins */
|
||||
for (uint8_t i = 0; i < num_channels; i++) {
|
||||
const struct pios_tim_channel * chan = &(channels[i]);
|
||||
|
||||
/* Enable the peripheral clock for the GPIO */
|
||||
switch ((uint32_t)chan->pin.gpio) {
|
||||
case (uint32_t) GPIOA:
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
|
||||
break;
|
||||
case (uint32_t) GPIOB:
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
|
||||
break;
|
||||
case (uint32_t) GPIOC:
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
|
||||
break;
|
||||
default:
|
||||
PIOS_Assert(0);
|
||||
break;
|
||||
}
|
||||
GPIO_Init(chan->pin.gpio, &chan->pin.init);
|
||||
|
||||
if (chan->remap) {
|
||||
GPIO_PinRemapConfig(chan->remap, ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
*tim_id = (uint32_t)tim_dev;
|
||||
|
||||
return(0);
|
||||
|
||||
out_fail:
|
||||
return(-1);
|
||||
}
|
||||
|
||||
static void PIOS_TIM_generic_irq_handler(TIM_TypeDef * timer)
|
||||
{
|
||||
/* Iterate over all registered clients of the TIM layer to find channels on this timer */
|
||||
for (uint8_t i = 0; i < pios_tim_num_devs; i++) {
|
||||
const struct pios_tim_dev * tim_dev = &pios_tim_devs[i];
|
||||
|
||||
if (!tim_dev->channels || tim_dev->num_channels == 0) {
|
||||
/* No channels to process on this client */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for an overflow event on this timer */
|
||||
bool overflow_event;
|
||||
uint16_t overflow_count;
|
||||
if (TIM_GetITStatus(timer, TIM_IT_Update) == SET) {
|
||||
TIM_ClearITPendingBit(timer, TIM_IT_Update);
|
||||
overflow_count = timer->ARR;
|
||||
overflow_event = true;
|
||||
} else {
|
||||
overflow_count = 0;
|
||||
overflow_event = false;
|
||||
}
|
||||
|
||||
for (uint8_t j = 0; j < tim_dev->num_channels; j++) {
|
||||
const struct pios_tim_channel * chan = &tim_dev->channels[j];
|
||||
|
||||
if (chan->timer != timer) {
|
||||
/* channel is not on this timer */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Figure out which interrupt bit we should be looking at */
|
||||
uint16_t timer_it;
|
||||
switch (chan->timer_chan) {
|
||||
case TIM_Channel_1:
|
||||
timer_it = TIM_IT_CC1;
|
||||
break;
|
||||
case TIM_Channel_2:
|
||||
timer_it = TIM_IT_CC2;
|
||||
break;
|
||||
case TIM_Channel_3:
|
||||
timer_it = TIM_IT_CC3;
|
||||
break;
|
||||
case TIM_Channel_4:
|
||||
timer_it = TIM_IT_CC4;
|
||||
break;
|
||||
default:
|
||||
PIOS_Assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
bool edge_event;
|
||||
uint16_t edge_count;
|
||||
if (TIM_GetITStatus(chan->timer, timer_it) == SET) {
|
||||
TIM_ClearITPendingBit(chan->timer, timer_it);
|
||||
|
||||
/* Read the current counter */
|
||||
switch(chan->timer_chan) {
|
||||
case TIM_Channel_1:
|
||||
edge_count = TIM_GetCapture1(chan->timer);
|
||||
break;
|
||||
case TIM_Channel_2:
|
||||
edge_count = TIM_GetCapture2(chan->timer);
|
||||
break;
|
||||
case TIM_Channel_3:
|
||||
edge_count = TIM_GetCapture3(chan->timer);
|
||||
break;
|
||||
case TIM_Channel_4:
|
||||
edge_count = TIM_GetCapture4(chan->timer);
|
||||
break;
|
||||
default:
|
||||
PIOS_Assert(0);
|
||||
break;
|
||||
}
|
||||
edge_event = true;
|
||||
} else {
|
||||
edge_event = false;
|
||||
edge_count = 0;
|
||||
}
|
||||
|
||||
if (!tim_dev->callbacks) {
|
||||
/* No callbacks registered, we're done with this channel */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Generate the appropriate callbacks */
|
||||
if (overflow_event & edge_event) {
|
||||
/*
|
||||
* When both edge and overflow happen in the same interrupt, we
|
||||
* need a heuristic to determine the order of the edge and overflow
|
||||
* events so that the callbacks happen in the right order. If we
|
||||
* get the order wrong, our pulse width calculations could be off by up
|
||||
* to ARR ticks. That could be bad.
|
||||
*
|
||||
* Heuristic: If the edge_count is < 16 ticks above zero then we assume the
|
||||
* edge happened just after the overflow.
|
||||
*/
|
||||
|
||||
if (edge_count < 16) {
|
||||
/* Call the overflow callback first */
|
||||
if (tim_dev->callbacks->overflow) {
|
||||
(*tim_dev->callbacks->overflow)((uint32_t)tim_dev,
|
||||
tim_dev->context,
|
||||
j,
|
||||
overflow_count);
|
||||
}
|
||||
/* Call the edge callback second */
|
||||
if (tim_dev->callbacks->edge) {
|
||||
(*tim_dev->callbacks->edge)((uint32_t)tim_dev,
|
||||
tim_dev->context,
|
||||
j,
|
||||
edge_count);
|
||||
}
|
||||
} else {
|
||||
/* Call the edge callback first */
|
||||
if (tim_dev->callbacks->edge) {
|
||||
(*tim_dev->callbacks->edge)((uint32_t)tim_dev,
|
||||
tim_dev->context,
|
||||
j,
|
||||
edge_count);
|
||||
}
|
||||
/* Call the overflow callback second */
|
||||
if (tim_dev->callbacks->overflow) {
|
||||
(*tim_dev->callbacks->overflow)((uint32_t)tim_dev,
|
||||
tim_dev->context,
|
||||
j,
|
||||
overflow_count);
|
||||
}
|
||||
}
|
||||
} else if (overflow_event && tim_dev->callbacks->overflow) {
|
||||
(*tim_dev->callbacks->overflow)((uint32_t)tim_dev,
|
||||
tim_dev->context,
|
||||
j,
|
||||
overflow_count);
|
||||
} else if (edge_event && tim_dev->callbacks->edge) {
|
||||
(*tim_dev->callbacks->edge)((uint32_t)tim_dev,
|
||||
tim_dev->context,
|
||||
j,
|
||||
edge_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
uint16_t val = 0;
|
||||
for(uint8_t i = 0; i < pios_pwm_cfg.num_channels; i++) {
|
||||
struct pios_pwm_channel channel = pios_pwm_cfg.channels[i];
|
||||
if ((channel.timer == timer) && (TIM_GetITStatus(channel.timer, channel.ccr) == SET)) {
|
||||
|
||||
TIM_ClearITPendingBit(channel.timer, channel.ccr);
|
||||
|
||||
switch(channel.channel) {
|
||||
case TIM_Channel_1:
|
||||
val = TIM_GetCapture1(channel.timer);
|
||||
break;
|
||||
case TIM_Channel_2:
|
||||
val = TIM_GetCapture2(channel.timer);
|
||||
break;
|
||||
case TIM_Channel_3:
|
||||
val = TIM_GetCapture3(channel.timer);
|
||||
break;
|
||||
case TIM_Channel_4:
|
||||
val = TIM_GetCapture4(channel.timer);
|
||||
break;
|
||||
}
|
||||
|
||||
if (CaptureState[i] == 0) {
|
||||
RiseValue[i] = val;
|
||||
} else {
|
||||
FallValue[i] = val;
|
||||
}
|
||||
|
||||
// flip state machine and capture value here
|
||||
/* Simple rise or fall state machine */
|
||||
TIM_ICInitTypeDef TIM_ICInitStructure = pios_pwm_cfg.tim_ic_init;
|
||||
if (CaptureState[i] == 0) {
|
||||
/* Switch states */
|
||||
CaptureState[i] = 1;
|
||||
|
||||
/* Switch polarity of input capture */
|
||||
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
|
||||
TIM_ICInitStructure.TIM_Channel = channel.channel;
|
||||
TIM_ICInit(channel.timer, &TIM_ICInitStructure);
|
||||
} else {
|
||||
/* Capture computation */
|
||||
if (FallValue[i] > RiseValue[i]) {
|
||||
CaptureValue[i] = (FallValue[i] - RiseValue[i]);
|
||||
} else {
|
||||
CaptureValue[i] = ((channel.timer->ARR - RiseValue[i]) + FallValue[i]);
|
||||
}
|
||||
|
||||
/* Switch states */
|
||||
CaptureState[i] = 0;
|
||||
|
||||
/* Increase supervisor counter */
|
||||
CapCounter[i]++;
|
||||
|
||||
/* Switch polarity of input capture */
|
||||
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
|
||||
TIM_ICInitStructure.TIM_Channel = channel.channel;
|
||||
TIM_ICInit(channel.timer, &TIM_ICInitStructure);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Bind Interrupt Handlers
|
||||
*
|
||||
* Map all valid TIM IRQs to the common interrupt handler
|
||||
* and give it enough context to properly demux the various timers
|
||||
*/
|
||||
static void PIOS_TIM_1_irq_handler (void)
|
||||
{
|
||||
PIOS_TIM_generic_irq_handler (TIM1);
|
||||
}
|
||||
void TIM1_IRQHandler(void) __attribute__ ((alias ("PIOS_TIM_1_irq_handler")));
|
||||
|
||||
static void PIOS_TIM_2_irq_handler (void)
|
||||
{
|
||||
PIOS_TIM_generic_irq_handler (TIM2);
|
||||
}
|
||||
void TIM2_IRQHandler(void) __attribute__ ((alias ("PIOS_TIM_2_irq_handler")));
|
||||
|
||||
void TIM3_IRQHandler(void) __attribute__ ((alias ("PIOS_TIM_3_irq_handler")));
|
||||
static void PIOS_TIM_3_irq_handler (void)
|
||||
{
|
||||
PIOS_TIM_generic_irq_handler (TIM3);
|
||||
}
|
||||
|
||||
void TIM4_IRQHandler(void) __attribute__ ((alias ("PIOS_TIM_4_irq_handler")));
|
||||
static void PIOS_TIM_4_irq_handler (void)
|
||||
{
|
||||
PIOS_TIM_generic_irq_handler (TIM4);
|
||||
}
|
||||
|
@ -33,7 +33,9 @@
|
||||
|
||||
extern const char *PIOS_DEBUG_AssertMsg;
|
||||
|
||||
void PIOS_DEBUG_Init(void);
|
||||
#include <pios_tim_priv.h>
|
||||
|
||||
void PIOS_DEBUG_Init(const struct pios_tim_channel * channels, uint8_t num_channels);
|
||||
void PIOS_DEBUG_PinHigh(uint8_t pin);
|
||||
void PIOS_DEBUG_PinLow(uint8_t pin);
|
||||
void PIOS_DEBUG_PinValue8Bit(uint8_t value);
|
||||
|
@ -35,24 +35,14 @@
|
||||
#include <pios_stm32.h>
|
||||
|
||||
struct pios_ppm_cfg {
|
||||
TIM_TimeBaseInitTypeDef tim_base_init;
|
||||
TIM_ICInitTypeDef tim_ic_init;
|
||||
GPIO_InitTypeDef gpio_init;
|
||||
uint32_t remap; /* GPIO_Remap_* */
|
||||
struct stm32_irq irq;
|
||||
TIM_TypeDef * timer;
|
||||
GPIO_TypeDef * port;
|
||||
uint16_t ccr;
|
||||
const struct pios_tim_channel * channels;
|
||||
uint8_t num_channels;
|
||||
};
|
||||
|
||||
extern void PIOS_PPM_irq_handler();
|
||||
|
||||
extern uint8_t pios_ppm_num_channels;
|
||||
extern const struct pios_ppm_cfg pios_ppm_cfg;
|
||||
|
||||
extern const struct pios_rcvr_driver pios_ppm_rcvr_driver;
|
||||
|
||||
extern void PIOS_PPM_Init(void);
|
||||
extern int32_t PIOS_PPM_Init(uint32_t * ppm_id, const struct pios_ppm_cfg * cfg);
|
||||
|
||||
#endif /* PIOS_PPM_PRIV_H */
|
||||
|
||||
|
@ -34,32 +34,17 @@
|
||||
#include <pios.h>
|
||||
#include <pios_stm32.h>
|
||||
|
||||
struct pios_pwm_channel {
|
||||
TIM_TypeDef * timer;
|
||||
GPIO_TypeDef * port;
|
||||
uint16_t ccr;
|
||||
uint8_t channel;
|
||||
uint16_t pin;
|
||||
};
|
||||
#include <pios_tim_priv.h>
|
||||
|
||||
struct pios_pwm_cfg {
|
||||
TIM_TimeBaseInitTypeDef tim_base_init;
|
||||
TIM_ICInitTypeDef tim_ic_init;
|
||||
GPIO_InitTypeDef gpio_init;
|
||||
uint32_t remap; /* GPIO_Remap_* */
|
||||
struct stm32_irq irq;
|
||||
const struct pios_pwm_channel *const channels;
|
||||
const struct pios_tim_channel * channels;
|
||||
uint8_t num_channels;
|
||||
};
|
||||
|
||||
extern void PIOS_PWM_irq_handler(TIM_TypeDef * timer);
|
||||
|
||||
extern uint8_t pios_pwm_num_channels;
|
||||
extern const struct pios_pwm_cfg pios_pwm_cfg;
|
||||
|
||||
extern const struct pios_rcvr_driver pios_pwm_rcvr_driver;
|
||||
|
||||
extern void PIOS_PWM_Init(void);
|
||||
extern int32_t PIOS_PWM_Init(uint32_t * pwm_id, const struct pios_pwm_cfg * cfg);
|
||||
|
||||
#endif /* PIOS_PWM_PRIV_H */
|
||||
|
||||
|
@ -31,7 +31,6 @@
|
||||
#define PIOS_SERVO_H
|
||||
|
||||
/* Public Functions */
|
||||
extern void PIOS_Servo_Init(void);
|
||||
extern void PIOS_Servo_SetHz(uint16_t * update_rates, uint8_t channels);
|
||||
extern void PIOS_Servo_Set(uint8_t Servo, uint16_t Position);
|
||||
|
||||
|
@ -33,25 +33,18 @@
|
||||
|
||||
#include <pios.h>
|
||||
#include <pios_stm32.h>
|
||||
|
||||
struct pios_servo_channel {
|
||||
TIM_TypeDef * timer;
|
||||
GPIO_TypeDef * port;
|
||||
uint8_t channel;
|
||||
uint16_t pin;
|
||||
};
|
||||
#include <pios_tim_priv.h>
|
||||
|
||||
struct pios_servo_cfg {
|
||||
TIM_TimeBaseInitTypeDef tim_base_init;
|
||||
TIM_OCInitTypeDef tim_oc_init;
|
||||
GPIO_InitTypeDef gpio_init;
|
||||
uint32_t remap;
|
||||
const struct pios_servo_channel *const channels;
|
||||
const struct pios_tim_channel * channels;
|
||||
uint8_t num_channels;
|
||||
};
|
||||
|
||||
|
||||
extern const struct pios_servo_cfg pios_servo_cfg;
|
||||
extern int32_t PIOS_Servo_Init(const struct pios_servo_cfg * cfg);
|
||||
|
||||
#endif /* PIOS_SERVO_PRIV_H */
|
||||
|
||||
|
4
flight/PiOS/inc/pios_tim.h
Normal file
4
flight/PiOS/inc/pios_tim.h
Normal file
@ -0,0 +1,4 @@
|
||||
#ifndef PIOS_TIM_H
|
||||
#define PIOS_TIM_H
|
||||
|
||||
#endif /* PIOS_TIM_H */
|
28
flight/PiOS/inc/pios_tim_priv.h
Normal file
28
flight/PiOS/inc/pios_tim_priv.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef PIOS_TIM_PRIV_H
|
||||
#define PIOS_TIM_PRIV_H
|
||||
|
||||
#include <pios_stm32.h>
|
||||
|
||||
struct pios_tim_clock_cfg {
|
||||
TIM_TypeDef * timer;
|
||||
const TIM_TimeBaseInitTypeDef * time_base_init;
|
||||
struct stm32_irq irq;
|
||||
};
|
||||
|
||||
struct pios_tim_channel {
|
||||
TIM_TypeDef * timer;
|
||||
uint8_t timer_chan;
|
||||
|
||||
struct stm32_gpio pin;
|
||||
uint32_t remap;
|
||||
};
|
||||
|
||||
struct pios_tim_callbacks {
|
||||
void (*overflow)(uint32_t tim_id, uint32_t context, uint8_t chan_idx, uint16_t count);
|
||||
void (*edge)(uint32_t tim_id, uint32_t context, uint8_t chan_idx, uint16_t count);
|
||||
};
|
||||
|
||||
extern int32_t PIOS_TIM_InitClock(const struct pios_tim_clock_cfg * cfg);
|
||||
extern int32_t PIOS_TIM_InitChannels(uint32_t * tim_id, const struct pios_tim_channel * channels, uint8_t num_channels, const struct pios_tim_callbacks * callbacks, uint32_t context);
|
||||
|
||||
#endif /* PIOS_TIM_PRIV_H */
|
Loading…
Reference in New Issue
Block a user