51单片机串口通信协议(51单片机串口收发程序)

      最后更新:2023-03-20 02:37:45 手机定位技术交流文章

      单片机中所说的通信协议是什么?

      单片机通信协议 现在大部分的仪器设备都要求能过通过上位机软件来操作,这样方便调试,利于操作。其中就涉及到通信的过程。在实际制作的几个设备中,笔者总结出了通信程序的通用写法,包括上位机端和下位机端等。1.自定义数据通信协议这里所说的数据协议是建立在物理层之上的通信数据包格式。所谓通信的物理层就是指我们通常所用到的RS232、RS485、红外、光纤、无线等等通信方式。在这个层面上,底层软件提供两个基本的操作函数:发送一个字节数据、接收一个字节数据。所有的数据协议全部建立在这两个操作方法之上。通信中的数据往往以数据包的形式进行传送的,我们把这样的一个数据包称作为一帧数据。类似于网络通信中的TCPIP协议一般,比较可靠的通信协议往往包含有以下几个组成部分:帧头、地址信息、数据类型、数据长度、数据块、校验码、帧尾。帧头和帧尾用于数据包完整性的判别,通常选择一定长度的固定字节组成,要求是在整个数据链中判别数据包的误码率越低越好。减小固定字节数据的匹配机会,也就是说使帧头和帧尾的特征字节在整个数据链中能够匹配的机会最小。通常有两种做法,一、减小特征字节的匹配几率。二、增加特征字节的长度。通常选取第一种方法的情况是整个数据链路中的数据不具有随即性,数据可预测,可以通过人为选择帧头和帧尾的特征字来避开,从而减小特征字节的匹配几率。使用第二种方法的情况更加通用,适合于数据随即的场合。通过增加特征字节的长度减小匹配几率,虽然不能够完全的避免匹配的情况,但可以使匹配几率大大减小,如果碰到匹配的情况也可以由校验码来进行检测,因此这种情况在绝大多说情况下比较可靠。地址信息主要用于多机通信中,通过地址信息的不同来识别不同的通信终端。在一对多的通信系统中,可以只包含目的地址信息。同时包含源地址和目的地址则适用于多对多的通信系统。数据类型、数据长度和数据块是主要的数据部分。数据类型可以标识后面紧接着的是命令还是数据。数据长度用于指示有效数据的个数。校验码则用来检验数据的完整性和正确性。通常对数据类型、数据长度和数据块三个部分进行相关的运算得到。最简单的做法可是对数据段作累加和,复杂的也可以对数据进行CRC运算等等,可以根据运算速度、容错度等要求来选取。2.上位机和下位机中的数据发送物理通信层中提供了两个基本的操作函数,发送一个字节数据则为数据发送的基础。数据包的发送即把数据包中的左右字节按照顺序一个一个的发送数据而已。当然发送的方法也有不同。在单片机系统中,比较常用的方法是直接调用串口发送单个字节数据的函数。这种方法的缺点是需要处理器在发送过程中全程参与,优点是所要发送的数据能够立即的出现在通信线路上,能够立即被接收端接收到。另外一种方法是采用中断发送的方式,所有需要发送的数据被送入一个缓冲区,利用发送中断将缓冲区中的数据发送出去。这种方法的优点是占用处理器资源小,但是可能出现需要发送的数据不能立即被发送的情况,不过这种时延相当的小。对于51系列单片机,比较倾向于采用直接发送的方式,采用中断发送的方式比较占用RAM资源,而且对比直接发送来说也没有太多的优点。以下是51系列单片机中发送单个字节的函数。void SendByte(unsigned char ch){SBUF = ch;while(TI == 0);TI = 0;}上位机中关于串口通信的方式也有多种,这种方式不是指数据有没有缓冲的问题,而是操作串口的方式不同,因为PC上数据发送基本上都会被缓冲后再发送。对于编程来说操作串口有三种方式,一、使用windows系统中自带的串口通信控件,这种方式使用起来比较简单,需要注意的是接收时的阻塞处理和线程机制。二、使用系统的API直接进行串口数据的读取,在windows和linux系统中,设备被虚拟为文件,只需要利用系统提供的API函数即可进行串口数据的发送和读取。三、使用串口类进行串口操作。在此只介绍windows环境下利用串口类编程的方式。CSerialPort是比较好用的串口类。它提供如下的串口操作方法:void WriteToPort(char* string, int len);串口初始化成功后,调用此函数即可向串口发送数据。为了避免串口缓冲所带来的延时,可以开启串口的冲刷机制。3.下位机中的数据接收和协议解析下位机接收数据也有两种方式,一、等待接收,处理器一直查询串口状态,来判断是否接收到数据。二、中断接收。两种方法的优缺点在此前的一篇关于串口通信的文章中详细讨论过。得出的结论是采用中断接收的方法比较好。数据包的解析过程可以设置到不同的位置。如果协议比较简单,整个系统只是处理一些简单的命令,那么可以直接把数据包的解析过程放入到中断处理函数中,当收到正确的数据包的时候,置位相应的标志,在主程序中再对命令进行处理。如果协议稍微复杂,比较好的方式是将接收的数据存放于缓冲区中,主程序读取数据后进行解析。也有两种方式交叉使用的,比如一对多的系统中,首先在接收中断中解析“连接”命令,连接命令接收到后主程序进入设置状态,采用查询的方式来解析其余的协议。以下给出具体的实例。在这个系统中,串口的命令非常简单。所有的协议全部在串口中断中进行。数据包的格式如下:0x55, 0xAA, 0x7E, 0x12, 0xF0, 0x02, 0x23, 0x45, SUM, XOR, 0x0D其中0x55, 0xAA, 0x7E为数据帧的帧头,0x0D为帧尾,0x12为设备的目的地址,0xF0为源地址,0x02为数据长度,后面接着两个数据0x23, 0x45,从目的地址开始结算累加、异或校验和,到数据的最后一位结束。协议解析的目的,首先判断数据包的完整性,正确性,然后提取数据类型,数据等数据,存放起来用于主程序处理。代码如下:if(state_machine == 0) // 协议解析状态机{if(rcvdat == 0x55) // 接收到帧头第一个数据state_machine = 1;elsestate_machine = 0;// 状态机复位}else if(state_machine == 1){if(rcvdat == 0xAA) // 接收到帧头第二个数据state_machine = 2;elsestate_machine = 0;// 状态机复位}else if(state_machine == 2){if(rcvdat == 0x7E) // 接收到帧头第三个数据state_machine = 3;elsestate_machine = 0;// 状态机复位}else if(state_machine == 3){sumchkm = rcvdat; // 开始计算累加、异或校验和xorchkm = rcvdat;if(rcvdat == m_SrcAdr)// 判断目的地址是否正确state_machine = 4;elsestate_machine = 0;}else if(state_machine == 4){sumchkm += rcvdat;xorchkm ^= rcvdat;if(rcvdat == m_DstAdr)// 判断源地址是否正确state_machine = 5;elsestate_machine = 0;}else if(state_machine == 5){lencnt = 0;// 接收数据计数器rcvcount = rcvdat;// 接收数据长度sumchkm += rcvdat;xorchkm ^= rcvdat;state_machine = 6;}else if(state _machine == 6 || state _machine == 7){m_ucData[lencnt++] = rcvdat; // 数据保存sumchkm += rcvdat;xorchkm ^= rcvdat;if(lencnt == rcvcount)// 判断数据是否接收完毕state_machine = 8;elsestate_machine = 7;}else if(state_machine == 8){if(sumchkm == rcvdat)// 判断累加和是否相等state_machine = 9;elsestate_machine = 0;}else if(state_machine == 9){if(xorchkm == rcvdat)// 判断异或校验和是否相等state_machine = 10;elsestate_machine = 0;}else if(state_machine == 10){if(0x0D == rcvdat) // 判断是否接收到帧尾结束符{retval = 0xaa;// 置标志,表示一个数据包接收到}state_machine = 0; // 复位状态机}此过程中,使用了一个变量state_machine作为协议状态机的转换状态,用于确定当前字节处于一帧数据中的那个部位,同时在接收过程中自动对接收数据进行校验和处理,在数据包接收完的同时也进行了校验的比较。因此当帧尾结束符接收到的时候,则表示一帧数据已经接收完毕,并且通过了校验,关键数据也保存到了缓冲去中。主程序即可通过retval的标志位来进行协议的解析处理。接收过程中,只要哪一步收到的数据不是预期值,则直接将状态机复位,用于下一帧数据的判断,因此系统出现状态死锁的情况非常少,系统比较稳定,如果出现丢失数据包的情况也可由上位机进行命令的补发,不过这种情况笔者还没有碰到。对于主程序中进行协议处理的过程与此类似,主程序循环中不断的读取串口缓冲区的数据,此数据即参与到主循环中的协议处理过程中,代码与上面所述完全一样。4.上位机中的数据接收和命令处理上位机中数据接收的过程与下位机可以做到完全一致,不过针对不同的串口操作方法有所不同。对于阻赛式的串口读函数,例如直接进行API操作或者调用windows的串口通信控件,最好能够开启一个线程专门用于监视串口的数据接收,每接收到一个数据可以向系统发送一个消息。笔者常用的CSerialPort类中就是这样的处理过程。CSerialPort打开串口后开启线程监视串口的数据接收,将接收的数据保存到缓冲区,并向父进程发送接收数据的消息,数据将随消息一起发送到父进程。父进程中开启此消息的处理函数,从中获取串口数据后就可以把以上的代码拷贝过来使用。CSerialPort向父类发送的消息号如下:#define WM_COMM_RXCHAR WM_USER+7 // A character was received and placed in the input buffer.因此需要手动添加此消息的响应函数:afx_msg LONG OnCommunication(WPARAM ch, LPARAM port);ON_MESSAGE(WM_COMM_RXCHAR, OnCommunication)响应函数的具体代码如下:LONG CWellInfoView::OnCommunication(WPARAM ch, LPARAM port){int retval = 0;rcvdat = (BYTE)ch;if(state_machine == 0) // 协议解析状态机{if(rcvdat == 0x55) // 接收到帧头第一个数据state_machine = 1;elsestate_machine = 0;// 状态机复位}else if(state_machine == 1){if(rcvdat == 0xAA) // 接收到帧头第二个数据state_machine = 2;elsestate_machine = 0;// 状态机复位......5.总结 以上给出的是通信系统运作的基本雏形,虽然简单,但是可行。实际的通信系统中协议比这个要复杂,而且涉及到数据包响应、命令错误、延时等等一系列的问题,在这样的一个基础上可以克服这些困难并且实现出较为稳定可靠的系统
      单片机与别的单片机或者设备及电脑之间传输数据,就需要一个通讯协议,有I2C,SPI,AUSART等协议,具体可协议内容可网上查找。
      你是要串口通信协议嘛?还有IIC,SPI,等呢
      用我个人理解说一下吧,这个协议一般都是自己订的吧,比如我发一个字符串,总长度为13字节,然后开头和结尾都用‘~’表示,占用了两个字节,剩下的11个字节可以表示要发的内容,而上位机和下位机进行通信,或两单片机进行通信时,内部程序在接收模块处将收到的信息进行程序判断,如果判断出开头是~然后才开始接收,当再次收到~,时停止接收,再进行程序校验,这些大部分要体现在程序里,按自己的要求来定,不然那不轻易被人破解了,那还有个什么意思呀,呵呵,仅个人理解 ,欢迎交流。
      单片机中所说的通信协议是什么?

      51单片机接收上位机发送的多个字节的串口通信

      具体程序可以参考楼下的例程 串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。典型地,串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线,(2)发送,(3)接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配:a,波特率:这是一个衡量通信速度的参数。它表示每秒钟传送的bit的个数。例如300波特表示每秒钟发送300个bit。当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。b,数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。c,停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。d,奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。 如果是奇校验,校验位位1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
      //试试以下程序 #include#define uchar unsigned charuchar i,j;unchar RX_BUF[10],TxBuf[10];void Inituart(){IE=0x90;//打开中断总开关及串口中断开关SCON = 0x50;//设定串行口工作方式1 允许接受TMOD = 0x20;//定时器1,自动重载, 产生波特率//PCON=0x80;// 加上他后波特率是19200SMOD为1TL1 = 0xfa;TH1 = 0xfa; //波特率为9600 ,22.1184MHzTR1 = 1;}/**************************接收上位机传送数据***************************************/void uart_js() interrupt 4//RI==1时执行串口中断{if(RI==1){RX_BUF[i]=SBUF; //保存数据RI=0;i++;}if(TI)TI=0;if(i==10){i=0;RXend = 1;LED0=0;//LED长亮}}void main(){Inituart();i=j=0;while(1){while(RXend==0);//waiting receive endRXend=0;for(j=0;j<10;j++){TxBuf[j] =RX_BUF[j];//将需发送数据存入无线发送缓冲区}} }
      #define FOSC 110592 //晶振频率 #define Baud 96//波特率void IE_INIT() //初始化串口{//IPH=PSH; //串口中断优先级控制PCON &= 0x7f; //波特率不倍速//PCON |= 0X80; //波特率倍速SCON= 0x50; //8位数据,可变波特率/*使用定时器1作为波特率发生器并赋值 */TMOD &= 0x0f; //清除定时器1模式位TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1=TH1=256-(FOSC/Baud/32/12);//设定定时初值 设定定时器重装值ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1/*使用定时器2作为波特率发生器并赋值 *///TL2=RCAP2L=(65535-FOSC/(32*Baud)); //设定定时初值//TH2=RCAP2H=(65535-FOSC/(32*Baud))>>8;//设定定时器重装值// RCLK=1; //接收时钟标志,0:使用定时器1作为串口接收发生器 1:使用定时器2作为串口接收发生器// TCLK=1; //发送时钟标志,0:使用定时器1作为串口发送发生器 1:使用定时器2作为串口发送发生器// TR2=1; //启动定时期2/*启动串口中断 总中断*/ES=1;//串口使能EA=1;//总中断开 }
      大概看了一下,你这个程序问题可能就出在 static uchar i = 0; 这个i应该是全局变量吧,不然每次进入中断 i 都等于0,那都把数据赋值给数组的第一个元素了,所以最后只能得到发送的最后一个字节数据了
      51单片机接收上位机发送的多个字节的串口通信

      51单片机串口通信

      ////////////////////////////////////////////////////////////////////////////////////////////////// //E51Pro.c//Easy 51Pro编程器主程序,负责通讯,管理编程操作///////////////////////////////////////////////////////////////////////////////////////////////////#include BYTE ComBuf[18];//串口通讯数据缓存,发送和接收都使用UINT nAddress;//ROM中地址计数UINT nTimeOut;//超时计数ProWork pw;//编程器一般操作void Delay_us(BYTE nUs)//微秒级延时<255us{TH0=0;TL0=0;TR0=1;while(TL010000)//后17个字节都有超时限制return 0;}ComBuf[n]=SBUF;RI=0;}return 1;}BOOL WaitResp()//等待上位机回应,1字节,有超时限制{nTimeOut=0;RI=0;while(!RI){nTimeOut++;if(nTimeOut>50000){return 0;}}RI=0;ComBuf[0]=SBUF;return 1;}BOOL WaitData()//写器件时等待上位机数据,18字节,有超时限制{BYTE n;RI=0;for(n=0;n<=17;n++){nTimeOut=0;while(!RI){nTimeOut++;if(nTimeOut>10000){return 0;}}RI=0;ComBuf[n]=SBUF;}return 1;}void SendData()//发送数据或回应操作完成,18字节{BYTE n=0;for(n;n<=17;n++){TI=0;SBUF=ComBuf[n];while(!TI){}TI=0;}}void SendResp()//回应上位机1个字节,在写器件函数中使用{TI=0;SBUF=ComBuf[0];while(!TI){}TI=0;}void SetVpp5V()//设置Vpp为5v{P3_4=0;P3_3=0;}void SetVpp0V()//设置Vpp为0v{P3_3=0;P3_4=1;}void SetVpp12V()//设置Vpp为12v{P3_4=0;P3_3=1;}void RstPro()//编程器复位{pw.fpProOver();//直接编程结束SendData();//通知上位机,表示编程器就绪,可以直接用此函数因为协议号(ComBuf[0])还没被修改,下同}void ReadSign()//读特征字{pw.fpReadSign();SendData();//通知上位机,送出读出器件特征字}void Erase()//擦除器件{pw.fpErase();SendData();//通知上位机,擦除了器件}void Write()//写器件{BYTE n;pw.fpInitPro();//编程前的准备工作SendData();//回应上位机表示进入写器件状态,可以发来数据while(1){if(WaitData())//如果等待数据成功{if(ComBuf[0]==0x07)//判断是否继续写{for(n=2;n<=17;n++)//ComBuf[2~17]为待写入数据块{if(!pw.fpWrite(ComBuf[n]))//<<<<<<<<<<<<<<<<<<<调用写该器件一个单元的函数{pw.fpProOver();//出错了就结束编程ComBuf[0]=0xff;SendResp();//回应上位机一个字节,表示写数据出错了WaitData();//等待上位机的回应后就结束return;}nAddress++;//下一个单元}ComBuf[0]=1;//回应上位机一个字节,表示数据块顺利完成,请求继续SendResp();}else if(ComBuf[0]==0x00)//写器件结束break;else//可能是通讯出错了{pw.fpProOver();return;}}else//等待数据失败{pw.fpProOver();return;}}pw.fpProOver();//编程结束后的工作Delay_ms(50);//延时等待上位机写线程结束ComBuf[0]=0;//通知上位机编程器进入就绪状态SendData();}void Read()//读器件{BYTE n;pw.fpInitPro();//先设置成编程状态SendData();//回应上位机表示进入读状态while(1){if(WaitResp())//等待上位机回应1个字节{if(ComBuf[0]==0)//ComBuf[0]==0表示读结束{break;}else if(ComBuf[0]==0xff)//0xff表示重发{nAddress=nAddress-0x0010;}for(n=2;n<=17;n++)//ComBuf[2~17]保存读出的数据块{ComBuf[n]=pw.fpRead();//<<<<<<<<<<<<<<<<<<<调用写该器件一个单元的函数nAddress++;//下一个单元}ComBuf[0]=6;//向上位机发送读出的数据块SendData();}elsebreak;//等待回应失败}pw.fpProOver();//操作结束设置为运行状态ComBuf[0]=0;//通知上位机编程器进入就绪状态SendData();}void Lock()//写锁定位{pw.fpLock();SendData();}/////////////////////////////////////////////////////////////////////////////////////////////////////所支持的FID,请在这里继续添加///////////////////////////////////////////////////////////////////////////////////////////////////extern void PreparePro00();//FID=00:AT89C51编程器extern void PreparePro01();//FID=01:AT89C2051编程器extern void PreparePro02();//FID=02:AT89S51编程器void main(){SP=0x60;SetVpp5V();//先初始化Vpp为5vSCON=0x00;TCON=0x00;//PCON=0x00;//波特率*2IE=0x00;//TMOD: GATE|C/!T|M1|M0|GATE|C/!T|M1|M0//00 10 00 01TMOD=0x21;//T0用于延时程序TH1=0xff;TL1=0xff;//波特率28800*2,注意PCON//SCON: SM0|SM1|SM2|REN|TB8|RB8|TI|RI//0 1 0 1 0 000SCON=0x50;TR1=1;Delay_ms(1000);//延时1秒后编程器自举ComBuf[0]=0;SendData();while(1)//串口通讯采用查询方式{if(!WaitComm())//如果超时,通讯出错{Delay_ms(500);ComBuf[0]=0;//让编程器复位,使编程器就绪}switch(ComBuf[1])//根据FID设置(ProWork)pw中的函数指针{case 0://at89c51编程器PreparePro00();break;case 1://at89c2051编程器PreparePro01();break;case 2://at89s51编程器PreparePro02();break;//case 3:支持新器件时,请继续向下添加// break;//case 4:// break;default:ComBuf[0]=0xff;ComBuf[1]=0xff;//表示无效的操作break;}switch(ComBuf[0])//根据操作ID跳到不同的操作函数{case 0x00:RstPro();//编程器复位break;case 0x01:ReadSign();//读特征字break;case 0x02:Erase();//擦除器件break;case 0x03:Write();//写器件break;case 0x04:Read();//读器件break;case 0x05:Lock();//写锁定位break;default:SendData();break;}} }
      // 单片机串行口发送/接收程序,每接收到字节即发送出去 // 和微机相接后键入的字符回显示在屏幕上// 可用此程序测试//#include #define XTAL 11059200// CUP 晶振频率#define baudrate 9600// 通信波特率void main(void){unsigned char c;TMOD = 0x20; // 定时器1工作于8位自动重载模式, 用于产生波特率TH1=(unsigned char)(256 - (XTAL / (32L * 12L * baudrate)));TL1=(unsigned char)(256 - (XTAL / (32L * 12L * baudrate)));// 定时器0赋初值SCON = 0x50;PCON = 0x00;TR1 = 1;IE = 0x00;// 禁止任何中断while(1){while(RI == 0);RI = 0;c = SBUF;// 从缓冲区中把接收的字符放入c中SBUF = c;// 要发送的字符放入缓冲区while(TI == 0);TI = 0;} }
      void InitSerial(void) {TMOD = 0x20; // T1 方式2PCON=0x00;// PCON=00H,SMOD=0 PD = PCON.2 = 1 进入掉电模式TH1 = TL1 = BAUD_9600;// BAUD: 9600SCON = 0x50; // 串行通信方式1 REN=1 允许接收ET1 = 0;// 不允许中断TR1 = 1;// 开启定时器1IE = 0; // 关闭所有中断允许位memset(&SerialBuf, 0x00, SERIAL_BUF_LEN); // 初始化SerialBuf[SERIAL_BUF_LEN]}/**********************************************************名称:SendByte()**功能:串口发送一个字节**输入:ucData**返回:无**说明:无********************************************************/void SendByte(unsigned char ucData){SBUF = ucData;while(!TI){_CLRWDT_;}TI = 0; }
      #include #include unsigned char ch;bit read_flag= 0 ;void init_serialcom( void ) //串口通信初始设定{SCON = 0x50 ; //UART为模式1,8位数据,允许接收TMOD |= 0x20 ; //定时器1为模式2,8位自动重装PCON |= 0x80 ; //SMOD=1;TH1 = 0xFD ; //Baud:19200 fosc="11".0592MHzIE |= 0x90 ; //Enable Serial InterruptTR1 = 1 ; // timer 1 runTI=1;}//向串口发送一个字符void send_char_com( unsigned char ch){SBUF=ch;while (TI== 0);TI= 0 ;}//串口接收中断函数void serial () interrupt 4 using 3{if (RI){RI = 0 ;ch=SBUF;read_flag= 1 ; //就置位取数标志}}main(){init_serialcom(); //初始化串口while ( 1 ){if (read_flag) //如果取数标志已置位,就将读到的数从串口发出{read_flag= 0 ; //取数标志清0send_char_com(ch);}} }
      51单片机串口通信

      51单片机的串行口有几种工作方式?它们的帧格式是如何规定的?

      串行口分四种工作方式,由SM0、SM1二位决定,其定义如下: SM0、SM1 工作方式 功能描述 波特率 0 0 方式0 8位移位寄存器 Fosc/12 0 1 方式1 10位UART 可变 1 0 方式2 11位UART Fosc/64或fosc/32 1 1 方式3 11位UART 可变 (1)方式0:串行口的工作方式0为移位寄存器I/O方式,可外接移位寄存器,一扩展I/O口,也可外接同步I/O设备。发送操作:当执行一条“MOV SBUF,A”指令时,启动发送操作,由TXD输出移位脉冲,由RXD串行SBUF中的数据。发送完8位数据后自动置TI=1.请求中断。要继续发送时,TI必须有指令清零。接收操作:REN是串行口接收允许控制位。REN=0时禁止接收;REN=1时允许接收。当软件将REN置“1”时,即开始从RXD端口以fosc/12波特率输入数据,当接收到8位数据时,将中断标志RI置“1”。再次接收数据之前,必须用软件将RI清0。(2)方式1:串行口位10位通用异步接口。发送或接收一帧数据信息为10位,包括1位起始位“0”、8位数据位、1位停止位“1”。发送数据:数据从TXD端口输出,当数据写入发送缓冲器SBUF时,就启动发送器发送。发送完一帧数据后,置中断标志TI=1,申请中断,通知CPU可以发送下一个数据了。接收数据:首先使REN=1(允许接收数据),串行口从RXD接收数据,当采样到1至0跳变时,确认是起始位“0”,就开始接收一帧数据,当接收完一帧数据时,置中断标志RI=1,申请中断,通知CPU从SBUF取走接收到的数据(3)方式2:串行口为11位异步通信接口。发送或接收一帧信息包括1位起始位“0”、8位数据位、1位可编程位、1位停止位“1”。发送数据:发送前,先根据通信协议由软件设置TB8为“奇偶校验位”或“数据标识位”,然后将要发送的数据写入SBUF,即能启动发送器。发送过程是由执行任何一条以SBUF为目的寄存器的指令而启动的,把8位数据装入SBUF,同时还把TB8装到发送移位寄存器的第9位上,然后从TXD(P3.1)端口输出一帧数据。接收数据:先置REN=1,使串行口为允许接收状态,同时还要将RI清“0”。然后再根据SM2的状态和所接收到的RB8的状态决定此串行口在信息到来后是否置R1=1,并申请中断,通知CPU接收数据。当SM2=0时,不管RB8为“0”还是为“1”,都置RI=1,此串行口将接收发送来的信息。当SM2=1时,且RB8=1,表示在多机通信情况下,接收的信息为“地址帧”, 此时置RI=1, 串行口将接收发来的地址。当SM2=1时,且RB8=0,表示在多机通信情况下,接收的信息为“数据帧”, 但不是发给本从机的,此时RI不置为“1”,因而SBUF中接收的数据帧将丢失。 (4)方式3:为波特率可变的11位异步通信方式,除了波特率有所区别之外,其余方式都与方式2相同。
      串行口分四种工作方式,由SM0、SM1二位决定,其定义如下: SM0、SM1 工作方式 功能描述 波特率 0 0 方式0 8位移位寄存器 Fosc/12 0 1 方式1 10位UART 可变 1 0 方式2 11位UART Fosc/64或fosc/32 1 1 方式3 11位UART 可变 (1)方式0:串行口的工作方式0为移位寄存器I/O方式,可外接移位寄存器,一扩展I/O口,也可外接同步I/O设备。发送操作:当执行一条“MOV SBUF,A”指令时,启动发送操作,由TXD输出移位脉冲,由RXD串行SBUF中的数据。发送完8位数据后自动置TI=1.请求中断。要继续发送时,TI必须有指令清零。接收操作:REN是串行口接收允许控制位。REN=0时禁止接收;REN=1时允许接收。当软件将REN置“1”时,即开始从RXD端口以fosc/12波特率输入数据,当接收到8位数据时,将中断标志RI置“1”。再次接收数据之前,必须用软件将RI清0。(2)方式1:串行口位10位通用异步接口。发送或接收一帧数据信息为10位,包括1位起始位“0”、8位数据位、1位停止位“1”。发送数据:数据从TXD端口输出,当数据写入发送缓冲器SBUF时,就启动发送器发送。发送完一帧数据后,置中断标志TI=1,申请中断,通知CPU可以发送下一个数据了。接收数据:首先使REN=1(允许接收数据),串行口从RXD接收数据,当采样到1至0跳变时,确认是起始位“0”,就开始接收一帧数据,当接收完一帧数据时,置中断标志RI=1,申请中断,通知CPU从SBUF取走接收到的数据(3)方式2:串行口为11位异步通信接口。发送或接收一帧信息包括1位起始位“0”、8位数据位、1位可编程位、1位停止位“1”。发送数据:发送前,先根据通信协议由软件设置TB8为“奇偶校验位”或“数据标识位”,然后将要发送的数据写入SBUF,即能启动发送器。发送过程是由执行任何一条以SBUF为目的寄存器的指令而启动的,把8位数据装入SBUF,同时还把TB8装到发送移位寄存器的第9位上,然后从TXD(P3.1)端口输出一帧数据。接收数据:先置REN=1,使串行口为允许接收状态,同时还要将RI清“0”。然后再根据SM2的状态和所接收到的RB8的状态决定此串行口在信息到来后是否置R1=1,并申请中断,通知CPU接收数据。当SM2=0时,不管RB8为“0”还是为“1”,都置RI=1,此串行口将接收发送来的信息。当SM2=1时,且RB8=1,表示在多机通信情况下,接收的信息为“地址帧”, 此时置RI=1, 串行口将接收发来的地址。当SM2=1时,且RB8=0,表示在多机通信情况下,接收的信息为“数据帧”, 但不是发给本从机的,此时RI不置为“1”,因而SBUF中接收的数据帧将丢失。 (4)方式3:为波特率可变的11位异步通信方式,除了波特率有所区别之外,其余方式都与方式2相同。
      是com口吗一般是起始位 8位数据 停止位 编程的时候这样就ok了 每个数据位的时间你自己定 不过有规定的几种波特率 例如 19200bps每位的时间就是52us可以下载个串口调试助手
      51单片机的串行口有几种工作方式?它们的帧格式是如何规定的?

      单片机串口通信是否必须要建立通信协议?单片机之间通信是否也必须建立通信协议?

      单片机进行串口通信,需要设置波特率和一些电气参数(一般定义1BIT起始位,8BIT数据位,1BIT停止位,无奇偶校验位)这些就属于通信协议的范畴。再则,单片机接收的数据,需要进行处理,所以你必须要知道接收的数据代表的意义,这也是通信协议,只是不太严格的通信协议。而比较严格的通信协议则一般会定义起始字节,数据字节,校验字节,结束字节等等,这点对单片机来说到不是必须的,你只要知道接收到相应的数据,能够进行处理就好了。
      51单片机的话可以通过外接一个max232与电脑通信通信的协议都是硬件弄好了的,用户只需要用就行了;如果是51单片机之间的通讯可以直接把两块单片机的RXD和TXD交叉连接就可以 但是这样的传输距离比较近而且容易受到干扰,想要远距离传输还是需要用到232或者是422、485传输协议。说明:232的软件协议是单片机内部固化好的,硬件协议需要用到232芯片。
      不要协议收到的是一系列的数据流。但你对数据流的处理,必须有最简单的定义,如数据流的起始字节,数据流的长度,数据流的CRC检验。这就是协议。 如果两个单片机之间的通信内容能用一个字节来表达清楚,则不需要任何协议;就相当于两个单片机之间定义了255种状态而忆。 谢谢!
      双方必须约定:波特率、位数、奇偶校验的方式。 这些是最基本的串口通信协议。
      那是必须的,没有协议怎么知道收到的数是什么意思?
      单片机串口通信是否必须要建立通信协议?单片机之间通信是否也必须建立通信协议?

      本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/58682.html

          热门文章

          文章分类