全部例程

GPIO - 位带操作

W55MH32

更新于 2026年8月12日

本篇总结

使用 GPIO 位带操作,控制 LED 闪烁。

🔑 KEIL MDK、UART调试、GPIO

本示例使用位带操作控制 GPIO 引脚输出,实现 LED 闪烁,展示位带操作的简洁与高效。

前置工具准备

开始本例程前,请确认以下工具已安装:

  • WIZ UartTool V1.0:串口收发测试工具:点击下载

硬件连接

本例程使用 PD14 引脚控制 LED 闪烁。W55MH32L-EVB 开发板上 PD14 通常连接用户 LED(低电平点亮)。USART1 用于输出调试信息。

引脚功能方向模式
PD14LED 控制输出(低电平点亮)开发板 → LED推挽输出 (Out_PP)
PA9USART1_TX开发板 → PC复用推挽输出 (AF_PP)
PA10USART1_RXPC → 开发板浮空输入 (IN_FLOATING)

工程结构

工程位于 SDK 内 1.SDK\ModuleDemo\GPIO_BitBand\,核心文件如下:

GPIO_BitBand/
├── USER/
│   ├── main.c            ← 位带宏定义 + GPIO配置 + LED闪烁
│   ├── w55mh32_conf.h    ← 外设头文件集中包含
│   ├── w55mh32_it.c/h    ← 中断服务函数
│   ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│   └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
    └── delay/
        └── delay.c/h      ← SysTick 延时函数

步骤1:位带操作宏定义与主函数

文件:USER/main.c — 定义位带操作宏,将 GPIO 输出寄存器的每个比特映射到独立的别名地址,通过 PDout(14) = 1 直接控制 PD14 输出。


/* 位带操作宏定义 */
#define BITBAND(addr, bitnum)  ((addr & 0xF0000000) + 0x2000000 + \
                                ((addr & 0xFFFFF) << 5) + (bitnum << 2))
#define MEM_ADDR(addr)         *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum)  MEM_ADDR(BITBAND(addr, bitnum))

/* GPIO 输出寄存器地址映射 */
#define GPIOA_ODR_Addr    (GPIOA_BASE + 12)
#define GPIOD_ODR_Addr    (GPIOD_BASE + 12)
#define GPIOC_ODR_Addr    (GPIOC_BASE + 12)

/* PAout(n) → 操作 GPIOA 第 n 位 */
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr, n)
#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr, n)
#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr, n)

int main(void)
{
    RCC_ClocksTypeDef clocks;
    GPIO_InitTypeDef  GPIO_InitStructure;

    delay_init();
    UART_Configuration(115200);
    RCC_GetClocksFreq(&clocks);
    printf("GPIO Bit-Band Test.\n");

    /* 使能 GPIOD 时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);

    /* PD14: 推挽输出 */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    while (1)
    {
        /* 位带操作:直接对单个位赋值,无需读-改-写 */
        PDout(14) = 1;       /* PD14 = 1,LED 灭 */
        delay_ms(500);
        PDout(14) = 0;       /* PD14 = 0,LED 亮 */
        delay_ms(500);
    }
}
  

位带映射公式:AliasAddr = 0x42000000 + (ByteOffset × 32) + (BitNumber × 4)。位带区地址范围 0x40000000~0x400FFFFF(外设区),每个比特映射到位带别名区的一个 32 位字地址。通过 PDout(n) 宏可直接对 GPIOD 的第 n 位赋值,等价于原子操作,无需 GPIO_SetBits/GPIO_ResetBits

如果你想自定义

  • 控制其他引脚 → 使用 PAout(n)PCout(n) 等宏操作其他 GPIO
  • 改闪烁速度 → 修改 delay_ms() 参数
  • 读取输入 → 定义 PBin(n) 宏映射 GPIO 输入数据寄存器(IDR)

编译与下载

Keil MDK 中打开 GPIO_BitBand.uvprojxF7 编译,确认 0 Error(s)。连接 DEBUG USB 口到 PC,F8 下载。

运行验证

下载固件并按 RESET 后,PD14 连接的 LED 以 500ms 间隔闪烁。串口打印 “GPIO Bit-Band Test.” 提示信息。通过示波器测量 PD14 可观察到方波信号,频率约 1Hz。

本例程的运行效果为 LED 闪烁,可通过观察板载 LED 或示波器测量 PD14 引脚波形验证。无串口输出截图。

常见问题

Q: LED 不闪烁

确认 PD14 引脚连接了 LED。检查 GPIO 是否配置为推挽输出模式(GPIO_Mode_Out_PP)。确认 RCC_APB2PeriphClockCmd 已使能 GPIOD 时钟。

Q: 位带操作编译报错

检查宏定义是否正确。BITBAND、MEM_ADDR、BIT_ADDR 宏需在文件顶部定义。确保 GPIOx_ODR_Addr 宏的偏移量正确(ODR 偏移为基地址 + 12)。

Q: 位带操作和 GPIO_SetBits/ResetBits 有什么区别

位带操作是单条STR指令的原子操作,不会被中断打断。GPIO_SetBits 实际是读-改-写操作(先读 ODR,修改位,再写回),在多任务或中断环境中可能产生竞态条件。位带操作更安全且效率更高。

总结

本文基于 W55MH32L 的 Cortex-M3 位带机制实现了 GPIO 单比特原子操作:

  • 位带映射:将外设区 0x40000000~0x400FFFFF 的每个比特映射到位带别名区的独立字地址
  • 通过 PDout(n) = 1/0 宏直接对 GPIO 某一位赋值,等价于原子操作
  • 相比 GPIO_SetBits/ResetBits 的读-改-写流程,位带操作更简洁、更安全(无竞态条件)
  • 示例使用 PD14 控制 LED 以 500ms 间隔闪烁

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5 配套开发板

Andy

W5500 技术支持专家