STM32单片机编程实战指南:C语言实战,快速上手
发布时间: 2024-07-02 06:46:08 阅读量: 479 订阅数: 59 ![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![STM32单片机编程实战指南:C语言实战,快速上手](https://img-blog.csdnimg.cn/img_convert/7bccd48cc923d795c1895b27b8100291.png)
# 1. STM32单片机简介及开发环境搭建
STM32单片机是意法半导体(STMicroelectronics)公司推出的一系列基于ARM Cortex-M内核的32位微控制器。它以其高性能、低功耗和丰富的外设而著称,广泛应用于工业控制、物联网、汽车电子等领域。
### 1.1 STM32单片机特点
STM32单片机的特点包括:
- 基于ARM Cortex-M内核,性能强劲
- 低功耗设计,适合电池供电设备
- 丰富的片上外设,如GPIO、定时器、串口、ADC等
- 高度集成的开发环境,简化开发过程
### 1.2 开发环境搭建
STM32单片机的开发环境搭建主要包括以下步骤:
1. 安装Keil MDK软件(开发工具)
2. 安装STMicroelectronics的STM32CubeMX工具(外设配置工具)
3. 安装ST-Link调试器(调试工具)
4. 配置Keil MDK软件的编译器和调试器选项
# 2. C语言基础语法及STM32开发环境应用
### 2.1 C语言基础语法
#### 2.1.1 数据类型和变量
**数据类型**
C语言中定义了多种数据类型,用于表示不同类型的数值和字符。主要的数据类型包括:
- 整数类型:int、short、long、long long
- 浮点数类型:float、double、long double
- 字符类型:char、wchar_t
- 布尔类型:bool
**变量**
变量是用于存储数据的内存空间。在使用变量之前,需要对其进行声明,声明包括数据类型和变量名。例如:
```c
int num;
float temp;
char letter;
```
#### 2.1.2 运算符和表达式
**运算符**
运算符用于对操作数进行各种操作。C语言中常用的运算符包括:
- 算术运算符:+、-、*、/、%
- 关系运算符:==、!=、>、<、>=、<=
- 逻辑运算符:&&、||、!
- 位运算符:<<、>>、&、|、^
**表达式**
表达式是一组运算符和操作数组合在一起形成的语句。表达式可以求值,得到一个结果。例如:
```c
int result = 10 + 5;
```
#### 2.1.3 控制流语句
**条件语句**
条件语句用于根据条件执行不同的代码块。常用的条件语句包括:
- if-else 语句
- switch-case 语句
**循环语句**
循环语句用于重复执行一段代码块。常用的循环语句包括:
- for 循环
- while 循环
- do-while 循环
### 2.2 STM32开发环境应用
#### 2.2.1 Keil MDK简介
Keil MDK(Microcontroller Development Kit)是ARM公司提供的一款STM32单片机开发环境,包括编译器、调试器、仿真器等工具。
#### 2.2.2 创建和编译STM32工程
在Keil MDK中创建STM32工程的步骤:
1. 打开Keil MDK,选择 **File** -> **New** -> **uVision Project**。
2. 在 **New Project Wizard** 中,选择目标设备、编译器版本等信息。
3. 点击 **OK** 创建工程。
编译工程的步骤:
1. 点击 **Build** -> **Build Target**。
2. 编译成功后,会在工程目录下生成可执行文件(.hex)。
#### 2.2.3 调试和仿真
**调试**
调试用于检测和修复代码中的错误。在Keil MDK中,可以通过设置断点、单步执行等方式进行调试。
**仿真**
仿真用于在实际硬件上运行代码,验证代码的正确性。在Keil MDK中,可以通过连接仿真器,使用 **Debug** -> **Start/Stop Debug Session** 选项进行仿真。
**代码示例:**
```c
// main.c
#include <stm32f10x.h>
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 初始化GPIOC第13引脚为输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
while (1) {
// 设置GPIOC第13引脚为高电平
GPIO_SetBits(GPIOC, GPIO_Pin_13);
// 延时1秒
Delay(1000);
// 设置GPIOC第13引脚为低电平
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
// 延时1秒
Delay(1000);
}
}
```
**代码逻辑分析:**
- 初始化GPIOC第13引脚为输出模式。
- 在一个无限循环中,交替设置GPIOC第13引脚为高电平和低电平,并延时1秒。
- 延时函数 **Delay** 由STM32标准库提供,用于产生指定时间的延时。
# 3. STM32单片机外设编程
STM32单片机外设丰富,功能强大,本章节将介绍STM32单片机的GPIO、定时器和串口外设的编程方法。
### 3.1 GPIO编程
GPIO(General Purpose Input/Output)是STM32单片机最基本的I/O外设,可以用于控制外部设备的输入输出操作。
#### 3.1.1 GPIO的结构和配置
STM32单片机的GPIO外设由16个GPIO端口组成,每个端口有16个GPIO引脚。GPIO端口的寄存器结构如下:
```c
typedef struct {
uint32_t MODER; // 模式寄存器
uint32_t OTYPER; // 输出类型寄存器
uint32_t OSPEEDR; // 输出速度寄存器
uint32_t PUPDR; // 上拉/下拉寄存器
uint32_t IDR; // 输入数据寄存器
uint32_t ODR; // 输出数据寄存器
uint32_t BSRR; // 位设置/复位寄存器
uint32_t LCKR; // 锁定寄存器
uint32_t AFR[2]; // 备用功能寄存器
} GPIO_TypeDef;
```
GPIO的配置主要通过设置MODER寄存器来实现,MODER寄存器的每两位对应一个GPIO引脚的模式配置,可以配置为输入模式、输出模式、推挽输出模式或开漏输出模式。
#### 3.1.2 GPIO的输入输出操作
GPIO的输入输出操作可以通过设置ODR寄存器来实现。当ODR寄存器的某一位为1时,对应的GPIO引脚输出高电平;当ODR寄存器的某一位为0时,对应的GPIO引脚输出低电平。
GPIO的输入操作可以通过读取IDR寄存器来实现。IDR寄存器的每一位对应一个GPIO引脚的输入状态,当IDR寄存器的某一位为1时,对应的GPIO引脚输入高电平;当IDR寄存器的某一位为0时,对应的GPIO引脚输入低电平。
### 3.2 定时器编程
定时器是STM32单片机常用的外设,可以用于产生定时中断、产生PWM波形、捕获外部信号等。
#### 3.2.1 定时器的结构和配置
STM32单片机有16个定时器,分为通用定时器和高级定时器两种类型。通用定时器主要用于产生定时中断和PWM波形,高级定时器功能更丰富,可以用于产生定时中断、PWM波形、捕获外部信号、输出比较等。
定时器的寄存器结构如下:
```c
typedef struct {
uint32_t CR1; // 控制寄存器1
uint32_t CR2; // 控制寄存器2
uint32_t SMCR; // 状态和模式寄存器
uint32_t DIER; // 中断和事件寄存器
uint32_t SR; // 状态寄存器
uint32_t EGR; // 事件生成寄存器
uint32_t CCMR1; // 捕获/比较模式寄存器1
uint32_t CCMR2; // 捕获/比较模式寄存器2
uint32_tCCER; // 捕获/比较使能寄存器
uint32_t CNT; // 计数器寄存器
uint32_t PSC; // 预分频器寄存器
uint32_t ARR; // 自动重装载寄存器
uint32_t RCR; // 重复计数器寄存器
uint32_t CCR1; // 捕获/比较寄存器1
uint32_t CCR2; // 捕获/比较寄存器2
uint32_t CCR3; // 捕获/比较寄存器3
uint32_t CCR4; // 捕获/比较寄存器4
} TIM_TypeDef;
```
定时器的配置主要通过设置CR1寄存器和ARR寄存器来实现。CR1寄存器的CEN位用于使能定时器,ARR寄存器的值用于设置定时器的重装载值。
#### 3.2.2 定时器的基本操作
定时器的基本操作包括启动定时器、停止定时器、设置定时器中断等。
启动定时器可以通过设置CR1寄存器的CEN位为1来实现。
停止定时器可以通过设置CR1寄存器的CEN位为0来实现。
设置定时器中断可以通过设置DIER寄存器的UIE位为1来实现。
#### 3.2.3 定时器的高级应用
定时器的高级应用包括产生PWM波形、捕获外部信号、输出比较等。
产生PWM波形可以通过设置定时器的CCMR1寄存器和CCER寄存器来实现。
捕获外部信号可以通过设置定时器的CCMR1寄存器和CCER寄存器来实现。
输出比较可以通过设置定时器的CCMR1寄存器和CCER寄存器来实现。
### 3.3 串口编程
串口是STM32单片机常用的外设,可以用于与外部设备进行数据通信。
#### 3.3.1 串口的结构和配置
STM32单片机有6个串口,分为USART和UART两种类型。USART是通用同步异步收发器,UART是通用异步收发器。
串口的寄存器结构如下:
```c
typedef struct {
uint32_t SR; // 状态寄存器
uint32_t DR; // 数据寄存器
uint32_t BRR; // 波特率寄存器
uint32_t CR1; // 控制寄存器1
uint32_t CR2; // 控制寄存器2
uint32_t CR3; // 控制寄存器3
uint32_t GTPR; // 保证时间和预分频器寄存器
} USART_TypeDef;
```
串口的配置主要通过设置CR1寄存器和BRR寄存器来实现。CR1寄存器的UE位用于使能串口,BRR寄存器的值用于设置串口的波特率。
#### 3.3.2 串口的发送和接收
串口的发送和接收操作可以通过设置DR寄存器来实现。
串口的发送操作可以通过向DR寄存器写入数据来实现。
串口的接收操作可以通过读取DR寄存器来实现。
#### 3.3.3 串口的协议和应用
串口可以用于实现多种通信协议,如UART协议、RS-232协议、RS-485协议等。
串口在实际应用中非常广泛,如数据采集、设备控制、人机交互等。
# 4.1 中断基础
### 4.1.1 中断的分类和优先级
中断可以根据其来源和处理方式进行分类:
- **硬件中断:**由外部设备或内部外设(如定时器、串口等)产生的中断。
- **软件中断:**由软件指令(如SVC、BKPT等)产生的中断。
中断的优先级决定了中断处理的顺序。STM32单片机支持多级中断优先级,每个中断源都有一个优先级值。优先级较高的中断将在优先级较低的中断之前处理。
### 4.1.2 中断的处理流程
当一个中断发生时,CPU会执行以下步骤:
1. **保存当前上下文:**CPU将当前程序计数器(PC)、程序状态寄存器(PSR)和寄存器值压入堆栈。
2. **跳转到中断向量表:**CPU根据中断源的优先级跳转到中断向量表中对应的中断服务函数(ISR)地址。
3. **执行ISR:**ISR处理中断并执行必要的操作。
4. **恢复上下文:**ISR执行完毕后,CPU从堆栈中恢复保存的上下文,并继续执行中断发生前的程序。
## 4.2 STM32中断编程
### 4.2.1 中断向量表
STM32单片机的中断向量表位于存储器地址0x0000 0000。它包含了一系列中断向量,每个向量对应一个特定的中断源。当一个中断发生时,CPU会根据中断源的优先级跳转到对应的中断向量。
```
// 中断向量表
void Reset_Handler(void);
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
void WWDG_IRQHandler(void);
void PVD_IRQHandler(void);
void TAMP_STAMP_IRQHandler(void);
void RTC_WKUP_IRQHandler(void);
void FLASH_IRQHandler(void);
void RCC_IRQHandler(void);
void EXTI0_IRQHandler(void);
void EXTI1_IRQHandler(void);
void EXTI2_IRQHandler(void);
void EXTI3_IRQHandler(void);
void EXTI4_IRQHandler(void);
void DMA1_Stream0_IRQHandler(void);
void DMA1_Stream1_IRQHandler(void);
void DMA1_Stream2_IRQHandler(void);
void DMA1_Stream3_IRQHandler(void);
void DMA1_Stream4_IRQHandler(void);
void DMA1_Stream5_IRQHandler(void);
void DMA1_Stream6_IRQHandler(void);
void ADC1_2_IRQHandler(void);
void CAN1_TX_IRQHandler(void);
void CAN1_RX0_IRQHandler(void);
void CAN1_RX1_IRQHandler(void);
void CAN1_SCE_IRQHandler(void);
void EXTI9_5_IRQHandler(void);
void TIM1_BRK_TIM9_IRQHandler(void);
void TIM1_UP_TIM10_IRQHandler(void);
void TIM1_TRG_COM_TIM11_IRQHandler(void);
void TIM1_CC_IRQHandler(void);
void TIM2_IRQHandler(void);
void TIM3_IRQHandler(void);
void TIM4_IRQHandler(void);
void I2C1_EV_IRQHandler(void);
void I2C1_ER_IRQHandler(void);
void I2C2_EV_IRQHandler(void);
void I2C2_ER_IRQHandler(void);
void SPI1_IRQHandler(void);
void SPI2_IRQHandler(void);
void USART1_IRQHandler(void);
void USART2_IRQHandler(void);
void USART3_IRQHandler(void);
void EXTI15_10_IRQHandler(void);
void RTC_Alarm_IRQHandler(void);
void OTG_FS_WKUP_IRQHandler(void);
void TIM8_BRK_TIM12_IRQHandler(void);
void TIM8_UP_TIM13_IRQHandler(void);
void TIM8_TRG_COM_TIM14_IRQHandler(void);
void TIM8_CC_IRQHandler(void);
void DMA1_Stream7_IRQHandler(void);
void FMC_IRQHandler(void);
void SDIO_IRQHandler(void);
void TIM5_IRQHandler(void);
void SPI3_IRQHandler(void);
void UART4_IRQHandler(void);
void UART5_IRQHandler(void);
void TIM6_DAC_IRQHandler(void);
void TIM7_IRQHandler(void);
void DMA2_Stream0_IRQHandler(void);
void DMA2_Stream1_IRQHandler(void);
void DMA2_Stream2_IRQHandler(void);
void DMA2_Stream3_IRQHandler(void);
void DMA2_Stream4_IRQHandler(void);
void ETH_IRQHandler(void);
void ETH_WKUP_IRQHandler(void);
void CAN2_TX_IRQHandler(void);
void CAN2_RX0_IRQHandler(void);
void CAN2_RX1_IRQHandler(void);
void CAN2_SCE_IRQHandler(void);
void OTG_FS_IRQHandler(void);
void DMA2_Stream5_IRQHandler(void);
void DMA2_Stream6_IRQHandler(void);
void DMA2_Stream7_IRQHandler(void);
void USART6_IRQHandler(void);
void I2C3_EV_IRQHandler(void);
void I2C3_ER_IRQHandler(void);
void OTG_HS_EP1_OUT_IRQHandler(void);
void OTG_HS_EP1_IN_IRQHandler(void);
void OTG_HS_WKUP_IRQHandler(void);
void OTG_HS_IRQHandler(void);
void DCMI_IRQHandler(void);
void CRYP_IRQHandler(void);
void HASH_RNG_IRQHandler(void);
void FPU_IRQHandler(void);
void UART7_IRQHandler(void);
void UART8_IRQHandler(void);
void SPI4_IRQHandler(void);
void SPI5_IRQHandler(void);
void SPI6_IRQHandler(void);
void SAI1_IRQHandler(void);
void LCD_TFT_IRQHandler(void);
void LPTIM1_IRQHandler(void);
void TIM22_IRQHandler(void);
void TIM23_IRQHandler(void);
void TIM24_IRQHandler(void);
void TIM25_IRQHandler(void);
void TIM26_IRQHandler(void);
void TIM27_IRQHandler(void);
void I2C4_EV_IRQHandler(void);
void I2C4_ER_IRQHandler(void);
void SPI7_IRQHandler(void);
void FMPI2C1_EV_IRQHandler(void);
void FMPI2C1_ER_IRQHandler(void);
void LPUART1_IRQHandler(void);
void QUADSPI_IRQHandler(void);
void I2C5_EV_IRQHandler(void);
void I2C5_ER_IRQHandler(void);
void SPI8_IRQHandler(void);
void CCIPI_IRQHandler(void);
void FMC_SDMMC1_IRQHandler(void);
```
### 4.2.2 中断服务函数
中断服务函数(ISR)是响应中断而执行的代码。ISR必须定义为`__attribute__((interrupt("IRQ"))) `,并位于中断向量表中对应的地址。
```c
// 中断服务函数
void TIM2_IRQHandler(void)
{
// 清除中断标志位
TIM2->SR &= ~TIM_SR_UIF;
// 执行中断处理操作
// ...
}
```
### 4.2.3 中断使能和禁止
中断可以通过设置或清除中断使能寄存器(ISER)或中断禁止寄存器(ICER)来使能或禁止。
```c
// 使能中断
NVIC_EnableIRQ(TIM2_IRQn);
// 禁止中断
NVIC_DisableIRQ(TIM2_IRQn);
```
# 5.1 ADC编程
### 5.1.1 ADC的结构和配置
STM32单片机集成了多个ADC模块,每个模块包含多个通道,可以将模拟信号转换成数字信号。ADC模块的结构主要包括:
- **采样保持电路:**用于对模拟信号进行采样并保持,以保证在转换过程中信号不会发生变化。
- **模数转换器:**将采样后的模拟信号转换成数字信号。
- **参考电压源:**提供ADC模块的参考电压,通常为3.3V或5V。
- **控制寄存器:**用于配置ADC模块的工作模式、采样时间、转换速率等参数。
ADC模块的配置需要通过寄存器进行,主要包括:
- **ADC_CR1寄存器:**控制ADC模块的使能、采样时间、转换速率等参数。
- **ADC_CR2寄存器:**控制ADC模块的触发源、通道选择、连续转换模式等参数。
- **ADC_SMPRx寄存器:**控制各通道的采样时间。
### 5.1.2 ADC的采样和转换
ADC模块的采样和转换过程主要分为以下几个步骤:
1. **采样:**ADC模块通过采样保持电路对模拟信号进行采样,并将其保持在采样保持电容上。
2. **保持:**采样保持电路将采样后的信号保持一段时间,以保证在转换过程中信号不会发生变化。
3. **转换:**模数转换器将采样保持电路中的模拟信号转换成数字信号。
4. **读数:**转换完成后的数字信号可以通过ADC数据寄存器读取。
### 5.1.3 ADC的高级应用
除了基本采样和转换功能外,STM32 ADC模块还支持一些高级应用,例如:
- **DMA传输:**支持通过DMA控制器将ADC转换结果直接传输到内存中,提高数据传输效率。
- **中断触发:**支持通过中断触发ADC转换,当ADC转换完成时产生中断,提高程序响应速度。
- **多通道扫描:**支持同时对多个通道进行采样和转换,提高数据采集效率。
以下代码示例演示了如何使用STM32 ADC模块进行单次转换:
```c
// 使能ADC模块
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// 配置ADC模块
ADC1->CR1 = ADC_CR1_EOCIE | ADC_CR1_SCAN;
ADC1->CR2 = ADC_CR2_ADON;
// 启动ADC转换
ADC1->CR2 |= ADC_CR2_SWSTART;
// 等待转换完成
while (!(ADC1->SR & ADC_SR_EOC));
// 读取ADC转换结果
uint16_t adc_value = ADC1->DR;
```
代码逻辑逐行解读:
- 使能ADC模块的时钟。
- 配置ADC模块的工作模式、采样时间、转换速率等参数。
- 启动ADC转换。
- 等待转换完成。
- 读取ADC转换结果。
# 6. STM32单片机项目实战
### 6.1 LED灯控制系统
#### 6.1.1 需求分析和系统设计
**需求分析:**
* 控制多个LED灯的亮灭状态
* 通过按钮或其他输入设备进行控制
* 具有可扩展性,便于增加或减少LED灯的数量
**系统设计:**
* 使用STM32单片机作为控制核心
* 使用GPIO端口控制LED灯的亮灭
* 使用按钮或其他输入设备作为控制信号
* 采用模块化设计,便于扩展
#### 6.1.2 软件实现和调试
**软件实现:**
1. 初始化GPIO端口,配置为输出模式
2. 初始化输入设备,配置为中断模式
3. 在中断服务函数中处理输入信号,控制LED灯的亮灭
4. 编写主循环程序,不断扫描输入设备的状态
**调试:**
1. 检查GPIO端口的配置是否正确
2. 检查输入设备的中断配置是否正确
3. 检查中断服务函数的逻辑是否正确
4. 检查主循环程序的逻辑是否正确
### 6.2 温度检测与显示系统
#### 6.2.1 需求分析和系统设计
**需求分析:**
* 检测环境温度
* 将温度值显示在LCD屏幕上
* 具有温度报警功能
**系统设计:**
* 使用STM32单片机作为控制核心
* 使用温度传感器检测温度
* 使用LCD屏幕显示温度值
* 使用蜂鸣器作为报警输出
#### 6.2.2 软件实现和调试
**软件实现:**
1. 初始化温度传感器,配置为中断模式
2. 初始化LCD屏幕,配置为显示模式
3. 初始化蜂鸣器,配置为输出模式
4. 在中断服务函数中处理温度传感器的数据,计算温度值
5. 在主循环程序中更新LCD屏幕上的温度值
6. 当温度值超过设定阈值时,触发蜂鸣器报警
**调试:**
1. 检查温度传感器的配置是否正确
2. 检查LCD屏幕的配置是否正确
3. 检查蜂鸣器的配置是否正确
4. 检查中断服务函数的逻辑是否正确
5. 检查主循环程序的逻辑是否正确
0
0
相关推荐
![rar](https://img-home.csdnimg.cn/images/20241231044955.png)
![zip](https://img-home.csdnimg.cn/images/20241231045053.png)
![pdf](https://img-home.csdnimg.cn/images/20241231044930.png)
![-](https://img-home.csdnimg.cn/images/20241226111658.png)
![-](https://img-home.csdnimg.cn/images/20241231044955.png)
![-](https://img-home.csdnimg.cn/images/20241226111658.png)
![-](https://img-home.csdnimg.cn/images/20241226111658.png)
![-](https://img-home.csdnimg.cn/images/20241226111658.png)
![-](https://img-home.csdnimg.cn/images/20241226111658.png)