UART——串口详解 沉迷仁王无法自拔>.<
串口通信原理
单工:数据传输只支持数据在一个方向上传输
半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;
全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
同步通信 :带时钟同步信号传输。SPI,IIC通信接口。
异步通信: 不带时钟同步信号。UART(通用异步收发器),单总线。
常见的串行通信接口 :
通信标准
引脚说明
通信方式
通信方向
UART (通用异步收发器)
TXD:发送端 RXD:接受端 GND:公共地
异步通信
全双工
单总线 (1-wire)
DQ:发送/接受端
异步通信
半双工
SPI
SCK:同步时钟 MISO:主机输入,从机输出 MOSI:主机输出,从机输入
同步通信
全双工
I2C
SCL:同步时钟 SDA:数据输入/输出端
同步通信
半双工
异步通信UART包含三点知识:
① 物理层(电气层:接口决定):通信接口(RS232,RS485,RS422,TTL)
② 数据格式(数据层:芯片决定)
③ 通信协议(协议层:程序决定)
UART异步通信方式引脚连接方法:RXD:数据输入引脚。数据接受;TXD:数据发送引脚。数据发送。
TTL 串口 & RS232 & RS485 & RS422
接口类型
逻辑 1 典型值
逻辑 0 典型值
说明
优缺点
TTL
+15/3.3
0
一般MCU串口引脚都支持TTL
RS232
+15V
-15V
3线全双工,点对点
接口电平高,传输速度相对较低,传输距离近
RS485
压差+(2~6)V
压差-(2~6)V
2线半双工,点对多,主从通信。使用压差传递信号。
传输速度高可达10M,抗干扰能力强,距离远。
RS422
相对比较少用
USB串口
STM32 UART异步通信方式引脚:
串口号
RXD
TXD
1
PA10(PB7)
PA9(PB6)
2
PA3(PD6)
PA2(PD5)
3
PB11(PC11/PD9)
PB10(PC10/PD8)
4
PC11(PA1)
PC10(PA0)
5
PD2
PC12
6
PC7(PG9)
PC6(PG14)
STM32串口异步通信需要定义的参数:
① 起始位:1个逻辑0数据位开始
② 数据位(8位或者9位)
③ 奇偶校验位(第9位)
④ 停止位(1,1.5,2位)
⑤ 波特率设置
UART串口波特率计算 STM32串口框图
根据波特率和串口时钟频率,计算出USARTDIV的值。
DIV_Fraction=USART的小数部分 X16所得的整数
DIV_Mantissa=USART的整数部分
串口发送配置流程 HAL库中定义了串口寄存器描述
1 2 3 4 5 6 7 8 9 10 typedef struct { __IO uint32_t SR; __IO uint32_t DR; __IO uint32_t BRR; __IO uint32_t CR1; __IO uint32_t CR2; __IO uint32_t CR3; __IO uint32_t GTPR; } USART_TypeDef;
串口字节发送流程 (寄存器版本):
①编程USARTx_CR1的M位来定义字长。
② 编程USARTx_CR2的STOP位来定义停止位位数。
③ 编程USARTx_BRR寄存器确定波特率。
④ 使能USARTx_CR1的UE位使能USARTx。
⑤ 如果进行多缓冲通信,配置USARTx_CR3的DMA使能(DMAT)。具体请参考后面DMA实验。
⑥使能USARTx_CR1的TE位使能发送器。
⑦向发送数据寄存器TDR写入要发送的数据(对于M3,发送和接收共用DR寄存器)。
⑧向TRD寄存器写入最后一个数据后,等待状态寄存器USARTx_SR(ISR)的TC位置1,传输完成。
串口发送程序配置过程 (HAL库) :
①初始化串口相关参数,使能串口:
1 HAL_UART_Init(UART_HandleTypeDef *huart);
UART_HandleTypeDef串口句柄
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 typedef struct { USART_TypeDef *Instance; UART_InitTypeDef Init; uint8_t *pTxBuffPtr; uint16_t TxXferSize; uint16_t TxXferCount; uint8_t *pRxBuffPtr; uint16_t RxXferSize; uint16_t RxXferCount; DMA_HandleTypeDef *hdmatx; DMA_HandleTypeDef *hdmarx; HAL_LockTypeDef Lock; __IO HAL_UART_StateTypeDef State; __IO uint32_t ErrorCode; }UART_HandleTypeDef;
串口参数初始化结构体UART_InitTypeDef Init;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 typedef struct { uint32_t BaudRate; uint32_t WordLength; uint32_t StopBits; uint32_t Parity; uint32_t Mode; uint32_t HwFlowCtl; uint32_t OverSampling; }UART_InitTypeDef;
②串口相关IO口配置,复用配置:在HAL_UART_MspInit中调用HAL_GPIO_Init函数。
③发送数据,并等待数据发送完成:HAL_UART_Transmit()函数;
1 HAL_StatusTypeDef HAL_UART_Transmit (UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) ;
有限性判断——可以查看函数提供的有限性判断确定要输入的值有哪些!
串口有效性判断
1 IS_UART_HWFLOW_INSTANCE(huart->Instance);
1 2 3 4 #define IS_UART_HWFLOW_INSTANCE(INSTANCE) (((INSTANCE) == USART1) || \ ((INSTANCE) == USART2) || \ ((INSTANCE) == USART3) || \ ((INSTANCE) == USART6))
字长有效性判断
1 IS_UART_WORD_LENGTH(huart->Init.WordLength);
1 2 #define IS_UART_WORD_LENGTH(LENGTH) (((LENGTH) == UART_WORDLENGTH_8B) || \ ((LENGTH) == UART_WORDLENGTH_9B))
串口发送过程函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include "sys.h" #include "delay.h" #include "usart.h" UART_HandleTypeDef usart1_handler; void uart_init1 () { usart1_handler.Instance=USART1; usart1_handler.Init.BaudRate=115200 ; usart1_handler.Init.WordLength=UART_WORDLENGTH_8B; usart1_handler.Init.StopBits=UART_STOPBITS_1; usart1_handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; usart1_handler.Init.Mode=UART_MODE_TX_RX; usart1_handler.Init.Parity=UART_PARITY_NONE; HAL_UART_Init(&usart1_handler); } void HAL_UART_MspInit (UART_HandleTypeDef *huart) { GPIO_InitTypeDef GPIO_Initure; if (huart->Instance==USART1) { __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART1_CLK_ENABLE(); GPIO_Initure.Pin=GPIO_PIN_9; GPIO_Initure.Mode=GPIO_MODE_AF_PP; GPIO_Initure.Pull=GPIO_PULLUP; GPIO_Initure.Speed=GPIO_SPEED_FAST; GPIO_Initure.Alternate=GPIO_AF7_USART1; HAL_GPIO_Init(GPIOA,&GPIO_Initure); GPIO_Initure.Pin=GPIO_PIN_10; HAL_GPIO_Init(GPIOA,&GPIO_Initure); } } int main (void ) { u8 buff[]="test" ; HAL_Init(); Stm32_Clock_Init(360 ,25 ,2 ,8 ); delay_init(180 ); uart_init1(); while (1 ) { HAL_UART_Transmit(&usart1_handler,buff,sizeof (buff),1000 ); delay_ms(300 ); } }
串口接收流程 串口接收流程(寄存器版本):
①编程USARTx_CR1的M位来定义字长。
② 编程USARTx_CR2的STOP位来定义停止位位数。
③ 编程USARTx_BRR寄存器确定波特率。
④ 使能USARTx_CR1的UE位使能USARTx。
⑤ 如果进行多缓冲通信,配置USARTx_CR3的DMA使能(DMAT)。具体请参考后面DMA实验。
⑥使能USARTx_CR1的RE位为1使能接收器。
⑦如果要使能接收中断(接收到数据后产生中断),使能USARTx_CR1的RXNEIE位为1。
当串口接收到数据时:
① USARTx_SR(ISR)的RXNE位置1。表明移位寄存器内容已经传输到RDR(DR)寄存器。已经接收到数据并且等待读取。
②如果开启了接收数据中断(USARTx_CR1寄存器的RXNEIE位为1),则会产生中断。(程序上会执行中断服务函数)
③如果开启了其他中断(帧错误等),相应标志位会置1。
④读取USARTx_TDR(DR)寄存器的值,该操作会自动将RXNE位清零,等待下次接收后置位。
串口接收中断程序配置过程 (HAL库):
①初始化串口相关参数,使能串口:HAL_UART_Init();//同发送
②串口相关IO口配置,复用配置:
在HAL_UART_MspInit中调用HAL_GPIO_Init函数。//同发送即配置GPIO口
③串口接收中断优先级配置和使能:
HAL_NVIC_EnableIRQ();
HAL_NVIC_SetPriority();
④使能串口接收中断:HAL_UART_Receive_IT();
1 HAL_StatusTypeDef HAL_UART_Receive_IT (UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
⑤编写中断服务函数:USARTx_IRQHandler
中断处理函数
1 void HAL_UART_IRQHandler (UART_HandleTypeDef *huart) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 void HAL_UART_IRQHandler (UART_HandleTypeDef *huart) { uint32_t tmp1 = 0 , tmp2 = 0 ; tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_PE); tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_PE); if ((tmp1 != RESET) && (tmp2 != RESET)) { __HAL_UART_CLEAR_PEFLAG(huart); huart->ErrorCode |= HAL_UART_ERROR_PE; } tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_FE); tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR); if ((tmp1 != RESET) && (tmp2 != RESET)) { __HAL_UART_CLEAR_FEFLAG(huart); huart->ErrorCode |= HAL_UART_ERROR_FE; } tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_NE); tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR); if ((tmp1 != RESET) && (tmp2 != RESET)) { __HAL_UART_CLEAR_NEFLAG(huart); huart->ErrorCode |= HAL_UART_ERROR_NE; } tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_ORE); tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR); if ((tmp1 != RESET) && (tmp2 != RESET)) { __HAL_UART_CLEAR_OREFLAG(huart); huart->ErrorCode |= HAL_UART_ERROR_ORE; } tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE); tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE); if ((tmp1 != RESET) && (tmp2 != RESET)) { UART_Receive_IT(huart); } tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_TXE); tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE); if ((tmp1 != RESET) && (tmp2 != RESET)) { UART_Transmit_IT(huart); } tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_TC); tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TC); if ((tmp1 != RESET) && (tmp2 != RESET)) { UART_EndTransmit_IT(huart); } if (huart->ErrorCode != HAL_UART_ERROR_NONE) { huart->State = HAL_UART_STATE_READY; HAL_UART_ErrorCallback(huart); } }
由中端服务函数调用中断处理函数判断不同的中断类型以调用不同处理函数——如下图
串口接收中断流程
调用回调函数,用户可以自己定义
1 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
串口接受过程函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include "sys.h" #include "delay.h" #include "usart.h" UART_HandleTypeDef usart1_handler; u8 rdata[1 ]; void uart_init1 () { usart1_handler.Instance=USART1; usart1_handler.Init.BaudRate=115200 ; usart1_handler.Init.WordLength=UART_WORDLENGTH_8B; usart1_handler.Init.StopBits=UART_STOPBITS_1; usart1_handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; usart1_handler.Init.Mode=UART_MODE_TX_RX; usart1_handler.Init.Parity=UART_PARITY_NONE; HAL_UART_Init(&usart1_handler); } void HAL_UART_MspInit (UART_HandleTypeDef *huart) { GPIO_InitTypeDef GPIO_Initure; if (huart->Instance==USART1) { __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART1_CLK_ENABLE(); GPIO_Initure.Pin=GPIO_PIN_9; GPIO_Initure.Mode=GPIO_MODE_AF_PP; GPIO_Initure.Pull=GPIO_PULLUP; GPIO_Initure.Speed=GPIO_SPEED_FAST; GPIO_Initure.Alternate=GPIO_AF7_USART1; HAL_GPIO_Init(GPIOA,&GPIO_Initure); GPIO_Initure.Pin=GPIO_PIN_10; HAL_GPIO_Init(GPIOA,&GPIO_Initure); HAL_NVIC_SetPriority(USART1_IRQn,3 ,3 ); HAL_NVIC_EnableIRQ(USART1_IRQn); } } void USART1_IRQHandler (void ) { HAL_UART_IRQHandler(&usart1_handler); HAL_UART_Receive_IT(&usart1_handler,rdata,sizeof (rdata)); } void HAL_UART_RxCpltCallback (UART_HandleTypeDef *huart) { u8 rec; if (huart->Instance==USART1) { rec=*((huart->pRxBuffPtr)-1 ); HAL_UART_Transmit(&usart1_handler,&rec,1 ,1000 ); } } int main (void ) { HAL_Init(); Stm32_Clock_Init(360 ,25 ,2 ,8 ); delay_init(180 ); uart_init1(); HAL_UART_Receive_IT(&usart1_handler,rdata,sizeof (rdata)); while (1 ) { } }
中断服务函数和回调函数均为HAL库中定义的__weak弱函数,可由用户重新定义以实现其功能。
串口接受发送实验 接受的数据为ABCD……..(0x0D)(0x0A)格式,单片机才会接受,具体实现逻辑如下:
把每个接收到的数据保存在一个程序定义的Buffer数组中(数组长度为200),同时把接收到的数据个数保存在定义的变量中。程序通过对接收到的每个数据进行结束判断(接收到回车0x0d之后再接收到换行0x0a),程序接收结束之后,设置相应的标记位,标记结束。。。外部 循环通过判断标志位来判断程序结束,然后一次性通过串口1发送出来。发送完成之后,所有标志位和数据量都清零。
USART_RX_STA为一个16位的寄存器
USART_RX_STA
bit15
bit14
bit13~0
接收完成标志
接收到0X0D标志(置为1 )
接收到的有效数据个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void HAL_UART_RxCpltCallback (UART_HandleTypeDef *huart) { if (huart->Instance==USART1) { if ((USART_RX_STA&0x8000 )==0 ) { if (USART_RX_STA&0x4000 ) { if (aRxBuffer[0 ]!=0x0a )USART_RX_STA=0 ; else USART_RX_STA|=0x8000 ; } else { if (aRxBuffer[0 ]==0x0d )USART_RX_STA|=0x4000 ; else { USART_RX_BUF[USART_RX_STA&0X3FFF ]=aRxBuffer[0 ] ; USART_RX_STA++; if (USART_RX_STA>(USART_REC_LEN-1 ))USART_RX_STA=0 ; } } } } }