环境配好、接线踩坑之后,接下来就是让外设动起来。这组实验全部围绕 GPIO 输出:先点亮一颗 LED,再扩展成八路流水灯,最后用同样的思路驱动蜂鸣器。三个实验的代码都在工程的 User\main.c 里(如 3-1点亮LED灯、3-2流水灯、3-3蜂鸣器)。

一、点亮第一颗 LED

之前两天都在跟接线较劲,怀疑人生;当 LED 终于亮起来的时候,成就感爆棚。核心教训:嵌入式开发,硬件接线和软件一样重要。

// PA0 配置为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 低电平点亮 LED
GPIO_ResetBits(GPIOA, GPIO_Pin_0);

关键理解:

  • GPIO 配置:推挽输出模式,速度 50MHz
  • LED 驱动:低电平有效,GPIO_ResetBits 拉低点亮
  • 当前状态:常亮,while(1) 为空

二、流水灯:PA0-PA7 八个 LED

在单颗 LED 的基础上,实现 PA0-PA7 八个 LED 流水灯效果。

// 配置 GPIOA 所有引脚为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;  // 一次性配置所有引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 流水灯核心:GPIO_Write 一次写入 16 位数据
while(1)
{
    GPIO_Write(GPIOA, ~0x0001);  // 0x0001取反=0xFFFE,PA0低电平点亮
    Delay_ms(300);
    GPIO_Write(GPIOA, ~0x0002);  // PA1点亮
    Delay_ms(300);
    GPIO_Write(GPIOA, ~0x0004);  // PA2点亮
    // ... 依次类推到 PA7
}

GPIO 操作函数对比

函数作用使用场景
GPIO_ResetBits单引脚拉低单个LED控制
GPIO_SetBits单引脚拉高单个LED控制
GPIO_WriteBit按位设置单引脚精确控制
GPIO_Write16位整体写入多引脚同时控制

位操作技巧

  • ~0x0001 = 0xFFFE → PA0 低电平(点亮),其余高电平(熄灭)
  • ~0x0002 = 0xFFFD → PA1 低电平(点亮)
  • 位左移:0x0001 << n 可以简化代码

优化思路

当前代码是 8 行重复,可以用循环 + 移位简化:

uint16_t led = 0x0001;
while(1)
{
    GPIO_Write(GPIOA, ~led);
    Delay_ms(300);
    led <<= 1;
    if(led > 0x0080) led = 0x0001;  // 从PA7回到PA0
}

三、蜂鸣器控制

与 LED 完全同构的实验:PA9 推挽输出,低电平响。

// 配置 PA9 为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 蜂鸣器控制:低电平响,高电平停
while(1)
{
    GPIO_ResetBits(GPIOA, GPIO_Pin_9);  // PA9拉低→蜂鸣器响
    Delay_ms(300);
    GPIO_SetBits(GPIOA, GPIO_Pin_9);    // PA9拉高→蜂鸣器停
    Delay_ms(300);
}

关键理解:

  • 蜂鸣器是低电平触发:ResetBits 响,SetBits 停
  • 与 LED 控制逻辑一致:低电平有效
  • 频率决定音调:Delay_us 越短,声音越尖锐(音调段用微秒级延时翻转 IO 产生音频频率;鸣停节奏才用 Delay_ms)

扩展:音调控制

// 不同频率对应不同音调
// Do: Delay_us(956)  Re: Delay_us(852)  Mi: Delay_us(758)
// Fa: Delay_us(716)  Sol: Delay_us(638) La: Delay_us(568)

// 简单旋律示例
int melody[] = {956, 852, 758, 716, 638, 568, 506, 478};
for(int i = 0; i < 8; i++)
{
    for(int j = 0; j < 50; j++)  // 每个音持续一段时间
    {
        GPIO_ResetBits(GPIOA, GPIO_Pin_9);
        Delay_us(melody[i]);
        GPIO_SetBits(GPIOA, GPIO_Pin_9);
        Delay_us(melody[i]);
    }
}

四、小结

三个实验的共同套路:

  1. 配置 GPIO 为推挽输出(GPIO_Mode_Out_PP,50MHz);
  2. 外设基本都是低电平有效——拉低动作(点亮/响),拉高停止;
  3. 单引脚控制用 SetBits/ResetBits,多引脚批量控制用 GPIO_Write + 位操作;
  4. 后续方向:加延时函数做闪烁、学定时器配置、尝试按键中断控制。