#include "holychip_define.h"
#define TXD PORTB1
//#define RXD PORTB4
unsigned 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; //定时50ms
T0IE = 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 char
u8 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,可以保存形参dat
TX=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); //0x41
delay(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:1bit
case 0:
Ptxd = LOW;
SEQsend = 1;
break;
// 数据位SEND
case 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)
{
// 起始位bit0
case 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;
}
}
}