單片機與PC機通過RS232相連,編寫一個異步串行口通信程序,實現單片機與PC機上的串口助手之間的通信。 功能實現: 1、當PC機向單片機發送字符‘1’~‘8’,打開對應的8個燈; 2、當PC機再次向單片機發送字符‘1’~‘8’時,關閉對應的燈; 3、當PC機向單片機發送字符‘d’時,會在液晶屏上刪除一個字符; 4、當PC機向單片機發送字符‘n’時,會在液晶屏上換行顯示; 5、當PC機向單片機發送字符‘c’時,會清屏顯示; 6、當PC機向單片機發送字符‘m’時,會打開音樂播放,結束后可繼續操作;
7、當PC機向單片機發送其它字符時,會顯示在1602液晶屏上; 8、當矩陣按鍵有按鍵按下時,單片機會把鍵值發送到PC機上。
仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)
單片機源程序如下:
- #include<reg52.h>
- #include<string.h>
- #include<intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- uchar str[17];
- uchar ch;
- uchar m,n;
- sbit K1=P3^7;
- bit play=0;
- sbit LEN=P3^4;
- sbit LCDEN=P3^6;
- sbit RS=P3^3;
- sbit RW=P3^5;
- sbit BF=P2^7;
- sbit LED=P0;
- sbit BEEP=P3^2;
- uint I=0,J,K;
- uchar code keyval[]="123456789*0#"; //按鍵對應的符號
- char s[ ]="MCS-51 Microcomputer";
- uchar OpenLight[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
- uchar CloseLight[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
- uchar code T[49][2]={{0,0},
- {0xF8,0x8B},{0xF8,0xF2},{0xF9,0x5B},{0xF9,0xB7},{0xFA,0x14},{0xFA,0x66},{0xFA,0xB9},{0xFB,0x03},{0xFB,0x4A},{0xFB,0x8F},{0xFB,0xCF},{0xFC,0x0B},
- {0xFC,0x43},{0xFC,0x78},{0xFC,0xAB},{0xFC,0xDB},{0xFD,0x08},{0xFD,0x33},{0xFD,0x5B},{0xFD,0x81},{0xFD,0xA5},{0xFD,0xC7},{0xFD,0xE7},{0xFE,0x05},
- {0xFE,0x21},{0xFE,0x3C},{0xFE,0x55},{0xFE,0x6D},{0xFE,0x84},{0xFE,0x99},{0xFE,0xAD},{0xFE,0xC0},{0xFE,0x02},{0xFE,0xE3},{0xFE,0xF3},{0xFF,0x02},
- {0xFF,0x10},{0xFF,0x1D},{0xFF,0x2A},{0xFF,0x36},{0xFF,0x42},{0xFF,0x4C},{0xFF,0x56},{0xFF,0x60},{0xFF,0x69},{0xFF,0x71},{0xFF,0x79},{0xFF,0x81}
- };
- uchar code music[][2]={
- {0,4},{20,8},{25,4},{22,4},{20,8},
- {20,8},{17,4},{20,4},{22,4},{25,4},
- {20,8},{20,8},{22,8},{20,4},{17,4},
- {15,8},{15,8},{17,8},{20,4},{17,4},
- {15,4},{17,4},{13,8},{22,8},{20,8},
- {22,8},{20,8},{17,8},{22,8},{20,16},
- {20,4},{20,4},{17,4},{15,4},{13,16},
- {13,4},{13,4},{15,4},{17,4},{13,16},
- {0xFF,0xFF}};
- //生日快樂歌的音符頻率表,不同頻率由不同的延時來決定
- uchar code SONG_TONE[]={212,212,190,212,159,169,212,212,190,212,142,159,
- 212,212,106,126,159,169,190,119,119,126,159,142,159,0};
- //生日快樂歌節拍表,節拍決定每個音符的演奏長短
- uchar code SONG_LONG[]={9,3,12,12,12,24,9,3,12,12,12,24,
- 9,3,12,12,12,12,12,9,3,12,12,12,24,0};
- void delay(uchar p)
- {
- uchar i,j;
- for(;p>0;p--)
- for(i=181;i>0;i--)
- for(j=181;j>0;j--);
- }
- void DelayMS(uint x)
- {
- uchar t;
- while(x--) for(t=0;t<120;t++);
- }
- //2¥·?oˉêy
- void PlayMusic2()
- {
- // uint i=0,j,k;
- if(SONG_LONG[I]!=0||SONG_TONE[I]!=0)
- {
- for(J=0;J<SONG_LONG[I]*20;J++)
- {
- BEEP=~BEEP;
-
- for(K=0;K<SONG_TONE[I]/3;K++);
- }
- DelayMS(10);
- I++;
- }
- else
- I=0;
- }
- void delay_us(uchar t) //微秒延時
- {uchar i;
- for(i=0;i<=t;i++);
- }
- void Alarm(uchar t) //報警
- {
- uchar i,j,k;
- for(j=0;j<t;j++)
- { for(i=0;i<200;i++)
- {BEEP=0;delay_us(50);BEEP=1;delay_us(50);}
- for(k=0;k<100;k++)
- {BEEP=0;delay_us(110);BEEP=1;delay_us(110);}
- }
- }
- void pause()
- {
- uchar i,j;
- for(i=150;i>0;i--)
- for(j=150;j>0;j--);
- }
- void delay_ms(unsigned int xms)
- {
- int i,j;
- for(i=xms;i>0;i--)
- for(j=110;j>0;j--);
- }
- void PlayMusic()
- {
- uchar i=0;
-
- while(1)
- { if(i==40){ ET0=0;
- break; }
- m=music[i][0];
- n=music[i][1];
- if(m==0x00)
- {
- TR0=0;
- delay(n);
- i++;
- }
- else if(m==0xFF)
- {
- TR0=0;
- delay(30);
- i=0;
- }
- else if(m==music[i+1][0])
- {
- TR0=1;
- delay(n);
- TR0=0;
- pause();
- i++;
- }
- else
- {
- TR0=1;
- delay(n);
- i++;
- }
- }
- }
- uchar keypad4_3()//按鍵掃描函數:要去抖,若有按鍵按下,返回對應的按鍵值(0-11),沒有按鍵按下返回12
- {
- uchar i,row,temp;
- uchar key=12;//按鍵號,初值設置為12,目的是:沒有按鍵按下時返回12;
- //若不設初值(默認值為0),沒有按鍵按下時,將返回0,會誤認為0被按下
- row=0xef; //從第一行開始
- for(i=0;i<4;i++)
- {
- P1=0xff;
- P1=row; //第i行信號,對應行為低,其他全為高
- row=_crol_(row,1); //生成下一行信號
- temp=P1; //讀入掃描信號
- temp=temp&0x07; //屏蔽高5位信號,只保留低3位列信號
- if(temp!=0x07)//有按鍵被按下,因為第i行某列有按鍵按下,則低3位中有一位為低
- {
- delay_ms(20); //延時去抖
- temp=P1;
- temp=temp&0x07;
- if(temp!=0x07) //再次確認有按鍵被按下
- {
- switch(temp) //根據低3位列信號,判斷哪個按鍵被按下
- {
- case 0x06:key=0+3*i;break; //第i行第1列按鍵被按下
- case 0x05:key=1+3*i;break; //第i行第2列按鍵被按下
- case 0x03:key=2+3*i;break; //第i行第3列按鍵被按下
- }
-
- do
- {
- temp=P1; //再次掃描按鍵
- temp=temp&0x07;
- }while(temp!=0x07); //等待按鍵釋放
- }
- }
- }
- return(key);//掃面結束,返回按鍵值
- }
- uchar RdACAdr()//讀當前光標地址
- {
- uchar result;
- P2 = 0xff; //讀地址前先置高電平,防止誤判
- RS = 0;
- delay_ms(5);
- RW = 1;
- LCDEN = 1;
- delay_ms(5);
- result=P2&0x7f; //去掉最高位忙閑標記,只保留低7位地址值
- LCDEN = 0;
- return result;
- }
- uchar DectectBusyBit(void)//狀態判斷函數(忙/閑?)
- {
- bit result;
- P2 = 0xff; //讀狀態前先置高電平,防止誤判
- RS = 0;
- delay_ms(5);
- RW = 1;
- LCDEN = 1;
- delay_ms(5);
- result=BF; //若LCM忙,則反復測試,在此處原地踏步;當LCM閑時,才往下繼續
- LCDEN = 0;
- return result;
- }
- void WrComLCD(unsigned char ComVal)//寫命令函數
- {
- while(DectectBusyBit()==1); //先檢測LCM是否空閑
- RS = 0;
- delay_ms(1);
- RW = 0;
- LCDEN = 1;
- P2 = ComVal;
- delay_ms(1);
- LCDEN = 0;
- }
- void WrDatLCD(uchar DatVal)//寫數據函數
- {
- while(DectectBusyBit()==1);
- RS = 1;
- delay_ms(1);
- RW = 0;
- LCDEN = 1;
- P2 = DatVal;
- delay_ms(1);
- LCDEN = 0;
- }
- void WrStrDat(uchar *p)//顯示英文字符串(長度不超過32)
- {
- uchar i=0,t;
-
- while(p[i]!='\0')
- {
- WrDatLCD(p[i]);
- i++;
- delay_ms(5);
-
- t=RdACAdr();
- if(t==0x10) WrComLCD(0xc0);//讀當前坐標,如果第1行寫完換行到第2行
- if(t==0x50) WrComLCD(0x80);//讀當前坐標,如果第2行寫完換行到第1行
- }
- }
- void LCD_Init(void)//1602初始化函數
- {
- //delay(15);
- WrComLCD(0x38);
- //delay(5); // 功能設定:16*2行、5*7點陣、8位數據接口
- WrComLCD(0x38);
- //delay(5);
- WrComLCD(0x38);
- //多次重復設定功能指令,因為LCD啟動后并不知道使用的是4位數據接口還是8位的,所以開始時總是默認為4位,這樣剛開始寫入功能設定指令時,低4位被忽略,為了可靠,最好多寫幾遍該指令
- WrComLCD(0x01); // 清屏
- WrComLCD(0x06); // 光標自增、屏幕不動
- delay_ms(1); // 延時,等待上面的指令生效,下面再顯示,防止出現亂碼
- WrComLCD(0x0C); // 開顯示、關光標
- }
- void uart_init(unsigned int bps)
- { unsigned char t;
- SCON=0x50; //工作方式一:8位異步收發,波特率可變,允許接收數據
- PCON=0x00; //SMOD=0
- TI=0; //軟件清零,表示未發送完成
- EA=1; //開總中斷
- ET1=1;
- ET0=1; //開T0
- ES=1; //開串口中斷
- TMOD=0x21; //設置T1定時器8位自動裝載模式
- switch(bps)
- { case 1200:t=0xe8;break;
- case 2400:t=0xf4;break;
- case 4800:t=0xfa;break;
- case 9600:t=0xfd;break;
- }
- TH1=t;
- TL1=t;
- TR1=1; //開啟T1
- }
- void uart_send(unsigned char ch)
- {
- ES=0; //關串口中斷
- SBUF=ch;
- while(TI==0); //等待發送完成
- TI=0; //清除中斷標記
- ES=1; //開中斷
- }
- unsigned char receive(void)
- { unsigned char dat;
- while(RI==0); //等待接收完畢
- RI=0; //將接收中斷標志RI清0,為接收下一幀數據做準備
- dat=SBUF; //將接收緩沖器中的數據存入dat
- return dat; //將接收到的數據返回
- }
- void main (void)
- { uchar temp=0xff;
- uchar tt=0xff;
- uchar a,b=0;
- uchar keypadVal=12;
- //TMOD=0x21; EA=1; ET0=1;
- //ET1=0;
- //LEN=1;
- PT0=1;
- //PS=1;
- P0=0xff;
- //LEN=0;
- uart_init(9600);//串口初始化
- LCD_Init();//1602液晶屏初始化
- delay_ms(5); //延時,等待初始化完成
- WrComLCD(0x80); //設置顯示地址第一行第一位:0X00(0x80+0x00)
-
- while(1)
- {
-
- keypadVal=keypad4_3();
- if(keypadVal<12)
- {
- uart_send(keyval[keypadVal]);
- }
- /*if(K1==0)//如按鍵按下,就發送"MCS-51 Microcomputer"
- {
- b=0;//計數器清0
- a=strlen(s);//取數組長度
- for(;b<a;b++)//循環取數據發送,從0到a
- { SBUF=s[b];//發送"MCS-51 Microcomputer"
- while(!TI);//沒有發送完,就等待.
- TI=0;//清發送結束標志
- }
- while(!K1);//如果按鍵沒有松開,等待
- }*/
- if(RI||a=='m')//如果接收到數據,就把接收到的數回發給PC
- { uchar pos;
-
- RI=0;//清接收標志
-
- //if(a!='m')
- //{
- a=SBUF ;//讀串行口數據
- SBUF=a;//把從PC機發送過來的數據返回給PC機
- // if(a=='m')
- // play=~play;
- // }
-
-
- while(!TI);//沒有發送完等待
- TI=0;//清發送完中斷標志
- if(a>'0'&&a<='8')
- {
- if(temp==a)
- tt=tt^(_crol_(0x01,a-'0'-1));
- //P0=0x00;
- else
- tt=tt&(~OpenLight[a-'0'-1]);
- temp=a;
- P0=tt;
- }
- else
- {
- if(a=='n')
- {
- //換行
- pos=RdACAdr();
- if(pos>=0x00&&pos<0x10){ WrComLCD(0xc0);WrStrDat(" ");WrComLCD(0xc0);}//讀當前坐標,如果第1行寫完換行到第2行
- else if(pos>=0x40&&pos<0x50) { WrComLCD(0x80);WrStrDat(" ");WrComLCD(0x80);}//讀當前坐標,如果第2行寫完換行到第1行
- }
- else if(a=='c')
- {
- WrComLCD(0x01);
- WrComLCD(0x80);
- }
- else if(a=='d')
- {
- //刪除
- WrComLCD(0x10);
- WrDatLCD(' ');
- WrComLCD(0x10);
- }
- else if(a=='m')
- { //ET0=1;
- PlayMusic2();
-
- //if(play)
- //Alarm(5);
- }
- else
- {
- WrDatLCD(a);
- //WrComLCD(0x80);
- pos=RdACAdr();
- if(pos==0x10) WrComLCD(0xc0);//讀當前坐標,如果第1行寫完換行到第2行
- if(pos==0x50) WrComLCD(0x80);//讀當前坐標,如果第2行寫完換行到第1行
- }
-
- }
- }
-
- }
- }
- void T0_int() interrupt 1
- {
- BEEP=!BEEP;
- TH0=T[m][0]; TL0=T[m][1];
- }
復制代碼
所有資料51hei提供下載:
PC機控制單片機.zip
(259.53 KB, 下載次數: 345)
2019-2-3 00:04 上傳
點擊文件名下載附件
|