前言在 STM32、单片机、机器人和嵌入式项目中最常见的通信方式主要有 UART 串口、I2C、SPI 和 CAN。它们都属于串行通信但适用场景差别很大。本文从通信特点、接线方式、优缺点、典型应用和 STM32 HAL 模板代码几个方面进行总结适合用于快速复习和项目选型。一、四种通信方式总览通信方式常用引脚通信类型主从关系典型速率通信距离典型应用UART 串口TX、RX、GND异步串行点对点为主9600、115200、1Mbps短到中等调试、蓝牙、GPS、串口屏、上位机I2C / IICSCL、SDA、GND同步串行一主多从100k、400k、1MHz短距离OLED、EEPROM、RTC、低速传感器SPISCK、MOSI、MISO、CS、GND同步串行一主多从几 MHz 到几十 MHz短距离Flash、屏幕、ADC、IMUCANCAN_H、CAN_L、GND差分总线多主通信125k、250k、500k、1Mbps中长距离电机控制、汽车电子、机器人、工业控制简单选择原则调试、上位机、模块通信优先选 UART。多个低速传感器、节省引脚优先选 I2C。高速外设、大量数据传输优先选 SPI。多节点、强干扰、远距离、可靠通信优先选 CAN。二、UART 串口通信1. 基本特点UART 是异步串行通信不需要时钟线通信双方只需要约定相同的波特率、数据位、停止位和校验位。常见参数波特率9600、115200、500000、1000000数据位8 bit停止位1 bit校验位None / Even / Odd2. 接线方式设备A TX - 设备B RX 设备A RX - 设备B TX 设备A GND - 设备B GND注意TX 和 RX 要交叉连接GND 必须共地。3. STM32 HAL 模板代码#includemain.h#includestring.hexternUART_HandleTypeDef huart1;voidUART_SendString(constchar*str){HAL_UART_Transmit(huart1,(uint8_t*)str,strlen(str),100);}voidUART_ReceiveExample(void){uint8_trx_data0;if(HAL_UART_Receive(huart1,rx_data,1,100)HAL_OK){HAL_UART_Transmit(huart1,rx_data,1,100);}}中断接收模板#includemain.hexternUART_HandleTypeDef huart1;uint8_tuart_rx_byte;voidUART_StartReceiveIT(void){HAL_UART_Receive_IT(huart1,uart_rx_byte,1);}voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){if(huart-InstanceUSART1){HAL_UART_Transmit(huart1,uart_rx_byte,1,100);HAL_UART_Receive_IT(huart1,uart_rx_byte,1);}}4. 优缺点优点使用简单调试方便。适合和上位机、串口模块通信。硬件资源占用少。缺点常见情况下主要是点对点通信。抗干扰能力一般。双方波特率不一致会乱码。三、I2C / IIC 通信1. 基本特点I2C 是两线同步通信由 SCL 时钟线和 SDA 数据线组成。一个主机可以挂多个从机每个从机通过地址区分。I2C 常见速率标准模式100 kHz快速模式400 kHz高速模式1 MHz 及以上具体取决于芯片支持2. 接线方式主机 SCL - 从机 SCL 主机 SDA - 从机 SDA GND - GNDSCL 和 SDA 通常需要上拉电阻一般上拉到 3.3V 或 5V取决于芯片电平。3. STM32 HAL 模板代码以向 I2C 设备寄存器写入和读取为例#includemain.hexternI2C_HandleTypeDef hi2c1;#defineDEV_ADDR(0x681)#defineREG_ADDR0x75voidI2C_WriteReg(uint8_treg,uint8_tdata){HAL_I2C_Mem_Write(hi2c1,DEV_ADDR,reg,I2C_MEMADD_SIZE_8BIT,data,1,100);}uint8_tI2C_ReadReg(uint8_treg){uint8_tdata0;HAL_I2C_Mem_Read(hi2c1,DEV_ADDR,reg,I2C_MEMADD_SIZE_8BIT,data,1,100);returndata;}扫描 I2C 地址模板#includemain.h#includestdio.hexternI2C_HandleTypeDef hi2c1;externUART_HandleTypeDef huart1;voidI2C_Scan(void){charmsg[64];for(uint8_taddr1;addr127;addr){if(HAL_I2C_IsDeviceReady(hi2c1,addr1,2,10)HAL_OK){snprintf(msg,sizeof(msg),I2C device found: 0x%02X\r\n,addr);HAL_UART_Transmit(huart1,(uint8_t*)msg,strlen(msg),100);}}}4. 优缺点优点只占用两根信号线。一个总线上可以挂多个设备。很适合低速传感器。缺点距离短。总线速度不高。需要上拉电阻。某个设备异常时可能导致总线被拉死。四、SPI 通信1. 基本特点SPI 是高速同步串行通信通常由主机提供时钟从机通过片选信号 CS 选择。常用信号线SCK时钟线MOSI主机输出从机输入MISO主机输入从机输出CS / NSS片选线GND共地2. 接线方式主机 SCK - 从机 SCK 主机 MOSI - 从机 MOSI 主机 MISO - 从机 MISO 主机 CS - 从机 CS GND - GND多个 SPI 从设备通常共用 SCK、MOSI、MISO每个从设备单独使用一个 CS。3. STM32 HAL 模板代码#includemain.hexternSPI_HandleTypeDef hspi1;#defineSPI_CS_GPIO_PortGPIOA#defineSPI_CS_PinGPIO_PIN_4staticvoidSPI_CS_LOW(void){HAL_GPIO_WritePin(SPI_CS_GPIO_Port,SPI_CS_Pin,GPIO_PIN_RESET);}staticvoidSPI_CS_HIGH(void){HAL_GPIO_WritePin(SPI_CS_GPIO_Port,SPI_CS_Pin,GPIO_PIN_SET);}voidSPI_WriteRead(uint8_t*tx_buf,uint8_t*rx_buf,uint16_tlen){SPI_CS_LOW();HAL_SPI_TransmitReceive(hspi1,tx_buf,rx_buf,len,100);SPI_CS_HIGH();}SPI 写寄存器模板voidSPI_WriteReg(uint8_treg,uint8_tdata){uint8_ttx_buf[2];tx_buf[0]reg;tx_buf[1]data;SPI_CS_LOW();HAL_SPI_Transmit(hspi1,tx_buf,2,100);SPI_CS_HIGH();}SPI 读寄存器模板uint8_tSPI_ReadReg(uint8_treg){uint8_ttx_buf[2];uint8_trx_buf[2];tx_buf[0]reg|0x80;tx_buf[1]0xFF;SPI_CS_LOW();HAL_SPI_TransmitReceive(hspi1,tx_buf,rx_buf,2,100);SPI_CS_HIGH();returnrx_buf[1];}说明不同 SPI 设备的读写位定义不同reg | 0x80只是常见写法具体要看芯片手册。4. 优缺点优点速度快。协议简单。支持全双工通信。缺点占用引脚较多。多个从机需要多个 CS。不适合远距离通信。五、CAN 通信1. 基本特点CAN 是一种差分总线通信方式常用于汽车电子、机器人底盘、电机控制和工业控制。它不是通过设备地址通信而是通过消息 ID 区分数据类型和优先级。CAN 的核心特点多主通信。差分传输抗干扰能力强。支持仲裁ID 越小优先级越高。适合多节点总线。2. 接线方式CAN_H - CAN_H CAN_L - CAN_L GND - GND注意事项MCU 通常需要外接 CAN 收发器例如 TJA1050、SN65HVD230。总线两端需要各接一个 120Ω 终端电阻。CAN_H 和 CAN_L 不要接反。3. STM32 HAL 模板代码CAN 启动和滤波器配置#includemain.hexternCAN_HandleTypeDef hcan1;voidCAN_FilterInit(void){CAN_FilterTypeDef filter;filter.FilterBank0;filter.FilterModeCAN_FILTERMODE_IDMASK;filter.FilterScaleCAN_FILTERSCALE_32BIT;filter.FilterIdHigh0x0000;filter.FilterIdLow0x0000;filter.FilterMaskIdHigh0x0000;filter.FilterMaskIdLow0x0000;filter.FilterFIFOAssignmentCAN_RX_FIFO0;filter.FilterActivationENABLE;filter.SlaveStartFilterBank14;HAL_CAN_ConfigFilter(hcan1,filter);HAL_CAN_Start(hcan1);HAL_CAN_ActivateNotification(hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);}CAN 发送模板voidCAN_SendData(uint32_tstd_id,uint8_t*data,uint8_tlen){CAN_TxHeaderTypeDef tx_header;uint32_ttx_mailbox;tx_header.StdIdstd_id;tx_header.ExtId0;tx_header.IDECAN_ID_STD;tx_header.RTRCAN_RTR_DATA;tx_header.DLClen;tx_header.TransmitGlobalTimeDISABLE;HAL_CAN_AddTxMessage(hcan1,tx_header,data,tx_mailbox);}CAN 接收中断模板voidHAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef*hcan){CAN_RxHeaderTypeDef rx_header;uint8_trx_data[8];if(hcan-InstanceCAN1){HAL_CAN_GetRxMessage(hcan,CAN_RX_FIFO0,rx_header,rx_data);switch(rx_header.StdId){case0x201:/* 处理 ID 为 0x201 的数据 */break;case0x202:/* 处理 ID 为 0x202 的数据 */break;default:break;}}}4. 优缺点优点抗干扰强。适合多节点通信。总线仲裁机制可靠。非常适合电机、车载、机器人底盘等场景。缺点配置比 UART、I2C、SPI 复杂。需要 CAN 收发器。波特率、采样点、终端电阻配置错误时容易通信失败。六、实际项目中的选型建议1. 连接上位机调试推荐 UART。原因是简单、直观配合 USB 转 TTL 和串口助手即可快速调试。2. 连接 OLED、EEPROM、RTC、气压计等低速外设推荐 I2C。原因是省引脚一个总线可以挂多个设备。3. 连接 Flash、TFT 屏幕、高速 ADC、IMU推荐 SPI。原因是速度高时序简单适合连续读写大量数据。4. 连接电机控制器、机器人底盘、多个控制节点推荐 CAN。原因是抗干扰强支持多节点可靠性高。七、常见问题总结1. UART 收到乱码常见原因波特率不一致。TX/RX 接反或没有共地。电平不匹配例如 3.3V 和 RS232 电平直接连接。2. I2C 找不到设备常见原因SDA/SCL 没有上拉电阻。地址写错注意 HAL 库中地址通常需要左移一位。设备没有供电。SDA/SCL 接反。3. SPI 读出来全是 0x00 或 0xFF常见原因CS 没有正确拉低。SPI 模式 CPOL/CPHA 配错。MOSI/MISO 接反。设备读写命令格式不对。4. CAN 无法通信常见原因CAN_H 和 CAN_L 接反。没有 120Ω 终端电阻。波特率不一致。CAN 收发器没有供电。滤波器配置过严导致消息被过滤。八、总结UART、I2C、SPI、CAN 都是嵌入式开发中非常重要的通信方式UART简单、常用于调试和模块通信。I2C省引脚、适合低速多外设。SPI速度快、适合高速外设。CAN可靠、抗干扰、适合多节点控制系统。项目中不要只看“能不能通信”还要考虑通信距离、速率、抗干扰能力、节点数量和硬件成本。掌握这四种通信方式基本可以覆盖大多数 STM32 和嵌入式项目的外设通信需求。