[嵌入式]定時器

丫就是熊個貓貓發表於2016-12-24

定時器

7.1  通用定時器概述

    定時器或計數器的邏輯電路本質上是相同的,它們之間的區別主要在用途上。它們都是主要由帶有儲存當前值的暫存器和當前暫存器值加1或減1邏輯組成,其內部工作原理圖是以一個N位的加1或減1計數器為核心,計數器的初始值由初始化程式設計設定,計數脈衝的來源有兩類:系統時鐘和外部事件脈衝。

 

定時工作方式

    若程式設計設定定時/計數器為定時工作方式時,則N位計數器的計數脈衝來源於內部系統時鐘,並經過M分頻。每個計數脈衝使計數器加1或減1,當N位計數器裡的數加到0或減到0時,則會產生一個“回0訊號”,該訊號有效時表示N位計數器裡的當前值是0。因為系統時鐘的頻率是固定的,其M分頻後所得到的計數脈衝頻率也就是固定的,因此通過對該頻率脈衝的計數就轉換為定時,實現了定時功能。

計數工作方式

    若程式設計設定定時/計數器為計數方式時,則N位計數器的計數脈衝來源於外部事件產生的脈衝訊號。有一個外部事件脈衝,計數器加1或減1,直到N位計數器中的值為0,產生“回0訊號”。

N位計數器裡初始值的計算,在不同的定時部件中其具體的計算公式是不同的。  

7.2S5PV210的脈寬調製定時器

    S5PV210有五個32位脈衝寬度調製定時器。這些定時器為ARM分系統產生內部中斷。此外,定時器0、1、2、3包含一個PWM功能模組,用於驅動外部I/O訊號。定時器0中的PWM可選擇的死區發生器,能夠支援一個大電流裝置。定時器4是沒有外部引腳的內部定時器。


    定時器將APB—PCLK作為時鐘源。定時器0和1共用一個可程式設計8位元分頻器,該分頻器為PCLK提供第一層分頻。定時器2、3、4共用一個不同的8位元分頻器。每個定時器有自己的專用時鐘分頻器,用於提供第二層的時鐘分頻器(分頻器分為2、4、8、16分頻)。

    每一個定時器有自己的由定時器時鐘分頻得到的32位元減法計數器。減法計數器最初從定時器計數緩衝暫存器(TCNTBn)中得到初值,然後在定時器時鐘控制下進行減法操作。在自動過載工作狀態,如果減法計數器降到了0,TCNTBn相應的值會過載到減法計數器來啟動下一個迴圈。

    PWM功能要用到TCMPBn比較緩衝暫存器的值。如果在定時器控制邏輯下,減法計數器的值與比較暫存器的值相匹配,定時器邏輯會改變輸出電平。因此,比較緩衝暫存器決定了PWM輸出的開啟和關閉時間。

    TCNTBn和TCMPBn暫存器都是都是雙快取配置,能夠使定時器引數在迴圈的中間得到更新。新的數值直到當前時鐘迴圈完成後才會起作用。

定時器組成:

   減法計數器、初值暫存器、比較暫存器、觀察暫存器、控制邏輯等5部分構成。

 

輸入時脈頻率的公式:

Timer input clock Frequency =

 PCLK / {prescaler value+1} / {clock divider }


{prescaler value} = 0~255
{ clock divider } = 2, 4, 8, 16

PWM定時器的暫存器

暫存器宣告:

// define Timer register
#define rTCFG0       (*(volatile unsigned int *)0xEA000000)
#define rTCFG1       (*(volatile unsigned int *)0xEA000004)
#define rTCON        (*(volatile unsigned int *)0xEA000008)
#define rTCNTB0   (*(volatile unsigned int *)0xEA00000C)
#define rTCMPB0   (*(volatile unsigned int *)0xEA000010)
#define rTCNTO0   (*(volatile unsigned int *)0xEA000014)

    ……


   前兩個是定時器配置暫存器,主要用來設定預分頻值和分割器值。第三個是控制暫存器,主要用來設定各定時器功能,第四個是計數緩衝暫存器,第五個是比較緩衝暫存器,第六個是計數觀察暫存器。

定時器配置暫存器0(TCFG0)

用來配置2個預分頻器和死區的的長度的暫存器。其中:

定時器輸入時脈頻率=PCLK/({預分頻器值+1})/{分割器的值};

{預分頻器值}=1~255;

{分割器的值}=1,2,4,8,16,TCLK;

死區長度=0~254。

 

定時器配置暫存器1(TCFG1)

配置每個定時器獨有分割器的值。

 

定時器控制暫存器(TCON)

控制定時器的工作模式、定時器啟停等。

 

定時器n計數緩衝暫存器(TCNTBn)

    用於定時器n的時間計數值。該暫存器值在定時器啟動時被送入減法計數器中,作為初值開始減法計數。在定時器啟動週期內改變該暫存器的值不會影響當前的定時器工作,改變的值在定時器減至0並開始下一次定時操作時才會生效。

 

定時器n 比較緩衝暫存器(TCMPBn)

    該暫存器用於PWM波形輸出佔空比的設定。在定時器工作時,若減法計數器的值與該比較緩衝暫存器的值相匹配,定時器會改變輸出電平。因此,比較緩衝暫存器決定了PWM輸出的開啟和關閉時間。

 

定時器n的觀察暫存器(TCNTOn)

    用於觀察PWM定時器當前定時值的暫存器。減法計數器當前計數值只能通過該觀察暫存器讀取。

 

PWM雙緩衝定時器工作流程

 

1)程式開始,設定TCMPBn、TCNTBn這兩個暫存器,表示定時器n的比較值、初始計數值。

2)設定TCON暫存器啟動定時器n,這時TCMPBn、TCNTBn的值將被裝入內部暫存器TCMPn、TCNTn中,在定時器n的時脈頻率下,TCNTn開始減1計數,其值可以通過讀取TCNTON暫存器得知。

3)當TCNTn的值等於TCMPn的值時,定時器n的輸出管腳TOUTn反轉,TCNTn繼續減1計數。

4)當TCNTn的值到達0時,其輸出管腳TOUTn再次反轉,並觸發定時器n的中斷。

5)當TCNTn的值到達0時,如果在TCON暫存器中將定時器n設定為自動載入,則TCMPBn和TCNTBn暫存器的值被自動裝入TCMPn和TCNTn暫存器中,下一次計數流程開始。

 

要完成如圖所示的簡單的PWM迴圈,其步驟為:

·使用159(50+109)初始化TCNTBn暫存器,用109初始化TCMPBn。

·啟動定時器,設定開始位並手動將該位更新至關閉狀態。159的TCNTBn值會被載入減法

  計數器,109的TCMPBn值會被載入比較計數器,然後TOUTn輸出置為低電平。

·減法計數器開始進行減法計數,TCNTn的值減至TCNTn暫存器值109,輸出TOUTn從低電

  平變為高電平。

·如果減法計數器到達0,它會產生一箇中斷請求。

·減法計數器自動載入TCNTBn,會重啟迴圈。

PWM定時器控制示例程式

void timer_request(void)

{

printf("\r\n#############Timer test############\r\n");

// 禁止所有timer

pwm_stopall();

counter = 0;

// 設定timer0中斷的中斷處理函式

intc_setvectaddr(NUM_TIMER0, irs_timer);

// 使能timer0中斷

intc_enable(NUM_TIMER0);

// 設定timer0

timer_init(0,65,4,62500,0);

}

void timer_init(unsigned long utimer,unsigned long uprescaler,unsigned long udivider,unsigned long utcntb,unsigned long utcmpb)

{unsigned long temp0;

//定時器時鐘=PCLK/({prescaler value + 1} ) / {divider value} = PCLK/(65+1)/16=62500hz

temp0 = TCFG0;       //設定預分頻係數為66

temp0 = (temp0 & (~(0xff00ff))) | ((uprescaler-1)<<0);

TCFG0 = temp0;

temp0 = TCFG1; // 16分頻

temp0 = (temp0 & (~(0xf<<4*utimer))& (~(1<<20))) |(udivider<<4*utimer);

TCFG1 = temp0;

TCNTB0 = utcntb;

TCMPB0 = utcmpb;

TCON |= 1<<1; // 手動更新

TCON &= ~(1<<1); // 清手動更新位

TCON |= (1<<0)|(1<<3); // 自動載入和啟動timer0

temp0 = TINT_CSTAT; // 使能timer0中斷

temp0 = (temp0 & (~(1<<utimer)))|(1<<(utimer));

TINT_CSTAT = temp0;

}

// 停止所有timer

void pwm_stopall(void)

{TCON = 0;}

 

void irs_timer()

{unsigned long uTmp;

//清timer0的中斷狀態暫存器

uTmp = TINT_CSTAT;

TINT_CSTAT = uTmp;      

// 列印中斷髮生次數

printf("Timer0IntCounter = %d \r\n",counter++);

// vic相關的中斷清除

intc_clearvectaddr();

}

主函式初始化串列埠和中斷,設定定時器,然後就等待定時器中斷髮生。

int main(void)

{// 初始化串列埠

uart_init();

// 中斷相關初始化

system_initexception();

// 設定timer

timer_request();

while(1);

}

7.3 看門狗定時器

看門狗定時器概述

    watchdog,中文名稱叫做“看門狗”,全稱watchdog timer,從字面上我們可以知道其實它屬於一種定時器。然而它與我們平常所接觸的定時器在作用上又有所不同。普通的定時器一般起計時作用,計時超時 (Timer Out)則引起一箇中斷,例如觸發一個系統時鐘中斷。watchdog本質上是一種定時器,那麼普通定時器所擁有的特徵它也應該具備,當它記時超時時也會引起事件的發生,只是這個事件除了可以是系統中斷外,它也可以是一個系統重起訊號(Reset Signal),能傳送系統重起訊號的定時器我們就叫它watchdog。

看門狗定時器框圖

 

   PCLK為系統時鐘,看門狗定時器的時鐘由PCLK經過預分頻後再分割得到。預分頻器值和頻率分割因子由看門狗定時器的控制暫存器(WTCON)進行程式設計設定,可選範圍是0~255。頻率分割因子可選擇的值為16、32、64、128。

下面公式計算看門狗定時器的計數時鐘週期:

       T_watchdog = 1/ (PCLK / (預分頻器值 + 1) / 分割因子)

    看門狗定時器在計數器變為0時,會產生一個寬度為128個PCLK的復位脈衝訊號。程式在正常工作時,應該定期將看門狗定時器重置。如果程式跑飛,則看門狗定時器回0時會將系統復位,防止應用系統出現當機。

 

看門狗定時器控制暫存器(WTCON)

    WTCON暫存器的內容包括:使用者是否啟用看門狗定時器、4個分頻比的選擇、是否允許中斷產生、是否允許復位操作等。如果使用者想把看門狗定時器當做一般定時器使用,應中斷使能,禁止看門狗定時器復位。

 

看門狗定時器資料暫存器(WTDAT)

   WTCON用於指定超時時間,在初始化看門狗操作後看門狗資料暫存器的值不能被自動裝載到看門狗計數暫存器(WTCNT)中。然而,初始值0x8000可以自動裝載WTDAT的值到WTCNT中。

 

看門狗定時器計數暫存器(WTCNT)

    WTCNT包含看門狗定時器工作時計數器的當前計數值。注意在初始化看門狗操作後,看門狗資料暫存器的值不能被自動裝載到看門狗計數暫存器(WTCNT)中,所以看門狗被允許之前應該初始化看門狗計數暫存器的值。

 

看門狗定時器參考程式

    在系統程式正常執行情況下,應用程式必須在一定週期內(不能大於看門狗定時器所產生的時間間隔)執行重置看門狗動作,使其計數器值不會遞減到0,因而也不會產生復位訊號。一旦程式出現死鎖,就不能週期性地重置看門狗,看門狗定時器計數溢位,產生一個“回0訊號”,利用該訊號復位微處理器,從而使系統程式退出死鎖,重新進入正常執行狀態。

1. 使能看門狗定時器

void enable_watchdog()
{           rWTCON=0x7F01;    //0b0111111100000001
                rWTDAT=0x8000;
                rWTCON|=1<<5;       //開啟看門狗
}

從上面的設定可知暫存器WTCON的值為0x7F01,分解出來得:

Prescaler Value               =255

Division_factor                =16(Clock Select=16)

Interrupt Generation       =0(不產生中斷)

Reset                            =1(開啟Reset Signal)

2. 給看門狗定時器寫數

void feed_dog()

{        rWTCNT=0x8000;

}

3. 使用看門狗定時器

在主程式中,先初始化看門狗定時器,再在各個正常工作流程中進行喂狗操作。

void main()

{init_system();

       .....

        enable_watchdog();

       .....

        while(1)

        {feed_dog();       

        }

}

7.4  RTC實時時鐘

RTC實時時鐘概述

    實時時鐘部件RTC是用於提供年、月、日、時、分、秒、星期等實時時間資訊的定時部件。

    RTC部件可以將年、月、日、時、分、秒、星期等資訊的8位資料以BCD碼格式輸出。它由外部時鐘驅動工作,外部時脈頻率為32.768 kHz晶體,必須XTIrtc和XTOrtc引腳接外部晶體,並接合適的電容。同時RTC部件還可以具有報警功能。

RTC控制器

 

XTIrtc、XTOrtc是外部時鐘引腳,一般外接32.768kHz的晶振。

RTC控制器可以由後備電池提供電力。後備電池通過RTCVDD引腳接到RTC控制器。當系統電源關閉時,後備電池僅驅動RTC部件的振盪器電路和BCD計數器,以使功耗降到最小。

閏年發生器可以通過年、月、日的BCD碼判斷是不是閏年。

在節電模式或者正常執行模式下,RTC可以在特定的時候觸發蜂鳴器。在正常執行模式下,啟用的是報警中斷訊號(ALMINT),在節電模式下,電源管理部件的喚醒訊號(PWMKUP)啟用的同時啟用中斷訊號(ALMINT)。RTC內部的報警暫存器(RTCALM)可以設定報警工作狀態的使能/不使能以及報警時間的條件。

RTC的時間片計時器用於產生一箇中斷請求。當計數器的值變為0時,引起時間片計時中斷。中斷訊號的週期用下列公式計算:

週期(秒)=(n+1)/128

n代表時間片計數器中的值,範圍是1~127。

進位復位功能可以由RTC的進位復位暫存器(RTCRST)來控制。秒的進位週期可以進行選擇(30、40、50),在進位復位發生後,秒的數值又循回到0。例如當前時間是8:12:49,進位週期選為50秒,則當前時間將變為8:13:00。

RTC相關暫存器

 

RTC程式設計

1. RTC暫存器宣告

#define RTC_BASE(0xE2800000)
#defineINTP      ( *((volatile unsigned long *)(RTC_BASE + 0x30)) )
#defineRTCCON    ( *((volatile unsigned long *)(RTC_BASE + 0x40)) )
#defineTICCNT    ( *((volatile unsigned long *)(RTC_BASE + 0x44)) )
#defineRTCALM    ( *((volatile unsigned long *)(RTC_BASE + 0x50)) )
#defineALMSEC    ( *((volatile unsigned long *)(RTC_BASE + 0x54)) )
#defineALMMIN    ( *((volatile unsigned long *)(RTC_BASE + 0x58)) )
#defineALMHOUR  ( *((volatile unsigned long *)(RTC_BASE + 0x5c)) )
#defineALMDATE    ( *((volatile unsigned long *)(RTC_BASE + 0x60)) )
#defineALMMON    ( *((volatile unsigned long *)(RTC_BASE + 0x64)) )
#defineALMYEAR  ( *((volatile unsigned long *)(RTC_BASE + 0x68)) )
#defineRTCRST     ( *((volatile unsigned long *)(RTC_BASE + 0x6c)) )
#defineBCDSEC    ( *((volatile unsigned long *)(RTC_BASE + 0x70)) )
#defineBCDMIN   ( *((volatile unsigned long *)(RTC_BASE + 0x74)) )
#defineBCDHOUR    ( *((volatile unsigned long *)(RTC_BASE + 0x78)) )
#defineBCDDATE    ( *((volatile unsigned long *)(RTC_BASE + 0x7c)) )
#defineBCDDAY     ( *((volatile unsigned long *)(RTC_BASE + 0x80)) )
#defineBCDMON     ( *((volatile unsigned long *)(RTC_BASE + 0x84)) )
#defineBCDYEAR     ( *((volatile unsigned long *)(RTC_BASE + 0x88)) )
#defineCURTICCNT   ( *((volatile unsigned long *)(RTC_BASE + 0x90)) )
#defineRTCLVD    ( *((volatile unsigned long *)(RTC_BASE + 0x94)) )


2. 使能/關閉RTC控制器

// 使能/關閉rtc控制器
void rtc_enable(unsigned char bdata)
{
unsigned long uread;
 uread = RTCCON;
RTCCON = (uread&~(1<<0))|(bdata);
}


 3. 設定RTC時間

void rtc_settime(void)
{// 初始值為重置值
unsigned long year = 12;unsigned long month = 5;unsigned long date = 1;
unsigned long hour = 12;unsigned long min = 0;unsigned long sec = 0;
unsigned long weekday= 3;
//將時間轉化為BCD碼
year = ( ((year/100)<<8) +(((year/10)%10)<<4) + (year%10)  );
month  = ( ((month/10)<<4)+ (month%10) );
date = ( ((date/10)<<4) + (date%10) );
weekday = (weekday%10);
hour =( ((hour/10)<<4) + (hour%10) );
min  =( ((min/10)<<4)  + (min%10) );
sec  =( ((sec/10)<<4)  + (sec%10) );
rtc_enable(true);
// 儲存BCD碼
BCDSEC  = sec;    BCDMIN  = min;    BCDHOUR = hour;BCDDATE = date;
BCDDAY  = weekday;BCDMON  = month;BCDYEAR = year;
rtc_enable(false);printf("reset success\r\n");
}


4. 列印當前時間

void rtc_print(void)
{
unsigned long uyear,umonth,udate,uday,uhour,umin,usec;
uyear = BCDYEAR;
uyear = 0x2000 + uyear;
umonth= BCDMON;
udate = BCDDATE;
uhour = BCDHOUR;
umin  = BCDMIN;
usec  = BCDSEC;
uday  = BCDDAY;
printf("%2x : %2x : %2x  %10s,  %2x/%2x/%4x\r\n", uhour, umin, usec, day[uday], umonth, udate, uyear);
}


相關文章