您当前的位置: 首页 >  嵌入式

张巧龙

暂无认证

  • 1浏览

    0关注

    1208博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

C语言、嵌入式位操作精华技巧大汇总

张巧龙 发布时间:2019-12-10 13:16:41 ,浏览量:1

最近有点忙,好久没分享笔记了~今天分享关于位操作的一点小笔记。

一、位操作简单介绍

首先,以下是按位运算符:

嵌入式编程中,常常需要对一些寄存器进行配置,有的情况下需要改变一个字节中的某一位或者几位,但是又不想改变其它位原有的值,这时就可以使用按位运算符进行操作。下面进行举例说明,假如有一个8位的TEST寄存器:

当我们要设置第0位bit0的值为1时,可能会这样进行设置:

TEST = 0x01;

但是,这样设置是不够准确的,因为这时候已经同时操作到了高7位:bit1~bit7,如果这高7位没有用到的话,这么设置没有什么影响;但是,如果这7位正在被使用,结果就不是我们想要的了。

在这种情况下,我们就可以借用按位操作运算符进行配置。

对于二进制位操作来说,不管该位原来的值是0还是1,它跟0进行&运算,得到的结果都是0,而跟1进行&运算,将保持原来的值不变;不管该位原来的值是0还是1,它跟1进行|运算,得到的结果都是1,而跟0进行|运算,将保持原来的值不变。

所以,此时可以设置为:

TEST = TEST | 0x01;

其意义为:TEST寄存器的高7位均不变,最低位变成1了。在实际编程中,常改写为:

TEST |= 0x01;

这种写法可以一定程度上简化代码,是 C 语言常用的一种编程风格。设置寄存器的某一位还有另一种操作方法,以上的等价方法如:

TEST |= (0x01 >>

#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个字节 */

示例:

(2)获取某一位:

左右滑动查看全部代码>>>

#define	GET_BIT(x, bit)	((x & (1 > bit)	/* 获取第bit位 */

示例:

2、一个32bit数据的位、字节清零操作

(1)清零某个字节:

左右滑动查看全部代码>>>

#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个字节 */

示例:

(2)清零某一位:

左右滑动查看全部代码>>>

#define	CLEAR_BIT(x, bit)	(x &= ~(1 >>

#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 */

示例:

(2)置位某一位:

左右滑动查看全部代码>>>

#define	SET_BIT(x, bit)	(x |= (1 >>

/* 获取第[n:m]位的值 */
#define BIT_M_TO_N(x, m, n)  ((unsigned int)(x > ((31 - (n)) + (m)))

示例:

这是一个查询连续状态位的例子,因为有些情况不止有0、1两种状态,可能会有多种状态,这种情况下就可以用这种方法来取出状态位,再去执行相应操作。

以上是对32bit数据的一些操作进行总结,其它位数的数据类似,可根据需要进行修改。

三、STM32寄存器配置

STM32有几套固件函数库,这些固件库函数以函数的形式进行1层或者多层封装(软件开发中很重要的思想之一:分层思想),但是到了最里面的一层就是对寄存器的配置。

我们平时都比较喜欢固件库来开发,大概是因为固件库用起来比较简单,用固件库写出来的代码比较容易阅读。

最近一段时间一直在配置寄存器,越发地发现使用寄存器来进行一些外设的配置也是很容易懂的。

使用寄存器的方式编程无非就是往寄存器的某些位置1、清零以及对寄存器一些状态位进行判断、读取寄存器的内容等。

这些基本操作在上面的例子中已经有介绍,我们依旧以实例来巩固上面的知识点(以STM32F1xx为例):

(1)寄存器配置

看一下GPIO功能的端口输出数据寄存器  (GPIOx_ODR) (x=A..E)  :

假设我们要让PA10引脚输出高、输出低,可以这么做:

方法一:

GPIOA->ODR |= 1 ODR &= ~(1 ODR, 10);    /* PA10输出高(置1操作) */
CLEAR_BIT(GPIOA->ODR, 10);  /* PA10输出低(清0操作) */

方法二:

GPIOA->ODR |= (uint16_t)0x0400;   /* PA10输出高(置1操作) */
GPIOA->ODR &= ~(uint16_t)0x0400;  /* PA10输出低(清0操作) */

貌似第二种方法更麻烦?还得去细心地去构造一个数据。

但是,其实第二种方法其实是ST推荐我们用的方法,为什么这么说呢?因为ST官方已经把这些我们要用到的值给我们配好了,在stm32f10x.h中:

这个头文件中存放的就是外设寄存器的一些位配置。

所以我们的方法二等价于:

GPIOA->ODR |= GPIO_ODR_ODR10;   /* PA10输出高(置1操作) */
GPIOA->ODR &= ~GPIO_ODR_ODR10;  /* PA10输出低(清0操作) */

两种方法都是很好的方法,但方法一似乎更好理解。

配置连续几位的方法也是一样的,就不介绍了。简单介绍配置不连续位的方法,以TIM1的CR1寄存器为例:

设置CEN位为1、设置CMS[1:0]位为01、设置CKD[1:0]位为10:

TIM1->CR1 |= (0x1             
关注
打赏
1665727216
查看更多评论
0.0460s