[软件资料] 映射操作

[复制链接]
 楼主| belindagraham 发表于 2025-7-12 08:23 | 显示全部楼层 |阅读模式
Bit-banding 简称位带、位段
支持位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写
对于硬件 I/O 密集型的底层程序最有用处

优点
  • 使代码更简洁
  • 在多任务中,用于实现共享资源在任务间的“互锁”访问
    多任务的共享资源必须满足一次只有一个任务访问它,即“原子操作”,位带操作可以满足这个条件
    以前的”读-改-写“需要 3 条指令,导致这中间留有两个能被中断的空当,可能会出现紊乱危象


紊乱危象主程序是一个任务,ISR 是另一个任务,这两个任务并发执行
ISR 所作的改动已丢失:指 ISR 对输出端口所做的修改 0x3,被后面的主程序的修改 0x0 覆盖了

位带操作把这个“读-改-写”做成一个硬件级别支持的原子操作(具体如何实现我还不知道),避免了紊乱危象
位带操作并不只限于以字为单位的传送,也可以按半字和字节为单位传送。例如,可以使用 LDRB/STRB 来以字节为长度单位去访问位带别名区,同理可用于 LDRH/STRH。但是不管用哪一个,都必须保证目标地址对齐到字的边界上

产生由来在 STM32 中不能直接操作寄存器的某一个 Bit 位,比如 PA 端口 port input data register 中的 ODR,只能以 32bit 去操作

而 port output data register 这种 ODR 可以单独操作某个 bit 位

为了能直接操作 ODR 的某个 Bit 位,在内核中开辟了一块地址区域(位带别名):可将 ODR这类 Bit 位(位带区)映射到位带别名区域对应的地址,只需操作映射后的地址,就可实现操作 ODR1 位
原理
  • 位带区: 支持位带操作的地址区
  • 位带别名: 对别名地址的访问最终作 CM3 将用到位带区的访问上(中途有一个地址映射过程)
位带区大小 1MB,由于每个 bit 都要映射到位带别名区,每个 bit 需要膨胀为一个 字(只有 LSB 有效的字)。对于一个 32 位的 MCU 来说,1MB 需要 1MB * 32bit = 32MB(这里计算式不严谨)的位带别名区空间
当一个别名地址被访问时,会先把该地址变换成位带地址:
        对于读操作,读取位带地址中的一个字,再把需要的位右移到 LSB,并把 LSB 返回
        对于写操作,把需要写的位左移至对应的位序号处,然后执行一个原子的“读-改-写”过程

示例
  • 在地址 0x20000000 处写入一个字的数据 0x3355AACC
  • 读取地址 0x22000008,本次读访问将读取 0x20000000,并提取 bit2,值为 1
  • 往地址 0x22000008 处写 0,本次操作将被映射成对地址 0x20000000 的“读-改-写”操作(原子的),把 bit2 清 0
  • 现在再读取 0x20000000,将返回 0x3355AAC8(bit2 已清零)
位带别名区的字只有 LSB 有意义。在访问位带别名区时,不管使用哪一种长度的数据传送指令(字/半字/字节),都把地址对齐到字的边界上,否则会产生不可预料的结果


CM3在 Cortex-M3 中有两个区实现了位带操作
        SRAM 区的最低 1MB 范围
        片内外设区的最低 1MB 范围



  1. // SRAM 位带区:    0X2000 0000~0X2010 0000
  2. // SRAM 位带别名区:0X2200 0000~0X23FF FFFF

  3. // 外设 位带区:    0X4000 0000~0X4010 0000
  4. // 外设 位带别名区:0X4200 0000~0X43FF FFFF

  5. // 把“位带地址+位序号”转换成别名地址的宏
  6. #define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x02000000 + ((addr & 0x00FFFFFF) << 5) + (bitnum << 2))
  7. /*
  8. * addr & 0xF0000000,取地址的高 4 位,提取 SRAM 或 外设地址 的高地址
  9. * 如 2,+ 0x02000000 则 =0X2200 0000,即是 SRAM
  10. * 如 4,+ 0x02000000 则 =0X4200 0000,即是 外设
  11. *
  12. * addr & 0x00FFFFFF,屏蔽高 8 位,偏移位带区多少个字节
  13. * <<5 等于 *8*4,位带区一个地址表示一个字节,一个 bit 膨胀成一个字(32位MCU),即 4byte
  14. * <<2 等于 *4,一个 bit 膨胀成一个字(32位MCU),即 4byte
  15. *
  16. * SRAM位带别名地址
  17. * AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4
  18. * 外设位带别名地址
  19. * AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4
  20. */

  21. // 把一个地址转换成一个指针
  22. #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))

  23. // 把位带别名区地址转换成指针
  24. #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
  25. #define DEVICE_REG0 ((volatile unsigned long *) (0x40000000))
  26. #define DEVICE_REG0_BIT0 ((volatile unsigned long *) (0x42000000))
  27. #define DEVICE_REG0_BIT1 ((volatile unsigned long *) (0x42000004))

  28. *DEVICE_REG0 = 0xAB;  // 使用正常地址访问寄存器
  29. *DEVICE_REG0 = *DEVICE_REG0 | 0x2; // 使用传统方法设置 bit1

  30. *DEVICE_REG0_BIT1 = 0x1;           // 通过位带别名地址设置 bit1
  31. CM4
  32. #define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5) + (bitnum << 2))
  33. #define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
  34. #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))

  35. // IO口地址映射
  36. #define GPIOA_ODR_Addr (GPIOA_BASE + 20) // 0x40020014
  37. #define GPIOB_ODR_Addr (GPIOB_BASE + 20) // 0x40020414
  38. #define GPIOC_ODR_Addr (GPIOC_BASE + 20) // 0x40020814
  39. #define GPIOD_ODR_Addr (GPIOD_BASE + 20) // 0x40020C14
  40. #define GPIOE_ODR_Addr (GPIOE_BASE + 20) // 0x40021014
  41. #define GPIOF_ODR_Addr (GPIOF_BASE + 20) // 0x40021414
  42. #define GPIOG_ODR_Addr (GPIOG_BASE + 20) // 0x40021814
  43. #define GPIOH_ODR_Addr (GPIOH_BASE + 20) // 0x40021C14
  44. #define GPIOI_ODR_Addr (GPIOI_BASE + 20) // 0x40022014


AdaMaYun 发表于 2025-7-16 20:48 | 显示全部楼层
这个映射是位代操作嘛?
OKAKAKO 发表于 2025-7-16 22:38 | 显示全部楼层
支持位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写
中国龙芯CDX 发表于 2025-7-17 22:21 | 显示全部楼层
位代操作非常重要的操作
您需要登录后才可以回帖 登录 | 注册

本版积分规则

30

主题

1700

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部