專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> STM32 >> 瀏覽文章

STM32學(xué)習(xí)之通用定時(shí)器的使用

作者:竹葉聽箏   來源:竹葉聽箏   點(diǎn)擊數(shù):  更新時(shí)間:2014年06月19日   【字體:

1、STM32F103內(nèi)部定時(shí)器有哪些?

    STM32一共有8個(gè)通用16Timer,其中TIMER1TIMER8是高級(jí)定時(shí)器,其它的TIMER2~TIMER7是普通定時(shí)器。
   
此外還有一個(gè)Systick(系統(tǒng)滴答定時(shí)器),這個(gè)定時(shí)器通常在操作系統(tǒng)中作為系統(tǒng)的任務(wù)切換周期。
   
還有一個(gè)RTC,是一個(gè)毫秒定時(shí)器,支持秒級(jí)中斷,用來做實(shí)時(shí)時(shí)鐘計(jì)數(shù)器。

    看門狗定時(shí)器 也可以算一個(gè)。

    8個(gè)定時(shí)器中,Timer1 Timer8是由APB2(輸出最高頻率為72MHZ)預(yù)分頻后,再通過一個(gè)倍頻器得到時(shí)鐘頻率,最高為72MHz。Timer2~Timer7則是由APB1(輸出最高頻率為36MHZ)預(yù)分頻后,再通過一個(gè)倍頻器得到時(shí)鐘頻率,最高為36MHz

 

2、如何進(jìn)行程序編寫     

  這里我通過定時(shí)器來控制一個(gè)LED0.5s 0.5s ,交替閃爍。當(dāng)然要讓定時(shí)器正常工作起來,還要配置中斷NVIC。定時(shí)器計(jì)數(shù)到某個(gè)數(shù),產(chǎn)生中斷,從而進(jìn)入中斷服務(wù)程序,點(diǎn)亮LED燈。

main函數(shù)分析:

#include "stm32f10x.h"

 

void GPIO_Config(void)//GPIO配置

{

   GPIO_InitTypeDef GPIO_InitStructure;

   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能gpioc的時(shí)    

   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;          //設(shè)置輸出模式為推挽輸出

   GPIO_Init(GPIOC, &GPIO_InitStructure);             //將上述設(shè)置寫入到GPIOC里去

}

 

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

{

NVIC_InitTypeDef NVIC_InitStructure;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);                   //優(yōu)先組設(shè)置

NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn ;           //TIM2中斷選通

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;      //搶占優(yōu)先級(jí)

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;           //子優(yōu)先級(jí)

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

NVIC_Init(&NVIC_InitStructure);

}

void Timer_Config(void)         //定時(shí)器的配置

{

       TIM_TimeBaseInitTypeDef   TIM_TimeBaseStructure;   

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);  //Timer2 時(shí)鐘使能

TIM_DeInit(TIM2);                              //復(fù)位TIM2定時(shí)器      

TIM_TimeBaseStructure.TIM_Period=1000;          //定時(shí)器周期

TIM_TimeBaseStructure.TIM_Prescaler=36000-1;     //預(yù)分頻數(shù)

TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;   //TIM2時(shí)鐘分頻,1表示不分頻

TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//定時(shí)器計(jì)數(shù)為向上計(jì)數(shù)模式

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);     

TIM_ClearFlag(TIM2, TIM_FLAG_Update);           //清除定時(shí)器2的溢出標(biāo)志位

TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);      //使能定時(shí)器2溢出中斷

TIM_Cmd(TIM2, ENABLE);                       //定時(shí)器2使能

}

 

 

int main(void)

{

  SystemInit();//初始化時(shí)鐘,配置為72MHz,我試過將這句注釋掉,好像不影響結(jié)果。查            

              了一下,在配置

               //main函數(shù)之前的啟動(dòng)代碼有這樣一句  LDR R0, =SystemInit,我疑惑的是難               

             道啟動(dòng)的時(shí)候就配成72Mhz? 

    GPIO_Config();

       NVIC_Config();

       Timer_Config();

  while(1)

       { 

    ;     

  }

}

 

 


中斷服務(wù)函數(shù)

void TIM2_IRQHandler(void)

{

static int flag_bit=0;//定義一個(gè)標(biāo)志位

      

if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET ) //判斷中斷溢出標(biāo)志為是否為1

       {

   TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update); //清除溢出中斷標(biāo)志位

               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);           //點(diǎn)亮LED

        }

 

   這個(gè)函數(shù)是寫在stm32f10x_it.c 里面的,我對(duì)TIM2_IRQHandler()函數(shù)的理解應(yīng)該是這樣的:

    首先由定時(shí)器定時(shí),定時(shí)好了產(chǎn)生中斷溢出標(biāo)志位,發(fā)送中斷

    然后進(jìn)入中斷服務(wù)函數(shù)TIM2_IRQHandler(),進(jìn)入函數(shù)之后要做的就是清除中斷溢出 

    標(biāo)志位。

    最后再執(zhí)行函數(shù)里的其他內(nèi)容。

定時(shí)器定時(shí)時(shí)間計(jì)算是這兩句:

                   TIM_TimeBaseStructure.TIM_Period=1000;          //定時(shí)器周期

                   TIM_TimeBaseStructure.TIM_Prescaler=36000-1;     //預(yù)分頻數(shù)

   Prescaler可以理解為定時(shí)器的基數(shù)是72M  /  Prescaler+1  = 2000k,也就是500us ,Period 可以理解為要計(jì)數(shù)多少次,這里是1000次。 所以就是每500us記一次,計(jì)數(shù)1000次,就是500ms。

公式為:

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

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

 

 

我有的一些疑問:1、進(jìn)入中斷函數(shù)之后,定時(shí)器要干些什么,是不是就停止計(jì)數(shù)了?

                2、計(jì)數(shù)記到1000發(fā)生中斷,計(jì)數(shù)值是不是有自動(dòng)清零

  問題先放到這兒,邊學(xué)習(xí),邊解決。             

               

 

 

3仿真結(jié)果觀察

前面第三章已經(jīng)過如何仿真波形的步驟,可以參看前面。點(diǎn)擊setup 按鈕 會(huì)彈出一個(gè)窗口,在窗口的右上邊,有個(gè)new的按鈕,點(diǎn)擊后輸入 PORTC.13

 

仿真運(yùn)行結(jié)果如下:


可以從仿真結(jié)果中觀察到,方波的周期為一秒。占空比為0.5 ,跟預(yù)期一致。

 

4對(duì)第四章串口的補(bǔ)充

  第四章介紹了串口的打印函數(shù)printf 是如何調(diào)用實(shí)現(xiàn)的。但要使用keil自帶的微庫(kù)microLIB ,那能不能不使用這個(gè)微庫(kù)呢。我參照野火的教程,修改了程序,自己編寫usart_printf()函數(shù)來實(shí)現(xiàn)打印的功能。

 

   USRT1的配置不改變,主要的就是添加打印函數(shù)實(shí)現(xiàn)串口輸出功能。代碼感覺可能很長(zhǎng),但無非就是一些判斷,看看字符串最后一位是不是\0 ,不是的話,遇到轉(zhuǎn)義字符,/n /r 怎么做,以及將數(shù)字轉(zhuǎn)換成字符這些。

這些很多時(shí)候我都沒注意:

  1、  這一句while ( *Data != 0)     // 判斷是否到達(dá)字符串結(jié)束符

我們平時(shí)不是都用 \0 嗎? 0開始我還沒反應(yīng)過來。 其實(shí)ASCII的十六進(jìn)制的0 就是\0

 如果要使用\0,while ( *Data !='\0') ,主要要加單引號(hào)表字符串,上面沒有加就是十六進(jìn)制,后面的也就能明白了。

  2、stdarg.h這個(gè)函數(shù),以前都沒見過,但學(xué)習(xí)就是要學(xué)習(xí)新知識(shí)。 知識(shí)改變命運(yùn),我一直都相信這句!

  3、char *itoa(int value, char *string, int radix)  是指針函數(shù),返回值是一個(gè)地址

  4、其他的都是涉及指針的操作,所以C指針一定要學(xué)好,沒學(xué)好,不要緊,趁這個(gè)機(jī)會(huì)把它弄明白,當(dāng)我看懂了下面這些,也就明白了。

 

#ifndef __usart_debug_H

#define    __usart_debug_H

 

#include "stm32f10x.h"

#include   //stdarg.hC語(yǔ)言中C標(biāo)準(zhǔn)函數(shù)庫(kù)的頭文件,目的為讓函數(shù)能夠接收可變參數(shù)。

 

extern void usart_debug_config(void);  //提供給外部函數(shù)調(diào)用usart_debug_config()函數(shù)。

//

//  函數(shù)名:itoa

//  描述  :將整形數(shù)據(jù)轉(zhuǎn)換成字符串

// 輸入  -radix =10 表示10進(jìn)制,其他結(jié)果為0

//         -value 要轉(zhuǎn)換的整形數(shù)

 //       -buf 轉(zhuǎn)換后的字符串

 //       -radix = 10

 * 輸出  :無

 * 返回  :無

 * 調(diào)用  :被USART1_printf()調(diào)用

 *

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

 

 

 // 函數(shù)名:USART1_printf

 //描述  :格式化輸出,類似于C庫(kù)中的printf,但這里沒有用到C庫(kù)

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

 //                -Data   要發(fā)送到串口的內(nèi)容的指針

//                     -...    其他參數(shù)

// 輸出  :無

 // 返回  :無

 //調(diào)用  :外部調(diào)用

//         典型應(yīng)用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);//具體用法請(qǐng)參照相關(guān)資料。

 

       while ( *Data != 0)     // 判斷是否到達(dá)字符串結(jié)束符

       {                                                   

              if ( *Data == 0x5c )  // '\'  ASCII 0x5c就是轉(zhuǎn)義字符'\'

              {                                                              

                     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':                                                                   //十進(jìn)制

          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口狀態(tài) 串口輸出 都在下面圖里了

 



5工程代碼   

  定時(shí)器中斷                    百度云盤:http://pan.baidu.com/s/1mzOIE

  定時(shí)器中斷帶串口          百度云盤:http://pan.baidu.com/s/1dD06Olb

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

  word文檔教程                 百度文庫(kù): 

關(guān)閉窗口

相關(guān)文章