专注电子技术学习与研究
当前位置:单片机教程网 >> STM32 >> 浏览文章

STM32学习之通用定时器的使用

作者:竹叶听筝   来源:竹叶听筝   点击数:  更新时间:2014年06月19日   【字体:

1STM32F103内部定时器有哪些?

    STM32一共有8个通用16Timer,其中TIMER1TIMER8是高级定时器,其它的TIMER2~TIMER7是普通定时器。
   
此外还有一个Systick(系统滴答定时器),这个定时器通常在操作系统中作为系统的任务切换周期。
   
还有一个RTC,是一个毫秒定时器,支持秒级中断,用来做实时时钟计数器。

    看门狗定时器 也可以算一个。

    8个定时器中,Timer1 Timer8是由APB2(输出最高频率为72MHZ)预分频后,再通过一个倍频器得到时钟频率,最高为72MHzTimer2~Timer7则是由APB1(输出最高频率为36MHZ)预分频后,再通过一个倍频器得到时钟频率,最高为36MHz

 

2、如何进行程序编写     

  这里我通过定时器来控制一个LED0.5s 0.5s ,交替闪烁。当然要让定时器正常工作起来,还要配置中断NVIC。定时器计数到某个数,产生中断,从而进入中断服务程序,点亮LED灯。

main函数分析:

#include "stm32f10x.h"

 

void GPIO_Config(void)//GPIO配置

{

   GPIO_InitTypeDef GPIO_InitStructure;

   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能gpioc的时    

   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;                 //选择管脚PC.13LED

   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //管脚速度为50M

 

        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          //设置输出模式为推挽输出

   GPIO_Init(GPIOC, &GPIO_InitStructure);             //将上述设置写入到GPIOC里去

}

 

      void NVIC_Config(void)                                           //中断控制器的配置

{

NVIC_InitTypeDef NVIC_InitStructure;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);                   //优先组设置

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

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;      //抢占优先级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;           //子优先级

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

NVIC_Init(&NVIC_InitStructure);

}

void Timer_Config(void)         //定时器的配置

{

       TIM_TimeBaseInitTypeDef   TIM_TimeBaseStructure;   

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);  //Timer2 时钟使能

TIM_DeInit(TIM2);                              //复位TIM2定时器      

TIM_TimeBaseStructure.TIM_Period=1000;          //定时器周期

TIM_TimeBaseStructure.TIM_Prescaler=36000-1;     //预分频数

TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;   //TIM2时钟分频,1表示不分频

TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//定时器计数为向上计数模式

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);     

TIM_ClearFlag(TIM2, TIM_FLAG_Update);           //清除定时器2的溢出标志位

TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);      //使能定时器2溢出中断

TIM_Cmd(TIM2, ENABLE);                       //定时器2使能

}

 

 

int main(void)

{

  SystemInit();//初始化时钟,配置为72MHz,我试过将这句注释掉,好像不影响结果。查            

              了一下,在配置

               //main函数之前的启动代码有这样一句  LDR R0, =SystemInit,我疑惑的是难               

             道启动的时候就配成72Mhz? 

    GPIO_Config();

       NVIC_Config();

       Timer_Config();

  while(1)

       { 

    ;     

  }

}

 

 


中断服务函数

void TIM2_IRQHandler(void)

{

static int flag_bit=0;//定义一个标志位

      

if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET ) //判断中断溢出标志为是否为1

       {

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

               flag_bit = !flag_bit;

               if(flag_bit == 1)

          GPIO_SetBits(GPIOC, GPIO_Pin_13);             //熄灭LED

               if(flag_bit == 0)

     GPIO_ResetBits(GPIOC, GPIO_Pin_13);           //点亮LED

        }

 

   这个函数是写在stm32f10x_it.c 里面的,我对TIM2_IRQHandler()函数的理解应该是这样的:

    首先由定时器定时,定时好了产生中断溢出标志位,发送中断

    然后进入中断服务函数TIM2_IRQHandler(),进入函数之后要做的就是清除中断溢出 

    标志位。

    最后再执行函数里的其他内容。

定时器定时时间计算是这两句:

                   TIM_TimeBaseStructure.TIM_Period=1000;          //定时器周期

                   TIM_TimeBaseStructure.TIM_Prescaler=36000-1;     //预分频数

   Prescaler可以理解为定时器的基数是72M  /  Prescaler+1  = 2000k,也就是500us Period 可以理解为要计数多少次,这里是1000次。 所以就是每500us记一次,计数1000次,就是500ms

公式为:

   Period / (72M / (Prescaler+1) )=____

    1000 / (72 M/ (35999+1) )  =  0.5

 

 

我有的一些疑问:1、进入中断函数之后,定时器要干些什么,是不是就停止计数了?

                2、计数记到1000发生中断,计数值是不是有自动清零

  问题先放到这儿,边学习,边解决。             

               

 

 

3仿真结果观察

前面第三章已经过如何仿真波形的步骤,可以参看前面。点击setup 按钮 会弹出一个窗口,在窗口的右上边,有个new的按钮,点击后输入 PORTC.13

 

仿真运行结果如下:


可以从仿真结果中观察到,方波的周期为一秒。占空比为0.5 ,跟预期一致。

 

4对第四章串口的补充

  第四章介绍了串口的打印函数printf 是如何调用实现的。但要使用keil自带的微库microLIB ,那能不能不使用这个微库呢。我参照野火的教程,修改了程序,自己编写usart_printf()函数来实现打印的功能。

 

   USRT1的配置不改变,主要的就是添加打印函数实现串口输出功能。代码感觉可能很长,但无非就是一些判断,看看字符串最后一位是不是\0 ,不是的话,遇到转义字符,/n /r 怎么做,以及将数字转换成字符这些。

这些很多时候我都没注意:

  1  这一句while ( *Data != 0)     // 判断是否到达字符串结束符

我们平时不是都用 \0 吗? 0开始我还没反应过来。 其实ASCII的十六进制的0 就是\0

 如果要使用\0while ( *Data !='\0') ,主要要加单引号表字符串,上面没有加就是十六进制,后面的也就能明白了。

  2stdarg.h这个函数,以前都没见过,但学习就是要学习新知识。 知识改变命运,我一直都相信这句!

  3char *itoa(int value, char *string, int radix)  是指针函数,返回值是一个地址

  4、其他的都是涉及指针的操作,所以C指针一定要学好,没学好,不要紧,趁这个机会把它弄明白,当我看懂了下面这些,也就明白了。

 

#ifndef __usart_debug_H

#define    __usart_debug_H

 

#include "stm32f10x.h"

#include   //stdarg.hC语言中C标准函数库的头文件,目的为让函数能够接收可变参数。

 

extern void usart_debug_config(void);  //提供给外部函数调用usart_debug_config()函数。

//

//  函数名:itoa

//  描述  :将整形数据转换成字符串

// 输入  -radix =10 表示10进制,其他结果为0

//         -value 要转换的整形数

 //       -buf 转换后的字符串

 //       -radix = 10

 * 输出  :无

 * 返回  :无

 * 调用  :被USART1_printf()调用

 *

static char *itoa(int value, char *string, int radix)

{

    int     i, d;

    int     flag = 0;

    char    *ptr = string;

 

    //This implementation only works for decimal numbers.  

    if (radix != 10)

    {

        *ptr = 0;

        return string;

    }

 

   if (!value)

    {

        *ptr++ = 0x30;

        *ptr = 0;

        return string;

    }

 

    //if this is a negative value insert the minus sign.

    if (value < 0)

    {

        *ptr++ = '-';

 

        // Make the value positive.

        value *= -1;

    }

 

    for (i = 10000; i > 0; i /= 10)

    {

        d = value / i;

 

        if (d || flag)

        {

            *ptr++ = (char)(d + 0x30);

            value -= (d * i);

            flag = 1;

        }

    }

 

    // Null terminate the string. 

    *ptr = 0;

 

    return string;

 

} //NCL_Itoa

 

 

 // 函数名:USART1_printf

 //描述  :格式化输出,类似于C库中的printf,但这里没有用到C

 //输入  -USARTx 串口通道,这里只用到了串口1,即USART1

 //                -Data   要发送到串口的内容的指针

//                     -...    其他参数

// 输出  :无

 // 返回  :无

 //调用  :外部调用

//         典型应用USART1_printf( USART1, "\r\n this is a demo \r\n" );

//                        USART1_printf( USART1, "\r\n %d \r\n", i );

//                        USART1_printf( USART1, "\r\n %s \r\n", j );

//

static   void USART1_printf(USART_TypeDef* USARTx, uint8_t *Data,...)

{

       const char *s;

  int d;  

  char buf[16];

 

  va_list ap;        // va_list ap  va_start(ap, Data)以及后面的va_arg() 都来自 stdarg.h

  va_start(ap, Data);//具体用法请参照相关资料。

 

       while ( *Data != 0)     // 判断是否到达字符串结束符

       {                                                   

              if ( *Data == 0x5c )  // '\'  ASCII 0x5c就是转义字符'\'

              {                                                              

                     switch ( *++Data )

                     {

                            case 'r':                                                        //回车符

                                   USART_ClearFlag(USART2,USART_FLAG_TC);

                                   USART_SendData(USARTx, 0x0d);

                              while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );

                                   Data ++;

                                   break;

 

                            case 'n':                                                        //换行符

                                   USART_ClearFlag(USART2,USART_FLAG_TC);

                                   USART_SendData(USARTx, 0x0a);    

                            while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );

                                   Data ++;

                                   break;

                           

                            default:

                                   Data ++;

                                break;

                     }                  

              }

              else if ( *Data == '%')

              {                                                               //

                     switch ( *++Data )

                     {                         

                            case 's':                                                                     //字符串

                                   s = va_arg(ap, const char *);

          for ( ; *s; s++)

                                   {

                                          USART_ClearFlag(USART2,USART_FLAG_TC);

                                          USART_SendData(USARTx,*s);

                                          while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );

          }

                                   Data++;

          break;

 

        case 'd':                                                                   //十进制

          d = va_arg(ap, int);

          itoa(d, buf, 10);

          for (s = buf; *s; s++)

                                   {

                                     USART_ClearFlag(USART2,USART_FLAG_TC);

                                          USART_SendData(USARTx,*s);

                                          while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );

          }

                                   Data++;

          break;

                             default:

                                          Data++;

                                break;

                     }           

              } //end of else if 

              else  {

                          USART_ClearFlag(USART2,USART_FLAG_TC);

                          USART_SendData(USARTx, *Data++);

                          while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );

                    }      

                     while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );

       }

}

 

#endif // __USART1_H 

 

软件仿真中的效果图:

 波形图 IO口状态 串口输出 都在下面图里了

 



5工程代码   

  定时器中断                    百度云盘:http://pan.baidu.com/s/1mzOIE

  定时器中断带串口          百度云盘:http://pan.baidu.com/s/1dD06Olb

  野火串口工程                 百度云盘:http://pan.baidu.com/s/1o6pzjZ0

  word文档教程                 百度文库: 

关闭窗口

相关文章