STM8S串口通讯协议のUART的实现
理解STM8S的UART关于理解以下几个概念:
-
协议收发格式
- 起始位(start bit)
- 数据位 (a date word)
- 结束位(stop bit)
奇偶校验位(parity)
-
帧(frame)
- 以起始位 + 数据位 + 结束为 +
奇偶校验位构成的一次数据收发被称为帧,同时也是UART传输的基本单位 - UART时序如图,截自STM8S参考手册:
- 以起始位 + 数据位 + 结束为 +
-
状态寄存器(status register)
- 可以将寄存器中的每一个bit看作一支flag,通过读状态寄存器某个bit可以监视数据的收发状态,决定是否该继续收发
如在接收数据时,数据还没有从RSR(Receive Shift Register)中转移到RDR(Receive Date Register),那么此时就不应该读数据,因为被读的数据将存放在RDR中
此时就可以用 while(!(UART1_SR && (1<<5))); 来监视数据是否已经转移完成,如完成,则仅需开始进行读操作
- 可以将寄存器中的每一个bit看作一支flag,通过读状态寄存器某个bit可以监视数据的收发状态,决定是否该继续收发
-
数据寄存器(Date Register)
- 如图,截自STM8S参考手册:
可见数据寄存器并非简单的指一个寄存器,而是由四个寄存器组成的寄存器组(这就是为什么UART能做到全双工通讯的原因,接收和发送由不同的寄存器分别处理)。
以接收和发送数据为目的划分,数据帧在寄存器组中有以下流向关系:
接收数据: 数据帧接收 -> RSR -> RDR
发送数据:数据帧发送 -> TDR -> TSR
- 如图,截自STM8S参考手册:
-
16位波特率寄存器(Baud Rate Registers)
- 波特率寄存器由两个8位寄存器UART_BRR2和UART_BRR1构成(UART_BRR2应先与UART_BRR1被赋值,因为当UART_BRR1被写入一个值,UART的通讯波特率就会被更新;但在传输过程中这是不被允许的),红字信息来源STM8S参考手册
- UART_BRR2和UART_BRR1的值根据所需波特率求得,如下图:
代码及实现思路O(∩_∩)O
/* MAIN.C file
*
* Copyright (c) 2002-2005 STMicroelectronics
*/
#include <stm8s003f3.h>
// 功能:输入一个字符,将字符+1,然后输入
// 思路
// 1. 时钟初始化
// 2. UART初始化 - 波特率 - 数据位 - 基偶校验位 - 结束位
// 3. 接收数据函数 - 接收的主体是STM8S
// 4. 发送数据函数 - 发送的主体是STM8S
// 5. 在主函数写实现逻辑
// ATTENTION: 通过UART_SR(status register)的bit来monitor发送/接收是否完成(注意查看参考手册是否需要清0)
unsigned char UART_RX(void);
void UART_TX(unsigned char);
void UART_Init(void);
main()
{
// 初始化时钟
// 不知这种直接写寄存器有没有用,STM8S默认时钟频率为2Mhz
CLK_CKDIVR = 0x00; // set clock to 16Mhz
CLK_PCKENR1 = 0xFF; // enable periperals
UART_Init();
while (1)
{
// 实参,发送到屏幕的数据
// 要传给UART_TX函数
unsigned char ch = 0;
// 功能:STM8s通过UART将从键盘输入的字符+1,然后再返回到屏幕
// 实现:
// 0. 开始
// 1. 处理数据前,想判断一下在RSR中的数据是否已经被转移
// 到RDR。理由是防止数据还没转移到RDR就被新输入的数据刷新掉
// 2. 先调用UART_RX,通过返回值,查看从键盘上输入的
// 存放在UART_DR的字符
// 3. 将UART_RX的返回值赋值给实参ch
// 4. 将ch++
// 5. 将自增后的ch作为形参,传给UART_TX函数
// 6. 结束
// 实现
if(UART1_SR && (1<<5))
{
ch = UART_RX();
ch++;
UART_TX(ch);
}
}
}
// @brief 接收函数
// @retval unsigned char
// @param null
unsigned char UART_RX(void)
{
// 等待数据从RSR(Receive Shift Register)转移到RDR(Receive Date Register)
// 查看参考手册UART_SR哪一位用来查看该状态
// Bit5为RXNE位,该为置1,则表示数据已经从RSR -> RDR,则需要读UART_DR中的数据(返回该寄存器值即可)
while(!(UART1_SR && (1<<5)));
return UART1_DR;
}
// @brief 发送函数
// @retval null
// @param 发送UART_DR中的数据(即TDR中的数据)
void UART_TX(unsigned char val){
// 数据发送,将形参val赋值给UART1_DR
UART1_DR = val;
// 参考RX函数中的写法
// 等待数据从TDR转移到TSR中
// Bit7为TXE位,该位置1,则表示数据已从TDR -> TSR
// 可以进行数据发送
//while(!(UART1_SR && (1<<7)));
// 等待数据发送完成
// Bit6为bit complete位,置1表示数据已发送完毕!!!!!!
while(!((UART1_SR && (1<<6))));
}
// @brief UART1初始化函数
void UART_Init(void)
{
// 求波特率具体方式查看芯片参考手册
UART1_BRR2 = 0x03;
UART1_BRR1 = 0x68; // 9600 baud,先写BBR2寄存器
UART1_CR3 &= ~((1<<4) | (1<<5)); // 1 stop bit
UART1_CR1 &= ~((1<<4)); // 8 bits Date, no parity
// 全部设置完后,使能!!!
UART1_CR2 |= (1<<2) | (1<<3); // set receiver and transmitter enable without interrupt
}
结果
实现预期效果,且收发正常