在嵌入式系统开发中,串口通信是最基础且重要的通信方式之一。无论是设备调试、模块对接还是远程通信,串口都扮演着关键角色。本文将从通信协议原理出发,结合STM32F4系列MCU,深入讲解串口通信的硬件实现和软件配置,并通过实战案例展示其应用。
串行接口(Serial Port)采用逐位顺序传输方式,其优势在于:
常见串口类型对比:
类型 | 电平标准 | 传输距离 | 通信方式 |
---|---|---|---|
TTL | 0-3.3V/5V | <3m | 全双工 |
RS232 | ±3-15V | 15m | 全双工 |
RS485 | ±1.5-6V | 1200m | 半双工 |
物理层规范:
协议层规范:
标准异步串行数据帧构成:
[Start Bit][Data Bits][Parity Bit][Stop Bits]
典型参数配置示例:
c
Copy
/* 8位数据位,无校验,1位停止位 */
UART_InitStructure.WordLength = UART_WORDLENGTH_8B;
UART_InitStructure.StopBits = UART_STOPBITS_1;
UART_InitStructure.Parity = UART_PARITY_NONE;
校验方式性能对比:
校验类型 | 错误检测能力 | 数据开销 |
---|---|---|
奇校验 | 单比特错误 | +1 bit |
偶校验 | 单比特错误 | +1 bit |
CRC校验 | 多比特错误 | +1-4字节 |
https://example.com/stm32-usart-block-diagram.png
关键功能模块:
时钟树路径:
APBx总线时钟 → USARTDIV分频器 → 生成比特周期
波特率计算公式:
波特率 = fCK / (16 * USARTDIV)
其中USARTDIV为16位浮点数(高12位整数,低4位小数)
配置示例(72MHz时钟,115200波特率):
c
Copy
// USARTDIV = 72000000/(16 * 115200) = 39.0625
USART1->BRR = (39 << 4) | 0x1; // 0x271
STM32F407 USART1引脚映射:
功能 | 引脚 | 复用功能 |
---|---|---|
TX | PA9 | AF7 |
RX | PA10 | AF7 |
CTS | PA11 | AF7 |
RTS | PA12 | AF7 |
配置代码示例:
c
Copy
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
初始化流程:
c
Copy
UART_HandleTypeDef huart1;
void USART1_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);
}
数据收发函数:
c
Copy
// 阻塞式发送
HAL_UART_Transmit(&huart1, (uint8_t*)"Hello", 5, 1000);
// 中断接收
HAL_UART_Receive_IT(&huart1, &rx_data, 1);
中断服务函数示例:
c
Copy
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
}
// 回调函数实现
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1){
// 处理接收数据
process_data(rx_data);
// 重新使能接收
HAL_UART_Receive_IT(huart, &rx_data, 1);
}
}
DMA配置示例:
c
Copy
// 发送DMA配置
hdma_usart1_tx.Instance = DMA2_Stream7;
hdma_usart1_tx.Init.Channel = DMA_CHANNEL_4;
hdma_usart_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart_tx.Init.Mode = DMA_NORMAL;
HAL_DMA_Init(&hdma_usart_tx);
// 启动DMA传输
HAL_UART_Transmit_DMA(&huart1, tx_buffer, BUFFER_SIZE);
重定向printf函数:
c
Copy
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 1000);
return ch;
}
使用示例:
c
Copy
printf("System Clock: %ld Hz\r\n", HAL_RCC_GetSysClockFreq());
协议帧结构:
[地址][功能码][数据][CRC校验]
CRC16校验实现:
c
Copy
uint16_t CRC16(uint8_t *buf, int len)
{
uint16_t crc = 0xFFFF;
for(int pos=0; pos<len; pos++){
crc ^= (uint16_t)buf[pos];
for(int i=8; i!=0; i--){
if((crc & 0x0001) != 0){
crc >>= 1;
crc ^= 0xA001;
}
else
crc >>= 1;
}
}
return crc;
}
通过本文的系统讲解,我们不仅深入理解了串口通信的协议原理,还掌握了STM32平台下的硬件配置和软件开发技巧。在实际项目中,建议根据具体需求选择合适的通信模式(轮询/中断/DMA),并注意做好电磁兼容设计。随着对串口技术的深入掌握,开发者可以更高效地实现各种工业通信需求。