|
STM8L集成了RTC模塊,這個(gè)模塊除了具有時(shí)鐘鬧鈴功能外,還具有自動(dòng)喚醒的功能.自動(dòng)喚醒在低功耗模式中很有用,因?yàn)镽TC使用的是LSE(外部32768Hz的時(shí)鐘),可以運(yùn)行在低功耗模式下,可以使用此功能周期性的喚醒CPU,執(zhí)行任務(wù),而不需要CPU一直運(yùn)行,這樣可以達(dá)到低功耗.
此公眾號(hào)已開啟留言功能,歡迎大家留言,評(píng)論

從上圖可以看出,RTC共有三個(gè)部分,第一部分是時(shí)鐘,第二部分是時(shí)鐘鬧鈴,第三部分是自動(dòng)喚醒.本文對(duì)自動(dòng)喚醒功能,不做介紹.
我們先看上圖的時(shí)鐘部分,RTC的時(shí)鐘選擇LSE,為32768Hz,RTCDIV[2:0]這三位我們?cè)O(shè)置為0,不分頻.然后進(jìn)入到PRE_DIV[6:0]這個(gè)異步分頻器,這個(gè)7位的分頻器,默認(rèn)值為0x7F,保持默認(rèn)值.至此時(shí)鐘頻率為32768/(127+1)=256Hz.繼續(xù)往下走,遇到PREDIV_S[12:0]這個(gè)是十三位同步分頻器,此分頻器默認(rèn)值為0xFF,同樣保持默認(rèn)值,至此頻率為256/(255+1)=1Hz.提供給時(shí)鐘部分的頻率是1Hz,上圖中的Calendar部分,一秒鐘計(jì)數(shù)一次,更新一次時(shí)鐘寄存器,這么低頻率可以降低系統(tǒng)的功耗.

上圖是STM8L的內(nèi)部時(shí)鐘寄存器,對(duì)這些寄存器進(jìn)行寫操作時(shí),需要先解鎖RTC寄存器的寫保護(hù)功能,讀操作無需解鎖.
上圖是,RTC時(shí)鐘功能的初始化配置流程.主要是配置年月日時(shí)分秒的初始值.

上圖是,RTC鬧鐘功能的初始化配置流程,這里主要是設(shè)置定時(shí)的時(shí)間.

要將RTC的寄存器中的時(shí)間,讀取出來顯示到液晶屏上,首先要判斷RSF位是否置位,置位說明RTC_TRx,RTC_DRx這些寄存器可以被讀取.讀取時(shí),先讀TR1寄存器,此時(shí)其他寄存器中的數(shù)據(jù)被鎖存,直到RTC_DR3被讀取.

上圖是,本例程執(zhí)行時(shí)的圖片,液晶屏1S更新一次數(shù)據(jù),圖中顯示當(dāng)前時(shí)間為21:46:30.同時(shí)右下角的藍(lán)燈會(huì)以2Hz的頻率閃爍,在鬧鐘中斷中取反藍(lán)燈控制IO,所以會(huì)是2Hz閃爍.
- /****************************************************************************************
- *開發(fā)環(huán)境:IAR for stm8 v6.5.3
- *硬件平臺(tái):STM8L-DISCOVERY
- *功能說明:使用STM8L-DISCOVERY液晶屏顯示時(shí)間
- *作 者:茗風(fēng)
- ****************************************************************************************/
- #include"iostm8l152c6.h"
- #include"stdint.h"
- #include"stdbool.h"
- bool bRTC_Update_Flag = false;//標(biāo)志位
- /* =========================================================================
- LCD MAPPING
- =========================================================================
- A
- _ ----------
- COL |_| |\ |J /|
- F| H | K |B
- _ | \ | / |
- COL |_| --G-- --M--
- | /| \ |
- E| Q | N |C
- _ | / |P \|
- DP |_| -----------
- D
- */
- #define a 0x01
- #define b 0x02
- #define c 0x04
- #define d 0x08
- #define e 0x10
- #define f 0x20
- #define g 0x40
- #define m 0x80
- const uint8_t LCD_Tab[10] = {
- a + b + c + d + e + f, // Displays "0"
- b + c, // Displays "1"
- a + b + m + g + e + d, // Displays "2"
- a + b + m + g + c + d, // Displays "3"
- f + g + m + b + c, // Displays "4"
- a + f + g + m + c +d, // Displays "5"
- a + f + e + d + c + g + m , // Displays "6"
- a + b + c, // Displays "7"
- a + b + c + d + e + f + g + m, // Displays "8"
- a + b + c + d + f + g + m // Displays "9"
- };
- /******************************************************************************************************
- * 名 稱:void GPIO_Init(void)
- * 功 能:初始化PC7為高速推挽輸出
- * 入口參數(shù):無
- * 出口參數(shù):無
- * 說 明:
- * 范 例:無
- ******************************************************************************************************/
- void GPIO_Init(void)
- {
- PC_CR1_C17 =1;//推挽輸出
- PC_CR2_C27 =1;//高速輸出
- PC_DDR_DDR7 =1;//PC4輸出
- // PC_ODR_ODR7 =0;//輸出低電平
- }
- /******************************************************************************************************
- * 名 稱:void LCD_Config(void)
- * 功 能:配置LCD
- * 入口參數(shù):無
- * 出口參數(shù):無
- * 說 明:
- * 范 例:無
- ******************************************************************************************************/
- void LCD_Config(void)
- {
- //------打開LCD/RTC時(shí)鐘------
- // CLK_PCKENR2_PCKEN22=1;//打開RTC時(shí)鐘,LCD刷新頻率與此時(shí)鐘有關(guān)
- CLK_PCKENR2_PCKEN23=1;//打開LCD時(shí)鐘,讀寫LCD寄存器用到此時(shí)鐘
-
- //---選擇LSE作為RTC時(shí)鐘---
- // CLK_CRTCR_RTCSEL0=0;
- // CLK_CRTCR_RTCSEL1=0;
- // CLK_CRTCR_RTCSEL2=0;
- // CLK_CRTCR_RTCSEL3=1;
- /* 0000: No clock selected
- 0001: HSI clock used as RTC clock source
- 0010: LSI clock used as RTC clock source
- 0100: HSE clock used as RTC clock source
- 1000: LSE clock used as RTC clock sourc*/
-
- //----設(shè)置RTC時(shí)鐘分頻值----
- // CLK_CRTCR_RTCDIV0=0;
- // CLK_CRTCR_RTCDIV1=0;
- // CLK_CRTCR_RTCDIV2=0;
- /*000: RTC clock source/1
- 001: RTC clock source /2
- 010: RTC clock source /4
- 011: RTC clock source /8
- 100: RTC clock source /16
- 101: RTC clock source /32
- 110: RTC clock source /64
- 111: RTC clock source /128*/
-
-
- //----設(shè)置LCD預(yù)分頻值----
- LCD_FRQ_PS0=0;// 2^PS[3:0]
- LCD_FRQ_PS1=0;//分頻值為1
- LCD_FRQ_PS2=0;
- LCD_FRQ_PS3=0;
-
- //----設(shè)置LCD分頻值----
- LCD_FRQ_DIV0=1;//DIV[3:0]+16
- LCD_FRQ_DIV1=1;//分頻值為15+16=31
- LCD_FRQ_DIV2=1;
- LCD_FRQ_DIV3=1;
-
- //以上分頻值的設(shè)置,最為了得到適合的LCD的刷新頻率,如果增大分頻值,會(huì)導(dǎo)致
- //LCD刷新頻率變低,會(huì)看到LCD顯示出現(xiàn)閃爍
- //比如,我們將PS[3:0]設(shè)置為0011,會(huì)看到液晶閃爍
-
- //----1/4 duty----
- LCD_CR1_DUTY0=1;//1/4 duty
- LCD_CR1_DUTY1=1;
- /* Duty ratio selection
- 00: Static duty
- 01: 1/2 duty
- 10: 1/3 duty
- 11: 1/4 duty */
- //----1/3 bias----
- LCD_CR1_B2=0;//1/3 bias
- /* 0: 1/3 bias
- 1: 1/2 bias */
- //----內(nèi)部電壓源----
- LCD_CR2_VSEL=0;
-
- //----打開引腳的SEG功能----
- // LCD_PM0=0xFF;//頭文件這個(gè)地方定義錯(cuò)誤,無法直接向LCD_PM0寫入數(shù)據(jù)
- // LCD_PM1=0xFF;//PM0寄存器定義錯(cuò)誤,導(dǎo)致PM1也無法直接寫入
- // LCD_PM2=0xFF;//PM0寄存器定義錯(cuò)誤,導(dǎo)致PM2也無法直接寫入
- *((uint8_t *)0x5404)=0xFF;//直接向LCD_PM0寄存器的地址寫入數(shù)據(jù)
- *((uint8_t *)0x5405)=0xFF;//直接向LCD_PM1寄存器的地址寫入數(shù)據(jù)
- *((uint8_t *)0x5406)=0xFF;//直接向LCD_PM2寄存器的地址寫入數(shù)據(jù)
-
- //----To set contrast to mean value----
- LCD_CR2_CC0=0;//對(duì)比度
- LCD_CR2_CC1=1;
- LCD_CR2_CC2=0;
- /* 000: VLCD0 2.6V
- 001: VLCD1 2.7V
- 010: VLCD2 2.8V
- 011: VLCD3 2.9V
- 100: VLCD4 3.0V
- 101: VLCD5 3.1V
- 110: VLCD6 3.2V
- 111: VLCD7 */
-
- //----Dead time 0----
- LCD_CR3_DEAD0=0;//no dead time
- LCD_CR3_DEAD1=0;
- LCD_CR3_DEAD2=0;
- //----LCD_PulseOnDuration_1----
- LCD_CR2_PON0=1;
- LCD_CR2_PON1=0;
- LCD_CR2_PON2=0;
- /* 000: 0 CLKps pulses
- 001: 1 CLKps pulses
- 010: 2 CLKps pulses
- 011: 3 CLKps pulses
- 100: 4 CLKps pulses
- 101: 5 CLKps pulses
- 110: 6 CLKps pulses
- 111: 7 CLKps pulses */
-
- //----Enable LCD peripheral----
- LCD_CR3_LCDEN=1;
- }
- /******************************************************************************************************
- * 名 稱:LCD_DisplayNum(uint8_t number)
- * 功 能:控制段式液晶屏的數(shù)字顯示部分
- * 入口參數(shù):number:要顯示的數(shù)字
- * 出口參數(shù):無
- * 說 明:根據(jù)數(shù)字的長度,判斷要顯示的長度,長度大于6位,只顯示后六位
- * 范 例:無
- ******************************************************************************************************/
- void LCD_DisplayNum(uint32_t number)
- {
- uint8_t cnts=0,tmp=0;
-
- if(number<10)cnts=1;
- else if(number<100)cnts=2;
- else if(number<1000)cnts=3;
- else if(number<10000)cnts=4;
- else if(number<100000)cnts=5;
- else if(number<(uint32_t) 1000000)cnts=6;
- else cnts=6;
- //判斷需要顯示數(shù)字的長度,確定在LCD屏上需要的位數(shù)
- switch(cnts)
- {
- case 6:
- tmp = LCD_Tab[number%1000000/100000];
- ((tmp&m)==0)?(LCD_RAM0&=~0x02):(LCD_RAM0 |=0x02) ;
- ((tmp&e)==0)?(LCD_RAM0&=~0x01):(LCD_RAM0 |=0x01) ;
- ((tmp&g)==0)?(LCD_RAM2&=~0x80):(LCD_RAM2 |=0x80) ;
- ((tmp&b)==0)?(LCD_RAM2&=~0x40):(LCD_RAM2 |=0x40) ;
- ((tmp&f)==0)?(LCD_RAM6&=~0x08):(LCD_RAM6 |=0x08) ;
- ((tmp&a)==0)?(LCD_RAM6&=~0x04):(LCD_RAM6 |=0x04) ;
- ((tmp&c)==0)?(LCD_RAM3&=~0x20):(LCD_RAM3 |=0x20) ;
- ((tmp&d)==0)?(LCD_RAM3&=~0x10):(LCD_RAM3 |=0x10) ;
- case 5:
- tmp = LCD_Tab[number%100000/10000];
- ((tmp&m)==0)?(LCD_RAM0&=~0x08):(LCD_RAM0 |=0x08) ;
- ((tmp&e)==0)?(LCD_RAM0&=~0x04):(LCD_RAM0 |=0x04) ;
- ((tmp&g)==0)?(LCD_RAM2&=~0x20):(LCD_RAM2 |=0x20) ;
- ((tmp&b)==0)?(LCD_RAM2&=~0x10):(LCD_RAM2 |=0x10) ;
- ((tmp&f)==0)?(LCD_RAM6&=~0x02):(LCD_RAM6 |=0x02) ;
- ((tmp&a)==0)?(LCD_RAM6&=~0x01):(LCD_RAM6 |=0x01) ;
- ((tmp&c)==0)?(LCD_RAM3&=~0x80):(LCD_RAM3 |=0x80) ;
- ((tmp&d)==0)?(LCD_RAM3&=~0x40):(LCD_RAM3 |=0x40) ;
-
- case 4:
- tmp = LCD_Tab[number%10000/1000];
- ((tmp&m)==0)?(LCD_RAM0&=~0x20):(LCD_RAM0 |=0x20) ;
- ((tmp&e)==0)?(LCD_RAM0&=~0x10):(LCD_RAM0 |=0x10) ;
- ((tmp&g)==0)?(LCD_RAM2&=~0x08):(LCD_RAM2 |=0x08) ;
- ((tmp&b)==0)?(LCD_RAM2&=~0x04):(LCD_RAM2 |=0x04) ;
- ((tmp&f)==0)?(LCD_RAM5&=~0x80):(LCD_RAM5 |=0x80) ;
- ((tmp&a)==0)?(LCD_RAM5&=~0x40):(LCD_RAM5 |=0x40) ;
- ((tmp&c)==0)?(LCD_RAM4&=~0x02):(LCD_RAM4 |=0x02) ;
- ((tmp&d)==0)?(LCD_RAM4&=~0x01):(LCD_RAM4 |=0x01) ;
- case 3:
- tmp = LCD_Tab[number%1000/100];
- ((tmp&m)==0)?(LCD_RAM0&=~0x80):(LCD_RAM0 |=0x80) ;
- ((tmp&e)==0)?(LCD_RAM0&=~0x40):(LCD_RAM0 |=0x40) ;
- ((tmp&g)==0)?(LCD_RAM2&=~0x02):(LCD_RAM2 |=0x02) ;
- ((tmp&b)==0)?(LCD_RAM2&=~0x01):(LCD_RAM2 |=0x01) ;
- ((tmp&f)==0)?(LCD_RAM5&=~0x20):(LCD_RAM5 |=0x20) ;
- ((tmp&a)==0)?(LCD_RAM5&=~0x10):(LCD_RAM5 |=0x10) ;
- ((tmp&c)==0)?(LCD_RAM4&=~0x08):(LCD_RAM4 |=0x08) ;
- ((tmp&d)==0)?(LCD_RAM4&=~0x04):(LCD_RAM4 |=0x04) ;
- case 2:
- tmp = LCD_Tab[number%100/10];
- ((tmp&m)==0)?(LCD_RAM1&=~0x02):(LCD_RAM1 |=0x02) ;
- ((tmp&e)==0)?(LCD_RAM1&=~0x01):(LCD_RAM1 |=0x01) ;
- ((tmp&g)==0)?(LCD_RAM1&=~0x80):(LCD_RAM1 |=0x80) ;
- ((tmp&b)==0)?(LCD_RAM1&=~0x40):(LCD_RAM1 |=0x40) ;
- ((tmp&f)==0)?(LCD_RAM5&=~0x08):(LCD_RAM5 |=0x08) ;
- ((tmp&a)==0)?(LCD_RAM5&=~0x04):(LCD_RAM5 |=0x04) ;
- ((tmp&c)==0)?(LCD_RAM4&=~0x20):(LCD_RAM4 |=0x20) ;
- ((tmp&d)==0)?(LCD_RAM4&=~0x10):(LCD_RAM4 |=0x10) ;
- case 1:
- tmp = LCD_Tab[number%10];
- ((tmp&m)==0)?(LCD_RAM1&=~0x08):(LCD_RAM1 |=0x08) ;
- ((tmp&e)==0)?(LCD_RAM1&=~0x04):(LCD_RAM1 |=0x04) ;
- ((tmp&g)==0)?(LCD_RAM1&=~0x20):(LCD_RAM1 |=0x20) ;
- ((tmp&b)==0)?(LCD_RAM1&=~0x10):(LCD_RAM1 |=0x10) ;
- ((tmp&f)==0)?(LCD_RAM5&=~0x02):(LCD_RAM5 |=0x02) ;
- ((tmp&a)==0)?(LCD_RAM5&=~0x01):(LCD_RAM5 |=0x01) ;
- ((tmp&c)==0)?(LCD_RAM4&=~0x80):(LCD_RAM4 |=0x80) ;
- ((tmp&d)==0)?(LCD_RAM4&=~0x40):(LCD_RAM4 |=0x40) ;
- break;
- default:break;
- }
- }
- /******************************************************************************************************
- * 名 稱:LCD_DisplayTime(uint8_t number)
- * 功 能:調(diào)用LCD數(shù)字顯示程序,顯示RTC的時(shí)分秒
- * 入口參數(shù):無
- * 出口參數(shù):1:運(yùn)行出錯(cuò),退出 0:函數(shù)執(zhí)行完成
- * 說 明:此函數(shù),首先將RTC的時(shí)分秒寄存器按照順序,組合成一個(gè)數(shù),然后調(diào)用上面的函數(shù)進(jìn)行顯示
- * 范 例:無
- ******************************************************************************************************/
- uint8_t LCD_DisplayTime(void)
- {
- uint32_t tmp=0;
- if(RTC_ISR1_RSF==0)return 1;//判斷RTC時(shí)分秒寄存器的值有沒有被拷貝到TR影子寄存器,沒有就退出
-
- tmp += (uint32_t)RTC_TR1_SU;
- tmp += (uint32_t)RTC_TR1_ST*10;
- tmp += (uint32_t)RTC_TR2_MNU*100;
- tmp += (uint32_t)RTC_TR2_MNT*1000;
- tmp += (uint32_t)RTC_TR3_HU*10000;
- tmp += (uint32_t)RTC_TR3_HT*100000;
- RTC_ISR1_RSF=0; //清零
- LCD_DisplayNum(tmp);
- return 0;
- }
- /******************************************************************************************************
- * 名 稱:void RTC_Config(void)
- * 功 能:重新設(shè)置RTC的時(shí)分秒年月日,同時(shí)鬧鈴設(shè)置為一秒進(jìn)入一次中斷
- * 入口參數(shù):無
- * 出口參數(shù):無
- * 說 明:
- * 范 例:無
- ******************************************************************************************************/
- void RTC_Config(void)
- {
- //------打開LCD/RTC時(shí)鐘------
- CLK_PCKENR2_PCKEN22=1;
-
- //---選擇LSE作為RTC時(shí)鐘---
- CLK_CRTCR_RTCSEL0=0;
- CLK_CRTCR_RTCSEL1=0;
- CLK_CRTCR_RTCSEL2=0;
- CLK_CRTCR_RTCSEL3=1;
- /* 0000: No clock selected
- 0001: HSI clock used as RTC clock source
- 0010: LSI clock used as RTC clock source
- 0100: HSE clock used as RTC clock source
- 1000: LSE clock used as RTC clock sourc*/
-
- //----設(shè)置RTC時(shí)鐘分頻值----
- CLK_CRTCR_RTCDIV0=0;
- CLK_CRTCR_RTCDIV1=0;
- CLK_CRTCR_RTCDIV2=0;
- /*000: RTC clock source/1
- 001: RTC clock source /2
- 010: RTC clock source /4
- 011: RTC clock source /8
- 100: RTC clock source /16
- 101: RTC clock source /32
- 110: RTC clock source /64
- 111: RTC clock source /128*/
-
- //關(guān)閉RTC寄存器的寫保護(hù)功能
- RTC_WPR=0xCA;
- RTC_WPR=0x53;
-
- RTC_ISR1_INIT=1;//進(jìn)入初始化模式,計(jì)數(shù)器停止工作
-
- while(!RTC_ISR1_INITF);//等待同步完成
-
- //初始化時(shí)間和日期
- RTC_TR1_ST=0;//second tens
- RTC_TR1_SU=0;//second units
-
- RTC_TR2_MNT=4;//minute tens
- RTC_TR2_MNU=7;//minute units
-
- RTC_TR3_PM=0;//AM/PM
- RTC_TR3_HT=2;//hour tens
- RTC_TR3_HU=1;//hour units
-
- RTC_DR1_DT=2;//date tens
- RTC_DR1_DU=6;//date units
-
- RTC_DR2_WDU=1;//week day units
- RTC_DR2_MT=0;//month tens
- RTC_DR2_MU=9;//month units
- RTC_DR3_YT=1;//year tens
- RTC_DR3_YU=6;//year units
-
- RTC_CR1_FMT=0;//24小時(shí)模式
-
- // RTC_APRER=0x7F;//保持默認(rèn)值0x7F
- // RTC_SPRERL=0x00;
- // RTC_SPRERL=0xFF;//保持默認(rèn)值0xFF
- //以上兩個(gè)RTC時(shí)鐘分頻值保持默認(rèn)
- //最終提供給日歷模塊的時(shí)鐘為 32768Hz/( (127+1)*(255+1) ) =1Hz
-
- RTC_ISR1_INIT=0;//退出初始化模式
-
- //設(shè)置鬧鐘
- RTC_CR2_ALRAE=0;//disable the alarm
- while(!RTC_ISR1_ALRAWF);//
- //設(shè)置鬧鐘時(shí)間
- //屏蔽了所有時(shí)間,導(dǎo)致鬧鐘1秒中執(zhí)行一次,進(jìn)入一次鬧鐘中斷
- RTC_ALRMAR1_MSK1=1;//屏蔽秒定時(shí)
- RTC_ALRMAR1_ALST=2;//second tens
- RTC_ALRMAR1_ALSU=0;//second units
-
- RTC_ALRMAR2_MSK2=1;//屏蔽分鐘定時(shí)
- RTC_ALRMAR2_ALMNT=0;//minute tens
- RTC_ALRMAR2_ALMNU=0;//minute units
-
- RTC_ALRMAR3_MSK3=1;//屏蔽小時(shí)定時(shí)
- RTC_ALRMAR3_ALHT=0;//hour tens
- RTC_ALRMAR3_ALHU=6;//hour units
- RTC_ALRMAR3_PM=0;
-
- RTC_ALRMAR4_MSK4=1;//屏蔽關(guān)閉天定時(shí)
- RTC_ALRMAR4_ALDT=0;
- RTC_ALRMAR4_ALDU=0;
- RTC_ALRMAR4_WDSEL=0;
- RTC_CR2_ALRAE=1;//使能鬧鈴功能
- RTC_CR2_ALRAIE=1;//使能鬧鈴中斷
-
- //programming the auto-wakeup timer
- // RTC_CR2_WUTE=0;//disable the wakeup timer
- // while(!RTC_ISR1_WUTWF);
- // RTC_WUTRH=0;//裝初值
- // RTC_WUTRL=8;//
-
-
- //配置自動(dòng)喚醒分頻值
- //RTC_CR1_WUCKSEL=0x03;//32768Hz/2=16384Hz
- /* 000: RTCCLK/16 clock is selected
- 001: RTCCLK/8 clock is selected
- 010: RTCCLK/4 clock is selected
- 011: RTCCLK/2 clock is selected */
- // RTC_CR2_WUTE=0;//enable the tiemr again
- // RTC_CR2_WUTIE=0;
-
- RTC_WPR=0x55;//使能寫保護(hù)
- RTC_WPR=0x55;
- }
- void main(void)
- {
- GPIO_Init();
- LCD_Config();
- RTC_Config();
- asm("rim"); //enable interrupts
- while(1)
- {
- if(bRTC_Update_Flag)
- {
- bRTC_Update_Flag=false;
- LCD_DisplayTime();
- }
- asm("halt");//執(zhí)行此條語句后,STM8L152C6進(jìn)入低功耗模式,主時(shí)鐘關(guān)閉,但RTC仍然在運(yùn)行
- }
- }
- #pragma vector=RTC_ALARM_vector
- __interrupt void RTC_ALARM_ISR(void)
- {
- PC_ODR_ODR7 ^=0x01;
- bRTC_Update_Flag=true;
- RTC_ISR2_ALRAF=0;
- }
復(fù)制代碼
|
|