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功能相关库函数。