【STM32单片机开发秘籍】:从入门到精通的10大实战案例
发布时间: 2024-07-02 15:19:20 阅读量: 112 订阅数: 53
STM32开发指南(例子)
![【STM32单片机开发秘籍】:从入门到精通的10大实战案例](https://img-blog.csdnimg.cn/direct/0dd32f15f1cd45869db1898d38f0da8e.png)
# 1. STM32单片机简介及开发环境搭建
STM32是一款基于ARM Cortex-M内核的32位微控制器,广泛应用于嵌入式系统开发。其特点包括:
- **高性能:**基于ARM Cortex-M内核,提供高处理能力和低功耗。
- **丰富的外设:**集成了丰富的片上外设,如GPIO、定时器、ADC和UART,简化了外围设备的连接。
- **低功耗:**提供多种低功耗模式,可延长电池寿命。
# 2. STM32单片机基础编程
### 2.1 C语言基础语法
#### 2.1.1 数据类型和变量
在C语言中,数据类型决定了变量可以存储的值的类型和范围。STM32单片机主要使用以下数据类型:
- **整型:**int、short、long,用于存储整数。
- **浮点型:**float、double,用于存储小数。
- **字符型:**char,用于存储单个字符。
- **布尔型:**bool,用于存储真或假。
变量用于存储数据,其声明语法为:
```c
<数据类型> <变量名>;
```
例如:
```c
int num;
float temp;
char ch;
```
#### 2.1.2 运算符和表达式
运算符用于对数据进行操作,表达式由运算符和操作数组成。STM32单片机中常用的运算符包括:
- **算术运算符:**+、-、*、/、%
- **关系运算符:**==、!=、>、<、>=、<=
- **逻辑运算符:**&&、||、!
表达式用于计算值,其语法为:
```c
<操作数> <运算符> <操作数>
```
例如:
```c
int sum = a + b;
float avg = (x + y) / 2;
bool result = (x > 0) && (y < 0);
```
### 2.2 STM32单片机硬件架构
#### 2.2.1 内核和外设
STM32单片机采用基于ARM Cortex-M内核,该内核具有高性能、低功耗的特点。内核负责执行程序指令,而外设负责与外部设备交互。
STM32单片机的外设包括:
- **GPIO:**通用输入/输出端口,用于连接外部设备。
- **定时器:**用于生成脉冲、测量时间和创建PWM信号。
- **串口:**用于与其他设备进行串行通信。
- **I2C:**用于与I2C设备进行通信。
- **ADC:**用于将模拟信号转换为数字信号。
#### 2.2.2 GPIO和定时器
**GPIO(通用输入/输出端口)**
GPIO端口是STM32单片机上最基本的输入/输出接口,可以配置为输入或输出模式。每个GPIO端口有16个引脚,可以连接外部设备。
GPIO配置和操作函数:
```c
void GPIO_Init(GPIO_TypeDef *GPIOx, uint32_t Pin, GPIO_Mode_TypeDef Mode);
void GPIO_WriteBit(GPIO_TypeDef *GPIOx, uint32_t Pin, BitAction BitVal);
uint32_t GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint32_t Pin);
```
**定时器**
定时器是STM32单片机上用于生成脉冲、测量时间和创建PWM信号的外设。每个STM32单片机有多个定时器,每个定时器有不同的功能。
定时器配置和操作函数:
```c
void TIM_TimeBaseInit(TIM_TypeDef *TIMx, TIM_TimeBaseInitTypeDef *TIM_TimeBaseInitStruct);
void TIM_Cmd(TIM_TypeDef *TIMx, FunctionalState NewState);
void TIM_SetCounter(TIM_TypeDef *TIMx, uint32_t Counter);
uint32_t TIM_GetCounter(TIM_TypeDef *TIMx);
```
# 3. STM32单片机外设编程
### 3.1 GPIO编程
GPIO(通用输入/输出)是STM32单片机中最重要的外设之一,它允许单片机与外部设备进行交互。
#### 3.1.1 GPIO配置和操作
**GPIO配置**
```c
// 初始化GPIO端口
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
// 设置GPIO引脚模式
GPIOA->MODER &= ~GPIO_MODER_MODE5;
GPIOA->MODER |= GPIO_MODER_MODE5_0;
// 设置GPIO引脚类型
GPIOA->OTYPER &= ~GPIO_OTYPER_OT5;
// 设置GPIO引脚速率
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED5;
GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED5_0;
// 设置GPIO引脚上拉/下拉电阻
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD5;
GPIOA->PUPDR |= GPIO_PUPDR_PUPD5_0;
```
**参数说明:**
* `RCC_AHB1ENR_GPIOAEN`:使能GPIOA端口时钟
* `GPIO_MODER_MODE5_0`:将PA5引脚设置为输出模式
* `GPIO_OTYPER_OT5`:将PA5引脚设置为推挽输出
* `GPIO_OSPEEDR_OSPEED5_0`:将PA5引脚的输出速率设置为低速
* `GPIO_PUPDR_PUPD5_0`:将PA5引脚的上拉电阻设置为无
**GPIO操作**
```c
// 设置GPIO引脚输出高电平
GPIOA->BSRR |= GPIO_BSRR_BS_5;
// 设置GPIO引脚输出低电平
GPIOA->BSRR |= GPIO_BSRR_BR_5;
// 读取GPIO引脚输入电平
uint32_t input = GPIOA->IDR & GPIO_IDR_ID5;
```
**参数说明:**
* `GPIO_BSRR_BS_5`:将PA5引脚设置为高电平
* `GPIO_BSRR_BR_5`:将PA5引脚设置为低电平
* `GPIO_IDR_ID5`:读取PA5引脚的输入电平
#### 3.1.2 中断处理
STM32单片机支持GPIO中断,当GPIO引脚电平发生变化时,可以触发中断。
**中断配置**
```c
// 使能GPIOA中断
NVIC_EnableIRQ(EXTI0_IRQn);
// 配置GPIOA中断
EXTI->IMR |= EXTI_IMR_IM0;
EXTI->RTSR |= EXTI_RTSR_RT0;
```
**参数说明:**
* `EXTI0_IRQn`:GPIOA中断号
* `EXTI_IMR_IM0`:使能GPIOA中断
* `EXTI_RTSR_RT0`:触发GPIOA中断的事件为上升沿
**中断处理函数**
```c
void EXTI0_IRQHandler(void)
{
// 清除中断标志位
EXTI->PR |= EXTI_PR_PR0;
// 执行中断处理代码
}
```
### 3.2 定时器编程
定时器是STM32单片机中另一个重要的外设,它可以生成精确的时间间隔和脉冲。
#### 3.2.1 定时器配置和操作
**定时器配置**
```c
// 使能定时器2时钟
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// 设置定时器2预分频器
TIM2->PSC = 7200 - 1;
// 设置定时器2自动重装载值
TIM2->ARR = 1000 - 1;
// 使能定时器2
TIM2->CR1 |= TIM_CR1_CEN;
```
**参数说明:**
* `RCC_APB1ENR_TIM2EN`:使能定时器2时钟
* `TIM2->PSC`:设置定时器2预分频器,时钟频率为72MHz / (7200 + 1) = 10kHz
* `TIM2->ARR`:设置定时器2自动重装载值,定时周期为10kHz / (1000 + 1) = 10ms
* `TIM_CR1_CEN`:使能定时器2
**定时器操作**
```c
// 获取定时器2当前计数值
uint32_t count = TIM2->CNT;
// 设置定时器2比较输出值
TIM2->CCR1 = 500;
```
**参数说明:**
* `TIM2->CNT`:获取定时器2当前计数值
* `TIM2->CCR1`:设置定时器2比较输出值,当计数值达到比较输出值时,将触发中断
#### 3.2.2 PWM和捕获
STM32单片机中的定时器还支持PWM(脉冲宽度调制)和捕获功能。
**PWM配置**
```c
// 设置定时器2通道1为PWM输出模式
TIM2->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2;
// 设置定时器2通道1比较输出值
TIM2->CCR1 = 500;
```
**参数说明:**
* `TIM_CCMR1_OC1M_1`和`TIM_CCMR1_OC1M_2`:设置定时器2通道1为PWM输出模式
* `TIM2->CCR1`:设置定时器2通道1比较输出值,占空比为50%
**捕获配置**
```c
// 设置定时器2通道2为输入捕获模式
TIM2->CCMR1 |= TIM_CCMR1_CC2S_0;
// 设置定时器2通道2输入滤波器
TIM2->CCMR1 |= TIM_CCMR1_IC2F_0 | TIM_CCMR1_IC2F_1 | TIM_CCMR1_IC2F_2;
```
**参数说明:**
* `TIM_CCMR1_CC2S_0`:设置定时器2通道2为输入捕获模式
* `TIM_CCMR1_IC2F_0`、`TIM_CCMR1_IC2F_1`和`TIM_CCMR1_IC2F_2`:设置定时器2通道2输入滤波器,滤波时间为8个时钟周期
# 4. STM32单片机通信编程
### 4.1 串口通信
**4.1.1 串口配置和操作**
串口是STM32单片机常用的通信外设,用于与其他设备进行数据传输。STM32单片机提供了多个串口,每个串口都有自己的配置寄存器和数据寄存器。
要配置串口,需要设置波特率、数据位、停止位和校验位等参数。波特率表示数据传输速率,单位为比特/秒(bps)。数据位表示每个数据帧中传输的数据位数,通常为8位。停止位表示数据帧结束时发送的停止位数,通常为1或2位。校验位用于检测数据传输过程中的错误,通常为无校验、奇校验或偶校验。
```c
// 配置串口1
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 使能串口1时钟
USART1->BRR = 0x683; // 设置波特率为9600bps
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE; // 使能发送器和接收器
USART1->CR2 |= USART_CR2_STOP1; // 设置1个停止位
```
**4.1.2 数据收发**
配置好串口后,就可以进行数据收发操作。数据发送通过发送数据寄存器(USART_DR),数据接收通过接收数据寄存器(USART_DR)。
```c
// 发送数据
USART1->DR = 'A'; // 发送字符'A'
// 接收数据
while (!(USART1->SR & USART_SR_RXNE)) {} // 等待接收数据
char data = USART1->DR; // 读取接收到的数据
```
### 4.2 I2C通信
**4.2.1 I2C配置和操作**
I2C(Inter-Integrated Circuit)是一种串行通信协议,用于连接多个设备。STM32单片机提供了多个I2C接口,每个接口都有自己的配置寄存器和数据寄存器。
要配置I2C接口,需要设置时钟频率、从机地址和通信模式等参数。时钟频率通常为100kHz或400kHz。从机地址是设备在I2C总线上的唯一标识符。通信模式可以为主机模式或从机模式。
```c
// 配置I2C1
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // 使能I2C1时钟
I2C1->CR2 |= I2C_CR2_FREQ_100K; // 设置时钟频率为100kHz
I2C1->OAR1 |= 0x10; // 设置从机地址为0x10
I2C1->CR1 |= I2C_CR1_PE; // 使能I2C接口
```
**4.2.2 传感器和显示器连接**
I2C接口可以连接各种传感器和显示器,如温度传感器、湿度传感器、OLED显示器等。通过I2C协议,可以读取传感器数据或向显示器发送数据。
```c
// 读取温度传感器数据
uint8_t data[2];
I2C1->CR1 |= I2C_CR1_START; // 发送起始信号
while (!(I2C1->SR1 & I2C_SR1_SB)); // 等待起始信号发送完成
I2C1->DR = 0x40; // 发送从机地址(读)
while (!(I2C1->SR1 & I2C_SR1_ADDR)); // 等待从机地址发送完成
I2C1->DR = 0x00; // 发送寄存器地址
while (!(I2C1->SR1 & I2C_SR1_TXE)); // 等待数据发送完成
I2C1->CR1 |= I2C_CR1_STOP; // 发送停止信号
I2C1->CR1 |= I2C_CR1_START; // 发送起始信号
while (!(I2C1->SR1 & I2C_SR1_SB)); // 等待起始信号发送完成
I2C1->DR = 0x41; // 发送从机地址(读)
while (!(I2C1->SR1 & I2C_SR1_ADDR)); // 等待从机地址发送完成
I2C1->CR1 |= I2C_CR1_ACK; // 发送ACK信号
while (!(I2C1->SR1 & I2C_SR1_RXNE)); // 等待数据接收完成
data[0] = I2C1->DR; // 读取数据
I2C1->CR1 |= I2C_CR1_NACK; // 发送NACK信号
while (!(I2C1->SR1 & I2C_SR1_RXNE)); // 等待数据接收完成
data[1] = I2C1->DR; // 读取数据
I2C1->CR1 |= I2C_CR1_STOP; // 发送停止信号
// 向OLED显示器发送数据
I2C1->CR1 |= I2C_CR1_START; // 发送起始信号
while (!(I2C1->SR1 & I2C_SR1_SB)); // 等待起始信号发送完成
I2C1->DR = 0x3C; // 发送从机地址(写)
while (!(I2C1->SR1 & I2C_SR1_ADDR)); // 等待从机地址发送完成
I2C1->DR = 0x00; // 发送命令代码
while (!(I2C1->SR1 & I2C_SR1_TXE)); // 等待数据发送完成
I2C1->DR = 0x00; // 发送数据
while (!(I2C1->SR1 & I2C_SR1_TXE)); // 等待数据发送完成
I2C1->CR1 |= I2C_CR1_STOP; // 发送停止信号
```
# 5. STM32单片机实战案例
### 5.1 LED控制
#### 5.1.1 GPIO配置
**代码块:**
```c
/* GPIO初始化 */
void GPIO_Init(void)
{
/* 使能GPIOA时钟 */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
/* 配置GPIOA.5为输出模式 */
GPIOA->CRH &= ~(GPIO_CRH_MODE5);
GPIOA->CRH |= GPIO_CRH_MODE5_0;
}
```
**逻辑分析:**
* RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;:使能GPIOA时钟,确保GPIOA外设可以正常工作。
* GPIOA->CRH &= ~(GPIO_CRH_MODE5);:清除GPIOA.5的模式位,将其复位为输入模式。
* GPIOA->CRH |= GPIO_CRH_MODE5_0;:设置GPIOA.5的模式位为输出模式。
#### 5.1.2 定时器配置
**代码块:**
```c
/* 定时器初始化 */
void TIM_Init(void)
{
/* 使能TIM2时钟 */
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
/* 配置TIM2为向上计数模式 */
TIM2->CR1 &= ~(TIM_CR1_DIR);
/* 设置TIM2的预分频系数为99 */
TIM2->PSC = 99;
/* 设置TIM2的重装载值 */
TIM2->ARR = 1000;
/* 使能TIM2中断 */
TIM2->DIER |= TIM_DIER_UIE;
/* 启动TIM2 */
TIM2->CR1 |= TIM_CR1_CEN;
}
```
**逻辑分析:**
* RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;:使能TIM2时钟,确保TIM2外设可以正常工作。
* TIM2->CR1 &= ~(TIM_CR1_DIR);:清除TIM2的计数方向位,将其设置为向上计数模式。
* TIM2->PSC = 99;:设置TIM2的预分频系数为99,即TIM2的时钟频率为系统时钟频率的100分之一。
* TIM2->ARR = 1000;:设置TIM2的重装载值,即TIM2每1000个时钟周期计数一次。
* TIM2->DIER |= TIM_DIER_UIE;:使能TIM2的中断,以便在TIM2计数溢出时产生中断。
* TIM2->CR1 |= TIM_CR1_CEN;:启动TIM2,开始计数。
### 5.2 温度测量
#### 5.2.1 传感器连接
**表格:**
| 传感器 | 引脚 | 连接 |
|---|---|---|
| LM35 | VCC | 5V |
| LM35 | GND | GND |
| LM35 | OUT | PA0 |
**说明:**
* LM35温度传感器通过VCC和GND引脚连接到电源和地。
* LM35的OUT引脚连接到GPIOA.0,用于采集温度数据。
#### 5.2.2 数据采集和处理
**代码块:**
```c
/* 温度采集函数 */
float GetTemperature(void)
{
/* 读取ADC值 */
uint16_t adcValue = ADC_GetConversionValue(ADC1);
/* 计算温度值 */
float temperature = (float)adcValue * 0.48828125;
/* 返回温度值 */
return temperature;
}
```
**逻辑分析:**
* ADC_GetConversionValue(ADC1);:读取ADC1的转换值,即温度传感器的输出电压对应的ADC值。
* float temperature = (float)adcValue * 0.48828125;:将ADC值转换为温度值。LM35温度传感器的输出电压与温度成线性关系,转换系数为0.48828125。
* 返回温度值:返回计算得到的温度值。
# 6.1 实时操作系统(RTOS)
### 6.1.1 RTOS简介和选择
**实时操作系统(RTOS)**是一种专门为嵌入式系统设计的操作系统,它能够保证系统在实时约束下可靠、可预测地运行。RTOS提供了任务管理、调度、同步和通信等基本服务,使开发者能够构建复杂的嵌入式系统。
**选择RTOS时需要考虑以下因素:**
- **实时性要求:**RTOS必须能够满足系统的实时约束,如响应时间和时延。
- **内存占用:**RTOS的内存占用必须与系统的资源限制相匹配。
- **功能特性:**RTOS应提供所需的特性,如任务管理、调度算法、同步机制和通信协议。
- **支持的硬件平台:**RTOS必须与系统的硬件平台兼容。
- **开发工具:**RTOS应提供完善的开发工具,如IDE、调试器和文档。
### 6.1.2 任务管理和调度
**任务管理**是RTOS的核心功能之一,它负责创建、管理和销毁任务。**任务**是一个独立的执行线程,它具有自己的栈和程序计数器。
**调度**是RTOS负责决定哪个任务在某个时刻执行的过程。常见的调度算法包括:
- **先来先服务(FCFS):**任务按照到达顺序执行。
- **轮转调度(RR):**任务轮流执行,每个任务获得一个固定的时间片。
- **优先级调度:**任务根据优先级执行,优先级高的任务优先执行。
**任务调度算法的选择取决于系统的实时性要求和资源限制。**
0
0