stm32位带操作

发布时间:2024-07-16  

  STM32系列基于专为要求高性能、低成本、低功耗的嵌入式应用专门设计的ARM Cortex-M3内核(ST‘s product portfolio contains a comprehensive range of microcontrollers, from robust, low-cost 8-bit MCUs up to 32-bit ARM-based Cortex®-M0 and M0+, Cortex®-M3, Cortex®-M4 Flash microcontrollers with a great choice of peripherals. ST has also extended this range to include an ultra-low-power MCU platform)[1] 。按内核架构分为不同产品:

  其中STM32F系列有:

  STM32F103“增强型”系列

  STM32F101“基本型”系列

  STM32F105、STM32F107“互联型”系列

  增强型系列时钟频率达到72MHz,是同类产品中性能最高的产品;基本型时钟频率为36MHz,以16位产品的价格得到比16位产品大幅提升的性能,是32位产品用户的最佳选择。两个系列都内置32K到128K的闪存,不同的是SRAM的最大容量和外设接口的组合。时钟频率72MHz时,从闪存执行代码,STM32功耗36mA,相当于0.5mA/MHz。

  

  在CM3中,有两个区中实现了位带。其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB 范围。这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

  在位带区中,每个比特都映射到别名地址区的一个字(这是只有 LSB 有效的字)。当一个别名地址被访问时,会先把该地址变换成位带地址。对于读操作,读取位带地址中的一个字,再把需要的位右移到 LSB,并把 LSB 返回。对于写操作,把需要写的位左移至对应的位序号处,然后执行一个原子的“读-改-写”过程。

  支持位带操作的两个内存区的范围是:

  0x2000_0000‐0x200F_FFFF(SRAM 区中的最低1MB) 0x4000_0000‐0x400F_FFFF(片上外设区中的最低 1MB) 位带(Bit-band)操作是Cortex-M3提供的特殊操作:位带区的每个位都有位带别名区的一个字与之对应。Bit-band区域的存储器以32位方式进行访问,其中有效的仅仅是BIT0位,只有BIT0的值才对应到相应的普通区域的比特位上,其他位无效。STM32F系列芯片为所有外设寄存器和SRAM提供相对应的Bit-band区域,以简化对外设寄存器和SRAM的操作位带操作最重要的一环就是寻址,即为需要操作的“目标位”找到位带别名区相对应的地址:

  位带别名区首地址+(操作字节的偏移量*32) +(操作位的偏移量*4) 内置SRAM区的位带别名区首地址 = 0x2200,0000 外设寄存器区的位带别名区首地址 = 0x4200,0000

  例如:GPIOA的端口输出数据寄存器地址0x4001080c(stm32f1xx系列),对于PA.0来说控制其输出电平的比特位的位带操作地址为:

  0x42000000+(0x1080c*32)+(0*4) = 0x42021018 以stm32f207为例,介绍stm32的位带操作。

  

  在《ARM Cortex M3权威指南》92页介绍了如何在C语言中使用位带操作。个人在使用位带操作过程中将其大致总结为三种:

  ① 查找需要定义的位所在的地址,将地址强制转换为指针,通过取指针内容的方式使用。

  例如:

  #define PD12 ((volatile unsigned long *)(0x424182b0))

  //0x424182b0为GPIOD_Pin12引脚对应的映射地址

  *PD12=0x01; //PD12引脚置高

  ② 直接使用地址进行操作

  (*((u32*)0x424182b0))=0x01; //PD12引脚置高 当然也可以通过如下定义使用 例如:

  #define PD12 *((volatile unsigned long *)(0x424182b0))

  //0x424182b0为GPIOD_Pin12引脚对应的映射地址

  PD12=0x01; //PD12引脚置高

  ③在多个引脚需要定义时,显然上面两种方法都比较繁琐。为简化位带操作,也可以定义一些宏。比如,我们可以建立一个把“位带地址+位序号”换成别名地址的宏,再建立一个把别名地址转换成指针类型的宏。

  例如

  #define GPIOD_ODR_Addr ((uint32_t)(GPIOD_BASE+0x14)) // GPIOD_BASE已经定义过 #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) ) #define PD12 BIT_ADDR(GPIOD_ODR_Addr, 12) //12为GPIOD对应的引脚号 #define PD12 BIT_ADDR(GPIOD_ODR_Addr, 13) //13为GPIOD对应的引脚号 PD12=0x01; //PD12引脚置高 PD13=0x01; //PD13引脚置高

  

  说明:GPIOD_BASE已经在库文件stm32f2xx.h中定义过,直接使用即可,对应地址是GPIOD的基地址,0x14是ODR的偏移地址。第二条语句是把“位带地址+序号”转换为对应的位带别名区地址。第三条语句MEM_ADDR(addr)代表的是(addr)地址中的内容。volatile必须使用,原因如下。

  volatile 是易变的、不稳定的意思。用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

  先看看下面的例子: int i=10; int j = i;//(1)语句 int k = i;//(2)语句

  这时候编译器对代码进行优化,因为在(1)、(2)两条语句中,i 没有被用作左值。这时候编译器认为 i 的值没有发生改变,所以在(1)语句时从内存中取出 i 的值赋给 j 之后,这个值并没有被丢掉,而是在(2)语句时继续用这个值给 k 赋值。编译器不会生成出汇编代码重新从内存里取 i 的值,这样提高了效率。但要注意:(1)、(2)语句之间 i 没有被用作左值才行。

  再看另一个例子: volatile int i=10; int j = i;//(3)语句 int k = i;//(4)语句

  volatile 关键字告诉编译器 i 是随时可能发生变化的,每次使用它的时候必须从内存中取出 i的值,因而编译器生成的汇编代码会重新从 i 的地址处读取数据放在 k 中。这样看来,如果 i 是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说 volatile 可以保证对特殊地址的稳定访问。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>