位域使用时虽然语句比较简洁,但是编译成汇编之后,其实比较麻烦,并且存在一定的风险。
例如:
C语言: PWMx->TBCTL_b.HSPCLKDIV = 0; // 将PWMx->TBCTL寄存器的7,8,9位写0
汇编 :
0x00000F60 6801 LDR r1,[r0,#0x00] ; 将r0地址偏移0个地址之后存储的值赋值给r1寄存器 ,即 r1 = PWMx->TBCTL
0x00000F3E 2207 MOVS r2,#0x07 ; 将r2寄存器等于7,即 r2= 0x07
0x00000F40 01D2 LSLS r2,r2,#7 ; 将r2寄存器等于r2寄存器左移7位,即 r2 = r2<<7 = 0x0380
0x00000F42 4391 BICS r1,r1,r2 ; 将r1寄存器等于r1寄存器第7,8,9位清零, 参考
BIC
(位清除)指令
0x00000F44 6001 STR r1,[r0,#0x00] ;将r0偏移0个地址中的值放到以r1中的值为地址的存储单元去。
从上面例子可以看出位域操作某个存储单元的某一位或者几位都是通过先把该单元读取出来保存,再把要设置的位清空再赋值,最后把该值再赋值给存储单元;
风险就在如果程序刚把存储单元(数据0x04)读取保存之后,有其他中断响应之后跳转到中断服务函数中也对该存储单元进行修改(将最高位置1),执行完服务函数之后继续执行设置位复制,再把值赋给存储单元(数据0x04),这时在中断中对存储单元写的无效;
不推荐使用位域,不然程序会遇到bug(大部分时候程序是正常跑的,有时会异常),亲身经历这种bug查到怀疑人生。