单片机外部通讯协议全解析:UART、I2C、SPI、CAN,助力你轻松掌握
发布时间: 2024-07-11 02:48:55 阅读量: 114 订阅数: 24
基于freeRTOS和STM32F103x的手机远程控制浴室温度系统设计源码
![单片机外部通讯协议全解析:UART、I2C、SPI、CAN,助力你轻松掌握](https://img-blog.csdnimg.cn/4cf6051a63564541916cfb295d948427.png)
# 1. 单片机外部通讯协议概述
单片机外部通讯协议是单片机与外部设备进行数据交换和控制的规范和规则。它定义了数据传输的格式、时序、控制信号和错误处理机制。外部通讯协议的种类繁多,根据数据传输方式的不同,可分为串行通讯协议和并行通讯协议。
串行通讯协议是将数据按位逐个传输,优点是线路简单,成本低。常见的串行通讯协议有UART、I2C等。并行通讯协议是将数据按字节或字逐个传输,优点是传输速度快,但线路复杂,成本高。常见的并行通讯协议有SPI、CAN等。
单片机外部通讯协议的选择需要考虑以下因素:数据传输速率、传输距离、抗干扰能力、成本等。在实际应用中,根据不同的需求选择合适的通讯协议,可以有效提高单片机系统的性能和可靠性。
# 2. 串行通讯协议
### 2.1 UART协议
#### 2.1.1 UART协议原理
UART(通用异步收发传输器)是一种异步串行通讯协议,用于在两个设备之间传输数据。它使用单根信号线进行数据传输,数据以比特为单位发送,每个比特由一个起始位、8个数据位、一个奇偶校验位(可选)和一个停止位组成。
起始位表示数据传输的开始,是一个低电平信号。数据位包含要传输的数据,每个数据位表示一个二进制位。奇偶校验位用于检测数据传输中的错误,可以是奇校验或偶校验。停止位表示数据传输的结束,是一个高电平信号。
UART协议的传输速率由波特率决定,单位为比特/秒(bps)。常见的波特率有9600、115200、921600等。
#### 2.1.2 UART协议的实现
UART协议的实现需要使用UART硬件模块,该模块通常集成在单片机中。UART模块负责生成起始位、数据位、奇偶校验位和停止位,并接收来自另一设备的数据。
以下是一个使用UART模块发送数据的代码示例:
```c
#include <stdio.h>
#include <stm32f10x.h>
void UART_Init(void)
{
// 初始化UART模块
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
void UART_SendData(uint8_t data)
{
// 发送数据
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, data);
}
int main(void)
{
UART_Init();
UART_SendData('A');
while (1);
}
```
逻辑分析:
* `UART_Init()`函数初始化UART模块,设置波特率、数据位、停止位和校验位。
* `UART_SendData()`函数发送一个字节的数据。
* 主函数中,初始化UART模块并发送字符'A'。
### 2.2 I2C协议
#### 2.2.1 I2C协议原理
I2C(Inter-Integrated Circuit)是一种同步串行通讯协议,用于在多个设备之间传输数据。它使用两根信号线进行数据传输,分别是串行数据线(SDA)和串行时钟线(SCL)。
I2C协议采用主从模式,一个设备为主设备,其他设备为从设备。主设备发起数据传输,并控制数据传输的时序。从设备响应主设备的请求,并传输或接收数据。
I2C协议的数据传输以帧为单位,每个帧包含一个起始位、一个设备地址、一个读/写位、数据字节和一个停止位。
#### 2.2.2 I2C协议的实现
I2C协议的实现需要使用I2C硬件模块,该模块通常集成在单片机中。I2C模块负责生成起始位、设备地址、读/写位、数据字节和停止位,并接收来自另一设备的数据。
以下是一个使用I2C模块发送数据的代码示例:
```c
#include <stdio.h>
#include <stm32f10x.h>
void I2C_Init(void)
{
// 初始化I2C模块
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_Init(I2C2, &I2C_InitStructure);
I2C_Cmd(I2C2, ENABLE);
}
void I2C_SendData(uint8_t slaveAddress, uint8_t data)
{
// 发送数据
while (I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY) != RESET);
I2C_GenerateSTART(I2C2, ENABLE);
while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C2, slaveAddress, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C2, data);
while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C2, ENABLE);
}
int main(void)
{
I2C_Init();
I2C_SendData(0x50, 'A');
while (1);
}
```
逻辑分析:
* `I2C_Init()`函数初始化I2C模块,设置时钟速度、数据格式和从设备地址。
* `I2C_SendData()`函数发送一个字节的数据到指定的从设备。
* 主函数中,初始化I2C模块并发送字符'A'到从设备地址为0x50的设备。
# 3. 并行通讯协议
### 3.1 SPI协议
#### 3.1.1 SPI协议原理
串行外围接口(SPI)是一种同步串行通信协议,用于在主设备和一个或多个从设备之间进行全双工通信。SPI协议使用四根线:时钟线(SCK)、主输出从输入线(MOSI)、主输入从输出线(MISO)和片选线(CS)。
SPI协议的通信过程如下:
1. 主设备将CS线拉低,选择要通信的从设备。
2. 主设备发送时钟信号,从设备根据时钟信号将数据从MOSI线移入,并将数据从MISO线移出。
3. 通信完成后,主设备将CS线拉高,释放从设备。
#### 3.1.2 SPI协议的实现
```c
// 主设备代码
void spi_master_init() {
// 初始化时钟、MOSI和MISO引脚
}
void spi_master_write(uint8_t data) {
// 拉低CS线
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
// 发送时钟信号
for (int i = 0; i < 8; i++) {
// 设置时钟高电平
HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);
// 发送数据位
if (data & (1 << i)) {
HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_RESET);
}
// 设置时钟低电平
HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
}
// 拉高CS线
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
}
uint8_t spi_master_read() {
// 拉低CS线
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
uint8_t data = 0;
// 发送时钟信号
for (int i = 0; i < 8; i++) {
// 设置时钟高电平
HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);
// 读取数据位
if (HAL_GPIO_ReadPin(MISO_GPIO_Port, MISO_Pin) == GPIO_PIN_SET) {
data |= (1 << i);
}
// 设置时钟低电平
HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
}
// 拉高CS线
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
return data;
}
// 从设备代码
void spi_slave_init() {
// 初始化时钟、MOSI和MISO引脚
}
void spi_slave_write(uint8_t data) {
// 等待CS线拉低
while (HAL_GPIO_ReadPin(CS_GPIO_Port, CS_Pin) == GPIO_PIN_SET);
// 接收时钟信号
for (int i = 0; i < 8; i++) {
// 等待时钟高电平
while (HAL_GPIO_ReadPin(SCK_GPIO_Port, SCK_Pin) == GPIO_PIN_RESET);
// 接收数据位
if (HAL_GPIO_ReadPin(MOSI_GPIO_Port, MOSI_Pin) == GPIO_PIN_SET) {
data |= (1 << i);
}
// 等待时钟低电平
while (HAL_GPIO_ReadPin(SCK_GPIO_Port, SCK_Pin) == GPIO_PIN_SET);
}
// CS线拉高
while (HAL_GPIO_ReadPin(CS_GPIO_Port, CS_Pin) == GPIO_PIN_RESET);
}
uint8_t spi_slave_read() {
// 等待CS线拉低
while (HAL_GPIO_ReadPin(CS_GPIO_Port, CS_Pin) == GPIO_PIN_SET);
uint8_t data = 0;
// 发送时钟信号
for (int i = 0; i < 8; i++) {
// 等待时钟高电平
while (HAL_GPIO_ReadPin(SCK_GPIO_Port, SCK_Pin) == GPIO_PIN_RESET);
// 发送数据位
if (data & (1 << i)) {
HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_RESET);
}
// 等待时钟低电平
while (HAL_GPIO_ReadPin(SCK_GPIO_Port, SCK_Pin) == GPIO_PIN_SET);
}
// CS线拉高
while (HAL_GPIO_ReadPin(CS_GPIO_Port, CS_Pin) == GPIO_PIN_RESET);
return data;
}
```
### 3.2 CAN协议
#### 3.2.1 CAN协议原理
控制器局域网络(CAN)是一种串行通信协议,用于在多个设备之间进行高速可靠的通信。CAN协议使用两根线:高电平线(CANH)和低电平线(CANL)。
CAN协议的通信过程如下:
1. 发送设备发送一个报文,报文包含一个标识符、控制字段、数据字段和循环冗余校验(CRC)字段。
2. 报文在CAN总线上广播,所有设备都可以接收。
3. 接收设备根据标识符判断报文是否与自己相关,如果相关则接收报文,否则丢弃报文。
#### 3.2.2 CAN协议的实现
```c
// CAN控制器初始化
void can_init() {
// 初始化CAN控制器
CAN_InitTypeDef can_init_struct;
can_init_struct.CAN_TTCM = DISABLE;
can_init_struct.CAN_ABOM = DISABLE;
can_init_struct.CAN_AWUM = DISABLE;
can_init_struct.CAN_NART = DISABLE;
can_init_struct.CAN_RFLM = DISABLE;
can_init_struct.CAN_TXFP = DISABLE;
can_init_struct.CAN_Mode = CAN_MODE_NORMAL;
can_init_struct.CAN_SJW = CAN_SJW_1TQ;
can_init_struct.CAN_BS1 = CAN_BS1_1TQ;
can_init_struct.CAN_BS2 = CAN_BS2_1TQ;
can_init_struct.CAN_Prescaler = 16;
HAL_CAN_Init(&hcan, &can_init_struct);
// 初始化CAN过滤器
CAN_FilterTypeDef can_filter_struct;
can_filter_struct.FilterActivation = ENABLE;
can_filter_struct.FilterMode = CAN_FILTERMODE_IDMASK;
can_filter_struct.FilterScale = CAN_FILTERSCALE_32BIT;
can_filter_struct.FilterIdHigh = 0x0000;
can_filter_struct.FilterIdLow = 0x0000;
can_filter_struct.FilterMaskIdHigh = 0x0000;
can_filter_struct.FilterMaskIdLow = 0x0000;
can_filter_struct.FilterFIFOAssignment = CAN_RX_FIFO0;
HAL_CAN_ConfigFilter(&hcan, &can_filter_struct);
// 启动CAN控制器
HAL_CAN_Start(&hcan);
}
// CAN报文发送
void can_send(uint32_t identifier, uint8_t data[], uint8_t data_length) {
CAN_TxHeaderTypeDef can_tx_header;
can_tx_header.StdId = identifier;
can_tx_header.ExtId = 0;
can_tx_header.RTR = CAN_RTR_DATA;
can_tx_header.IDE = CAN_ID_STD;
can_tx_header.DLC = data_length;
can_tx_header.TransmitGlobalTime = DISABLE;
# 4. 单片机外部通讯协议应用
### 4.1 单片机与PC的通讯
#### 4.1.1 串口通讯
串口通讯是单片机与PC之间最常用的通讯方式,它通过串口线连接单片机和PC,实现数据的双向传输。
**原理:**
串口通讯采用异步传输方式,即数据以串行方式传输,每个字节包含一个起始位、8个数据位、一个奇偶校验位和一个停止位。起始位为逻辑0,数据位为数据本身,奇偶校验位用于校验数据的正确性,停止位为逻辑1。
**实现:**
单片机和PC的串口通讯需要使用UART(通用异步收发器)芯片。UART负责数据的收发和格式转换。
#### 4.1.2 USB通讯
USB(通用串行总线)通讯是一种高速、低成本的通讯方式,它通过USB线缆连接单片机和PC,实现数据的双向传输。
**原理:**
USB通讯采用差分信号传输,即数据以两条信号线传输,一条信号线传输正向数据,另一条信号线传输反向数据。接收端通过比较两条信号线的电压差来恢复数据。
**实现:**
单片机和PC的USB通讯需要使用USB接口芯片。USB接口芯片负责数据的收发和格式转换。
### 4.2 单片机与传感器/执行器的通讯
#### 4.2.1 I2C通讯
I2C(Inter-Integrated Circuit)通讯是一种串行通讯协议,它通过两条信号线(SDA和SCL)连接单片机和传感器/执行器,实现数据的双向传输。
**原理:**
I2C通讯采用主从模式,单片机为主设备,传感器/执行器为从设备。主设备通过发送起始信号和从设备地址来发起通讯,从设备收到地址后进行应答。数据传输采用半双工方式,即主设备和从设备不能同时发送数据。
**实现:**
单片机和传感器/执行器的I2C通讯需要使用I2C接口芯片。I2C接口芯片负责数据的收发和格式转换。
#### 4.2.2 SPI通讯
SPI(Serial Peripheral Interface)通讯是一种串行通讯协议,它通过四条信号线(SCLK、MOSI、MISO和SS)连接单片机和传感器/执行器,实现数据的双向传输。
**原理:**
SPI通讯采用主从模式,单片机为主设备,传感器/执行器为从设备。主设备通过发送时钟信号(SCLK)来控制数据传输,MOSI(主设备输出,从设备输入)和MISO(主设备输入,从设备输出)用于数据的传输。SS(从设备选择)信号用于选择从设备。
**实现:**
单片机和传感器/执行器的SPI通讯需要使用SPI接口芯片。SPI接口芯片负责数据的收发和格式转换。
# 5. 单片机外部通讯协议的选型与设计
### 5.1 通讯协议的选型原则
在单片机外部通讯协议的选型过程中,需要考虑以下原则:
- **通讯距离:**不同协议支持的通讯距离不同,如串口通讯的距离较短,而CAN协议的距离较长。
- **通讯速率:**不同协议支持的通讯速率不同,如UART协议的速率较低,而SPI协议的速率较高。
- **数据量:**不同协议支持的数据量不同,如I2C协议支持的数据量较小,而CAN协议支持的数据量较大。
- **抗干扰性:**不同协议的抗干扰性不同,如CAN协议的抗干扰性较强,而UART协议的抗干扰性较弱。
- **成本:**不同协议的成本不同,如串口通讯的成本较低,而CAN协议的成本较高。
### 5.2 通讯协议的设计要点
在单片机外部通讯协议的设计过程中,需要考虑以下要点:
- **协议的确定:**根据通讯需求选择合适的通讯协议,如需要长距离通讯时选择CAN协议,需要高速通讯时选择SPI协议。
- **硬件接口的设计:**根据选择的协议设计对应的硬件接口,如UART协议需要设计串口接口,I2C协议需要设计I2C接口。
- **软件协议的实现:**根据选择的协议编写对应的软件协议,如UART协议需要编写串口通信程序,I2C协议需要编写I2C通信程序。
- **通讯参数的设置:**根据通讯需求设置通讯参数,如波特率、数据位、校验位等。
- **数据传输的处理:**编写数据传输处理程序,对发送和接收的数据进行处理,如数据打包、解包、校验等。
0
0