STM32单片机外设接口详解:串口、I2C、SPI、ADC,轻松掌握
发布时间: 2024-07-04 17:02:06 阅读量: 152 订阅数: 67
STM32单片机FPGA毕设电路原理论文报告基于单片机的多通道温湿度检测系统设计
![stm32单片机课程设计](https://img-blog.csdnimg.cn/c3437fdc0e3e4032a7d40fcf04887831.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiN55-l5ZCN55qE5aW95Lq6,size_20,color_FFFFFF,t_70,g_se,x_16)
# 1. STM32外设接口概述**
STM32单片机集成了丰富的片上外设接口,包括串口、I2C、SPI和ADC,这些接口在各种嵌入式系统中广泛应用。本章将对STM32外设接口进行概述,介绍其特点、功能和应用场景。
外设接口是单片机与外部设备通信的桥梁,通过这些接口,单片机可以与传感器、显示器、存储器等外设进行数据交换。STM32外设接口具有以下特点:
- **可配置性强:**STM32外设接口提供了丰富的配置选项,可以根据不同的应用场景进行灵活配置,满足不同的通信需求。
- **高性能:**STM32外设接口支持高速数据传输,可以满足实时性要求较高的应用。
- **易于使用:**STM32外设接口提供了完善的库函数和例程,简化了开发过程,降低了开发难度。
# 2. 串口接口
### 2.1 串口通信原理
串口(UART,Universal Asynchronous Receiver Transmitter)是一种异步串行通信接口,用于在两个设备之间传输数据。它使用一条数据线和一条控制线,数据以位为单位逐个传输。
串口通信的基本原理如下:
* **起始位:**一个低电平信号,表示数据传输的开始。
* **数据位:**传输实际数据的位,通常为 8 位。
* **停止位:**一个或多个高电平信号,表示数据传输的结束。
* **奇偶校验位:**可选的位,用于检测数据传输中的错误。
### 2.2 STM32串口寄存器和配置
STM32单片机提供了多个串口外设,每个串口都有自己的寄存器组。主要寄存器包括:
| 寄存器 | 描述 |
|---|---|
| USART_CR1 | 控制寄存器 1,配置串口模式、波特率、数据位、停止位等 |
| USART_CR2 | 控制寄存器 2,配置中断、硬件流控制等 |
| USART_SR | 状态寄存器,指示串口状态,如发送缓冲区满、接收缓冲区空等 |
| USART_DR | 数据寄存器,用于发送和接收数据 |
串口配置步骤:
1. 使能串口时钟。
2. 配置串口引脚功能。
3. 设置串口参数(波特率、数据位、停止位等)。
4. 使能串口。
### 2.3 串口中断处理
STM32串口支持中断处理,当接收缓冲区有数据或发送缓冲区空时会产生中断。中断处理函数需要:
1. 读取接收缓冲区数据。
2. 写入发送缓冲区数据。
3. 清除中断标志位。
### 2.4 串口通信实例
以下是一个使用 STM32 串口发送和接收数据的示例代码:
```c
#include "stm32f10x.h"
void USART_Init(void) {
// 使能串口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// 配置串口引脚
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 设置串口参数
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_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能串口
USART_Cmd(USART1, ENABLE);
}
void USART_SendData(uint8_t data) {
// 等待发送缓冲区空
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
// 写入发送缓冲区
USART_SendData(USART1, data);
}
uint8_t USART_ReceiveData(void) {
// 等待接收缓冲区有数据
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
// 读取接收缓冲区数据
return USART_ReceiveData(USART1);
}
```
# 3. I2C接口
### 3.1 I2C通信原理
I2C(Inter-Integrated Circuit)总线是一种串行通信协议,用于连接多个集成电路(IC)设备。它是一种双线总线,包括一条数据线(SDA)和一条时钟线(SCL)。
I2C通信采用主从模式,其中一个设备充当主设备,而其他设备充当从设备。主设备启动通信,发送起始信号,并指定从设备的地址。从设备响应主设备的请求,并发送或接收数据。
### 3.2 STM32 I2C寄存器和配置
STM32微控制器具有专用的I2C外设,通过寄存器进行配置和控制。主要寄存器包括:
- **I2C_CR1**:控制寄存器,用于配置I2C模式、时钟频率和中断。
- **I2C_CR2**:控制寄存器,用于配置I2C地址和从设备模式。
- **I2C_SR1**:状态寄存器,指示I2C总线状态和错误。
- **I2C_SR2**:状态寄存器,提供有关从设备地址和传输方向的信息。
- **I2C_DR**:数据寄存器,用于发送和接收数据。
配置I2C外设时,需要设置时钟频率、从设备地址和中断使能。时钟频率通常设置为100 kHz或400 kHz,具体取决于应用要求。
### 3.3 I2C中断处理
STM32 I2C外设支持中断处理,以提高通信效率。中断源包括:
- **TXE**:传输数据寄存器为空,可以发送数据。
- **RXNE**:接收数据寄存器非空,可以接收数据。
- **TC**:传输完成,通信结束。
- **NACKF**:从设备未应答,通信失败。
可以通过配置I2C_CR1寄存器的IER位来使能中断。中断处理程序负责读取I2C_SR1和I2C_SR2寄存器,并根据总线状态采取适当的操作。
### 3.4 I2C通信实例
以下是一个STM32 I2C通信实例,演示如何使用I2C外设与从设备通信:
```c
#include "stm32f10x.h"
// I2C配置结构体
I2C_InitTypeDef I2C_InitStructure;
// 从设备地址
#define SLAVE_ADDRESS 0x50
// 主设备发送数据
void I2C_SendData(uint8_t *data, uint8_t length) {
// 等待传输数据寄存器为空
while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE));
// 发送起始信号
I2C_GenerateSTART(I2C1, ENABLE);
// 等待起始信号发送完成
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// 发送从设备地址和写标志
I2C_Send7bitAddress(I2C1, SLAVE_ADDRESS, I2C_Direction_Transmitter);
// 等待从设备应答
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
// 发送数据
for (uint8_t i = 0; i < length; i++) {
// 等待传输数据寄存器为空
while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE));
// 发送数据
I2C_SendData(I2C1, data[i]);
}
// 发送停止信号
I2C_GenerateSTOP(I2C1, ENABLE);
}
// 主设备接收数据
void I2C_ReceiveData(uint8_t *data, uint8_t length) {
// 等待接收数据寄存器非空
while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE));
// 接收数据
for (uint8_t i = 0; i < length; i++) {
// 等待接收数据寄存器非空
while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE));
// 接收数据
data[i] = I2C_ReceiveData(I2C1);
}
// 发送停止信号
I2C_GenerateSTOP(I2C1, ENABLE);
}
```
在该实例中,`I2C_SendData()`函数用于发送数据,而`I2C_ReceiveData()`函数用于接收数据。这些函数使用I2C外设的寄存器和中断来管理通信过程。
# 4. SPI接口
### 4.1 SPI通信原理
SPI(Serial Peripheral Interface,串行外围设备接口)是一种高速、全双工、同步串行通信接口,广泛应用于微控制器与外围设备之间的通信。SPI通信采用主从模式,由一个主设备和一个或多个从设备组成。
SPI通信过程遵循以下步骤:
1. **时钟极性(CPOL)和时钟相位(CPHA)设置:**主设备通过CPOL和CPHA位配置SPI时钟的极性和相位,以与从设备同步。
2. **数据传输:**主设备和从设备通过MOSI(主输出从输入)和MISO(主输入从输出)线进行全双工数据传输。
3. **片选(CS)信号:**主设备通过CS信号选择要通信的从设备。
### 4.2 STM32 SPI寄存器和配置
STM32微控制器提供了专用的SPI外设,具有以下主要寄存器:
- **SPI_CR1:**控制寄存器,配置数据帧格式、时钟极性、时钟相位等参数。
- **SPI_CR2:**配置寄存器,设置NSS模式、中断使能等参数。
- **SPI_SR:**状态寄存器,指示数据传输状态、中断标志等信息。
- **SPI_DR:**数据寄存器,用于发送和接收数据。
以下代码示例展示了如何配置STM32 SPI外设:
```c
// 初始化SPI外设
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; // 波特率预分频系数
SPI_InitStruct.Direction = SPI_DIRECTION_2LINES; // 双线模式
SPI_InitStruct.CLKPhase = SPI_CLKPHASE_1EDGE; // 时钟相位1
SPI_InitStruct.CLKPolarity = SPI_CLKPOLARITY_LOW; // 时钟极性0
SPI_InitStruct.DataSize = SPI_DATASIZE_8BIT; // 数据长度8位
SPI_InitStruct.FirstBit = SPI_FIRSTBIT_MSB; // MSB优先
SPI_InitStruct.NSS = SPI_NSS_SOFT; // 软件NSS模式
SPI_InitStruct.Mode = SPI_MODE_MASTER; // 主模式
SPI_Init(SPI1, &SPI_InitStruct);
```
### 4.3 SPI中断处理
STM32 SPI外设支持中断处理,允许在数据传输完成时触发中断。以下代码示例展示了如何配置SPI中断:
```c
// 配置SPI中断
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.IRQChannel = SPI1_IRQn; // SPI1中断通道
NVIC_InitStruct.PreemptPriority = 0; // 抢占优先级
NVIC_InitStruct.SubPriority = 0; // 子优先级
NVIC_EnableIRQ(SPI1_IRQn); // 使能SPI1中断
```
在中断服务函数中,可以通过读取SPI_SR寄存器来检查中断标志,并根据不同的标志执行相应的操作。
### 4.4 SPI通信实例
以下代码示例展示了如何使用STM32 SPI外设与外围设备进行通信:
```c
// 发送数据
uint8_t data = 0x55;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // 等待发送缓冲区为空
SPI_I2S_SendData(SPI1, data); // 发送数据
// 接收数据
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); // 等待接收缓冲区非空
uint8_t receivedData = SPI_I2S_ReceiveData(SPI1); // 接收数据
```
# 5. ADC接口
### 5.1 ADC转换原理
模数转换器(ADC)是一种将模拟信号(如电压或电流)转换为数字信号的电子器件。在STM32单片机中,ADC外设用于将模拟输入信号转换为数字值,以便微控制器可以处理和分析。
ADC转换过程涉及以下步骤:
1. **采样:**ADC外设对模拟输入信号进行采样,获取其瞬时值。
2. **保持:**采样值被保持在ADC外设内部的保持电容上。
3. **量化:**保持的模拟值被转换为数字值,该数字值表示模拟信号的幅度。
4. **存储:**转换后的数字值被存储在ADC外设的寄存器中。
### 5.2 STM32 ADC寄存器和配置
STM32单片机中的ADC外设由多个寄存器控制和配置。主要寄存器包括:
- **ADCx_CR1:**控制ADC转换模式、触发源、时钟分频等。
- **ADCx_CR2:**控制ADC中断、DMA传输、扫描模式等。
- **ADCx_SMPR1/SMPR2:**控制模拟输入通道的采样时间。
- **ADCx_DR:**存储转换后的数字值。
ADC配置过程涉及以下步骤:
1. **使能ADC时钟:**在RCC寄存器中使能ADC外设时钟。
2. **配置ADC模式:**在ADCx_CR1寄存器中设置ADC转换模式,如单次转换、连续转换或扫描转换。
3. **配置触发源:**在ADCx_CR2寄存器中设置ADC转换触发源,如软件触发、外部触发或定时器触发。
4. **配置采样时间:**在ADCx_SMPR1/SMPR2寄存器中设置模拟输入通道的采样时间。
5. **配置中断:**在ADCx_CR2寄存器中使能ADC中断,并设置中断触发条件。
### 5.3 ADC中断处理
ADC外设提供中断功能,允许微控制器在ADC转换完成后执行特定操作。ADC中断处理过程涉及以下步骤:
1. **配置ADC中断:**在ADCx_CR2寄存器中使能ADC中断,并设置中断触发条件。
2. **编写中断服务程序:**编写一个中断服务程序,在ADC转换完成后执行所需的操作,如读取转换结果、更新显示或触发其他事件。
3. **清除中断标志:**在中断服务程序中清除ADC中断标志,以防止中断再次触发。
### 5.4 ADC转换实例
以下是一个STM32 ADC转换实例,演示如何使用ADC外设将模拟电压转换为数字值:
```c
#include "stm32f10x.h"
int main(void)
{
// 使能ADC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// 配置ADC模式
ADC_InitTypeDef adc_init;
adc_init.ADC_Mode = ADC_Mode_Independent;
adc_init.ADC_ScanConvMode = DISABLE;
adc_init.ADC_ContinuousConvMode = DISABLE;
adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adc_init.ADC_DataAlign = ADC_DataAlign_Right;
adc_init.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &adc_init);
// 配置ADC通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
// 使能ADC
ADC_Cmd(ADC1, ENABLE);
// ADC校准
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
// ADC转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
// 读取转换结果
uint16_t adc_value = ADC_GetConversionValue(ADC1);
// 处理转换结果
return 0;
}
```
**代码逻辑分析:**
1. 使能ADC1时钟,配置ADC模式,包括转换模式、触发源、数据对齐方式等。
2. 配置ADC通道,包括通道号和采样时间。
3. 使能ADC,进行ADC校准。
4. 软件触发ADC转换,等待转换完成。
5. 读取转换结果,并进行处理。
# 6. I2C、SPI、ADC协同工作
在实际应用中,STM32单片机的外设接口往往需要协同工作,以实现更复杂的功能。例如:
- 串口用于与上位机通信,接收指令和发送数据。
- I2C用于连接外部传感器或EEPROM,获取数据或存储数据。
- SPI用于连接外部显示器或SD卡,显示信息或存储数据。
- ADC用于采集模拟信号,如温度、湿度等。
这些外设接口协同工作时,需要考虑以下问题:
- **通信时序:**不同外设接口的通信时序不同,需要协调好通信速率和数据格式。
- **中断处理:**多个外设接口同时工作时,需要设置中断优先级,以保证重要事件及时处理。
- **资源分配:**STM32单片机资源有限,需要合理分配GPIO、DMA和中断资源,以避免冲突。
下面是一个串口、I2C、SPI和ADC协同工作的示例:
```c
// 初始化串口、I2C、SPI和ADC
USART_Init();
I2C_Init();
SPI_Init();
ADC_Init();
// 启用串口中断
NVIC_EnableIRQ(USARTx_IRQn);
// 启用I2C中断
NVIC_EnableIRQ(I2Cx_IRQn);
// 启用SPI中断
NVIC_EnableIRQ(SPIx_IRQn);
// 启用ADC中断
NVIC_EnableIRQ(ADCx_IRQn);
// 主循环
while (1) {
// 处理串口中断
if (USARTx_IRQHandler()) {
// ...
}
// 处理I2C中断
if (I2Cx_IRQHandler()) {
// ...
}
// 处理SPI中断
if (SPIx_IRQHandler()) {
// ...
}
// 处理ADC中断
if (ADCx_IRQHandler()) {
// ...
}
}
```
在这个示例中,串口用于与上位机通信,I2C用于连接外部传感器,SPI用于连接外部显示器,ADC用于采集模拟信号。当某个外设接口产生中断时,对应的中断处理函数会被调用,执行相应的操作。
## 6.2 外设接口在实际项目中的应用
STM32单片机的外设接口在实际项目中有着广泛的应用,例如:
- **数据采集:**使用ADC采集模拟信号,如温度、湿度、光照强度等。
- **人机交互:**使用串口与上位机通信,接收指令和显示信息。
- **数据存储:**使用I2C连接外部EEPROM或SD卡,存储数据。
- **设备控制:**使用SPI连接外部显示器或电机驱动器,控制设备。
以下是一些实际项目示例:
- **温湿度监测系统:**使用ADC采集温度和湿度数据,通过串口发送给上位机,上位机负责数据显示和存储。
- **智能家居控制系统:**使用I2C连接外部传感器和执行器,通过串口与上位机通信,实现远程控制和数据采集。
- **数据记录仪:**使用SPI连接外部SD卡,通过串口接收数据并存储在SD卡中。
- **机器人控制系统:**使用SPI连接外部电机驱动器,通过串口接收指令并控制机器人运动。
0
0