这年过的,哈哈哈,都忘了这茬事,昨天和二弟三弟一起竟然看了一下午电影,真是够够了。。。
不扯淡了,马上就要开学了,我得抓紧把模块先全搞完了!
记住重要一点,串口是低位在先,高位在后!
UART模块,51内集成有硬件模块,直接调用寄存器即可使用。但是嘛,我们既然打着学习的旗号,又岂有不去瞧瞧软件模拟的道理!
/*
*******************************************************************************
* 文件名:main.c
* 描 述:UART的软件模拟
* 作 者:CLAY
* 版本号:v1.0.0
* 日 期: 2018年2月17日
* 备 注:硬件模拟有硬件模拟的便利,软件让我们更知其所以然。
* 串口通信低位在先,高位在后。起始信号低电平,停止信号高电平。
*******************************************************************************
*/
#include
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
sbit PIN_TXD = P2^0;
sbit PIN_RXD = P2^1;
void CloseFucker();
void ConfigUART(u16 baud);
void StartRxd();
void StartTxd(u8 dat);
bit TxdEnd = 0;
bit RxdEnd = 0;
bit TxdOrRxd = 0; // 1-发; 2-接。
u8 TxdBuf = 0;
u8 RxdBuf = 0;
void main()
{
EA = 1;
CloseFucker();
ConfigUART(9600);
while(1)
{
while(PIN_RXD);
StartRxd();
while(!RxdEnd);
StartTxd(RxdBuf+1);
while(!TxdEnd);
}
}
void CloseFucker()
{
P2 = (P2 & 0x1F) | 0xA0;
P0 = P0 & 0xAF;
P2 = P2 & 0x1F;
}
void ConfigUART(u16 baud)
{
TMOD &= 0xF0;
TMOD |= 0x02;
TH0 = 256 - (11059200/12)/baud;
}
void StartRxd()
{
TL0 = 256 - ((256-TH0)>>1); //在半个波特率周期开始接受
ET0 = 1;
TR0 = 1;
RxdEnd = 0;
TxdOrRxd = 0;
}
void StartTxd(u8 dat)
{
TxdBuf = dat;
TL0 = TH0;
ET0 = 1;
TR0 = 1;
PIN_TXD = 0;
TxdEnd = 0;
TxdOrRxd = 1;
}
void InterruptTimer0() interrupt 1
{
static u8 cnt = 0;
if(TxdOrRxd)//发送
{
cnt++; //起始位已经发送完毕
if(cnt <= 8)
{
PIN_TXD = TxdBuf & 0x01;
TxdBuf >>= 1;
}
else if(cnt == 9)//注意发送停止位这次过程中只需要操作PIN_TXD,置位Txd_End关闭定时器是在第十次中进行
{
PIN_TXD = 1;
}
else
{
cnt = 0;
TR0 = 0;
TxdEnd = 1;
}
}
else //接收
{
if(cnt == 0)
{
if(!PIN_RXD)
{
RxdBuf = 0;
cnt++;
}
else
{
TR0 = 0;
}
}
else if(cnt <= 8)
{
RxdBuf >>= 1;
if(PIN_RXD)
{
RxdBuf |= 0x80;//注意是低位在先,所以低位先到缓冲区高位!
}
cnt ++;
}
else
{
cnt = 0;
TR0 = 0;
if(PIN_RXD)
{
RxdEnd = 1;
}
}
}
}
上面的模拟,其实还算是比较简练直观的。
实际的串口模块要比模拟的更复杂和精确点!而且,STC89C52 单片机来讲,这个波特率发生器只能由定时器 T1 或定时器 T2 产生,而不能由定时器 T0 产生。
/*
*******************************************************************************
* 文件名:main.c
* 描 述:硬件UART
* 作 者:CLAY
* 版本号:v1.0.0
* 日 期: 2018年2月17日
* 备 注:注意,15的AUXR寄存器必须配置下!同时PCON寄存器最好也配置下!
*
*******************************************************************************
*/
#include
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
void ConfigUART(unsigned int baud);
void main()
{
ConfigUART(9600); //配置波特率为9600
while (1)
{
while (!RI); //等待接收完成
RI = 0; //清零接收中断标志位
SBUF = SBUF + 1; //接收到的数据+1后,发送回去
while (!TI); //等待发送完成
TI = 0; //清零发送中断标志位
}
}
void ConfigUART(unsigned int baud)
{
PCON &= 0x7F; //波特率不倍速
AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
SCON = 0x50; //配置串口为模式1
TMOD &= 0x0F; //清零T1的控制位
TMOD |= 0x20; //配置T1为模式2
TH1 = 256 - (11059200/12/32)/baud; //计算T1重载值
// TH1 = 0xFD; //这个是上面初值
TL1 = TH1; //初值等于重载值
ET1 = 0; //禁止T1中断
TR1 = 1; //启动T1
}
更进一步
/*
*******************************************************************************
* 文件名:main.c
* 描 述:硬件UART
* 作 者:CLAY
* 版本号:v1.0.1
* 日 期: 2018年2月17日
* 备 注:使用串口中断不放到while(1)里面了!
*
*******************************************************************************
*/
#include
void ConfigUART(unsigned int baud);
void main()
{
EA = 1;
ConfigUART(9600); //配置波特率为9600
while (1);
}
void ConfigUART(unsigned int baud)
{
PCON &= 0x7F; //波特率不倍速
AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
SCON = 0x50; //配置串口为模式1
TMOD &= 0x0F; //清零T1的控制位
TMOD |= 0x20; //配置T1为模式2
TH1 = 256 - (11059200/12/32)/baud; //计算T1重载值
// TH1 = 0xFD; //这个是上面初值
TL1 = TH1; //初值等于重载值
ET1 = 0; //禁止T1中断
ES = 1;
TR1 = 1; //启动T1
}
void InterruptUART() interrupt 4
{
if(RI)
{
RI = 0;
SBUF = SBUF + 1;
}
if(TI)
{
TI = 0;
}
}
再继续深入
/*
*******************************************************************************
* 文件名:main.c
* 描 述:数码管显示ASCII
* 作 者:CLAY
* 版本号:v1.0.0
* 日 期: 2018年2月17日
* 备 注:串口中断发送并回显,数码管显示!
*
*******************************************************************************
*/
#include
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
u8 code LedChar[] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
u8 LedBuff[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
u8 RxdByte = 0;
u8 T0RH = 0;
u8 T0RL = 0;
void ConfigUART(u16 baud);
void ConfigTimer0(u16 ms);
void main()
{
EA = 1;
ConfigTimer0(1);
ConfigUART(9600);
while(1)
{
LedBuff[0] = LedChar[RxdByte & 0x0F];
LedBuff[1] = LedChar[RxdByte >> 4];
}
}
void ConfigUART(u16 baud)
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F;
TMOD |= 0x20;
TH1 = 256 - (11059200/12/32)/baud;
TL1 = TH1;
ET1 = 0;
ES = 1;
TR1 = 1;
}
void ConfigTimer0(u16 ms)
{
u32 tmp = 0;
tmp = 11059200 / 12;
tmp = (tmp * ms) / 1000;
tmp = 65536 - tmp;
T0RH = (u8)(tmp >> 8);
T0RL = (u8)tmp;
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1;
TR0 = 1;
}
void LedScan()
{
static u8 index = 0;
P2 = (P2 & 0x1F) | 0xE0;
P0 = 0xFF;
P2 = P2 & 0x1F;
P2 = (P2 & 0x1F) | 0xC0;
P0 = 0x80 >> index;
P2 = P2 & 0x1F;
P2 = (P2 & 0x1F) | 0xE0;
P0 = LedBuff[index];
P2 = P2 & 0x1F;
if(index < 7)
index++;
else
index = 0;
}
void InterruptTimer0() interrupt 1
{
TH0 = T0RH;
TL0 = T0RL;
LedScan();
}
void InterruptUART() interrupt 4
{
if(RI)
{
RI = 0;
RxdByte = SBUF;
SBUF = RxdByte;
}
if(TI)
{
TI = 0;
}
}
emmm,再看一个升级版的!
/*
*******************************************************************************
* 文件名:main.c
* 描 述:数码管显示ASCII
* 作 者:CLAY
* 版本号:v1.0.1
* 日 期: 2018年2月17日
* 备 注:任意字符触发或关闭流水灯
*
*******************************************************************************
*/
#include
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
u8 code LedChar[] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
u8 LedBuff[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
bit flag200ms = 0;
bit flagLight = 0;
u8 RxdByte = 0;
u8 T0RH = 0;
u8 T0RL = 0;
void ConfigUART(u16 baud);
void ConfigTimer0(u16 ms);
void CloseLed();
void FlowLight();
void CloseFucker();
void main()
{
EA = 1;
ConfigTimer0(1);
ConfigUART(9600);
CloseFucker();
while(1)
{
if(flagLight == 0)
{
CloseLed();
}
else
{
if(flag200ms)
{
flag200ms = 0;
FlowLight();
}
}
LedBuff[0] = LedChar[RxdByte & 0x0F];
LedBuff[1] = LedChar[RxdByte >> 4];
}
}
void CloseFucker()
{
P2 = (P2 & 0x1F) | 0xA0;
P0 = P0 & 0xAF;
P2 = P2 & 0x1F;
}
void FlowLight()
{
static bit dir = 0;
static u8 shift = 0x80;
P2 = (P2 & 0x1F) | 0x80;
P0 = ~shift;
P2 = 0x00;
if(dir == 0)
{
shift >>= 1;
if(shift == 0x01)
dir = 1;
}
else
{
shift <<= 1;
if(shift == 0x80)
dir = 0;
}
}
void CloseLed()
{
P2 = (P2 & 0x1F) | 0x80;
P0 = 0xFF;
P2 = P2 & 0x1F;
}
void ConfigUART(u16 baud)
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F;
TMOD |= 0x20;
TH1 = 256 - (11059200/12/32)/baud;
TL1 = TH1;
ET1 = 0;
ES = 1;
TR1 = 1;
}
void ConfigTimer0(u16 ms)
{
u32 tmp = 0;
tmp = 11059200 / 12;
tmp = (tmp * ms) / 1000;
tmp = 65536 - tmp;
T0RH = (u8)(tmp >> 8);
T0RL = (u8)tmp;
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1;
TR0 = 1;
}
void LedScan()
{
static u8 index = 0;
P2 = (P2 & 0x1F) | 0xE0;
P0 = 0xFF;
P2 = P2 & 0x1F;
P2 = (P2 & 0x1F) | 0xC0;
P0 = 0x80 >> index;
P2 = P2 & 0x1F;
P2 = (P2 & 0x1F) | 0xE0;
P0 = LedBuff[index];
P2 = P2 & 0x1F;
if(index < 7)
index++;
else
index = 0;
}
void InterruptTimer0() interrupt 1
{
static u8 tmr200ms = 0;
TH0 = T0RH;
TL0 = T0RL;
LedScan();
tmr200ms++;
if(tmr200ms == 200)
{
tmr200ms = 0;
flag200ms = 1;
}
}
void InterruptUART() interrupt 4
{
if(RI)
{
RI = 0;
RxdByte = SBUF;
SBUF = RxdByte;
flagLight = !flagLight;
}
if(TI)
{
TI = 0;
}
}
串口的使用多用在调试中。实际中帧模式的空闲串口实现也很有意思,值得研究一下!
一个实用的串口调试程序
uart.c
/*
*******************************************************************************
* 文件名:uart.c
* 描 述:
* 作 者:CLAY
* 版本号:v1.0.0
* 日 期:
* 备 注:
*
*******************************************************************************
*/
#include "config.h"
void UartInit() //[email protected]
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
void SendData(u8 dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
void SendString(u8 *str)
{
while(*str != '\0')
{
SendData(*str++);
}
}
uart.h
/*
*******************************************************************************
* 文件名:uart.h
* 描 述:
* 作 者:CLAY
* 版本号:v1.0.0
* 日 期:
* 备 注:
*
*******************************************************************************
*/
#ifndef UART_H
#define UART_H
extern void UartInit();
extern void SendData(u8 dat);
extern void SendString(u8 *str);
#endif