#include "holychip_define.h"#define TXD PORTB1//#define RXD PORTB4unsigned int Count=0;unsigned int Time_1s;unsigned int i ;void Delay(unsigned char i){while(i--);}/*------------------------------------------------mS延时函数,含有输入参数 unsigned int t,无返回值unsigned int 是定义无符号字符变量,其值的范围是0~1024 精确延时请使用汇编-------------------------------------------------*/void DelayMS(unsigned int t){unsigned char count = 195;while(t--){while(count--);}}void Init(void){PORTB = 0B00110010; //PB1上电高电平TRISB = 0X00;PHCON = 0XFF;PDCON = 0xff;ODCON = 0X00;}void Time0_Init(void){OPTION = 0x16;T0 = 178; //定时50msT0IE = 1;T0IF = 0;GIE = 1;}void WriteByte(unsigned char Byte){unsigned char i = 8;TXD = 0; //发送起始位Delay(25);//发送8位数据位while(i--){TXD = (Byte&0x01); //先传最低位Byte = Byte>>1;Delay(25);}//发送校验位TXD = 1;Delay(25);}void UART_SendString(unsigned char *buf){while(*buf!='\0'){WriteByte(*buf);buf++;}}void main(void){unsigned int i ;Init();Time0_Init();while(1){if(Time_1s==1){Time_1s=0;for(i=0;i<3;i++){UART_SendString("Hello,World!");DelayMS(1);}asm(sleep);asm(nop);asm(nop);asm(nop);}}}//*****************************中断服务程序*****************************//进中断时间=1/(时钟源/xT/分频比)*(256-T0初值)void Intr(void) __interrupt 0{if(T0IF){T0IF =0;T0 = 178;Count++;if(Count>=200){Count=0;Time_1s=1; //1s时间到}}}
#include "reg51.h"/*将P1.0虚拟成串口发送脚TX以9600bit/s的比特率向外发送数据因为波特率是 9600bit/s所以me发送一位的时间是 t=1000000us/9600=104us*/sbit TX=P3^1; //P1^0 output TTL signal, need to transferred to rs232 signal, can be connected to P3^1#define u16 unsigned int //宏定义#define u8 unsigned charu8 sbuf;bit ti=0;void delay(u16 x){while(x--);}void Timer0_Init(){TMOD |= 0x01;TH0=65440/256;TH0=65440%256;TR0=0;}void Isr_Init(){EA=1;ET0=1;}void Send_Byte(u8 dat){sbuf=dat;//通过引入全局变量sbuf,可以保存形参datTX=0; //A 起始位TR0=1;while(ti==0); //等待发送完成ti=0; //清除发送完成标志}void TF0_isr() interrupt 1 //每104us进入一次中断{static u8 i; //记录进入中断的次数TH0=65440/256;TL0=65440%256;i++;if(i>=1 && i<=8){if((sbuf&(1<<(i-1)))==0) // (sbuf&(1<<(i-1)))表示取出i-1位{TX=0;}else{TX=1;}}if(i==9) //停止位{TX=1;}if(i==10){TR0=0;i=0;ti=1; //发送完成}}void main(){TX=1; //使TX处于空闲状态Timer0_Init();Isr_Init();while(1){Send_Byte(65); //0x41delay(60000);}}
思路
1>数据的发送其实就是控制发送引脚Ptxd的电平随着固定时序变化,那么固定的、周期性的时序需要一个定时器来产生。为了保证定时器周期高精度稳定可控,选用自动重装定时器模式 。为了尽可能的减少资源占用,发送和接收共用一个定时器资源。
2>程序的设计采用状态机设计模式,这样可以避免独占CPU,并且利于移植到各种状态机系统。状态迁移程序在ISR中实现。  
3>定时器的中断频率为波特率的3倍[之所以这样做后面介绍接收时详述]。
4>开启发送之后,ISR中发送相关程序得到执行,每3次中断处理1bit.根据时序,先产生bit0[起始位],然后根据8bit型数据依次在时序线上控制Ptxd的电平高低。数据位发送完毕后,若设置了校验位,则对8bits数据中bit1的数量进行判断,根据奇偶校验对bit1的数量通过控制Prxd的电平进行奇偶补全[发送数据位时对bit1进行计数]。然后根据设置发送对应长度的bit1[停止位]。最后,判断发送数据是否达到发送长度,若发送长度到达,则结束发送程序执行,否则继续循环发送。
5>开启接收之后,ISR中接收相关程序得到执行,并与发送程序互不干涉。首先每次中断都判断Prxd的电平是否置低[检测起始位],当检测到bit0之后,四个中断后进行数据位最低位的接收。这里之所以选择四个中断的延时,是因为当检测到bit0[起始位]的时刻,不管此时处于实际起始位的哪个时间点位置,四个中断后即下一次判断的位置总会是最接近一位数据中心那个点[一个bit中有三次中断点,有一个最接近中心,越接近中心越能免受发送与接收波特率不同步的影响,具体细节画图可知]。之后的每一位改为每三个中断进行一次判断处理[与波特率同步]。处理到校验位时,如果设置了奇偶校验,则对校验结果输出至设置好的校验标志位中,外围程序可根据此标准位判断是否校验出错。然后判断接收数据的长度是否达到设置长度,若没有,则等待至Prxd变为高电平后继续循环接收。
static void Xuart_isr(void) interrupt 3{Pts = ~Pts;if(++CNTisr == 3){CNTisr = 0;/***************************SEND***************************/if(Fsend == SET){switch(SEQsend){// 起始位0:1bitcase 0:Ptxd = LOW;SEQsend = 1;break;// 数据位SENDcase 1:if((*ptr_send) & (1 << CNTsend_i)){CNTbit1++;Ptxd = HIGH;}else{Ptxd = LOW;}if(CNTsend_i >= 7){CNTsend_i = 0;switch(Xuart_config.bit_parity){case NONE: // 无校验SEQsend = 4;break;case ODD: // 奇校验SEQsend = 2;break;case EVEN: // 偶校验SEQsend = 3;break;default:SEQsend = 4;break;}}else{CNTsend_i++;}break;// 奇校验case 2:if((CNTbit1 % 2) == 0) // 偶数个1{Ptxd = HIGH;}else{Ptxd = LOW;}CNTbit1 = 0;SEQsend = 4;break;// 偶校验case 3:if((CNTbit1 % 2) == 0) // 偶数个1{Ptxd = LOW;}else{Ptxd = HIGH;}SEQsend = 4;break;// 停止位case 4:CNTbit1 = 0;switch(Xuart_config.bit_stop){case S_1BIT:SEQsend = 5;break;case S_2BIT:if(++CNTstbit == 2){CNTstbit = 0;SEQsend = 5;}break;default://SEQsend = 5;break;}Ptxd = HIGH;break;// 下一帧or结束case 5:if(CNTsend_j >= len_send - 1) // 发送完毕处理{CNTsend_j = 0;Fsend_end = SET;Fsend = CLR;//TR1 = 0;}else{CNTsend_j++;ptr_send++;}SEQsend = 0;break;default:SEQsend = 0;CNTsend_i = 0;CNTsend_j = 0;CNTbit1 = 0;Fsend_end = SET;Fsend = CLR;TR1 = 0;}}}/**************************RECEIVE*************************/if(Freceive == SET){switch(SEQreceive){// 起始位bit0case 0:if(Prxd == LOW) // 收到起始位bit0{SEQreceive = 1;CNTrec = 0;break;}break;// 8bits_bit1数据case 1:if(++CNTrec == 4) // 第一次进来时为4个周期,提升容错率{ // 其它时机进来为3个周期,保持同步性CNTrec = 1;if(Prxd == HIGH){CNTbit1_++;*ptr_receive |= (U8)(1 << CNTreceive_i);}else{*ptr_receive &= (U8)(~(1 << CNTreceive_i));}if(CNTreceive_i >= 7) // 1帧接收完毕{CNTreceive_i = 0;CNTrec = 0;switch(Xuart_config.bit_parity){case NONE: // 无校验SEQreceive = 4;break;case ODD: // 奇校验SEQreceive = 2;break;case EVEN: // 偶校验SEQreceive = 3;break;default:SEQreceive = 4;break;}}else{CNTreceive_i++;}}break;// 奇校验case 2:if(++CNTrec == 3) // 3个周期进入{CNTrec = 0;if((CNTbit1_ % 2) & (!Prxd)) // 校验正确{Freceive_parity = RIGHT;}else // 校验错误{Freceive_parity = ERROR;}CNTbit1_ = 0;SEQreceive = 4;break;}break;// 偶校验case 3:if(++CNTrec == 3) // 3个周期进入{CNTrec = 0;if((CNTbit1_ % 2) & (Prxd)) // 校验正确{Freceive_parity = RIGHT;}else // 校验错误{Freceive_parity = ERROR;}SEQreceive = 4;break;}break;// 帧结束case 4:if(Prxd == HIGH){if(CNTreceive_j >= (len_receive - 1)){CNTreceive_j = 0;Freceive = CLR;Freceive_end = SET;}else{CNTreceive_j++;ptr_receive++;}SEQreceive = 0;break;}break;default:break;}}}
