目录
1、通信分类
(1)按数据通信方式分类,可分为串行通信和并行通信两种。
串行通信的基本特征是数据逐位顺序依次传输,优点是传输线少、布线成本低、灵活度高,一般用于近距离人机交互,特殊处理后也可以用于远距离,缺点是传输速率低。
并行通信是数据各位可以通过多条线同时传输,优点是传输速率高,缺点就是布线成本高,抗干扰能力差因而适用于短距离、高速率的通信。
(2)根据数据传输方向,通信又可分为全双工、半双工和单工通信。
单工是指数据传输仅能沿一个方向,不能实现反方向传输,如校园广播。
半双工是指数据传输可以沿着两个方向,但是需要分时进行,共用一条线路实现双向通信,如对讲机。
全双工是指数据可以同时进行双向传输,用两条线路,一条用于发送数据,另一条用于接收数据,日常的打电话属于这种情形。
(3)根据数据同步方式,通信又可分为同步通信和异步通信。
同步通信要求通信双方共用同一时钟信号,在总线上保持统一的时序和周期完成信息传输。优点:可以实现高速率、大容量的数据传输,以及点对多点传输。缺点:要求发送时钟和接收时钟保持严格同步,收发双方时钟允许的误差较小,同时硬件复杂。
异步通信不需要时钟信号,而是在数据信号中加入开始位和停止位等一些同步信号,以便使接收端能够正确地将每一个字符接收下来,某些通信中还需要双方约定传输速率。优点:没有时钟信号硬件简单,双方时钟可允许一定误差。缺点:通信速率较低,只适用点对点传输。
简单区分同步和异步就是看通信时需不需要对外提供时钟输出。
2、串口通信协议:
(1)波特率
波特率表示每秒钟传送的码元符号的个数,它决定了数据帧里面每一个位的时间长度。两个要通信的设备的波特率一定要设置相同。
(2)数据帧
数据帧格式需要提前约定好,串口通信的数据帧包括起始位、停止位、有效数据位以及校验位。
1. 起始位和停止位
串口通信的一个数据帧是从起始位开始,直到停止位。数据帧中的起始位是由一个逻辑 0 的数据位表示,而数据帧的停止位可以是 0.5、1、1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。
2. 有效数据位
数据帧的起始位之后,就接着是数据位,也称有效数据位,这就是我们真正需要的数据,有效数据位通常会被约定为 5、6、7 或者 8 个位长。有效数据位是低位(LSB)在前,高位(MSB)在后。
3. 校验位
校验位可以认为是一个特殊的数据位。校验位一般用来判断接收的数据位有无错误,检验方法有:奇检验、偶检验、0 检验、1 检验以及无检验。
奇校验要求帧数据和校验位中“1”的个数为奇数。比如一个 8 位长的有效数据为:10101001,总共有 4 个“1”,为达到奇校验效果,校验位设置为“1”,最后传输的数据是 8 位的有效数据加上 1 位的校验位总共 9 位。
偶校验要求帧数据和校验位中“1”的个数为偶数。
0 校验是校验位总为“0”。
1 校验是校验位总为“1”。
无校验是指数据帧中不包含校验位。
3、程序
串口初始化
#define USART_REC_LEN 200 /* 定义最大接收字节数 200 */
#define USART_EN_RX 1 /* 使能(1)/禁止(0)串口 1 接收 */
#define RXBUFFERSIZE 1 /* 缓存大小 */
/**
* @brief 串口 X 初始化函数
* @param baudrate: 波特率, 根据自己需要设置波特率值
* @note 注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.
* 这里的 USART 的时钟源在 sys_stm32_clock_init()函数中已经设置过了.
* @retval 无
*/
void usart_init(uint32_t baudrate)
{
uartx_handle.Instance = USART1; /* USART1 */
uartx_handle.Init.BaudRate = baudrate; /* 波特率 */
uartx_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为 8 位数据格式 */
uartx_handle.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */
uartx_handle.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */
uartx_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
uartx_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */
HAL_UART_Init(&uartx_handle); /* HAL_UART_Init()会使能 UART1 */
/*该函数会开启接收中断:标志位 UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量*/
HAL_UART_Receive_IT(&uartx_handle, (uint8_t *)aRxbuffer, RXBUFFERSIZE);
}
引脚初始化
/**
* @brief UART 底层初始化函数
* @param huart: UART 句柄类型指针
* @note 此函数会被 HAL_UART_Init()调用
* 完成时钟使能,引脚配置,中断配置
* @retval 无
*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef gpio_init_struct;
if(huart->Instance == USART_UX) /* 如果是串口 1,进行串口 1 MSP 初始化 */
{
__HAL_RCC_USART1_CLK_ENABLE(); /* USART1 时钟使能 */
__HAL_RCC_GPIOA_CLK_ENABLE(); /* 发送引脚时钟使能 */
__HAL_RCC_GPIOA_CLK_ENABLE(); /* 接收引脚时钟使能 */
gpio_init_struct.Pin = GPIO_PIN_9; /* TX 引脚 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
gpio_init_struct.Alternate = GPIO_AF7_USART1; /* 复用为 USART1 */
HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 初始化发送引脚 */
gpio_init_struct.Pin = GPIO_PIN_10; /* RX 引脚 */
gpio_init_struct.Alternate = GPIO_AF7_USART1;/* 复用为 USART1 */
HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 初始化接收引脚 */
#if USART_EN_RX
HAL_NVIC_EnableIRQ(USART1_IRQn); /* 使能 USART1 中断通道 */
HAL_NVIC_SetPriority(USART1_IRQn, 3, 3); /* 抢占优先级 3,子优先级 3 */
#endif
}
}
中断函数
/**
* @brief 串口 X 中断服务函数
注意,读取 USARTx->SR 能避免莫名其妙的错误
* @param 无
* @retval 无
*/
void USART_UX_IRQHandler(void)
{
#if SYSTEM_SUPPORT_OS /*使用 OS*/
OSIntEnter();
#endif
HAL_UART_IRQHandler(&uartx_handler); /*调用 HAL 库中断处理公用函数*/
while (HAL_UART_Receive_IT(&uartx_handler, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK) /*一次处理完成之后,重新开启中断并设置 RxXferCount 为 1*/
{
/* 如果出错会卡死在这里 */
}
#if SYSTEM_SUPPORT_OS /*使用 OS*/
OSIntExit();
#endif
}
串口回调函数
/**
* @brief UART 数据接收回调接口
数据处理在这里进行
* @param huart:串口句柄
* @retval 无
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1) /* 如果是串口 1 */
{
if((g_usart_rx_sta & 0x8000) == 0) /* 接收未完成 */
{
if(g_usart_rx_sta & 0x4000) /* 接收到了 0x0D(即回车键)*/
{
if(aRxBuffer[0] != 0x0A) /* 接收到的不是 0x0A(即不是换行键)*/
{
g_usart_rx_sta = 0; /*接收错误,重新开始 */
}
else /* 接收到的是 0x0a(即换行键)*/
{
g_usart_rx_sta |= 0x8000; /* 接收完成了 */
}
}
else /* 还没收到 0x0D(即回车键) */
{
if (aRxBuffer[0] == 0x0D)
g_usart_rx_sta |= 0x4000;
else
{
g_usart_rx_buf[g_usart_rx_sta & 0x3FFF] = aRxBuffer[0];
g_usart_rx_sta++;
if (g_usart_rx_sta > (USART_REC_LEN - 1))
{
g_usart_rx_sta = 0; /* 接收数据错误,重新开始接收 */
}
}
}
}
}
}