经过上述学习,我们已经对串口有了一定的了解,接下来就是实现串口通信
单片机通过串口发送数据
我们使用UART串口通信,首先要进行初始化,如:选择工作方式,初始化中断系统,设置波特率,初始化定时器
选择工作模式
涉及到SCON
和 PCON
首先是PCON的SMOD0
,当 SMOD0 = 0 时,SMOD 的 SM0 才会被用来选择工作方式
其次,我们选择 8位UART,波特率可变模式,即方式1,SCON的SM0 = 0,SM1 = 1
最后,如果要允许串口接收数据,还需要置SCON的REN = 1
初始化中断系统
首先,初始化串口收发数据的中断请求标志位,SCON的 TI
和 RI
,由硬件置1,我们初始化时清零即可:TI = 0, RI = 0
到此,SCON的设置就结束了
总结一下,SM0 = 0, SM1 = 1, REN = 1/0, TI = 0, RI = 0,其他默认为0即可,所以SCON = 0x40/0x50
然后是中断开关
ES = 1, EA = 1
SCON = 0x50; //选择工作方式 & 允许串口接收数据
PCON |= 0x80; //使SM0为选择工作方式
//中断开关
ES = 1; //串口中断开关
EA = 1; //总中断开关
初始化定时器
初始化定时器可参看&&&&&&&&&&&&&&&&&&&
此处定时器1选择工作模式2——8位自动重装
8位自动重装
一次只对TL1或TH1计数加一
当一个溢出后,直接使用另一个计数单元的初值
//设置定时器1
TMOD &= 0x0F; //高4位清零
TMOD |= 0x20; //0010,模式3——8位自动重载
TR1 = 1; //启用定时器T1
ET1 = 0; //禁止定时器T1中断
//定时器初值
TL1 = 0xF3; //设定定时初值
TH1 = 0xF3; //设定定时器重装值
设置波特率
我们设置波特率为4800
设置波特率需要通过设置定时器1的初始值
TL1 = 0xF3; //设定定时初值
TH1 = 0xF3; //设定定时器重装值
讲解一下为什么定时器初值是这个
假设系统频率为12MHz,使用12T模式,则定时器频率为12 / 12 = 1MHz,即每1us,计数单元加1。
使用8位自动重装,256时会溢出,0xF3 = 243,256 - 243 = 13。所以定时器溢出需要13us
溢出率:1 / 13 = 0.07692
使用SMOD = 1,波特率加倍(不除2)
还需要 0.07692 / 16 = 0.0048076923MHz
转化为Hz:4807.6923Hz,这个就是波特率
会存在一定误差
也可参看如下计算
到此,串口的初始化就完成了
完整代码如下:
/**
* @brief 初始化串口
* @parm 无
* @retval 无
*/
void UART_Init()
{
//SCON高4位分别为SM0、SM1、SM2、REN
//SM0和SM1控制串口模式,选择01——8位UART,波特率可变
//REN接收使能,REN = 0禁止接收,REN = 1允许接收
//所以设置0101 0000
SCON = 0x50;
//PCON包含波特率和电源设置
//前两位为SMOD和SMOD0
//SMOD = 1波特率加倍,SMOD = 0,波特率不加倍
//SMOD0是帧错误的,此处不用
//所以设置1000 0000
PCON |= 0x80;
//设置定时器1
TMOD &= 0x0F; //高4位清零
TMOD |= 0x20; //0010,模式3——8位自动重载
TR1 = 1; //启用定时器T1
ET1 = 0; //禁止定时器T1中断
//定时器初值
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
//中断开关
ES = 1; //串口中断开关
EA = 1; //总中断开关
}
博主的单片机系统频率为11.0592MHz
可以使用STC-ICP
生成波特率设置代码
注意:
配置一定要选择正确;代码中的AUXR寄存器
为高版本单片机才有的,低版本不认识这个寄存器,可以直接删掉
串口发送数据通过赋值SBUF
,数据发送完后,硬件置位TI = 1
,需要我们手动对TI清零
代码如下:
/**
* @brief 通过串口发送一个字节数据
* @parm Byte:要发送字节数据
* @retval 无
*/
void UART_SendByte(unsigned char Byte)
{
SBUF = Byte;
while(TI == 0);//数据发送完,硬件置1
TI = 0; //软件置0
}
模块化编程,完整代码如下:
延时模块——控制串口发送数据速率Delay.h
#ifndef __DELAY_H__
#define __DELAT_H__
void Delayms(unsigned int xms);//等待指定毫秒
#endif
Delay.c
#include <INTRINS.h>
/**
* @brief 延迟一定时间
* @parm 延迟的时间,单位是毫秒,范围:0 ~ 65535
* @retval 无
*/
void Delayms(unsigned int xms) //@11.0592MHz
{
while(xms--)
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
UART串口模块
UART.h
#ifndef __UART_H__
#define __UART_H__
void UART_Init();
void UART_SendByte(unsigned char Byte);
#endif
UART.c
#include <REGX52.H>
/**
* @brief 初始化串口
* @parm 无
* @retval 无
*/
void UART_Init()
{
//SCON高4位分别为SM0、SM1、SM2、REN
//SM0和SM1控制串口模式,选择01——8位UART,波特率可变
//REN接收使能,REN = 0禁止接收,REN = 1允许接收
//所以设置0101 0000
SCON = 0x50;
//PCON包含波特率和电源设置
//前两位为SMOD和SMOD0
//SMOD = 1波特率加倍,SMOD = 0,波特率不加倍
//SMOD0是帧错误的,此处不用
//所以设置1000 0000
PCON |= 0x80;
//设置定时器1
TMOD &= 0x0F; //高4位清零
TMOD |= 0x20; //0010,模式3——8位自动重载
TR1 = 1; //启用定时器T1
ET1 = 0; //禁止定时器T1中断
//定时器初值
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
//中断开关
ES = 1; //串口中断开关
EA = 1; //总中断开关
}
/**
* @brief 通过串口发送一个字节数据
* @parm Byte 要发送字节数据
* @retval 无
*/
void UART_SendByte(unsigned char Byte)
{
SBUF = Byte;
while(TI == 0);//数据发送完,硬件置1
TI = 0; //软件置0
}
///**
// * @brief 接收数据 模版
// * @parm 无
// * @retval 无
// */
//void UART_Routine() interrupt 4
//{
// if(RI == 1)//检测是否是接收数据中断
// {
// RI = 0;//软件置0
// }
//}
主程序——每隔一秒通过串口发送递增数据
#include <REGX52.H>
#include "UART.h"
#include "Delay.h"
/**
* @brief 通过串口每隔1s发送递增的数据 范围:0 ~ 255
* @parm 无
* @retval 无
*/
void SendIncreasingNum()
{
static unsigned char num;
UART_SendByte(num++);
Delayms(1000);
}
void main()
{
UART_Init();
while(1)
{
SendIncreasingNum();
}
}
使用STC-IST
的 串口助手 查看效果
注意
:下面一行的配置要正确
电脑通过串口发送数据控制LED灯
电脑发送数据给单片机需要USB转串口,自带的USB线就已经实现了这一转换,所以我们直接编写单片机通过串口接收数据的逻辑即可。
串口接收数据会存放在SBUF
,接收完毕后会将RI置1
,发出中断请求,中断号为4,然后需要手动清零RI
代码如下:
void UART_Routine() interrupt 4
{
if(RI == 1)//检测是否是接收数据中断
{
P2 = SBUF;
RI = 0;//软件置0
}
}
注意
:P2
寄存器用于控制LED亮灭,为0亮起,为1熄灭
还可以将数据重新返回给电脑,同样使用Delay
和 UART
模块,只有main.c不同
main.c
#include <REGX52.H>
#include "UART.h"
#include "Delay.h"
/**
* @brief 接收数据,亮相应的灯,并返回数据
* @parm 无
* @retval 无
*/
void UART_Routine() interrupt 4
{
if(RI == 1)//检测是否是接收数据中断
{
P2 = SBUF;
UART_SendByte(SBUF);
RI = 0;//软件置0
}
}
void main()
{
UART_Init();
while(1)
{
}
}
效果如下:
我们通过串口助手,发送 0xAA = 1010 1010
LED灯效果如下: