STM32单片机编程软件实战教程:从零到精通的快速通道
发布时间: 2024-07-01 19:48:53 阅读量: 4 订阅数: 10 ![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![STM32单片机编程软件实战教程:从零到精通的快速通道](https://img-blog.csdnimg.cn/5903670652a243edb66b0e8e6199b383.jpg)
# 1. STM32单片机简介和开发环境搭建**
STM32单片机是意法半导体(STMicroelectronics)公司推出的基于ARM Cortex-M内核的高性能微控制器系列。其特点包括:
- 高性能:基于ARM Cortex-M内核,主频可达168MHz
- 低功耗:采用先进的低功耗技术,具有多种低功耗模式
- 丰富的外设:集成丰富的片上外设,如GPIO、定时器、ADC等
- 开发环境完善:提供完善的开发工具链和支持库,方便开发
要开始使用STM32单片机,需要搭建开发环境,包括:
- **集成开发环境(IDE):**推荐使用Keil MDK或IAR Embedded Workbench
- **编译器:**使用ARM Compiler或GCC编译器
- **仿真器或调试器:**用于下载程序和调试
- **开发板:**提供必要的硬件资源,如电源、时钟和外设
# 2. STM32单片机编程基础
### 2.1 STM32单片机架构和外设介绍
STM32单片机采用ARM Cortex-M内核,具有高性能、低功耗的特点。其内部架构主要包括:
- **CPU内核:**负责执行程序指令,处理数据。
- **存储器:**包括程序存储器(Flash)和数据存储器(RAM)。
- **外设:**提供各种功能,如GPIO、定时器、ADC、USB等。
STM32单片机的外设非常丰富,可以满足各种应用需求。常见的外设包括:
- **GPIO(通用输入/输出):**用于控制外部设备,如LED、按键等。
- **定时器:**用于产生定时中断,控制PWM输出等。
- **ADC(模数转换器):**用于将模拟信号转换为数字信号。
- **USB:**用于与外部设备进行高速数据传输。
### 2.2 C语言在STM32单片机中的应用
C语言是一种广泛应用于嵌入式开发的编程语言。STM32单片机支持C语言编程,提供了丰富的库函数和开发工具。
使用C语言编程STM32单片机需要遵循以下步骤:
1. **编写程序:**使用C语言编写程序代码,定义变量、函数和数据结构。
2. **编译:**使用编译器将C语言代码编译成汇编代码。
3. **汇编:**使用汇编器将汇编代码汇编成机器指令。
4. **链接:**使用链接器将汇编代码与库函数链接成可执行文件。
### 2.3 STM32单片机编程流程
STM32单片机编程流程主要包括以下步骤:
1. **初始化系统:**初始化时钟、外设和中断。
2. **配置外设:**根据应用需求配置GPIO、定时器、ADC等外设。
3. **编写应用代码:**编写应用程序代码,实现具体功能。
4. **调试程序:**使用调试器调试程序,查找错误。
5. **生成固件:**将调试好的程序编译、链接生成固件文件。
6. **烧写固件:**将固件文件烧写到STM32单片机中。
**代码块:**
```c
// 初始化系统
void SystemInit(void)
{
// 配置时钟
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
RCC_PLLConfig(RCC_PLLSource_HSE, 8, 168, 2, 2);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div2);
RCC_PCLK1Config(RCC_HCLK_Div4);
// 配置中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
NVIC_Init(&NVIC_InitStructure);
}
```
**逻辑分析:**
该代码块实现了STM32单片机的系统初始化。首先配置时钟,然后配置中断。
**参数说明:**
- `RCC_DeInit():`复位时钟配置。
- `RCC_HSEConfig(RCC_HSE_ON):`开启高速外部时钟(HSE)。
- `while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);:`等待HSE稳定。
- `RCC_PLLConfig(RCC_PLLSource_HSE, 8, 168, 2, 2):`配置PLL时钟,使用HSE作为时钟源,分频系数为8,倍频系数为168,预分频系数为2,输出分频系数为2。
- `RCC_HCLKConfig(RCC_SYSCLK_Div1):`将系统时钟设置为PLL时钟,不分频。
- `RCC_PCLK2Config(RCC_HCLK_Div2):`将APB2时钟设置为HCLK时钟,分频系数为2。
- `RCC_PCLK1Config(RCC_HCLK_Div4):`将APB1时钟设置为HCLK时钟,分频系数为4。
- `NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2):`设置中断优先级分组为2。
- `NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0):`将中断向量表设置在Flash中,从地址0x0开始。
- `NVIC_Init(&NVIC_InitStructure):`初始化NVIC,根据NVIC_InitStructure结构体配置中断。
# 3. STM32 单片机外设编程
### 3.1 GPIO 编程
#### 3.1.1 GPIO 的配置和使用
**GPIO 配置**
STM32 单片机上的 GPIO 引脚可以配置为输入、输出或模拟功能。GPIO 配置寄存器(GPIOx_MODER)用于设置引脚模式。
```c
// 设置 GPIOA 引脚 0 为输出模式
GPIOA->MODER &= ~(3 << (0 * 2));
GPIOA->MODER |= (1 << (0 * 2));
```
**逻辑分析:**
* `GPIOA->MODER &= ~(3 << (0 * 2))`:清除引脚 0 的模式位(第 0 和第 1 位)。
* `GPIOA->MODER |= (1 << (0 * 2))`:将引脚 0 的模式位设置为输出模式(第 0 位为 1)。
**GPIO 输出**
配置为输出模式的引脚可以通过 GPIOx_ODR 寄存器进行输出。
```c
// 将 GPIOA 引脚 0 输出高电平
GPIOA->ODR |= (1 << 0);
```
**逻辑分析:**
* `GPIOA->ODR |= (1 << 0)`:将引脚 0 的输出数据寄存器位(第 0 位)设置为 1,输出高电平。
#### 3.1.2 GPIO 中断处理
**GPIO 中断配置**
GPIO 中断可以通过 GPIOx_IER 寄存器进行配置。
```c
// 使能 GPIOA 引脚 0 的上升沿中断
GPIOA->IER |= (1 << 0);
```
**逻辑分析:**
* `GPIOA->IER |= (1 << 0)`:将引脚 0 的中断使能寄存器位(第 0 位)设置为 1,使能上升沿中断。
**GPIO 中断服务函数**
当引脚 0 发生上升沿中断时,会触发中断服务函数。
```c
void EXTI0_IRQHandler(void)
{
// 清除中断标志位
EXTI->PR |= (1 << 0);
// 中断处理代码
}
```
**逻辑分析:**
* `EXTI->PR |= (1 << 0)`:清除引脚 0 的中断标志位(第 0 位)。
* 中断处理代码:在中断服务函数中执行中断处理代码,例如读取输入数据或执行其他操作。
### 3.2 定时器编程
#### 3.2.1 定时器的配置和使用
**定时器配置**
STM32 单片机上的定时器可以用于生成脉冲、测量时间或产生中断。定时器配置寄存器(TIMx_CR1)用于设置定时器的模式和时钟源。
```c
// 配置 TIM2 为向上计数模式,时钟源为内部时钟
TIM2->CR1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS);
TIM2->CR1 |= TIM_CR1_CEN;
```
**逻辑分析:**
* `TIM2->CR1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS)`:清除方向位和时钟源位。
* `TIM2->CR1 |= TIM_CR1_CEN`:使能定时器。
**定时器计数**
定时器的计数值存储在 TIMx_CNT 寄存器中。
```c
// 获取 TIM2 的当前计数值
uint32_t count = TIM2->CNT;
```
**逻辑分析:**
* `TIM2->CNT`:获取 TIM2 的当前计数值。
#### 3.2.2 定时器中断处理
**定时器中断配置**
定时器中断可以通过 TIMx_DIER 寄存器进行配置。
```c
// 使能 TIM2 的更新中断
TIM2->DIER |= TIM_DIER_UIE;
```
**逻辑分析:**
* `TIM2->DIER |= TIM_DIER_UIE`:将 TIM2 的更新中断使能位(第 0 位)设置为 1,使能更新中断。
**定时器中断服务函数**
当定时器发生更新中断时,会触发中断服务函数。
```c
void TIM2_IRQHandler(void)
{
// 清除中断标志位
TIM2->SR &= ~TIM_SR_UIF;
// 中断处理代码
}
```
**逻辑分析:**
* `TIM2->SR &= ~TIM_SR_UIF`:清除 TIM2 的更新中断标志位(第 0 位)。
* 中断处理代码:在中断服务函数中执行中断处理代码,例如执行特定操作或更新数据。
### 3.3 ADC 编程
#### 3.3.1 ADC 的配置和使用
**ADC 配置**
ADC 配置寄存器(ADCx_CR1)用于设置 ADC 的采样时间、分辨率和触发源。
```c
// 配置 ADC1 为 12 位分辨率,采样时间为 239.5 个时钟周期
ADC1->CR1 &= ~(ADC_CR1_RES | ADC_CR1_SMP);
ADC1->CR1 |= ADC_CR1_RES_1 | ADC_CR1_SMP_2;
```
**逻辑分析:**
* `ADC1->CR1 &= ~(ADC_CR1_RES | ADC_CR1_SMP)`:清除分辨率位和采样时间位。
* `ADC1->CR1 |= ADC_CR1_RES_1 | ADC_CR1_SMP_2`:将分辨率设置为 12 位(第 1 位为 1),将采样时间设置为 239.5 个时钟周期(第 2 位为 1)。
**ADC 转换**
ADC 转换通过 ADCx_CR2 寄存器中的 ADON 位启动。
```c
// 启动 ADC1 转换
ADC1->CR2 |= ADC_CR2_ADON;
```
**逻辑分析:**
* `ADC1->CR2 |= ADC_CR2_ADON`:将 ADON 位设置为 1,启动 ADC1 转换。
#### 3.3.2 ADC 中断处理
**ADC 中断配置**
ADC 中断可以通过 ADCx_IER 寄存器进行配置。
```c
// 使能 ADC1 的转换完成中断
ADC1->IER |= ADC_IER_EOCIE;
```
**逻辑分析:**
* `ADC1->IER |= ADC_IER_EOCIE`:将 ADC1 的转换完成中断使能位(第 0 位)设置为 1,使能转换完成中断。
**ADC 中断服务函数**
当 ADC 转换完成时,会触发中断服务函数。
```c
void ADC1_IRQHandler(void)
{
// 清除中断标志位
ADC1->SR &= ~ADC_SR_EOC;
// 中断处理代码
}
```
**逻辑分析:**
* `ADC1->SR &= ~ADC_SR_EOC`:清除 ADC1 的转换完成标志位(第 0 位)。
* 中断处理代码:在中断服务函数中执行中断处理代码,例如读取转换结果或执行其他操作。
# 4.1 DMA编程
### 4.1.1 DMA的配置和使用
**DMA(直接存储器访问)**是一种硬件机制,允许外设直接与内存进行数据传输,而无需CPU的干预。这可以大大提高数据传输的效率,特别是对于大数据量传输的情况。
**STM32单片机上DMA的配置和使用步骤如下:**
1. **使能DMA时钟:**在RCC寄存器中使能DMA时钟。
2. **配置DMA通道:**选择要使用的DMA通道,并配置其源地址、目标地址、数据传输长度、传输方向等参数。
3. **配置外设:**配置外设的DMA请求源,并使能DMA请求。
4. **启动DMA传输:**启动DMA传输,DMA控制器将根据配置的参数自动进行数据传输。
**DMA配置示例代码:**
```c
#include "stm32f10x.h"
void DMA_Config(void)
{
// 使能DMA1时钟
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// 选择DMA通道1
DMA1_Channel1->CCR &= ~DMA_CCR1_EN;
// 配置源地址
DMA1_Channel1->CPAR = (uint32_t)&source_buffer;
// 配置目标地址
DMA1_Channel1->CMAR = (uint32_t)&destination_buffer;
// 配置数据传输长度
DMA1_Channel1->CNDTR = data_length;
// 配置传输方向
DMA1_Channel1->CCR |= DMA_CCR1_DIR;
// 配置外设请求源
DMA1_Channel1->CCR |= DMA_CCR1_PL;
// 使能DMA请求
DMA1_Channel1->CCR |= DMA_CCR1_EN;
}
```
### 4.1.2 DMA中断处理
DMA传输完成后,DMA控制器会触发一个中断。可以通过配置DMA中断服务函数来处理该中断。
**DMA中断处理示例代码:**
```c
void DMA1_Channel1_IRQHandler(void)
{
// 检查中断标志位
if (DMA1->ISR & DMA_ISR_TCIF1)
{
// 清除中断标志位
DMA1->IFCR |= DMA_IFCR_CTCIF1;
// DMA传输完成,执行后续操作
}
}
```
# 5.1 LED闪烁程序
LED闪烁程序是STM32单片机入门最经典的实战项目之一。通过该程序,我们可以学习如何配置GPIO引脚、控制LED灯的亮灭。
### 程序实现步骤
1. **配置GPIO引脚**
首先,我们需要配置一个GPIO引脚作为LED灯的控制引脚。STM32单片机有多个GPIO端口,每个端口有16个GPIO引脚。我们可以根据需要选择一个GPIO引脚,例如GPIOA的第5个引脚(PA5)。
```c
// 定义LED灯控制引脚
#define LED_PIN GPIO_PIN_5
// 配置GPIO引脚为输出模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
```
2. **控制LED灯亮灭**
配置好GPIO引脚后,就可以通过设置引脚电平来控制LED灯的亮灭。当引脚电平为高电平时,LED灯亮;当引脚电平为低电平时,LED灯灭。
```c
// 点亮LED灯
HAL_GPIO_WritePin(GPIOA, LED_PIN, GPIO_PIN_SET);
// 熄灭LED灯
HAL_GPIO_WritePin(GPIOA, LED_PIN, GPIO_PIN_RESET);
```
3. **循环闪烁**
为了让LED灯循环闪烁,我们需要在循环中不断点亮和熄灭LED灯。我们可以使用延时函数来控制闪烁的频率。
```c
while (1)
{
// 点亮LED灯
HAL_GPIO_WritePin(GPIOA, LED_PIN, GPIO_PIN_SET);
HAL_Delay(500); // 延时500ms
// 熄灭LED灯
HAL_GPIO_WritePin(GPIOA, LED_PIN, GPIO_PIN_RESET);
HAL_Delay(500); // 延时500ms
}
```
0
0
相关推荐
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)