36.USART控制程序
用串口實現遠程控制
通過串口助手點亮LED;
USART串口控制程序
#include "stm32f10x.h" //STM32頭文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "usart.h"
int main (void){//主程序
u8 a;
//初始化程序
RCC_Configuration(); //時鐘設置
LED_Init();//LED初始化
KEY_Init();//按鍵初始化
BUZZER_Init();//蜂鳴器初始化
USART1_Init(115200); //串口初始化(參數是波特率)
//主循環
while(1){
//查詢方式接收 (有電腦端控制指示燈亮滅)
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) != RESET){
//查詢串口待處理標誌位,檢查串口是否收到數據
a =USART_ReceiveData(USART1);//讀取接收到的數據
switch (a){
//a='0' 表示ASCAL碼錶中的,而不是十六進制的數字0
case '0':
GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0));
//LED控制,改變IO端口的狀態,將LED變爲低電平,關閉指示燈。
printf("%c:LED1 OFF ",a);
//表示燈關閉
break;
case '1':
GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));
//LED控制,LED1輸出高電平,點亮
printf("%c:LED1 ON ",a); //
break;
case '2':
BUZZER_BEEP1(); //蜂鳴一聲
printf("%c:BUZZER ",a); //把收到的數據發送回電腦
break;
default:
break;
}
}
//按鍵控制
if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //讀按鍵接口的電平
delay_ms(20); //延時20ms去抖動
if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){ //讀按鍵接口的電平
while(!GPIO_ReadInputDataBit(KEYPORT,KEY1)); //等待按鍵鬆開
printf("KEY1 "); //
}
}
if(!GPIO_ReadInputDataBit(KEYPORT,KEY2)){ //讀按鍵接口的電平
delay_ms(20); //延時20ms去抖動
if(!GPIO_ReadInputDataBit(KEYPORT,KEY2)){ //讀按鍵接口的電平
while(!GPIO_ReadInputDataBit(KEYPORT,KEY2)); //等待按鍵鬆開
printf("KEY2 "); //
}
}
// delay_ms(1000); //延時
}
}
超級終端 HyperTerminal
37.超級終端串口控制程序
超級終端串口控制程序
#include "stm32f10x.h" //STM32頭文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "usart.h"
int main (void){//主程序
RCC_Configuration();
LED_Init();//LED初始化
KEY_Init();//按鍵初始化
BUZZER_Init();//蜂鳴器初始化
USART1_Init(115200); //串口初始化,參數中寫波特率
USART1_RX_STA=0xC000; //初始值設爲有回車的狀態,即顯示一次歡迎詞
while(1){
if(USART1_RX_STA&0xC000){ //如果標誌位是0xC000表示收到數據串完成,可以處理。
if((USART1_RX_STA&0x3FFF)==0){
//單獨的回車鍵再顯示一次歡迎詞
printf("\033[1;47;33m\r\n");
//設置顏色(參考超級終端使用)
printf(" 1y--開LED1燈 1n--關LED1燈 \r\n");
printf(" 2y--開LED2燈 2n--關LED2燈 \r\n");
printf(" 請輸入控制指令,按回車鍵執行! \033[0m\r\n");
}else if((USART1_RX_STA&0x3FFF)==2 && USART1_RX_BUF[0]=='1' && USART1_RX_BUF[1]=='y'){ //判斷數據是不是2個,第一個數據是不是“1”,第二個是不是“y”
GPIO_SetBits(LEDPORT,LED1); //LED燈都爲高電平(1)
printf("1y -- LED1燈已經點亮!\r\n");
}else if((USART1_RX_STA&0x3FFF)==2 && USART1_RX_BUF[0]=='1' && USART1_RX_BUF[1]=='n'){
GPIO_ResetBits(LEDPORT,LED1); ////LED燈都爲低電平(0)
printf("1n -- LED1燈已經熄滅!\r\n");
}else if((USART1_RX_STA&0x3FFF)==2 && USART1_RX_BUF[0]=='2' && USART1_RX_BUF[1]=='y'){
GPIO_SetBits(LEDPORT,LED2); //LED燈都爲高電平(1)
printf("2y -- LED2燈已經點亮!\r\n");
}else if((USART1_RX_STA&0x3FFF)==2 && USART1_RX_BUF[0]=='2' && USART1_RX_BUF[1]=='n'){
GPIO_ResetBits(LEDPORT,LED2); ////LED燈都爲低電平(0)
printf("2n -- LED2燈已經熄滅!\r\n");
}else{ //如果以上都不是,即是錯誤的指令。
printf("指令錯誤!\r\n");
}
USART1_RX_STA=0; //將串口數據標誌位清0
}
}
}
38.RTC原理與驅動程序
RTC實時時鐘,系統時間計時,作爲鬧鐘喚醒低功耗模式。
STM32的RTC只用一個32位計數器來計時,而不是年月日時分秒的分組寄存器。
通過設置可以讓計數器1秒加1,從0-0XFFFFFFFF大約計時136年
時間起點一般設置爲 1970-01-01 00:00:00(因現有函數如此定義)
如果要讀當前的年月日時分秒,先獨處32位TRC計數器值,
然後以1970-01-01 00:00:00爲起點,加上計數器中的秒數,再換算成年月日時分秒,即可得出當 前時間。
LED燈顯示RTC走時程序
LED分別以一定頻率閃爍LED2以分鐘爲間隔,LED1以秒爲間隔,奇偶分。
增加了rtc.h和rtc.c文件
main.c程序
#include "stm32f10x.h" //STM32頭文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "usart.h"
#include "rtc.h"
int main (void){//主程序
RCC_Configuration(); //系統時鐘初始化
RTC_Config(); //實時時鐘初始化
LED_Init();//LED初始化
KEY_Init();//按鍵初始化
BUZZER_Init();//蜂鳴器初始化
USART1_Init(115200); //串口初始化,參數中寫波特率
USART1_RX_STA=0xC000; //初始值設爲有回車的狀態,即顯示一次歡迎詞
while(1){
if(RTC_Get()==0){ //讀出時間值,同時判斷返回值是不是0,非0時讀取的值是錯誤的。
GPIO_WriteBit(LEDPORT,LED1,(BitAction)(rsec%2)); //LED1接口
GPIO_WriteBit(LEDPORT,LED2,(BitAction)(rmin%2)); //LED2接口
}
}
}
39.RTC驅動程序分析
rtc.h程序
#ifndef __RTC_H
#define __RTC_H
#include "sys.h"
//全局變量的聲明,在rtc.c文件中定義
//以下2條是使用extern語句聲明全局變量
//注意:這裏不能給變量賦值
extern u16 ryear;
extern u8 rmon,rday,rhour,rmin,rsec,rweek;
u8 RTC_Get(void);//讀出當前時間值
void RTC_First_Config(void);//首次啓用RTC的設置,對RTC完全初始化
void RTC_Config(void);//實時時鐘初始化,如果備用電池沒有斷開就不需要全部初始化
//兩個初始化函數,因爲不僅有主電源供電還有後備電源,
u8 Is_Leap_Year(u16 year);//判斷是否是閏年函數,
u8 RTC_Get(void); //讀取當前時間值
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//寫入當前時間
u8 RTC_Get_Week(u16 year,u8 month,u8 day);//按年月日計算星期
#endif
rtc.c程序
/*
//時間讀寫與設置說明//
1,在mani函數開頭放入RTC_Config();就可以使能時鐘了。
在RTC_Config();函數中自帶判斷是不是首次使用RTC
2,使用 RTC_Get();讀出時間。讀出的數據存放在:
年 ryear (16位)
月 rmon (以下都是8位)
日 rday
時 rhour
分 rmin
秒 rsec
周 rweek
3,使用 RTC_Set(4位年,2位月,2位日,2位時,2位分,2位秒); 寫入時間。例如:RTC_Get(2017,08,06,21,34,00);
其他函數都是幫助如上3個函數的,不需要調用。
注意要使用RTC_Get和RTC_Set的返回值,爲0時表示讀寫正確。
*/
#include "sys.h"
#include "rtc.h"
//以下2條全局變量--用於RTC時間的讀取
u16 ryear; //4位年
u8 rmon,rday,rhour,rmin,rsec,rweek;//2位月日時分秒周
void RTC_First_Config(void){
//首次啓用RTC的設置(假設RTC第一次使用,或者備用電池斷開,數據重新設置)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
//啓用PWR和BKP的時鐘(from APB1)
PWR_BackupAccessCmd(ENABLE);//後備域解鎖 (p189頁,使能或者失能RTC和後備寄存器訪問)
BKP_DeInit();//備份寄存器模塊復位(p64,將全部寄存器設爲缺省值)
RCC_LSEConfig(RCC_LSE_ON);//外部32.768KHZ晶振開啓
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//等待穩定
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//RTC時鐘源配置成LSE(外部低速晶振32.768KHZ)
RCC_RTCCLKCmd(ENABLE);//RTC開啓
RTC_WaitForSynchro();//開啓後需要等待APB1時鐘與RTC時鐘同步,才能讀寫寄存器
RTC_WaitForLastTask();//讀寫寄存器前,要確定上一個操作已經結束
RTC_SetPrescaler(32767);//設置RTC分頻器,使RTC時鐘爲1Hz,RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1)
RTC_WaitForLastTask();//等待寄存器寫入完成
//當不使用RTC秒中斷,可以屏蔽下面2條
// RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中斷
// RTC_WaitForLastTask();//等待寫入完成
}
void RTC_Config(void){ //實時時鐘初始化
//在BKP的後備寄存器1中,存了一個特殊字符0xA5A5
//第一次上電或後備電源掉電後,該寄存器數據丟失,表明RTC數據丟失,需要重新配置
if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){//判斷寄存數據是否丟失
RTC_First_Config();//重新配置RTC
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成後,向後備寄存器中寫特殊字符0xA5A5
}else{
//若後備寄存器沒有掉電,則無需重新配置RTC
//這裏我們可以利用RCC_GetFlagStatus()函數查看本次復位類型
if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET){
//這是上電覆位
}
else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET){
//這是外部RST管腳復位
}
RCC_ClearFlag();//清除RCC中復位標誌
//雖然RTC模塊不需要重新配置,且掉電後依靠後備電池依然運行
//但是每次上電後,還是要使能RTCCLK
RCC_RTCCLKCmd(ENABLE);//使能RTCCLK
RTC_WaitForSynchro();//等待RTC時鐘與APB1時鐘同步
//當不使用RTC秒中斷,可以屏蔽下面2條
// RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中斷
// RTC_WaitForLastTask();//等待操作完成
}
#ifdef RTCClockOutput_Enable //判斷是否使用RTC輸出功能,一般不使用。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
BKP_TamperPinCmd(DISABLE);
BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock);
#endif
}
void RTC_IRQHandler(void){ //RTC時鐘1秒觸發中斷函數(名稱固定不可修改)
if (RTC_GetITStatus(RTC_IT_SEC) != RESET){
}
RTC_ClearITPendingBit(RTC_IT_SEC);
RTC_WaitForLastTask();
}
void RTCAlarm_IRQHandler(void){ //鬧鐘中斷處理(啓用時必須調高其優先級)
if(RTC_GetITStatus(RTC_IT_ALR) != RESET){
}
RTC_ClearITPendingBit(RTC_IT_ALR);
RTC_WaitForLastTask();
}
//判斷是否是閏年函數
//月份 1 2 3 4 5 6 7 8 9 10 11 12
//閏年 31 29 31 30 31 30 31 31 30 31 30 31
//非閏年 31 28 31 30 31 30 31 31 30 31 30 31
//輸入:年份
//輸出:該年份是不是閏年.1,是.0,不是
u8 Is_Leap_Year(u16 year){
if(year%4==0){ //必須能被4整除
if(year%100==0){
if(year%400==0)return 1;//如果以00結尾,還要能被400整除
else return 0;
}else return 1;
}else return 0;
}
//設置時鐘
//把輸入的時鐘轉換爲秒鐘
//以1970年1月1日爲基準
//1970~2099年爲合法年份
//月份數據表
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正數據表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年的月份日期表
//寫入時間
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec){
//寫入當前時間(1970~2099年有效),
u16 t;
u32 seccount=0;
if(syear<2000||syear>2099)return 1;//syear範圍1970-2099,此處設置範圍爲2000-2099
for(t=1970;t<syear;t++){ //把所有年份的秒鐘相加
if(Is_Leap_Year(t))seccount+=31622400;//閏年的秒鐘數
else seccount+=31536000; //平年的秒鐘數
}
smon-=1;
for(t=0;t<smon;t++){ //把前面月份的秒鐘數相加
seccount+=(u32)mon_table[t]*86400;//月份秒鐘數相加
if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//閏年2月份增加一天的秒鐘數
}
seccount+=(u32)(sday-1)*86400;//把前面日期的秒鐘數相加
seccount+=(u32)hour*3600;//小時秒鐘數
seccount+=(u32)min*60; //分鐘秒鐘數
seccount+=sec;//最後的秒鐘加上去
RTC_First_Config(); //重新初始化時鐘
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成後,向後備寄存器中寫特殊字符0xA5A5
RTC_SetCounter(seccount);//把換算好的計數器值寫入
RTC_WaitForLastTask(); //等待寫入完成
return 0; //返回值:0,成功;其他:錯誤代碼.
}
//讀出時間
u8 RTC_Get(void){//讀出當前時間值 //返回值:0,成功;其他:錯誤代碼.
static u16 daycnt=0;
u32 timecount=0;
u32 temp=0;
u16 temp1=0;
timecount=RTC_GetCounter();
temp=timecount/86400; //得到天數(秒鐘數對應的)
if(daycnt!=temp){//超過一天了
daycnt=temp;
temp1=1970; //從1970年開始
while(temp>=365){
if(Is_Leap_Year(temp1)){//是閏年
if(temp>=366)temp-=366;//閏年的秒鐘數
else {temp1++;break;}
}
else temp-=365; //平年
temp1++;
}
ryear=temp1;//得到年份
temp1=0;
while(temp>=28){//超過了一個月
if(Is_Leap_Year(ryear)&&temp1==1){//當年是不是閏年/2月份
if(temp>=29)temp-=29;//閏年的秒鐘數
else break;
}else{
if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
else break;
}
temp1++;
}
rmon=temp1+1;//得到月份
rday=temp+1; //得到日期
}
temp=timecount%86400; //得到秒鐘數
rhour=temp/3600; //小時
rmin=(temp%3600)/60; //分鐘
rsec=(temp%3600)%60; //秒鐘
rweek=RTC_Get_Week(ryear,rmon,rday);//獲取星期值
return 0;
}
u8 RTC_Get_Week(u16 year,u8 month,u8 day){ //按年月日計算星期(只允許1901-2099年)//已由RTC_Get調用
u16 temp2;
u8 yearH,yearL;
yearH=year/100;
yearL=year%100;
// 如果爲21世紀,年份數加100
if (yearH>19)yearL+=100;
// 所過閏年數只算1900年之後的
temp2=yearL+yearL/4;
temp2=temp2%7;
temp2=temp2+day+table_week[month-1];
if (yearL%4==0&&month<3)temp2--;
return(temp2%7); //返回星期值
}
重點觀看固件庫RTC庫函數63-69 備用寄存器相關函數和215-222頁RTC功能相關庫函數。