今天在调试STM32F103c8t6单片机定时器功能时,突然想看看定时器输出最大的频率是多少?为了方便验证在定时器中断中用LED灯翻转来判断定时器的频率。为了保证测试准确性,先测试测试LED直接用IO口驱动时的最大翻转速度。
测试代码如下:
#define LED PCout(13) // PC13
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStucture;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStucture.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStucture.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStucture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStucture);
GPIO_SetBits(GPIOC,GPIO_Pin_13);
}
int main(void)
{
LED_Init(); //初始化与LED连接的硬件接口
while(1)
{
LED = !LED;
}
}
PC13口为LED控制口,低电平LED亮,高电平LED灭。在循环中翻转LED电平。用示波器测试LED波形如下:
LED的翻转频率为 1.38MHz,初始化的时候IO口频率设置的是50MHz,主程序中就一行代码,为什么LED翻转速度最快才1M多,什么原因造成的?会不会和代码有关系。将LED翻转改为直接给LED赋值试试。
while(1)
{
LED = 0;
LED = 1;
}
直接给IO口赋值,这时在看看LED的波形。
从波形可以看到LED的频率变成了3MHz多,看来指令不同,执行速度差别很大。
那还没不能再改快点,LED用的是位操作,要更快就直接操作寄存器。直接给输出寄存器赋值来控制PC13口的电平。
重新修改代码如下:
while(1)
{
GPIOC->ODR=0x0000;
GPIOC->ODR= 0x2000;
}
测试波形如下:
LED翻转频率变成了4.24MHz,但是波形发生了变化,可以看到低电平的时间28ns,高电平的时间208ns。说明将PC13口置为高电平后,程序执行其他代码花费了180ns的时间。看来要提高LED的翻转速度,代码还有很大的优化空间。但是今天主要是测试定时器,所以在这块就不深究了。下来测试定时器代码。
这块用的是定时器3,在定时器3中断中翻转LED,然后测试波形。看看定时器的速度有多快。
定时器代码如下:
//APB1时钟分频为2 TIM2-7 时钟数为APB1 2倍
// Tout= (arr+1)*(psc+1) / Tclk
void TIM3_Init(u16 arr, u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3, ENABLE);
}
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
LED = !LED;
}
}
int main(void)
{
LED_Init(); //初始化与LED连接的硬件接口
TIM3_Init(4999,7199); // 5000 * 7200 /72M =500ms
while(1)
{
}
}
定时器3定时500ms,在定时中断中翻转LED电平。测试波形如下:
由波形可以看出LED的翻转频率为1K,周期500ms。说明定时器定时是比较准确的。
下来缩短定时器时间,定时器初始化代码改为:
TIM3_Init(1,7199); // 2 * 72 /72M = 0.2ms
继续测试,波形如下:
频率为2.5K,周期为200us。下来将预分频值缩小百分之一。初始化改为:
TIM3_Init(1,71); // 2* 72 / 72M =2us
测试波形如下:
频率为250K,周期为2us。看来设置理论计算的实际输出一样。
下来再将预分频值缩小十分之一:
TIM3_Init(1,7); // 2* 9 / 72M = 200ns
测试波形如下:
频率为313K,周期为1.6us,频率并不是刚才的10倍,什么原因引起的?根据刚才测试LED经验来看,应该是代码的问题引起的。那么在中断中将LED翻转改为寄存器操作看看。
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
GPIOC->ODR=0x0000;
GPIOC->ODR= 0x2000;
}
}
可以看出优化代码后,频率有提高,但是变化还是不大。继续优化代码,由于程序中只用了定时器3一个中断,所以在中断函数中就不判断标志位了,直接将标志位清0。清标志位代码也改为直接操作寄存器。
void TIM3_IRQHandler(void)
{
TIM3->SR=0x0000;
GPIOC->ODR=0x0000;
GPIOC->ODR= 0x2000;
}
测试波形如下:
可以看出,这次频率有了明显得提高,频率2.4MHz,高电平时间388ns,低电平时间28ns。低电平时间和刚测IO直接翻转测试的时间一样,说明将PC13口置高后程序执行其他代码花费了时间。
TIM3_Init(1,71); // 2* 72 / 72M =2us
TIM3_Init(1,7); // 2* 9 / 72M = 200ns
刚才预分频值为71的时候时候,频率为250K,我们将预分频值缩小为1/10,那么频率应该增大10倍。所以预分频值设置为7的时候,频率理论值应该为2.5MHz,实际测试值为2.4MHz,说明理论计算的值是正确的,定时器应该实际上也能输出这么高的频率,但是由于代码的原因导致不能正真观察到输出的频率。所以想要真正观察到实际输出频率,代码就得直接用汇编来实现了。
这块可以理论计算下最大输出频率,假如高电平的时间和低电平的时间一样是28ns,那么周期就是56ns,频率就为17.86MHz。说明理论上定时器输出频率可以达到十几MHz。但是由于代码原因我们不能验证这个,以后有机会了可以在验证下。
看来在实际应用中想要定时器输出更高频率,在写代码的时候就得好好优化.通过上面测试也发现,同样的功能,不同的操作方法对代码的执行效率影响很大。在以后写程序的时候不能仅仅实现功能就行,还要注意代码的执行效率。