ST意法半导体
直播中

刘继牛

7年用户 942经验值
私信 关注
[问答]

STM32H7 DMA为什么没有将正确的值传输到TIM15 CCR1寄存器?

大家好,

我觉得我已经没有办法解释为什么会发生这种情况,因为 RM 对于使用定时器设置 DMA + PWM 并不那么准确。

目标是将 tiM15 用作 PWM 发生器,以 800kHz 的频率运行,直到输出 24 个脉冲,然后停止,直到我命令它再次运行。在这 24 个脉冲中,可能会出现不同的占空比,高电平为 64%,低电平为 32%。该 TIM15 使用重复计数器寄存器来指示 24 个脉冲何时过去。

到目前为止,我所做的是一个很好的 24 脉冲 PWM 流,但只有低 (32%) 存在,即使我传输到 CCR1 的缓冲区包含高值 (64%)

TIM15 的配置如下:
  • LL_DMA_InitTypeDef      WS2812B_Config_DMA;
  •         LL_GPIO_InitTypeDef     WS2812B_Config_GPIO;
  •         LL_TIM_InitTypeDef      WS2812B_Config_TIM15;
  •         LL_TIM_OC_InitTypeDef   WS2812B_Config_OC;
  •         LL_TIM_BDTR_InitTypeDef WS2812B_Config_BDTR;
  •         LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM15);
  •         LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
  •         LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
  •         LL_TIM_StructInit(&WS2812B_Config_TIM15);
  •         LL_TIM_OC_StructInit(&WS2812B_Config_OC);
  •         LL_TIM_BDTR_StructInit(&WS2812B_Config_BDTR);
  •         LL_GPIO_StructInit(&WS2812B_Config_GPIO);
  •         LL_DMA_StructInit(&WS2812B_Config_DMA);
  •         //Configure GPIO
  •         WS2812B_Config_GPIO.Alternate  = LL_GPIO_AF_4;
  •         WS2812B_Config_GPIO.Mode       = LL_GPIO_MODE_ALTERNATE;
  •         WS2812B_Config_GPIO.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  •         WS2812B_Config_GPIO.Pin        = LL_GPIO_PIN_2;
  •         WS2812B_Config_GPIO.Pull       = LL_GPIO_PULL_DOWN;
  •         WS2812B_Config_GPIO.Speed      = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  •         LL_GPIO_Init(GPIOA, &WS2812B_Config_GPIO);
  •         //Configure DMA
  •         WS2812B_Config_DMA.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
  •         WS2812B_Config_DMA.FIFOMode = LL_DMA_FIFOMODE_DISABLE;
  •         WS2812B_Config_DMA.MemBurst = LL_DMA_MBURST_SINGLE;
  •         WS2812B_Config_DMA.MemoryOrM2MDstAddress = (uint32_t)WS28128B_BUFF;
  •         WS2812B_Config_DMA.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
  •         WS2812B_Config_DMA.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
  •         WS2812B_Config_DMA.Mode = LL_DMA_MODE_NORMAL;
  •         WS2812B_Config_DMA.NbData = MAX_BUFF;
  •         WS2812B_Config_DMA.PeriphBurst = LL_DMA_PBURST_SINGLE;
  •         WS2812B_Config_DMA.PeriphOrM2MSrcAddress = (uint32_t)&TIM15->CCR1;
  •         WS2812B_Config_DMA.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
  •         WS2812B_Config_DMA.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
  •         WS2812B_Config_DMA.PeriphRequest = LL_DMAMUX1_REQ_TIM15_UP;
  •         WS2812B_Config_DMA.Priority = LL_DMA_PRIORITY_LOW;
  •         //Interrupt: DMA
  •         LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_4);
  •         NVIC_EnableIRQ(DMA1_Stream4_IRQn);
  •         NVIC_SetPriority(DMA1_Stream4_IRQn, 0);
  •         NVIC_EnableIRQ(TIM15_IRQn);
  •         NVIC_SetPriority(TIM15_IRQn, 0);
  •         LL_DMA_Init(DMA1, LL_DMA_STREAM_4, &WS2812B_Config_DMA);
  •         WS2812B_Config_TIM15.Autoreload    = (240e6/WS2812B_FREQ) - 1;
  •         WS2812B_Config_TIM15.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  •         WS2812B_Config_TIM15.CounterMode   = LL_TIM_COUNTERMODE_UP;
  •         WS2812B_Config_TIM15.Prescaler     = 0;
  •         WS2812B_Config_TIM15.RepetitionCounter = MAX_BUFF - 1;
  •         LL_TIM_Init(TIM15, &WS2812B_Config_TIM15);
  •         WS2812B_Config_OC.CompareValue = 0;
  •         WS2812B_Config_OC.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
  •         WS2812B_Config_OC.OCMode = LL_TIM_OCMODE_PWM1;
  •         WS2812B_Config_OC.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
  •         WS2812B_Config_OC.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;
  •         WS2812B_Config_OC.OCNState = LL_TIM_OCSTATE_DISABLE;
  •         WS2812B_Config_OC.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
  •         WS2812B_Config_OC.OCState = LL_TIM_OCSTATE_ENABLE;
  •         LL_TIM_OC_Init(TIM15, LL_TIM_CHANNEL_CH1, &WS2812B_Config_OC);
  •         WS2812B_Config_BDTR.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;
  •         WS2812B_Config_BDTR.Break2Filter = LL_TIM_BREAK2_FILTER_FDIV1;
  •         WS2812B_Config_BDTR.Break2Polarity = LL_TIM_BREAK2_POLARITY_LOW;
  •         WS2812B_Config_BDTR.Break2State = LL_TIM_BREAK2_DISABLE;
  •         WS2812B_Config_BDTR.BreakFilter = LL_TIM_BREAK_FILTER_FDIV1;
  •         WS2812B_Config_BDTR.BreakPolarity = LL_TIM_BREAK_POLARITY_LOW;
  •         WS2812B_Config_BDTR.BreakState = LL_TIM_BREAK_DISABLE;
  •         WS2812B_Config_BDTR.DeadTime = 0;
  •         WS2812B_Config_BDTR.LockLevel = LL_TIM_LOCKLEVEL_OFF;
  •         WS2812B_Config_BDTR.OSSIState = LL_TIM_OSSI_DISABLE;
  •         WS2812B_Config_BDTR.OSSRState = LL_TIM_OSSR_DISABLE;
  •         LL_TIM_BDTR_Init(TIM15, &WS2812B_Config_BDTR);
  •         LL_TIM_OC_EnablePreload(TIM15, LL_TIM_CHANNEL_CH1);
  •         LL_TIM_EnableARRPreload(TIM15);
  •         LL_TIM_EnableDMAReq_UPDATE(TIM15);
  •         LL_TIM_EnableIT_UPDATE(TIM15);
  •         LL_TIM_GenerateEvent_UPDATE(TIM15);
  •         LL_TIM_ClearFlag_UPDATE(TIM15);
  •         LL_TIM_EnableAllOutputs(TIM15);
当前激活的中断是 DMA1_Stream 4 Transfer Complete 和 TIM15_Update。对于 DMA,我只是清除 TC4 中断以指示缓冲区中的所有内容都已传输到 CCR1 并清除它。
TIM15_Update 中断表明 24 个脉冲已经消失,我相应地处理它

他们如何设置的中断是一样的:
  • extern "C" void DMA1_Stream4_IRQHandler() {
  •         if (LL_DMA_IsActiveFlag_TC4(DMA1)) {
  •                 LL_DMA_ClearFlag_TC4(DMA1);
  •         }
  • }
  • extern "C" void TIM15_IRQHandler() {
  •         if (LL_TIM_IsActiveFlag_UPDATE(TIM15)) {
  •                 LL_TIM_ClearFlag_UPDATE(TIM15);
  •                 LL_TIM_DisableCounter(TIM15);
  •                 LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_4);
  •                 WS2812B::block = false;
  •         }
  • }
我一直在使用的缓冲区内容:
低 = 0x5F = 32% 占空比
高 = 0xBF = 64% 占空比
  • WS28128B_BUFF[0]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[1]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[2]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[3]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[4]        uint32_t        0xbf (Hex)
  • WS28128B_BUFF[5]        uint32_t        0xbf (Hex)
  • WS28128B_BUFF[6]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[7]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[8]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[9]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[10]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[11]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[12]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[13]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[14]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[15]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[16]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[17]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[18]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[19]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[20]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[21]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[22]        uint32_t        0x5f (Hex)
  • WS28128B_BUFF[23]        uint32_t        0x5f (Hex)
逻辑分析仪图片

为什么我看不到我的 HIGH?



回帖(1)

王雪

2022-12-27 10:17:00
我的猜测:因为 DMA 在 24 个脉冲后触发,而不是在每个脉冲上触发,这是使用重复计数器的结果。此外,确保 CCR1 已缓冲。
另一个问题:缓冲区包含 32 位数据,但 DMA 被编程为 16 位字。

老实说,至少有两种更有效的方式来输出 WS2812 数据——使用 UART 或 SPI。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分