51单片机的串口通信程序(C语言)
- 格式:docx
- 大小:37.59 KB
- 文档页数:5
现实生活中,我们总是要与人打交道,互通有无。
单片机也一样,需要跟各种设备交互。
例如汽车的显示仪表需要知道汽车的转速及电动机的运行参数,那么显示仪表就需要从汽车的底层控制器取得数据。
而这个数据的获得过程就是一个通信过程。
类似的例子还有控制器通常是单片机或者PLC与变频器的通信。
通信的双方需要遵守一套既定的规则也称为协议,这就好比我们人之间的对话,需要在双方都遵守一套语言语法规则才有可能达成对话。
通信协议又分为硬件层协议和软件层协议。
硬件层协议主要规范了物理上的连线,传输电平信号及传输的秩序等硬件性质的内容。
常用的硬件协议有串口,IIC,SPI,RS485,CAN和USB。
软件层协议则更侧重上层应用的规范,比如modbus协议。
好了,那这里我们就着重介绍51单片机的串口通信协议,以下简称串口。
串口的6个特征如下。
(1)、物理上的连线至少3根,分别是Tx数据发送线,Rx数据接收线,GND共用地线。
(2)、0与1的约定。
RS232电平,约定﹣5V至﹣25V之间的电压信号为1,﹢5V至﹢25V之间的电压信号为0 。
TTL电平,约定5V的电压信号为1,0V电压信号为0 。
CMOS电平,约定3.3V的电压信号为1,0V电压信号为0 。
其中,CMOS电平一般用于ARM芯片中。
(3)、发送秩序。
低位先发。
(4)、波特率。
收发双方共同约定的一个数据位(0或1)在数据传输线上维持的时间。
也可理解为每秒可以传输的位数。
常用的波特率有300bit/s, 600bit/s, 2400bit/s, 4800bit/s, 9600bit/s。
(5)、通信的起始信号。
发送方在没有发送数据时,应该将Tx置1 。
当需发送时,先将Tx置0,并且保持1位的时间。
接受方不断地侦测Rx,如果发现Rx常时间变高后,突然被拉低(置为0),则视为发送方将要发送数据,迅速启动自己的定时器,从而保证了收发双方定时器同步定时。
(6)、停止信号。
发送方发送完最后一个有效位时,必须再将Tx保持1位的时间,即为停止位。
51单片机与串口通信代码作者:佚名来源:本站原创点击数: 9407 更新时间:2007年06月17日【字体:大中小】1. 发送:向总线上发命令2. 接收:从总线接收命令,并分析是地址还是数据。
3. 定时发送:从内存中取数并向主机发送.经过调试,以上功能基本实现,目前可以通过上位机对单片机进行实时控制。
程序如下://这是一个单片机C51串口接收(中断)和发送例程,可以用来测试51单片机的中断接收//和查询发送,另外我觉得发送没有必要用中断,因为程序的开销是一样的#i nclude <reg51.h>#i nclude<stdio.h>#i nclude <string.h>#define INBUF_LEN 4 //数据长度unsigned char inbuf1[INBUF_LEN];unsigned char checksum,count3 , flag,temp,ch;bit read_flag=0;sbit cp=P1^1;sbit DIR=P1^2;int i;unsigned int xdata *RAMDATA; /*定义RAM地址指针*/unsigned char a[6] ={0x11,0x22,0x33,0x44,0x55,0x66} ;void init_serialcomm(void){SCON=0x50; //在11.0592MHz下,设置串行口波特率为9600,方式1,并允许接收 PCON=0x00;ES=1;TMOD=0x21; //定时器工作于方式2,自动装载方式TH0=(65536-1000)%256;TL0=(65536-1000)/256;TL1=0xfd;TH1=0xfd;ET0=1;TR0=1;TR1=1;// TI=0;EA=1;// TI=1;RAMDATA=0x1F45;}void serial () interrupt 4 using 3{if(RI){ RI=0;ch=SBUF;TI=1; //置SBUF空switch(ch){case 0x01 :printf("A"); TI=0;break;case 0x02 :printf("B"); TI=0;break;case 0x03 :printf("C"); TI=0;break;case 0x04 :printf("D"); TI=0;break;default :printf("fg"); TI=0;break;}}}//向串口发送一个字符void timer0() interrupt 1 using 3{// char i;flag++;TH0=0x00;TL0=0x00;if(flag==10){// cp=!cp;// for(i=0;i<6;i++)P2=0x25;TI=1;temp=*RAMDATA;printf("%c",temp);TI=0;// RAMDATA--;flag=0;}}//主程序main(){init_serialcomm(); //初始化串口//向6264中送数据{*RAMDATA=0x33;}while(1){*RAMDATA=0x33;;}}调试过程中遇到的问题:1. 发送过程:在发送时必须保证TI=1:即发送缓冲器为空,否则将导致数据发不出去,如果想强制发送可以用:T I=1.具体发送数据:利用printf(“akjdfaklfj”);函数直接发送即可。
现实生活中,我们总是要与人打交道,互通有无。
单片机也一样,需要跟各种设备交互。
例如汽车的显示仪表需要知道汽车的转速及电动机的运行参数,那么显示仪表就需要从汽车的底层控制器取得数据。
而这个数据的获得过程就是一个通信过程。
类似的例子还有控制器通常是单片机或者PLC与变频器的通信。
通信的双方需要遵守一套既定的规则也称为协议,这就好比我们人之间的对话,需要在双方都遵守一套语言语法规则才有可能达成对话。
通信协议又分为硬件层协议和软件层协议。
硬件层协议主要规范了物理上的连线,传输电平信号及传输的秩序等硬件性质的内容。
常用的硬件协议有串口,IIC,SPI,RS485,CAN和USB。
软件层协议则更侧重上层应用的规范,比如modbus协议。
好了,那这里我们就着重介绍51单片机的串口通信协议,以下简称串口。
串口的6个特征如下。
(1)、物理上的连线至少3根,分别是Tx数据发送线,Rx数据接收线,GND共用地线。
(2)、0与1的约定。
RS232电平,约定﹣5V至﹣25V之间的电压信号为1,﹢5V至﹢25V之间的电压信号为0 。
TTL电平,约定5V的电压信号为1,0V电压信号为0 。
CMOS电平,约定3.3V的电压信号为1,0V电压信号为0 。
其中,CMOS电平一般用于ARM芯片中。
(3)、发送秩序。
低位先发。
(4)、波特率。
收发双方共同约定的一个数据位(0或1)在数据传输线上维持的时间。
也可理解为每秒可以传输的位数。
常用的波特率有300bit/s, 600bit/s, 2400bit/s, 4800bit/s, 9600bit/s。
(5)、通信的起始信号。
发送方在没有发送数据时,应该将Tx置1 。
当需发送时,先将Tx置0,并且保持1位的时间。
接受方不断地侦测Rx,如果发现Rx常时间变高后,突然被拉低(置为0),则视为发送方将要发送数据,迅速启动自己的定时器,从而保证了收发双方定时器同步定时。
(6)、停止信号。
发送方发送完最后一个有效位时,必须再将Tx保持1位的时间,即为停止位。
#include <reg52.h>#include<intrins.h>#include <stdio.h>#include <math.h>#define uchar unsigned char#define uint unsigned intsbit Key1 = P2^3;sbit Key2 = P2^2;sbit Key3 = P2^1;sbit Key4 = P2^0;sbit BELL = P3^6;sbit CONNECT = P3^7;unsigned int Key1_flag = 0;unsigned int Key2_flag = 0;unsigned int Key3_flag = 0;unsigned int Key4_flag = 0;unsigned char b;unsigned char code Num[21]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00, 0x10,0x89};unsigned char code Disdigit[4] = {0x7F,0xBF,0xDF,0xEF};unsigned char Disbuf[4];void delayms(uint t){uint i;while(t--){/* 对于11.0592M时钟,约延时1ms */for (i=0;i<125;i++){}}}//-----------------------------------------------------void SendData(uchar Dat){uchar i=0;SBUF = Dat;while (1){if(TI){TI=0;break;}}}void ScanKey(){if(Key1 == 0){delayms(100); if(Key1 == 0){Key1_flag = 1; Key2_flag = 0; Key3_flag = 0;Key4_flag = 0;Key1 = 1;}else;}if(Key2 == 0){delayms(100);if(Key2 == 0){Key2_flag = 1; Key1_flag = 0; Key3_flag = 0;Key4_flag = 0;Key2 = 1;}else;}if(Key3 == 0){delayms(50);if(Key3 == 0){Key3_flag = 1; Key1_flag = 0; Key2_flag = 0;Key4_flag = 0;Key3 = 1;}else;}if(Key4 == 0){delayms(50);if(Key4 == 0){Key4_flag = 1;Key1_flag = 0;Key2_flag = 0;Key3_flag = 0;Key4 = 1;}else;}else;}void KeyProc(){if(Key1_flag){TR1 = 1;SendData(0x55);Key1_flag = 0; }else if(Key2_flag){TR1 = 1;SendData(0x11); Key2_flag = 0;}else if(Key3_flag) {P1=0xff;BELL = 0;CONNECT = 1;Key3_flag = 0;}else if(Key4_flag){CONNECT = 0;BELL = 1;Key4_flag = 0;}else;}void Initdisplay(void){Disbuf[0] = 1;Disbuf[1] = 2;Disbuf[2] = 3;Disbuf[3] = 4;}void Display() //显示{unsigned int i = 0;unsigned int temp,count;temp = Disdigit[count]; P2 =temp;temp = Disbuf[count];temp = Num[temp];P0 =temp;count++;if (count==4)count=0;}void time0() interrupt 1 using 2 {Display();TH0 = (65535 - 2000)/256;TL0 = (65535 - 2000)%256;}void main(){Initdisplay();TMOD = 0x21;TH0 = (65535 - 2000)/256;TL0 = (65535 - 2000)%256;TR0 = 1;ET0 = 1;TH1 = 0xFD; //11.0592MTL1 = 0xFD;PCON&=0x80;TR1 = 1;ET1 = 1;SCON = 0x40; //串口方式REN = 1;PT1 = 0;PT0 = 1;EA = 1;while(1){ScanKey();KeyProc();if(RI){Disbuf[0] = 0;Disbuf[1] = 20;Disbuf[2] = SBUF>>4;Disbuf[3] = SBUF&0x0f;RI = 0;}else;}}51单片机串口通信C语言程序2**************************************************************; 平凡单片机工作室;ckss.asm;功能:反复向主机送AA和55两个数;主机使用一个串口调试软件设置19200,n,8,1***************************************************************/#include "reg51.h"#define uchar unsigned char#define uint unsigned int//延时程序//////////////////由Delay参数确定延迟时间*/void mDelay(unsigned int Delay){ unsigned int i;for(;Delay>0;Delay--){ for(i=0;i<124;i++){;}}}//////////////////// 主程序////////////////////void main(){ uchar OutDat; //定义输出变量TMOD=0x20; //TMOD=0TH1=0xf3; //12MHZ ,BPS:4800,N,8,1TL1=0xf3;PCON=0x80; //方式一TR1=1; //?????????????????????????????SCON=0x40; //串口通信控制寄存器模式一OutDat=0xaa; //向串口发送固定数据值for(;;) //循环程序{SBUF=OutDat;//发送数据for(;;){ if(TI) //发送中断位当发送停止位时置1,表示发送完成break;}mDelay(500);TI=0; //清零中断位OutDat=~OutDat; //显示内容按位取反}}。
论坛新老朋友们。
祝大家新年快乐。
在新的一年开始的时候,给大家一点小小的玩意。
工程师经常碰到需要多个串口通信的时候,而低端单片机大多只有一个串行口,甚至没有串口。
这时候无论是选择高端芯片,还是更改系统设计都是比较麻烦的事。
我把以前搞的用普通I/O口模拟串行口通讯的程序拿出来,供大家参考,希望各位兄弟轻点拍砖。
基本原理:我们模拟的是串行口方式1.就是最普通的方式。
一个起始位、8个数据位、一个停止位。
模拟串行口最关键的就是要计算出每个位的时间。
以波特率9600为例,每秒发9 600个位,每个位就是1/9600秒,约104个微秒。
我们需要做一个精确的延时,延时时间+对IO口置位的时间=104微秒。
起始位是低状态,再延时一个位的时间。
停止位是高状态,也是一个位的时间。
数据位是8个位,发送时低位先发出去,接收时先接低位。
了解这些以后,做个IO模拟串口的程序,就是很容易的事。
我们开始。
先上简单原理图:就一个MAX232芯片,没什么好说的,一看就明白。
使用单片机普通I/ O口,232数据输入端使用51单片机P3.2口(外部中断1口,接到普通口上也可以,模拟中断方式的串行口会有用。
呵呵)。
数据输出为P0.4(随便哪个口都行)。
下面这个程序,您只需吧P0.4 和P3.2 当成串口直接使用即可,经过测试完全没有问题.2、底层函数代码如下:sbit TXD1 = P0^4; //定义模拟输出脚sbit RXD1 = P3^2; //定义模拟输入脚bdata unsigned char SBUF1; //定义一个位操作变量sbit SBUF1_bit0 = SBUF1^0;sbit SBUF1_bit1 = SBUF1^1;sbit SBUF1_bit2 = SBUF1^2;sbit SBUF1_bit3 = SBUF1^3;sbit SBUF1_bit4 = SBUF1^4;sbit SBUF1_bit5 = SBUF1^5;sbit SBUF1_bit6 = SBUF1^6;sbit SBUF1_bit7 = SBUF1^7;void delay_bps() {unsigned char i; for (i = 0; i < 29; i++); _nop_(); _nop_();} //波特率9600 模拟一个9600波特率unsigned char getchar2() //模拟接收一个字节数据{while (RXD1);_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); delay_bps();SBUF1_bit0 = RXD1; //0delay_bps();SBUF1_bit1 = RXD1; //1delay_bps();SBUF1_bit2 = RXD1; //2delay_bps();SBUF1_bit3 = RXD1; //3delay_bps();SBUF1_bit4 = RXD1; //4delay_bps();SBUF1_bit5 = RXD1; //5delay_bps();SBUF1_bit6 = RXD1; //6delay_bps();SBUF1_bit7 = RXD1; //7delay_bps();return(SBUF1) ; //返回读取的数据}void putchar2(unsigned char input) //模拟发送一个字节数据{SBUF1 = input;TXD1 = 0; //起始位delay_bps();TXD1 = SBUF1_bit0; //0delay_bps();TXD1 = SBUF1_bit1; //1delay_bps();TXD1 = SBUF1_bit2; //2delay_bps();TXD1 = SBUF1_bit3; //3delay_bps();TXD1 = SBUF1_bit4; //4delay_bps();TXD1 = SBUF1_bit5; //5delay_bps();TXD1 = SBUF1_bit6; //6delay_bps();TXD1 = SBUF1_bit7; //7delay_bps();TXD1 = 1; //停止位delay_bps();}3、实现串行通讯。
51单片机与串口通信代码在当今科技发展迅速的时代,嵌入式系统的应用越来越广泛。
而51单片机是一类常见的嵌入式控制器,它具有体积小巧、功耗低、价格便宜等特点,因此被广泛应用于各个领域。
而串口通信是实现单片机与计算机之间数据传输的常见方式,本文将介绍51单片机与串口通信的相关代码。
1. 串口通信概述串口通信是指通过串行接口,将数据一位一位地传输。
单片机通过串口与计算机或其他设备之间进行数据传输,实现信息的收发和控制指令的执行。
串口通信常用的协议包括RS232、RS485和UART等。
在51单片机中,一般选用UART协议。
2. 串口通信的硬件连接在使用51单片机与计算机进行串口通信时,需要进行相应的硬件连接。
首先,需要将单片机的串口引脚(一般是P3口)与计算机的串口(COM口)进行连接。
单片机的TXD引脚连接到计算机的RXD引脚,而单片机的RXD引脚连接到计算机的TXD引脚。
此外,还需要相连的地线进行电位的匹配。
3. 串口通信的软件设置在使用51单片机进行串口通信时,需要对单片机的串口进行相应的软件设置。
首先,需要设置波特率,波特率指每秒传送的位数,常见的波特率有9600、115200等。
通过设置相同的波特率,实现单片机与计算机之间的数据传输。
其次,还需要设置数据位、停止位和校验位等参数,以确保数据的正确传输。
4. 单片机发送数据的代码示例下面是一个简单的51单片机发送数据的代码示例:```c#include <reg51.h>void main() {TMOD = 0x20; // 配置定时器1为工作方式2TH1 = 0xFD; // 设置波特率为9600SCON = 0x50; // 允许串口工作TR1 = 1; // 启动定时器1while (1) {SBUF = 'A'; // 发送数据while (!TI); // 等待数据发送完毕TI = 0; // 清除发送标志位}}```5. 单片机接收数据的代码示例下面是一个简单的51单片机接收数据的代码示例:```c#include <reg51.h>void main() {TMOD = 0x20; // 配置定时器1为工作方式2TH1 = 0xFD; // 设置波特率为9600SCON = 0x50; // 允许串口工作TR1 = 1; // 启动定时器1while (1) {if (RI) { // 判断是否有数据接收P1 = SBUF; // 将接收到的数据存入P1口RI = 0; // 清除接收标志位}}}```6. 总结本文介绍了51单片机与串口通信代码的相关内容。
51单片机两机串口通信c程序(共2页)--本页仅作为文档封面,使用时请直接删除即可----内页可以根据需求调整合适字体及大小--发送机#include<>#define uchar unsigned char#define uint unsigned intuchar code xuehao1[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar code xuehao2[]={2,0,0,9,0,5,4,0,2,1,5,3};void send(uchar dat){SBUF=dat; //将待发送的数据写入发送缓存器中while(TI==0) //只要接收中断标志位; //空操作TI=0; //为了接收下一帧数据,需用软件降RI清零}void delay(){uchar m,n;for(m=0;m<200;m++)for(n=0;n<250;n++);}void main(){uchar i;TMOD=0x20;//定时器T1工作于方式2SCON=0x50;//串口工作方式1PCON=0x00;//电源控制寄存器,波特率不加倍TH1=0xfd; //波特率为 9600()TL1=0xfd;TR1=1; //启动定时器T1while(1){for(i=0;i<12;i++){send(xuehao1[xuehao2[i]]); //发送数据idelay();delay();delay();delay();}}}接受机#include<>#define uchar unsigned char#define uint unsigned intuchar receive(){uchar dat;while(RI==0); //只要接收中断标志位RI=0; //为了接收下一帧数据,需用软件降RI清零dat=SBUF; // 将接收缓存器中的数据存于datreturn dat; //将接收到的数据返回}void main(){TMOD=0x20;//定时器T1工作于方式2SCON=0x50;//串口工作方式1PCON=0x00;TH1=0xfd; //波特率为 9600()TL1=0xfd;TR1=1; //启动定时器T1REN=1; //允许接收while(1){P0=receive();}}。
51 单片机实现通讯协议的串口通讯编程摘要: 我们以51 单片机为例。
51 中一般针对串口通讯编程,通常采取中断接受查询发送的方式。
中断函数在接受数据到达时被重复调用,其实是个重复入栈的过程,所以不宜将函数写的太长,函数太长一般会导致栈太深占用系统资源,...我们以51 单片机为例。
51 中一般针对串口通讯编程,通常采取中断接受查询发送的方式。
中断函数在接受数据到达时被重复调用,其实是个重复入栈的过程,所以不宜将函数写的太长,函数太长一般会导致栈太深占用系统资源,二是处理时间过长,可能导致通讯出错。
为了防止在处理数据过程中不受干扰,通常在处理接受数据前关闭中断,处理完后再开。
通常的的编程方式如下:STatic void UartInterruptService(void)interrupt 4{ES = 0;RI = 0;uart_process(SBUF);ES=1;}下面重点介绍数据处理函数uart_process(SBUF);其实很多时候,对于通讯传输的数据处理才是关键,尤其对于设计通讯协议而言。
笔者在刚刚做的一个系统上就碰到这样的问题,当系统庞大了,资源十分有限的情况下,数据处理一旦占用资源太多,效率太低将导致系统崩溃而无法运行。
到了这里,很多工程师可能会考虑开个大的缓冲区FIFO 将接收到的数据保存在缓冲区,然后对其进行解析、判断进行下一步程序编写,当然这在系统资源比较丰富的情况下是没有问题的,ARM 上采取的就是这样的方式。
但如何系统庞大呢,留给的资源缺乏则不行。
这样做的一个很大缺点必须是将数据帧接收完了才能够判断,降低了效率和运行速度。
其实还有另外的方式,可以采取在每接收一个字节就对其解析,解析完判断转到下一个状态,并将其中的有用数据存储在相应的数据结构中去,可以采取状态机实现。
将状态机设计为两个控制状态,一是串口状态——uart_state ,一是命令类型状态——CMD_state 。
《51系列单片机_串口通信》源文件此程序使用单片机89SC52//1.说明:首先由上位机发送一个字符数据给单片机,单片机接收到字符数据后产生中断,由中断程序将接收到的字符存储到tmp中// 然后由单片机将字符串"Get the char: "和接收到的字符发送给上位机#include<reg52.h>char str[]=" Get the char: ", flag, tmp;//串行通信初始化void init(){ //设置通信波特率TMOD = 0x20; //设置T1定时器工作于方式2(8位计数自动重装)TH1 = TL1 = 0xfd; //此计数初值,使得晶振11.0592MHZ串口通信方式1产生波特率9600bps的通信速率TR1 = 1; //开启定时器1//设置串行通信模式SCON = 0x50; //01(设置串行口工作于10位数据异步通信) 01(允许串行口接收数据) 0000EA = 1; //开总中断ES = 1; //开串行口中断}//发送字符cvoid send(char c){SBUF = c; //将字符c存入SBUF中,由CPU自行发送while(!TI); //等待发送完毕,发送完成时TI会被置1TI = 0; //发送完成后将TI请0}//发送字符串strvoid sendS(char str[]){int i=0;while(str[i++]!=0) //字符串未到达结尾时,发送字符{SBUF = str[i]; //将字符str[i]存入SBUF中,由CPU自行发送while(!TI); //等待发送完毕,发送完成时TI会被置1TI = 0; //发送完成后将TI请0}}//使用串行口中断接收数据void ser_Int() interrupt 4 //串行口中断,接收完数据(RI置1) 或发送完数据(TI置1)时,产生中断{tmp = SBUF; //将接收到的数据放入tmp中保存flag = 1; //标志位置1,表示接收到新数据RI = 0; //接收完数据,清除中断标志}void main(){init(); //串行口初始while(1){if(flag==1) //当接收到新数据tmp时,将字符串str[]="Get the char: " 和tmp一起发送给上位机{ES = 0; //发送数据时暂时关闭串口中断,使得数据发送完成时不进入串口中断,串口中断在此程序中即用于接收数据sendS(str); //发送字符串strsend(tmp); //发送字符tmpES = 1; //数据发送完成重启串口中断flag = 0; //标志位清0}}}//2.串口通信应用秒表简单记录秒数值//说明:上位机发送一个字符数据'S'或者'C'给单片机,单片机根据接收到的字符数据控制定时器T0进行计时和暂停操作,并将计数值用数码管加以显示#include<reg52.h>#define iniNum 45872 //晶振频率为11.0592计时50ms的计数值int num, count;char str[]=" 'S' to start or stop the timer; 'C' to continue timing ", flag, tmp;void delay_ms(unsigned int n){ //软件延时函数,延时n毫秒unsigned int i, j;for(i=n;i>0;i--)for(j=110;j>0;j--);}void display(int num,int rep) //控制数码管按位输出显示数值num,显示时间为2*rep 毫秒{char BitSet[8] ={0x7f, 0xbf, 0xdf, 0xef,0xf7, 0xfb, 0xfd, 0xfe}; //用于设置(低电平位选)数码管的位选信号,从低到高对应8个数码管char NumberCode[16] ={0x3f, 0x06, 0x5b, 0x4f,0x66, 0x6d, 0x7d, 0x07,0x7f, 0x6f, 0x77, 0x7c,0x39, 0x5e, 0x79, 0x71,}; //用于设置(共阴极)数码管的段选信号,从0~f共16个数值int n, r = rep, i;while(r-- > 0){n = num;i = 0;while(n>=0){P0 = 0xff; //关闭数码管当前位的显示,共阳极关闭显示段选信号P2 = BitSet[i]; //选中数码管对应的位P0 = ~NumberCode[n%10]; //向数码管的对应位中送入该位数值对应的段选信号delay_ms(2); //每一位延时显示2msP2 = 0xff; //关闭所有位选i++; //位标记n=n/10; //取数值n的商if(n==0) break; //当取得的商为0时退出while循环}}}//发送字符cvoid send(char c){SBUF = c; //将字符c存入SBUF中,由CPU自行发送while(!TI); //等待发送完毕,发送完成时TI会被置1TI = 0; //发送完成后将TI请0}//发送字符串strvoid sendS(char str[]){int i=0;while(str[i++]!=0) //字符串未到达结尾时,发送字符{SBUF = str[i]; //将字符str[i]存入SBUF中,由CPU自行发送while(!TI); //等待发送完毕,发送完成时TI会被置1TI = 0; //发送完成后将TI请0}}void int_T0() interrupt 1 //定时器T0中断,此程序中用于计时{count++;TH0=(65536-iniNum)/256;TL0=(65536-iniNum)%256;}//使用串行口中断接收数据void ser_Int() interrupt 4 //串行口中断,接收完数据(RI置1) 或发送完数据(TI置1)时,产生中断{tmp = SBUF; //将接收到的数据放入tmp中保存flag = 1; //标志位置1,表示接收到新数据RI = 0; //接收完数据,清除中断标志}void main(){TMOD = 0x21; //设置T1定时器工作于方式2(8位计数自动重装),T0定时器工作于方式方式1(16位定时计数)//设置通信波特率TH1 = TL1 = 0xfd; //此计数初值,使得晶振11.0592MHZ串口通信方式1产生波特率9600bps的通信速率TR1 = 1; //开启定时器1//设置串行通信模式SCON = 0x50; //01(设置串行口工作于10位数据异步通信) 01(允许串行口接收数据) 0000EA = 1; //开总中断ES = 1; //开串行口中断//为定时器0装入初值TH0=(65536-iniNum)/256;TL0=(65536-iniNum)%256; //计iniNum数,每次计时50msET0=1; //开定时器0中断ES = 0; //发送数据时暂时关闭串口中断sendS(str); //发送操作提示字符串,给出提示信息ES = 1; //数据发送完成重启串口中断while(1){if(flag==1) //当接收到新数据tmp时{ES = 0; //发送数据时暂时关闭串口中断,使得数据发送完成时不进入串口中断,串口中断在此程序中即用于接收数据//开始计时if(tmp=='S' | tmp=='s'){if(TR0==0) //当计时器停止时启动计时{num=0; //计数值清0,计数器重装初值TH0=(65536-iniNum)/256;TL0=(65536-iniNum)%256;TR0=1; //启动定时器0,进行计时sendS(" the tiner started ");}if(TR0==1) //当计时器已经启动时{TR0=0; //暂停计时sendS(" the tiner paused ");}}//继续计时else if(tmp=='C' | tmp=='c'){TR0=1; //启动定时器0,继续计时sendS(" tiner continue ");}else sendS(str);//发送操作提示字符串ES = 1; //数据发送完成重启串口中断flag = 0; //标志位清0}if(count==20) //每20次中断即每1s,处理一次{num++; //显示数值加1count=0;P1=~P1; //指示灯状态取反}display(num, 1); //用数码管输出显示num}}//3.串口通信应用秒表按时分秒格式显示//说明:上位机发送一个字符数据'S'或者'C'给单片机,单片机根据接收到的字符数据控制定时器T0进行计时和暂停操作,并将计数值用数码管加以显示#include<reg52.h>#define iniNum 45872 //晶振频率为11.0592计时50ms的计数值int count, time[4]=0;char str[]=" 'S' to start or stop the timer; 'C' to continue timing ", flag, tmp;void delay_ms(unsigned int n){ //软件延时函数,延时n毫秒unsigned int i, j;for(i=n;i>0;i--)for(j=110;j>0;j--);}void timer_display(int time[], int rep)//控制数码管按位输出显示time数组中的时分秒值数值,显示时间为2*rep 毫秒{char BitSet[8] ={0x7f, 0xbf, 0xdf, 0xef,0xf7, 0xfb, 0xfd, 0xfe}; //用于设置(低电平位选)数码管的位选信号,从低到高对应8个数码管char NumberCode[16] ={0x3f, 0x06, 0x5b, 0x4f,0x66, 0x6d, 0x7d, 0x07,0x7f, 0x6f, 0x77, 0x7c,0x39, 0x5e, 0x79, 0x71,}; //用于设置(共阴极)数码管的段选信号,从0~f共16个数值int n, r = rep, i, j, zero=0;while(r-- > 0){i = 0;for(j=3; j>=0; j--){n = time[j];if((j==2|j==1|j==0) & time[j]<10) zero=1;//当时、分、秒三位的数值小于10时,显示对应数值后在高位添加一个0while(n>=0){P0 = 0xff; //关闭数码管当前位的显示,共阳极关闭显示段选信号P2 = BitSet[i]; //选中数码管对应的位if(i==1|i==3|i==5){ //在数码管倒数第1,3,5(时,分,秒)三个位置的数值后加上小数点P0 = ~(NumberCode[n%10]+0x80);}else{ //向数码管的对应位中送入该位数值对应的段选信号P0 = ~NumberCode[n%10];}delay_ms(2); //每一位延时显示2msP2 = 0xff; //关闭所有位选i++; //位标记n=n/10; //取数值n的商if(n==0) break;//当取得的商为0时退出while循环}if(zero==1) //在数码管高位添加0{P0 = 0xff; //关闭数码管显示P2 = BitSet[i]; //选中数码管对应的位P0 = ~NumberCode[0]; //显示数值0delay_ms(2); //每一位延时显示2msP2 = 0xff; //关闭所有位选i++; //位标记zero=0;}}}}//发送字符cvoid send(char c){SBUF = c; //将字符c存入SBUF中,由CPU自行发送while(!TI); //等待发送完毕,发送完成时TI会被置1 TI = 0; //发送完成后将TI请0}//发送字符串strvoid sendS(char str[]){int i=0;while(str[i++]!=0) //字符串未到达结尾时,发送字符{SBUF = str[i]; //将字符str[i]存入SBUF中,由CPU自行发送while(!TI); //等待发送完毕,发送完成时TI会被置1TI = 0; //发送完成后将TI请0}}void int_T0() interrupt 1 //定时器T0中断,此程序中用于计时{count++;TH0=(65536-iniNum)/256;TL0=(65536-iniNum)%256;}//使用串行口中断接收数据void ser_Int() interrupt 4 //串行口中断,接收完数据(RI置1) 或发送完数据(TI置1)时,产生中断{tmp = SBUF; //将接收到的数据放入tmp中保存flag = 1; //标志位置1,表示接收到新数据RI = 0; //接收完数据,清除中断标志}void main(){TMOD = 0x21; //设置T1定时器工作于方式2(8位计数自动重装),T0定时器工作于方式方式1(16位定时计数)//设置通信波特率TH1 = TL1 = 0xfd; //此计数初值,使得晶振11.0592MHZ串口通信方式1产生波特率9600bps的通信速率TR1 = 1; //开启定时器1//设置串行通信模式SCON = 0x50; //01(设置串行口工作于10位数据异步通信) 01(允许串行口接收数据) 0000EA = 1; //开总中断ES = 1; //开串行口中断//为定时器0装入初值TH0=(65536-iniNum)/256;TL0=(65536-iniNum)%256; //计iniNum数,每次计时50msET0=1; //开定时器0中断ES = 0; //发送数据时暂时关闭串口中断sendS(str); //发送操作提示字符串,给出提示信息ES = 1; //数据发送完成重启串口中断while(1){if(flag==1) //当接收到新数据tmp时{ES = 0; //发送数据时暂时关闭串口中断,使得数据发送完成时不进入串口中断,串口中断在此程序中即用于接收数据//开始计时if(tmp=='S' | tmp=='s'){if(TR0==0) //当计时器停止时启动计时{int i;for(i=0; i<4;i++) time[i]=0; //计数值清0,计数器重装初值TH0=(65536-iniNum)/256;TL0=(65536-iniNum)%256;TR0=1; //启动定时器0,进行计时sendS(" the tiner started ");}if(TR0==1) //当计时器已经启动时{TR0=0; //暂停计时sendS(" the tiner paused ");}}//继续计时else if(tmp=='C' | tmp=='c'){TR0=1; //启动定时器0,继续计时sendS(" tiner continue ");}else sendS(str);//发送操作提示字符串ES = 1; //数据发送完成重启串口中断flag = 0; //标志位清0}if(count==2) //每2次中断即每0.1s,处理一次{time[4]++; //秒表最低位(1/10秒)值加1if(time[4]==10) //进位{time[4]=0; //清零time[3]++; //秒进位if(time[3]==60){time[3]=0;time[2]++; //分进位if(time[2]==60){time[2]=0;time[1]++; //时进位}}}count=0;}timer_display(time, 1); //用数码管输出显示秒表计数值}}。
C51单片机串口通讯通用模块代码(可用于操作系统串口通讯)#include#include "UART1.h"#include "commdriver.h"//当前适用于C51//可以根据具体CPU型号,修改宏定义和串口初始化代码就可以#define CPU_XTAL 22118400 //CPU频率#define FUNCTION_NULL 0 //没有定义函数#define COM_TI TI //发送中断寄存器#define COM_TI0() {TI = 0;} //发送中断寄存器清零#define COM_TI1() {TI = 1;} //发送中断寄存器置一( 强行置一触发发送中断)#define COM_TI_SBUF(dat) {SBUF = dat;} //发送数据到硬件缓冲区#define COM_TI_End() {return;} //发送结束处理函数#define COM_RI RI //接收中断寄存器#define COM_RI0() {RI = 0;} //接收中断寄存器清零#define COM_RI1() {RI = 1;} //接收中断寄存器置一#define COM_RI_SBUF(dat) {dat = SBUF;} //提取接收硬件缓冲区数据#define COM_TI_FLAG COM_TI //发送中断标志寄存器(非中断发送方式使用)#define COM_TI_FLAG0() COM_TI0() //发送中断标志寄存器清零(非中断发送方式使用)void (*COM1RevEvent)(unsigned char dat); //数据接收事件#define LenTxBuf 30 //发送缓冲区长度#define LenRxBuf 1 //接收缓冲区长度volatile unsigned char TxBuf1[LenTxBuf],RxBuf1[LenRxBuf]; //收发缓冲区实体volatile unsigned char *inTxBuf1,*outTxBuf1,*inRxBuf1,*outRxBuf1; //收发缓冲区读写指针volatile unsigned char TIflag1=1;//Note:It must be 1. //发送缓冲区为空标志//*********************#define _TxBuf TxBuf1#define _RxBuf RxBuf1#define _inTxBuf inTxBuf1#define _outTxBuf outTxBuf1#define _inRxBuf inRxBuf1#define _outRxBuf outRxBuf1#define _TIflag TIflag1/*************函数声明****************/#define _COMRevEvent COM1RevEvent //串口接收事件#define _USART_IRQ void USART1_IRQHandler(void) interrupt 4 //串口中断服务程序#define _COM_Buffer_Init void COM1_Buffer_Init(void) //串口缓冲区初始化#define _COM_Open void COM1_Open(unsigned int baudrate,void (*revevent)(unsigned char dat)) // 串口初始化#define _COM_Close void COM1_Close(void) //关闭串口#define _COM_GetOneByte unsigned charCOM1_GetOneByte(unsigned char *ch) //获取一个字节#define _COM_GetPChar unsigned char COM1_GetPChar(unsigned char *ch,unsigned char len) //获取指定长度字节数组#define _COM_RxByte unsigned char COM1_RxByte(void) //获取接收字节个数//********缓冲区中断方式发送(安全性高)#define _COM_SendOneByte unsigned char COM1_SendOneByte(unsigned char one_byte) // 发送一个字节#define _COM_SendPChar void COM1_SendPChar(unsigned char *P,unsigned char Len) //发送定长字节数组#define _COM_SendString void COM1_SendString(unsigned char *P) //发送字符串//********非缓冲区中断方式发送#define _COM_PrintOneByte void COM1_PrintOneByte(unsigned char c) // 发送一个字节#define _COM_PrintPChar void COM1_PrintPChar(unsigned char *buf,unsigned int len) //发送定长字节数组#define _COM_PrintString void COM1_PrintString(unsigned char *P) //发送字符串//*************内部引用模型函数(外部不关心,移植时修改后面函数名,要与上面对应的外部声明的函数名一致)*******************#define COM_GetOneByte_(ptr) COM1_GetOneByte(ptr) //与上面获取一个字节的函数对应#define COM_SendOneByte_(dat) COM1_SendOneByte(dat) //与上面发送一个字节的函数对应#define COM_PrintOneByte_(dat) COM1_PrintOneByte(dat) //与上面发送一个字节的函数对应//*********************函数模型定义区(不需要修改)*****************/****************************函数模型:void USART_IRQHandler(void)函数功能:串口中断服务程序入口参数:无返回值:无修改者:修改时间:修改内容简要:***************************/_USART_IRQ //串口1中断{volatile unsigned char *t;if(COM_RI){COM_RI0(); //清接收标记if(_COMRevEvent != FUNCTION_NULL) //自定义接收事件{COM_RI_SBUF(*_inRxBuf);_COMRevEvent(*_inRxBuf);}else{t = _inRxBuf;t++;if(t == (_RxBuf + LenRxBuf)) t = _RxBuf;if(t != _outRxBuf) //RxBuf No Full{COM_RI_SBUF(*_inRxBuf);_inRxBuf = t;}}}if(COM_TI){COM_TI0();if(_inTxBuf == _outTxBuf) {_TIflag = 1;COM_TI_End();};//TxBuf1 EmptyCOM_TI_SBUF(*_outTxBuf); _outTxBuf++;if(_outTxBuf == (_TxBuf+LenTxBuf)) _outTxBuf = _TxBuf;}}/****************************函数模型:void COM1_Buffer_Init(void)函数功能:串口1缓冲区初始化入口参数:无返回值:无修改者:修改时间:修改内容简要:***************************/_COM_Buffer_Init{_inTxBuf = _TxBuf;_outTxBuf = _TxBuf;_inRxBuf = _RxBuf;_outRxBuf = _RxBuf;}/****************************函数模型:void COM_Open(unsigned int baudrate,void (*revevent)(unsigned char dat))函数功能:系统串口初始化入口参数: unsigned int baudrate:串口波特率返回值:无修改者:修改时间:修改内容简要:***************************/_COM_Open{//定时器1做波特率发生器TI = 0; /* clear transmit interrupt */TR1 = 0; /* stop timer 1 */ET1 = 0; /* disable timer 1 interrupt */PCON |= 0x80; /* 0x80=SMOD: set serial baudrate doubler */ TMOD &= ~0xF0; /* clear timer 1 mode bits */TMOD |= 0x20; /* put timer 1 into MODE 2 */TH1 = (unsigned char) (256 - (CPU_XTAL / (16L * 12L * baudrate)));TR1 = 1; /* start timer 1 *///设置串口1模式SM0 = 0; SM1 = 1; /* serial port MODE 1 */SM2 = 0;REN = 1; /* enable serial receiver */RI = 0; /* clear receiver interrupt */TI = 0; /* clear transmit interrupt */ES = 1; /* enable serial interrupts */PS = 1; /* set serial interrupts to low priority */_COMRevEvent = revevent;}/****************************函数模型:void COM1_Close(void)函数功能:关闭系统串口入口参数:无返回值:无修改者:修改时间:修改内容简要:***************************/_COM_Close{}/****************************函数模型:unsigned char COM1_GetOneByte(unsigned char *ch)函数功能:获取一个字节入口参数:unsigned char *ch:接收字节指针返回值: unsigned char *ch:接收字节指针unsigned char :获取状态;0:成功,1:失败(缓冲区为空)修改者:修改时间:修改内容简要:***************************/_COM_GetOneByte{if(_inRxBuf == _outRxBuf) {return 0;}; //RxBuf Empty*ch = *_outRxBuf; _outRxBuf++;if(_outRxBuf==_RxBuf+LenRxBuf) _outRxBuf = _RxBuf;return 1;}/****************************函数模型:unsigned char COM_GetPChar(unsigned char *ch,unsigned char len)函数功能:获取指定长度字节数组入口参数: unsigned char *ch:接收字节数组指针unsigned char len:接收字节个数返回值: unsigned char *ch:接收字节数组指针unsigned char:实际接收字节个数修改者:修改时间:修改内容简要:***************************/_COM_GetPChar{unsigned char i = 0;do{len--;if(!COM_GetOneByte_(ch)) break;i++;ch++;}while(len);return i;}/****************************函数模型:unsigned char COM_RxByte(void)函数功能:获取接收缓冲区有效字节个数入口参数:无返回值: unsigned char:接收缓冲区有效字节个数修改者:修改时间:修改内容简要:***************************/_COM_RxByte{if(_inRxBuf>=_outRxBuf) return (_inRxBuf-_outRxBuf);else return LenRxBuf-(_outRxBuf-_inRxBuf);}/****************************函数模型:unsigned char COM_SendOneByte(unsigned char one_byte)函数功能:发送一个字节入口参数: unsigned char one_byte:发送的字节返回值:unsigned char:发送状态,0:成功,1:失败(缓冲区满)修改者:修改时间:修改内容简要:***************************/_COM_SendOneByte{volatile unsigned char *t;if(_TIflag){_TIflag = 0;COM_TI1();}t = _inTxBuf;t++;if(t == _TxBuf + LenTxBuf) t = _TxBuf;if(t == _outTxBuf) {return 1;};//TxBuf Full*_inTxBuf = one_byte;_inTxBuf = t;return 0;}/****************************函数模型:void COM_SendPChar(unsigned char *P,unsigned char Len)函数功能:发送定长字节数组入口参数: unsigned char *P:字节数组指针unsigned char Len:发送长度返回值:无修改者:修改时间:修改内容简要:***************************/_COM_SendPChar{while(Len){//while(COM1_SendOneByte(*P)); //发送失败,继续发送,知道发送成功COM_SendOneByte_(*P);P++;Len--;}}/****************************函数模型:void COM_SendString(unsigned char *P)函数功能:发送字符串入口参数: unsigned char *P:字符串指针返回值:无修改者:修改时间:修改内容简要:***************************/_COM_SendString{while(*P){COM_SendOneByte_(*P);P++;}}//**************** 非缓冲区中断方式发送/****************************函数模型:void COM_PrintOneByte(unsigned char c)函数功能:发送一个字节入口参数: unsigned char one_byte:发送的字节返回值:无修改者:修改时间:修改内容简要:***************************/_COM_PrintOneByte{COM_TI_FLAG0();COM_TI_SBUF(c); //发送数据while(!COM_TI_FLAG) ; //等待发送结束}/****************************函数模型:void COM_PrintPChar(unsigned char *buf,unsigned int len)函数功能:发送定长字节数组入口参数: unsigned char *P:字节数组指针unsigned char Len:发送长度返回值:无修改者:修改时间:修改内容简要:***************************/_COM_PrintPChar{while(len){COM_PrintOneByte_(*buf);buf ++;len--;}}/****************************函数模型:void COM_PrintString(unsigned char *P) 函数功能:发送字符串入口参数: unsigned char *P:字符串指针返回值:无修改者:修改时间:修改内容简要:***************************/_COM_PrintString{while(*P){COM_PrintOneByte_(*P);P++;}}。
8. 结构实例6:单片机串口通信虽然那个流水灯游戏的可玩性和按键手感问题还值得再好好提升一下,但小月更希望调剂一下,转而开始了对手头烧写板上关于RS-232转换部分的学习。
小月的做法并不难以理解,毕竟与RS-232转换的相关电路在原理图中还是相当显眼的,甚至于他手头编程器的别名就是RS-232转换器。
图8.1 单片机中负责RS-232通讯的电路在烧写器一端与电脑连接的两个接头中,9针的RS-232接口就是串口通信线,而另一个USB口仅接通了+5V和GND,只有给烧写器供电的作用。
这样就可以知道,电脑可以通过RS-232对单片机的内部程序进行改写。
那么,这就意味着单片机与电脑间必然可以进行数据的交换,这种交换,就叫做通信。
所谓串口通信,就是指这种基于RS-232串口的通信方式。
RS-232(ANSI/EIA-232标准)是IBM-PC及其兼容机上的串行连接标准。
最早是为使电脑通过电话线系统相互通信的调制解调器上而是设计的。
后来发展到连接鼠标或打印机上,目前已经被支持设备的即插即用和热插拔功能的USB所替代,但仍广泛的用于工业仪器仪表中,同时也是单片机最基础和最常见的通信方式。
不过要把“最基础和最常见”这两个最拆开来说,就要在后面加上“之一”了。
虽然目前的通信技术日新月异,但这种说法在今后很长一段时期内都是成立的,也正因为这样的特点,STC的51系列单片机都是默认通过RS-232方式进行烧写的。
作为两台设备之间进行的通信,必然需要共同遵守某种规定或规则,包括交流什么、怎样交流及何时交流。
这个规则就是通信协议。
RS-232通信中通信协议的原则就是串口按位(bit)发送和接收数据。
线路上,RS-232通信使用3根线完成,分别是地线、发送、接收。
端口能够在一根线上发送数据的同时在另一根线上接收数据,即全双工传输。
全双工传输是传输制式的一种分类方式中的一类,除此还有单工传输和半双工传输。
单工传输,是指消息只能单方向传输的工作方式。
单片机串口通信程序创作:欧阳化#inelude <reg52.h> #include<intrins.h>#include<stdio.h>#include <math.h>#define uchar unsigned char #define uint unsigned int sbit Keyl = P2A3;sbit Key2 = P2A2;sbit Key3 = P2A1;sbit Key4 = P2A0;sbit BELL = P3A6;sbit CONNECT = P3A7;unsigned int Keyl_flag = 0;iinsigned int Key2_flag = 0;unsigned int Key3_flag = 0;unsigned int Key4_flag = 0;unsigned charb;unsigned char codeNum[21] = {0xc0,0xf9,0xa4, OxbO, 0x99,0x92,0x82z 0xf8,0x80,0x90,0x40,0x79,0x24,0x30,0x19,0x12, 0x02,0x78,0x00, 0x10,0x89}; unsigned char code Disdigit[4]= {0x7F,0xBF,0xDF,0xEF};unsignedchar Disbuf[4];void delayms(uint t){ uint i; while(t--) { /* 对于11.0592M 时钟,约延时 1ms */ for (i=0;i<125;i + +) {} }}//void SendData(uchar Dat){uchar i=0;SBUF = Dat;while(1) { if(TI) { H=o ; break; } }} void SeanKey(){ if(Keyl ==0) { delayms(lOO); if(Keyl ==0) { Keyljlag = 1; Key2Jlag = 0; Key3Jlag = 0; Key4Jlag = 0; Keyl =1;} else; } if(Key2 == 0) { delayms(lOO); if(Key2 ==0) { Key2Jlag = 1; Keyljlag = 0; Key3Jlag = 0; Key4Jlag = 0; Key2 =1;} else; } if(Key3 == 1; SendData(0x55); Keyljlag = 0; } else0) { delayms(50); if(Key3 == 0) { =1; Keyl_flag = 0; Key2_flag = 0; 0; Key3 = 1; }else; } if(Key4 ==0) { delayms(50); if(Key4 == 0) { =1; Keyl_flag = 0; Key2_flag = 0;0; Key4 = 1; } else; } else; }voidKeyProc(){ if(Keyl_flag) { TRI =Key3JlagKey4_flag = Key4JlagKey3Jlag =if(Key2_flag) { TRI =1; SendData(Oxll); Key2_flag = 0; } elseif(Key3_flag) { Pl=Oxff; BELL = 0; CONNECT =1; Key3_flag = 0; ) else if(Key4_flag) { CONNECT =0; BELL = 1; Key4_flag = 0; } else; }void Initdisplay(void){Disbuf[0] = l;Disbuf[l] = 2;Disbuf[2]=3;Disbuf[3] = 4;}void DisplayO 〃显示{unsigned int i = 0;unsignedint temp,count; temp = Disdigit[count]; P2=temp; temp = Disbuf[count]; temp =Num [temp]; P0=temp; count++; if (count==4) count =0; ) void timeOO interrupt 1 using2{ DisplayO; THO = (65535 ・ 2000)/256; TLO = (65535 ・2000)%256;} void main(){Initdisplay();TMOD = 0x21;TH0 = (65535 ・ 2000)/256;TL0 = (65535 ・ 2000)%256;TR0 = 1;ETO = 1; TH1 = OxFD;//11.0592MTL1 = OxFD;PCO N&=0x80;TRl = 1;ET1 = 1;SCON = 0x40; 〃串口方式REN = 1; PT1 = O;PTO = 1;EA =1;while(l) { ScanKeyO; KeyProc(); if(RI) { D isbuf[O] = 0; Disbuf[l] = 20; Disbuf[2]=SBUF>>4; Disbuf[3]二SBUF&0x0f; RI = 0; } else; }}。
本文以51单片机控制四路开关量输入/六路开关量输出为例,设计了一个简单的上位机下位机通信作品,作为模块学习和整理,供读者参考!难为,2010-11-28,于上海!四路开关量输入/六路开关量输出C语言程序#include<reg51.h>#define uint unsigned int#define uchar unsigned charuchar indata[4];uchar outdata[4];volatile uchar temp1 = 0,temp2 = 0;uchar i,j;void initial_comm(void){SCON = 0x50; //设定串行口工作方式:mode 1 ; 8-bit UART,enable ucvrTMOD = 0x21; //TIMER 1;mode 2 ;8-Bit ReloadPCON = 0x80; //波特率不加倍SMOD = 1TH1 = 0xfa; //baud: 9600;fosc = 11.0596IE = 0x90; // enable serial interruptTR1 = 1; // timer 1}void uart_send(void){ES = 0;if(indata[3] == indata[0]^indata[1]^indata[2]){if(indata[1]== 0x01){temp1 = P0;temp1 = temp1&0x0f;outdata[0] = 0xee;outdata[1] = 0x02;outdata[2] = temp1;outdata[3] = outdata[0]^outdata[1]^outdata[2];for(i=0;i<4;i++){SBUF = outdata[i];while(TI == 0);TI = 0;}}if(indata[1]== 0x03){temp2 = indata[2];P1 = temp2;}if(indata[1]==0x04){outdata[0] = 0xee;outdata[1] = 0x05;outdata[2] = temp2;outdata[3] = outdata[0]^outdata[1]^outdata[2];for(i=0;i<4;i++){SBUF = outdata[i];while(TI == 0);TI = 0;}}}ES = 1;}void main(){initial_comm();ES = 1;while(1){uart_send();}}void uart_receive(void) interrupt 4{RI = 0;for(j=0;j<4;j++){indata[j] = SBUF;while(RI==0);RI =0;}}四路开关量输入/六路开关量输出通讯协议通信设置:波特率9600b/s1位起始位8位数据位无校验位1位停止位命令格式:区别码(1字节)+ Command(1字节)+ Content(1字节)+ Xor(1字节)区别码:上位发送/单片机接收:0xff单片机发送/上位机接收:0xeeCommand = 0x01上位机命令单片机读取四路输入开关量的输入状态如:0xff + 0x01 + 0x00 + XorCommand = 0x02单片机返回四路输入状态给上位机此时,Content 的1字节中的低四位分别表示四路开关量的输入状态1表示输入高电平,0表示输入低电平第一路输入(第0位)第二位输入(第1位)第三路输入(第2位)第四路输入(第3位)如:假设某一时刻四路输入开关量的输入状态为0x03,上位机读取其状态为:0xee + 0x02 + 0x03 + XorCommand = 0x03上位机命令单片机控制六路输出的输出状态此时,Content 的1字节中的低六位分别表示六路开关量的输出状态1表示输出高电平,0表示输出低电平第一路输出(第0位)第二路输出(第1位)第三路输出(第2位)第四路输出(第3位)第五路输出(第4位)第六路输出(第5位)如:上位机欲设置某一时刻六路输出开关量的输出状态为0x01:0xff + 0x03 + 0x01 + XorCommand = 0x04上位机命令单片机读取六路输出开关量的输出状态如:0xff + 0x04 + 0x00 + XorCommand = 0x05 单片机返回六路输出状态给上位机此时,Content 的1字节中的低六位分别表示六路开关量的输出状态1表示输出高电平,0表示输出低电平第一路输出(第0位)第二路输出(第1位)第三路输出(第2位)第四路输出(第3位)第五路输出(第4位)第六路输出(第5位)如:某一时刻六路输出开关量的输出状态为0x01,上位机读取其状态为:0xee + 0x05 + 0x01 + XorContent:表示四路输入开关量的输入状态(数据)或者六路输出开关量的输出状态(数据),在不使用它的命令格式中,它的值无意义,为通信方便,在不使用它的命令格式中,规定其值为0x00;Xor :从第一个字节开始到Xor的前一个字节(总共3个字节),做异或运算。
51单片机的串口通信程序(C语言) 51单片机的串口通信程序(C语言)
在嵌入式系统中,串口通信是一种常见的数据传输方式,也是单片
机与外部设备进行通信的重要手段之一。
本文将介绍使用C语言编写
51单片机的串口通信程序。
1. 硬件准备
在开始编写串口通信程序之前,需要准备好相应的硬件设备。
首先,我们需要一块51单片机开发板,内置了串口通信功能。
另外,我们还
需要连接一个与单片机通信的外部设备,例如计算机或其他单片机。
2. 引入头文件
在C语言中,我们需要引入相应的头文件来使用串口通信相关的函数。
在51单片机中,我们需要引入reg51.h头文件,以便使用单片机
的寄存器操作相关函数。
同时,我们还需要引入头文件来定义串口通
信的相关寄存器。
3. 配置串口参数
在使用串口通信之前,我们需要配置串口的参数,例如波特率、数
据位、停止位等。
这些参数的配置需要根据实际需要进行调整。
在51
单片机中,我们可以通过写入相应的寄存器来配置串口参数。
4. 初始化串口
在配置完串口参数之后,我们需要初始化串口,以便开始进行数据
的发送和接收。
初始化串口的过程包括打开串口、设置中断等。
5. 数据发送
在串口通信中,数据的发送通常分为两种方式:阻塞发送和非阻塞
发送。
阻塞发送是指程序在发送完数据之后才会继续执行下面的代码,而非阻塞发送是指程序在发送数据的同时可以继续执行其他代码。
6. 数据接收
数据的接收与数据的发送类似,同样有阻塞接收和非阻塞接收两种
方式。
在接收数据时,需要不断地检测是否有数据到达,并及时进行
处理。
7. 中断处理
在串口通信中,中断是一种常见的处理方式。
通过使用中断,可以
及时地响应串口数据的到达或者发送完成等事件,提高程序的处理效率。
8. 串口通信实例
下面是一个简单的串口通信实例,用于在51单片机与计算机之间
进行数据的传输。
```c
#include <reg51.h>
#include <stdio.h>
#define BAUDRATE 9600
#define FOSC 11059200
void UART_init()
{
TMOD = 0x20; // 设置定时器1为模式2
SCON = 0x50; // 设置串口为模式1,允许接收
TH1 = 256 - FOSC / 12 / 32 / BAUDRATE; // 计算波特率定时器重载值
TR1 = 1; // 启动定时器1
EA = 1; // 允许中断
ES = 1; // 允许串口中断
}
void UART_send_byte(unsigned char byte)
{
SBUF = byte;
while (!TI); // 等待发送完成
TI = 0; // 清除发送完成标志位
}
unsigned char UART_receive_byte()
{
while (!RI); // 等待接收完成
RI = 0; // 清除接收完成标志位
return SBUF;
}
void UART_send_string(char *s)
{
while (*s)
{
UART_send_byte(*s);
s++;
}
}
void main()
{
UART_init();
UART_send_string("Hello, World!"); while (1)
{
unsigned char data = UART_receive_byte();
// 对接收到的数据进行处理
}
}
```
总结:
通过以上步骤,我们可以编写出简单的51单片机串口通信程序。
该程序通过配置串口参数、初始化串口、实现数据的发送和接收,以及使用中断来实现串口通信功能。
通过不断学习和实践,我们可以进一步完善和拓展串口通信程序,实现更复杂的功能。