思路:以STM32F103C8T6为例,定时器外设有两个重要的东西:自动重装寄存器(ARR)和32位计数器(CNT),规定计数器跳动一次需要1us,将要延时的时间直接给ARR,每次让CNT的值变0,向上计数。当CNT=ARR时,会触发更新事件标志位,通过该标志位可以知道延时是否结束。注意最后要清除该标志位!
TIM_HandleTypeDef htim2;
/*延时选择TIM2*/
void delay_init(void)
{
//开启TIM2外设的时钟
__HAL_RCC_TIM2_CLK_ENABLE();
// TIM_ClockConfigTypeDef sClockSourceConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72-1; //若系统主频是72MHZ,预分频71表明计数器跳动一次需要1us
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period =1; //重装载值随便,后面进行动态调整
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
//定时器配置
HAL_TIM_Base_Init(&htim2);
//定时器配置时钟源:内部时钟,上电后会默认选择内部时钟
//sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
//HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
}
//根据所传参数,动态调整ARR寄存器的值
void delay_us(uint32_t us)
{
__HAL_TIM_SET_AUTORELOAD(&htim2, us); // 设置自动重载值(ARR = us)
__HAL_TIM_SET_COUNTER(&htim2, 0); // 清零计数器
HAL_TIM_Base_Start(&htim2); // 启动定时器
while (!__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)); // 等待计数完成
HAL_TIM_Base_Stop(&htim2); // 停止定时器
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); // 清除标志
}
为了能更精确的延时,最好直接操作TIM寄存器
//根据所传参数,动态调整ARR寄存器的值
void delay_us(uint32_t us)
{
TIM2->ARR = us; // 直接设置ARR
TIM2->CNT = 0; // 清零计数器
TIM2->CR1 |= TIM_CR1_CEN; // 启动定时器
while (!(TIM2->SR & TIM_FLAG_UPDATE)); // 轮询等待
TIM2->CR1 &= ~TIM_CR1_CEN; // 停止定时器
TIM2->SR &= ~TIM_FLAG_UPDATE; // 清除标志位
}