UART串口通讯协议
硬件UART:
1.传输方式:
串行传输,低位先行。
2.传输类型:
帧传输。有校验位则9位一帧,无校验位则8位一帧。
3.引脚数:2
RX
TX
4.波特率:
一秒钟能传输的bit个数。
波特率 = 外设时钟频率÷ 分频系数 ÷16。
外设时钟是指外挂载总线APB1或APB2的时钟,两者频率不同。
要÷16是因为在读取数据时,一位bit需要16个时钟采样,来防止/判断噪音。
所以÷16得到波特率,是为了保证系统时钟能够有足够快的频率,保证对每bit的传输都×16倍时间去检测,得到真正的一位bit发送接收时间。
不然假设16MHz的外设时钟,不分频得到波特率,那么实际一位的时间需要16*16Mhz的时钟才能正确发送,但这已经远远超出了外设时钟的上限(个人理解)(1MHz = 1 000 000 Hz)
5.停止位时间:
发送一个字节数据(8bit)之后,停止位占多少个bit时钟周期(0.5,1,1.5,2),一般1个。(结构体初始化时配置)
6.字节长度:
8位、9位。一般8位不加校验位,9位加校验位。
7.DR寄存器:
内分 发送数据寄存器+发送移位寄存器、接收寄存器+接收移位寄存器。
对软件代码而言,收发都是访问DR地址,不对其内部的地址进行细分。也就是一个地址两种功能,Rdata = DR就是接收;DR = Tdata就是发送。具体逻辑由硬件判断。
发送流程:
①调用函数判断发送数据移位寄存器是否空(标志位TXE—TE Empty空),判断数据有(1)没有(0)从发送数据寄存器被硬件自动转移到发送移位数据寄存器。
②空的话则把数据发送给DR(赋值给DR : DR = Tdata)。
③为了下一次方便发送的话,可以在每次发送后等待TXE=1(即发送移位寄存器空),让下一次数据进来的时候不用等待,直接发送。
接收流程:
①调用函数判断接收数据寄存器是否非空(标志位TXNE — TX Not Empyt ),判断数据有(1)没有(0)从接收移位寄存器被硬件自动转移到接收数据寄存器
②读取值为1(非空)的话,就要读取接收寄存器的数据(Rdata = DR)
③在接收寄存器内数据时(RXNE == 1),对寄存器进行读取,会自动清除标志位
8空闲状态:
TX高电平
9.启动与发送:
启动:TX拉低,并保持一个bit周期,在这个TX拉低开始后,会有16个时钟来采样后续的1bit周期内的TX电平,来确定这个启动是真的启动而不是电平不稳导致。
发送:启动之后的8/9bit时间用于数据的发送,流程也是1bit时间有16个时钟采样才确定最终发送数据。
1bit时间内的16个时钟采样吗,分别在第3、5、7、8、9、10这6个时间内采样,由8、9、10决定最终采样结果:3个相同采样电平则是采样结果,其余按2:1多数发送。
在启动模式时,如果3个采样不是全0,而是2个0与1个1,会产生一个噪音标志位NE(NOISE ERROR)
数据发送:在1bit时间的中间时间段拉高SCL,表示发送数据。(在中间更能保证数据是稳定而非翻转不及时导致)
STM32F103C8T6所有的USART资源:
APB2:USART1;
APB1:USART2,USART3;
对应引脚( 不使用—>不考虑其他应引脚)
USART1:RX(PA10),TX(PA9);
USART2:RX(PA3),TX(PA2);
USART3:RX(PB11),TX(PB10);
10.配置流程:
开启外设时钟——>利用结构体初始化
①开启USART1的外设时钟
②定义结构体,初始化结构体参数(波特率、收发模式、停止位时间、奇偶校验、数据字长、数据流控制(没用到?待研究))
③用结构体初始化USART1
代码(USART1,只发不收)
usart.c
#include "usart.h"/* 初始化 USART1:PA9 TX, PA10 RX */
void USART1_Init(uint32_t baud)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;/* 使能时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);/* PA9 TX AF 推挽 */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);/* PA10 RX 输入浮空 */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure);/* USART 参数 */USART_InitStructure.USART_BaudRate = baud;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_Init(USART1, &USART_InitStructure);/* 使能 USART */USART_Cmd(USART1, ENABLE);
}/* 阻塞发送一个字节(等 TC) */
void USART1_SendByte(uint8_t ch)
{USART_SendData(USART1, ch);while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}// 发送字符串
void USART1_SendString(char *str)
{while (*str){while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);USART_SendData(USART1, *str++);}
}// 发送数据缓冲区
void USART1_SendDataBuffer(uint8_t *buffer, uint16_t length)
{uint16_t i;for (i = 0; i < length; i++){while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);USART_SendData(USART1, buffer[i]);}
}
usart.h
#ifndef __USART_H
#define __USART_H#include "stm32f10x.h"
#include <stdio.h>
#include <stdint.h>void USART1_Init(uint32_t baud);
void USART1_SendByte(uint8_t ch);
void USART1_SendString(char *str);
void USART1_SendDataBuffer(uint8_t *buffer, uint16_t length);#endif
重定向printf(放在usart.c中即可)
/* 重定向 printf 到 USART1 */
int fputc(int ch, FILE *f)
{USART1_SendByte((uint8_t)ch);return ch;
}
main.c
#include "stm32f10x.h"
#include "usart.h"int main(void)
{USART1_Init(115200);while(1){printf("你好\r\n"); }
}