你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【初学】中断控制STM32硬件timer

[复制链接]
swang93 发布时间:2015-3-21 21:37
本帖最后由 swang93 于 2015-3-21 21:37 编辑

虽然作为嵌入式方向的大学生,但是并没有接触多少硬件。平时只学习了各种语言,忽略了硬件的学习。之前也只是在Arduino上做了一些开发,自己做了个小东西。前不久决定学习stm32,看了一些资料,算是刚入门,有些地方说的不严谨,大家尽管提出来,一起学习。想和大家分享的就是如何控制stm32的硬件Timer,大神轻喷。

1、  硬件timer是一个独立的计时器,在计时时以一个给定的速度在0和最大值之间计时,同时产生各种事件。它独立于你的C/C++程序,而且它的值遵循下面的规律:
01-timer.png

2、我通过一个简单的程序高明白了这个timer。我设置timer的周期为500,当timer的值达到400时让LED灯亮,当值达到500时再关闭LED。Timer每次达到500时就会自动重置为0。
02-timer.png

3、注意,在我们对timer进行任何动作的之前,需通过调用RCC_APB1PeriphClockCmd()来启动它:
  1. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  2. TIM_TimeBaseInitTypeDef timerInitStructure;
  3. timerInitStructure.TIM_Prescaler = 40000;
  4. timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  5. timerInitStructure.TIM_Period = 500;
  6. timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  7. timerInitStructure.TIM_RepetitionCounter = 0;
  8. TIM_TimeBaseInit(TIM2, &timerInitStructure);
  9. TIM_Cmd(TIM2, ENABLE);
复制代码

4、在main函数里,我们只简单地检查timer的值,并通过这个值来控制LED。第一版的完整程序如下,下面还会修改代码。

  1. #include <stm32f4xx_gpio.h>
  2. #include <stm32f4xx_tim.h>
  3. #include <stm32f4xx_rcc.h>
  4. #include <misc.h>

  5. void InitializeLEDs()
  6. {
  7.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

  8.     GPIO_InitTypeDef gpioStructure;
  9.     gpioStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
  10.     gpioStructure.GPIO_Mode = GPIO_Mode_OUT;
  11.     gpioStructure.GPIO_Speed = GPIO_Speed_50MHz;
  12.     GPIO_Init(GPIOD, &gpioStructure);

  13.     GPIO_WriteBit(GPIOD, GPIO_Pin_12 | GPIO_Pin_13, Bit_RESET);
  14. }

  15. void InitializeTimer()
  16. {
  17.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  18.     TIM_TimeBaseInitTypeDef timerInitStructure;
  19.     timerInitStructure.TIM_Prescaler = 40000;
  20.     timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  21.     timerInitStructure.TIM_Period = 500;
  22.     timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  23.     timerInitStructure.TIM_RepetitionCounter = 0;
  24.     TIM_TimeBaseInit(TIM2, &timerInitStructure);
  25.     TIM_Cmd(TIM2, ENABLE);
  26. }

  27. int main()
  28. {
  29.     InitializeLEDs();
  30.     InitializeTimer();

  31.     for (;;)
  32.     {
  33.         int timerValue = TIM_GetCounter(TIM2);
  34.         if (timerValue == 400)
  35.             GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_SET);
  36.         else if (timerValue == 500)
  37.             GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_RESET);
  38.     }
  39. }
复制代码

5、如果像下面那样设置一个断点,我们就可以观察到,每次执行到这里,timerValue的值都会在0-500之间。这就和我们设置的TIM_Period是符合的。
03-timer.png

6、  每次STM32timers达到周期值是,就可以自动的产生”update”事件。在TIM_Cmd(TIM2,ENABLE)下添加如下代码:
  1. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
复制代码
就可以产生update事件。这里需要注意一下,由于我们没有设置中断控制器,所以这个中断不会影响代码的执行,我们需要手动的检查。

7、 如果用如下代码替换main函数的代码:
  1. InitializeLEDs();
  2. InitializeTimer();
  3. for (;;)
  4. {
  5.     if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
  6.     {
  7.         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
  8.         GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
  9.     }
  10. }
复制代码
运行代码,我们就可以发现LED的闪烁模式已经改变了。

8、硬件timer真正强大的地方是,他们可以向CPU发送中断,这样就可以不用手动检查这些状态。在代码里添加下面这个函数,然后在main函数里在调用IntializeTimer()之后再调用它。
  1. void EnableTimerInterrupt()
  2. {
  3.     NVIC_InitTypeDef nvicStructure;
  4.     nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
  5.     nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
  6.     nvicStructure.NVIC_IRQChannelSubPriority = 1;
  7.     nvicStructure.NVIC_IRQChannelCmd = ENABLE;
  8.     NVIC_Init(&nvicStructure);
  9. }
复制代码

9、 最后去掉main函数里for循环里的代码,然后把这些代码添加到TIM_IRQHandler()函数里:
  1. extern "C" void TIM2_IRQHandler()
  2. {
  3.     if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
  4.     {
  5.         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
  6.         GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
  7.     }
  8. }
复制代码
注意,如果你创建的是C++文件,就需要extern “C”


10、把代码跑起,观察LED闪烁的情况。设置断点,观察寄存器的值:
04-timer.png

11、注意,链接寄存器(lr)通常包含函数的返回地址0xfffffff9,这是个特别的值,和ARMCortex CPU文档里说的EXC_RETURN一样,表明当前函数是被CPU自动调用用于处理中断的。


12、 用下面的代码代替main函数里for循环的代码:
  1. for (;;)
  2.     {
  3.         for (int i = 0; i < 1000000; i++)
  4.             asm("nop");
  5.         GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
  6.     }
复制代码


13、跑起代码,我们就可以看到LED灯以不同的频率闪烁,互不影响。

最后把代码贴出来,一起学习! demo.zip (758 Bytes, 下载次数: 12)
收藏 1 评论38 发布时间:2015-3-21 21:37

举报

38个回答
swang93 回答时间:2015-3-24 00:09:23
有几个小伙伴对VS2013创建Embedded Project比较感兴趣,其实不复杂,只是大家平时都用Keil创建工程,用VS的人少而已。今天就趁晚上的时间大致介绍介绍,有说的不严谨的地方,大家指出来就行了,多多交流。

用VS创建Keil下的工程,VS本身是不支持创建的。这里要用到一款工具VisualGDB,安装VisualGDB也不复杂,为了方便有兴趣的小伙伴尝试一下,我上传了安装包。多说一句,压缩包里有个“Crack”文件夹,把里面的文件代替安装目录下的文件,就成功破解VisualGDB了。

安装的图就不贴了,贴几张图创建工程的图,供大家参考一下。创建过程不需要做什么修改,也就不一一解释了,大家动手做了印象会更深刻,下面贴图:


01-testGDB.png

02-testGDB.png

在这步的时候需要先选择一个toolchain,第一次需要下载安装。
03-testGDB.png

04-testGDB.png

05-testGDB.png

06-testGDB.png

工程创建好之后,在本地调试,就会生成elf文件,也就出现了最开始的画面。

这个是VisualGDB的安装包,手痒的小伙伴下载下来试试吧。 VisualGDB-4.2r4.rar (4.85 MB, 下载次数: 0)
党国特派员 回答时间:2015-3-24 10:43:13
swang93 发表于 2015-3-24 00:10
下面介绍了一下,感兴趣看一下吧,多多交流

受益非浅。 blank.png blank.png blank.png blank.png blank.png blank.png blank.png blank.png blank.png blank.png
swang93 回答时间:2015-3-22 12:03:44
_ilikerome_ 发表于 2015-3-22 10:01
感觉LZ写的挺好的,我这几天在搞高级定时器,驱动电机。

感觉你的这个教程写的不错

谢谢!细看一下跑的其实是elf,用keil当然是更方便了
kqh1120 回答时间:2015-3-22 00:38:03
学习了 3.gif
nocoyou 回答时间:2015-3-22 03:07:07
vs2015?
_ilikerome_ 回答时间:2015-3-22 10:01:28
感觉LZ写的挺好的,我这几天在搞高级定时器,驱动电机。

感觉你的这个教程写的不错

还有你用VS编辑??可以吗?
swang93 回答时间:2015-3-22 12:06:19
wamcncn 回答时间:2015-3-22 12:29:22

好像是mbed
coloree 回答时间:2015-3-22 15:05:47
支持一下
foxglove 回答时间:2015-3-22 16:32:47
教程写的不错
拼命三郎 回答时间:2015-3-22 19:19:50
xxxxxxxxxx.jpg
swang93 回答时间:2015-3-22 22:40:56
swang93 回答时间:2015-3-22 22:41:23

谢谢支持了
swang93 回答时间:2015-3-22 22:41:54

谢谢支持,共同学习
swang93 回答时间:2015-3-22 22:42:22
埃斯提爱慕 回答时间:2015-3-22 22:47:04
提示: 作者被禁止或删除 内容自动屏蔽
123下一页

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版