目的:利用PROTUES仿真软件、串口调试助手、虚拟串口,搭建单片机与PC通信仿真平台,熟悉单片机串口的配置及与PC机的通信方法;尝试制定通信协议(含开始码、指令、数据、停止码),单片机根据通信协议解析接收到的内容,并根据接收的指令执行相应的操作。
1、proteus仿真实验电路:
2、基本功能
程序功能:1.时分秒的动态显示。2.用三个按键实现时分秒的修改,调节的数字闪烁提示。
3.串口控制时钟的暂停、开始、清零、读取、设置时间
串口协议格式:s+指令+数据+e
s:表示开始码
指令:
p:计时暂停(无数据位)
r:计时重启(无数据位)
c:显示清零(无数据位)
q:查询当前时间(无数据位)
t:设置时间(数据位格式为6位数字)
3、效果
文章来源:https://www.toymoban.com/news/detail-755843.html
文章来源地址https://www.toymoban.com/news/detail-755843.html
4、源代码
/*******************************************************************************
程序功能:1.时分秒的动态显示。2.用三个按键实现时分秒的修改,调节的数字闪烁提示。
3.串口控制时钟的暂停、开始、清零、读取、设置时间
串口协议格式:s+指令+数据+e
s:表示开始码
指令:
| p:计时暂停(无数据位) |
| r:计时重启(无数据位) |
| c:显示清零(无数据位) |
| q:查询当前时间(无数据位)
| t:设置时间(数据位格式为6位数字)
*******************************************************************************/
#include <reg52.h> //包含需要的头文件
#define u8 unsigned char
#define u16 unsigned int
u8 WeiMa[6]={0xFE,0xFD,0xFB,0xF7,0xEF,0xDF};
u8 DuanMa[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
//函数声明
void Delay_ms(u16 xms);
void ShuMaGuan(u8 wei,u8 duan);
void Display_Timer(u8 hour,u8 min,u8 sec);
u8 Key_Scan();
void PutChar(u8 n);
void UartInit();
void PutString(u8 *p);
void Key_Timer_Set();
void Uart_Timer_Set();
void Pintf_Uart();
//引脚定义
sbit SW1=P3^2;
sbit SW2=P3^3;
sbit SW3=P3^4;
u8 Hour=0,Min=0,Sec=0;//全局变量,时分秒
u8 mode=0;//全局变量:状态切换,0:时钟显示,1:调节时;2:调节分;3:调节秒
bit flash_tip=1;//数码管闪烁标志,为0时数码管熄灭,为一时数码管显示
#define Data_SIZE 15 //数据长度
u8 USART_RX_BUF[Data_SIZE]; //接收缓冲,最大Data_SIZE个字节.末字节为换行符
u8 Start_Receiving=0; //开始接收标志位
u8 Receive_OK=0; //接收完毕接收位 1为接收完毕
//函数功能:定时器初始化
void Time0init()
{
TMOD|=0x01; //设置定时器模式
TF0=0; //清除TF0标志
TH0=(65536-50000)/256; //设置定时初值
TL0=(65536-50000)%256; //设置定时初值
TR0=1; //定时器0允许计时
ET0=1; //中断允许
EA=1; //CPU中断允许位打开
}
//串口初始化
void UartInit() //9600bps@11.0592MHz
{
PCON &= 0x8F; //波特率倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
TR1 = 1; //启动定时器1
EA=1;
ES=1; //打开接收中断
}
/*******************************************************************************
* 函 数 名: void main()
* 函数功能: 主函数
*******************************************************************************/
void main()
{
Time0init();//定时器
UartInit(); //串口
Pintf_Uart();//输入提示
while(1)
{
Key_Timer_Set();//按键控制时钟
Uart_Timer_Set();//按键调节时钟
Display_Timer(Hour,Min,Sec);//数码管显示
}
}
/*******************************************************************************
* 函 数 名: void Pintf_Uart()
* 函数功能: 串口助手输入指示
*******************************************************************************/
void Pintf_Uart()
{
/***************输入指示*******************/
PutString("Please input:s+command(data)+e\r\n");
PutString("Stop_Time:spe\r\n");
PutString("Start_Time:sre\r\n");
PutString("Zero_Time:sce\r\n");
PutString("Read_Time:sqe\r\n");
PutString("Set_Time:st******e\r\n");
/*******************************************/
}
/*******************************************************************************
* 函 数 名: void Key_Timer_Set()
* 函数功能: //按键调节时钟
*******************************************************************************/
void Key_Timer_Set()
{
u8 keynum;
keynum=Key_Scan();//按键返回值
if(keynum) //非0表示有按键按下
{
switch(keynum) //判断是哪个按键按下,按键一调节模式,按键2自加,按键3自减
{
case 1:if(++mode>=4) mode=0;break; //++mode为先自增再判断是否大于4
case 2:
if(mode==1) if(++Hour>=24) Hour=0;//++mode为先自增再判断是否大于4
if(mode==2) if(++Min>=60) Min=0;//++Min先自增再判断是否大于60
if(mode==3) if(++Sec>=60) Sec=0;//++Sec先自增再判断是否大于60
break;
case 3:
if(mode==1) if(--Hour==255) Hour=23;//--Hour先自增再判断是否溢出
if(mode==2) if(--Min==255) Min=59;//--Min先自增再判断是否大溢出
if(mode==3) if(--Sec==255) Sec=59;//--Sec先自增再判断是否大溢出
break;
default:break;
}
}
}
/*******************************************************************************
* 函 数 名: void Key_Timer_Set()
* 函数功能: 串口调节时钟
串口协议格式:s+指令+数据+e
s:表示开始码
指令:
| p:计时暂停(无数据位) |
| r:计时重启(无数据位) |
| c:显示清零(无数据位) |
| q:查询当前时间(无数据位)
| t:设置时间(数据位格式为6位数字)
*******************************************************************************/
void Uart_Timer_Set()
{
static u8 Hour_1,Min_1,Sec_1;//时间暂存
if(Receive_OK) //如果串口接收到数据
{
Receive_OK=0;
switch(USART_RX_BUF[0])//存入数组的数据没有开始码,所以第一个数据是指令
{
case 'p': //时钟暂停
if(USART_RX_BUF[1]!='e')//说明第2位数组的数据不是停止码
PutString("Error!\r\n");
else
{
TR0=0;
PutString("Stop OK!\r\n");
}
break;
case 'r': //开始计时
if(USART_RX_BUF[1]!='e')//说明第2位数组的数据不是停止码
PutString("Error!\r\n");
else
{
TR0=1;
PutString("Start OK!\r\n");
}
break;
case 'c': //清零
if(USART_RX_BUF[1]!='e')//说明第2位数组的数据不是停止码
PutString("Error!\r\n");
else
{
Hour=0;
Min=0;
Sec=0;
PutString("Zero OK!\r\n");
}
break;
case 'q': //查询当前时间
if(USART_RX_BUF[1]!='e')//说明第2位数组的数据不是停止码
PutString("Error!\r\n");
else
{
PutString("The time is:");
PutChar(Hour/10+48); //转化ASCII码字符,0为48,1为48+1=49.....
PutChar(Hour%10+48);
PutChar(':');
PutChar(Min/10+48);
PutChar(Min%10+48);
PutChar(':');
PutChar(Sec/10+48);
PutChar(Sec%10+48);
PutString("\r\n");
}
break;
case 't': //设置时间
if(USART_RX_BUF[7]!='e')//如果接收到的数据不是6位数字,说明第7位数组的数据不是停止码
PutString("Error!\r\n");
else
{
Hour_1=(USART_RX_BUF[1]-48)*10+(USART_RX_BUF[2]-48); //转化成十进制
Min_1=(USART_RX_BUF[3]-48)*10+(USART_RX_BUF[4]-48); //转化成十进制
Sec_1=(USART_RX_BUF[5]-48)*10+(USART_RX_BUF[6]-48); //转化成十进制
if(Hour_1>23 || Min_1>59 || Sec_1>59) //超出时间阈值
PutString("Error!\r\n");
else
{
Hour=Hour_1;
Min=Min_1;
Sec=Sec_1;
PutString("Set OK!\r\n");
}
}
break;
default:PutString("Error!\r\n");break;
}
ES=1; //开启接收
}
}
/*******************************************************************************
* 函 数 名: void Delay_ms(u16 xms)
* 函数功能: 软件延时函数,xms为延时多少毫秒
*******************************************************************************/
void Delay_ms(u16 xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 135;
do
{
while (--j);
} while (--i);
}
}
/*******************************************************************************
* 函 数 名: void ShuMaGuan(u8 wei,u8 duan)
* 函数功能: 静态显示一位,参数:wei控制位选duan控制段选,表示要显示的一个数字
*******************************************************************************/
void ShuMaGuan(u8 wei,u8 duan)
{
P1=WeiMa[wei]; //位选
P2=DuanMa[duan]; //段选
Delay_ms(1); //间隔一段时间扫描
P1=0xFF;
P2=0xFF; //消隐
}
/*******************************************************************************
* 函 数 名: void Display_Timer(u8 hour,u8 min,u8 sec)
* 函数功能:数码管动态显示
flash_tip为数码管闪烁标志,为0时数码管熄灭,为一时数码管显示
flash_tip每4.5秒进行取反
*******************************************************************************/
void Display_Timer(u8 hour,u8 min,u8 sec)
{
if(mode!=1 || flash_tip==1) //mode=1时,左边的条件一直为假,当flash_tip=1时,或运算为真,进入if,数码管显示
{
ShuMaGuan(5,hour/10);
ShuMaGuan(4,hour%10);
}
else P1=0xFF;
if(mode!=2 || flash_tip==1)//mode=2时,左边的条件一直为假,当flash_tip=1时,或运算为真,进入if,数码管显示
{
ShuMaGuan(3,min/10);
ShuMaGuan(2,min%10);
}
else P1=0xFF;
if(mode!=3 || flash_tip==1)//mode=3时,左边的条件一直为假,当flash_tip=1时,或运算为真,进入if,数码管显示
{
ShuMaGuan(1,sec/10);
ShuMaGuan(0,sec%10);
}
else P1=0xFF;
}
/*******************************************************************************
* 函 数 名: u8 Key_Scan()
* 函数功能: 独立按键检测,按键按下分别返回1.2.3
*******************************************************************************/
u8 Key_Scan()
{
static u8 key_up=1; //按键按松开标志
if(key_up && (SW1==0 || SW2==0 || SW3==0))
{
Delay_ms(10); //去抖动
key_up=0; //松手标志为0,那么下次再检测,if结果为0,则不会进入这里的语句
if(SW1==0) return 1;
if(SW2==0) return 2;
if(SW3==0) return 3;
}
else if(SW1 == 1 && SW2 == 1 && SW3 == 1) key_up=1; //松手标志
return 0; // 无按键按下
}
/*******************************************************************************
* 函 数 名: void PutChar(u8 n)
* 函数功能: 发送一个字符
*******************************************************************************/
void PutChar(u8 n)
{
SBUF=n;
while(!TI);
TI=0;
}
/*******************************************************************************
* 函 数 名: void PutString(u8 *p)
* 函数功能: 发送字符串
*******************************************************************************/
void PutString(u8 *p)
{
while(*p!='\0')
{
PutChar(*p);
p++;
}
}
/*******************************************************************************
* 函 数 名: void uart() interrupt 4
* 函数功能: 串口中断服务函数,单片机接收数据并存入USART_RX_BUF[]数组中
开始码s不存入数组
*******************************************************************************/
void uart() interrupt 4
{
static u8 Data_count=0;
u8 Data;
if(RI==1)
{
RI=0;
Data=SBUF;
if(Data=='s')
{
Start_Receiving=1; //开始接收数据
}
else if(Start_Receiving)
{
USART_RX_BUF[Data_count++]=Data;//数据还没结束发送,就存到USART_RX_BUF[]数组中
if(Data=='e')
{
Start_Receiving=0;
Data_count=0;
Receive_OK=1;
ES=0;
}
}
}
}
/*******************************************************************************
* 函 数 名: void Time0() interrupt 1
* 函数功能: 定时器0中断服务函数,时钟效果
*******************************************************************************/
void Time0() interrupt 1
{
static unsigned char flag_1,flag_2;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;//重新赋初值
if(mode==0)flag_1++; //mode为0时,数码管正常显示
else flag_2++;
if(flag_1==20 && mode==0) //每秒执行一次
{
flag_1=0;
if(++Sec>=60) //++Sec先自增再判断是否大于60
{
Sec=0;
if(++Min>=60)//++Min先自增再判断是否大于60
{
Min=0;
if(++Hour>=24)//++Hour先自增再判断是否大于60
{
Hour=0;
}
}
}
}
if(flag_2==9)
{
flash_tip=~flash_tip;//每4.5秒进行取反
flag_2=0;
}
}
到了这里,关于【笔记】6位数码管显示定时器定时的时分秒,通过按键控制时间,自定义串口通信协议,根据单片机接收到的指令控制数码管显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!