单片机控制寄存器常用C语言方法

一、位操作概述

针对MCU的嵌入是开发中经常涉及到寄存器的操作,例如GPIO配置低寄存器GPIOx_CRL(共32个bit),有时需要改变其中的一位或者几位bit值,同时不能影响其它bit位的值。

例如,需要设置第0位bit=1时,不能简单的设置为:GPIOx_CRL=0x01  ,这样的方法会使得低配置寄存器GPIOx_CRL的其它比特位都置为0。

对于二进制位操作来说:

不管该位原来的值是0还是1,它跟0进行&运算,得到的结果都是0,而跟1进行&运算,将保持原来的值不变;

不管该位原来的值是0还是1,它跟1进行|运算,得到的结果都是1,而跟0进行|运算,将保持原来的值不变。

正确的方法是:

GPIOx_CRL=GPIOx_CRL | 0x01      或者        GPIOx_CRL |= 0x01

或者使用如下方法更加清晰,第几位需要置1就将0x01左移几位。

GPIOx_CRL  |=   (0x01 << 0);

二、位操作具体用法-提取、清零、置1、判断

位操作的用法可以分为四类:

1、寄存器数据的位、字节读取操作:1)提取某一个字节,2)提取某一位;

2、寄存器数据的位、字节清零操作:1)清零某一个字节,2)清零某一位;

3、寄存器数据的位、字节置1操作:  1)将某一个字节置1, 2)将某一位置1;

4、判断某一位或某几位连续的值:1)判断某一位的值,2)判断某几位连续位的值。

1、寄存器数据的位、字节读取操作

1)提取某一个字节

#include <stdio.h>
#include <stdlib.h>
 
#define	GET_LOW_BYTE0(x)	((x >>  0) & 0x000000ff)	/* 获取第0个字节 */
#define	GET_LOW_BYTE1(x)	((x >>  8) & 0x000000ff)	/* 获取第1个字节 */
#define	GET_LOW_BYTE2(x)	((x >> 16) & 0x000000ff)	/* 获取第2个字节 */
#define	GET_LOW_BYTE3(x)	((x >> 24) & 0x000000ff)	/* 获取第3个字节 */
 
int main()
{
   unsigned int a=0x44332211;
 
    printf("0x%x的第0个字节为:0x%x\n",a,GET_LOW_BYTE0(a));
    printf("0x%x的第1个字节为:0x%x\n",a,GET_LOW_BYTE1(a));
    printf("0x%x的第2个字节为:0x%x\n",a,GET_LOW_BYTE2(a));
    printf("0x%x的第3个字节为:0x%x\n",a,GET_LOW_BYTE3(a));
    return 0;
}

2)提取某一位

#include <stdio.h>
#include <stdlib.h>
 
#define	GET_BIT(x, bit)	((x & (1 << bit)) >> bit)	/* 获取第bit位 */
 
int main()
{
   unsigned int a=0x51;
 
    printf("0x%x的对应的二进制是:%x %x %x %x %x %x %x %x \n",a,GET_BIT(a,7),GET_BIT(a,6),GET_BIT(a,5),GET_BIT(a,4),GET_BIT(a,3),GET_BIT(a,2),GET_BIT(a,1),GET_BIT(a,0));
    printf("0x%x的第0位的值是:%x\n",a,GET_BIT(a,0));
    printf("0x%x的第1位的值是:%x\n",a,GET_BIT(a,1));
    printf("0x%x的第2位的值是:%x\n",a,GET_BIT(a,2));
    printf("0x%x的第3位的值是:%x\n",a,GET_BIT(a,3));
    printf("0x%x的第3位的值是:%x\n",a,GET_BIT(a,4));
    printf("0x%x的第3位的值是:%x\n",a,GET_BIT(a,5));
    printf("0x%x的第3位的值是:%x\n",a,GET_BIT(a,6));
    printf("0x%x的第3位的值是:%x\n",a,GET_BIT(a,7));
    return 0;
}

2、寄存器数据的位、字节清零操作

1)清零某一个字节
#include <stdio.h>
#include <stdlib.h>
 
 
#define	CLEAR_LOW_BYTE0(x)	(x &= 0xffffff00)	/* 清零第0个字节 */
#define	CLEAR_LOW_BYTE1(x)	(x &= 0xffff00ff)	/* 清零第1个字节 */
#define	CLEAR_LOW_BYTE2(x)	(x &= 0xff00ffff)	/* 清零第2个字节 */
#define	CLEAR_LOW_BYTE3(x)	(x &= 0x00ffffff)	/* 清零第3个字节 */
 
int main()
{
   unsigned int a=0x44332211;
   unsigned int b=0x44332211;
   unsigned int c=0x44332211;
   unsigned int d=0x44332211;
 
    printf("0x44332211的第0个字节清零后的值是:%#.8x\n",a,CLEAR_LOW_BYTE0(a));
    printf("0x44332211的第1个字节清零后的值是:%#.8x\n",b,CLEAR_LOW_BYTE1(b));
    printf("0x44332211的第2个字节清零后的值是:%#.8x\n",c,CLEAR_LOW_BYTE2(c));
    printf("0x44332211的第3个字节清零后的值是:%#.8x\n",d,CLEAR_LOW_BYTE3(d));
    return 0;
}

2)清零某一位
#include <stdio.h>
#include <stdlib.h>
 
 
#define	CLEAR_BIT(x, bit)	(x &= ~(1 << bit))	/* 清零第bit位 */
 
int main()
{
   unsigned int a0=0x81;
   unsigned int a1=0x82;
   unsigned int a2=0x84;
   unsigned int a3=0x88;
 
 
    printf("0x81的第0个bit清零后的值是:%#.2x\n",a0,CLEAR_BIT(a0, 0));
    printf("0x82的第1个bit清零后的值是:%#.2x\n",a1,CLEAR_BIT(a1, 1));
    printf("0x84的第2个bit清零后的值是:%#.2x\n",a2,CLEAR_BIT(a2, 2));
    printf("0x88的第3个bit清零后的值是:%#.2x\n",a3,CLEAR_BIT(a3, 3));
    return 0;
}

3)清零某几位

如将a中的bit2和bit3位清零。

a &= ~(3<<2*1);    // a &= ~(数值<<每组位数*组编号)

如果想要对a中的连续几位进行操作,首先要将二进制位分组,在这里我们将其按照两个位一组的方式进行分组,也就是bit0bit1为第0组,bit2bit3为第1组,bit4bit5为第2组,bit6bit7为第3组。现在我们想要对bit2,bit3位清零,也就是对第1组进行清零。感兴趣的可以思考一下为什么要分组这个问题,因为我们马上就会用到。

3的二进制为:0011
(3<<21)我们现在将其左移两位得到 0000 1100 b
~(3<<2
1)得到 —————————>1111 0011 b
在对a做位与运算 a = (1001 1111 b)&(1111 0011 b)=1001 0011 b

在这里大家可能有点懵的点在于括号里的321都是个啥?一个一个来解释:
这里的"1"就是我们刚才分的组号,如果想要清除第3组bit6bit7,那么将这里的1换成3即可。"2"是每组的位数,每组有两个二进制位,如果我们在分组的时候分成4个一组,那么这里就是4。"3"是我们的一组两个数二进制表示 11 时的值,如果是一组4个,此处就是二进制数1111 b了。

3、寄存器数据的位、字节置1操作

1)将某一个字节置1
#include <stdio.h>
#include <stdlib.h>
 
 
#define	SET_LOW_BYTE0(x)  (x |= 0x000000ff)	/* 第0个字节置1 */
#define	SET_LOW_BYTE1(x)  (x |= 0x0000ff00)	/* 第1个字节置1 */
#define	SET_LOW_BYTE2(x)  (x |= 0x00ff0000)	/* 第2个字节置1 */
#define	SET_LOW_BYTE3(x)  (x |= 0xff000000)	/* 第3个字节置1 */
 
 
 
int main()
{
   unsigned int a0=0x11111111;
   unsigned int a1=0x11111111;
   unsigned int a2=0x11111111;
   unsigned int a3=0x11111111;
 
 
 
    printf("0x11111111的第0个字节置1后的值是:%#.2x\n",a0,SET_LOW_BYTE0(a0));
    printf("0x11111111的第1个字节置1后的值是:%#.2x\n",a1,SET_LOW_BYTE1(a1));
    printf("0x11111111的第2个字节置1后的值是:%#.2x\n",a2,SET_LOW_BYTE2(a2));
    printf("0x11111111的第3个字节置1后的值是:%#.2x\n",a3,SET_LOW_BYTE3(a3));
    return 0;
}

 2)将某一位置1
#include <stdio.h>
#include <stdlib.h>
 
 
#define	SET_BIT(x, bit)	(x |= (1 << bit))	/* 置位第bit位 */
 
 
 
int main()
{
   unsigned int a0=0x10;
   unsigned int a1=0x10;
   unsigned int a2=0x10;
   unsigned int a3=0x10;
 
 
 
    printf("0x10的第0个bit置1后的值是:%#.2x\n",a0,SET_BIT(a0, 0));
    printf("0x10的第1个bit置1后的值是:%#.2x\n",a1,SET_BIT(a1, 1));
    printf("0x10的第2个bit置1后的值是:%#.2x\n",a2,SET_BIT(a2, 2));
    printf("0x10的第3个bit置1后的值是:%#.2x\n",a3,SET_BIT(a3, 3));
    return 0;
}

 3)将某几位置1
// 关键点:我们需要一个算式来得到(m-n+1)个1的十六进制数
// 第1步,先得到32个1: ~0U	(~按位取反得到32个1,如果直接1U那么就只有bit0位1)
// 第2步,将得到的数右移x位即可得到(m-n+1)个: (~0U) >> (32-(m-n+1)) 或 ~(~0U<<(m-n+1))
#define SET_BIT_N_M(x, n, m)	(x | (((~0U) >> (32-(m-n+1))) << (n-1)))
#define SET_BIT_N_M(x, n, m)	(x | ~(~0U<<(m-n+1))<<(n-1))

4、判断某一位或某几位连续的值

1)判断某一位的值
#include <stdio.h>
#include <stdlib.h>
 
 
#define	SET_BIT(x, bit)	(x |= (1 << bit))	/* 置位第bit位 */
 
 
 
int main()
{
   unsigned int a=0x66;//二进制 01100110
    int i;
     for(i=0;i<8;i++)
    {
        if(a&(1<<i))//关键点
        {
            printf("0x66二进制的bit%d位的值是1\n",i);
        }
        else
        {
            printf("0x66二进制的bit%d位的值是0\n",i);
        }
    }
 
 
    return 0;
}

要判断第几位的值,if里就左移几位(不要移过头了)。在针对MCU的嵌入式编程中,可通过这样的方式来判断寄存器的状态位是否被置位。

2)判断某几位连续位的值

#include <stdio.h>
#include <stdlib.h>
 
 
/* 获取第[n:m]位的值 */
#define BIT_M_TO_N(x, m, n)  ((unsigned int)(x << (31-(n))) >> ((31 - (n)) + (m)))
 
 
int main()
{
   unsigned int a=0xAA;//二进制 10101010
   unsigned int value=0;
   int i,j;
    for(i=0,j=1;i<8,j<8;i++,j++)
    {
         value=BIT_M_TO_N(a, i, j);
        switch(value)
        {
            case 0:
                printf("0xAA的bit[%d,%d]位是:00\n",i,j);
            break;
            case 1:
                printf("0xAA的bit[%d,%d]位是:01\n",i,j);
            break;
            case 2:
                printf("0xAA的bit[%d,%d]位是:10\n",i,j);
            break;
            case 3:
                printf("0xAA的bit[%d,%d]位是:11\n",i,j);
            break;
        }
    }
 
    return 0;
}

这只是一个查询连续2个状态位的例子,当连续bit位大于2时,会有多种状态,这种情况下就可以用这种方法来取出状态位,再去执行相应操作。

作者:A星空123

物联沃分享整理
物联沃-IOTWORD物联网 » 单片机控制寄存器常用C语言方法

发表回复