STM32单片机开发环境搭建指南:快速上手,开启编程之旅
发布时间: 2024-07-04 04:23:49 阅读量: 60 订阅数: 39
![STM32单片机开发环境搭建指南:快速上手,开启编程之旅](https://img-blog.csdnimg.cn/fded9ce9e2494e97893960644ecca53b.png)
# 1. STM32单片机开发环境简介
STM32单片机开发环境主要由工具链和开发板两部分组成。工具链是用于开发和调试STM32单片机程序的软件工具集,常用的工具链有Keil MDK和IAR Embedded Workbench。开发板是用于连接STM32单片机并提供其运行所需的硬件平台,选择开发板时需要考虑单片机的型号、外设功能和供电方式等因素。
搭建STM32单片机开发环境需要先安装工具链,然后连接和配置开发板。工具链的安装过程通常比较简单,只需按照官方文档的步骤进行即可。开发板的连接和配置则需要根据具体开发板的型号和使用情况进行设置,包括供电、下载接口和调试接口的配置等。
# 2. 开发环境搭建实战
### 2.1 工具链的选择和安装
#### 2.1.1 Keil MDK
**简介:**
Keil MDK(Microcontroller Development Kit)是ARM官方提供的集成开发环境(IDE),专为ARM Cortex-M内核微控制器开发而设计。它包含了编译器、汇编器、调试器、仿真器等一系列开发工具。
**安装步骤:**
1. 下载 Keil MDK 安装程序:https://www.keil.com/download/product/
2. 运行安装程序并按照提示进行安装。
3. 安装完成后,启动 Keil MDK。
**代码示例:**
```
// Keil MDK 中创建新项目
File -> New -> Project
```
**逻辑分析:**
此代码在 Keil MDK 中创建一个新的项目,用于开发 STM32 单片机程序。
**参数说明:**
* `File`:文件菜单。
* `New`:新建菜单。
* `Project`:项目选项。
#### 2.1.2 IAR Embedded Workbench
**简介:**
IAR Embedded Workbench 是 IAR Systems 公司提供的商业集成开发环境,同样专用于 ARM Cortex-M 内核微控制器开发。它提供了强大的编辑器、调试器、仿真器和代码分析工具。
**安装步骤:**
1. 下载 IAR Embedded Workbench 安装程序:https://www.iar.com/iar-embedded-workbench/
2. 运行安装程序并按照提示进行安装。
3. 安装完成后,启动 IAR Embedded Workbench。
**代码示例:**
```
// IAR Embedded Workbench 中创建新项目
Project -> Create New Project
```
**逻辑分析:**
此代码在 IAR Embedded Workbench 中创建一个新的项目,用于开发 STM32 单片机程序。
**参数说明:**
* `Project`:项目菜单。
* `Create New Project`:新建项目选项。
### 2.2 开发板的连接和配置
#### 2.2.1 开发板的选型和购买
**选型要点:**
* 根据项目需求选择合适的 STM32 单片机型号。
* 考虑开发板的扩展性、外设资源和调试接口。
* 了解开发板的供电方式和通信接口。
**购买渠道:**
* 官方授权经销商。
* 电子元器件电商平台。
* 线下电子市场。
#### 2.2.2 开发板的连接和供电
**连接步骤:**
1. 将开发板连接到计算机的 USB 接口。
2. 根据开发板的供电方式,连接电源。
**供电方式:**
* USB 供电:通过 USB 接口为开发板供电。
* 外部电源:通过外部电源适配器为开发板供电。
**表格:STM32 开发板常见供电方式**
| 供电方式 | 优点 | 缺点 |
|---|---|---|
| USB 供电 | 方便,无需额外电源 | 电流限制,不适用于高功耗应用 |
| 外部电源 | 稳定可靠,电流充足 | 需要额外的电源适配器 |
**代码示例:**
```
// 通过 USB 供电
SystemInit();
```
**逻辑分析:**
此代码初始化 STM32 单片机系统,并通过 USB 接口为开发板供电。
**参数说明:**
* `SystemInit()`:初始化 STM32 单片机系统。
# 3. STM32单片机基础知识
### 3.1 STM32单片机架构
#### 3.1.1 内核和外设
STM32单片机采用ARM Cortex-M系列内核,具有高性能、低功耗的特性。内核负责执行程序指令,管理系统资源。
单片机还集成了丰富的片上外设,包括GPIO、定时器、ADC、串口等。这些外设为用户提供了丰富的功能,可以满足各种应用需求。
#### 3.1.2 存储器和总线
STM32单片机通常配备了多种类型的存储器,包括Flash、SRAM和EEPROM。Flash存储程序代码和常量数据,SRAM存储运行时数据,EEPROM存储可擦写的数据。
单片机内部采用总线结构,包括数据总线、地址总线和控制总线。总线连接着内核、外设和存储器,实现数据的传输和控制。
### 3.2 STM32单片机编程语言
#### 3.2.1 C语言基础
C语言是一种广泛应用于嵌入式开发的高级语言。它提供了丰富的语法结构和数据类型,可以高效地实现复杂的算法。
STM32单片机支持C语言编程,提供了标准库和外设驱动库,简化了开发过程。
#### 3.2.2 汇编语言基础
汇编语言是一种低级语言,直接操作单片机的寄存器和指令。它可以实现对硬件的精细控制,但代码可读性较差。
STM32单片机也支持汇编语言编程,但通常仅用于特殊场景,例如对性能要求极高的场合。
### 3.2.3 代码示例
```c
// C语言代码示例
int main() {
// 初始化GPIO
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
GPIOA->MODER |= GPIO_MODER_MODE5_0;
// 设置GPIO5为输出模式
GPIOA->ODR |= GPIO_ODR_OD5;
while (1) {
// 点亮LED
GPIOA->ODR |= GPIO_ODR_OD5;
// 延时1秒
for (int i = 0; i < 1000000; i++);
// 熄灭LED
GPIOA->ODR &= ~GPIO_ODR_OD5;
// 延时1秒
for (int i = 0; i < 1000000; i++);
}
}
```
```assembly
// 汇编语言代码示例
.equ LED_PIN, 5
.equ GPIOA_BASE, 0x40020000
.equ RCC_AHB1ENR, 0x38
.equ GPIOA_MODER, 0x00
.equ GPIOA_ODR, 0x14
.text
.global main
main:
// 初始化RCC
ldr r0, =RCC_AHB1ENR
ldr r1, =GPIOA_BASE
str r1, [r0]
// 初始化GPIO
ldr r0, =GPIOA_MODER
ldr r1, =LED_PIN
lsl r1, r1, #1
ldr r2, =0b01
lsl r2, r2, r1
str r2, [r0]
// 设置GPIO5为输出模式
ldr r0, =GPIOA_ODR
ldr r1, =LED_PIN
lsl r1, r1, #1
str r1, [r0]
// 循环点亮和熄灭LED
loop:
// 点亮LED
ldr r0, =GPIOA_ODR
ldr r1, =LED_PIN
lsl r1, r1, #1
str r1, [r0]
// 延时1秒
mov r0, #1000000
delay_loop:
subs r0, r0, #1
bne delay_loop
// 熄灭LED
ldr r0, =GPIOA_ODR
ldr r1, =LED_PIN
lsl r1, r1, #1
str r1, [r0]
// 延时1秒
mov r0, #1000000
delay_loop:
subs r0, r0, #1
bne delay_loop
b loop
```
# 4. STM32单片机外设编程
### 4.1 GPIO编程
#### 4.1.1 GPIO的配置和操作
GPIO(通用输入/输出)是STM32单片机上最重要的外设之一,它可以用来控制外部设备,如LED、按钮、传感器等。GPIO的配置和操作涉及以下几个步骤:
1. **初始化GPIO时钟:**在使用GPIO之前,需要先初始化其时钟。这可以通过调用`RCC_AHB1PeriphClockCmd()`函数来实现,该函数的参数为要初始化的GPIO端口。例如,要初始化GPIOA,可以调用以下代码:
```c
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
```
2. **配置GPIO模式:**GPIO可以配置为输入、输出或模拟模式。通过调用`GPIO_Init()`函数来配置GPIO模式,该函数的参数包括GPIO端口、引脚号、模式和输出类型。例如,要将GPIOA的第0引脚配置为输出模式,可以调用以下代码:
```c
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
```
3. **读写GPIO:**配置好GPIO模式后,就可以通过调用`GPIO_ReadInputDataBit()`和`GPIO_WriteBit()`函数来读写GPIO引脚。例如,要读取GPIOA的第0引脚,可以调用以下代码:
```c
uint8_t input = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
```
要写入GPIOA的第0引脚,可以调用以下代码:
```c
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
```
#### 4.1.2 GPIO的中断处理
GPIO中断是一种当GPIO引脚状态发生变化时触发的中断。GPIO中断的配置和处理涉及以下几个步骤:
1. **初始化GPIO中断:**在使用GPIO中断之前,需要先初始化GPIO中断。这可以通过调用`EXTI_Init()`函数来实现,该函数的参数包括GPIO端口、引脚号和中断模式。例如,要初始化GPIOA的第0引脚的中断,可以调用以下代码:
```c
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
```
2. **配置NVIC:**初始化好GPIO中断后,还需要配置NVIC(嵌套向量中断控制器)来使能中断。这可以通过调用`NVIC_EnableIRQ()`函数来实现,该函数的参数为中断号。例如,要使能GPIOA的第0引脚的中断,可以调用以下代码:
```c
NVIC_EnableIRQ(EXTI0_IRQn);
```
3. **编写中断服务函数:**编写中断服务函数来处理GPIO中断。中断服务函数的名称由中断号决定,例如,GPIOA的第0引脚的中断服务函数为`EXTI0_IRQHandler()`。在中断服务函数中,可以读取GPIO引脚的状态并执行相应的操作。例如,以下代码读取GPIOA的第0引脚的状态并点亮一个LED:
```c
void EXTI0_IRQHandler(void)
{
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_SET)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_SET);
}
else
{
GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_RESET);
}
}
```
### 4.2 定时器编程
#### 4.2.1 定时器的配置和操作
定时器是STM32单片机上另一个重要的外设,它可以用来产生定时中断、生成PWM波形等。STM32单片机有多个定时器,每种定时器都有不同的功能和特性。
以下以TIM2为例,介绍定时器的配置和操作:
1. **初始化定时器时钟:**在使用定时器之前,需要先初始化其时钟。这可以通过调用`RCC_APB1PeriphClockCmd()`函数来实现,该函数的参数为要初始化的定时器。例如,要初始化TIM2,可以调用以下代码:
```c
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
```
2. **配置定时器模式:**TIM2可以配置为多种模式,包括向上计数、向下计数、中心对齐模式等。通过调用`TIM_SetCounterMode()`函数来配置定时器模式,该函数的参数包括定时器、模式和分频系数。例如,要将TIM2配置为向上计数模式,可以调用以下代码:
```c
TIM_SetCounterMode(TIM2, TIM_CounterMode_Up);
```
3. **设置定时器时钟频率:**通过调用`TIM_SetClockDivision()`函数来设置定时器时钟频率,该函数的参数包括定时器和分频系数。例如,要将TIM2的时钟频率设置为1MHz,可以调用以下代码:
```c
TIM_SetClockDivision(TIM2, TIM_CKD_DIV1);
```
4. **设置定时器溢出值:**通过调用`TIM_SetAutoreload()`函数来设置定时器溢出值,该函数的参数包括定时器和溢出值。例如,要将TIM2的溢出值设置为1000,可以调用以下代码:
```c
TIM_SetAutoreload(TIM2, 1000);
```
5. **启动定时器:**配置好定时器后,通过调用`TIM_Cmd()`函数来启动定时器,该函数的参数为定时器和启动/停止标志。例如,要启动TIM2,可以调用以下代码:
```c
TIM_Cmd(TIM2, ENABLE);
```
#### 4.2.2 定时器的中断处理
定时器中断是一种当定时器溢出或发生捕获/比较事件时触发的中断。定时器中断的配置和处理涉及以下几个步骤:
1. **初始化定时器中断:**在使用定时器中断之前,需要先初始化定时器中断。这可以通过调用`TIM_ITConfig()`函数来实现,该函数的参数包括定时器、中断类型和使能/禁止标志。例如,要初始化TIM2的溢出中断,可以调用以下代码:
```c
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
```
2. **配置NVIC:**初始化好定时器中断后,还需要配置NVIC来使能中断。这可以通过调用`NVIC_EnableIRQ()`函数来实现,该函数的参数为中断号。例如,要使能TIM2的溢出中断,可以调用以下代码:
```c
NVIC_EnableIRQ(TIM2_IRQn);
```
3. **编写中断服务函数:**编写中断服务函数来处理定时器中断。中断服务函数的名称由中断号决定,例如,TIM2的溢出中断服务函数为`TIM2_IRQHandler()`。在中断服务函数中,可以读取定时器的溢出标志并执行相应的操作。例如,以下代码读取TIM2的溢出标志并点亮一个LED:
```c
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_SET);
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
```
# 5. STM32单片机高级应用
### 5.1 串口通信
#### 5.1.1 串口通信原理
串口通信是一种异步串行通信协议,它通过一根或多根导线在两个设备之间传输数据。串口通信的优点是简单、成本低廉,并且可以用于各种应用,例如数据采集、控制和调试。
串口通信的基本原理是将数据分解为一串位,并逐个发送这些位。每个位由一个起始位、一个数据位、一个奇偶校验位(可选)和一个停止位组成。起始位表示数据的开始,停止位表示数据的结束。数据位携带实际数据,奇偶校验位用于检测数据传输中的错误。
#### 5.1.2 STM32单片机串口编程
STM32单片机具有多个串口外设,称为USART(通用同步异步接收发送器)。USART外设支持各种串口通信协议,包括UART、RS-232和RS-485。
要使用STM32单片机进行串口通信,需要完成以下步骤:
1. **配置USART外设:**设置波特率、数据位、停止位和奇偶校验位等参数。
2. **初始化中断:**配置USART中断,以便在接收到数据或发生错误时触发中断。
3. **发送数据:**使用`USART_SendData()`函数发送数据。
4. **接收数据:**使用`USART_ReceiveData()`函数接收数据。
**代码示例:**
```c
#include "stm32f10x.h"
void USART1_Init(void)
{
// 配置USART1外设
USART_InitTypeDef USART1_InitStructure;
USART1_InitStructure.USART_BaudRate = 9600;
USART1_InitStructure.USART_WordLength = USART_WordLength_8b;
USART1_InitStructure.USART_StopBits = USART_StopBits_1;
USART1_InitStructure.USART_Parity = USART_Parity_No;
USART1_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &USART1_InitStructure);
// 启用USART1
USART_Cmd(USART1, ENABLE);
}
void USART1_SendData(uint8_t data)
{
// 发送数据
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, data);
}
uint8_t USART1_ReceiveData(void)
{
// 接收数据
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return USART_ReceiveData(USART1);
}
```
### 5.2 ADC编程
#### 5.2.1 ADC的基本原理
模数转换器(ADC)是一种将模拟信号(例如电压)转换为数字信号(例如二进制数)的电子设备。ADC广泛用于各种应用,例如数据采集、控制和测量。
ADC的基本原理是将模拟信号采样并将其转换为数字值。采样过程涉及将模拟信号转换为一系列离散值。然后,将这些离散值转换为数字值,通常使用二进制数表示。
#### 5.2.2 STM32单片机ADC编程
STM32单片机具有多个ADC外设,称为ADCx(其中x为1、2或3)。ADCx外设支持各种ADC配置,包括单次转换、连续转换和扫描转换。
要使用STM32单片机进行ADC转换,需要完成以下步骤:
1. **配置ADC外设:**设置ADC时钟、分辨率、采样时间和转换模式等参数。
2. **初始化ADC通道:**配置要转换的模拟通道。
3. **启动ADC转换:**使用`ADC_StartConversion()`函数启动ADC转换。
4. **读取ADC转换结果:**使用`ADC_GetConversionValue()`函数读取ADC转换结果。
**代码示例:**
```c
#include "stm32f10x.h"
void ADC1_Init(void)
{
// 配置ADC1外设
ADC_InitTypeDef ADC1_InitStructure;
ADC1_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC1_InitStructure.ADC_ScanConvMode = DISABLE;
ADC1_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC1_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC1_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC1_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC1_InitStructure);
// 启用ADC1
ADC_Cmd(ADC1, ENABLE);
// 校准ADC1
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
}
uint16_t ADC1_ReadChannel(uint8_t channel)
{
// 配置ADC通道
ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_239Cycles5);
// 启动ADC转换
ADC_StartConversion(ADC1);
// 等待ADC转换完成
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
// 读取ADC转换结果
return ADC_GetConversionValue(ADC1);
}
```
# 6. STM32单片机项目实战
### 6.1 LED闪烁程序
#### 6.1.1 程序设计
LED闪烁程序是一个简单的程序,用于控制LED的闪烁。以下是用C语言编写的LED闪烁程序:
```c
#include "stm32f10x.h"
int main(void)
{
// 初始化GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIOA5为输出模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while (1)
{
// 点亮LED
GPIO_SetBits(GPIOA, GPIO_Pin_5);
// 延时1秒
for (int i = 0; i < 1000000; i++);
// 熄灭LED
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
// 延时1秒
for (int i = 0; i < 1000000; i++);
}
}
```
#### 6.1.2 程序编译和烧录
将程序代码复制到Keil MDK或IAR Embedded Workbench中,然后编译程序。编译成功后,将程序烧录到STM32单片机中。
### 6.2 温度采集程序
#### 6.2.1 程序设计
温度采集程序用于采集温度传感器的数据并将其显示在串口终端上。以下是用C语言编写的温度采集程序:
```c
#include "stm32f10x.h"
int main(void)
{
// 初始化GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIOA0为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_In_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 初始化ADC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// 配置ADC通道1
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
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));
while (1)
{
// 启动ADC转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// 等待转换完成
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
// 读取转换结果
uint16_t adc_value = ADC_GetConversionValue(ADC1);
// 计算温度
float temperature = (float)adc_value * 3.3 / 4096 * 100;
// 发送温度数据到串口
printf("Temperature: %.2f degC\n", temperature);
}
}
```
#### 6.2.2 程序编译和烧录
将程序代码复制到Keil MDK或IAR Embedded Workbench中,然后编译程序。编译成功后,将程序烧录到STM32单片机中。
0
0