本帖最后由 swang93 于 2015-3-21 21:37 编辑
虽然作为嵌入式方向的大学生,但是并没有接触多少硬件。平时只学习了各种语言,忽略了硬件的学习。之前也只是在Arduino上做了一些开发,自己做了个小东西。前不久决定学习stm32,看了一些资料,算是刚入门,有些地方说的不严谨,大家尽管提出来,一起学习。想和大家分享的就是如何控制stm32的硬件Timer,大神轻喷。
1、 硬件timer是一个独立的计时器,在计时时以一个给定的速度在0和最大值之间计时,同时产生各种事件。它独立于你的C/C++程序,而且它的值遵循下面的规律:
2、我通过一个简单的程序高明白了这个timer。我设置timer的周期为500,当timer的值达到400时让LED灯亮,当值达到500时再关闭LED。Timer每次达到500时就会自动重置为0。
3、注意,在我们对timer进行任何动作的之前,需通过调用RCC_APB1PeriphClockCmd()来启动它: - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
- TIM_TimeBaseInitTypeDef timerInitStructure;
- timerInitStructure.TIM_Prescaler = 40000;
- timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
- timerInitStructure.TIM_Period = 500;
- timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
- timerInitStructure.TIM_RepetitionCounter = 0;
- TIM_TimeBaseInit(TIM2, &timerInitStructure);
- TIM_Cmd(TIM2, ENABLE);
复制代码
4、在main函数里,我们只简单地检查timer的值,并通过这个值来控制LED。第一版的完整程序如下,下面还会修改代码。
- #include <stm32f4xx_gpio.h>
- #include <stm32f4xx_tim.h>
- #include <stm32f4xx_rcc.h>
- #include <misc.h>
- void InitializeLEDs()
- {
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
- GPIO_InitTypeDef gpioStructure;
- gpioStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
- gpioStructure.GPIO_Mode = GPIO_Mode_OUT;
- gpioStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOD, &gpioStructure);
- GPIO_WriteBit(GPIOD, GPIO_Pin_12 | GPIO_Pin_13, Bit_RESET);
- }
- void InitializeTimer()
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
- TIM_TimeBaseInitTypeDef timerInitStructure;
- timerInitStructure.TIM_Prescaler = 40000;
- timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
- timerInitStructure.TIM_Period = 500;
- timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
- timerInitStructure.TIM_RepetitionCounter = 0;
- TIM_TimeBaseInit(TIM2, &timerInitStructure);
- TIM_Cmd(TIM2, ENABLE);
- }
- int main()
- {
- InitializeLEDs();
- InitializeTimer();
- for (;;)
- {
- int timerValue = TIM_GetCounter(TIM2);
- if (timerValue == 400)
- GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_SET);
- else if (timerValue == 500)
- GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_RESET);
- }
- }
复制代码
5、如果像下面那样设置一个断点,我们就可以观察到,每次执行到这里,timerValue的值都会在0-500之间。这就和我们设置的TIM_Period是符合的。
6、 每次STM32的timers达到周期值是,就可以自动的产生”update”事件。在TIM_Cmd(TIM2,ENABLE)下添加如下代码: - TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
复制代码 这就可以产生update事件。这里需要注意一下,由于我们没有设置中断控制器,所以这个中断不会影响代码的执行,我们需要手动的检查。
7、 如果用如下代码替换main函数的代码: - InitializeLEDs();
- InitializeTimer();
- for (;;)
- {
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
- {
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
- GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
- }
- }
复制代码 运行代码,我们就可以发现LED的闪烁模式已经改变了。
8、硬件timer真正强大的地方是,他们可以向CPU发送中断,这样就可以不用手动检查这些状态。在代码里添加下面这个函数,然后在main函数里在调用IntializeTimer()之后再调用它。
- void EnableTimerInterrupt()
- {
- NVIC_InitTypeDef nvicStructure;
- nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
- nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
- nvicStructure.NVIC_IRQChannelSubPriority = 1;
- nvicStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&nvicStructure);
- }
复制代码
9、 最后去掉main函数里for循环里的代码,然后把这些代码添加到TIM_IRQHandler()函数里:
- extern "C" void TIM2_IRQHandler()
- {
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
- {
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
- GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
- }
- }
复制代码 注意,如果你创建的是C++文件,就需要extern “C”。
10、把代码跑起,观察LED闪烁的情况。设置断点,观察寄存器的值:
11、注意,链接寄存器(lr)通常包含函数的返回地址0xfffffff9,这是个特别的值,和ARMCortex CPU文档里说的EXC_RETURN一样,表明当前函数是被CPU自动调用用于处理中断的。
12、 用下面的代码代替main函数里for循环的代码:- for (;;)
- {
- for (int i = 0; i < 1000000; i++)
- asm("nop");
- GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
- }
复制代码
13、跑起代码,我们就可以看到LED灯以不同的频率闪烁,互不影响。
最后把代码贴出来,一起学习!
demo.zip
(758 Bytes, 下载次数: 12)
|
用VS创建Keil下的工程,VS本身是不支持创建的。这里要用到一款工具VisualGDB,安装VisualGDB也不复杂,为了方便有兴趣的小伙伴尝试一下,我上传了安装包。多说一句,压缩包里有个“Crack”文件夹,把里面的文件代替安装目录下的文件,就成功破解VisualGDB了。
安装的图就不贴了,贴几张图创建工程的图,供大家参考一下。创建过程不需要做什么修改,也就不一一解释了,大家动手做了印象会更深刻,下面贴图:
在这步的时候需要先选择一个toolchain,第一次需要下载安装。
工程创建好之后,在本地调试,就会生成elf文件,也就出现了最开始的画面。
这个是VisualGDB的安装包,手痒的小伙伴下载下来试试吧。
受益非浅。
谢谢!细看一下跑的其实是elf,用keil当然是更方便了
感觉你的这个教程写的不错
还有你用VS编辑??可以吗?
用的13
好像是mbed
谢谢
谢谢支持了
谢谢支持,共同学习
谢谢谢谢