STM32微控制器中的位带操作(Bit-Banding)是一种特殊的内存映射技术,它允许将特定的位(Bit)与特定的内存地址绑定,从而实现对单个位的原子级操作。
位带操作在STM32中通常用于对GPIO端口、寄存器以及其他外设的单个位进行读写操作,提高了代码的可读性和效率。
2.位带操作的一些特点(1)原子级操作位带操作可以确保对单个位的读写是原子性的,即在任何时候只有一个CPU周期可以访问这个位,从而避免了多线程并发访问时可能出现的竞态条件和数据不一致性问题。
(2)内存映射位带操作利用了STM32微控制器中的特殊内存区域,将每个位与一个特定的内存地址绑定。通过读写这个内存地址,可以实现对位的操作。
(3)语法简单位带操作的语法非常简单明了,通常使用类似于“(uint32_t)BITBAND_ADDRESS=VALUE;”的形式进行位的设置或清除。
(4)应用广泛位带操作可以用于对GPIO端口的单个引脚进行操作,也可以用于对外设寄存器的单个位进行设置或清除,因此在嵌入式开发中应用非常广泛。
(5)性能优化由于位带操作是在硬件层面实现的,因此具有非常高的执行效率,适用于对性能要求较高的应用场景。
3.位带操作的缺点在多线程或者中断环境下,需要特别注意原子性操作的问题,以避免可能的竞态条件和数据不一致性问题。
位带操作也可能会使代码的可移植性降低,因为不同的微控制器架构可能对位带操作的支持程度不同。
二、位带操作的步骤1.确定位带区域首先需要确定哪些地址是位带区域,只有位带区域才支持位带操作。
在STM32中,支持位带操作的区域有:
SRAM区的最低1M范围(APB1、APB2、AHB外设)
片内外设区的最低1M范围。
在《Cortex权威指南中(中文)》中可以查到具体说明:
具体地址范围:
0x20000000-0x200FFFFF(SRAM区中的最低1MB)
0x40000000-0x400FFFFF(片上外设区中的最低1MB)
对应别名区的范围为:
0x22000000-0x23FFFFFF(32MB)
0x42000000-0x43FFFFFF(32MB)
下面介绍使用的都片内外设区位带操作。
2.计算位带地址对于要进行位带操作的位,需要计算其对应的位带地址。位带地址的计算通常使用公式:
BITBAND_ALIAS_BASE+(BYTE_OFFSET*32)+(BIT_NUMBER*4),
其中:
BITBAND_ALIAS_BASE是位带区域的基地址,
BYTE_OFFSET是位所在字节相对于位带区域起始地址的偏移量,
BIT_NUMBER是位在字节中的偏移量,乘以4是因为每个位的地址是4个字节对齐的。
当使用片内外设别位带区时,起始地址是BITBAND_ALIAS_BASE=0x42000000。
当使用SRAM位带区时,BITBAND_ALIAS_BASE=0x20000000。
为方便操作,用一个公式来计算位带地址:
KaTeXparseerror:Expected'EOF',got''atposition5:((A̲0xF0000000)+…
其中:
A是要操作的寄存器地址
n是位号。
用宏来表示:
//计算地址值defineMEM_ADDR(addr)*((volatileunsignedlong*)(addr))//位带操作definePAin(n)BIT_ADDR(GPIOA-IDR,n)definePCin(n)BIT_ADDR(GPIOC-IDR,n)definePEin(n)BIT_ADDR(GPIOE-IDR,n)4.写入位操作
definePBout(n)BIT_ADDR(GPIOB-ODR,n)definePDout(n)BIT_ADDR(GPIOD-ODR,n)ifndef__utilsH__include""defineGPIOA_IDR_A(GPIOA_BASE+0x08)defineGPIOB_IDR_A(GPIOB_BASE+0x08)defineGPIOC_IDR_A(GPIOC_BASE+0x08)defineGPIOD_IDR_A(GPIOD_BASE+0x08)defineGPIOE_IDR_A(GPIOE_BASE+0x08)definePAout(n)BitBind(GPIOA_ODR_A,n)//输出definePBout(n)BitBind(GPIOB_ODR_A,n)definePCout(n)BitBind(GPIOC_ODR_A,n)definePDout(n)BitBind(GPIOD_ODR_A,n)definePEout(n)BitBind(GPIOE_ODR_A,n)if3.
include""#include""voidSystemInit(){}intmain(void){GPIO_Configuration();intj;while(1){allOff();delay(0xfffff);for(j=0;j8;j++){PCout(j)=0;delay(0xfffff);}}}