一、定时器实现阻塞us级延时
虽然说非阻塞是一个项目的基础,但是在实际应用中,还是少不了用到阻塞延时,比如I2C、或者开机自检、再或者调试。下面就使用定时器来做一个阻塞的us级延时。
我使用到的是TIM2通用定时器,也可根据实际使用情况自己更换。
流程:
1、使能定时器的时钟
2、初始化
3、构造us函数
TIM_TimeBaseInitTypeDef TimerInitStructer;
RCC_ClocksTypeDef RCC_ClocksStatus;
//获取系统时钟
RCC_GetClocksFreq(&RCC_ClocksStatus);
//使能TIM2时钟
RCC_APB1PeriphClockCmd( DELAY_TIME_BASE_CLK, ENABLE );
TimerInitStructer.TIM_ClockDivision = TIM_CKD_DIV1;//设置时钟分割:TDTS = Tck_tim
TimerInitStructer.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数模式
TimerInitStructer.TIM_Period = 0x00FF;//指定要加载到活动中的周期值,在下一次更新事件时自动重新加载寄存器。
TimerInitStructer.TIM_RepetitionCounter = DISABLE;//指定重复计数器值。每次RCR下计数器达到0时,生成更新事件并计算重新启动从RCR值(N)。
TimerInitStructer.TIM_Prescaler = ( RCC_ClocksStatus.PCLK1_Frequency / 1000000 ) - 1;//指定用于划分TIM时钟的预计算程序值
TIM_TimeBaseInit( DELAY_TIME_BASE, &TimerInitStructer );
TIM_ClearFlag( DELAY_TIME_BASE, TIM_FLAG_Update ); //清除挂起
TIM_SetCounter( DELAY_TIME_BASE, 0 );//设置计数寄存器的值
TIM_Cmd( DELAY_TIME_BASE, ENABLE ); // 开始计数
while( RESET == TIM_GetFlagStatus( DELAY_TIME_BASE, TIM_FLAG_Update ));
TIM_Cmd( DELAY_TIME_BASE, DISABLE ); // 停止计数
TIM_SetCounter( DELAY_TIME_BASE, 0 );
TIM_ClearFlag( DELAY_TIME_BASE, TIM_FLAG_Update );
其中:time = (arr+1)*(psc+1)/Tck 如果需要用定时器的话,可以根据等式进行换算,然后将
arr 填充到 TimerInitStructer.TIM_Period 中,psc 填充到:TimerInitStructer.TIM_Prescaler
这个可参考下面的定时器中断使用。
定时器设置完成后,需要构造us函数
void Delay_us (uint32_t us)
{
DELAY_TIME_BASE->ARR = us;
DELAY_TIME_BASE->CNT = 0;
DELAY_TIME_BASE->CR1 |= (uint32_t)0x01; // 开始计数
while( RESET == ( DELAY_TIME_BASE->SR & TIM_FLAG_Update ));
DELAY_TIME_BASE->SR &= (uint32_t)( ~(uint32_t)TIM_FLAG_Update ); //清溢出标志
DELAY_TIME_BASE->CR1 &= (uint32_t)( ~(uint32_t)0x01 ); //停止计数
}
此时,Delay_us就可以正常使用了,例如I2C的时序中。
二、定时器中断
定时器中断与上面的实现基本类似,只是打开了中断使能开关,填充中断函数。
用到了TIM4来做中断,如下:
void My_TIM4_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
//定时器TIM4初始化 实现的是1ms中断一次
//T = (arr+1)*(psc+1)/Tck
TIM_DeInit(TIM4);//使用缺省值初始化TIM外设寄存器
TIM_TimeBaseStructure.TIM_Period = (1000-1); //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = (72-1); //1ms中断一次
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ClearFlag(TIM4,TIM_FLAG_Update);//清除更新标志位
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE ); //使能指定的TIM4中断,允许更新中断
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
TIM_Cmd(TIM4, ENABLE); //使能定时器4
}
接下来是中断函数:
void TIM4_IRQHandler (void)
{
TIME_DELAY->SR &=~ (1<<0);//清标志
//下面做你想定时做的任务,一般这里不放延时处理
}