本节我们来学习STM32最基础的定时器应用,并利用hal库生成的代码,添加用户自己的功能到回调函数中。
程序实现的功能是:以10ms的定时器中断为周期,循环检测一个按键的状态,如果按下,则点亮LED灯;没有按下则熄灭LED灯。
1)cubemx生成代码
首先还是在cubemx中生成代码,选择器件、设置SYS(调试接口)、设置RCC(外部晶振时钟源)这几个步骤和前一节一样。
然后设置用户IO口,我们需要一个引脚输出驱动LED灯,一个引脚输入作为按键。这里我们的硬件上LED仍然使用开发板上的PC13;按键的一端连接到PA0,另一端接地。
所以GPIO如下设置,把PC13设置为输出,PA0设置为输入:
设置完成后引脚会变成绿色。
然后,将PA0设置为上拉,因为我们的按键另一端连到了地,那么不按按键的时候,硬件上需要给PA0引脚一个高电平才能被识别,正好STM32的引脚可以设置内部上拉电阻:
然后设置定时器:
这里我们选择定时器3,使用内部时钟(72M主频),由于我们希望每10ms产生一个中断,所以分频系数和计数周期如下图设置,最终的中断周期为:
(分频数 1)*(计数周期 1)/72M = 7200*100/72M = 0.01s
打开定时器中断的使能:
之后,和上一节后续的一样,在时钟选项卡设置主时钟为72M;在project Manager选项卡设置工程名和路径,生成工程代码。
2)keil中的编程和下载
在keil中打开生成的工程,可以看到软件生成的代码中,main函数中已经有硬件初始化的代码,比上一节基本的GPIO多了定时器的初始化。
我们先在main函数中添加如下语句,功能是打开定时器3中断。
然后,我们打开stm32f1xx_it.c这个文件,这个文件里的函数是各中断的服务程序,我们找到定时器3的中断服务程序,通过前面的设置,这个函数会在每10ms被调用一次:
再通过其中调用的HAL_TIM_IRQHandler函数,发现它内部是这么实现中断处理的:
由于我们只使用了最基本的定时器更新中断,所以在这个函数里,只会进入TIM Update event这个处理内部,清除中断标志后,还会调用HAL_TIM_PeriodElapsedCallback这个函数。
而HAL_TIM_PeriodElapsedCallback这个函数是这么实现的:
它有一个__weak关键词修饰,函数内部是空的,没做任何处理。
这种有__weak修饰的函数,是可以被用户重写的。HAL_TIM_PeriodElapsedCallback这类函数一般称为回调函数,是留给用户的接口函数,用于给用户重写实现,以加入自己的功能。
我们在tim.c文件里重写这个回调函数,在其中加入读取PA0引脚(按键)状态的语句,这样就实现了每10ms读取一次按键的状态,存在key_state这个变量中:
然后,在main.c文件中加入如下代码:
这段代码在主循环中检测按键key_state的状态,如果按下(key_state为0),则点亮LED,反之熄灭LED。
要注意定义key_state变量时,一定要加上volatile修饰,否则优化等级开得比较高的话,主循环中可能检测不到按键的变化。
然后就可以编译、下载到开发板运行了。下载成功后,按下按键,可以看到LED灯点亮,松开后LED熄灭。
好了本节就到这里了。这一节主要的知识点有:定时器的配置、回调函数的使用、volatile关键词的使用。