研究了一整晚,终于搞明白了一点东西。谨以此文献给像我一样彻头彻尾的软件门外汉。。。们
如果我文中描述有错误,欢迎各路神仙指正
*******************************************************
范例 MCU: Atmega328p
关于GPIO的设置,我们经常看见这样的语法
DDRB |= (1 << DDB4);
PORTB |= (1 << PORTB4);
PORTB &= ~(1 << PORTB4);
啥意思?
首先是要看datasheet (Atmega328p)
![93f4b1d11071686af97c57ae9ca79d48.png](https://i-blog.csdnimg.cn/blog_migrate/c76bc3c6e74ed5b8a5d61b5f9cdb5d8a.jpeg)
DDRB,PORTB,这些看上去是一些普通的变量,实际上在avr/iom328p.h头文件里面都已经被宏定义了
#define DDRB _SFRIO(0x04)
也就是说, DDRB其实就是阿拉伯数字0x04(16进制),表示SRAM地址0x04,也就是Atmega328p芯片PortB端口(共8个)的GPIO方向设置register
DDRB |= (1<<DDB4); 这一行简单的代码,其实有多重意思
意思一:PortB这8个端口的第五个,即DDB4,无论它原来的状态如何,它都要被写入1,即为output
意思二:PortB除了DDB4的其他7个端口的方向,维持原来的状态不变(这一点非常重要)
那么这个变量赋值语法是怎么用一行完成的
- 在C语言里面 DDRB |= (1<<DDB4) 可以等效表达成 DDRB = DDRB | (1<<DDB4),意思是新DDRB的值 等于原来的DDRB"或" (1<<DDB4)的值
- DDB4是什么?DDB4 其实也是被iom328p.h 里面宏定义了#define DDB4 4,其实DDB4就是阿拉伯数字4,所以(1<<DDB4) 在这里和 (1<<4)是一个意思
- 那(1<<DDB4) 或者说 (1<<4) 又是什么鬼?这其实是一个移位语法,我们把这个"1"换算成8位二进制数字,1即 0000 0001,1<<4就表示0000 0001 往"左"挪4位,结果就0000 0001就变成了 0001 0000,我们现在仔细看,新的值0001 0000 里面的这个"1"处于哪个位置,是不是正好是DDRB这个8位register,从右(低位)往左(高位)数,第五个(如下图),它是不是正好就是DDB4的位置?
![2777ced813c52ee8f0a6798ade422ff0.png](https://i-blog.csdnimg.cn/blog_migrate/c54ec42e9b1ba45b7cdd0642ea132669.jpeg)
这也意味着,1<<4就变了 0001 0000,把这个新的值带入到上面的变量赋值
DDRB | (1<< 4) 就变成了 DDRB | 0001 0000
现在我们把DDRB 也换算成一个8bit的二进制,xxxx xxxx
"x"表示原来的状态,无论是0还是1,通过逻辑门"或"上一个0,还是x,即状态不改变,"或"上一个1,即状态一定被改变成1
那么新的DDRB的值就变成了 xxx1 xxxx
现在大家懂了吗,通过DDRB |= (1<<4),这样一个巧妙的移位,把DDRB的第5个bit(即DDB4)变成了1,而其他bit维持原状态不变,即PORTB上的第5个PIN被设置成output,其他的PIN保持原状态不变
同理,PORTB |= (1 << PORTB4); 表示,PORTB上的PORTB4号pin被强制输出HIGH,其他pin维持原状态不变
最后,PORTB &= ~(1 << PORTB4); 这个语法和上边的"或"门正好相反
利用"与门" 符号 &,还有"非门"符号 ~,利用相同的移位原理,把PORTB上的第5个pin,即PORTB4强制变成LOW,而其他的pin维持原状不变
简单来说,如果不用汇编,刚才那样的移位应该来说是效率最高的语法了