我对位带操作的新认识

    接触STM32快一年了,前段时间才偶尔听到一个叫位带操作的概念... 惭愧惭愧

    经过查阅前辈们的经验和相关资料,有了点认识在此记录一下,有不正确的地方请多多指教

/***********************************************万能分割线*************************************************/

    网上已有很多前辈细致介绍了何为位带操作,我看后受益匪浅,经过自己的理解,在此,个人觉得有必要回归英文技术手册,定位到这个操作最重要的一个特性,也是它最值得被使用的地方:

    高亮处很清晰地描述了位带操作的“原子”特性(概念不清晰的自行查阅一下),即在位带操作过程中硬件层面不允许被中断线程打断。  我们知道在一个项目中会出现main进程下可能还有很多中断线程,中断出现的机制为程序处理“突发”事件带来很大的便捷,提高了效率,但是也带来一个隐患:变量的原子操作问题(姑且这么个叫法吧,没找着术语╮(╯▽╰)╭)

    这个问题最大的体现就是一个变量进入中断服务函数被修改后,退出时被原进程(打断前在执行的进程)干扰而未完成真实修改,一方面是编译器的优化,另外就是在汇编层面这个修改的指令是可以被打断的,放入一个例子吧:

    这是在汇编语言中同一个修改变量的指令,左侧是没有位带操作的,四条汇编指令每条的中间都有间隔,实际上都是可以被打断的,假设在左侧任一箭头处被打断进入服务函数修改同一个变量,退出时回到箭头下方继续执行,我们会发现箭头下方的指令也在修改原地址处的值!这可能导致中断修改的又被改了回去!  而图的右侧可以知道使用了位带操作即实现了“原子操作”,想要打断此修改只能在箭头处打断,而无论在哪个箭头处打断退出,变量可以完成整个:地址设置--修改值--写回地址 流程,这保证了变量安全被使用,不会因为是共享资源而被各线程强占

/*****************************************************分割一下******************************************************/

    明白了位带操作最大的好处,是时候深入研究什么是位带操作了(笔者发现M3,M4内核都有位带操作的描述,而未在M0手册上找到,不知是不是本来就没有)

    用过51的朋友都知道,在操作51的IO口时,用其定义好的sbit P1_0 = P1^0; P1_0 = 1;就可以对P1.0口置1,用起来贼方便,可是STM32中一大堆GPIO寄存器意味着不能愉快地这么简单写了......    然而ARM内核还是提供了另一套可以这么写的方式,就是使用位带区:每个GPIO寄存器都有一个唯一的内存地址,这些地址都在位带区中有定义,想要操作它就需要在RAM中先改写这个地址的内容,最后再从RAM写回寄存器。内核不允许我们直接修改IO口寄存器地址的值,却也提供了一条路,就是用位带别名区,把位带区的地址映射到别名区,修改别名区的内容就等于修改了位带区地址里的内容

    STM32的位带别名区有两个,如下图:

    这两个别名区用法差不多,都可以对相应的位带区内容进行修改

   

上图是STM32F1手册中给的映射公式和简单的举例,没有加以详细的说明,接下来说说我的理解方式,这里放一张很多前辈都用过的图,我发现这张图足以解释并理解公式的含义了:

    我们先不看手册举的例子,假设我要设置图中0X20000000的第1个bit为1,来理解使用位带操作要知道些什么,这里要交代一下,位带区的1个字节(8bit)地址对应位带别名区4个字节(32bit)的地址,要时刻记住这个前提!

①知道要设置的寄存器地址是什么

②确定要设置寄存器的哪一位

③知道该寄存器地址对应的位带别名区地址是什么

④找到位带别名区的基地址和位带区基地址

    经过以上几步来看看我们要配置些什么:

bit_word_addr = 位带别名区的基地址 + (要设置的地址 - 要设置地址所在位带区的基地址)* 32 + (要设置地址的位数 * 4),好啦,翻译成代码就是:

bit_word_addr = 0X22000000 + (0X20000000 - 0X20000000) * 32 + (1 * 4),由此可以定位到要位带操作的地址为bit_word_addr = 0X22000000 + 4 = 0X22000004,也就是图中第3编号的0X22000004地址

    这里乘以32是为了定位到要设置位的寄存器地址在位带别名区的地址,定位到寄存器在别名区的地址后,再按照位带区一个位占别名区四个位的规律定位到要设置的位在别名区的哪个地址,这句话理解了这个公式也就看懂啦

    当我们要操作一个地址,写入值进去时要做的一件事就是把这个地址变成指针指向的地址来操作这个指针:

*( (volatile unsigned long)* (addr) ),addr即为上面计算出来的地址,为了方便通常都会封装这步得到下面大家喜闻乐见的样子:

#define BITBAND(addr, bitnum) (0X22000000 + (addr - 0X20000000) * 32 + (bitnum * 4))

#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))

#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

至此,我们想设置一个GPIO口时,可以用库函数已经封装好地址的GPIO->ODR,传入要操作的位即可咯,用起来跟51一样舒服

BIT_ADDR(GPIO->ODR,5) = 1; 搞定,喜欢更简洁的朋友还能再继续封装自己喜欢用的样子,位带操作就这样实现了,既可以简化IO设置的问题,还有安全保证,一举两得

  • 28
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论
STM32F407的位带操作是指通过位带别名区域来对特定的位进行操作,以提高代码的执行效率和简化编程过程。 在STM32F407中,位带别名区域是一段特殊的内存区域,用来映射特定位的地址,以此来实现对这些位的直接访问和修改。通过位带操作,我们可以通过直接读取和写入一个位的别名地址来实现对该位的操作,而不需要进行位运算或移位操作位带别名区域的地址在内存映射中通常是高于SRAM的地址范围,所以它不会和其他数据冲突。在使用位带操作时,首先需要确定要操作的位在内存中的位置,并计算其别名地址。然后,可以通过读取或写入该别名地址来操作这个位。 位带操作的主要优势是提高代码的执行效率。因为在执行位带操作时,可以直接换位别名地址的读写操作,从而避免了对位进行逐位运算的过程,大大节省了时间。此外,位带操作还可以简化代码的书写,使代码更加清晰和易读。 需要注意的是,位带操作只能在位可选操作的寄存器或内存位置上进行。对于不支持位带操作的寄存器或内存位置,仍然需要使用位运算或移位操作。另外,由于位带别名区域的映射地址与SRAM相隔较远,所以在使用位带操作时需要额外的地址计算,这也可能增加了代码的复杂性。 总而言之,STM32F407的位带操作是一种通过位带别名区域来对特定位进行操作的方法。它可以提高代码执行效率和简化编程过程,但需要在位可选操作的寄存器或内存位置上进行,并需要进行额外的地址计算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kx_Kevin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值