如何便捷访问STM32系列芯片USB外设的PMA存储器
STM32 USB PMA 访问的优雅实现方案
下面是兼顾可维护性、可读性和兼容性的三种实现方案,可根据实际使用场景选择:
方案1:封装PMA专用访问宏/内联函数(最通用)
根据PMA的固定映射规则:16位PMA逻辑地址n对应的CPU物理地址 = PMA基地址 + (n << 1),直接封装底层读写接口,完全屏蔽硬件对齐细节:
// 定义PMA外设基地址 #define PMA_BASE 0x40006000UL // 读取指定PMA逻辑地址的16位数据 #define PMA_READ16(n) (*(volatile uint16_t *)(PMA_BASE + ((n) << 1))) // 向指定PMA逻辑地址写入16位数据 #define PMA_WRITE16(n, val) (*(volatile uint16_t *)(PMA_BASE + ((n) << 1)) = (val))
使用时直接传入PMA逻辑地址即可,不需要处理对齐、填充:
// 示例:写PMA地址0x006 PMA_WRITE16(0x006, 0x1234); // 示例:读PMA地址0x001 uint16_t val = PMA_READ16(0x001);
方案2:封装PMA专用类型,保留结构体访问习惯
如果需要保持结构体的访问方式,可以定义强制32位对齐的PMA专用16位类型,编译器会自动处理填充,不需要手动加保留位:
// PMA专用16位类型,自动占4字节空间,低16位有效 typedef union { volatile uint16_t val; volatile uint32_t _reserved; // 强制32位对齐 } pma_uint16_t; // 缓冲区描述符结构体,无需手动加填充、无需packed属性 struct buf_desc_entry { pma_uint16_t addr; pma_uint16_t count; };
使用时直接访问val成员即可:
struct buf_desc_entry *desc = (struct buf_desc_entry *)PMA_BASE; desc->addr.val = 0x008; desc->count.val = 64;
方案3:封装批量读写函数,适配8位数据访问
如果需要在PMA和普通SRAM之间批量传输8位数据(比如USB数据包读写),可以封装专用拷贝函数,内部自动处理地址转换和拼接:
// 将普通SRAM的8位数据批量写入PMA void pma_write_buf(uint16_t pma_start, const uint8_t *src, uint16_t len) { for (uint16_t i = 0; i < len; i += 2) { uint16_t tmp = src[i]; if (i + 1 < len) tmp |= (uint16_t)src[i + 1] << 8; PMA_WRITE16(pma_start + (i >> 1), tmp); } } // 将PMA数据批量读到普通SRAM的8位缓冲区 void pma_read_buf(uint8_t *dst, uint16_t pma_start, uint16_t len) { for (uint16_t i = 0; i < len; i += 2) { uint16_t tmp = PMA_READ16(pma_start + (i >> 1)); dst[i] = tmp & 0xFF; if (i + 1 < len) dst[i + 1] = (tmp >> 8) & 0xFF; } }
方案优势
- 无需手动为结构体添加填充位,避免人为错误
- 不需要强制全局变量32位对齐,不影响普通8位/16位变量的使用
- 硬件细节完全抽象,上层代码不需要记忆PMA映射规则,可读性更高
- 可移植性强,更换不同PMA映射的STM32型号时,仅需修改底层宏和函数实现即可
内容的提问来源于stack exchange,提问作者Sam Bazley
相关产品推荐
相关产品推荐

