STM32学习之普通定时器是否能定时1us?

对于STM32F10x系列使用普通定时器能否能定时1us,笔者一直抱有怀疑态度。

于是笔者做了下述实验:

代码:

主函数:

int main(void)
{
SystemClock_Config();
LED_Init();//LED初始化
NVIC_Config();
Timer2_Config();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
while(1)
{
}

}

GPIO设置

void LED_Init(void)
{
GPIO_InitTypeDef  GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE); //使能PB,PE时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;            //端口PC0配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //配置推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO速度50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure);     //初始化GPIOC.0
GPIO_SetBits(GPIOC,GPIO_Pin_0);                      //PC.0输出高


GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;              //端口PD3配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //配置推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO速度50MHz
GPIO_Init(GPIOD, &GPIO_InitStructure);     //初始化GPIOD.3
GPIO_SetBits(GPIOD,GPIO_Pin_3);                      //PD.3输出高

}

NVIC设置:

void NVIC_Config(void)  
{  
NVIC_InitTypeDef NVIC_InitStructure;  

//选择中断分组0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);  


//选择TIM2的中断通道  
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;       

//抢占式中断优先级设置为0  
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  

//响应式中断优先级设置为0  
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  

//使能中断  
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  

NVIC_Init(&NVIC_InitStructure);  

TIM2设置:

void Timer2_Config(void)  
{   
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);   

TIM_DeInit(TIM2);  

//定时公式:(TIM_Period + 1) * (TIM_Prescaler + 1) / TIM2 Clock,向上
//TIM2时钟设置72MHz  
TIM_TimeBaseStructure.TIM_Period=10-1;  //自动重装载寄存器的值  

TIM_TimeBaseStructure.TIM_Prescaler=(36-1);         //时钟预分频数  

TIM_TimeBaseStructure.TIM_ClockDivision=0;  //采样分频(捕获)普通定时不需要这个设置 ,无论怎么改变不影响输出频率

TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式  

TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);  

TIM_ClearFlag(TIM2,TIM_FLAG_Update);               //清除溢出中断标志  

TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);  //使能更新中断

TIM_Cmd(TIM2,ENABLE);                              //开启时钟  

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , DISABLE); /*先关闭等待使用*/    
}   


中断函数:

void TIM2_IRQHandler(void)   
{  
u8 i;
u8 ReadValue;  
if(TIM_GetITStatus(TIM2,TIM_IT_Update) != RESET)    //检测是否发生溢出更新事件  
{  
TIM_ClearITPendingBit(TIM2,TIM_FLAG_Update);//清除TIM2的中断待处理位  

                /************************************************/

                /***********************重点********************/
/*
for(i=0;i<10;i++)
{
GPIO_ResetBits(GPIOD,GPIO_Pin_3);
}
 */

                /***********************重点********************/

                /*************************************************/
ReadValue = GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_0);  

if(ReadValue == 0)                   //若该端口当前为低电平,  
{  
GPIO_SetBits(GPIOC,GPIO_Pin_0);//将其改为高电平输出 ;  
}      
else                            //若该端口当前为高电平,  
{  
GPIO_ResetBits(GPIOC,GPIO_Pin_0);   //将其改为低电平输出;  
}  
}          
}  

上述代码下载到单片机里面,输出100KHz的方波(5us定时),但是,笔者欲1us定时,简单认为:

TIM_TimeBaseStructure.TIM_Period=2-1;  //自动重装载寄存器的值  

TIM_TimeBaseStructure.TIM_Prescaler=(36-1);         //时钟预分频数  

后来发现,输出方波最高频率为250KHz,在网上查阅各类博客,发现并不能解决问题。

当随手在中断函数中加了几句话(Period,Prescaler值不变),发现波形改变,于是就开始怀疑1us定时寄存器那样设置,是不是中断函数还未执行完毕,又发生了中断,导致一直在中断函数中。

为验证猜想,笔者做了一个实验,在中断函数中执行了10个:

for(i=0;i<10;i++)
{
GPIO_ResetBits(GPIOD,GPIO_Pin_3);
}

输出波形是这样的:


正常100KHz波形是这样的(上面那个函数屏蔽):


10个多余的周期会导致波形频率差这么多,欲定时1us(前提中断函数比较复杂),采用普通定时器这样的办法是不可以的。

如果中断函数代码时一句或者两句,定时1us还有可能,普通定时器否则定时1us不可能实现。

STM32微控制器系列中,想要定时1us(微秒),通常会利用其丰富的定时器资源,比如SysTick定时器或者通用定时器如TIM1、TIM8等。这里以TIM1为例说明如何设置基本的1us定时: 1. **启用SysTick定时器**(如果你需要精确到1us,推荐使用SysTick,因为它有更高的精度和较低的中断开销): - 首先,需要配置SysTick的工作模式,例如每1000个系统周期更新计数器,这样大约对应1us时间间隔。 ```c // STM32F10x中,启用SysTick定时器 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SYSTICK, ENABLE); SysTick_Config(SystemCoreClock / 1000); // 设置计数值,SystemCoreClock是CPU频率 ``` 2. **配置定时器中断**: - 为了让定时器触发中断,在必要的中断管理函数里开启SysTick中断,并连接适当的回调函数处理1us事件。 ```c NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = SYSTICK_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); ``` 3. **编写回调函数**: - 创建一个空的回调函数,用于处理1us定时器到期的事件。 ```c void SysTick_Handler(void) { /* 这里可以添加你的1us时间相关操作 */ // 如果你需要准确到1us,那么这里的代码应该尽可能简单快速 // 例如计数器加1或者其他简单的任务 } ``` 4. **定期检查和调整时间间隔**: - 如果系统性能稳定,通常不需要调整。如果系统负载变化,可能需要动态调整SysTick的分频系数来保持1us的准确性。 注意:实际使用时,由于硬件寄存器和时钟频率的不同,可能会有一些误差,所以测试是非常重要的。另外,对于其他型号的STM32定时器,配置过程可能会有所差异。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值