STM32单片机外设接口全攻略:串口、I2C、SPI、ADC详解
发布时间: 2024-07-04 00:53:42 阅读量: 203 订阅数: 56
ADM9226数据采集模块原理图.pdf
![STM32单片机外设接口全攻略:串口、I2C、SPI、ADC详解](https://img-blog.csdnimg.cn/462d1baed8a243eda9bfffaad722bec9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWU9VUlVPTEk=,size_20,color_FFFFFF,t_70,g_se,x_16)
# 1. STM32单片机外设接口概述**
STM32单片机外设接口是连接单片机与外部设备的重要桥梁,广泛应用于各种电子系统中。常见的STM32单片机外设接口包括串口、I2C、SPI和ADC,它们各有不同的功能和特点。
串口接口主要用于异步串行通信,具有简单易用、成本低廉的特点。I2C接口是一种串行总线,支持多主控设备,适用于低速通信和设备寻址。SPI接口是一种高速串行接口,支持全双工通信,常用于连接高速外设。ADC接口用于将模拟信号转换为数字信号,是实现数据采集和控制的重要外设。
# 2. 串口接口
### 2.1 串口协议和通信原理
**2.1.1 串口数据格式**
串口数据格式定义了数据在串行总线上的传输方式。常用的串口数据格式有:
- **数据位:**指定传输的数据位数,通常为 5、6、7 或 8 位。
- **停止位:**指定数据位后面停止位的数量,通常为 1 或 2 位。
- **校验位:**用于检测数据传输过程中是否发生错误,可以是奇校验或偶校验。
**2.1.2 串口通信速率和校验**
串口通信速率是指每秒传输的数据位数,单位为波特率(bps)。常见的通信速率有 9600、19200、38400、115200 等。
校验位用于检测数据传输过程中的错误。奇校验要求数据位和校验位中 1 的个数为奇数,偶校验要求 1 的个数为偶数。如果接收到的数据与校验位不匹配,则表明数据传输过程中发生了错误。
### 2.2 STM32单片机串口硬件结构
**2.2.1 串口寄存器和功能**
STM32单片机提供了多个串口外设,每个串口外设都有自己的寄存器组。主要寄存器包括:
- **数据寄存器(DR):**用于读写数据。
- **状态寄存器(SR):**指示串口的状态,如发送完成、接收完成等。
- **控制寄存器(CR1):**配置串口参数,如数据位、停止位、校验位等。
- **波特率寄存器(BRR):**设置串口通信速率。
**2.2.2 中断处理机制**
STM32单片机串口外设支持中断处理。当串口状态发生变化时,如数据接收完成或发送完成,会触发中断。中断处理程序可以对事件进行响应,如读取接收到的数据或发送新的数据。
### 2.3 串口接口编程实践
**2.3.1 串口初始化和配置**
串口初始化和配置包括设置数据位、停止位、校验位、通信速率等参数。代码示例如下:
```c
// 初始化串口1
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 使能串口1时钟
USART1->CR1 = USART_CR1_TE | USART_CR1_RE; // 使能发送器和接收器
USART1->CR2 = 0; // 清除控制寄存器2
USART1->BRR = 9600; // 设置波特率为 9600
```
**2.3.2 串口数据收发**
串口数据收发通过数据寄存器(DR)进行。发送数据时,将数据写入 DR 寄存器;接收数据时,从 DR 寄存器读取数据。代码示例如下:
```c
// 发送数据
USART1->DR = 'A'; // 发送字符 'A'
// 接收数据
uint8_t data = USART1->DR; // 读取接收到的数据
```
**2.3.3 串口中断处理**
串口中断处理程序在串口状态发生变化时执行。代码示例如下:
```c
void USART1_IRQHandler(void)
{
if (USART1->SR & USART_SR_RXNE) {
// 接收数据完成中断
uint8_t data = USART1->DR; // 读取接收到的数据
} else if (USART1->SR & USART_SR_TXE) {
// 发送数据完成中断
// ...
}
}
```
# 3. I2C接口**
### 3.1 I2C协议和通信原理
#### 3.1.1 I2C总线结构和通信时序
I2C(Inter-Integrated Circuit)总线是一种串行通信协议,用于在集成电路(IC)之间进行数据传输。它采用主从式通信方式,其中一个设备(主设备)控制总线,其他设备(从设备)响应主设备的请求。
I2C总线由两条双向信号线组成:串行数据线(SDA)和串行时钟线(SCL)。主设备通过SDA线发送和接收数据,而从设备通过SDA线接收和发送数据。SCL线由主设备控制,它为数据传输提供时钟信号。
I2C通信时序包括以下步骤:
1. **起始条件:**主设备拉低SDA线,同时保持SCL线高电平。
2. **设备地址:**主设备发送从设备的7位地址。
3. **读/写位:**主设备发送一个读/写位,表示它是要从从设备读取数据还是向从设备写入数据。
4. **从设备应答:**从设备通过拉低SDA线来应答主设备的请求。
5. **数据传输:**主设备和从设备根据读/写位进行数据传输。
6. **停止条件:**主设备拉高SDA线,同时保持SCL线高电平。
#### 3.1.2 I2C数据格式和寻址方式
I2C数据格式为8位,包括一个数据字节和一个校验位。校验位使用奇偶校验算法计算,以确保数据的完整性。
I2C寻址方式采用7位地址,其中第0位为读/写位。对于读操作,读/写位为0;对于写操作,读/写位为1。
### 3.2 STM32单片机I2C硬件结构
#### 3.2.1 I2C寄存器和功能
STM32单片机中,I2C接口由I2C外设控制器(I2C1、I2C2、I2C3)实现。I2C外设控制器包含以下主要寄存器:
- **CR1寄存器:**控制I2C外设的全局配置,包括使能、时钟频率、传输模式等。
- **CR2寄存器:**控制I2C外设的通信参数,包括从设备地址、数据长度等。
- **SR1寄存器:**指示I2C外设的状态,包括总线状态、传输方向、中断标志等。
- **SR2寄存器:**提供有关I2C外设的附加信息,例如从设备地址匹配状态、错误标志等。
- **DR寄存器:**用于发送和接收数据。
#### 3.2.2 中断处理机制
STM32单片机I2C外设支持多种中断,包括:
- **TXE中断:**当数据传输寄存器(DR)为空时触发,表示可以发送数据。
- **RXNE中断:**当数据接收寄存器(DR)中有数据时触发,表示可以接收数据。
- **TC中断:**当一个数据传输或接收操作完成时触发。
- **STOPF中断:**当一个停止条件被检测到时触发。
### 3.3 I2C接口编程实践
#### 3.3.1 I2C初始化和配置
```c
// 使能I2C外设
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// 配置I2C外设
I2C1->CR1 |= I2C_CR1_PE; // 使能I2C外设
I2C1->CR2 |= (100 << I2C_CR2_FREQ_Pos); // 设置时钟频率为100kHz
```
#### 3.3.2 I2C数据读写
**读取数据:**
```c
// 发送从设备地址和读写位
I2C1->DR = (slave_address << 1) | I2C_Direction_Receiver;
// 等待从设备应答
while (!(I2C1->SR1 & I2C_SR1_ADDR));
// 接收数据
I2C1->CR1 |= I2C_CR1_ACK; // 发送应答信号
while (!(I2C1->SR1 & I2C_SR1_RXNE)); // 等待数据接收完成
data = I2C1->DR;
// 发送停止条件
I2C1->CR1 |= I2C_CR1_STOP;
```
**写入数据:**
```c
// 发送从设备地址和读写位
I2C1->DR = (slave_address << 1) | I2C_Direction_Transmitter;
// 等待从设备应答
while (!(I2C1->SR1 & I2C_SR1_ADDR));
// 发送数据
I2C1->DR = data;
// 等待数据发送完成
while (!(I2C1->SR1 & I2C_SR1_TXE));
// 发送停止条件
I2C1->CR1 |= I2C_CR1_STOP;
```
#### 3.3.3 I2C中断处理
```c
// 使能I2C中断
I2C1->CR2 |= I2C_CR2_ITBUFEN | I2C_CR2_ITERREN | I2C_CR2_ITEVTEN;
// 中断服务函数
void I2C1_IRQHandler(void)
{
// 处理中断标志
if (I2C1->SR1 & I2C_SR1_TXE) {
// 数据发送完成中断
} else if (I2C1->SR1 & I2C_SR1_RXNE) {
// 数据接收完成中断
} else if (I2C1->SR1 & I2C_SR1_ADDR) {
// 从设备地址匹配中断
} else if (I2C1->SR1 & I2C_SR1_STOPF) {
// 停止条件中断
}
}
```
# 4. SPI接口**
## 4.1 SPI协议和通信原理
### 4.1.1 SPI总线结构和通信时序
SPI(Serial Peripheral Interface,串行外设接口)是一种同步串行通信协议,用于在主设备和一个或多个从设备之间传输数据。SPI总线由以下信号线组成:
* **SCLK(时钟线):**主设备提供时钟信号,同步数据传输。
* **MOSI(主输出从输入):**主设备向从设备发送数据的线路。
* **MISO(主输入从输出):**从设备向主设备发送数据的线路。
* **SS(从设备选择):**主设备用于选择要通信的从设备。
SPI通信时序遵循以下步骤:
1. **主设备拉低SS线:**选择要通信的从设备。
2. **主设备发送时钟信号:**同步数据传输。
3. **主设备发送数据:**通过MOSI线发送数据。
4. **从设备接收数据:**通过MISO线接收数据。
5. **主设备释放SS线:**结束通信。
### 4.1.2 SPI数据格式和通信模式
SPI数据格式可以是8位、16位或32位。通信模式有以下几种:
* **全双工模式:**主设备和从设备可以同时发送和接收数据。
* **半双工模式:**主设备和从设备轮流发送和接收数据。
* **单工模式:**主设备只能发送或接收数据,从设备只能接收或发送数据。
## 4.2 STM32单片机SPI硬件结构
### 4.2.1 SPI寄存器和功能
STM32单片机上的SPI外设包含以下主要寄存器:
* **SPI_CR1(控制寄存器 1):**配置SPI模式、数据大小、时钟极性和相位。
* **SPI_CR2(控制寄存器 2):**配置NSS引脚模式、数据传输方向和DMA传输。
* **SPI_SR(状态寄存器):**指示SPI状态,包括传输完成、接收缓冲区满、发送缓冲区空等。
* **SPI_DR(数据寄存器):**用于发送和接收数据。
### 4.2.2 中断处理机制
SPI外设支持以下中断:
* **TXE(传输完成中断):**当发送缓冲区为空时触发。
* **RXNE(接收缓冲区非空中断):**当接收缓冲区中有数据时触发。
* **ERR(错误中断):**当发生错误(例如CRC错误或超时)时触发。
## 4.3 SPI接口编程实践
### 4.3.1 SPI初始化和配置
以下代码示例演示如何初始化和配置SPI接口:
```c
// 初始化SPI外设
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
SPI_InitStruct.Direction = SPI_DIRECTION_2LINES;
SPI_InitStruct.DataSize = SPI_DATASIZE_8BIT;
SPI_InitStruct.CLKPolarity = SPI_POLARITY_LOW;
SPI_InitStruct.CLKPhase = SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = SPI_NSS_SOFT;
SPI_InitStruct.FirstBit = SPI_FIRSTBIT_MSB;
SPI_InitStruct.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
SPI_Init(SPIx, &SPI_InitStruct);
// 使能SPI外设
SPI_Cmd(SPIx, ENABLE);
```
### 4.3.2 SPI数据收发
以下代码示例演示如何通过SPI接口发送和接收数据:
```c
// 发送数据
uint8_t data = 0x55;
SPI_SendData(SPIx, data);
// 等待传输完成
while (SPI_GetFlagStatus(SPIx, SPI_FLAG_TXE) == RESET);
// 接收数据
uint8_t receivedData = SPI_ReceiveData(SPIx);
```
### 4.3.3 SPI中断处理
以下代码示例演示如何配置和处理SPI中断:
```c
// 配置中断
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = SPIx_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
// 使能中断
SPI_ITConfig(SPIx, SPI_IT_TXE, ENABLE);
SPI_ITConfig(SPIx, SPI_IT_RXNE, ENABLE);
// 中断处理函数
void SPIx_IRQHandler(void)
{
if (SPI_GetITStatus(SPIx, SPI_IT_TXE) != RESET)
{
// 发送数据
uint8_t data = 0x55;
SPI_SendData(SPIx, data);
}
if (SPI_GetITStatus(SPIx, SPI_IT_RXNE) != RESET)
{
// 接收数据
uint8_t receivedData = SPI_ReceiveData(SPIx);
}
}
```
# 5. ADC接口**
**5.1 ADC原理和转换过程**
ADC(模数转换器)是一种将模拟信号(电压或电流)转换为数字信号的电子器件。其转换过程主要分为以下两个步骤:
**5.1.1 ADC量化和采样**
* **量化:**将模拟信号的连续幅值离散化为有限个量化电平。
* **采样:**在离散的时间点对模拟信号进行采样,获得其瞬时幅值。
**5.1.2 ADC转换精度和误差**
ADC转换精度由以下因素决定:
* **分辨率:**ADC能够区分的最小电压变化。
* **量化误差:**由于量化过程导致的误差。
* **非线性误差:**ADC转换曲线与理想直线之间的偏差。
**5.2 STM32单片机ADC硬件结构**
STM32单片机集成了多个ADC外设,其硬件结构主要包括:
* **ADC寄存器:**控制ADC操作和配置的寄存器。
* **多路复用器:**用于选择要转换的模拟输入信号。
* **采样保持电路:**在采样过程中保持信号稳定。
* **模数转换器:**执行实际的模数转换。
**5.2.1 ADC寄存器和功能**
```
| 寄存器 | 功能 |
|---|---|
| ADCx_CR1 | 控制寄存器 1 |
| ADCx_CR2 | 控制寄存器 2 |
| ADCx_SMPR | 采样时间寄存器 |
| ADCx_SR | 状态寄存器 |
| ADCx_DR | 数据寄存器 |
```
**5.2.2 中断处理机制**
ADC提供中断机制,当转换完成或发生错误时触发中断。中断处理程序可以读取转换结果并采取相应的操作。
**5.3 ADC接口编程实践**
**5.3.1 ADC初始化和配置**
```c
// 初始化 ADC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// 配置 ADC
ADC_InitTypeDef adc_init_struct;
adc_init_struct.ADC_Resolution = ADC_Resolution_12b;
adc_init_struct.ADC_ScanConvMode = DISABLE;
adc_init_struct.ADC_ContinuousConvMode = DISABLE;
adc_init_struct.ADC_DataAlign = ADC_DataAlign_Right;
adc_init_struct.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &adc_init_struct);
```
**5.3.2 ADC数据采集和处理**
```c
// 启动 ADC 转换
ADC_SoftwareStartConv(ADC1);
// 等待转换完成
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
// 读取转换结果
uint16_t adc_value = ADC_GetConversionValue(ADC1);
```
**5.3.3 ADC中断处理**
```c
// 配置 ADC 中断
NVIC_InitTypeDef nvic_init_struct;
nvic_init_struct.NVIC_IRQChannel = ADC1_IRQn;
nvic_init_struct.NVIC_IRQChannelPreemptionPriority = 0;
nvic_init_struct.NVIC_IRQChannelSubPriority = 0;
nvic_init_struct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_init_struct);
// ADC 中断处理函数
void ADC1_IRQHandler(void)
{
// 读取转换结果
uint16_t adc_value = ADC_GetConversionValue(ADC1);
// 处理转换结果
}
```
0
0