历史上的今天
返回首页

历史上的今天

今天是:2024年12月29日(星期日)

2020年12月29日 | STM32 位段详解

2020-12-29 来源:eefocus

1 定义

首先需要明确下,位段,位带和别名区这三个名词


位段:STM32用户参考手册使用的名字


位带:CortexM3参考手册使用的


别名区:地址总线上用来位访问地址区域,


 

所以说,位段和位带是一个意思,是不同手册的不同叫法。


由上述的名词解释得知,位带功能并不是STM32独有的,是CortexM3的功能(CortexM4也有这样的功能)。MCS51有位操作,以一位(bit)为数据对象的操作,MCS51可以简单的将P1口的第2位独立操作:P1.2=0;P1.2=1 ;这样就把P1口的第三个脚(bit2)置0置1。而STM32的位段、位带别名区最重要的就为了实现这样的功能。


2 位带操作

2.1 范围

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


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


 


0x2000_0000‐0x200F_FFFF(SRAM 区中最低1MB区域)


0x4000_0000‐0x400F_FFFF(片上外设区中的最低 1MB)


 


2.2 位带操作

对 SRAM 位带区的某个比特,记该比特所在字节的地址为A,位序号为 n (0<=n<=7),则它在别名区的地址为:


AliasAddr = 0x22000000 + ((A‐0x20000000)*8+n)*4 =0x22000000 + (A‐0x20000000)*32 + n*4

对于片上外设位带区的某个比特,记该比特所在字节的地址为A,位序号为 n (0<=n<=7),则该比特在别名区的地址为:


AliasAddr = 0x42000000 + ((A‐0x40000000)*8+n)*4 = 0x42000000 + (A‐0x40000000)*32 + n*4

上式中,“*4”表示一个字为 4 个字节,“*8”表示一个字节中有 8 个比特。


2.3代码实现

把“位带地址+位序号”转换别名地址宏为:


#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000 + ((addr &0xFF FFF)<<5) + (bitnum<<2))

把该地址转换成一个指针:


#define MEM_ADDR(addr, bitnum) *((volatile unsigned long *)((addr & 0xF0000000)+0x2000000 + ((addr &0xFF FFF)<<5) + (bitnum<<2)))

其中


addr的取值范围:


0x2000_0000‐0x200F_FFFF


0x4000_0000‐0x400F_FFFF


注意:addr取值要32位对齐


bitnum的取值范围:


0-31


解析:


(addr & 0xf0000000) + 0x02000000:


区分SRAM还是外设,如果是外设,结果为4,再加0x2000000就等于0x4200000,0x42000000就是外设别名位带区。如果是SRAM,结果为2,再加上0x2000000就等于0x22000000,0x22000000就是SRAM别名位带区。


 


addr & 0x00ffffff:


屏蔽了最高2位,相当于减去0x20000000或者0x40000000。因为位带区的有效范围是1M,即0x100000,这样子就做到了低6位有效。


 


<< 5:


等价于乘以32


 


<< 2:


等价于乘以4


 


特别提醒:


当你使用位带功能时,要访问的变量必须用 volatile 来定义。因为 C 编译器并不知道同一个比特可以有两个地址。所以就要通过 volatile,使得编译器每次都如实地把新数值写入存储器,而不再会出于优化的考虑。


 


3 位段的优点

最容易想到的就是通过 GPIO 的管脚来单独控制每盏 LED 的点亮与熄灭。另一方面,也对操作串行接口器件提供了很大的方便(典型如 74HC165,CD4094)。位带操作可以把代码缩小, 速度更快,效率更高,更安全。总之位带操作对于硬件 I/O 密集型的底层程序最有用处了


位带操作还能用来化简跳转的判断。


当跳转依据是某个位时,以前必须这样做


1、读取整个寄存器


2、掩蔽不需要的位


3、比较并跳转


使用位带操作后


1、从未带别名区读取状态位


2、比较并跳转

当然,对于写入操作也从4步精简到3步

 

4 STM32的3种不同GPIO驱动

4.1 库函数版

最常用的版本,使用ST标准外设库

void  Led_Key_Init(void)

{  

  GPIO_InitTypeDef GPIO_Init_s;

  

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);//使能时钟  

  GPIO_Init_s.GPIO_Pin = GPIO_Pin_4;

  GPIO_Init_s.GPIO_Mode = GPIO_Mode_OUT;

  GPIO_Init_s.GPIO_OType = GPIO_OType_PP;

  GPIO_Init_s.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init_s.GPIO_PuPd = GPIO_PuPd_UP;

  GPIO_Init(GPIOE, &GPIO_Init_s);

}

开源代码:


https://github.com/strongercjd/STM32F207VCT6/tree/master/06-GPIO-Input-Output


 


4.2 寄存器版

寄存器版其实就是把ST标准外设库拷贝出来,可以简化一些操作


 

/********使用寄存器---start********/

//TP---PA15

 

#define TP_PORT      GPIOA      

#define TP_PIN      GPIO_Pin_15

 

#define TP_OUT()   TP_PORT->CRH&=0X0FFFFFFF; TP_PORT->CRH|=0X50000000;   //!IO输出

#define TP_IN()    TP_PORT->CRH&=0X0FFFFFFF; TP_PORT->CRH|=0X40000000;   //!IO输入

#define TP_READ()  (((TP_PORT->IDR)>>15)?1:0)   //! 

#define TP_SET()    TP_PORT->BSRR = TP_PIN;//写1

#define TP_CLR()    TP_PORT->BRR  = TP_PIN;//写0

 

#define TP_DATA_SET()   TP_SET() //IO写1

#define TP_DATA_CLR()   TP_CLR() //IO写0

#define TP_DATA_OUT()   TP_OUT() //将IO设为输出

#define TP_DATA_IN()    TP_IN()  //将IO设为输入

#define TP_DATA_READ()  TP_READ()//读取IO的电平

 

/********使用寄存器---end********/

开源代码:


https://github.com/strongercjd/STM32F207VCT6/tree/master/06-GPIO-Input-Output


 


4.3 位段版

基于位段的,操作方便


/*LED配置---PE4*/

#define  GPIO_IDR_OFFSET  (GPIOE_BASE+0x10 - PERIPH_BASE)

#define  GPIO_ODR_OFFSET  (GPIOE_BASE+0x14 - PERIPH_BASE)

 

#define  GPIO_BitNumber   4

#define  GPIO_OUT_BB     (PERIPH_BB_BASE + (GPIO_ODR_OFFSET * 32) + (GPIO_BitNumber * 4))    

#define  GPIO_OUT_DATA           *(__IO uint32_t *)GPIO_OUT_BB

 

#define  GPIO_IN_BB     (PERIPH_BB_BASE + (GPIO_IDR_OFFSET * 32) + (GPIO_BitNumber * 4))    

#define  GPIO_IN_DATA           *(__IO uint32_t *)GPIO_IN_BB

 

#define  GPIO_DIR_REG     *(__IO uint32_t *)(GPIOE_BASE+0X00)

 

 

#define PE4_SET()  GPIO_OUT_DATA = 1   //!< IO写1

#define PE4_CLR()  GPIO_OUT_DATA = 0   //!< IO写0

#define PE4_OUT()  GPIO_DIR_REG = (((GPIO_DIR_REG) & 0xFFFFFCFF) | 0x00000100)  //!IO输出

#define PE4_IN()   GPIO_DIR_REG = ((GPIO_DIR_REG) & 0xFFFFFCFF)  //!IO输入

#define PE4_READ() GPIO_IN_DATA   //!将上述整理为通用GPIO驱动代码


*GPIO配置---PXX*/

#define GPIO_SET(GPIOx_BASE,GPIO_BitNumber)   *(__IO uint32_t *)((PERIPH_BB_BASE + ((GPIOx_BASE+0x14 - PERIPH_BASE) * 32) + (GPIO_BitNumber * 4))) = 1   //!< IO写1

#define GPIO_CLR(GPIOx_BASE,GPIO_BitNumber)   *(__IO uint32_t *)((PERIPH_BB_BASE + ((GPIOx_BASE+0x14 - PERIPH_BASE) * 32) + (GPIO_BitNumber * 4))) = 0   //!< IO写0

 

#define GPIO_OUT(GPIOx_BASE,GPIO_BitNumber)   *(__IO uint32_t *)(GPIOx_BASE+0X00)  &= ~(GPIO_MODER_MODER0 << (GPIO_BitNumber * 2));

                                              *(__IO uint32_t *)(GPIOx_BASE+0X00) |= ((GPIO_Mode_OUT) << (GPIO_BitNumber * 2)); //!IO输出

 

#define GPIO_IN(GPIOx_BASE,GPIO_BitNumber)    *(__IO uint32_t *)(GPIOx_BASE+0X00)  &= ~(GPIO_MODER_MODER0 << (GPIO_BitNumber * 2));

                                              *(__IO uint32_t *)(GPIOx_BASE+0X00) |= ((GPIO_Mode_IN) << (GPIO_BitNumber * 2));  //!IO输入

                                              

#define GPIO_READ(GPIOx_BASE,GPIO_BitNumber)  *(__IO uint32_t *)((PERIPH_BB_BASE + ((GPIOx_BASE+0x10 - PERIPH_BASE) * 32) + (GPIO_BitNumber * 4)))   //!调用方法


GPIOx_BASE:GPIOE_BASE基地址,而不是GPIOE


GPIO_BitNumber:4,而不是GPIO_Pin_4


GPIO_OUT(GPIOE_BASE,4);

开源代码:


https://github.com/strongercjd/STM32F207VCT6/tree/master/08-GPIO-bit-band


上一篇:STM32 中断详解

下一篇:STM32 GPIO详解

推荐阅读

史海拾趣

C-TON Industries公司的发展小趣事

随着公司规模的扩大,C-TON意识到单凭自己的力量难以在竞争激烈的电子行业中立足。于是,公司开始积极寻求与其他企业的战略合作。通过与一家知名的电子产品制造商建立合作伙伴关系,C-TON成功将其芯片技术应用于对方的产品中,进一步扩大了市场份额。这一合作不仅提升了C-TON的知名度,也为其带来了稳定的订单和收入来源。

虹冠电子(Champion)公司的发展小趣事

虹冠电子注重全球市场的拓展,通过建立完善的营销网络和合作伙伴关系,将产品销往世界各地。公司在新竹、汐止以及美国硅谷等地设有研发中心和生产基地,能够为客户提供及时、高效的技术支持和服务。同时,虹冠电子还与国内优秀的晶圆封测大厂等合作伙伴建立了紧密的合作关系,共同推动电源管理技术的发展和应用。

Eska公司的发展小趣事

Eska公司成立于荷兰北部,自创立之初就专注于造纸业务。公司引进了先进的造纸技术和设备,建立了两座灰板纸厂,配备了3台技术领先的纸板机和2台造纸机。凭借高效的生产能力和优质的产品质量,Eska迅速在造纸行业中崭露头角。Eska的灰板纸以“Eska(青蛙)”品牌销售,因其优良且稳定的质量而享誉全球。

AXTAL公司的发展小趣事

近年来,电子行业面临着诸多市场挑战与机遇。在这场全球供应链的博弈中,AXTAL公司展现出了敏锐的市场洞察力和应变能力。面对中国发布的镓锗禁令,AXTAL巧妙地抓住了第一个漏洞,通过在中国设立的子公司申请出口镓锗的许可证,成功绕过了禁令的限制。这一睿智之举不仅为公司打开了新的市场窗口,也彰显了公司在应对市场挑战时的灵活性和创造力。

AEC Design公司的发展小趣事
为了进一步提升技术实力和市场竞争力,AEC Design公司积极寻求与其他企业或研究机构的战略合作。通过与上下游企业建立紧密的供应链合作关系,公司确保了原材料的稳定供应和产品质量的有效控制。同时,与高校和研究机构的合作则为公司带来了源源不断的技术创新动力。
COILCRAFT公司的发展小趣事

为了更好地服务全球客户,COILCRAFT公司在北美、亚洲和欧洲等地建立了制造、分销和工程设施。这一全球布局使得公司能够快速地响应客户需求,提供高效的服务和运输。同时,COILCRAFT还拥有丰富的网络工具库和免费评估样品政策,这些措施简化了部件选择过程,提高了客户的满意度。

问答坊 | AI 解惑

STM32 中断与嵌套NVIC 快速入门。

TM32 中断与嵌套NVIC 快速入门。 我也是靠看这本书才弄懂的: Cortex-M3 权威指南 Joseph Yiu 著 宋岩 译 其实很简单。 //CM3 有 最多240个中断(通常外部中断写作IRQs),就是 软件上说的 IRQ CHANAELx(中断通道号x) 每个中断有自己的可编 ...…

查看全部问答>

请好心人帮忙下载一下软件,然后发我! 万分谢谢.

http://www.96qd.com/driver/softdown.asp?softid=166173 请哪位好心人帮我下载这个驱动程序,之后压缩打包后发我一下, 万分谢谢! 我的email: hkds2008@163.com 在线等!…

查看全部问答>

如何对第二代身份证的内部信息进行过读写

请问各位大虾,有没有人对第二代身份证的内部信息进行过读写,或者进行二次开发,如何读写,有没有什么相关的设备。…

查看全部问答>

【我给xilinx资源中心做贡献】Xilinx Spartan-3E开发板相关资源

概述 Spartan-3E初学者电路板向工业上基于Spartan-3E电路设计平台提供了最受欢迎最超值的产品。板载的50万门Spartan 3E FPGA可以构建32位RISC处理器和DDR接口。开发板上拥有的Xilinx Platform Flash、USB和JTAG并 ...…

查看全部问答>

急请教,这是哪个公司的产品!谢谢

最近在弄气敏型烟雾监测装置,手里面有一个传感器元件,性能测试不错,但是不知道是哪个厂家的产品,只能从产品上面看到企业logo。 急求各位大侠,哪个知道是哪个公司的产品吗?…

查看全部问答>

MSP430F149内部资源(一)

一、上电复位 POR 和 上电清除 PUC 二、低功耗控制 但系统时钟发生器基本功能建立之后,CPU内的状态寄存器SR中的SCG1、SCG2、OscOff、CpuOff是低功耗的重要控制位; 系统工作模式一共有6种,1种活动模式和5种低功耗模式; 可以通过设置控制位使 ...…

查看全部问答>

430 IO 数字量

430的IO都是数字量类型的管脚,如果电流经模数转换后的数字量可不可以1个输出接一个IO口呢,在程序里循环扫描这几个IO口,来获得这个数字量,不知道这个想法是否可行,不行的话,麻烦讲解一下 …

查看全部问答>

晒WEBENCH设计的过程+500流明LED设计

本帖最后由 游乐场 于 2014-7-16 21:51 编辑 普通的白炽灯要有500流明的光通量大概需要60W,而500流明的LED大概在5W到20W左右,每一盏LED都比白炽灯更加节能。现在用WEBENCH设计一款500流明光通量的LED。第一步  在LED设计框选择输入设 ...…

查看全部问答>

一点点小分享

偶尔看到群里各种没法编译,我觉得要上阵必须要磨好枪,不过我最近很忙,没什么时间学习瑞萨,只能靠挤时间来完成。 我的版本是2.02的,也能完成大家所要求的大部分操作。在此再次说明一下,单纯使用光碟里面的程序是不能完成下载程序的,就是说使 ...…

查看全部问答>

求个STM32编译MDK 或 IAR。

求个STM32编译MDK 或 IAR。 …

查看全部问答>