news 2026/10/3 7:38:10

课时01 嵌入式技术应用设计实训 | GPIO 输出:跑马灯与蜂鸣器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
课时01 嵌入式技术应用设计实训 | GPIO 输出:跑马灯与蜂鸣器

嵌入式技术应用设计实训 | GPIO 输出:跑马灯与蜂鸣器

本课程开源地址(Gitee):https://gitee.com/fujianxinxi/qianrushishixundianzi.git
课件、示例代码与验收脚本都在该仓库,可直接git clone或下载 ZIP 使用。

开发板资料A盘(百度网盘):https://pan.baidu.com/s/1_EXrjRUewwmGMgcy38zEZg(提取码:zbri)
正点原子官方资料下载:http://www.openedv.com/docs/boards/stm32/zdyz_stm32f103_nano.html

模块一 基础外设开发 · 2 学时 · 环境:Keil MDK5 + VS Code + 正点原子 STM32F103 Nano V1.3

一句话概述:从 GPIO 的“开时钟 → 配推挽 → 写电平”三步入手,把 PC0~PC7(板载 DS0~DS7)配成推挽输出点亮 8 个 LED;同一份main.c里用DEMO_MODE宏切换五种现象——全亮、间隔亮、跑马灯、全亮全灭交替四种灯效,外加一种只响蜂鸣器,蜂鸣器只在第 5 种现象里响,其余现象全程静音;节奏全靠软件延时排出来,不涉及定时器与中断。

本节目标

  1. 能说出点亮一个 LED 要按“开时钟 → 配推挽 → 写电平”三步走,并说清顺序反了为什么既不报错也不亮
  2. 会改main.c里的DEMO_MODE宏(改成 1、2、3、4),把板载 8 个 LED 跑出全亮、间隔亮、跑马灯三种现象
  3. 会用同一套写法驱动 PB8 上的蜂鸣器(把DEMO_MODE改成 5 单独试),并说清“有源蜂鸣器给对电平就响,要调音才用 PWM”

一、原理说明

GPIO 输出要按固定顺序做三步:先给引脚所在的端口开时钟,再把引脚配成推挽输出,最后才是写电平——顺序不能反,时钟没开的时候写寄存器等于没写,可编译器一声不响,这是新手最容易踩的坑。板载 8 个 LED(DS0~DS7)的阳极已经通过限流电阻接在 3.3V 上,阴极分别连到 PC0~PC7,所以 IO 输出低电平灯才亮、输出高电平灯灭;蜂鸣器接在 PB8 上,也是“写个电平就能响”的器件,只不过响不响还取决于板子上的接法极性。“跑马灯”说到底就是“点亮一个 → 等一会儿 → 灭掉它、点亮下一个”的循环,等待用软件延时delay_ms()排出来,本课不碰定时器和中断。把这五种现象写进同一个main.c、用一个宏切换,是为了让你改一个数字就能对比不同写法,而不是每换一种现象就要重敲一遍代码。

二、讲解内容

2.1 知识讲解

先认清三个词:端口、引脚、位。STM32 的 IO 是按端口分组的,GPIOA、GPIOB、GPIOC各是一个端口,每个端口管 16 个引脚,引脚名叫PA0`PA15`、`PB0`PB15、PC0~PC15。在 HAL 库里,“位”就是一个 16 位整数里的一个 bit:GPIO_PIN_0等于0x0001,GPIO_PIN_1等于0x0002,……GPIO_PIN_7等于0x0080。所以想一次操作 8 个灯,把它们的位“或”起来就行——0x00FF就是 PC0~PC7 全部。

本课程用的引脚(正点原子 STM32F103 Nano V1.3 板载,不用额外接线):

板载元件开发板引脚点亮/工作电平说明
红色 LED DS0PC0低电平DS0~DS7 阳极接 3.3V,IO 拉低才亮
红色 LED DS1PC1低电平同上
红色 LED DS2PC2低电平同上
红色 LED DS3PC3低电平同上
红色 LED DS4PC4低电平同上
红色 LED DS5PC5低电平同上
红色 LED DS6PC6低电平同上
红色 LED DS7PC7低电平同上
蜂鸣器 BEEPPB8低电平响(V1.3 实测)有源蜂鸣器,给对电平就响;极性由代码里的BEEP_ACTIVE_HIGH开关决定

三步走,一步都不能少:

步骤干什么代码不做会怎样
① 开时钟给引脚所在端口使能时钟__HAL_RCC_GPIOC_CLK_ENABLE();写寄存器没有任何效果,且不报错(灯就是不亮)
② 配模式引脚方向、输出类型、上下拉、速度GPIO_InitTypeDef+HAL_GPIO_Init()引脚还是复位后的浮空输入,输出不了电平
③ 写电平把引脚拉高或拉低HAL_GPIO_WritePin()配置好了却不动,灯自然也不亮

推挽输出(GPIO_MODE_OUTPUT_PP)到底输出了什么:推挽的意思是引脚内部上下两个管子轮流导通,芯片能主动把引脚拉到 3.3V(高)或 0V(低),带得动 LED 这种小负载。另一种常见模式是开漏输出,它只能主动拉低,要输出高电平必须外面接上拉电阻——本课点灯、驱蜂鸣器都用推挽,最省事。

为什么要先全灭、再点亮当前这一个:如果只写“点亮下一个”,却不把上一个灭掉,灯的位会一直累加(第一次一个灯、第二次两个灯……),看起来就不是跑马灯而是一串“点亮进度条”。所以每一轮循环里第一句永远是LED_ALL_OFF(),或者像LED_ON(x)之前先清掉旧位——这是本课最容易写错的地方,写错时现象很好认:灯只增不减。

延时是怎么来的(本课与定时器的分界线):delay_ms()、delay_us()在SYSTEM/delay/里,是拿 Cortex-M3 内核自带的 SysTick 做忙等延时:CPU 原地数够时间再往下走,简单、直观、不占用任何外设定时器。所以本课只靠它就能排出“每 200ms 移一位”的节奏;等到课时04 再用真正的定时器中断来做“到点自动干活、CPU 不用死等”,你会立刻体会到两者的差别。

条件编译:一份工程,五种现象。#define DEMO_MODE 3写在文件开头,下面用#if / #elif / #else分成五段:编译时只保留DEMO_MODE对应的那一段,其余几段被预处理器直接删掉,既不占程序空间,也不会互相干扰。所以“改现象”在本课就等于“改一个数字 + 重新编译下载”,你甚至可以在同一份代码里来回切换、对比五种现象。

DEMO_MODE现象主要用到的写法蜂鸣器
18 个灯一直全亮整端口写电平0x00FF静音
2奇数灯、偶数灯交替闪(0.5 秒一换)整端口写电平0x00AA/0x0055静音
3(默认)上电自检(依次点亮 + 全亮/全灭)→ 跑马灯循环单个引脚1U << i+delay_ms()静音
48 个灯一起亮 0.5 秒、一起灭 0.5 秒整端口LED_ALL_ON()/LED_ALL_OFF()+ 延时静音
5灯全灭,蜂鸣器规律“嘀—嘀—”响写 PB8 电平 + 延时唯一会响

蜂鸣器为什么要留一个极性开关:板载蜂鸣器是有源蜂鸣器(内部自带振荡电路),只要给它对的那个电平就一直响,不需要 PWM、也不需要频率;但“高电平响”还是“低电平响”取决于板子上的驱动接法——本板(Nano V1.3)实测是低电平响,所以代码里写的是#define BEEP_ACTIVE_HIGH 0,BEEP_OFF()就是输出高电平。极性搞反时现象特别明显:本该静音的模式 1~4 里,蜂鸣器会一直叫——因为BEEP_Init()里当作“不响”的那个电平,恰恰是它的“响”电平;这时把开关在0/1之间对调、重新编译即可。这也正是本课把它做成开关而不是写死的原因。另外,本课只有现象 5 会真正让蜂鸣器响:BEEP_Init()照常初始化(引脚就绪、并停在“不响”电平上),其余四种现象从头到尾不碰 PB8,所以课堂上连着演示灯效不会吵,也不用把那几行代码删来删去。

2.2 代码示例

本课只改一个文件:课时文档/代码/课时01-GPIO输出跑马灯与蜂鸣器/USER/main.c。同一个工程里的HARDWARE/LED/、SYSTEM/、HALLIB/、CORE/都是现成的(厂家例程里已经写好),照用即可:

课时文档/代码/课时01-GPIO输出跑马灯与蜂鸣器/ ├── USER/LED.uvprojx ← Keil 打开这个工程(本课改的 main.c 也在这里) ├── USER/main.c ← 本课唯一的自写文件(五种现象都在里面) ├── HARDWARE/LED/ ← LED 驱动:led.c / led.h(PC0~PC7 配成推挽输出,不用改) ├── SYSTEM/ ← sys、delay(delay_ms 就在这里)、usart ├── HALLIB/ CORE/ ← HAL 库与启动文件(不用改) └── OBJ/ ← 编译输出(已加入 .gitignore,不进仓库)

下面是本课USER/main.c的完整代码(一个文件五种现象,靠DEMO_MODE切换):

#include"sys.h"#include"delay.h"#include"usart.h"#include"led.h"/************************************************ 《嵌入式技术应用设计实训》课时01 GPIO 输出:跑马灯与蜂鸣器 本文件基于正点原子 ALIENTEK NANO STM32F103 开发板例程(跑马灯实验-HAL库版本)改写, 原例程版权归广州市星翼电子科技有限公司所有。 硬件:正点原子 STM32F103 Nano V1.3 DS0~DS7(板载红色 LED)→ PC0~PC7,低电平点亮 蜂鸣器(板载) → PB8 本课只改这一个文件:下面 DEMO_MODE 换一个数字、重新编译下载,现象就换一种, 不涉及定时器、中断,等待节奏全部用软件延时 delay_ms() 排出来。 注意:五种现象里只有现象 5 会驱动蜂鸣器,其余现象全程静音(上课连着演示不吵)。 ************************************************//* ==================== 演示现象选择(改这一行即可) ==================== 1 = 全亮 :DS0~DS7 一直全亮 2 = 间隔亮 :DS0/DS2/DS4/DS6 与 DS1/DS3/DS5/DS7 交替闪,0.5 秒换一次 3 = 跑马灯 :上电先自检(DS0→DS7 依次点亮一遍 + 全亮/全灭),随后 DS0→DS7 循环流水 4 = 全闪 :8 个灯一起亮 0.5 秒、一起灭 0.5 秒 5 = 只蜂鸣 :LED 全灭,蜂鸣器“嘀—嘀—”规律响(本课只有这一种现象会响) ===================================================================== */#defineDEMO_MODE3/* ============ 蜂鸣器极性开关(只有现象 5 会用到,按实测结果选一个) ============ 1 = 高电平响(PB8 输出高电平时蜂鸣器响) 0 = 低电平响(PB8 输出低电平时蜂鸣器响) 本板(正点原子 Nano V1.3)实测是“低电平响”,所以这里填 0;换成高电平响的板子就改成 1。 有源蜂鸣器只要给对电平就响,不需要 PWM(要“调音”才用 PWM,那是课时05) =========================================================================== */#defineBEEP_ACTIVE_HIGH0/* ---------- 板载 LED:低电平点亮,PC0~PC7 分别对应 DS0~DS7 ---------- */#defineLED_ALL_OFF()HAL_GPIO_WritePin(GPIOC,(uint16_t)0x00FF,GPIO_PIN_SET)/* 8 个灯全灭 */#defineLED_ALL_ON()HAL_GPIO_WritePin(GPIOC,(uint16_t)0x00FF,GPIO_PIN_RESET)/* 8 个灯全亮 */#defineLED_ON(x)HAL_GPIO_WritePin(GPIOC,(uint16_t)(1U<<(x)),GPIO_PIN_RESET)/* 点亮第 x 个,x=0~7 */#defineLED_OFF(x)HAL_GPIO_WritePin(GPIOC,(uint16_t)(1U<<(x)),GPIO_PIN_SET)/* 熄灭第 x 个 *//* ---------- 板载蜂鸣器:BEEP_ON 响、BEEP_OFF 停(极性由上面开关决定) ---------- */#if(BEEP_ACTIVE_HIGH==1)#defineBEEP_ON()HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET)#defineBEEP_OFF()HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET)#else#defineBEEP_ON()HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET)#defineBEEP_OFF()HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET)#endif/* 蜂鸣器初始化:PB8 推挽输出 —— 和 LED_Init() 是同一套三步:开时钟 → 配模式 → 写电平 */staticvoidBEEP_Init(void){GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOB_CLK_ENABLE();/* 1. 开 GPIOB 时钟:不开时钟,后面全白写 */BEEP_OFF();/* 先写成“不响”的电平,避免上电瞬间乱响 */GPIO_Initure.Pin=GPIO_PIN_8;/* 2. 配模式:PB8 */GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;/* 推挽输出 */GPIO_Initure.Pull=GPIO_PULLUP;/* 上拉 */GPIO_Initure.Speed=GPIO_SPEED_HIGH;/* 高速 */HAL_GPIO_Init(GPIOB,&GPIO_Initure);}intmain(void){#if(DEMO_MODE==3)u8 i;/* 只有“跑马灯”这一种现象需要循环变量 */#endifHAL_Init();/* 初始化 HAL 库:顺带配好 1ms 延时基准 */Stm32_Clock_Init(RCC_PLL_MUL9);/* 设置时钟:主频 72MHz */delay_init(72);/* 初始化延时函数 */LED_Init();/* 初始化 LED:DS0~DS7(PC0~PC7)推挽输出,上电默认全灭 */BEEP_Init();/* 初始化蜂鸣器:PB8 推挽输出,上电默认不响(就绪但不出声) */#if(DEMO_MODE==1)/* ---------- 现象 1:全亮(一句话现象:8 个灯一直亮着) ---------- */LED_ALL_ON();while(1){/* 电平已经写下去了,灯就一直亮着;这里什么都不用做 */}#elif(DEMO_MODE==2)/* ---------- 现象 2:间隔亮(奇数灯、偶数灯交替闪) ---------- */while(1){HAL_GPIO_WritePin(GPIOC,(uint16_t)0x00AA,GPIO_PIN_RESET);/* DS1、DS3、DS5、DS7 亮 */HAL_GPIO_WritePin(GPIOC,(uint16_t)0x0055,GPIO_PIN_SET);/* DS0、DS2、DS4、DS6 灭 */delay_ms(500);HAL_GPIO_WritePin(GPIOC,(uint16_t)0x0055,GPIO_PIN_RESET);/* DS0、DS2、DS4、DS6 亮 */HAL_GPIO_WritePin(GPIOC,(uint16_t)0x00AA,GPIO_PIN_SET);/* DS1、DS3、DS5、DS7 灭 */delay_ms(500);}#elif(DEMO_MODE==3)/* ---------- 现象 3:上电自检 + 跑马灯(静音,默认) ---------- */for(i=0;i<8;i++)/* 上电自检:DS0→DS7 依次各亮 0.2 秒,走一遍共 1.6 秒 */{LED_ALL_OFF();/* 先全灭,再点亮当前位:否则会越亮越多(新手常见坑) */LED_ON(i);delay_ms(200);}LED_ALL_ON();/* 8 个灯全亮 0.3 秒:一眼看清 8 路 IO 是不是都通 */delay_ms(300);LED_ALL_OFF();/* 再全灭 0.3 秒 */delay_ms(300);while(1){for(i=0;i<8;i++)/* 跑马灯:每次只亮一个,0.2 秒移一位,一圈 1.6 秒 */{LED_ALL_OFF();LED_ON(i);delay_ms(200);}}#elif(DEMO_MODE==4)/* ---------- 现象 4:全闪(8 个灯一起亮、一起灭) ---------- */while(1){LED_ALL_ON();/* 整端口一次写:8 个灯全亮 0.5 秒 */delay_ms(500);LED_ALL_OFF();/* 再整端口一次写:8 个灯全灭 0.5 秒 */delay_ms(500);}#elif(DEMO_MODE==5)/* ---------- 现象 5:只试蜂鸣器(本课唯一让蜂鸣器响的现象,LED 全灭) ---------- */LED_ALL_OFF();/* 先把灯全灭,听声音时不受灯干扰 */while(1){BEEP_ON();/* 响 0.2 秒 */delay_ms(200);BEEP_OFF();/* 停 0.8 秒 */delay_ms(800);}#else#error"DEMO_MODE 只能填 1~5,请修改文件开头的 DEMO_MODE 宏"#endif}

代码里的关键宏与函数:

宏 / 函数作用取值与说明
DEMO_MODE选择本课演示哪种现象1全亮 /2间隔亮 /3跑马灯(默认)/4全闪 /5只蜂鸣器(唯一会让蜂鸣器响的模式)
BEEP_ACTIVE_HIGH蜂鸣器响对应的电平0低电平响(V1.3 实测,本课默认)/1高电平响;只有现象 5 用到
LED_ALL_ON()/LED_ALL_OFF()8 个灯全亮 / 全灭一次写0x00FF,整个端口一把写完
LED_ON(x)/LED_OFF(x)点亮 / 熄灭第 x 个灯x取0~7(对应 DS0~DS7),内部用1U << x算出位
BEEP_ON()/BEEP_OFF()蜂鸣器响 / 停就是写 PB8 的电平,响多久由后面的delay_ms()决定
BEEP_Init()蜂鸣器初始化与LED_Init()同一套三步:开时钟 → 配模式 → 写电平
delay_ms(n)延时 n 毫秒SYSTEM/delay里的 SysTick 忙等,不是外设定时器

配模式那一步的参数(GPIO_InitTypeDef):

字段类型必选说明
Pinuint32_t是选引脚,多个引脚用|连起来,如GPIO_PIN_0|GPIO_PIN_1(PC0、PC1 一起配)
Modeuint32_t是输出用GPIO_MODE_OUTPUT_PP(推挽);开漏是GPIO_MODE_OUTPUT_OD
Pulluint32_t是上拉GPIO_PULLUP、下拉GPIO_PULLDOWN、浮空GPIO_NOPULL
Speeduint32_t是输出速度:GPIO_SPEED_FREQ_LOW/MEDIUM/HIGH或简写GPIO_SPEED_HIGH

怎么换现象(本课的“改一行”):把文件开头的#define DEMO_MODE 3改成1、2、4、5,保存 →Build(F7)→Download(F8)→ 按复位键看新现象(只有5会听见蜂鸣器,1~4全程静音)。注意必须重新编译下载,只改代码不下载,板子上跑的还是上一个程序——这是新手最常见的一次“现象不对”。

三、实验操作与现象

3.1 接线与操作步骤

本课不用接任何线:LED 和蜂鸣器都在板子上,已经焊好连到引脚了,你只要插上 USB 供电、接好 ST-Link 就能做。

板载元件元件引脚开发板引脚说明(为什么这样接)
红色 LED DS0~DS7阴极(负极)PC0~PC7阳极经限流电阻接 3.3V,所以 IO输出低电平才亮;用 0Ω 电阻/排针连到 PC 口
蜂鸣器 BEEP信号脚PB8有源蜂鸣器、低电平响;信号脚接 PB8

操作步骤(照着做就能跑):

1. 上电接线:USB 线接板子供电口(课时00 认过的那个口)→ ST-Link 接 SWD 三根线 → ST-Link 插电脑 2. 打开工程:Keil 打开 课时文档/代码/课时01-GPIO输出跑马灯与蜂鸣器/USER/LED.uvprojx 3. 选现象 :看 main.c 开头的 #define DEMO_MODE 3(默认跑马灯);想先看静态的改成 1,想单独听蜂鸣器改成 5 4. 编译 :Build(F7)→ 确认 0 Error(s), 0 Warning(s) 5. 下载 :Download(F8)→ 确认 Programming Done. Verify OK. 6. 复位 :按板子 RST 键(下载时勾了 Reset and Run 的会自动跑)→ 对照 3.2 的现象核对

3.2 观察到的现象

本课现象以随课视频演示为准(视频里会把五种现象连着跑一遍),文档不逐条配截图;你自己做完请拍一张或录一段,作为提交物。

DEMO_MODE看到什么算成功怎么核对
1 全亮上电/复位后 DS0~DS7 八个红灯一起亮着不动数一数是 8 个都亮;有一个不亮就是那一路的问题(引脚、驱动、焊接)
2 间隔亮DS0、DS2、DS4、DS6 亮 0.5 秒 ↔ DS1、DS3、DS5、DS7 亮 0.5 秒,来回交替一秒变两次;相邻两个灯永远不同时亮,奇偶两组正好互补
3 跑马灯(默认)复位后先自检:DS0→DS7 依次各亮 0.2 秒走一遍 → 八个灯全亮 0.3 秒 → 全灭 0.3 秒;随后进入循环:每次只亮一个灯、0.2 秒移一位、一圈 1.6 秒先看见“走一遍”再看循环;任意时刻只有 1 个灯亮,全程没有声音
4 全闪八个灯一起亮 0.5 秒、一起灭 0.5 秒,整整齐齐地闪8 个灯永远同亮同灭;一秒变两次;同样没有声音
5 只蜂鸣器八个灯全灭,蜂鸣器“响 0.2 秒 → 停 0.8 秒”规律循环每秒一声、节奏均匀;灯一点都不亮(1~4 静音,要听声音就切到 5)

现象 3 的时间轴(从按复位键算起):

时刻看到
0 ~ 1.6 sDS0→DS7 依次点亮,每 0.2 秒移一位(自检)
1.6 ~ 1.9 s八个灯全亮(查 8 路是不是都通)
1.9 ~ 2.2 s八个灯全灭
2.2 s 起循环:DS0→DS7 逐个点亮,每 0.2 秒移一位,一圈 1.6 秒,周而复始

看不到现象怎么办(按最可能的顺序查):

现象最可能的原因怎么处理
八个灯一个都不亮忘了开时钟,或者电平写反了检查__HAL_RCC_GPIOC_CLK_ENABLE():本板低电平点亮,写GPIO_PIN_SET是“灭”
灯只增不减、越亮越多循环里漏了“先全灭”每一轮先LED_ALL_OFF(),再点亮当前位
下载过了,还是旧现象改了DEMO_MODE却只保存没重新编译下载重新Build(F7)+Download(F8)
现象 1~4 里蜂鸣器一直叫(本该静音)极性反了:板子其实是“高电平响”把#define BEEP_ACTIVE_HIGH 0改成1重新编译(本课默认0= 低电平响)
现象 5 里蜂鸣器节奏不对(一直响/不响)同样是极性不对把BEEP_ACTIVE_HIGH在0/1之间对调后重新编译
想听蜂鸣器却一直没声音1~4四种现象本来就是静音的把DEMO_MODE改成5重新编译下载
下载报No Cortex-M Device found下载器没连好 / 板子没上电查 SWD 三根线和供电(课时00 已练过一遍)

四、学生练习

课堂上按顺序做,一条一条改、一条一条看现象:

编号任务提示预期结果
L1.1只点亮一个灯在DEMO_MODE 1里把LED_ALL_ON()换成LED_ON(0),再换成LED_ON(7)各试一次只有 DS0(或只有 DS7)亮,其余全灭
L1.2改速度把现象 3 里的delay_ms(200)分别改成50、500各编译下载一次灯明显跑得更快 / 更慢,顺序不变(现象 3 全程静音)
L1.3来回跑在现象 3 的循环里再加一段for (i = 6; i > 0; i--)反向点亮DS0→DS7→DS0 来回跑,两头不停顿(反向那段从 6 开始,免得第 7 个灯连亮两次)
L1.4奇偶互换现象 2 里把两处0x00AA与0x0055对调亮的灯正好相反:这次是 DS0、DS2、DS4、DS6 先亮
L1.5改蜂鸣节奏在现象 5 里把delay_ms(200)/delay_ms(800)改成100/300,再试“嘀嘀—停”这种快节奏声音变得又急又密,LED 仍然全灭
L1.6挑战:改成“中间向外亮”用LED_ON(3)、LED_ON(4)再往两边扩,像 DS3/DS4 → DS2/DS5 → DS1/DS6 → DS0/DS7一圈从中间向两边散开(在现象 3 上改,仍然是静音的)

五、作业

  • 必做:把五种现象(DEMO_MODE改成 1~5)各跑一遍,把跑马灯那一种录成 10~15 秒短视频;再用一句话说清“哪种现象是整端口一次写 8 位、哪种是单个引脚移位”
  • 必做:完成练习 L1.2(delay_ms(200)改成50/500),各录 5 秒或截图,写一句观察:速度变了,灯的顺序变了吗?
  • 选做:把跑马灯改成“两个灯同时跑”(在LED_ON(i)后面再加一句LED_ON((i + 4) % 8)),录视频和原来的对比
  • 提交:现象视频或截图 + 一段说明“我改了哪一行、现象怎么变、卡在哪一步怎么解决的”;验收标准:跑马灯顺序正确(DS0→DS7,任意时刻只亮一个)、现象 5 里蜂鸣器能规律“嘀—嘀—”响、编译输出0 Error(s), 0 Warning(s)

六、本节小结与下节预告

本节把 GPIO 输出的三步(开时钟 → 配推挽 → 写电平)完整走了一遍:PC0~PC7 输出低电平点亮 DS0~DS7,PB8 写对电平让蜂鸣器响(蜂鸣器只在现象 5 里响,其余四种灯效全程静音);五种现象写在同一个main.c里、用DEMO_MODE切换,节奏全靠软件延时delay_ms()排出来,不涉及定时器和中断。记住三句话就够了:命令顺序不能反(先开时钟)、本板低电平点亮、点亮下一个之前先灭掉上一个。下节进入 GPIO 输入与外部中断:读按键的电平,让灯听你的话。

本节函数与宏速查

名称一句话作用
__HAL_RCC_GPIOC_CLK_ENABLE()/__HAL_RCC_GPIOB_CLK_ENABLE()给端口开时钟(GPIOC 点灯、GPIOB 控蜂鸣器)
HAL_GPIO_Init(端口, &结构体)按GPIO_InitTypeDef把引脚配成推挽输出
HAL_GPIO_WritePin(端口, 引脚, 电平)写一个或一组引脚的电平(GPIO_PIN_SET高、GPIO_PIN_RESET低)
HAL_GPIO_TogglePin(端口, 引脚)翻转引脚电平(选做里可以试:一行就能闪)
delay_ms(n)/delay_us(n)软件延时 n 毫秒 / n 微秒(SysTick 忙等,不是外设定时器)

附录

A. 怎么编译下载(照着做就能跑)

1. 上电接线:板子 USB 供电 → ST-Link 接 SWD 三根线(SWDIO / SWCLK / GND)→ ST-Link 插电脑 2. Keil 打开:课时文档/代码/课时01-GPIO输出跑马灯与蜂鸣器/USER/LED.uvprojx 3. 选下载器:Options for Target → Debug → ST-Link Debugger → OK 4. 选现象 :main.c 开头 #define DEMO_MODE 3(可改成 1 / 2 / 4 / 5;只有 5 会响蜂鸣器) 5. 编译 :Build(F7),确认 0 Error(s), 0 Warning(s) 6. 下载 :Download(F8),确认 Programming Done. Verify OK. 7. 复位 :按 RST 键,看 DS0→DS7 依次点亮,随后进入跑马灯循环

B. 本课代码

本课只改一个文件:课时文档/代码/课时01-GPIO输出跑马灯与蜂鸣器/USER/main.c(完整代码见 2.2 节)。工程里其余文件都是现成的,不用动:

文件作用
USER/LED.uvprojxKeil 工程文件(用 Keil 打开这个)
USER/main.c本课自写代码:五种现象(1~4 是灯效、5 只响蜂鸣器)+ 蜂鸣器初始化(BEEP_Init)
HARDWARE/LED/led.c、led.hLED 驱动:把 PC0~PC7 配成推挽输出并上电置全灭,提供LED0~LED7宏
SYSTEM/sys、SYSTEM/delay时钟配置、SysTick 延时(delay_ms/delay_us)
SYSTEM/usart串口(本课不用,课时03 才用)
HALLIB/、CORE/STM32F1 HAL 库与启动文件
OBJ/编译输出(已加入.gitignore,不进仓库)

C. 引脚对照表(贴在板子旁边用)

引脚板载元件点亮 / 工作电平
PC0DS0(红色 LED)低电平点亮
PC1DS1(红色 LED)低电平点亮
PC2DS2(红色 LED)低电平点亮
PC3DS3(红色 LED)低电平点亮
PC4DS4(红色 LED)低电平点亮
PC5DS5(红色 LED)低电平点亮
PC6DS6(红色 LED)低电平点亮
PC7DS7(红色 LED)低电平点亮
PB8蜂鸣器 BEEP低电平响(V1.3 实测;若你的板子是高电平响,把BEEP_ACTIVE_HIGH改成1)

相关文档

  • 课程大纲(课时01 在“一、基础外设开发模块”)
  • 课程文档写作规范(0.1 教学环境约定)
  • 上一课:课时00 开发环境搭建与开发板认识
  • 下一课:课时02 GPIO 输入与外部中断:按键
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!