±-------------------------+
B站:视频链接
单片机-通信基础
±-------------------------+
UART 概述
位段 | 内容 |
---|---|
空闲位 | TX保持高电平 |
起始位 | TX由高变低,保持=1bit位宽 |
数据为 | 5~8位ASCII的编码数据 |
校验位 | 校验方式:奇校验 / 偶校验 |
停止位 | TX由低变高,保持>=0.5bit位宽 |
波特率9600;
8N1: 8位数据位, 无校验位,1位停止位;
发送时序:
1> 定时器1作,脉冲发生器;
2> 并转串 移位寄存器,作数据通路;
3> TX/RX ConTROL单元,作控制单元;
注意: UART模块内部通过SMOD 和 [/16分频] 对定时器的溢出率进行分频
通过74HC165芯片,了解 移位寄存器,工作本质
一个CLK移动移位;
通过定时器1的溢出,产生CLK方波;
STC89C516RD+ 有3个定时器, 通常,Tim1 用作串口波特率发生器, Tim2用于整个系统定时, Timer0用于其他;
/**
* @brief UART initialization
* @note 8N1, 115200
*/
void UART_Init(void)
{
ES = 0; // 禁止串口中断;
ET1 = 0; // 禁止定时器1中断
RI = 0; // 清零接收中断请求标志
/* UART Config */
SCON = 0x50; // 串口工作模式:8位数据位, 0101 0000
PCON |= 0x80; // 串口波特率加倍;1000 0000
/* Tim1 Config */
TMOD &= 0x0F;
TMOD |= 0x20; // 定时器1工作模式2:8位自动重装载
TH1 = 0x100 - (SYS_CLK / 12 * 2 / 32 / 115200);
TL1 = TH1; // 定时器1初始值
TR1 = 1; // 启动定时器1
}
对SYSclk 分频后, 进行+1 计数器;
当加满溢出时, 硬件自动将TH1值装入TL1;
定时器溢出率 = SYSclk / 12 / (2 ^ 8 - N);
相关概念:
定时器的溢出率 : 1S内定时器加满次数,
波特率: 1S内传输二进制bit位数;
公式总结:
串口方式1, 定时器模式8位自动重装载:
波特率 = 定时器溢出率 * ( 2 ^ SMOD / 32 ) ;
定时器溢出率 = SYSclk / 12 / (2 ^ 8 - N);
波特率 = SYSclk / 12 / (256 - N) * ( 2 ^ SMOD / 32 )
256 - N = SYSclk / 12 * (2 ^ SMOD / 32) / 波特率
N = 256 - SYSclk / 12 * (2 ^ SMOD / 32) / 波特率
以外部晶振22.1184MHz, 12T模式, 加倍计算, 产生115200波特率的N值;
N = 256 - (22118400 / 12 * 2 / 32 / 115200);
= 256 - 1
= 255 //0xFF
与串口波特率计算工具的结果一样;
轮询方式,注意先发送数据,再判断TI;
/**
* @brief UART put one Byte
* @c put data
*/
void UART_Putchar(uchar8_t c)
{
SBUF = c;
while(!TI) {
/* Nothing */ ;
}
TI = 0;
}
void bsp_uart_putstring(unsigned char *str)
{
while (*str) {
if (*str == '\n') {
bsp_uart_putbyte('\r');
}
bsp_uart_putbyte(*str++);
}
}
Windows系统对于文本都是以‘\r’回车结束,再’\n’换行,所以要插入一个‘\r’;
用字节写的发送一个字符函数,编写库函数的putchar, 因为库函数printf调用putchar输出;
#include
char putchar(char c)
{
ES = 0;
if (c == '\n') {
SBUF = '\r';
while ( !TI ) { /* waiting */; }
TI = 0;
}
SBUF = c;
while ( !TI ) { /* waiting */; }
TI = 0;
ES = 1;
return 1;
}
格式化输出方式,注意char 用 %bd;
使用printf测试数据类型,所占内存大小
好嘛,有了printf, 51上就可以练习C语言了;