嵌入式串口通信研究
一、 通信过程:
通信方将信息编码 → 传输信息 → 接收方接收信号并解码
串口使用的图
二、 串口通信基本概念:
1) 同步通信和异步通信的区别
同步(USART):发送方和接收方按照统一的时序传输,适合通信时间不固定
USART是全双工。
异步(UART):发送方和接收房按照各自的时序工作,适合通信频率固定
UART是半双工。
2) 电平信号和差分信号
电平信号:有参考电平(GND)
差分信号:没有参考电平,两根电线的电压差,差分信号传输质量好。
3) 并行接口和串行接口
4) 波特率、数据格式
波特率:双方协商好的每秒传输的二进制位,常用9600、115200
波特率的设置:
FCLK:外设输入时钟
OSRVAL:过采样率,决定了一个采样一个数据需要几个时钟
BRGVVAL:波特率发生器值,决定外设时钟分频多少后用于串口外设
数据格式:起始位+数据位+奇偶校验位+停止位
5) 串口通信的基本框图
异步通信UART
同步通信USART
三、 协议介绍
概述:传输过程中,传输双方需要协商好,这个协商出的方案就是协议
目前使用较多的是UART,下面我们先介绍UART
1. UART传输时序:
UART是一种通用串行数据总线,数据传输过程是在数据线上一位一位的传输的。数据格式如下
2. USART时钟极性
时钟极性控制位---CPOL
决定了总线空闲时,SCK时钟线的电平状态
CPOL = 0 当数据总线空闲时,SCK时钟线为低电平
CPOL = 1 当数据总线空闲时,SCK时钟线为高电平
时钟相位控制位---CPHA
决定了总线上数据采样位置
CPHA = 0 总线在时钟线第一个跳变沿采样数据。
CPHA = 1 总线在时钟线第二个跳变沿采样数据。
3. USART传输时序
USART通过一根时钟线进行同步
4. 硬件流控
流控的目的就是可以保证数据发送和接受正好配合(因为如果发送方比接收方速度快,可能会导致数据丢失)。
目前流控使用很少,因为现在硬件水平很高,接受方速度很快。目前串口更多是用来测试调试信息。
5. 软件流控
6. 传输协议
传输协议在应用所处的位置如下图所示:
串口通讯中经常使用的几种文件传输协议:
l Xmodem
l 1KXmodem
l Ymodem
l Zmodem
l KERMIT
串口通讯中经常使用的总线协议:
Modbus协议
RS232
RS484
这里不详细论述
四、 芯片中的UART外设
1. UART外设
UART外设串口控制器,串口控制器分为transmitter和receiver两部分,两部分彼此独立,且这两部分都由缓冲区和移位器组成(缓冲区是关键,移位器是硬件控制的)。
1) FIFO模式
典型的串口设计,每次发送/接收只能处理一字节,这样效率低下,CPU要不断切换上下文,于是比较高级的CPU扩展了发送缓冲器(Transmit FIFO Register,FIFO是一种数据结构,队列:先进先出),FIFO模式始终轻量级的解决方案
2) DMA模式
直接内存访问。DMA本身是DSP中的一种技术,DMA技术的核心就是在交换数据时不需要CPU参与,模块自己完成。DMA模式适合大量数据迸发式的发送/接收时。
3) IrDA模式及其用法
IrDA就是红外通信,某些芯片支持红外模式,我盟只需向串口写数据,这些数据就会以红外光的方式向外发射出去,后接收方接收这些红外数据即可解码得到我们的发送信息。
2. 串口通信与中断的关系
一般使用中断来接收数据
五、 配置举例
如果使用固件库变成或者原厂提供的工具生成的库例如ST的STM32Cube、NXP的MCUconfigtool等等,你只需知道一些重要的参数及其意义即可,很多都已经写好了,如果为了提高效率使用寄存器直接开发的话,需要对照参考手册(也叫用户手册)去逐一配置了
1) 使用流程概述
2) STM32的使用举例(HAL库)
串口初始化
void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
引脚模式配置
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(uartHandle->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspInit 0 */
/* USER CODE END USART2_MspInit 0 */
/* USART2 clock enable */
__HAL_RCC_USART2_CLK_ENABLE();
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PD6 ------> USART2_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* USER CODE BEGIN USART2_MspInit 1 */
/* USER CODE END USART2_MspInit 1 */
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspDeInit 0 */
/* USER CODE END USART2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART2_CLK_DISABLE();
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PD6 ------> USART2_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2);
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_6);
/* USER CODE BEGIN USART2_MspDeInit 1 */
/* USER CODE END USART2_MspDeInit 1 */
}
}
将debug信息通过串口输出
/* Includes ------------------------------------------------------------------*/
#include "usart.h"
#include <stdio.h>
#include "gpio.h"
///* USER CODE BEGIN 0 */
// #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
///* USER CODE END 0 */
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
UART_HandleTypeDef huart2;
/* USART2 init function */
/* USER CODE BEGIN 1 */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
huart2.Instance->DR = (uint8_t) ch;
/* Loop until the end of transmission */
while(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TC) == RESET){}
return ch;
}
六、 注意事项
l 使用USART时需要区分主从模式;
l 使用硬件流控制时需要根据实际芯片介绍连接RTS与CTS
l 使用软件流控制时,若传输的是二进制数据,标志字符也有可能在数据流中出现;
l 引脚配置时注意,所使用的引脚是不是开漏引脚;
l 普通电线和RS232的布线距离不易过长,如果使用长距离通讯建议选择RS485或RS422;
l 过采样率设置的越小,抑制噪声能力越差