各位看官老爷们,点击关注不迷路哟。你的点赞、收藏,一键三连,是我持续更新的动力哟!!!
目录
普中STM32F103ZET6开发攻略
1. GPIO端口实验——点亮LED灯
1.1 实验目的
1.2 实验原理
1.3 实验环境和器材:
1.4 实验思路
1.5 实验代码
1.5.1 LED相关代码
1.5.2 delay相关代码
1.6 实验思考和拓展
1.6.1 如何调整LED灯的闪烁频率?
1.6.2 如何实现呼吸灯效果?
1.6.2.1 非 PWM 实现呼吸灯的原理
1.6.2.2 具体实现方法(以 STM32 为例)
1. 亮度等级划分
2. 控制亮灭时间比例
1.6.2.3 代码实例:
1.6.3 除了使用简单延时外,STM32还有哪些更加精准的延时方式?
1.7 注意事项
普中STM32F103ZET6开发攻略
1. GPIO端口实验——点亮LED灯
1.1 实验目的
-
了解STM32F10x微控制器的GPIO口结构和基本操作
-
掌握STM32标准库函数对GPIO的配置和使用方法
-
学会使用GPIO控制LED的亮灭,实现LED基本显示效果
-
掌握简单延时函数的编写方法
1.2 实验原理
-
GPIO原理
STM32的GPIO(通用输入/输出端口)用于外设信号的输入输出控制。STM32F10x系列GPIO具有以下特性:
(1) 每个I/O端口有16个可独立配置的I/O位
(2) 支持8种不同的工作模式
模式分类 具体模式 核心特点 典型应用 输入模式 浮空输入 无内部上下拉,电平由外部决定 外部信号采集(需外部上下拉) 上拉输入 内部上拉,默认高电平 按键输入(低电平有效) 下拉输入 内部下拉,默认低电平 按键输入(高电平有效) 模拟输入 连接 ADC,禁用数字输入功能 ADC 模数转换 输出模式 开漏输出 需外部上拉,支持线与逻辑 I2C 总线、电平转换 推挽输出 直接输出高低电平,驱动能力强 LED 控制、普通数字信号 开漏复用功能 外设驱动,需外部上拉 SPI/I2C 外设输出 推挽复用功能 外设驱动,直接输出高低电平 USART/CAN 外设输出 通过配置 GPIO 的模式寄存器(CRL/CRH)和输出类型寄存器,可灵活选择上述工作模式,满足不同外设的控制需求。
(3) 每个I/O口可以产生外部中断
(4) 位设置/复位寄存器,支持原子位操作
-
LED控制原理
LED (发光二极管) 是一种单向导电器件,只有在正向偏置时才会发光。根据开发板硬件设计,LED灯通常采用如下接法:
(1) 共阳极接法:LED阳极接VCC,阴极接单片机GPIO,GPIO输出低电平时LED点亮;
(2) 共阴极接法:LED阴极接地,阳极接单片机GPIO,GPIO输出高电平时LED点亮;
本实验中,LED灯采用的是共阳极接法。
1.3 实验环境和器材:
-
电脑:Keil5+Vstudio
-
硬件资源:普中玄武F103开发板(主控芯片:STM32F103ZET6)
1.4 实验思路
硬件接线图如下所示:
由上图可知:
-
DS0即LED0、DS1即LED1,分别连接着:PB5和PE5
LED0连接到GPIOB的Pin5引脚,LED1连接到GPIOE的Pin5引脚
-
为了完成实验目的:
我们需要再项目工程中新建两个“库函数”:LED.c+delay.c
1.5 实验代码
1.5.1 LED相关代码
头文件:
//led.h #ifndef _led_H #define _led_H #include "stm32f10x.h" #include "stm32f10x_gpio.h" // 包含GPIO相关的函数和宏定义 #include "stm32f10x_rcc.h" // 包含RCC(时钟控制)相关的函数和宏定义 void LED_init(void); void LED0_On(void); void LED0_Off(void); void LED1_On(void); void LED1_Off(void); void LED0_Toggle(void); void LED1_Toggle(void); #endif
源文件:
//led.c #include "led.h" void LED_init(void) {// 使能 GPIOB 和 GPIOE 的时钟(几乎所有外设(包括 GPIO)在使用前都需要先使能对应的时钟)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; // 配置 PB5 为推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置 PE5 为推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_Init(GPIOE, &GPIO_InitStructure); // 初始状态GPIO_SetBits(GPIOB, GPIO_Pin_5);GPIO_SetBits(GPIOE, GPIO_Pin_5); } void LED0_On(void) {GPIO_ResetBits(GPIOB, GPIO_Pin_5); } void LED0_Off(void){GPIO_SetBits(GPIOB, GPIO_Pin_5); } void LED1_On(void) {GPIO_ResetBits(GPIOE, GPIO_Pin_5); } void LED1_Off(void) {GPIO_SetBits(GPIOE, GPIO_Pin_5); } void LED0_Toggle(void) {GPIOB->ODR ^= GPIO_Pin_5; } void LED1_Toggle(void) {GPIOE->ODR ^= GPIO_Pin_5; }
因为我们这里是直接输出高低电平:所以采用的是推挽输出的方式,由上文可知:推完输出直接输出高低电平,驱动能力强。
初始状态需要设置成为高电平,由上文中的硬件接线图可知,LED(发光二极管)是在正向偏置时才会发光,所以应该是“低电平有效”
1.5.2 delay相关代码
头文件:
#ifndef __DELAY_H #define __DELAY_H #include "stm32f10x.h" void Delay_Init(void); void Delay_ms(u32 nms); #endif
源文件:
#include "delay.h" static u32 fac_ms = 0; void Delay_Init(void) {SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); // HCLK/8fac_ms = SystemCoreClock / 8000; } void Delay_ms(u32 nms) {u32 temp;SysTick->LOAD = nms * fac_ms;SysTick->VAL = 0x00;SysTick->CTRL = 0x01;do{temp = SysTick->CTRL;} while ((temp & 0x01) && !(temp & (1 << 16)));SysTick->CTRL = 0x00;SysTick->VAL = 0X00; }
1.6 实验思考和拓展
1.6.1 如何调整LED灯的闪烁频率?
LED灯闪烁的频率,归根结底还是由灯光的亮和灭的时间间隔决定的,亮和灭的时间间隔越小,那么频率就越高。
实现方法:
-
使用定时器实现精确延时
若使用自定义简单延时函数(如
void Delay_ms(uint32_t ms)
),直接调整传入的延时参数。 例:原闪烁周期为 0.6 秒(亮 0.3 秒 + 灭 0.3 秒),若改为亮 0.1 秒 + 灭 0.1 秒,则周期变为 0.2 秒,频率提高 3 倍。// 原代码(周期0.6秒) LED0_ON(); // 亮 Delay_ms(300); LED0_OFF(); // 灭 Delay_ms(300); // 调整后(周期0.2秒) LED0_ON(); Delay_ms(100); Delay_ms(100);
-
使用定时器实现精确延时:
简单延时函数依赖系统时钟和循环次数,可通过计算循环次数与时钟周期的关系提高精度。 例:若系统时钟为 72MHz,一个循环约耗时 1 微秒,则延时 1 毫秒需循环 1000 次。
-
使用定时器实现精确延时:
利用 STM32 的定时器(如 TIM2、TIM3)产生精确中断,通过中断计数实现延时,避免 CPU 空转,提升系统效率。 步骤: ① 初始化定时器,配置为定时中断模式(如定时 1 毫秒); ② 在中断服务函数中维护一个全局计数器; ③ 主函数中通过判断计数器值实现延时。
1.6.2 如何实现呼吸灯效果?
方法:软件模拟渐变效果
1.6.2.1 非 PWM 实现呼吸灯的原理
呼吸灯的本质是让人眼感知到 LED 亮度的线性渐变(从暗到亮或从亮到暗)。人眼对亮度的感知具有惰性,当 LED 在极短时间内(如几毫秒)频繁亮灭时,会将亮灭时间的平均值视为 “亮度”。因此,即使不使用硬件 PWM,也可以通过软件控制 LED 的亮灭占空比(即导通时间与周期的比例),逐步改变占空比来模拟亮度变化。
1.6.2.2 具体实现方法(以 STM32 为例)
LED 为共阳极接法(低电平点亮),通过以下步骤实现亮度渐变:
1. 亮度等级划分
将亮度从 0%(全灭)到 100%(最亮)划分为若干等级(如 256 级),用一个变量(如brightness
)表示当前等级(范围:0~255)。
-
brightness=0
:LED 全灭(高电平)。 -
brightness=255
:LED 全亮(低电平)。
2. 控制亮灭时间比例
每个亮度等级对应一个周期总时间(如 10ms),其中:
-
点亮时间 =
(brightness / 255) × 周期总时间
-
熄灭时间 = 周期总时间 - 点亮时间
通过循环改变brightness
的值(如从 0 逐渐增加到 255,再逐渐减小到 0),并在每个等级中控制 LED 的亮灭时间,即可实现渐变效果。
1.6.2.3 代码实例:
// 定义LED引脚(共阳极,低电平点亮) #define LED_GPIO_PORT GPIOB #define LED_PIN GPIO_Pin_5 // 亮度等级(0~255) uint8_t brightness = 0; uint8_t direction = 1; // 1表示亮度增加,-1表示亮度减少 void breathe_led(void) {// 亮度渐变方向控制(到达边界时反转方向)if (brightness == 255) direction = -1;if (brightness == 0) direction = 1;brightness += direction; // 计算亮灭时间(周期总时间设为10ms,可调整)uint16_t on_time = (brightness * 10) / 255; // 点亮时间(ms)uint16_t off_time = 10 - on_time; // 熄灭时间(ms) // 控制LED点亮GPIO_ResetBits(LED_GPIO_PORT, LED_PIN); // 低电平点亮delay_ms(on_time); // 点亮持续时间 // 控制LED熄灭GPIO_SetBits(LED_GPIO_PORT, LED_PIN); // 高电平熄灭delay_ms(off_time); // 熄灭持续时间 } // 主循环中调用 while (1) {breathe_led(); }
与 PWM 方案的对比
特性 | 非 PWM 软件模拟 | 硬件 PWM |
---|---|---|
实现复杂度 | 需编写循环逻辑和延时函数,代码较繁琐 | 直接配置定时器 PWM 模式,代码简洁 |
CPU 占用率 | 高(需实时控制亮灭时间,阻塞延时) | 低(定时器硬件自动更新占空比) |
亮度平滑度 | 受延时精度限制,可能有闪烁感 | 平滑(硬件驱动,频率稳定) |
适用场景 | 简单实验、对实时性要求低的场合 | 高要求场景(如电机调速、精密调光) |
1.6.3 除了使用简单延时外,STM32还有哪些更加精准的延时方式?
-
定时器延时(精确且不阻塞 CPU)
原理:利用定时器的计数功能,通过配置自动重装载值(TIMx_ARR)和预分频器(TIMx_PSC)设定定时时间,结合中断或查询方式实现延时。
优点:精度高(可达微秒级),延时期间 CPU 可执行其他任务。
-
SysTick 定时器延时(系统级延时)
原理:利用 STM32 内部的 SysTick 定时器(系统滴答定时器),基于系统时钟(如 72MHz)产生精确中断。
优点:无需额外外设,由 Cortex-M 内核直接支持,适合系统级延时。
-
实时操作系统(RTOS)任务调度
原理:在 RTOS(如 FreeRTOS、uCOS)中,通过任务调度函数(如
vTaskDelay()
)实现延时,本质是释放 CPU 资源给其他任务。优点:多任务并行处理,延时期间可执行其他任务,适合复杂系统。
-
总结与拓展
-
简单场景:优先使用 SysTick 或定时器查询方式,兼顾精度与代码复杂度。
-
复杂场景:引入 RTOS 或定时器中断,实现多任务协同和精确时序控制。
-
进阶实践:结合 DMA(直接内存访问)与定时器,实现无 CPU 参与的自动 PWM 输出,进一步提升效率。
-
1.7 注意事项
(1) 使用标准库函数时,需要注意头文件的包含和依赖关系。
(2) 共阳极LED的控制逻辑:低电平点亮,高电平熄灭。
(3) 简单延时函数的精确度受系统时钟和编译优化的影响。
(4) GPIO操作前必须先使能对应的外设时钟。
文章有写的不当的地方,欢迎在评论区中指正修改。如果感觉文章实用对你有帮助,欢迎点赞收藏和关注,你的点赞关注就是我动力,大家一起学习进步。
有不懂的可以在评论区里提出来哟,博主看见后会及时回答的。