STM32单片机外设编程:串口、定时器、中断、ADC的实战应用
发布时间: 2024-07-05 14:48:07 阅读量: 137 订阅数: 62
![STM32单片机外设编程:串口、定时器、中断、ADC的实战应用](https://img-blog.csdnimg.cn/509823d7be834421a341f28adb5146bf.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aW955qEX-a1qeWQjOWtpg==,size_20,color_FFFFFF,t_70,g_se,x_16)
# 1. STM32单片机外设简介**
STM32单片机是一款基于ARM Cortex-M内核的32位微控制器,集成了丰富的片上外设,为嵌入式系统开发提供了强大的硬件支持。本章将对STM32单片机的常用外设进行简介,包括串口、定时器、中断、ADC等,为后续章节的实战编程奠定基础。
# 2. 串口编程实战**
**2.1 串口通信原理及配置**
**2.1.1 串口通信基础**
串口通信是一种异步串行通信协议,用于在两个设备之间传输数据。它使用一条数据线和一条控制线,数据线传输数据位,控制线用于同步通信。串口通信的特点是:
* **异步传输:**数据位逐个发送,没有时钟信号同步。
* **半双工通信:**同一时间只能有一个设备发送或接收数据。
* **数据格式:**数据通常以 8 位字节的形式传输,并附加起始位和停止位。
**2.1.2 STM32串口配置**
STM32单片机集成了多个串口外设,用于实现串口通信。串口配置主要涉及以下步骤:
* **选择串口外设:**STM32单片机有多个串口外设,如 USART1、USART2 等。根据需要选择合适的串口外设。
* **配置波特率:**波特率决定了数据传输速率。通过设置串口外设的波特率寄存器来配置波特率。
* **配置数据格式:**数据格式包括数据位、停止位和奇偶校验位。通过设置串口外设的数据格式寄存器来配置数据格式。
* **配置中断:**串口外设支持中断,当数据接收或发送完成时会触发中断。通过设置串口外设的中断寄存器来配置中断。
**代码块:**
```c
#include "stm32f10x.h"
void USART1_Init(void)
{
// 选择串口外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// 配置波特率
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
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_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_EnableIRQ(USART1_IRQn);
}
```
**逻辑分析:**
* `RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);`:使能 USART1 外设时钟。
* `USART_Init(USART1, &USART_InitStructure);`:初始化 USART1 外设,配置波特率、数据格式和模式。
* `USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);`:使能 USART1 接收中断。
* `NVIC_EnableIRQ(USART1_IRQn);`:使能 USART1 中断向量。
**2.2 串口数据收发**
**2.2.1 字符串收发**
字符串收发是最常用的串口数据收发方式。通过串口发送和接收字符串,可以实现设备之间的文本信息交换。
**代码块:**
```c
void USART1_SendString(char *str)
{
while (*str)
{
USART_SendData(USART1, *str);
str++;
}
}
char USART1_ReceiveString(void)
{
char ch;
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)
;
ch = USART_ReceiveData(USART1);
return ch;
}
```
**逻辑分析:**
* `USART1_SendString(char *str)`:发送一个字符串。循环遍历字符串,逐个字符发送。
* `USART1_ReceiveString(void)`:接收一个字符。等待接收数据寄存器非空,然后读取接收到的字符。
**2.2.2 数据结构收发**
除了字符串,串口还可以收发自定义的数据结构。通过将数据结构序列化为字节流,可以在设备之间传输复杂的数据。
**代码块:**
```c
typedef struct {
int a;
float b;
} DataStruct;
void USART1_SendDataStruct(DataStruct *data)
{
USART_SendData(USART1, data->a);
USART_SendData(USART1, data->b);
}
DataStruct USART1_ReceiveDataStruct(void)
{
DataStruct data;
data.a = USART_ReceiveData(USART1);
data.b = USART_ReceiveData(USART1);
return data;
}
```
**逻辑分析:**
* `USART1_SendDataStruct(DataStruct *data)`:发送一个数据结构。逐个成员变量发送数据。
* `USART1_ReceiveDataStruct(void)`:接收一个数据结构。逐个成员变量接收数据。
# 3. 定时器编程实战
### 3.1 定时器工作原理及配置
#### 3.1.1 定时器基础
定时器是一种用于测量时间间隔或生成特定频率脉冲的硬件模块。STM32单片机中有多个定时器,每个定时器都有自己的功能和配置选项。
定时器的工作原理是基于一个计数器,该计数器以特定频率递增。当计数器达到预设值时,它会产生一个中断或触发一个事件。
#### 3.1.2 STM32定时器配置
STM32定时器可以通过寄存器进行配置。主要配置寄存器包括:
- **TIMx_CR1:**控制定时器的基本功能,如使能、时钟源、计数方向等。
- **TIMx_PSC:**预分频寄存器,用于设置定时器的时钟分频比。
- **TIMx_ARR:**自动重装载寄存器,用于设置定时器的计数上限。
**示例代码:**
```c
// 配置定时器3为向上计数模式,时钟源为APB1,分频比为100
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // 使能定时器3时钟
TIM3->CR1 = 0x0000; // 重置定时器3控制寄存器
TIM3->CR1 |= TIM_CR1_CEN; // 使能定时器3
TIM3->PSC = 99; // 设置分频比为100
TIM3->ARR = 10000; // 设置自动重装载值为10000
```
### 3.2 定时器中断应用
#### 3.2.1 定时器中断原理
当定时器计数器达到预设值时,会产生一个中断请求。中断请求由NVIC(嵌套向量中断控制器)处理,它会调用相应的中断服务函数。
#### 3.2.2 定时器中断应用示例
定时器中断可以用于各种应用,例如:
- **延时:**使用定时器中断实现精确的延时。
- **周期性任务:**定时器中断可以触发周期性任务,如LED闪烁或数据采集。
- **事件计数:**使用定时器中断计数外部事件,如按钮按下次数。
**示例代码:**
```c
// 定时器3中断服务函数
void TIM3_IRQHandler(void)
{
// 清除中断标志位
TIM3->SR &= ~TIM_SR_UIF;
// 执行中断处理逻辑
// ...
}
// 配置定时器3中断
void TIM3_Config_Interrupt(void)
{
// 使能定时器3中断
TIM3->DIER |= TIM_DIER_UIE;
// 设置NVIC中断优先级
NVIC_SetPriority(TIM3_IRQn, 1);
// 使能NVIC中断
NVIC_EnableIRQ(TIM3_IRQn);
}
```
# 4.1 中断原理及配置
### 4.1.1 中断基础
中断是一种硬件机制,当发生特定事件时,处理器会暂停当前正在执行的程序,转而执行中断服务程序。中断事件可以由外部设备(如串口、定时器)或内部事件(如错误)触发。
**中断类型**
STM32单片机支持多种中断类型,包括:
- **外部中断:**由外部引脚上的电平变化触发。
- **内部中断:**由内部外设(如串口、定时器)的事件触发。
- **NMI中断:**由非屏蔽中断请求触发,具有最高优先级。
- **HardFault中断:**由处理器检测到的错误触发,如内存访问违规。
**中断优先级**
每个中断都有一个优先级,决定了当多个中断同时发生时,哪个中断先被处理。STM32单片机支持多级中断优先级,允许用户根据中断的重要性进行配置。
### 4.1.2 STM32中断配置
**NVIC寄存器**
STM32的中断控制器称为NVIC(嵌套向量中断控制器)。它包含一系列寄存器,用于配置和管理中断。
**中断使能寄存器(ISER)**
ISER寄存器用于使能或禁用中断。每个中断都有一个对应的位,置1表示使能,置0表示禁用。
**中断挂起寄存器(ISPR)**
ISPR寄存器用于挂起中断。置1表示挂起中断,置0表示取消挂起。
**中断优先级寄存器(IPR)**
IPR寄存器用于设置中断优先级。每个中断都有一个对应的位,值越大表示优先级越高。
**中断配置步骤**
配置STM32中断的步骤如下:
1. **使能中断:**在ISER寄存器中置1对应中断的位。
2. **设置优先级:**在IPR寄存器中设置对应中断的优先级。
3. **编写中断服务函数:**为每个中断编写对应的中断服务函数。
4. **在中断服务函数中:**处理中断事件,并清除中断标志位。
**代码示例**
以下代码示例展示了如何使能和配置串口中断:
```c
// 使能串口中断
NVIC_ISER(NVIC_IRQ_USART1_IRQn) = 1;
// 设置串口中断优先级为3
NVIC_IPR(NVIC_IRQ_USART1_IRQn) = 3;
```
# 5. ADC编程实战
### 5.1 ADC工作原理及配置
#### 5.1.1 ADC基础
模数转换器(ADC)是一种将模拟信号(例如电压或电流)转换为数字信号的电子器件。在STM32单片机中,ADC外设负责执行此转换。
ADC的工作原理是将模拟信号采样并将其转换为数字值。采样频率和分辨率决定了ADC的精度和速度。采样频率是指ADC每秒采样模拟信号的次数,而分辨率是指ADC可以表示的数字值的位数。
#### 5.1.2 STM32 ADC配置
STM32单片机有多个ADC外设,每个外设都有自己的配置寄存器。要配置ADC,需要设置以下参数:
- **采样时间:**决定ADC将模拟信号保持多长时间以进行采样。
- **分辨率:**决定ADC可以表示的数字值的位数。
- **通道:**选择要转换的模拟输入通道。
- **触发源:**决定ADC何时开始转换。
### 5.2 ADC数据采集及处理
#### 5.2.1 ADC数据采集
ADC数据采集过程涉及以下步骤:
1. 配置ADC外设。
2. 触发ADC转换。
3. 读回转换结果。
```c
// ADC配置
ADC_HandleTypeDef hadc1;
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
HAL_ADC_Init(&hadc1);
// ADC触发转换
HAL_ADC_Start(&hadc1);
// 读回转换结果
uint16_t adcValue = HAL_ADC_GetValue(&hadc1);
```
#### 5.2.2 ADC数据处理
ADC数据采集后,通常需要进行处理以提取有用的信息。常见的处理技术包括:
- **滤波:**去除ADC数据中的噪声和干扰。
- **校准:**补偿ADC的误差和偏移。
- **单位转换:**将ADC值转换为物理单位(例如电压或电流)。
```c
// ADC数据滤波
uint16_t filteredValue = 0;
for (int i = 0; i < 10; i++) {
filteredValue += HAL_ADC_GetValue(&hadc1);
}
filteredValue /= 10;
// ADC数据校准
float calibratedValue = filteredValue * 0.985;
// ADC数据单位转换
float voltage = calibratedValue * 3.3 / 4095;
```
# 6. 外设综合应用实例
本章将介绍STM32单片机外设的综合应用实例,展示如何将多个外设组合使用,实现更加复杂的功能。
### 6.1 串口与定时器联动
**应用场景:**定时发送数据到串口
**操作步骤:**
1. 配置定时器,设置中断时间间隔。
2. 在定时器中断服务函数中,发送数据到串口。
```c
// 配置定时器
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 1000; // 1000ms
TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 1MHz
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 配置定时器中断
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
// 定时器中断服务函数
void TIM3_IRQHandler(void)
{
// 清除中断标志位
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
// 发送数据到串口
USART_SendData(USART1, 'A');
}
```
### 6.2 定时器与中断联动
**应用场景:**定时触发中断
**操作步骤:**
1. 配置定时器,设置中断时间间隔。
2. 在定时器中断服务函数中,执行中断处理逻辑。
```c
// 配置定时器
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 1000; // 1000ms
TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 1MHz
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 配置定时器中断
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
// 定时器中断服务函数
void TIM3_IRQHandler(void)
{
// 清除中断标志位
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
// 中断处理逻辑
// ...
}
```
### 6.3 ADC与串口联动
**应用场景:**采集模拟信号并通过串口发送
**操作步骤:**
1. 配置ADC,设置采样频率和分辨率。
2. 启动ADC转换。
3. 在ADC转换完成中断服务函数中,读取ADC数据并通过串口发送。
```c
// 配置ADC
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
// 启动ADC转换
ADC_Cmd(ADC1, ENABLE);
// ADC转换完成中断服务函数
void ADC1_IRQHandler(void)
{
// 清除中断标志位
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
// 读取ADC数据
uint16_t adc_data = ADC_GetConversionValue(ADC1);
// 通过串口发送ADC数据
USART_SendData(USART1, (adc_data >> 8) & 0xFF);
USART_SendData(USART1, adc_data & 0xFF);
}
```
0
0