关于STM32F429中定时器的触发问题

帮老师做项目的时候遇到了一个奇怪的问题
因为需要几个定时器同步,因此将其中一个定时器配置成主模式,将使能信号作为触发源输出,其他定时器作为从模式触发
但是高级定时器总是莫名其妙被先行被触发,排查后发现,原因来自TimeBaseInit函数,函数原型如下

/**
  * @brief  Initializes the TIMx Time Base Unit peripheral according to 
  *         the specified parameters in the TIM_TimeBaseInitStruct.
  * @param  TIMx: where x can be  1 to 14 to select the TIM peripheral.
  * @param  TIM_TimeBaseInitStruct: pointer to a TIM_TimeBaseInitTypeDef structure
  *         that contains the configuration information for the specified TIM peripheral.
  * @retval None
  */
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
{
  uint16_t tmpcr1 = 0;

  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx)); 
  assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
  assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));

  tmpcr1 = TIMx->CR1;  

  if((TIMx == TIM1) || (TIMx == TIM8)||
     (TIMx == TIM2) || (TIMx == TIM3)||
     (TIMx == TIM4) || (TIMx == TIM5)) 
  {
    /* Select the Counter Mode */
    tmpcr1 &= (uint16_t)(~(TIM_CR1_DIR | TIM_CR1_CMS));
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
  }
 
  if((TIMx != TIM6) && (TIMx != TIM7))
  {
    /* Set the clock division */
    tmpcr1 &=  (uint16_t)(~TIM_CR1_CKD);
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
  }

  TIMx->CR1 = tmpcr1;

  /* Set the Autoreload value */
  TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
 
  /* Set the Prescaler value */
  TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
    
  if ((TIMx == TIM1) || (TIMx == TIM8))  
  {
    /* Set the Repetition Counter value */
    TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
  }

  /* Generate an update event to reload the Prescaler 
     and the repetition counter(only for TIM1 and TIM8) value immediately */
  TIMx->EGR = TIM_PSCReloadMode_Immediate;          
}

可以看到里面有这么一句

/* Generate an update event to reload the Prescaler 
     and the repetition counter(only for TIM1 and TIM8) value immediately */
TIMx->EGR = TIM_PSCReloadMode_Immediate;

注释上写的大概意思是通过产生一个更新事件来立即重载预分频寄存器和重复计数寄存器

查参考手册找TIM8的EGR寄存器
来自RM0090 Rev 18
继续查右值,发现是个宏

#define TIM_PSCReloadMode_Immediate        ((uint16_t)0x0001)

UG位的作用
UG位
作用注释已经解释了,官方的参考手册里面也有讲,是使用更新事件来确保各个装在入影子寄存器的值立即更新,这是一种官方推荐的初始化定时器的方式

我的代码里,是先初始化高级定时器,然后初始化通用定时器,高级定时器使用通用定时器作为触发源

//设置为从模式,用TIM4触发
TIM_SelectSlaveMode(TIM8, TIM_SlaveMode_Trigger);
TIM_SelectInputTrigger(TIM8, TIM_TS_ITR2);

然而,当代码运行到通用定时器初始化时,发现TIM8神奇的被触发了,通过之前的分析感觉是UG的问题
可是,我TIM4输出的触发信号明明是使能信号

//主模式
//该函数是用作主定时器与从定时器触发的同步,并不是设置主定时器
TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);
TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Enable);

后来一想,运行到TimeBaseInit时,我还没设置输出的触发源,会不会是这个问题

再查数据手册,管理触发源的是CR2寄存器的MMS
在这里插入图片描述
可以看到,复位值是000,那么MMS是000的时候会怎么样呢?
在这里插入图片描述

the UG bit from the TIMx_EGR register is used as trigger output (TRGO). If the reset is generated by the trigger input (slave mode controller configured in reset mode) then the signal on TRGO is delayed compared to the actual reset.

TIMx_EGR寄存器中的UG位用作触发输出(TRGO)。 如果复位是由触发输入(从机模式控制器配置为复位模式)产生的,则TRGO上的信号与实际复位相比会延迟。

——来自谷歌翻译

至此,终于破案了

截图均来自STM32F429参考手册 RM0090 Rev 18

第一次写博客,就瞎搞一下
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于STM32F407的ADC定时器触发采样的库函数代码示例: ``` #include "stm32f4xx.h" // 定义ADC采样变量以及DMA缓存区 volatile uint16_t ADC_Value[16]; volatile uint16_t DMA_Buffer[16]; // 初始化ADC的函数 void ADC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 使能ADC和DMA时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 初始化DMA DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DMA_Buffer; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = 16; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &DMA_InitStructure); DMA_Cmd(DMA2_Stream0, ENABLE); // 初始化GPIO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化ADC ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStructure); ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 8; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_15Cycles); ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); // 初始化定时器,设为1kHz的频率 TIM_TimeBaseStructure.TIM_Period = 840 - 1; TIM_TimeBaseStructure.TIM_Prescaler = 100 - 1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 初始化定时器OC TIM_OCStructInit(&TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 420 - 1; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); TIM_Cmd(TIM2, ENABLE); } // 获取ADC采样值 uint16_t ADC_GetValue(uint8_t ch) { uint16_t value = 0; if (ch < 8) { value = ADC_Value[ch]; } return value; } // DMA传输完成断处理函数 void DMA2_Stream0_IRQHandler(void) { if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0)) { DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); } } // 主函数 int main(void) { ADC_Init(); while (1) { // 定时器触发采样 ADC_SoftwareStartConv(ADC1); // 等待DMA传输完成 while(!DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF0)); // 复制DMA缓存区数据到ADC采样变量 for (int i = 0; i < 8; i++) { ADC_Value[i] = DMA_Buffer[i]; } // 清除DMA传输完成标志位 DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_TCIF0); } } ``` 在上述示例代码,我们使用了定时器2作为ADC的外部触发源,并且将其配为1kHz的频率。在每次定时器溢出时,定时器触发一次ADC采样。我们使用DMA来传输ADC采样值,这样可以减少CPU的负担,并且提高采样速度。在DMA传输完成后,我们将DMA缓存区的数据复制到ADC采样变量,然后等待下一次定时器触发采样。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值