[TOC]

stm32学习笔记001

新建keil5工程

  1. 装好keil5
  2. project->new..project,选择目标文件夹
  3. 选好后把stm32库文件中有关启动和各种库粘贴到starup文件夹下;
  4. 在魔术棒->c/c++里面,添加define 为USE_STDPERIPH_DRIVER,,并把library文件夹添加到includepath里

寄存器点灯(任务1.1)

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "stm32f10x.h"  
int main()
{
RCC->APB2ENR = 0x00000008;
GPIOB->CRL = 0x00300033;
GPIOB->ODR = 0x00000000;

//若要熄灭,将odr设置为高电平:
//GPIOB->ODR = 0x00000001;

while(1);
}//这里下面要有最后一行空行,否则报错

代码解析:

  • RCC->APB2ENR = 0x00000008;

​ RCC代表寄存器,APB2ENR代表APB2外设时钟使能寄存器

image-20241012131906762

​ 要想使用各个外设(定义见文末附录),就要先把这个外设使能

​ 有官方手册可看出,GPIOB口在APB2中使能,且在“3”编号上,把3这个编号赋值1,其他为0,四个一组转换成16进制,得出0x00000008;

  • GPIOB->CRL = 0x00300033;

image-20241012132818313

GPIOx_CRL: 1.x:GPIO 后面的字母 2.CRL的L:LOW,代表低寄存器(H代表高)

  • GPIOB->ODR = 0x00000000;

image-20241012133231074

ODR的O:output

  • 运行结果:

image-20241012150653517

库函数点灯(任务1.2):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
	//pf11
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
//这句代码相当于寄存器点灯操作时的gpio使能,这里选用gpiof

GPIO_InitTypeDef GPIO_InitStructurePF11;
//创建一个GPIO_InitTypeDef类型的结构体,结构体用于初始化gpio的各个参数
//分别是 mode ,pin ,speed,这三个参数在库文件中均以枚举定义


GPIO_InitStructurePF11.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructurePF11.GPIO_Pin = GPIO_Pin_11; //11号引脚
GPIO_InitStructurePF11.GPIO_Speed = GPIO_Speed_50MHz; //速率

GPIO_Init(GPIOF,&GPIO_InitStructurePF11);
//初始化gpio引脚,第一个参数是GPIOx(实际上是指针),第二个是结构体初始化数组的地址


GPIO_ResetBits(GPIOF,GPIO_Pin_11);
//设置输出为低电平

涉及的可选参数详见附录

  • 效果:

image-20241012150710945

GPIO输入输出(任务2)

读取输入的方法

读取输入寄存器(IDR)
1
2
3
4
5
6
7
8
9
10
11
12
//读取GPIO输出的方法----读取它的输入寄存器
//即使goio是输出模式,IDR也能反映它的状态
int pinState;

pinState = (GPIOF->IDR & GPIO_PIN_x) ? 1 : 0; // 读取 GPIOF 11 引脚状态

//方法2 ---- 库函数GPIO_ReadInputDataBit(基于输入寄存器)
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==Bit_RESET)
{
//Bit_SET:高电平
//Bit_RESET:低电平
}
读取输出寄存器(ODR)
1
2
3
4
5
6
7
//直接读取输出寄存器(ODR)
int pinState;

pinState = (GPIOF->ODR & GPIO_PIN_x) ? 1 : 0; // 读取 GPIOF 11 引脚状态


//
二者的区别

IDR反应实际物理状态,ODR反应代码设置的状态(不一定是实际)

假设有一个 GPIO 引脚配置为输出模式,在代码中设置了它为高电平,但这个引脚连接了一个外部电路,外部电路强行把这个引脚拉低了的话,二者的区别就会显现

  • IDR:反映这个引脚的实际电平为低电平,因为它受到了外部电路的影响。
  • ODR:仍然会反映你在代码中设置的高电平,因为它表示的是你通过 MCU 设置的输出值,而不是实际的电平。

延时函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void SysTick_Init(void) {
// SystemCoreClock 是系统时钟频率,比如 72 MHz(72000000 Hz)
// 配置 SysTick 每 1 毫秒触发一次中断
SysTick_Config(SystemCoreClock / 1000);
}


volatile uint32_t sysTickCounter = 0;//定义一个计数器
//使用 volatile 关键字是因为该变量会在中断中修改,防止编译器优化

void SysTick_Handler(void) {
if (sysTickCounter > 0) {
sysTickCounter--;
}
}
//注意,SysTick_Handler函数在库文件中已经有定义了
//但是内容是空的
//因此,在此库文件中声明extern的计数器变量,然后在库文件中修改此函数


void Delay_ms(uint32_t ms) {
sysTickCounter = ms;
while (sysTickCounter != 0) {
// 等待延时结束
}
}

具体过程如下:

  1. sysTickCounter 被设置为延时的毫秒数。

  2. 每 1 毫秒,SysTick 触发中断,调用 SysTick_Handler() 函数,递减 sysTickCounter

    因此,起到延时作用的是SysTick_Handler() 函数

  3. 当 sysTickCounter 减到 0 时,while 循环结束,延时函数完成。

SysTick_Handler() 只有在倒计时中每隔 1 毫秒被调用一次,并且只有在 sysTickCounter > 0 时它才会递减计数器。一旦计数器减到 0,SysTick_Handler() 仍然会被 1 毫秒触发一次,只不过它不再执行任何递减操作

总代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include "stm32f10x.h"      // Device header

void SysTick_Init(void) {

SysTick_Config(SystemCoreClock / 1000);
} //初始化时钟延迟为1ms


volatile uint32_t sysTickCounter = 0;//初始化计数器



void Delay_ms(uint32_t ms) {
sysTickCounter = ms;
while (sysTickCounter != 0) {

}
}//定义delay函数


int main(void)
{
// RCC->APB2ENR = 0x00000008;
// GPIOB->CRL = 0x00300033;
// GPIOB->ODR = 0x00000000; //寄存器点灯的代码

//__enable_irq(); //启用全局中断(但是不启用也能跑)

SysTick_Init();//初始化计时器

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//使能GPIOB

// GPIO_InitTypeDef GPIO_InitStructurePB5;
// GPIO_InitStructurePB5.GPIO_Mode=GPIO_Mode_Out_PP;
// GPIO_InitStructurePB5.GPIO_Pin=GPIO_Pin_5;
// GPIO_InitStructurePB5.GPIO_Speed=GPIO_Speed_50MHz;
//
// GPIO_Init(GPIOB,&GPIO_InitStructurePB5);
// ///点亮GPIOB5的代码



//初始化gpio0
GPIO_InitTypeDef GPIO_InitStructurePB0;
GPIO_InitStructurePB0.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructurePB0.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructurePB0.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructurePB0);


GPIO_ResetBits(GPIOB,GPIO_Pin_0);
//³õʼ»¯GPIOB0£¬ÇÒ½«ÆäÉèΪµÍµçƽ£¨ÁÁµÆ£©


GPIO_InitTypeDef GPIO_InitStructurePB1;
GPIO_InitStructurePB1.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructurePB1.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructurePB1.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructurePB1);
//³õʼ»¯GPIOB1



//pf11(让它常亮吧)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);

GPIO_InitTypeDef GPIO_InitStructurePF11;
GPIO_InitStructurePF11.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructurePF11.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructurePF11.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOF,&GPIO_InitStructurePF11);
GPIO_ResetBits(GPIOF,GPIO_Pin_11);
GPIO_SetBits(GPIOB,GPIO_Pin_1);




while(1)
{
//判断GPIO0的状态
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==Bit_RESET)
{
Delay_ms(1000);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_SetBits(GPIOB,GPIO_Pin_0);
Delay_ms(1000);


}
else
{
GPIO_SetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_0);

}
}
}

外部中断

一个按键的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include "stm32f10x.h"      // Device header
#include <stdio.h>
volatile uint32_t sysTickCounter = 0;
void EXTI_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);

//灯的初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOF,&GPIO_InitStructure);


//按键初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA,&GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);//将GPIOA0连接到EXTI外设
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;

EXTI_Init(&EXTI_InitStructure);//初始化EXIT

//开启NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);


}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0)==SET)
{
if(GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_11)==Bit_SET)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_11);
}
else
{
GPIO_SetBits(GPIOF,GPIO_Pin_11);
}


EXTI_ClearITPendingBit(EXTI_Line0);
}

}


int main(void)
{
EXTI_Config();
while(1)
{

}

}

两个按键的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include "stm32f10x.h"      // Device header
#include <stdio.h>
volatile uint32_t sysTickCounter = 0;
void EXTI_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

//灯的初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOF,&GPIO_InitStructure);


//按键初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA,&GPIO_InitStructure);

//第二个按键初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOF,&GPIO_InitStructure);


//将GPIOA0连接到exti外设
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;

EXTI_Init(&EXTI_InitStructure);

//将GPIOF1连接到exti外设
GPIO_EXTILineConfig(GPIO_PortSourceGPIOF,GPIO_PinSource1);
//EXTI_InitStructure已经定义,可以直接拿来用
EXTI_InitStructure.EXTI_Line=EXTI_Line1;//换一个中断线路
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;

EXTI_Init(&EXTI_InitStructure);

//配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;

//为按键1配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);

//为按键2配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);

}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0)==SET)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_11);
}

EXTI_ClearITPendingBit(EXTI_Line0); //清空中断标志位

}

void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1)==SET)
{
GPIO_SetBits(GPIOF,GPIO_Pin_11);
}
EXTI_ClearITPendingBit(EXTI_Line1); //清空中断标志位

}


int main(void)
{
EXTI_Config();

while(1)
{

}

}

视频:

一个按键的:

点击查看视频

两个按键的:

点击查看视频

附录(信息整合)

外设相关信息

    • 1. GPIO(通用输入输出接口)

      功能:

      GPIO 是 STM32 控制器中最基本的外设之一,用于控制设备与外部电路之间的数字信号交互。每个 GPIO 引脚都可以独立配置为输入、输出、模拟模式或用于特定外设的功能复用模式(如 USART、I2C 等)。

      常见用途:

      • 数字输入:读取按键、开关或传感器状态。
      • 数字输出:控制 LED、继电器、蜂鸣器等。
      • PWM 输出:控制电机速度、LED 亮度。
      • 外部中断:用于检测引脚电平变化(如按键按下或松开时触发中断)。

      特点:

      • 模式配置

        • 输入模式:可以配置为浮空输入、上拉输入或下拉输入。
        • 输出模式:支持推挽输出和开漏输出,适合驱动不同类型的负载。
        • 模拟模式:用于连接模拟信号输入,如 ADC(模数转换器)的模拟信号通道。
        • 复用功能:引脚可以作为外设的信号引脚使用,如 USART 的 TX/RX。
      • 外部中断:支持引脚的上升沿、下降沿或双边沿触发中断事件,允许系统对外部事件做出快速响应。

      • 快速切换:GPIO 引脚的状态可以通过寄存器快速读取和修改,具备高效的响应能力。

      实际应用:

      配置 GPIO 为输出模式控制一个 LED,当按键按下时通过外部中断机制点亮 LED。

------

   ### 2. **USART / UART(通用异步收发器)**

   #### 功能:

   USART(Universal Synchronous Asynchronous Receiver Transmitter)或 UART 是常用于串行通信的外设,支持异步和同步通信。它通常用于与计算机、外部模块或其他微控制器之间的通信。

   #### 常见用途:

   - **调试接口**:将调试信息发送到串口终端,帮助开发人员实时查看系统运行状态。
   - **模块通信**:与蓝牙模块、GPS 模块、GSM 模块等外设进行数据交互。
   - **设备通信**:与传感器、执行器、其他微控制器通信。

   #### 特点:

   - **波特率**:支持多种波特率设置(如 9600、115200),可以适应不同速度的通信需求。

   - 数据格式

     :灵活设置数据位、停止位、校验位等,适应不同协议需求。

     - 常见配置:8 数据位、1 停止位、无校验位。

   - **硬件流控**:支持 RTS/CTS 硬件流控,减少数据丢失风险,特别适合高速通信。

   - **中断和 DMA 支持**:可以通过中断或 DMA 传输数据,减少 CPU 负担,提高系统效率。

   - **多处理器模式**:允许多处理器间的串行通信,通过地址标识进行选择性通信。

   #### 实际应用:

   使用 STM32 的 `USART2` 端口与计算机串口终端通信,将传感器数据通过 UART 发送到终端,以进行实时监控。

------

   ### 3. **SPI(串行外设接口)**

   #### 功能:

   SPI(Serial Peripheral Interface)是一种高速的同步串行通信接口,通常用于与外部传感器、存储设备(如 EEPROM、Flash)和显示器等外设进行通信。STM32 的 SPI 接口支持全双工通信,可以同时发送和接收数据。

   #### 常见用途:

   - **存储器通信**:与 SPI 闪存或 EEPROM 进行高速读写操作。
   - **显示器控制**:与 OLED、LCD 屏幕进行通信,快速刷新显示内容。
   - **传感器数据读取**:获取 MEMS 传感器、加速度计、陀螺仪等传感器的数据。
   - **音频模块**:通过 SPI 与音频芯片通信,进行音频数据的传输和控制。

   #### 特点:

   - **主从模式**:SPI 支持主从通信模式,STM32 可以配置为主设备或从设备。
   - **高数据速率**:SPI 的通信速率可达几十 MHz,适合高速数据传输应用。
   - **全双工通信**:支持同时发送和接收数据,适合需要高速双向通信的场景。
   - **多从设备支持**:通过片选引脚(CS),可以连接多个从设备,灵活控制不同的外设。

   #### 实际应用:

   使用 STM32 作为主设备,通过 SPI 接口读取外部 EEPROM 的数据,或者驱动 OLED 显示屏显示图像数据。

------

   ### 4. **I2C(集成电路互联)**

   #### 功能:

   I2C 是一种常用的双线串行通信协议,适用于低速外围设备(如传感器、存储器、RTC 模块)的通信。它只需要两根线:SCL(时钟)和 SDA(数据),即可在多主多从架构中进行通信。

   #### 常见用途:

   - **传感器通信**:与温度传感器、压力传感器等低速设备通信。
   - **存储器接口**:如 I2C EEPROM,用于存储配置信息或日志。
   - **RTC(实时时钟)模块**:读取实时时钟的日期和时间信息。
   - **显示模块**:驱动 I2C 协议的 OLED 或 LCD 显示屏。

   #### 特点:

   - **双线通信**:使用 SDA 和 SCL 两条线,减少通信接口数量。
   - **多主多从支持**:允许多个设备同时连接到同一条总线上,支持多主设备通信。
   - **地址识别**:每个从设备都有唯一的 7 位或 10 位地址,主设备通过地址访问特定从设备。
   - **多种通信速率**:支持标准模式(100kHz)、快速模式(400kHz)和高速模式(1MHz)。

   #### 实际应用:

   使用 STM32 的 I2C 接口与温度传感器通信,周期性读取环境温度,并通过串口将数据发送到电脑进行监控。

------

   ### 5. **ADC(模数转换器)**

   #### 功能:

   ADC(Analog to Digital Converter)用于将模拟信号转换为数字信号,是读取传感器等模拟设备数据的重要接口。STM32 的 ADC 通常具有多通道和高分辨率的特点,适用于高精度的模拟信号采集。

   #### 常见用途:

   - **传感器信号采集**:采集温度传感器、电位器等模拟设备的信号。
   - **电压监测**:监测电池电压、电源电压等,防止过充或欠压情况。
   - **音频输入**:在音频处理应用中,采集麦克风或音频信号。
   - **环境监测**:用于光照强度、气体浓度等的模拟数据读取。

   #### 特点:

   - **多通道支持**:STM32 的 ADC 通常支持多通道采样,可以轮询多个模拟信号源。
   - **高分辨率**:支持 12 位甚至 16 位分辨率,确保高精度的数据转换。
   - **连续转换模式**:可以配置为连续采样模式,持续采集模拟信号。
   - **DMA 支持**:与 DMA 协同工作,允许大数据量的自动采集而无需占用 CPU。
   - **采样时间可调**:可以根据不同输入信号源的阻抗调整采样时间,确保准确性。

   #### 实际应用:

   使用 ADC 接口读取电位器的模拟信号,通过 ADC 转换为数字信号后进行处理,调整系统的输出参数,如控制 LED 的亮度或调整电机转速。

------

   ### 6. **TIM(定时器)**

   #### 功能:

   定时器(TIM)是 STM32 中非常强大的外设,支持计时、事件控制、PWM 生成、输入捕获、输出比较等功能。STM32 中有多种类型的定时器,可用于不同的任务。

   #### 常见用途:

   - **时间延迟**:生成精确的时间延迟,用于定时任务或周期性中断。
   - **PWM 生成**:控制电机转速、调光 LED 亮度、音频输出等。
   - **输入捕获**:测量输入信号的脉冲宽度或频率,如测速应用。
   - **事件计数**:用于记录外部事件的发生次数,如脉冲计数。

   #### 特点:

   - **多种模式**:支持单次计时、周期性计时等多种模式。
   - **高精度计时**:可以通过定时器预分频器实现高精度时间控制,支持从微秒级别到数秒级别的计时任务。
   - **PWM 输出**:定时器支持多通道 PWM 输出,可以同时控制多个设备,如控制多路 LED 的亮度或多轴电机的速度。
   - **输入捕获/输出比较**:输入捕获用于精确测量输入信号的时间特性,输出比较用于在特定时间输出控制信号。

   #### 实际应用:

   配置 STM32 的 TIM 定时器生成 PWM 信号控制直流电机的转速,或者通过输入捕获功能测量外部信号的频率。

GPIO_InitTypeDef,EXTI_InitTypeDef,NVIC_InitTypeDef等常用的def的各个参数及使用方法

1. GPIO_InitTypeDef

GPIO_InitTypeDef 是用于配置 GPIO 引脚的结构体。它的成员变量决定了引脚的工作模式、输出类型、速度以及上下拉电阻配置。

结构体定义:

1
2
3
4
5
6
7
typedef struct {
uint32_t GPIO_Pin; // GPIO 引脚号
GPIOMode_TypeDef GPIO_Mode; // GPIO 模式(输入、输出、复用、模拟)
GPIOSpeed_TypeDef GPIO_Speed; // GPIO 速度
GPIOOType_TypeDef GPIO_OType; // GPIO 输出类型(推挽/开漏)
GPIOPuPd_TypeDef GPIO_PuPd; // GPIO 上拉/下拉电阻配置
} GPIO_InitTypeDef;

参数说明:

  • **GPIO_Pin**:指定要配置的 GPIO 引脚。可以是以下常量的组合,用于同时配置多个引脚:
    • GPIO_Pin_0, GPIO_Pin_1, GPIO_Pin_2, …, GPIO_Pin_15
  • **GPIO_Mode**:设置引脚的工作模式。可以选择以下模式:
    • GPIO_Mode_IN:输入模式。
    • GPIO_Mode_OUT:输出模式。
    • GPIO_Mode_AF:复用模式(用于外设,如 USART、SPI 等)。
    • GPIO_Mode_AN:模拟模式(用于 ADC)。
  • **GPIO_Speed**:配置引脚的速度,主要用于输出模式。速度取决于应用需求以及外设要求:
    • GPIO_Speed_2MHz:低速(2MHz)。
    • GPIO_Speed_25MHz:中速(25MHz)。
    • GPIO_Speed_50MHz:高速(50MHz)。
    • GPIO_Speed_100MHz:非常高速(100MHz)。
  • **GPIO_OType**:输出类型,决定引脚是推挽还是开漏输出。主要用于输出模式:
    • GPIO_OType_PP:推挽输出。
    • GPIO_OType_OD:开漏输出。
  • **GPIO_PuPd**:配置上拉或下拉电阻,主要用于输入模式:
    • GPIO_PuPd_NOPULL:无上拉或下拉电阻。
    • GPIO_PuPd_UP:上拉电阻。
    • GPIO_PuPd_DOWN:下拉电阻。

使用示例:

1
2
3
4
5
6
7
8
9
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 使能 GPIOA 时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // 配置 PA5 引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // 输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 50MHz 速度
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // 无上拉/下拉
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化 GPIOA 引脚

2. EXTI_InitTypeDef

EXTI_InitTypeDef 用于配置外部中断(EXTI),STM32 的 EXTI 模块可以通过 GPIO 引脚引发外部中断。

结构体定义:

1
2
3
4
5
6
typedef struct {
uint32_t EXTI_Line; // 外部中断线
EXTIMode_TypeDef EXTI_Mode; // 中断模式或事件模式
EXTITrigger_TypeDef EXTI_Trigger; // 中断触发条件(上升沿、下降沿、双边沿)
FunctionalState EXTI_LineCmd; // 外部中断使能或禁用
} EXTI_InitTypeDef;

参数说明:

  • **EXTI_Line**:指定要配置的 EXTI 线,范围从 EXTI_Line0EXTI_Line15,对应不同的 GPIO 引脚(例如,EXTI_Line0 对应 GPIO 0 号引脚)。
  • **EXTI_Mode**:配置 EXTI 的模式,可以是以下两种:
    • EXTI_Mode_Interrupt:中断模式。
    • EXTI_Mode_Event:事件模式,不产生中断,但可以触发事件。
  • **EXTI_Trigger**:设置中断触发条件,可以选择以下触发条件:
    • EXTI_Trigger_Rising:上升沿触发。
    • EXTI_Trigger_Falling:下降沿触发。
    • EXTI_Trigger_Rising_Falling:上升沿和下降沿都触发。
  • **EXTI_LineCmd**:用于使能或禁用 EXTI 线:
    • ENABLE:使能 EXTI 线。
    • DISABLE:禁用 EXTI 线。

使用示例:

1
2
3
4
5
6
7
8
9
10
EXTI_InitTypeDef EXTI_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // 使能 SYSCFG 时钟
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); // 将 PA0 配置为 EXTI_Line0

EXTI_InitStructure.EXTI_Line = EXTI_Line0; // 配置 EXTI Line0
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能 EXTI Line0
EXTI_Init(&EXTI_InitStructure); // 初始化 EXTI

3. NVIC_InitTypeDef

NVIC_InitTypeDef 用于配置嵌套向量中断控制器 (NVIC),用于管理中断优先级并使能中断请求。

结构体定义:

1
2
3
4
5
6
typedef struct {
uint8_t NVIC_IRQChannel; // 要配置的中断通道
uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级
uint8_t NVIC_IRQChannelSubPriority; // 响应优先级
FunctionalState NVIC_IRQChannelCmd; // 中断使能或禁用
} NVIC_InitTypeDef;

参数说明:

  • **NVIC_IRQChannel**:指定要配置的中断通道。例如:
    • EXTI0_IRQn:EXTI Line0 的中断。
    • TIM2_IRQn:TIM2 定时器的中断。
  • **NVIC_IRQChannelPreemptionPriority**:设置抢占优先级,数值越低优先级越高。在 STM32F4 中通常支持 4 位优先级划分。
  • **NVIC_IRQChannelSubPriority**:设置响应优先级,用于同一抢占优先级下的多个中断之间的调度,数值越低优先级越高。
  • **NVIC_IRQChannelCmd**:使能或禁用中断通道:
    • ENABLE:使能中断通道。
    • DISABLE:禁用中断通道。

使用示例:

1
2
3
4
5
6
7
NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // 配置 EXTI Line0 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; // 抢占优先级 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; // 响应优先级 1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
NVIC_Init(&NVIC_InitStructure); // 初始化 NVIC

综合示例:外部中断配置

假设我们要配置一个按键中断,按下按键时通过 EXTI 触发 PA0 引脚的中断,以下是完整的配置流程:

  1. 配置 GPIO 引脚为输入模式
1
2
3
4
5
6
7
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 配置 PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // 输入模式
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // 无上拉/下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
  1. 配置 EXTI
1
2
3
4
5
6
7
8
9
10
EXTI_InitTypeDef EXTI_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); // 配置 EXTI Line0 对应 PA0

EXTI_InitStructure.EXTI_Line = EXTI_Line0; // 配置 EXTI Line0
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能 EXTI Line0
EXTI_Init(&EXTI_InitStructure);
  1. 配置 NVIC
1
2
3
4
5
6
7
NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // 配置 EXTI0 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; // 抢占优先级 0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; // 响应优先级 0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
NVIC_Init(&NVIC_InitStructure);