嵌入式的寄存器操作

文章参考《嵌入式寄存器操作-----C语言位操作的学习》(https://blog.csdn.net/zeroubuntu/article/details/19617871)。
通过一个按键的实例来看嵌入式中的位操作。
转载如下:

a |= 1<<x         //第x位写1
a &=~(1<<x)        //第x位写0
(a &(1<<x)) == (1<<x)       //判断1,等号左边括号不能省略
(a& (1<<x))==0        //判断0
a>>n              //除法a/ 2^n
a<<n              //乘法a*2^n

转载如下:
学习嵌入式的过程中发现。C的位操作比较多,现根据资料整理如下。待以后查阅。

根据芯片手册配置寄存器的过程中,时常碰到要设置某一位或某几位的数据,根据情况置0或者置1。而对于该寄存器其他的位,我们不关心也不应该关心(谁知道其他位会不会有其他用途)。

这个时候,位操作是非常方便的。

寄存器的位操作主要是将特定位置0或者置1。一般情况是先擦除相应的位(就是置0),然后再置1。

1,如果只置为其中某一位为1

a &= ~(1<<x) //将x位置0

b | = (1<<x) //将x位置1

2,如果一下置位多个位,下面的方法可以用

a &=~0001 1100 //第2-4置0

b | =0001 1100 //第2-4位置1

3,但是这个多位的用法犯了上面说过的问题,我们关心了其他位,因此,推荐的用法应该如下。

a &=~((1<<2) | (1<<3) | (1<<4))//将2-4置0

a|=(1<<2) | (1<<3) | (1<<4)//将2-4置1

这个代码初看起来比较上面的复杂和不便于阅读,但是实际情况不是如此,因为我们通常将(1<<2)这种代码定义为宏操作。具体可以看下面我贴出的代码。

4,如何判断寄存器某一位是0还是1。寄存器底层操作中,时常用到判断某个寄存器的状态,即读入某个寄存器的值。

a & (1<<x) //与操作,如果相应位是0,则结果是0,如果相应位是1,则结果是1.

b | (0<<x) //或操作,如果相应位是0,则结果是0,如果相应位是1,则结果是1.

这两个应该是没有区别的。

下面就是根据TQ2440改写的按键LED程序,位操作使用宏定义。

/*按键控制LED灯*/
/*观察原理图得到,GPB5,6,7,8分别连接led1,2,3,4;低电平点亮*/
/*按键1,2,3,4分别对应EINT1,4,2,0外部中断,低电平按下*/
/*这里我们不用中断,EINT0-4对应GPF0-4*/
/*思路:不断扫描GPF0-4(引脚输入使能),相应输出GPB5-8,各对应关系如下*/
/*GPB	LED		KEY		EINI	GPF*/
/*5		1		1		1		1*/
/*6		2		2		4		4*/
/*7		3		3		2		2*/
/*8		4		4		0		0*/
/*这里主要注意下学习位操作*/
#include<stdio.h>
 
/*配置寄存器地址*/
/*GPBCON地址为(unsigned long*)0x56000010*/
/*所以GPBCON数据为(*(unsigned long*)0x56000010)*/
#define GPBCON   (*(volatile unsigned long*)0x56000010)
#define GPBDAT   (*(volatile unsigned long*)0x56000014)
 
#define GPFCON   (*(volatile unsigned long*)0x56000050)
#define GPFDAT   (*(volatile unsigned long*)0x56000054)
 
/*寄存器擦除某一位为0*/
/*这是16位寄存器,每一设置有2个二进制数位,因此一次擦除2位,3=0b11*/
#define GPB5_MSK	(3<<(5*2))		//位左移10位,[11.10]位为1,其余为0
#define GPB6_MSK	(3<<(6*2))
#define GPB7_MSK	(3<<(7*2))
#define GPB8_MSK	(3<<(8*2))
 
#define GPF0_MSK	(3<<(0*2))
#define GPF1_MSK	(3<<(1*2))
#define GPF2_MSK	(3<<(2*2))
#define	GPF4_MSK	(3<<(4*2))
 
/*设置引脚为输入功能,相应位置00*/
#define GPF0_IN		(0<<(0*2))
#define GPF1_IN		(0<<(1*2))
#define GPF2_IN		(0<<(2*2))
#define GPF4_IN		(0<<(4*2))
 
/*设置引脚为输出功能,相应位置01*/
#define GPB5_OUT	(1<<(5*2))
#define GPB6_OUT	(1<<(6*2))
#define GPB7_OUT	(1<<(7*2))
#define GPB8_OUT	(1<<(8*2))

int main(void)
{
    
    
	/*配置引脚功能,GPB[5,8]为输出,GPF[0,3]为输入*/
	GPBCON &=~(GPB5_MSK | GPB6_MSK | GPB7_MSK | GPB8_MSK);
	GPBCON |=GPB5_OUT | GPB6_OUT | GPB7_OUT | GPB8_OUT;
	GPFCON &=~(GPF0_MSK | GPF1_MSK | GPF2_MSK | GPF4_MSK);
	GPFCON |=GPF0_IN | GPF1_IN | GPF2_IN | GPF4_IN;
	
	unsigned long key_date;
 
	/*低电平表示按键按下*/
	while(1)
	{
    
    
		key_date=GPFDAT;
 
		if(key_date & (1<<0))		//判断第0位是否为1,对应按键4,GPB8
			GPBDAT |=(1<<8);		//是1,按键未按下,相应LED置1,不点亮
		else
			GPBDAT &=~(1<<8);		//是0,按键按下,相应LED低电平,点亮
		
 
		if(key_date & (1<<1))     //GPF1,KEY1,LED1,GPB5
			GPBDAT |=(1<<5);      
	    else
			GPBDAT &=~(1<<5);
			
		if(key_date & (1<<2))     //GPF2,KEY3,LED3,GPB57
			GPBDAT |=(1<<7);        
		else
			GPBDAT &=~(1<<7);
		
		if(key_date & (1<<4))     //GPF4,KE21,LED2,GPB6
			GPBDAT |=(1<<6);        
		else
			GPBDAT &=~(1<<6);
	}
}

最后还有自己想到的一些问题:
1,配置寄存器地址的时候,这个地址是怎么计算出来的?
这个地址应该是作者自定义的一个地址,不太清楚的是下面的问题,
2,这里怎么就数据地址就是(配置地址+4)了?
在这里插入图片描述

// 指针(*)、取地址(&)、解引用(*)
(volatile unsigned long*)0x56000010  //强制类型转换
(*(volatile unsigned long*)0x56000010)  //解引用

喵喵
https://blog.csdn.net/Ice_teapoy/article/details/78993878

猜你喜欢

转载自blog.csdn.net/skhhongtu/article/details/122324595