? ? ???目的:利用PROTUES仿真軟件、串口調(diào)試助手、虛擬串口,搭建單片機與PC通信仿真平臺,熟悉單片機串口的配置及與PC機的通信方法;嘗試制定通信協(xié)議,單片機根據(jù)通信協(xié)議解析接收到的內(nèi)容,并根據(jù)接收的指令執(zhí)行相應的操作。
1、proteus仿真實驗電路:
?2、單字符的接收和發(fā)送,串口通信控制單片機
源碼:通過PC端發(fā)送單個字符控制單片機,實現(xiàn)根據(jù)發(fā)送的字符指令控制數(shù)碼管顯示時鐘“暫停(輸入P)和開始(輸入S)”,“清零(C)”,顯示當前數(shù)碼管顯示的計時(R)
#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};
//函數(shù)聲明
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);
//引腳定義
sbit SW1=P3^2;
sbit SW2=P3^3;
sbit SW3=P3^4;
//函數(shù)功能:定時器初始化
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位數(shù)據(jù),可變波特率
TMOD &= 0x0F; //清除定時器1模式位
TMOD |= 0x20; //設定定時器1為8位自動重裝方式
TL1 = 0xFD; //設定定時初值
TH1 = 0xFD; //設定定時器重裝值
TR1 = 1; //啟動定時器1
EA=1;
ES=1;
}
u8 Hour,Min,Sec;//全局變量,時分秒
u8 mode;//全局變量:狀態(tài)切換,0:時鐘顯示,1:調(diào)節(jié)時;2:調(diào)節(jié)分;3:調(diào)節(jié)秒
bit flash_tip=1;//數(shù)碼管閃爍標志,為0時數(shù)碼管熄滅,為一時數(shù)碼管顯示
u8 Data;//接收數(shù)據(jù)
//主函數(shù)
void main()
{
u8 keynum;
Time0init();//定時器
UartInit();
Hour=0;
Min=0;
Sec=0;
mode=0;//初始化
PutString("開始");
while(1)
{
keynum=Key_Scan();//按鍵返回值
if(keynum) //非0表示有按鍵按下
{
switch(keynum) //判斷是哪個按鍵按下,按鍵一調(diào)節(jié)模式,按鍵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;
}
}
Display_Timer(Hour,Min,Sec);
}
}
//軟件延時函數(shù)
//xms為延時多少毫秒
void Delay_ms(u16 xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 135;
do
{
while (--j);
} while (--i);
}
}
//靜態(tài)顯示一位
//參數(shù):wei控制位選duan控制段選,表示要顯示的一個數(shù)字
void ShuMaGuan(u8 wei,u8 duan)
{
P1=WeiMa[wei]; //位選
P2=DuanMa[duan]; //段選
Delay_ms(1); //間隔一段時間掃描
P1=0xFF;
P2=0xFF; //消隱
}
//函數(shù)功能:數(shù)碼管動態(tài)顯示
//flash_tip為數(shù)碼管閃爍標志,為0時數(shù)碼管熄滅,為一時數(shù)碼管顯示
//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,數(shù)碼管顯示
{
ShuMaGuan(5,hour/10);
ShuMaGuan(4,hour%10);
}
else P1=0xFF;
if(mode!=2 || flash_tip==1)//mode=2時,左邊的條件一直為假,當flash_tip=1時,或運算為真,進入if,數(shù)碼管顯示
{
ShuMaGuan(3,min/10);
ShuMaGuan(2,min%10);
}
else P1=0xFF;
if(mode!=3 || flash_tip==1)//mode=3時,左邊的條件一直為假,當flash_tip=1時,或運算為真,進入if,數(shù)碼管顯示
{
ShuMaGuan(1,sec/10);
ShuMaGuan(0,sec%10);
}
else P1=0xFF;
}
//獨立按鍵檢測
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結(jié)果為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; // 無按鍵按下
}
//發(fā)送一個字符
void PutChar(u8 n)
{
SBUF=n;
while(!TI);
TI=0;
}
//發(fā)送字符串
void PutString(u8 *p)
{
while(*p!='\0')
{
PutChar(*p);
p++;
}
}
//串口中斷
void uart() interrupt 4
{
if(RI==1)
{
RI=0;
Data=SBUF;
if(Data=='P') //向單片機發(fā)送p-->停止計時
{
TR0=0;
}
else if(Data=='S') //s-->開始計時
{
TR0=1;
}
else if(Data=='C') //c-->時鐘清零
{
Hour=0;
Min=0;
Sec=0;
}
else if(Data=='R') //查詢當時時間
{
PutString("當前時間為:");
PutChar(Hour/10+48); //字符
PutChar(Hour%10+48);
PutChar(':');
PutChar(Min/10+48);
PutChar(Min%10+48);
PutChar(':');
PutChar(Sec/10+48);
PutChar(Sec%10+48);
PutString('\n');
}
}
}
//定時器中斷服務函數(shù)
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時,數(shù)碼管正常顯示
else flag_2++;
if(flag_1==20 && mode==0) //每秒執(zhí)行一次
{
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;
}
}
3、字符串的接收和發(fā)送
基本功能:
1.時分秒的動態(tài)顯示。2.用三個按鍵實現(xiàn)時分秒的修改,調(diào)節(jié)的數(shù)字閃爍提示。
3.串口控制時鐘的暫停、開始、清零、讀取
輸入指令(回車結(jié)束):
暫停計時:stop
開始計時:start
時鐘清零:reset
讀取當前時間:read
? ? ? ?思路: 串口通信時,發(fā)送數(shù)據(jù)是一個一個字符發(fā)送和接收的,所以可以每次接收到一個字符就保存在字符數(shù)組保存,再進行下一步處理。每次存儲好一個字符,同時記錄存儲的長度,單片機內(nèi)存有限,所以讀取完成子后就從首地址重新寫入。字符串的結(jié)束標志為‘’,用這點判斷輸入的長度。文章來源:http://www.zghlxwxcb.cn/news/detail-512452.html
(1)效果圖:?
文章來源地址http://www.zghlxwxcb.cn/news/detail-512452.html
?(2)源碼
/*******************************************************************************
程序功能:1.時分秒的動態(tài)顯示。2.用三個按鍵實現(xiàn)時分秒的修改,調(diào)節(jié)的數(shù)字閃爍提示。
3.串口控制時鐘的暫停、開始、清零、讀取
輸入指令(回車結(jié)束):
暫停計時:stop
開始計時:start
時鐘清零:reset
讀取當前時間:read
*******************************************************************************/
#include <reg52.h> //包含需要的頭文件
#include <string.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};
//函數(shù)聲明
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 crlf();
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;//全局變量:狀態(tài)切換,0:時鐘顯示,1:調(diào)節(jié)時;2:調(diào)節(jié)分;3:調(diào)節(jié)秒
bit flash_tip=1;//數(shù)碼管閃爍標志,為0時數(shù)碼管熄滅,為一時數(shù)碼管顯示
#define Data_SIZE 15 //數(shù)據(jù)長度
u8 USART_RX_BUF[Data_SIZE]; //接收緩沖,最大Data_SIZE個字節(jié).末字節(jié)為換行符
u8 Data_length=0; //數(shù)據(jù)長度
u8 USART_RX_STA=0; //接收狀態(tài)標記
//函數(shù)功能:定時器初始化
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位數(shù)據(jù),可變波特率
TMOD &= 0x0F; //清除定時器1模式位
TMOD |= 0x20; //設定定時器1為8位自動重裝方式
TL1 = 0xFD; //設定定時初值
TH1 = 0xFD; //設定定時器重裝值
TR1 = 1; //啟動定時器1
EA=1;
ES=1; //打開接收中斷
}
/*******************************************************************************
* 函 數(shù) 名: void main()
* 函數(shù)功能: 主函數(shù)
*******************************************************************************/
void main()
{
Time0init();//定時器
UartInit(); //串口
Pintf_Uart();//輸入提示
while(1)
{
Key_Timer_Set();//按鍵控制時鐘
Uart_Timer_Set();//按鍵調(diào)節(jié)時鐘
Display_Timer(Hour,Min,Sec);//數(shù)碼管顯示
}
}
/*******************************************************************************
* 函 數(shù) 名: void Pintf_Uart()
* 函數(shù)功能: 串口助手輸入指示
*******************************************************************************/
void Pintf_Uart()
{
/***************輸入指示*******************/
PutString("請輸入指令");
PutString("(回車結(jié)束):");
crlf();
PutString("暫停計時:stop");
crlf();
PutString("開始計時:start");
crlf();
PutString("時鐘清零:reset");
crlf();
PutString("讀取當前時間:read");
crlf();
/*******************************************/
}
/*******************************************************************************
* 函 數(shù) 名: void Key_Timer_Set()
* 函數(shù)功能: //按鍵調(diào)節(jié)時鐘
*******************************************************************************/
void Key_Timer_Set()
{
u8 keynum;
keynum=Key_Scan();//按鍵返回值
if(keynum) //非0表示有按鍵按下
{
switch(keynum) //判斷是哪個按鍵按下,按鍵一調(diào)節(jié)模式,按鍵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;
}
}
}
/*******************************************************************************
* 函 數(shù) 名: void Key_Timer_Set()
* 函數(shù)功能: 串口調(diào)節(jié)時鐘
//strstr(str1,str2) 函數(shù)用于判斷字符串str2是否是str1的子串。
//如果是,則該函數(shù)返回str2在str1中首次出現(xiàn)的地址;否則,返回NULL。
*******************************************************************************/
void Uart_Timer_Set()
{
static char str[10];//串口數(shù)據(jù)緩存區(qū)
u8 i;
if(USART_RX_STA) //如果串口接收到數(shù)據(jù)
{
for(i=0;i<Data_length;i++) //將數(shù)據(jù)存入數(shù)組
{
str[i]=USART_RX_BUF[i];
}
USART_RX_STA=0; //接收完畢
if(strstr(str,"stop")) //暫停計時
{
TR0=0;
PutString("已暫停");
crlf();
}
else if(strstr(str,"start")) //暫停計時
{
TR0=1;
PutString("已開始");
crlf();
}
else if(strstr(str,"reset")) //暫停計時
{
Hour=0;
Min=0;
Sec=0;
PutString("已清零");
crlf();
}
else if(strstr(str,"read")) //暫停計時
{
PutString("當前時間為:");
PutChar(Hour/10+48); //轉(zhuǎn)化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);
crlf();
}
else
{
PutString("指令錯誤!請重新輸入");
crlf();
}
ES=1;//打開接收中斷
}
}
/*******************************************************************************
* 函 數(shù) 名: void Delay_ms(u16 xms)
* 函數(shù)功能: 軟件延時函數(shù),xms為延時多少毫秒
*******************************************************************************/
void Delay_ms(u16 xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 135;
do
{
while (--j);
} while (--i);
}
}
/*******************************************************************************
* 函 數(shù) 名: void ShuMaGuan(u8 wei,u8 duan)
* 函數(shù)功能: 靜態(tài)顯示一位,參數(shù):wei控制位選duan控制段選,表示要顯示的一個數(shù)字
*******************************************************************************/
void ShuMaGuan(u8 wei,u8 duan)
{
P1=WeiMa[wei]; //位選
P2=DuanMa[duan]; //段選
Delay_ms(1); //間隔一段時間掃描
P1=0xFF;
P2=0xFF; //消隱
}
/*******************************************************************************
* 函 數(shù) 名: void Display_Timer(u8 hour,u8 min,u8 sec)
* 函數(shù)功能:數(shù)碼管動態(tài)顯示
flash_tip為數(shù)碼管閃爍標志,為0時數(shù)碼管熄滅,為一時數(shù)碼管顯示
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,數(shù)碼管顯示
{
ShuMaGuan(5,hour/10);
ShuMaGuan(4,hour%10);
}
else P1=0xFF;
if(mode!=2 || flash_tip==1)//mode=2時,左邊的條件一直為假,當flash_tip=1時,或運算為真,進入if,數(shù)碼管顯示
{
ShuMaGuan(3,min/10);
ShuMaGuan(2,min%10);
}
else P1=0xFF;
if(mode!=3 || flash_tip==1)//mode=3時,左邊的條件一直為假,當flash_tip=1時,或運算為真,進入if,數(shù)碼管顯示
{
ShuMaGuan(1,sec/10);
ShuMaGuan(0,sec%10);
}
else P1=0xFF;
}
/*******************************************************************************
* 函 數(shù) 名: u8 Key_Scan()
* 函數(shù)功能: 獨立按鍵檢測,按鍵按下分別返回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結(jié)果為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; // 無按鍵按下
}
/*******************************************************************************
* 函 數(shù) 名: void PutChar(u8 n)
* 函數(shù)功能: 發(fā)送一個字符
*******************************************************************************/
void PutChar(u8 n)
{
SBUF=n;
while(!TI);
TI=0;
}
/*******************************************************************************
* 函 數(shù) 名: void PutString(u8 *p)
* 函數(shù)功能: 發(fā)送字符串
*******************************************************************************/
void PutString(u8 *p)
{
while(*p!='\0')
{
PutChar(*p);
p++;
}
}
/*******************************************************************************
* 函 數(shù) 名: void crlf()
* 函數(shù)功能: 換行函數(shù)通過輸出兩個ASCII字符實現(xiàn)
*******************************************************************************/
void crlf()
{
PutChar(0x0D);
PutChar(0x0A);
}
/*******************************************************************************
* 函 數(shù) 名: void uart() interrupt 4
* 函數(shù)功能: 串口中斷服務函數(shù),單片機接收數(shù)據(jù)并存入USART_RX_BUF[]數(shù)組中
*******************************************************************************/
void uart() interrupt 4
{
static u8 Data_count=0;
u8 Data;
if(RI==1)
{
RI=0;
Data=SBUF;
if(Data!='\n') //判斷是否接收到結(jié)束符
{
USART_RX_BUF[Data_count]=Data;//數(shù)據(jù)還沒結(jié)束發(fā)送,就存到USART_RX_BUF[]數(shù)組中
Data_count++;
}
else
{
Data_length=Data_count;//記錄其數(shù)據(jù)長度
Data_count=0;
USART_RX_STA=1;//接收完成
ES=0;
}
}
}
/*******************************************************************************
* 函 數(shù) 名: void Time0() interrupt 1
* 函數(shù)功能: 定時器0中斷服務函數(shù),時鐘效果
*******************************************************************************/
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時,數(shù)碼管正常顯示
else flag_2++;
if(flag_1==20 && mode==0) //每秒執(zhí)行一次
{
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;
}
}
到了這里,關(guān)于【個人筆記】51單片機串口通信的字符串接收和發(fā)送,串口通信調(diào)節(jié)數(shù)碼管顯示時鐘(串口通信,定時器,數(shù)碼管)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!