51单片机汇编模拟串口通信程序
- 格式:doc
- 大小:22.50 KB
- 文档页数:5
51 单片机串口多机通信的实现和编程
一、51 单片机的主从模式,首先要设定工作方式3:(主从模式+波特率可变)
SCON 串口功能寄存器:SM0=1;SM1=1(工作方式3)
注:主机和从机都要为工作方式3。
【工作方式2 (SM0 SM1 :1 0):串行口为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,。
STC51单片机普通IO口模拟IIC(I2C)接口通讯的程序代码STC 51单片机普通IO口模拟IIC(I2C)接口通讯的程序代码原文:(改自周立功软件包)#include <reg51.h>#include <intrins.h>#define uchar unsigned char /*宏定义*/#define uint unsigned intextern void Delay1us(unsigned char );sbit SDA=P1^6; /*模拟I2C数据传送位*/sbit SCL=P1^7; /*模拟I2C时钟控制位*/bit ack; /*应答标志位*//************************************************************** *****起动总线函数函数原型: void Start_I2c();功能: 启动I2C总线,即发送I2C起始条件.*************************************************************** *****/void Start_I2c(){SDA=1; /*发送起始条件的数据信号*/Delay1us(1);SCL=1;Delay1us(5); /*起始条件建立时间大于4.7us,延时*/SDA=0; /*发送起始信号*/Delay1us(5); /* 起始条件锁定时间大于4μs*/SCL=0; /*钳住I2C总线,准备发送或接收数据 */Delay1us(2);}/************************************************************** *****结束总线函数函数原型: void Stop_I2c();功能: 结束I2C总线,即发送I2C结束条件.*************************************************************** *****/void Stop_I2c(){SDA=0; /*发送结束条件的数据信号*/Delay1us(1); /*发送结束条件的时钟信号*/SCL=1; /*结束条件建立时间大于4us*/Delay1us(5);SDA=1; /*发送I2C总线结束信号*/Delay1us(4);}/*******************************************************************字节数据发送函数函数原型: void SendByte(uchar c);功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对此状态位进行操作.(不应答或非应答都使ack=0)发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
51单片机模拟串口的三种方法51单片机模拟串口的三种方法随着单片机的使用日益频繁,用其作前置机进行采集和通信也常见于各种应用,一般是利用前置机采集各种终端数据后进行处理、存储,再主动或被动上报给管理站。
这种情况下下,采集会需要一个串口,上报又需要另一个串口,这就要求单片机具有双串口的功能,但我们知道一般的51系列只提供一个串口,那么另一个串口只能靠程序模拟。
本文所说的模拟串口,就是利用51的两个输入输出引脚如P1.0和P1.1,置1或0分别代表高低电平,也就是串口通信中所说的位,如起始位用低电平,则将其置0,停止位为高电平,则将其置1,各种数据位和校验位则根据情况置1或置0。
至于串口通信的波特率,说到底只是每位电平持续的时间,波特率越高,持续的时间越短。
如波特率为9600BPS,即每一位传送时间为1000ms/9600=0.104ms,即位与位之间的延时为为0.104毫秒。
单片机的延时是通过执行若干条指令来达到目的的,因为每条指令为1-3个指令周期,可即是通过若干个指令周期来进行延时的,单片机常用11.0592M的的晶振,现在我要告诉你这个奇怪数字的来历。
用此频率则每个指令周期的时间为(12/11.0592)us,那么波特率为9600BPS每位要间融多少个指令周期呢?指令周期s=(1000000/9600)/(12/11.0592)=96,刚好为一整数,如果为4800BP S则为96x2=192,如为19200BPS则为48,别的波特率就不算了,都刚好为整数个指令周期,妙吧。
至于别的晶振频率大家自已去算吧。
现在就以11.0592M的晶振为例,谈谈三种模拟串口的方法。
方法一:延时法通过上述计算大家知道,串口的每位需延时0.104秒,中间可执行96个指令周期。
#define uchar unsigned charsbit P1_0 = 0x90;sbit P1_1 = 0x91;sbit P1_2 = 0x92;#define RXD P1_0#define TXD P1_1#define WRDYN 44 //写延时#define RDDYN 43 //读延时//往串口写一个字节void WByte(uchar input){uchar i=8;TXD=(bit)0; //发送启始位Delay2cp(39);//发送8位数据位while(i--){TXD=(bit)(input&0x01); //先传低位Delay2cp(36);input=input>>1;}//发送校验位(无)TXD=(bit)1; //发送结束位Delay2cp(46);}//从串口读一个字节uchar RByte(void){uchar Output=0;uchar i=8;uchar temp=RDDYN;//发送8位数据位Delay2cp(RDDYN*1.5); //此处注意,等过起始位 while(i--){Output >>=1;if(RXD) Output |=0x80; //先收低位Delay2cp(35); //(96-26)/2,循环共占用26个指令周期}while(--temp) //在指定的时间内搜寻结束位。
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的两个输入输出引脚如P1.0和P1.1,置1或0分别代表高低电平,也就是串口通信中所说的位,如起始位用低电平,则将其置0,停止位为高电平,则将其置1,各种数据位和校验位则根据情况置1或置0。
至于串口通信的波特率,说到底只是每位电平持续的时间,波特率越高,持续的时间越短。
如波特率为9600BPS,即每一位传送时间为1000ms/9600=0.104ms,即位与位之间的延时为为0.104毫秒。
单片机的延时是通过执行若干条指令来达到目的的,因为每条指令为1-3个指令周期,可即是通过若干个指令周期来进行延时的,单片机常用11.0592M的的晶振,现在我要告诉你这个奇怪数字的来历。
用此频率则每个指令周期的时间为(12/11.0592)us,那么波特率为9600BPS每位要间融多少个指令周期呢?指令周期s=(1000000/9600)/(12/11.0592)=96,刚好为一整数,如果为4800BPS则为96x2=192,如为19200BPS则为48,别的波特率就不算了,都刚好为整数个指令周期,妙吧。
至于别的晶振频率大家自已去算吧。
现在就以11.0592M的晶振为例,谈谈三种模拟串口的方法。
方法一:延时法通过上述计算大家知道,串口的每位需延时0.104秒,中间可执行96个指令周期。
#define uchar unsigned charsbit P1_0 = 0x90;sbit P1_1 = 0x91;sbit P1_2 = 0x92;#define RXD P1_0#define TXD P1_1#define WRDYN 44 //写延时#define RDDYN 43 //读延时//往串口写一个字节void WByte(uchar input){uchar i=8;TXD=(bit)0; //发送启始位Delay2cp(39);//发送8位数据位while(i--){TXD=(bit)(input&0x01); //先传低位Delay2cp(36);input=input>>1;}//发送校验位(无)TXD=(bit)1; //发送结束位Delay2cp(46);}//从串口读一个字节uchar RByte(void){uchar Output=0;uchar i=8;uchar temp=RDDYN;//发送8位数据位Delay2cp(RDDYN*1.5); //此处注意,等过起始位while(i--){Output >>=1;if(RXD) Output |=0x80; //先收低位Delay2cp(35); //(96-26)/2,循环共占用26个指令周期}while(--temp) //在指定的时间内搜寻结束位。
51单片机与PC串口通信程序及硬件电路图#include <reg51.h>#define BUFFERLEGTH 10//----------------------------------------------------------------- void UART_init(); //串口初始化函数void COM_send(void); //串口发送函数char str[20];char j;//-------------------------------------------------------------------void main(void){unsigned char i;UART_init();j=0; //初始化串口for(i = 0;i < 10 ;i++){COM_send(); //首先发送一次数据作为测试用};while(1);}//-------------------------------------------------------------//--------------------------------------------------------------------------------------------------// 函数名称: UART_init()串口初始化函数// 函数功能:在系统时钟为11.059MHZ时,设定串口波特率为9600bit/s// 串口接收中断允许,发送中断禁止//--------------------------------------------------------------------------------------------------void UART_init(){//初始化串行口和波特率发生器SCON =0x50; //选择串口工作方式1,打开接收允许TMOD =0x20; //定时器1工作在方式2,定时器0工作在方式1 TH1 =0xfA; //实现波特率9600(系统时钟11.0592MHZ)PCON = 0x80;TR1 =1; //启动定时器T1ET1 =0;ES=1; //允许串行口中断PS=1; //设计串行口中断优先级EA =1; //单片机中断允许}//------------------------------------------------------------void COM_send(void){unsigned char point = 0;for(point=0;str[point]!='\0';point++) //连续发送二十位数据 //把缓存区的数据都发送到串口 {SBUF=str[point];while(!TI);TI=0;//str[point]='\0';}}//--------------------------------------------------------------//--------------------------------------------------------------------------------------------------// 函数名称: com_interrup()串口接收中断处理函数// 函数功能:接收包括起始位'S'在内的十位数据到数据缓冲区//--------------------------------------------------------------------------------------------------void com_interrupt(void) interrupt 4 using 3{unsigned char RECEIVR_buffer;bit flag=1;if(RI) //处理接收中断{RI=0; //清除中断标志位RECEIVR_buffer=SBUF; //接收串口数据str[j]=SBUF;if (RECEIVR_buffer == '$'){ ES=0;str[j]='\0';SCON =0x40; //接收不允许COM_send(); //发送数据ES=1;j=0;flag=0;SCON=0x50; //接收允许}if(flag)j++;}}。
单片机IO口模拟串口程序(发送+接收)前一阵一直在做单片机的程序,由于串口不够,需要用IO口来模拟出一个串口。
经过若干曲折并参考了一些现有的资料,基本上完成了。
现在将完整的测试程序,以及其中一些需要总结的部分贴出来。
程序硬件平台:11.0592M晶振,STC单片机(兼容51)/************************************** ************************** 在单片机上模拟了一个串口,使用P2.1作为发送端* 把单片机中存放的数据通过P2.1作为串口TXD发送出去*************************************** ************************/#include <reg51.h>#include <stdio.h>#include <string.h>typedef unsigned char uchar;int i;uchar code info[] ={0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x5 5,0x55,0x55,0x55,0x55,0x55,0x55,0x55 };sbit newTXD = P2^1;//模拟串口的发送端设为P2.1void UartInit(){SCON = 0x50; // SCON: serail mode 1, 8-bit UARTTMOD |= 0x21; // T0工作在方式1,十六位定时PCON |= 0x80; // SMOD=1;TH0 = 0xFE; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=11.0592MHzTL0 = 0x7F; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=11.0592MHz// TH0 = 0xFD; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=18.432MHz// TL0 = 0x7F; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=18.432MHz}void WaitTF0(void){while(!TF0);TF0=0;TH0=0xFE; // 定时器重装初值fosc=11.0592MHzTL0=0x7F; // 定时器重装初值fosc=11.0592MHz// TH0 = 0xFD; // 定时器重装初值 fosc=18.432MHz// TL0 = 0x7F; // 定时器重装初值 fosc=18.432MHz}void WByte(uchar input){//发送启始位uchar j=8;TR0=1;newTXD=(bit)0;WaitTF0();//发送8位数据位while(j--){newTXD=(bit)(input&0x01); //先传低位WaitTF0();input=input>>1;}//发送校验位(无)//发送结束位newTXD=(bit)1;WaitTF0();TR0=0;}void Sendata(){for(i=0;i<sizeof(info);i++)//外层循环,遍历数组{WByte(info[i]);}}void main(){UartInit();while(1){Sendata();}}########################################## ####################################/************************************** ************************** 模拟接收程序,这个程序的作用从模拟串口接收数据,然后将这些数据发送到实际串口* 在单片机上模拟了一个串口,使用P3.2作为发送和接收端* 以P3.2模拟串口接收端,从模拟串口接收数据发至串口*************************************** ************************/#include<reg51.h>#include<stdio.h>#include<string.h>typedef unsigned char uchar ;//这里用来切换晶振频率,支持11.0592MHz 和18.432MHz//#define F18_432#define F11_0592uchar tmpbuf2[64]={0};//用来作为模拟串口接收数据的缓存struct{uchar recv :6 ;//tmpbuf2数组下标,用来将模拟串口接收到的数据存放到tmpbuf2中uchar send :6 ;//tmpbuf2数组下标,用来将tmpbuf2中的数据发送到串口}tmpbuf2_point={0,0};sbit newRXD=P3^2 ;//模拟串口的接收端设为P3.2void UartInit(){SCON=0x50 ;// SCON: serail mode 1, 8-bit UARTTMOD|=0x21 ;// TMOD: timer 1, mode 2, 8-bit reload,自动装载预置数(自动将TH1送到TL1);T0工作在方式1,十六位定时PCON|=0x80 ;// SMOD=1;#ifdef F11_0592TH1=0xE8 ;// Baud:2400 fosc=11.0592MHz 2400bps为从串口接收数据的速率TL1=0xE8 ;// 计数器初始值,fosc=11.0592MHz 因为TH1一直往TL1送,所以这个初值的意义不大TH0=0xFF ;// 定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHzTL0=0xA0 ;// 定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHz#endif#ifdef F18_432TH1=0xD8 ;// Baud:2400fosc=18.432MHz 2400bps为从串口接收数据的速率TL1=0xD8 ;// 计数器初始值,fosc=18.432MHz 因为TH1一直往TL1送,所以这个初值的意义不大TH0=0xFF ;// 定时器0初始值,延时104us,目的是令模拟串口的波特率为9600bps fosc=18.432MHzTL0=0x60 ;// 定时器0初始值,延时104us,目的是令模拟串口的波特率为9600bps fosc=18.432MHz#endifIE|=0x81 ;// 中断允许总控制位EA=1;使能外部中断0TF0=0 ;IT0=1 ;// 设置外部中断0为边沿触发方式TR1=1 ;// 启动TIMER1,用于产生波特率}void WaitTF0(void){while(!TF0);TF0=0 ;#ifdef F11_0592TH0=0xFF ;// 定时器重装初值模拟串口的波特率为9600bps fosc=11.0592MHz TL0=0xA0 ;// 定时器重装初值模拟串口的波特率为9600bps fosc=11.0592MHz #endif#ifdef F18_432TH0=0xFF ;// 定时器重装初值 fosc=18.432MHzTL0=0x60 ;// 定时器重装初值 fosc=18.432MHz#endif}//接收一个字符uchar RByte(){uchar Output=0 ;uchar i=8 ;TR0=1 ;//启动Timer0#ifdef F11_0592TH0=0xFF ;// 定时器重装初值模拟串口的波特率为9600bps fosc=11.0592MHz TL0=0xA0 ;// 定时器重装初值模拟串口的波特率为9600bps fosc=11.0592MHz #endif#ifdef F18_432TH0=0xFF ;// 定时器重装初值fosc=18.432MHzTL0=0x60 ;// 定时器重装初值fosc=18.432MHz#endifTF0=0 ;WaitTF0();//等过起始位//接收8位数据位while(i--){Output>>=1 ;if(newRXD)Output|=0x80 ;//先收低位WaitTF0();//位间延时}TR0=0 ;//停止Timer0return Output ;}//向COM1发送一个字符void SendChar(uchar byteToSend){SBUF=byteToSend ;while(!TI);TI=0 ;}void main(){UartInit();while(1){if(tmpbuf2_point.recv!=tmpbuf2_point.send)//差值表示模拟串口接收数据缓存中还有多少个字节的数据未被处理(发送至串口){SendChar(tmpbuf2[tmpbuf2_point.send++]);}}}//外部中断0,说明模拟串口的起始位到来了void Simulated_Serial_Start()interrupt 0{EX0=0 ;//屏蔽外部中断0tmpbuf2[tmpbuf2_point.recv++]=RByte(); //从模拟串口读取数据,存放到tmpbuf2数组中IE0=0 ;//防止外部中断响应2次,防止外部中断函数执行2次EX0=1 ;//打开外部中断0}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~以上是两个独立的测试程序,分别是模拟串口发送的测试程序和接收的测试程序上面两个程序在编写过程中参考了这篇文章《51单片机模拟串口的三种方法》(在后文中简称《51》),但在它的基础上做了一些补充,下面是若干总结的内容:1、《51》在接收数据的程序中,采用的是循环等待的方法来检测起始位(见《51》的“附:51 IO 口模拟串口通讯C源程序(定时器计数法)”部分),这种方法在较大程序中,可能会错过起始位(比如起始位到来的时候程序正好在干别的,而没有处于判断起始位到来的状态),或者一直在检测起始位,而没有办法完成其他工作。
51单片机汇编模拟串口通信程序
T2作为波特率控制UART_RXD 是硬中断0或1口,如果能进入中断,说明该线有一个起始位产生,进入中断后调
用下面的接收程序。
退出硬中断之前还需要将硬中断标志重新复位。
UART_TXD是任何其它IO即可。
UART_SEND:
PUSH IE
PUSH DPH
PUSH DPL
PUSH PSW
PUSH 00H
PUSH ACC
CLR EA
SETB UART_TXD ;START BIT
MOV R0,A
CLR TR2 ;TR2置1,计数器2启动,时间计数启动。
MOV A,RCAP2L;计数器2重新装载值
MOV TL2,A ;置计数器2初值;T2需要重新装载
MOV A,DPH
MOV A,RCAP2H
MOV TH2,A
MOV A,R0
SETB TR2 ;TR2置1,计数器
JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2
CLR UART_TXD ;START BIT
JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2
MOV R0,#08H
UART_SEND_LOOP:
RRC A
MOV UART_TXD,C ;8 BIT JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2
DJNZ R0,UART_SEND_LOOP SETB UART_TXD ;END BIT JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2
POP ACC
POP 00H
POP PSW
POP DPL
POP DPH
POP IE
RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; UART_REC:
PUSH IE
PUSH DPH
PUSH DPL
CLR EA
CLR TR2 ;TR2置1,计数器2启动,时间计数启动。
MOV A,RCAP2L;计数器2重新装载值
MOV TL2,A ;置计数器2初值;T2需要重新装载MOV A,DPH
MOV A,RCAP2H
MOV TH2,A
JB UART_RXD,$ ;REC
SETB TR2 ;TR2置1,计数器2启动,时间计数启动。
JNB TF2,$
CLR TF2 ;0.5 BIT
JNB TF2,$
CLR TF2 ;1 BIT
JNB TF2,$
CLR TF2 ;1.5 BIT
MOV C,UART_RXD
MOV ACC.0,C
JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2 ;2.5
MOV C,UART_RXD
MOV ACC.1,C
JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2 ;3.5
MOV C,UART_RXD
MOV ACC.2,C
JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2 ;4.5
MOV C,UART_RXD MOV ACC.3,C
JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2 ;5.5
MOV C,UART_RXD MOV ACC.4,C
JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2 ;6.5
MOV C,UART_RXD MOV ACC.5,C
JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2 ;7.5
MOV C,UART_RXD MOV ACC.6,C
JNB TF2,$
CLR TF2
JNB TF2,$
CLR TF2 ;8.5
MOV C,UART_RXD MOV ACC.7,C
JNB TF2,$
CLR TF2 ;9.5
JNB UART_RXD,$ ;等待停止位,并重新复位计数器SETB UART_RXD
POP DPL
POP DPH
POP IE
RET。