[英]STM32F7 Two Timer Channels Share One DMA Channel
I am trying to write a driver that sends DShot commands to an ESC.我正在尝试编写一个将DShot 命令发送到 ESC 的驱动程序。 I am using an STM32F722 MCU.
我正在使用 STM32F722 MCU。 The DShot protocol is similar to addressable LEDs where 1/3 duty cycle represents a 0 and a 2/3 duty cycle represents a 1. The frequency is 600kHz for DShot600.
DShot 协议类似于可寻址 LED,其中 1/3 占空比代表 0,2/3 占空比代表 1。DShot600 的频率为 600kHz。
All of my PWM channels are on TIM2.我所有的 PWM 通道都在 TIM2 上。 The problem is that TIM2_CH2 and TIM2_CH4 both share DMA1_Stream6_CH3.
问题是 TIM2_CH2 和 TIM2_CH4 都共享 DMA1_Stream6_CH3。 TIM2_CH4 also exists on DMA1_Stream7_CH3.
TIM2_CH4 也存在于 DMA1_Stream7_CH3 上。
However, when I try to enable outputs on both TIM2_CH2 and TIM2_CH4, the outputs start looking weird (see pic below).
但是,当我尝试在 TIM2_CH2 和 TIM2_CH4 上启用输出时,输出开始看起来很奇怪(见下图)。 If I disable TIM2_CH2, the outputs on the 3 remaining channels look perfect (see pic below).
如果我禁用 TIM2_CH2,其余 3 个通道的输出看起来很完美(见下图)。
I think I might have to enable or disable the channels in a certain fashion to ensure they don't run at the same time.我想我可能必须以某种方式启用或禁用通道,以确保它们不会同时运行。 However, I need them to both output at the same time.
但是,我需要它们同时使用 output。 Is this possible?
这可能吗?
All relevant code is here.所有相关代码都在这里。 Fair warning, I don't use any HAL or LL libraries so it's a bit lengthy.
公平的警告,我不使用任何 HAL 或 LL 库,所以它有点冗长。
main.c
#include "stm32f7xx.h"
#include "pwm.h"
#include "rcc.h"
#include "dwt.h"
#include "usart.h"
#include "drv_dshot.h"
uint16_t motor_value[4] = {2000, 2000, 2000, 2000};
int main(void){
RCC_216MHz_Init();
DWT_Init();
dshot_init();
while(1){
dshot_write(motor_value);
delay(1);
}
}
void DMA1_Stream5_IRQHandler(void){
if(DMA1->HISR & DMA_HISR_TCIF5){
DMA1_Stream5->CR &= ~DMA_SxCR_EN;
while(DMA1_Stream5->CR & DMA_SxCR_EN){}
TIM2->DIER &= ~TIM_DIER_CC1DE;
DMA1->HIFCR |= DMA_HIFCR_CTCIF5;
DMA1_Stream5->NDTR = DSHOT_DMA_BUFFER_SIZE;
}
}
void DMA1_Stream6_IRQHandler(void){
if(DMA1->HISR & DMA_HISR_TCIF6){
DMA1_Stream6->CR &= ~DMA_SxCR_EN;
while(DMA1_Stream6->CR & DMA_SxCR_EN){}
TIM2->DIER &= ~TIM_DIER_CC2DE;
DMA1->HIFCR |= DMA_HIFCR_CTCIF6;
DMA1_Stream6->NDTR = DSHOT_DMA_BUFFER_SIZE;
}
}
void DMA1_Stream1_IRQHandler(void){
if(DMA1->LISR & DMA_LISR_TCIF1){
DMA1_Stream1->CR &= ~DMA_SxCR_EN;
while(DMA1_Stream1->CR & DMA_SxCR_EN){}
TIM2->DIER &= ~TIM_DIER_CC3DE;
DMA1->LIFCR |= DMA_LIFCR_CTCIF1;
DMA1_Stream1->NDTR = DSHOT_DMA_BUFFER_SIZE;
}
}
void DMA1_Stream7_IRQHandler(void){
if(DMA1->HISR & DMA_HISR_TCIF7){
DMA1_Stream7->CR &= ~DMA_SxCR_EN;
while(DMA1_Stream7->CR & DMA_SxCR_EN){}
TIM2->DIER &= ~TIM_DIER_CC4DE;
DMA1->HIFCR |= DMA_HIFCR_CTCIF7;
DMA1_Stream7->NDTR = DSHOT_DMA_BUFFER_SIZE;
}
}
drv_dshot.c
#include "stm32f7xx.h"
#include "stdbool.h"
#include "drv_dshot.h"
static uint32_t motor1_dmabuffer[DSHOT_DMA_BUFFER_SIZE];
static uint32_t motor2_dmabuffer[DSHOT_DMA_BUFFER_SIZE];
static uint32_t motor3_dmabuffer[DSHOT_DMA_BUFFER_SIZE];
static uint32_t motor4_dmabuffer[DSHOT_DMA_BUFFER_SIZE];
static uint16_t dshot_prepare_packet(uint16_t value);
static void dshot_prepare_dmabuffer_all(uint16_t *motor_value);
static void dshot_prepare_dmabuffer(uint32_t *motor_dmabuffer, uint16_t value);
static uint16_t dshot_prepare_packet(uint16_t value);
static void dshot_dma_start(void);
static void dshot_enable_dma_request(void);
void dshot_write(uint16_t *motor_value){
dshot_prepare_dmabuffer_all(motor_value);
dshot_enable_dma_request();
dshot_dma_start();
}
static void dshot_prepare_dmabuffer_all(uint16_t *motor_value){
dshot_prepare_dmabuffer(motor1_dmabuffer, motor_value[0]);
dshot_prepare_dmabuffer(motor2_dmabuffer, motor_value[1]);
DMA1_Stream1->NDTR |= DSHOT_DMA_BUFFER_SIZE;
dshot_prepare_dmabuffer(motor3_dmabuffer, motor_value[2]);
DMA1_Stream6->NDTR |= DSHOT_DMA_BUFFER_SIZE;
dshot_prepare_dmabuffer(motor4_dmabuffer, motor_value[3]);
DMA1_Stream5->NDTR |= DSHOT_DMA_BUFFER_SIZE;
}
static void dshot_prepare_dmabuffer(uint32_t *motor_dmabuffer, uint16_t value)
{
uint16_t packet;
packet = dshot_prepare_packet(value);
for(int i = 0; i < 16; i++)
{
motor_dmabuffer[i] = (packet & 0x8000) ? MOTOR_BIT_1 : MOTOR_BIT_0;
packet <<= 1;
}
motor_dmabuffer[16] = 0;
motor_dmabuffer[17] = 0;
}
static uint16_t dshot_prepare_packet(uint16_t value){
uint16_t packet;
bool dshot_telemetry = false;
packet = (value << 1) | (dshot_telemetry ? 1 : 0);
// compute checksum
unsigned csum = 0;
unsigned csum_data = packet;
for(int i = 0; i < 3; i++)
{
csum ^= csum_data; // xor data by nibbles
csum_data >>= 4;
}
csum &= 0xf;
packet = (packet << 4) | csum;
return packet;
}
static void dshot_dma_start(void){
TIM2->CNT = 0;
TIM2->DIER |= TIM_DIER_CC1DE;
TIM2->DIER |= TIM_DIER_CC2DE;
TIM2->DIER |= TIM_DIER_CC3DE;
TIM2->DIER |= TIM_DIER_CC4DE;
}
static void dshot_enable_dma_request(void){
DMA1_Stream5->CR |= DMA_SxCR_EN;
DMA1_Stream6->CR |= DMA_SxCR_EN;
DMA1_Stream1->CR |= DMA_SxCR_EN;
DMA1_Stream7->CR |= DMA_SxCR_EN;
}
void dshot_init(void){
/////////////////GPIO INIT///////////////////
// enable clock for GPIOA
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
// set mode, speed, type, pull, AF
GPIOA->MODER &= ~GPIO_MODER_MODER0;
GPIOA->MODER |= GPIO_MODER_MODER0_1;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEEDR0;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT0;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR0;
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL0;
GPIOA->AFR[0] |= (GPIO_AF1_TIM2 << (4U * 0U));
GPIOA->MODER &= ~GPIO_MODER_MODER1;
GPIOA->MODER |= GPIO_MODER_MODER1_1;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEEDR1;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT1;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR1;
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL1;
GPIOA->AFR[0] |= (GPIO_AF1_TIM2 << (4U * 1U));
GPIOA->MODER &= ~GPIO_MODER_MODER2;
GPIOA->MODER |= GPIO_MODER_MODER2_1;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEEDR2;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT2;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR2;
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL2;
GPIOA->AFR[0] |= (GPIO_AF1_TIM2 << (4U * 2U));
GPIOA->MODER &= ~GPIO_MODER_MODER3;
GPIOA->MODER |= GPIO_MODER_MODER3_1;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEEDR3;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT3;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR3;
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL3;
GPIOA->AFR[0] |= (GPIO_AF1_TIM2 << (4U * 3U));
/////////////////TIMER INIT///////////////////
// enable clock for TIM2
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->CR1 &= ~TIM_CR1_CEN;
// set PSC, AR, clock div, cnt, cnt mode
TIM2->PSC = 0; //(uint16_t)((float) TIMER_CLOCK / 12000000) - 1;
TIM2->ARR = MOTOR_BITLENGTH;
TIM2->CR1 &= ~TIM_CR1_CKD;
TIM2->CR1 &= ~TIM_CR1_DIR;
// set output compare mode
// channel 1
TIM2->CCER &= ~TIM_CCER_CC1E;
TIM2->CCMR1 |= TIM_CCMR1_OC1M_1 |
TIM_CCMR1_OC1M_2;
TIM2->CCER &= ~TIM_CCER_CC1P;
TIM2->CCR1 = 0;
TIM2->CCER |= TIM_CCER_CC1E;
// channel 2
TIM2->CCER &= ~TIM_CCER_CC2E;
TIM2->CCMR1 |= TIM_CCMR1_OC2M_1 |
TIM_CCMR1_OC2M_2;
TIM2->CCER &= ~TIM_CCER_CC2P;
TIM2->CCR2 = 0;
TIM2->CCER |= TIM_CCER_CC2E;
// channel 3
TIM2->CCER &= ~TIM_CCER_CC3E;
TIM2->CCMR2 |= TIM_CCMR2_OC3M_1 |
TIM_CCMR2_OC3M_2;
TIM2->CCER &= ~TIM_CCER_CC3P;
TIM2->CCR3 = 0;
TIM2->CCER |= TIM_CCER_CC3E;
// channel 4
TIM2->CCER &= ~TIM_CCER_CC4E;
TIM2->CCMR2 |= TIM_CCMR2_OC4M_1 |
TIM_CCMR2_OC4M_2;
TIM2->CCER &= ~TIM_CCER_CC4P;
TIM2->CCR4 = 0;
TIM2->CCER |= TIM_CCER_CC4E;
// enable preload
TIM2->CCMR1 |= TIM_CCMR1_OC1PE;
TIM2->CCMR1 |= TIM_CCMR1_OC2PE;
TIM2->CCMR2 |= TIM_CCMR2_OC3PE;
TIM2->CCMR2 |= TIM_CCMR2_OC4PE;
//
TIM2->CR1 |= TIM_CR1_ARPE;
// enable the counter
TIM2->CR1 |= TIM_CR1_CEN;
/////////////////DMA INIT///////////////////
/*
* TIM2_CH1 on Stream5_CH3
* TIM2_CH2 on Stream6_CH3
* TIM2_CH3 on Stream1_CH3
* TIM2_CH4 on Stream7_CH3
*/
// reset DMA
RCC->AHB1RSTR |= RCC_AHB1RSTR_DMA1RST;
RCC->AHB1RSTR &= ~RCC_AHB1RSTR_DMA1RST;
// disable DMA stream 5
DMA1_Stream5->CR &= ~DMA_SxCR_EN;
while(DMA1_Stream5->CR & DMA_SxCR_EN){}
DMA1_Stream5->CR = 0;
DMA1_Stream5->NDTR = 0;
DMA1_Stream5->PAR = 0;
DMA1_Stream5->M0AR = 0;
DMA1_Stream5->M1AR = 0;
DMA1_Stream5->FCR = 0x00000021U;
DMA1_Stream5->CR &= ~DMA_SxCR_CHSEL;
DMA1->HIFCR |= 0x00000F40U;
// disable DMA stream 6
DMA1_Stream6->CR &= ~DMA_SxCR_EN;
while(DMA1_Stream6->CR & DMA_SxCR_EN){}
DMA1_Stream6->CR = 0;
DMA1_Stream6->NDTR = 0;
DMA1_Stream6->PAR = 0;
DMA1_Stream6->M0AR = 0;
DMA1_Stream6->M1AR = 0;
DMA1_Stream6->FCR = 0x00000021U;
DMA1_Stream6->CR &= ~DMA_SxCR_CHSEL;
DMA1->HIFCR |= 0x003F0000U;
// disable DMA stream 1
DMA1_Stream1->CR &= ~DMA_SxCR_EN;
while(DMA1_Stream1->CR & DMA_SxCR_EN){}
DMA1_Stream1->CR = 0;
DMA1_Stream1->NDTR = 0;
DMA1_Stream1->PAR = 0;
DMA1_Stream1->M0AR = 0;
DMA1_Stream1->M1AR = 0;
DMA1_Stream1->FCR = 0x00000021U;
DMA1_Stream1->CR &= ~DMA_SxCR_CHSEL;
DMA1->LIFCR |= 0x00000F40U;
// disable DMA stream 7
DMA1_Stream7->CR &= ~DMA_SxCR_EN;
while(DMA1_Stream7->CR & DMA_SxCR_EN){}
DMA1_Stream7->CR = 0;
DMA1_Stream7->NDTR = 0;
DMA1_Stream7->PAR = 0;
DMA1_Stream7->M0AR = 0;
DMA1_Stream7->M1AR = 0;
DMA1_Stream7->FCR = 0x00000021U;
DMA1_Stream7->CR &= ~DMA_SxCR_CHSEL;
DMA1->HIFCR |= 0x0F400000U;
// enable clock
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
// enable interrupts
NVIC_SetPriority(DMA1_Stream5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream5_IRQn);
NVIC_SetPriority(DMA1_Stream6_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream6_IRQn);
NVIC_SetPriority(DMA1_Stream1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream1_IRQn);
NVIC_SetPriority(DMA1_Stream7_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Stream7_IRQn);
// motor 4 DMA settings
DMA1_Stream5->CR |= (0x3 << 25U);
DMA1_Stream5->M0AR = (uint32_t)motor4_dmabuffer;
DMA1_Stream5->CR |= DMA_SxCR_DIR_0; // mem to per
DMA1_Stream5->FCR |= DMA_SxFCR_DMDIS; // fifo en
DMA1_Stream5->FCR &= ~DMA_SxFCR_FTH; //1/4 full
DMA1_Stream5->CR &= ~DMA_SxCR_MBURST;
DMA1_Stream5->CR &= ~DMA_SxCR_PBURST;
DMA1_Stream5->PAR = (uint32_t)(&(TIM2->CCR1));
DMA1_Stream5->NDTR = DSHOT_DMA_BUFFER_SIZE;
DMA1_Stream5->CR &= ~DMA_SxCR_PINC;
DMA1_Stream5->CR |= DMA_SxCR_MINC;
DMA1_Stream5->CR |= DMA_SxCR_MSIZE_1;
DMA1_Stream5->CR |= DMA_SxCR_PSIZE_1;
DMA1_Stream5->CR &= ~DMA_SxCR_CIRC;
DMA1_Stream5->CR |= DMA_SxCR_PL_0;
// DMA transfer complete interrupt enable
DMA1_Stream5->CR |= DMA_SxCR_TCIE;
// motor 4 DMA settings
DMA1_Stream6->CR |= (0x3 << 25U);
DMA1_Stream6->M0AR = (uint32_t)motor3_dmabuffer;
DMA1_Stream6->CR |= DMA_SxCR_DIR_0; // mem to per
DMA1_Stream6->FCR |= DMA_SxFCR_DMDIS; // fifo en
DMA1_Stream6->FCR &= ~DMA_SxFCR_FTH; //1/4 full
DMA1_Stream6->CR &= ~DMA_SxCR_MBURST;
DMA1_Stream6->CR &= ~DMA_SxCR_PBURST;
DMA1_Stream6->PAR = (uint32_t)(&(TIM2->CCR2));
DMA1_Stream6->NDTR = DSHOT_DMA_BUFFER_SIZE;
DMA1_Stream6->CR &= ~DMA_SxCR_PINC;
DMA1_Stream6->CR |= DMA_SxCR_MINC;
DMA1_Stream6->CR |= DMA_SxCR_MSIZE_1;
DMA1_Stream6->CR |= DMA_SxCR_PSIZE_1;
DMA1_Stream6->CR &= ~DMA_SxCR_CIRC;
DMA1_Stream6->CR |= DMA_SxCR_PL;
// DMA transfer complete interrupt enable
DMA1_Stream6->CR |= DMA_SxCR_TCIE;
// motor 2 DMA settings
DMA1_Stream1->CR |= (0x3 << 25U);
DMA1_Stream1->M0AR = (uint32_t)motor2_dmabuffer;
DMA1_Stream1->CR |= DMA_SxCR_DIR_0; // mem to per
DMA1_Stream1->FCR |= DMA_SxFCR_DMDIS; // fifo en
DMA1_Stream1->FCR &= ~DMA_SxFCR_FTH; //1/4 full
DMA1_Stream1->CR &= ~DMA_SxCR_MBURST;
DMA1_Stream1->CR &= ~DMA_SxCR_PBURST;
DMA1_Stream1->PAR = (uint32_t)(&(TIM2->CCR3));
DMA1_Stream1->NDTR = DSHOT_DMA_BUFFER_SIZE;
DMA1_Stream1->CR &= ~DMA_SxCR_PINC;
DMA1_Stream1->CR |= DMA_SxCR_MINC;
DMA1_Stream1->CR |= DMA_SxCR_MSIZE_1;
DMA1_Stream1->CR |= DMA_SxCR_PSIZE_1;
DMA1_Stream1->CR &= ~DMA_SxCR_CIRC;
DMA1_Stream1->CR |= DMA_SxCR_PL_0;
// DMA transfer complete interrupt enable
DMA1_Stream1->CR |= DMA_SxCR_TCIE;
// motor 4 DMA settings
DMA1_Stream7->CR |= (0x3 << 25U);
DMA1_Stream7->M0AR = (uint32_t)motor1_dmabuffer;
DMA1_Stream7->CR |= DMA_SxCR_DIR_0; // mem to per
DMA1_Stream7->FCR |= DMA_SxFCR_DMDIS; // fifo en
DMA1_Stream7->FCR &= ~DMA_SxFCR_FTH; //1/4 full
DMA1_Stream7->CR &= ~DMA_SxCR_MBURST;
DMA1_Stream7->CR &= ~DMA_SxCR_PBURST;
DMA1_Stream7->PAR = (uint32_t)(&(TIM2->CCR4));
DMA1_Stream7->NDTR = DSHOT_DMA_BUFFER_SIZE;
DMA1_Stream7->CR &= ~DMA_SxCR_PINC;
DMA1_Stream7->CR |= DMA_SxCR_MINC;
DMA1_Stream7->CR |= DMA_SxCR_MSIZE_1;
DMA1_Stream7->CR |= DMA_SxCR_PSIZE_1;
DMA1_Stream7->CR &= ~DMA_SxCR_CIRC;
DMA1_Stream7->CR |= DMA_SxCR_PL_0;
// DMA transfer complete interrupt enable
DMA1_Stream7->CR |= DMA_SxCR_TCIE;
}
drv_dshot.h
/*
* drv_dshot.h
*
* Created on: Dec 29, 2021
* Author: jeremywolfe
*/
#ifndef DRV_DRV_DSHOT_H_
#define DRV_DRV_DSHOT_H_
/* User Configuration */
// Timer Clock
#define TIMER_CLOCK 108000000 // 100MHz
// MOTOR 1 (PA3) - TIM5 Channel 4, DMA1 Stream 3
#define MOTOR_1_TIM (&htim2)
#define MOTOR_1_TIM_CHANNEL TIM_CHANNEL_4
// MOTOR 2 (PA2) - TIM2 Channel 3, DMA1 Stream 1
#define MOTOR_2_TIM (&htim2)
#define MOTOR_2_TIM_CHANNEL TIM_CHANNEL_3
// MOTOR 3 (PA0) - TIM2 Channel 1, DMA1 Stream 5
#define MOTOR_3_TIM (&htim2)
#define MOTOR_3_TIM_CHANNEL TIM_CHANNEL_1
// MOTOR 4 (PA1) - TIM5 Channel 2, DMA1 Stream 4
#define MOTOR_4_TIM (&htim2)
#define MOTOR_4_TIM_CHANNEL TIM_CHANNEL_2
/* Definition */
#define MHZ_TO_HZ(x) ((x) * 1000000)
#define DSHOT600_HZ MHZ_TO_HZ(12)
#define DSHOT300_HZ MHZ_TO_HZ(6)
#define DSHOT150_HZ MHZ_TO_HZ(3)
#define MOTOR_BIT_0 60
#define MOTOR_BIT_1 120
#define MOTOR_BITLENGTH 180
#define DSHOT_FRAME_SIZE 16
#define DSHOT_DMA_BUFFER_SIZE 18 /* resolution + frame reset (2us) */
#define DSHOT_MIN_THROTTLE 48
#define DSHOT_MAX_THROTTLE 2047
#define DSHOT_RANGE (DSHOT_MAX_THROTTLE - DSHOT_MIN_THROTTLE)
/* Functions */
void dshot_init(void);
void dshot_write(uint16_t *motor_value);
#endif /* DRV_DRV_DSHOT_H_ */
I decided to instead use TIM5 because it shares the exact same pins as TIM2.我决定改用 TIM5,因为它与 TIM2 共享完全相同的引脚。 However, TIM5 has no overlaps on the DMA Request Mapping.
但是,TIM5 在 DMA 请求映射上没有重叠。 After changing the code, it works just as intended.
更改代码后,它按预期工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.