你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32F103C8T6 TIM1 CH2输入捕获触发DMA的问题

[复制链接]
托卡马克 提问时间:2025-2-20 11:36 / 未解决
STM32F103C8T6 TIM1 CH2输入捕获触发DMA的问题
最近在基于STM32F103C8T6同时使用LL库调试 TIM1 CH2输入捕获触发DMA的时候遇到点问题:  
我的期望是,通过TIM1 CH2捕获到上升沿后,触发DMA传输,将内存数据搬运到TIM1 CCR1寄存器,以修改PWM占空比;
现状是:
1.DMA TC中断远比我想象来的慢,表现为触发脉冲以及经过数十个后,才能进入一次TC中断;

2.在整个过程中TIM1 CCR1寄存器的值没有改变,那为什么还会触发DMA TC中断呢?整个过程中传输的数据去哪了?

3.在DMA请求映像中 DMA1 CH3可以被TIM1 CH2所触发,但是在CUBEMX中却没有对应的配置,是否意味着这样操作不可行亦或是CUBEMX本身的问题?

期待路过的诸位能帮忙解答,不胜感激!
相关代码如下:
“/* TIM1 init function */void MX_TIM1_Init(void){
/* USER CODE BEGIN TIM1_Init 0 */

/* USER CODE END TIM1_Init 0 */

LL_TIM_InitTypeDef TIM_InitStruct = {0};
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
LL_TIM_BDTR_InitTypeDef TIM_BDTRInitStruct = {0};

LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);

LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
/**TIM1 GPIO Configuration
PA9   ------> TIM1_CH2
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* USER CODE BEGIN TIM1_Init 1 */

/* USER CODE END TIM1_Init 1 */
TIM_InitStruct.Prescaler = 0;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 2;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIM1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1);
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.CompareValue = 0;
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_SetTriggerInput(TIM1, LL_TIM_TS_TI2FP2);
LL_TIM_SetSlaveMode(TIM1, LL_TIM_SLAVEMODE_RESET);
LL_TIM_CC_DisableChannel(TIM1, LL_TIM_CHANNEL_CH2);
LL_TIM_IC_SetFilter(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
LL_TIM_IC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
LL_TIM_DisableIT_TRIG(TIM1);
LL_TIM_DisableDMAReq_TRIG(TIM1);
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET);
LL_TIM_DisableMasterSlaveMode(TIM1);
LL_TIM_IC_SetActiveInput(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
TIM_BDTRInitStruct.OSSRState = LL_TIM_OSSR_DISABLE;
TIM_BDTRInitStruct.OSSIState = LL_TIM_OSSI_DISABLE;
TIM_BDTRInitStruct.LockLevel = LL_TIM_LOCKLEVEL_OFF;
TIM_BDTRInitStruct.DeadTime = 0;
TIM_BDTRInitStruct.BreakState = LL_TIM_BREAK_DISABLE;
TIM_BDTRInitStruct.BreakPolarity = LL_TIM_BREAK_POLARITY_LOW;
TIM_BDTRInitStruct.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;
LL_TIM_BDTR_Init(TIM1, &TIM_BDTRInitStruct);
/* USER CODE BEGIN TIM1_Init 2 */
/* TIM1 OC配置修正 */
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; // 启用主通道
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; // 启用互补通道
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH2);// 启用TIM1通道2的捕获功能/* DMA触发配置修正 */
LL_TIM_SetSlaveMode(TIM1, LL_TIM_SLAVEMODE_TRIGGER);
LL_TIM_DisableDMAReq_UPDATE(TIM1);
LL_TIM_EnableDMAReq_CC2(TIM1); // 使用通道2的捕获事件触发DMA

LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_CIRCULAR);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE);

LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_3, (uint32_t)&TIM1->CCR1);
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_3, (uint32_t)line_buffer0);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, (sizeof(line_buffer0) / sizeof(line_buffer0[0])));
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);

LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_3);

NVIC_SetPriority(DMA1_Channel3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Channel3_IRQn);

LL_TIM_EnableAllOutputs(TIM1);
LL_TIM_EnableCounter(TIM1);

/* USER CODE END TIM1_Init 2 */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
/**TIM1 GPIO Configuration
PB13     ------> TIM1_CH1N
PA8     ------> TIM1_CH1
*/
GPIO_InitStruct.Pin = CURSOR_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(CURSOR_GPIO_Port, &GPIO_InitStruct);

GPIO_InitStruct.Pin = CROSSHAIR_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(CROSSHAIR_GPIO_Port, &GPIO_InitStruct);
}”

触发波形

触发波形
屏幕截图 2025-02-20 113306.png
收藏 评论10 发布时间:2025-2-20 11:36

举报

10个回答
xmshao 回答时间:2025-2-20 14:57:01
对于你的需求,“通过TIM1 CH2捕获到上升沿后,触发DMA传输,将内存数据搬运到TIM1 CCR1寄存器,以修改PWM占空比” ,原理上讲是可以实现的。


这里就你的问题逐一做些交流:


1.DMA TC中断远比我想象来的慢,表现为触发脉冲以及经过数十个后,才能进入一次TC中断;
==》应该没有这个说法。DMA TC中断基于DMA传输完成事件而产生,它的快慢跟一轮传输中请求个数和快慢有关。
比方一轮传输涉及5个请求和50个请求的TC产生时间自然不一样,每1秒产生1个请求和每10秒产生1个请求使得最终产生DMA TC事件的时间肯定也不一样。
具体到你这里,到底触发几次后进TC中断是你决定的。当然,前提是DMA已经正常工作了。


2.在整个过程中TIM1 CCR1寄存器的值没有改变,那为什么还会触发DMA TC中断呢?整个过程中传输的数据去哪了?
==》这个要具体看。首先要看硬件上是否支持,即所选择的DMA通道是否能前往源端和目的端。如果硬件上支持而你实际上又没实现,那就得检查你的配置。


我这边查阅了技术手册相关内容,硬件是支持的。那么问题可能就出在你的配置方面。


3.在DMA请求映像中 DMA1 CH3可以被TIM1 CH2所触发,但是在CUBEMX中却没有对应的配置,是否意味着这样操作不可行亦或是CUBEMX本身的问题?
==》估计你看漏了,或者是哪里理解错了。使用cubeMx配置,可以看到基于TIM1-CH2事件所申请的DMA通道正是DMA1-CH3.


针对上面第2点,我找到F1系列开发板做了进一步验证确认,的确是可以实现的。即TIM1-CH2的捕获事件触发DMA,DMA将内存数据搬运到TIM1-CCR1寄存器以调整PWM输出。我使用HAL库验证,而不是LL库。验证结果可以给你信心。这里特意提醒的是,DMA的源端、目的端别配置错了,尤其是别配反了。





我这里基于CH2的捕获事件触发DMA传输,DMA做4次传输后就 停下,那么CCR1的数据应该停止0x12ab。
butterflyspring 回答时间:2025-2-20 12:01:52
从思路上看,DMA的request 就受限的。 定时器1的通道2 的DMA request 只有从该寄存器捕获值到内存,或内存到CCR2 寄存器。  或者说传递到CCR1的request没有CH2 捕获的RQUEST。 因此硬件不支持。


另外改变CCR去调整PWM,不能立刻生效,否则会引起脉冲过长或过短。 通常都是用更新事件触发的。

TIMER1 CH DMA mode.PNG
托卡马克 回答时间:2025-2-20 13:11:49

butterflyspring 发表于 2025-2-20 12:01
从思路上看,DMA的request 就受限的。 定时器1的通道2 的DMA request 只有从该寄存器捕获值到内存,或内存 ...

感谢!

请问你的意思是通过TI2FP2捕获,产生更新事件,之后由更新事件触发DMA,将内存数据写入CCR1寄存器吗?

托卡马克 回答时间:2025-2-24 13:35:49

xmshao 发表于 2025-2-20 14:57
对于你的需求,“通过TIM1 CH2捕获到上升沿后,触发DMA传输,将内存数据搬运到TIM1 CCR1寄存器,以修改PWM ...

感谢帮助!** **我也用示波器测试过,脉冲的时间间隔约32us,目前使用TIM1 CC2事件触发DMA传输,但现象仍为触发脉冲数十个后才完成TC中断,为此我做了如下验证:

修改DMA触发后传输的数据量为20,LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, 20);

此时经过20个脉冲后,DMA TC中断触发;

同样的,修改DMA触发后传输的数据量为10,LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, 10);

此时经过10个脉冲后,DMA TC中断触发;

也就意味着DMA的传输周期变为了单个触发事件只能传输一次数据,达到预设的数据量后,才完成中断,而不是我设想的每次捕获到脉冲上升沿,触发DMA 传输,将缓冲区内的数据全部传输至CCR1寄存器,请问是什么原因?

配置代码如下:

/ TIM1 init function /** void MX_TIM1_Init(void) **{

/* USER CODE BEGIN TIM1_Init 0 */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
/* USER CODE END TIM1_Init 0 */

LL_TIM_InitTypeDef TIM_InitStruct = {0};
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
LL_TIM_BDTR_InitTypeDef TIM_BDTRInitStruct = {0};

LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);

LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
/**TIM1 GPIO Configuration
PA9   ------> TIM1_CH2
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* TIM1 DMA Init */

/* TIM1_CH2 Init */
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_VERYHIGH);

LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_CIRCULAR);

LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);

LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);

LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_HALFWORD);

LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_HALFWORD);

/* TIM1 interrupt Init */
NVIC_SetPriority(TIM1_CC_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(TIM1_CC_IRQn);

/* USER CODE BEGIN TIM1_Init 1 */

/* USER CODE END TIM1_Init 1 */
TIM_InitStruct.Prescaler = 0;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 2;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIM1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1);
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.CompareValue = 0;
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_OC2REF);
LL_TIM_EnableMasterSlaveMode(TIM1);
LL_TIM_IC_SetActiveInput(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
LL_TIM_IC_SetFilter(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
LL_TIM_IC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
TIM_BDTRInitStruct.OSSRState = LL_TIM_OSSR_DISABLE;
TIM_BDTRInitStruct.OSSIState = LL_TIM_OSSI_DISABLE;
TIM_BDTRInitStruct.LockLevel = LL_TIM_LOCKLEVEL_OFF;
TIM_BDTRInitStruct.DeadTime = 0;
TIM_BDTRInitStruct.BreakState = LL_TIM_BREAK_DISABLE;
TIM_BDTRInitStruct.BreakPolarity = LL_TIM_BREAK_POLARITY_LOW;
TIM_BDTRInitStruct.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;
LL_TIM_BDTR_Init(TIM1, &TIM_BDTRInitStruct);
/* USER CODE BEGIN TIM1_Init 2 */

TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE;
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH2);
LL_TIM_SetUpdateSource(TIM1, LL_TIM_UPDATESOURCE_REGULAR);

LL_TIM_EnableDMAReq_CC2(TIM1);
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_3, (uint32_t)&TIM1->CCR1);
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_3, (uint32_t)line_buffer0);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, 10);

//** LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, (sizeof(line_buffer0) / sizeof(line_buffer0[0]))); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);**

LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_3);

LL_TIM_EnableIT_CC2(TIM1);
NVIC_SetPriority(DMA1_Channel3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_EnableIRQ(DMA1_Channel3_IRQn);

LL_TIM_EnableAllOutputs(TIM1);
LL_TIM_EnableCounter(TIM1);
/* USER CODE END TIM1_Init 2 */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
/**TIM1 GPIO Configuration
PB13     ------> TIM1_CH1N
PA8     ------> TIM1_CH1
*/
GPIO_InitStruct.Pin = CURSOR_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(CURSOR_GPIO_Port, &GPIO_InitStruct);

GPIO_InitStruct.Pin = CROSSHAIR_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(CROSSHAIR_GPIO_Port, &GPIO_InitStruct);

}

DMA中断函数如下:

/**

  • @brief This function handles DMA1 channel3 global interrupt.** */ void DMA1_Channel3_IRQHandler(void) { /* USER CODE BEGIN DMA1_Channel3_IRQn 0 */ if(LL_DMA_IsActiveFlag_TC3(DMA1)) { LL_DMA_ClearFlag_TC3(DMA1); LL_GPIO_TogglePin(DBG_LED_GPIO_Port, DBG_LED_Pin); current_buf ^= 1; target_buffer = current_buf ? line_buffer1 : line_buffer0; } if(LL_DMA_IsActiveFlag_TE3(DMA1)) { LL_DMA_ClearFlag_TE3(DMA1); } ***/ USER CODE END DMA1_Channel3_IRQn 0 / / USER CODE BEGIN DMA1_Channel3_IRQn 1 / / USER CODE END DMA1_Channel3_IRQn 1 */ }**

xmshao 回答时间:2025-2-24 14:52:01
没理解你说的这句: **也就意味着DMA的传输周期变为了单个触发事件只能传输一次数据,达到预设的数据量后,才完成中断,


而不是我设想的每次捕获到脉冲上升沿,触发DMA 传输,将缓冲区内的数据全部传输至CCR1寄存器,请问是什么原因?**


你说 触发1次 要将缓冲区的数据全部传输到CCR1寄存器,如何理解? 触发1次对CCR1连续写N次? 你具体什么意思,能详细点吗?
托卡马克 回答时间:2025-2-24 15:58:44

xmshao 发表于 2025-2-24 14:52
没理解你说的这句: **也就意味着DMA的传输周期变为了单个触发事件只能传输一次数据,达到预设的数据量后, ...

是这样的,我的期望是假设缓冲区内有10个数据,我需要在TIM1 CH2捕获到上升沿后,将这10个数据通过DMA传输至TIM1 CCR1,并且这个操作要在下一次TIM1 CH2的上升沿到来之前完成,也即在32us内完成这个操作;

而实际现象是,在TIM1 CH2的上升沿到来之际,DMA只传输了一个数据,经过10个上升沿之后,DMA才将数据传输完成,触发TC中断,此过程耗时为32*10us;

所以我的意思是,执行触发一次对CCR1连续写10次的操作;

xmshao 回答时间:2025-2-24 16:59:22

托卡马克 发表于 2025-2-24 15:58
是这样的,我的期望是假设缓冲区内有10个数据,我需要在TIM1 CH2捕获到上升沿后,将这10个数据通过DM ...

[md]呵呵~

假设触发一次,DMA连续帮你搬10次,请问你连续对ccr1寄存器写10次的目的何在呢?

当然,你现在的设计也的确 不能一次触发请求,实现多次传输。

托卡马克 回答时间:2025-2-24 17:29:31

xmshao 发表于 2025-2-24 16:59
呵呵~</p>
<p>假设触发一次,DMA连续帮你搬10次,请问你连续对ccr1寄存器写10次的目的何在呢?

[md]我这个问题的背景是通过VGA信号的行频来同步RGB信号的传输,因为不需要考虑亮度,就只需要在对应行的像素点处置高低电平即可,然后我希望能通过捕获HSYNC信号上升沿来当作同步信号的开始,之后将一整行的像素点通过DMA同步传输至CCR1寄存器,但目前看这样配置不合适,

然后我想通过TIM1捕获后,触发TIM2从模式,通过TIM2的更新事件作为像素时钟用于DMA传输的同步,但目前按照想法配置之后,又出现了TIM2没有启动,CNT未增加的问题😢,不知大佬可否过目?

/ TIM1 init function / void MX_TIM1_Init(void) {

/ USER CODE BEGIN TIM1_Init 0 / LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1); / USER CODE END TIM1_Init 0 /

LL_TIM_InitTypeDef TIM_InitStruct = {0}; LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; LL_TIM_BDTR_InitTypeDef TIM_BDTRInitStruct = {0};

LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

/ Peripheral clock enable / LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);

LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA); /*TIM1 GPIO Configuration PA9 ------> TIM1_CH2 / GPIO_InitStruct.Pin = LL_GPIO_PIN_9; GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/ USER CODE BEGIN TIM1_Init 1 /

/ USER CODE END TIM1_Init 1 / TIM_InitStruct.Prescaler = 0; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 2; TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; TIM_InitStruct.RepetitionCounter = 0; LL_TIM_Init(TIM1, &TIM_InitStruct); LL_TIM_EnableARRPreload(TIM1); LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1); TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE; TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE; TIM_OC_InitStruct.CompareValue = 0; TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH; TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH; TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW; TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW; LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct); LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH1); LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_OC2REF); LL_TIM_EnableMasterSlaveMode(TIM1); LL_TIM_IC_SetActiveInput(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); LL_TIM_IC_SetPrescaler(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); LL_TIM_IC_SetFilter(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); LL_TIM_IC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); TIM_BDTRInitStruct.OSSRState = LL_TIM_OSSR_DISABLE; TIM_BDTRInitStruct.OSSIState = LL_TIM_OSSI_DISABLE; TIM_BDTRInitStruct.LockLevel = LL_TIM_LOCKLEVEL_OFF; TIM_BDTRInitStruct.DeadTime = 0; TIM_BDTRInitStruct.BreakState = LL_TIM_BREAK_DISABLE; TIM_BDTRInitStruct.BreakPolarity = LL_TIM_BREAK_POLARITY_LOW; TIM_BDTRInitStruct.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE; LL_TIM_BDTR_Init(TIM1, &TIM_BDTRInitStruct); / USER CODE BEGIN TIM1_Init 2 / TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct); LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH2);

LL_TIM_EnableAllOutputs(TIM1); LL_TIM_EnableCounter(TIM1);

/ USER CODE END TIM1_Init 2 / LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB); LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA); /*TIM1 GPIO Configuration PB13 ------> TIM1_CH1N PA8 ------> TIM1_CH1 / GPIO_InitStruct.Pin = CURSOR_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; LL_GPIO_Init(CURSOR_GPIO_Port, &GPIO_InitStruct);

GPIO_InitStruct.Pin = CROSSHAIR_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; LL_GPIO_Init(CROSSHAIR_GPIO_Port, &GPIO_InitStruct); } / TIM2 init function / void MX_TIM2_Init(void) {

/ USER CODE BEGIN TIM2_Init 0 /

/ USER CODE END TIM2_Init 0 /

LL_TIM_InitTypeDef TIM_InitStruct = {0};

/ Peripheral clock enable / LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);

/ TIM2 DMA Init /

/ TIM2_UP Init / LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PRIORITY_VERYHIGH);

LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MODE_CIRCULAR);

LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PERIPH_NOINCREMENT);

LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MEMORY_INCREMENT);

LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_HALFWORD);

LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MDATAALIGN_HALFWORD);

/ USER CODE BEGIN TIM2_Init 1 /

/ USER CODE END TIM2_Init 1 / TIM_InitStruct.Prescaler = 0; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 2; TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; LL_TIM_Init(TIM2, &TIM_InitStruct); LL_TIM_EnableARRPreload(TIM2); LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_ITR0); LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_TRIGGER); LL_TIM_DisableIT_TRIG(TIM2); LL_TIM_DisableDMAReq_TRIG(TIM2); LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_UPDATE); LL_TIM_EnableMasterSlaveMode(TIM2); / USER CODE BEGIN TIM2_Init 2 /

LL_TIM_SetUpdateSource(TIM2, LL_TIM_UPDATESOURCE_REGULAR); LL_TIM_EnableDMAReq_UPDATE(TIM2); LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)&TIM1->CCR1); LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)line_buffer0); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 10); // LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, (sizeof(line_buffer0) / sizeof(line_buffer0[0]))); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);

LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2);

NVIC_SetPriority(DMA1_Channel2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0)); NVIC_EnableIRQ(DMA1_Channel2_IRQn); / USER CODE END TIM2_Init 2 / }

xmshao 回答时间:2025-2-25 11:38:31

托卡马克 发表于 2025-2-24 15:58
是这样的,我的期望是假设缓冲区内有10个数据,我需要在TIM1 CH2捕获到上升沿后,将这10个数据通过DM ...

[md]对你应用需求不太清晰。

如果你确认是要对CCR1做写操作,最终目的应该是调整PWM的脉宽。检测到CH2上沿触发事件后,要按什么样的节奏去改写CCR1呢,随便多快多慢 只要32us内 改写10次就行?我想这里应该有个用来控制修改CCR1快慢的机制存在。

先把原理弄清楚,再来组织、整理代码。

托卡马克 回答时间:2025-2-25 14:13:33

xmshao 发表于 2025-2-25 11:38
对你应用需求不太清晰。</p>
<p>如果你确认是要对CCR1做写操作,最终目的应该是调整PWM的脉宽。检测到CH2上 ...

[md]是的,我也这么想,目前的问题点就在如何让DMA的传输与CH2的捕获同步,以及如何让CCR1更新的周期满足我的期望

所属标签

相似问题

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版