蓝桥杯单片机学习8——串口通信(UART的使用示例)
串口通信概述、串口寄存器介绍、串口工作模式介绍、波特率的计算、串口中断配置函数代码、串口实现接口通信示例及代码实现
上期我们学习了如何通过定时器产生PWM波控制LED的亮度,现在我们开始学习串口通信
串口通信
1.串口通信概述
UART:通用异步收发器(Universal Asynchronous Receiver/Transmitter:UART),一种异步串行通信协议,原理是通过信号线将需要发送的数据以二进制的形式一位一位的发送出去,在传输的过程中高电平表示发送的数据为‘1’,低电平表示数据‘0’,
- 异步指的是在串口通信的过程中,通信双方之间没有特定的时钟线约定时间,各自使用各自的时钟信号,双方通过约定的波特率进行通信。
- 串行是指数据在发送的过程中以一位一位的形式发送
在串口通信的过程中,数据往往以数据帧的格式进行传输,数据帧的一般格式如下:
一个完整的数据帧应该包含:起始位、数据位、停止位。
- 起始位:发送一个低电平,表示通信开始。
- 数据位:一共含有8个数据位,可发送一个字节的数据
- 校验位:可分为无校验、奇校验、偶校验三种,三种发送的区别如下:
无校验:无校验的情况下数据位为8位,不对数据通信过程中的数据正确与否进行检验
奇校验:奇校验的情况下数据位为9位,第九位根据前八位数据计算而来,当前八位数据中1的个数的偶数个,则第九位为1,反之如果为奇数个,则为0.
偶校验:偶校验则是在第九位补0或1,使得这九位数字中1的总数始终为偶数个
- 停止位:一段时间的高电平,高电平的时间可以是1位、1.5位、2位、
2.串口寄存器
1.串口1的控制寄存器SCON(可位寻址)和PCON(不可位寻址)
- SM0/1用于控制串口1的工作模式
- SM2用于多机通信控制,这里不做解释
- REN是串口接收允许控制,为1时表示允许串口接收,反之则不允许
- TB8/RB8用于奇偶校验,这里也不做解释
- TI/RI是串口发送/接收中断标志位,在发送/接收一个数据帧完成时由硬件置1,需要软件清0
- SMOD:波特率选择位,控制波特率是否加倍
- SMOD0:在奇偶校验中会用到,这里也不做解释
2. 串行口数据缓存数据寄存器SUBF:
在串口通信的过程中,发送/接收的数据都会存入SBUF寄存器,通过对TI/RI的判断,可以进行对SBUF寄存器的数据写入/读出.
3.辅助寄存器AUXR(不可位寻址):
- S1ST2:为0时通过定时器1为串口1提供波特率,为1时通过定时器2为串口1提供波特率,在串口初始化的时候应该将该位置0
3.串口工作模式
根据SM0/SM1的值,串口的工作模式有以下四种:
SM0 | SM1 | 工作模式 |
---|---|---|
0 | 0 | 工作模式0:同步移位寄存器 |
0 | 1 | 工作模式1:8位UART,无校验位,波特率可调 |
1 | 0 | 工作模式2:9位UART,波特率固定,可使用奇偶校验 |
1 | 1 | 工作模式3:9位UART,波特率可调,可使用奇偶校验 |
- 在使用过程中,工作模式1使用最为广泛,下面重点介绍工作模式1.
4. 波特率计算
常见波特率与定时器1各参数关系图:
举个栗子:
在工作模式1的情况下,波特率由以下公式计算:
波特率 = (2^SOMD /32) * SYSclk / 12 / (256_TH1) ----(定时器1工作模式为8位自动重装载)
将上述公式化简: 256-TH1 = (波特率 * 12 * 32)/ (2^SMOD * SYSclk)
假设波特率为 9600,SMOD = 0 则: 256 - TH1 = (96001232) / 12MHZ = 3.072
TH1 = 256-3 = 253 = 0xFD
5.串口中断的配置
串口初始化函数:
1.设置定时器1工作模式2八位自动重装
2.代开定时器1 ,设置波特率(设置TH1和TL1的初始值)
3.设置串口工作模式,是否允许接收(设置SCON寄存器)
4.打开串口中断和总中断允许
5.设置定时器1为串口1提供波特率
具体函数如下:
//串口初始化函数,设置模式为波特率可调,八位数据位,通过定时器1提供波特率
void Uart_Init(void)
{
TMOD |= 0x20; //定时器1工作模式为八位自动重装载,
TL1 = TH1 = 0xFD; //设置波特率为9600
TR1 = 1; //打开定时器1
SCON = 0x50; //设置串口工作模式为波特率可调,八位数据位,无校验,允许接受
ES = 1; //打开串口中断允许
EA = 1; //打开中断总允许
AUXR = 0x00; //设置定时器1为串口1提供波特率,
}
串口中断服务函数:
//串口中断服务函数
void Uart_Hander() interrupt 4
{
}
串口通信进阶任务
1.单元训练:串行接口通信
- 在CD107D单片机综合训练平台上,利用51单片机的串行接口与上位机建立数据传输通道,利用8位的UART模式(模式1),设置波特率位9600BPS,数据发送采用查询方式,数据接收采用中断方式,具体要求如下:
- 系统上电初始化后,向上位机发送字符串: “Hello world!” ,换行回车
- 上位机通过串口串口发送单字节指令控制单片机的8个LED开关,单片机在接收到指令后将指令发回上位机
- 上位机通过串口发送单字节指令读取单片机的运行状态
- 通信规则如下:
高四位 | 低四位 | 说明 | |
---|---|---|---|
控制灯光指令1 | A | L4 L3 L2 L1 | 第四位每位控制一个LED灯,0表示关灯,1表示开灯 |
控制灯光指令2 | B | L8 L7 L6 L5 | 例如: 0xA3,打开L1、L2,关闭L3、L4 |
读取信息指令 | C | 0 | 发送字符串:“it is OK!”,回车换行。 |
2.实现思路
- 利用串口发送数据的功能,在上电后发送字符串: “Hello World!”
- 利用串口的接收数据功能,接收到一个字节的数据帧后,对数据进行解析,通过判断数据的格式,调用不同的函数,进行不同的操作。
- 在接收到数据之后,通过串口发送数据的功能,将接收到的数据再发出去。
3.代码实现
1.main.c
#include <STC15F2K60S2.H>
#include "Interrupt.h"
#include "LS138.h"
unsigned char Uart_arr[8] = {'1','2','3','4',5,6,7,8}; //用于存放串口接受的内容
//更具指令改变LED的状态,
void Uart_ControlLED(unsigned char dat)
{
switch(dat & 0xF0)
{
case 0xA0: LED_Write((P0 |0x0F)&(~dat | 0xF0)); //改变低四位LED的亮起情况
break;
case 0xB0: LED_Write((P0 |0xF0)&((~dat << 4)| 0x0F)); //改变高四位LED的亮起情况
break;
case 0xC0: Uart_SerdData("it is OK! \r\n"); //发送字符串"it is OK!"
break;
}
}
void main()
{
Uart_Init(); //串口初始化
Uart_SerdData("Hello world! \r\n"); //发送字符串"HEello world!"
while(1)
{
}
}
//串口中断服务函数
void Uart_Hander() interrupt 4
{
static unsigned char i =0;
if(RI)
{
Uart_arr[i%8] = SBUF;
RI = 0;
Uart_SendByte(Uart_arr[i%8]); //将接受到的内容发送回去
Uart_ControlLED(Uart_arr[i%8]); //通过接受到的指令,改变LED的状态
i++;
}
}
2.串口初始化函数:Uart_Init()
//串口初始化函数,设置模式为波特率可调,八位数据位,通过定时器1提供波特率
void Uart_Init(void)
{
TMOD |= 0x20; //定时器1工作模式为八位自动重装载,
TL1 = TH1 = 0xFD; //设置波特率为9600
TR1 = 1; //打开定时器1
SCON = 0x50; //设置串口工作模式为波特率可调,八位数据位,无校验,允许接受
ES = 1; //打开串口中断允许
EA = 1; //打开中断总允许
AUXR = 0x00; //设置定时器1为串口1提供波特率,
}
3.LED控制函数:LED_Write()
//控制对应的LED亮起,亮起的情况由dat决定,例如: dat = 0xF0 则对应:L1到L4亮起,L5到L8熄灭
void LED_Write(unsigned char dat)
{
LS138_Clear();
LS138_Set(4);
P0 = dat;
}
这里重点讲一下Uart_ControlLED()函数的实现原理:
- 首先我们会有一个传入进来的参数 ‘dat’,我们需要对‘dat’的高四位进行判断,那么我们就可以进行 dat & 0xF0
来获取dat的高四位,再去判断高四位是否符合要求,如果符合,再进行不同的操作。 假设我们现在有一个dat =0xA3 那么: 0xA3 & 0xF0 = 0xA0 。 - 接着,我们需要获取dat 的低四位,再不改变P0高四位的情况下,将 dat 的低四位赋值个 P0 的第四位。但是我们要求的是写1灯亮,但实际上是当这一位为0时灯亮,因此我们需要对dat进行取反后再赋值。
同样 假设 P0 = 0011 1000 , dat = 1010 1001 (L1和L4亮起)
那么我们需要P0 的值是 : 0011 0110(同样是L1和L4亮起)
则可以进行如下操作: (P0 | 0x0F)&(~dat | 0xF0)
P0 | 0x0F = 0011 1111 , ~dat | 0xF0 = 1111 0110
两者按位与完之后就是 0011 0110 刚好是我们希望的。
高四位的控制方法原理相同,这里不做解释
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)