51单片机与PC串口通信

2019-04-15 15:31发布

本人最近写了一个单片机与电脑的串口通信程序,程序调试成功,希望对大家有所帮助。本程序的功能有:1、通过DS18B20采集温度并通过1602显示出来。2、单片机与电脑进行通信,上位机给单片机一个读温度指令,单片机把温度值以一定的形式发送给电脑。电脑和单片机的协议如表1所示。 单片机反馈温度值给电脑的形式如表2所示。 表1  电脑读取温度值   帧头 帧长度 地址码 保留 功能码 寄存器地址 寄存器个数   校验码   1字节 1字节 4字节 1字节 1字节 1字节 1字节 1字节 读取温度值 0x01 0x0B 0X00000000 0X00 0X03 0X04  0x02 0x00   表2  单片机反馈温度值给电脑   帧头 帧长度 地址码 保留 功能码 寄存器地址 寄存器个数 数据   校验码   1字节 1字节 4字节 1字节 1字节 1字节 1字节 4字节 1字节 环境值 0x01 0x0F 0X00000000 0X00 0X03 0X04  0x02 0X00 0X00 0X00 0X01 0x00     其中单片机反馈温度的数据形式为:共有三字节数据(从数据区第二个字节开始算),分别为十位、个位、十分位,其中十位的BIT7表示温度正负,BIT7=0,表示为正值,BIT7=1,表示为负值,温度数值采用扩展BCD码表示; 例:  0x00,0x03,0x01,0x05表示+31.5℃    0x00,0x81,0x02,0x05表示-12.5℃ 串口通讯程序的编写采用的是C语言编写。单片机接收是通过中断来接收                  的,每收到一个字节的数据就产生一次中断,并在中断处理程序中把接收到的内容保存在数组中,这样就可以接收多个字节的数据。因为要向电脑发送多个字节的内容,所以单片机的发送采用循环发送的方式,通过循环把一帧数据发送出去。具体程序如下所示。 1602头文件: /*----------------------------------------------- 名称:LCD1602 引脚定义如下:1-VSS 2-VDD 3-V0 4-RS 5-R/W 6-E 7-14 DB0-DB7 15-BLA       16-BLK ------------------------------------------------*/ #include "1602.h" #include "delay.h"   sbit RS = P1^6;   //定义端口  sbit RW = P1^5; sbit EN = P1^4;   #define RS_CLR RS=0  #define RS_SET RS=1   #define RW_CLR RW=0  #define RW_SET RW=1    #define EN_CLR EN=0 #define EN_SET EN=1   #define DataPort P2   /*------------------------------------------------               判忙函数 ------------------------------------------------*/  bit LCD_Check_Busy(void)   {   DataPort= 0xFF;   RS_CLR;   RW_SET;   EN_CLR;   _nop_();   EN_SET;  return (bit)(DataPort & 0x80);  } /*------------------------------------------------               写入命令函数 ------------------------------------------------*/  void LCD_Write_Com(unsigned char com)   {    while(LCD_Check_Busy()); //忙则等待  RS_CLR;   RW_CLR;   EN_SET;   DataPort= com;   _nop_();   EN_CLR;  } /*------------------------------------------------               写入数据函数 ------------------------------------------------*/  void LCD_Write_Data(unsigned char Data)   {   while(LCD_Check_Busy()); //忙则等待  RS_SET;   RW_CLR;   EN_SET;   DataPort= Data;   _nop_();  EN_CLR;  }   /*------------------------------------------------                 清屏函数 ------------------------------------------------*/  void LCD_Clear(void)   {   LCD_Write_Com(0x01);   DelayMs(5);  } /*------------------------------------------------               写入字符串函数 ------------------------------------------------*/  void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)   {       if (y == 0)    {       LCD_Write_Com(0x80 + x);     //表示第一行   }  else    {         LCD_Write_Com(0xC0 + x);      //表示第二行   }          while (*s)    {       LCD_Write_Data( *s);       s ++;        }  } /*------------------------------------------------               写入字符函数 ------------------------------------------------*/  void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data)   {       if (y == 0)    {        LCD_Write_Com(0x80 + x);        }      else    {        LCD_Write_Com(0xC0 + x);        }          LCD_Write_Data( Data);    } /*------------------------------------------------               初始化函数 ------------------------------------------------*/  void LCD_Init(void)   {    LCD_Write_Com(0x38);    /*显示模式设置*/     DelayMs(5);     LCD_Write_Com(0x38);     DelayMs(5);     LCD_Write_Com(0x38);     DelayMs(5);     LCD_Write_Com(0x38);      LCD_Write_Com(0x08);    /*显示关闭*/     LCD_Write_Com(0x01);    /*显示清屏*/     LCD_Write_Com(0x06);    /*显示光标移动设置*/     DelayMs(5);     LCD_Write_Com(0x0C);    /*显示开及光标设置*/    } /*------------------------------------------------    设定二个自定义字符,LCD1602中自定义字符的地址为0x00--0x07, 即可定义8个字符 这里我们设定把一个自定义字符放在0x00位置(000), 另一个放在0x01位子(001) ------------------------------------------------*/ void Lcd_User_Chr(void) { //第一个自定义字符  LCD_Write_Com(0x40); //"01 000 000"  第1行地址 (D7D6为地址设定命令形式D5D4D3为字符存放位置(0--7),D2D1D0为字符行地址(0--7))  LCD_Write_Data(0x00); //"XXX 11111" 第1行数据(D7D6D5为XXX,表示为任意数(一般用000),D4D3D2D1D0为字符行数据(1-点亮,0-熄灭)  LCD_Write_Com(0x41); //"01 000 001"  第2行地址  LCD_Write_Data(0x04); //"XXX 10001" 第2行数据  LCD_Write_Com(0x42); //"01 000 010"  第3行地址  LCD_Write_Data(0x0e); //"XXX 10101" 第3行数据  LCD_Write_Com(0x43); //"01 000 011"  第4行地址  LCD_Write_Data(0x0e); //"XXX 10001" 第4行数据  LCD_Write_Com(0x44); //"01 000 100"  第5行地址  LCD_Write_Data(0x0e); //"XXX 11111" 第5行数据  LCD_Write_Com(0x45); //"01 000 101"  第6行地址  LCD_Write_Data(0x1f); //"XXX 01010" 第6行数据  LCD_Write_Com(0x46); //"01 000 110"  第7行地址  LCD_Write_Data(0x04); //"XXX 11111" 第7行数据  LCD_Write_Com(0x47); //"01 000 111"  第8行地址    LCD_Write_Data(0x00); //"XXX 00000" 第8行数据   //第二个自定义字符    LCD_Write_Com(0x48); //"01 001 000"  第1行地址    LCD_Write_Data(0x03); //"XXX 00001" 第1行数据   LCD_Write_Com(0x49); //"01 001 001"  第2行地址  LCD_Write_Data(0x03); //"XXX 11011" 第2行数据  LCD_Write_Com(0x4a); //"01 001 010"  第3行地址  LCD_Write_Data(0x00); //"XXX 11101" 第3行数据  LCD_Write_Com(0x4b); //"01 001 011"  第4行地址  LCD_Write_Data(0x00); //"XXX 11001" 第4行数据  LCD_Write_Com(0x4c); //"01 001 100"  第5行地址  LCD_Write_Data(0x00); //"XXX 11101" 第5行数据  LCD_Write_Com(0x4d); //"01 001 101"  第6行地址  LCD_Write_Data(0x00); //"XXX 11011" 第6行数据  LCD_Write_Com(0x4e); //"01 001 110"  第7行地址  LCD_Write_Data(0x00); //"XXX 00001" 第7行数据  LCD_Write_Com(0x4f); //"01 001 111"  第8行地址  LCD_Write_Data(0x00); //"XXX 00000" 第8行数据   } DS18B20头文件: /*-----------------------------------------------   名称:18B20温度传感器   内容:18B20单线温度检测的应用样例程序 ------------------------------------------------*/ #include"delay.h" #include"18b20.h" /*------------------------------------------------                     18b20初始化 ------------------------------------------------*/ bit Init_DS18B20(void) {  bit dat=0;  DQ = 1;    //DQ复位  DelayUs2x(5);   //稍做延时  DQ = 0;         //单片机将DQ拉低  DelayUs2x(200); //精确延时 大于 480us 小于960us  DelayUs2x(200);  DQ = 1;        //拉高总线  DelayUs2x(50); //15~60us 后 接收60-240us的存在脉冲  dat=DQ;        //如果x=0则初始化成功, x=1则初始化失败  DelayUs2x(25); //稍作延时返回  return dat; }   /*------------------------------------------------                     读取一个字节 ------------------------------------------------*/ unsigned char ReadOneChar(void) { unsigned char i=0; unsigned char dat = 0; for (i=8;i>0;i--)  {   DQ = 0; // 给脉冲信号   dat>>=1;   DQ = 1; // 给脉冲信号   if(DQ)    dat|=0x80;   DelayUs2x(25);  }  return(dat); } /*------------------------------------------------                     写入一个字节 ------------------------------------------------*/ void WriteOneChar(unsigned char dat) {  unsigned char i=0;  for (i=8; i>0; i--)  {   DQ = 0;   DQ = dat&0x01;   DelayUs2x(25);   DQ = 1;   dat>>=1;  } DelayUs2x(25); }   /*------------------------------------------------                     读取温度 ------------------------------------------------*/ unsigned int ReadTemperature(void) { unsigned char a=0; unsigned int b=0; unsigned int t=0; Init_DS18B20(); WriteOneChar(0xCC); // 跳过读序号列号的操作 WriteOneChar(0x44); // 启动温度转换 DelayMs(10); Init_DS18B20(); WriteOneChar(0xCC); //跳过读序号列号的操作  WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度 a=ReadOneChar();   //低位 b=ReadOneChar();   //高位   b<<=8; t=a+b;   return(t); } 延迟函数头文件: #include "delay.h" /*------------------------------------------------  uS延时函数,含有输入参数 unsigned char t,无返回值  unsigned char 是定义无符号字符变量,其值的范围是  0~255 这里使用晶振12M,精确延时请使用汇编,大致延时  长度如下 T=tx2+5 uS  ------------------------------------------------*/ void DelayUs2x(unsigned char t) {     while(--t); } /*------------------------------------------------  mS延时函数,含有输入参数 unsigned char t,无返回值  unsigned char 是定义无符号字符变量,其值的范围是  0~255 这里使用晶振12M,精确延时请使用汇编 ------------------------------------------------*/ void DelayMs(unsigned char t) {        while(t--)  {      //大致延时1mS      DelayUs2x(245);  DelayUs2x(245);  } } 主函数: /*-----------------------------------------------   名称:DS18b20 温度检测 LCD1602液晶显示 与上位机串口通信 ------------------------------------------------*/     #include //包含头文件,一般情况不需要改动             #include #include "18b20.h" #include "1602.h" #include "delay.h" bit ReadTempFlag;//定义读时间标志 char jsdata[16]={0}; char fsdata[16]={0}; char receive_end=0; char i=0; void Init_Timer0(void);//定时器初始化 /*------------------------------------------------               串口通讯初始化 ------------------------------------------------*/ void com_init(void) //设置串口调试助手的波特率为9600 {     TMOD=0X20;     SCON=0X50; TH1=0XFD; TL1=0XFD; TR1=1; EA=1;//总中断打开 ES=1; //串口中断打开,等待接收和发送中断 } /*------------------------------------------------                     主函数 ------------------------------------------------*/ void main (void) {                   int temp,j; float temperature; char displaytemp[16];//定义显示区域临时存储数组 RI=0; com_init();  LCD_Init();           //初始化液晶 DelayMs(20);          //延时有助于稳定 LCD_Clear();          //清屏 Init_Timer0(); Lcd_User_Chr();       //写入自定义字符 LCD_Write_String(0,0,"15.5.15"); LCD_Write_Char(14,0,0x01);//写入温度右上角点 LCD_Write_Char(15,0,'C'); //写入字符C while (1)         //主循环   {           if(ReadTempFlag==1)           {             ReadTempFlag=0;             temp=ReadTemperature();             temperature=(float)temp*0.0625;             sprintf(displaytemp,"%7.3f",temperature);//打印温度值              LCD_Write_String(7,0,displaytemp); //SBUF=temperature;           }          if(receive_end==1)       {      receive_end=0;              if(temperature>0)   {     fsdata[0]=0x01;     fsdata[1]=0x0F;     fsdata[2]=0X00; fsdata[3]=0X00; fsdata[4]=0X00; fsdata[5]=0X00; fsdata[6]=0X00;   fsdata[7]=0X03; fsdata[8]=0X04; fsdata[9]=0x02; fsdata[10]=0X00; fsdata[11]=temperature/10; fsdata[12]=((int)temperature)%10; fsdata[13]=(((int)(temperature*10))%100)%10; fsdata[14]=0x00; for(j=0;j<15;j++)    {  SBUF=fsdata[j];  while(!TI);   TI=0; }     }  if(temperature<0)    { fsdata[0]=0x01;     fsdata[1]=0x0F;     fsdata[2]=0X00; fsdata[3]=0X00; fsdata[4]=0X00; fsdata[5]=0X00; fsdata[6]=0X00; fsdata[7]=0X03; fsdata[8]=0X04; fsdata[9]=0x02; fsdata[10]=0X00; fsdata[11]=((int)temperature/10)|0x80; fsdata[12]=((int)temperature)%10; fsdata[13]=(((int)(temperature*10))%100)%10; fsdata[14]=0x00; for(j=0;j<15;j++)        {      SBUF=fsdata[j];  while(!TI);  TI=0;    }    }  }  } }   /*------------------------------------------------                     定时器初始化子程序 ------------------------------------------------*/ void Init_Timer0(void) {  TMOD |= 0x01;   //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响        //TH0=0x00;       //给定初值  //TL0=0x00;  EA=1;            //总中断打开  ET0=1;           //定时器中断打开  TR0=1;           //定时器开关打开 } /*------------------------------------------------                  定时器中断子程序 ------------------------------------------------*/ void Timer0_isr(void) interrupt 1  {  static unsigned int num;  TH0=(65536-2000)/256;   //重新赋值 2ms  TL0=(65536-2000)%256;  num++;  if(num==300)            {     num=0;     ReadTempFlag=1; //读标志位置1 } } /*------------------------------------------------              串口中断中断服务子程序 ------------------------------------------------*/ void jieshou() interrupt 4   {     if(RI==1)       {        RI=0;  jsdata[i]=SBUF; i++; if((i==11)&&(jsdata[i-4]==0x03))       {     i=0; receive_end=1;//接收完成标志置1    }   }     } 串口调试界面如下图所示,只要上位机发送一个读取温度指令,单片机就以一定的格式发送一帧温度数据给上位机。