stm32单片机架构揭秘:深入剖析内部结构,掌握核心原理
发布时间: 2024-07-03 18:48:02 阅读量: 144 订阅数: 45
STM32F103单片机核心板STM32最小系统开发板 protel99SE 设计硬件原理图PCB文件.zip
![stm32单片机架构揭秘:深入剖析内部结构,掌握核心原理](https://img-blog.csdnimg.cn/3ce6c8891127453d93c9442c628b4e10.png)
# 1. STM32单片机简介**
STM32单片机是意法半导体(STMicroelectronics)生产的一系列32位微控制器,基于ARM Cortex-M内核,广泛应用于嵌入式系统中。其特点包括:
- 高性能:基于ARM Cortex-M内核,提供卓越的处理能力和能效。
- 丰富的外设:集成多种外围设备,如GPIO、定时器、ADC和UART,满足各种应用需求。
- 低功耗:采用先进的低功耗技术,可实现超低功耗操作,延长电池续航时间。
- 广泛的应用:适用于各种嵌入式应用,包括工业控制、医疗设备、物联网和消费电子产品。
# 2. STM32单片机架构
### 2.1 核心架构
#### 2.1.1 Cortex-M内核
STM32单片机采用基于ARM架构的Cortex-M内核,该内核专为嵌入式应用而设计,具有低功耗、高性能和易于使用的特点。Cortex-M内核有多种型号,STM32单片机主要采用Cortex-M0、Cortex-M3和Cortex-M4内核。
**Cortex-M0内核:**
- 32位RISC架构
- 16位指令集
- 8KB Flash和4KB SRAM
- 低功耗设计,适用于电池供电设备
**Cortex-M3内核:**
- 32位RISC架构
- 32位指令集
- 32KB Flash和16KB SRAM
- 增强浮点运算能力
- 适用于需要较高性能的应用
**Cortex-M4内核:**
- 32位RISC架构
- 32位指令集
- 64KB Flash和32KB SRAM
- 浮点运算单元(FPU)
- 适用于需要高性能和复杂计算的应用
#### 2.1.2 内存结构
STM32单片机采用哈佛架构,即指令和数据存储在不同的存储器中。
**Flash存储器:**
- 存储程序代码和常量数据
- 非易失性存储器,即使断电后数据也不会丢失
- 容量从几KB到几MB不等
**SRAM存储器:**
- 存储变量和临时数据
- 易失性存储器,断电后数据丢失
- 容量从几KB到几十KB不等
**EEPROM存储器:**
- 一种非易失性存储器,可以多次擦除和写入
- 用于存储配置数据和参数
### 2.2 外围设备
STM32单片机集成了丰富的片上外围设备,包括时钟系统、GPIO接口、定时器和计数器等。
#### 2.2.1 时钟系统
STM32单片机通常有多个时钟源,包括内部时钟(HSI)、外部时钟(HSE)和低速时钟(LSI)。这些时钟源可以相互切换,以满足不同应用的时钟要求。
**时钟树:**
时钟系统通过时钟树将时钟信号分配到不同的外围设备。时钟树可以配置为不同的分频比,以生成所需的时钟频率。
**时钟配置:**
时钟配置寄存器用于配置时钟源、分频比和时钟输出。
#### 2.2.2 GPIO接口
GPIO(通用输入/输出)接口是STM32单片机上最基本的外部接口,可以配置为输入或输出模式。
**GPIO模式:**
- 输入模式:读取外部信号
- 输出模式:驱动外部设备
**GPIO中断:**
GPIO接口支持中断功能,当引脚状态发生变化时可以触发中断。
#### 2.2.3 定时器和计数器
STM32单片机集成了多个定时器和计数器外围设备,用于生成定时脉冲、测量时间间隔和产生PWM信号。
**定时器模式:**
- 定时器模式:生成定时脉冲
- 计数器模式:测量时间间隔
**PWM模式:**
PWM(脉宽调制)模式可以生成可调占空比的脉冲信号,用于控制电机速度、亮度等。
**捕获和比较功能:**
定时器和计数器还支持捕获和比较功能,可以测量外部信号的频率和占空比。
# 3. STM32单片机编程
### 3.1 嵌入式C语言基础
#### 3.1.1 数据类型和变量
嵌入式C语言的数据类型与标准C语言类似,主要包括:
- **整型:**int、short、long,用于表示整数
- **浮点型:**float、double,用于表示小数
- **字符型:**char,用于表示单个字符
- **指针:**用于存储其他变量的地址
- **结构体:**用于存储相关数据的集合
- **联合体:**用于存储不同类型数据的集合
变量用于存储数据,其类型决定了变量可以存储的数据类型和范围。变量声明时需要指定其类型和名称,如:
```c
int count;
float temperature;
```
#### 3.1.2 运算符和表达式
运算符用于对变量或常量进行操作,主要包括:
- **算术运算符:**+、-、*、/、%(模运算)
- **关系运算符:**==、!=、>、<、>=、<=
- **逻辑运算符:**&&(与)、||(或)、!(非)
表达式由运算符和操作数组成,用于计算结果。如:
```c
int result = count + temperature;
```
### 3.2 STM32单片机开发环境
#### 3.2.1 IDE选择和安装
STM32单片机开发需要使用集成开发环境(IDE),推荐使用以下IDE:
- **Keil uVision:**功能强大,支持多种STM32型号
- **IAR Embedded Workbench:**功能完善,代码质量高
- **Eclipse with CDT:**开源免费,可扩展性强
安装IDE时,需要选择与STM32型号对应的版本和工具链。
#### 3.2.2 项目创建和配置
创建STM32项目时,需要选择目标单片机型号和开发环境。IDE会自动生成项目模板,包含基本的头文件和源文件。
项目配置包括:
- **编译器选项:**优化级别、代码生成选项
- **调试选项:**调试器类型、断点设置
- **链接器选项:**库文件、启动文件
```mermaid
graph LR
subgraph STM32单片机开发环境
A[IDE选择和安装] --> B[项目创建和配置]
end
```
**代码块:**
```c
// 头文件包含
#include "stm32f10x.h"
// 变量声明
uint32_t count;
float temperature;
// 主函数
int main(void)
{
// 初始化
SystemInit();
// 循环
while (1)
{
// 获取温度
temperature = ...;
// 更新计数
count++;
}
}
```
**代码逻辑分析:**
- 头文件包含了STM32单片机的寄存器和函数定义。
- 变量声明了计数器`count`和温度值`temperature`。
- 主函数`main()`在系统初始化后进入循环。
- 循环中,获取温度并更新计数器。
# 4. STM32单片机外设应用
### 4.1 GPIO控制
#### 4.1.1 输入/输出模式配置
GPIO(General Purpose Input/Output)是STM32单片机上的一种通用输入/输出接口,它可以配置为输入或输出模式。
**输入模式配置**
```c
// 将GPIOA的第0位配置为输入模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
```
**参数说明:**
* `GPIO_InitStruct.Pin`:要配置的引脚,此处为GPIOA的第0位。
* `GPIO_InitStruct.Mode`:配置模式,此处为输入模式。
* `GPIO_InitStruct.Pull`:上拉/下拉电阻配置,此处为上拉电阻。
**输出模式配置**
```c
// 将GPIOA的第1位配置为输出模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
```
**参数说明:**
* `GPIO_InitStruct.Pin`:要配置的引脚,此处为GPIOA的第1位。
* `GPIO_InitStruct.Mode`:配置模式,此处为推挽输出模式。
* `GPIO_InitStruct.Speed`:输出速度,此处为低速。
#### 4.1.2 中断处理
GPIO可以配置为在输入状态发生变化时触发中断。
**中断配置**
```c
// 配置GPIOA的第0位为外部中断
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.Line = EXTI_LINE_0;
EXTI_InitStruct.Mode = EXTI_MODE_INTERRUPT;
EXTI_InitStruct.Trigger = EXTI_TRIGGER_RISING;
HAL_EXTI_Init(&EXTI_InitStruct);
// 配置NVIC中断控制器
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.PreemptPriority = 0;
NVIC_InitStruct.SubPriority = 0;
NVIC_InitStruct.Enable = ENABLE;
HAL_NVIC_Init(&NVIC_InitStruct);
```
**参数说明:**
* `EXTI_InitStruct.Line`:外部中断线,此处为GPIOA的第0位。
* `EXTI_InitStruct.Mode`:中断模式,此处为中断模式。
* `EXTI_InitStruct.Trigger`:触发方式,此处为上升沿触发。
* `NVIC_InitStruct.IRQChannel`:中断通道,此处为EXTI0中断通道。
* `NVIC_InitStruct.PreemptPriority`:抢占优先级,此处为0。
* `NVIC_InitStruct.SubPriority`:子优先级,此处为0。
**中断处理函数**
```c
void EXTI0_IRQHandler(void)
{
// 清除中断标志位
HAL_EXTI_IRQHandler(&EXTI_InitStruct);
// 执行中断处理逻辑
}
```
### 4.2 定时器和计数器应用
#### 4.2.1 定时器模式和配置
STM32单片机上有多个定时器和计数器,它们可以用于生成定时中断、测量脉冲宽度等。
**定时器模式**
STM32单片机上的定时器和计数器支持多种模式,包括:
* **向上计数模式:**从0开始计数,直到达到最大值。
* **向下计数模式:**从最大值开始计数,直到达到0。
* **中心对齐模式:**从最大值的一半开始计数,达到最大值或0时翻转。
**定时器配置**
```c
// 配置TIM2为向上计数模式,时钟频率为1000Hz
TIM_HandleTypeDef htim2;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.Period = 1000 - 1;
TIM_TimeBaseInitStruct.Prescaler = 8400 - 1;
TIM_TimeBaseInitStruct.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TIM_TimeBaseInitStruct.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_TimeBaseInit(&htim2, &TIM_TimeBaseInitStruct);
```
**参数说明:**
* `TIM_TimeBaseInitStruct.Period`:定时器周期,此处为1000ms。
* `TIM_TimeBaseInitStruct.Prescaler`:预分频器,此处为8400,使定时器时钟频率为1000Hz。
* `TIM_TimeBaseInitStruct.ClockDivision`:时钟分频,此处为不分频。
* `TIM_TimeBaseInitStruct.CounterMode`:计数模式,此处为向上计数模式。
#### 4.2.2 脉宽调制(PWM)生成
PWM(Pulse Width Modulation)是一种通过改变脉冲宽度来控制输出功率的技术。
**PWM配置**
```c
// 配置TIM3为PWM模式,输出频率为100Hz,占空比为50%
TIM_OC_InitTypeDef TIM_OCInitStruct;
TIM_OCInitStruct.OCMode = TIM_OCMODE_PWM1;
TIM_OCInitStruct.Pulse = 500 - 1;
TIM_OCInitStruct.OCPolarity = TIM_OCPOLARITY_HIGH;
TIM_OCInitStruct.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &TIM_OCInitStruct, TIM_CHANNEL_1);
```
**参数说明:**
* `TIM_OCInitStruct.OCMode`:输出比较模式,此处为PWM模式1。
* `TIM_OCInitStruct.Pulse`:脉冲宽度,此处为500,占空比为50%。
* `TIM_OCInitStruct.OCPolarity`:输出极性,此处为高电平有效。
* `TIM_OCInitStruct.OCFastMode`:快速模式,此处为禁用。
### 4.3 串口通信
#### 4.3.1 串口配置和初始化
串口(UART)是一种异步串行通信接口,用于与其他设备进行数据传输。
**串口配置**
```c
// 配置UART1,波特率为115200bps,数据位为8位,停止位为1位,校验位为无
UART_HandleTypeDef huart1;
UART_InitTypeDef UART_InitStruct;
UART_InitStruct.BaudRate = 115200;
UART_InitStruct.WordLength = UART_WORDLENGTH_8B;
UART_InitStruct.StopBits = UART_STOPBITS_1;
UART_InitStruct.Parity = UART_PARITY_NONE;
UART_InitStruct.HwFlowCtl = UART_HWCONTROL_NONE;
UART_InitStruct.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&huart1, &UART_InitStruct);
```
**参数说明:**
* `UART_InitStruct.BaudRate`:波特率,此处为115200bps。
* `UART_InitStruct.WordLength`:数据位,此处为8位。
* `UART_InitStruct.StopBits`:停止位,此处为1位。
* `UART_InitStruct.Parity`:校验位,此处为无。
* `UART_InitStruct.HwFlowCtl`:硬件流控制,此处为无。
* `UART_InitStruct.Mode`:工作模式,此处为收发模式。
#### 4.3.2 数据收发处理
**数据发送**
```c
// 发送数据"Hello World"到UART1
uint8_t data[] = "Hello World";
HAL_UART_Transmit(&huart1, data, sizeof(data), 1000);
```
**数据接收**
```c
// 接收数据到接收缓冲区,最大长度为100字节
uint8_t rx_buffer[100];
HAL_UART_Receive(&huart1, rx_buffer, 100, 1000);
```
# 5.1 实时操作系统(RTOS)
### 5.1.1 RTOS简介和选择
**什么是RTOS**
实时操作系统(RTOS)是一种专门设计用于嵌入式系统中的操作系统,它提供了可预测且可靠的实时性能。RTOS通过管理系统资源(如处理器时间、内存和外设)来确保任务以确定性的方式执行。
**RTOS的优点**
使用RTOS具有以下优点:
- **可预测性:**RTOS确保任务以可预测的方式执行,即使在系统负载高的情况下。
- **可靠性:**RTOS提供故障处理机制,以确保系统在出现故障时仍能继续运行。
- **并发性:**RTOS允许多个任务同时执行,从而提高了系统的效率。
- **模块化:**RTOS通常由模块化组件组成,这使得开发和维护应用程序变得更加容易。
**RTOS的选择**
选择合适的RTOS对于嵌入式系统至关重要。以下是一些需要考虑的因素:
- **系统要求:**考虑系统的实时性、并发性、内存和处理能力要求。
- **支持的外设:**确保RTOS支持系统中使用的外设。
- **开发工具:**选择提供全面开发工具(如IDE、调试器和文档)的RTOS。
- **社区支持:**考虑RTOS的社区支持水平,这对于获取帮助和解决问题至关重要。
### 5.1.2 任务调度和同步
**任务调度**
RTOS中的任务是执行特定功能的独立线程。RTOS负责调度任务,即决定何时执行每个任务。有几种不同的调度算法,包括:
- **先到先服务(FIFO):**任务按照它们到达就绪队列的顺序执行。
- **优先级调度:**任务根据其优先级执行,优先级高的任务优先执行。
- **时间片轮转:**每个任务分配一个时间片,任务在时间片内执行,然后将其切换到就绪队列的末尾。
**任务同步**
在并发系统中,任务可能需要协调它们的活动以避免冲突。RTOS提供同步机制,如:
- **互斥锁:**允许任务独占访问临界区(共享资源)。
- **信号量:**用于协调任务之间的事件。
- **消息队列:**用于在任务之间传递消息。
**代码示例**
以下代码示例演示了如何在FreeRTOS中创建和调度任务:
```c
#include "FreeRTOS.h"
#include "task.h"
void task1(void *pvParameters) {
while (1) {
// 执行任务1的代码
}
}
void task2(void *pvParameters) {
while (1) {
// 执行任务2的代码
}
}
int main(void) {
// 创建任务1
xTaskCreate(task1, "Task 1", 1024, NULL, 1, NULL);
// 创建任务2
xTaskCreate(task2, "Task 2", 1024, NULL, 1, NULL);
// 启动任务调度器
vTaskStartScheduler();
return 0;
}
```
**逻辑分析**
此代码创建了两个任务,task1和task2。任务1和任务2以无限循环执行,执行它们各自的代码。FreeRTOS调度器负责调度任务,确保它们以可预测的方式执行。
# 6. STM32单片机开发实战
本章将通过三个实战项目,带领大家深入了解STM32单片机的开发流程和应用场景。
### 6.1 LED闪烁程序
**目标:**控制LED灯闪烁,掌握基本的GPIO控制和定时器配置。
**步骤:**
1. **硬件准备:**连接LED灯至STM32单片机的GPIO引脚。
2. **代码编写:**
```c
#include "stm32f10x.h"
void main() {
// GPIO配置
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
GPIOC->CRH &= ~GPIO_CRH_MODE13;
GPIOC->CRH |= GPIO_CRH_MODE13_0;
// 定时器配置
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->PSC = 7200 - 1; // 分频系数为7200
TIM2->ARR = 1000 - 1; // 自动重装载值为1000
TIM2->CR1 |= TIM_CR1_CEN; // 启用定时器
while (1) {
// LED控制
if (TIM2->SR & TIM_SR_UIF) { // 定时器中断标志位
TIM2->SR &= ~TIM_SR_UIF; // 清除中断标志位
GPIOC->ODR ^= GPIO_ODR_ODR13; // 翻转LED状态
}
}
}
```
### 6.2 温度传感器读取程序
**目标:**使用温度传感器读取温度值,掌握ADC配置和数据采集。
**步骤:**
1. **硬件准备:**连接温度传感器至STM32单片机的ADC引脚。
2. **代码编写:**
```c
#include "stm32f10x.h"
void main() {
// ADC配置
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
ADC1->CR2 |= ADC_CR2_ADON; // 启用ADC
ADC1->SQR3 |= ADC_SQR3_SQ1_4; // 选择通道4(温度传感器)
while (1) {
// 温度采集
ADC1->CR2 |= ADC_CR2_SWSTART; // 启动转换
while (!(ADC1->SR & ADC_SR_EOC)); // 等待转换完成
uint16_t adc_value = ADC1->DR;
// 温度计算
float temperature = (adc_value * 3.3 / 4096) * 100; // 转换ADC值到温度值
}
}
```
### 6.3 无线通信模块控制程序
**目标:**使用无线通信模块发送和接收数据,掌握串口配置和数据传输。
**步骤:**
1. **硬件准备:**连接无线通信模块至STM32单片机的串口引脚。
2. **代码编写:**
```c
#include "stm32f10x.h"
void main() {
// 串口配置
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
USART1->BRR = 9600; // 波特率为9600
USART1->CR1 |= USART_CR1_UE; // 启用串口
while (1) {
// 数据发送
USART1->DR = 'A'; // 发送字符'A'
while (!(USART1->SR & USART_SR_TC)); // 等待发送完成
// 数据接收
while (!(USART1->SR & USART_SR_RXNE)); // 等待接收完成
uint8_t received_data = USART1->DR;
}
}
```
0
0