Files
edgetx-gx12/radio/src/targets/common/arm/stm32/stm32_pulse_driver.cpp
T
Raphael CoefficandGitHub 1bb27e7011 fix: PWM gimbals initialisation and inversion (#3745)
* fix: PWM gimbals initialisation and inversion

* feat: add periph clock support in PWM stick driver
2023-07-14 14:54:32 +10:00

337 lines
8.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (C) EdgeTx
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "stm32_pulse_driver.h"
#include "stm32_timer.h"
#include "stm32_dma.h"
#include "definitions.h"
#include <string.h>
// 0.5uS (2Mhz)
#define STM32_DEFAULT_TIMER_FREQ 2000000
#define STM32_DEFAULT_TIMER_AUTORELOAD 65535
// TODO:
// - DMA IRQ prio configurable (now 7)
// - Timer IRQ prio configurable (now 7)
void stm32_pulse_init(const stm32_pulse_timer_t* tim, uint32_t freq)
{
if (tim->DMA_TC_CallbackPtr) {
memset(tim->DMA_TC_CallbackPtr, 0, sizeof(stm32_pulse_dma_tc_cb_t));
}
LL_GPIO_InitTypeDef pinInit;
LL_GPIO_StructInit(&pinInit);
pinInit.Pin = tim->GPIO_Pin;
pinInit.Mode = LL_GPIO_MODE_ALTERNATE;
pinInit.Alternate = tim->GPIO_Alternate;
LL_GPIO_Init(tim->GPIOx, &pinInit);
LL_TIM_InitTypeDef timInit;
LL_TIM_StructInit(&timInit);
if (!freq) {
freq = STM32_DEFAULT_TIMER_FREQ;
}
timInit.Prescaler = __LL_TIM_CALC_PSC(tim->TIM_Freq, freq);
timInit.Autoreload = STM32_DEFAULT_TIMER_AUTORELOAD;
stm32_timer_enable_clock(tim->TIMx);
LL_TIM_Init(tim->TIMx, &timInit);
if (tim->DMAx) {
// Enable DMA IRQ
NVIC_EnableIRQ(tim->DMA_IRQn);
NVIC_SetPriority(tim->DMA_IRQn, 7);
}
// Enable timer IRQ
NVIC_EnableIRQ(tim->TIM_IRQn);
NVIC_SetPriority(tim->TIM_IRQn, 7);
}
void stm32_pulse_deinit(const stm32_pulse_timer_t* tim)
{
// Disable IRQs
if (tim->DMAx) {
NVIC_DisableIRQ(tim->DMA_IRQn);
}
NVIC_DisableIRQ(tim->TIM_IRQn);
if (tim->DMAx) {
LL_DMA_DeInit(tim->DMAx, tim->DMA_Stream);
}
if (tim->DMA_TC_CallbackPtr) {
memset(tim->DMA_TC_CallbackPtr, 0, sizeof(stm32_pulse_dma_tc_cb_t));
}
LL_TIM_DeInit(tim->TIMx);
stm32_timer_disable_clock(tim->TIMx);
// Reconfigure pin as input
LL_GPIO_InitTypeDef pinInit;
LL_GPIO_StructInit(&pinInit);
pinInit.Pin = tim->GPIO_Pin;
pinInit.Mode = LL_GPIO_MODE_INPUT;
LL_GPIO_Init(tim->GPIOx, &pinInit);
}
void stm32_pulse_config_output(const stm32_pulse_timer_t* tim, bool polarity,
uint32_t ocmode, uint32_t cmp_val)
{
LL_TIM_OC_InitTypeDef ocInit;
LL_TIM_OC_StructInit(&ocInit);
ocInit.OCMode = ocmode;
ocInit.CompareValue = cmp_val;
uint32_t channel = tim->TIM_Channel;
if (tim->TIM_Channel != LL_TIM_CHANNEL_CH1N) {
ocInit.OCState = LL_TIM_OCSTATE_ENABLE;
ocInit.OCNState = LL_TIM_OCSTATE_DISABLE;
} else {
ocInit.OCState = LL_TIM_OCSTATE_DISABLE;
ocInit.OCNState = LL_TIM_OCSTATE_ENABLE;
channel = LL_TIM_CHANNEL_CH1;
}
uint32_t ll_polarity;
if (polarity) {
ll_polarity = LL_TIM_OCPOLARITY_HIGH;
} else {
ll_polarity = LL_TIM_OCPOLARITY_LOW;
}
if (tim->TIM_Channel != LL_TIM_CHANNEL_CH1N) {
ocInit.OCPolarity = ll_polarity;
} else {
ocInit.OCNPolarity = ll_polarity;
}
LL_TIM_OC_Init(tim->TIMx, channel, &ocInit);
LL_TIM_OC_EnablePreload(tim->TIMx, channel);
if (IS_TIM_BREAK_INSTANCE(tim->TIMx)) {
LL_TIM_EnableAllOutputs(tim->TIMx);
}
}
void stm32_pulse_set_polarity(const stm32_pulse_timer_t* tim, bool polarity)
{
uint32_t ll_polarity;
if (polarity) {
ll_polarity = LL_TIM_OCPOLARITY_HIGH;
} else {
ll_polarity = LL_TIM_OCPOLARITY_LOW;
}
LL_TIM_OC_SetPolarity(tim->TIMx, tim->TIM_Channel, ll_polarity);
}
bool stm32_pulse_get_polarity(const stm32_pulse_timer_t* tim)
{
return LL_TIM_OC_GetPolarity(tim->TIMx, tim->TIM_Channel) ==
LL_TIM_OCPOLARITY_HIGH;
}
// return true if stopped, false otherwise
bool stm32_pulse_if_not_running_disable(const stm32_pulse_timer_t* tim)
{
if (LL_DMA_IsEnabledStream(tim->DMAx, tim->DMA_Stream))
return false;
// disable timer
LL_TIM_DisableCounter(tim->TIMx);
LL_TIM_DisableIT_UPDATE(tim->TIMx);
return true;
}
static void set_compare_reg(const stm32_pulse_timer_t* tim, uint32_t val)
{
switch(tim->TIM_Channel){
case LL_TIM_CHANNEL_CH1:
case LL_TIM_CHANNEL_CH1N:
LL_TIM_OC_SetCompareCH1(tim->TIMx, val);
break;
case LL_TIM_CHANNEL_CH2:
LL_TIM_OC_SetCompareCH2(tim->TIMx, val);
break;
case LL_TIM_CHANNEL_CH3:
LL_TIM_OC_SetCompareCH3(tim->TIMx, val);
break;
case LL_TIM_CHANNEL_CH4:
LL_TIM_OC_SetCompareCH4(tim->TIMx, val);
break;
}
}
void stm32_pulse_set_cmp_val(const stm32_pulse_timer_t* tim, uint32_t cmp_val)
{
set_compare_reg(tim, cmp_val);
}
static void set_oc_mode(const stm32_pulse_timer_t* tim, uint32_t ocmode)
{
uint32_t channel = tim->TIM_Channel;
if (channel == LL_TIM_CHANNEL_CH1N)
channel = LL_TIM_CHANNEL_CH1;
LL_TIM_OC_SetMode(tim->TIMx, channel, ocmode);
}
void stm32_pulse_wait_for_completed(const stm32_pulse_timer_t* tim)
{
uint32_t channel = tim->TIM_Channel;
if (channel == LL_TIM_CHANNEL_CH1N)
channel = LL_TIM_CHANNEL_CH1;
while(LL_TIM_IsEnabledCounter(tim->TIMx) &&
LL_TIM_OC_GetMode(tim->TIMx, channel) != LL_TIM_OCMODE_FORCED_INACTIVE);
}
static void force_start_level(const stm32_pulse_timer_t* tim)
{
uint32_t channel = tim->TIM_Channel;
if (channel == LL_TIM_CHANNEL_CH1N)
channel = LL_TIM_CHANNEL_CH1;
uint32_t mode = LL_TIM_OC_GetMode(tim->TIMx, channel);
LL_TIM_OC_SetMode(tim->TIMx, channel, LL_TIM_OCMODE_FORCED_ACTIVE);
LL_TIM_OC_SetMode(tim->TIMx, channel, mode);
}
void stm32_pulse_start_dma_req(const stm32_pulse_timer_t* tim,
const void* pulses, uint16_t length,
uint32_t ocmode, uint32_t cmp_val)
{
// Re-configure timer output
set_compare_reg(tim, cmp_val);
set_oc_mode(tim, ocmode);
// re-init DMA stream
LL_DMA_DeInit(tim->DMAx, tim->DMA_Stream);
LL_DMA_InitTypeDef dmaInit;
LL_DMA_StructInit(&dmaInit);
// Direction
dmaInit.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
// Source
dmaInit.MemoryOrM2MDstAddress = CONVERT_PTR_UINT(pulses);
dmaInit.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
// Destination
dmaInit.PeriphOrM2MSrcAddress = CONVERT_PTR_UINT(&tim->TIMx->ARR);
dmaInit.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
// Data width
if (IS_TIM_32B_COUNTER_INSTANCE(tim->TIMx)) {
// TODO: try using 16-bit source as well
dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
} else {
dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
}
dmaInit.NbData = length;
dmaInit.Channel = tim->DMA_Channel;
dmaInit.Priority = LL_DMA_PRIORITY_VERYHIGH;
LL_DMA_Init(tim->DMAx, tim->DMA_Stream, &dmaInit);
// Enable TC IRQ
LL_DMA_EnableIT_TC(tim->DMAx, tim->DMA_Stream);
if (ocmode == LL_TIM_OCMODE_PWM1) {
// preloads first period for PWM)
LL_TIM_SetCounter(tim->TIMx, 0x00);
LL_TIM_GenerateEvent_UPDATE(tim->TIMx);
} else {
// Reset counter close to overflow
if (IS_TIM_32B_COUNTER_INSTANCE(tim->TIMx)) {
LL_TIM_SetCounter(tim->TIMx, 0xFFFFFFFF);
} else {
LL_TIM_SetCounter(tim->TIMx, 0xFFFF);
}
}
LL_TIM_EnableDMAReq_UPDATE(tim->TIMx);
LL_DMA_EnableStream(tim->DMAx, tim->DMA_Stream);
// Trigger update to effect the first DMA transaction
// and thus load ARR with the first duration
// start timer
LL_TIM_EnableCounter(tim->TIMx);
}
void stm32_pulse_dma_tc_isr(const stm32_pulse_timer_t* tim)
{
if (!stm32_dma_check_tc_flag(tim->DMAx, tim->DMA_Stream))
return;
if (tim->DMA_TC_CallbackPtr) {
auto closure = tim->DMA_TC_CallbackPtr;
if (closure->cb && closure->cb(closure->ctx)) {
return;
}
}
LL_TIM_ClearFlag_UPDATE(tim->TIMx);
LL_TIM_EnableIT_UPDATE(tim->TIMx);
// PWM mode with compare value = 0 then OCxRef is held at ‘0’
set_compare_reg(tim, 0);
set_oc_mode(tim, LL_TIM_OCMODE_PWM1);
}
void stm32_pulse_tim_update_isr(const stm32_pulse_timer_t* tim)
{
if (!LL_TIM_IsActiveFlag_UPDATE(tim->TIMx))
return;
LL_TIM_ClearFlag_UPDATE(tim->TIMx);
LL_TIM_DisableIT_UPDATE(tim->TIMx);
// Halt pulses by forcing to inactive level
set_oc_mode(tim, LL_TIM_OCMODE_FORCED_INACTIVE);
LL_TIM_DisableCounter(tim->TIMx);
}
// input mode
void stm32_pulse_config_input(const stm32_pulse_timer_t* tim)
{
LL_TIM_IC_InitTypeDef icInit;
LL_TIM_IC_StructInit(&icInit);
icInit.ICActiveInput = LL_TIM_ACTIVEINPUT_DIRECTTI;
icInit.ICFilter = LL_TIM_IC_FILTER_FDIV1_N8;
LL_TIM_IC_Init(tim->TIMx, tim->TIM_Channel, &icInit);
}