下面分享关于位操作的一些笔记:

一、位操作简单介绍

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

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

第几位要置1就左移几位。

同样的,要给TEST的低4位清0,高4位保持不变,可以进行如下配置:

TEST &= 0xF0;

二、嵌入式中位操作一些常见用法

1、一个32bit数据的位、字节读取操作

(1)获取单字节:

#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)	/* 获取第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 << bit))	/* 清零第bit位 */

示例:

3、一个32bit数据的位、字节置1操作

(1)置某个字节为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 << bit))	/* 置位第bit位 */

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

(1)判断某一位的值

举例说明:判断0x68第3位的值。

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

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

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

示例:

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

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

三、STM32寄存器配置

STM32有几套固件库,这些固件库函数以函数的形式进行1层或者多层封装(软件开发中很重要的思想之一:分层思想),但是到了最里面的一层就是对寄存器的配置。我们平时都比较喜欢固件库来开发,大概是因为固件库用起来比较简单,用固件库写出来的代码比较容易阅读。最近一段时间一直在配置寄存器,越发地发现使用寄存器来进行一些外设的配置也是很容易懂的。使用寄存器的方式编程无非就是往寄存器的某些位置1、清零以及对寄存器一些状态位进行判断、读取寄存器的内容等。

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

(1)寄存器配置

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

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

方法一:

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

也可用我们上面的置位、清零的宏定义:

SET_BIT(GPIOA->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 << 1)| (0x1 << 5) |(0x2 << 8);

这是组合的写法。当然,像上面一样拆开来写也是可以的。

(2)判断标志位

以状态寄存器(USART_SR) 为例:

判断RXNE是否被置位:

/* 数据寄存器非空,RXNE标志置位 */
if (USART1->SR & (1 << 5))
{
/* 其它代码 */ USART1->SR &= ~(1 << 5); /* 清零RXNE标志 */
}

或者:

/* 数据寄存器非空,RXNE标志置位 */
if (USART1->SR & USART_SR_RXNE)
{
/* 其它代码 */ USART1->SR &= ~USART_SR_RXNE; /* 清零RXNE标志 */
}

四、总结

以上就是本次关于位操作的一点总结笔记,有必要掌握。虽然说在用STM32的时候有库函数可以用,但是最接近芯片内部原理的还是寄存器。有可能之后有用到其它芯片没有像ST这样把寄存器相关配置封装得那么好,那就不得不直接操控寄存器了。

此外,使用库函数的方式代码占用空间大,用寄存器的话,代码占用空间小。之前有个需求,我们能用的Flash的空间大小只有4KB,遇到类似这样的情况就不能那么随性的用库函数了。

最后,应用的时候当然是怎么简单就怎么用。学从“难”处学,用从易处用,与君共勉~

END:以上笔记中如有错误,欢迎指出!谢谢

嵌入式、C语言位操作的一些技巧汇总的更多相关文章

  1. 嵌入式C语言代码的调试技巧

    在项目开发的过程中,不可避免的会遇到调试代码的情况. 刚开始写代码时,我们想看具体执行到哪儿时,往往这么写: printf("***** Code is here! *****\n" ...

  2. 嵌入式C语言优化小技巧

    嵌入式C语言优化小技巧 1 概述 嵌入式系统是指完成一种或几种特定功能的计算机系统,具有自动化程度高,响应速度快等优点,目前已广泛应用于消费电子,工业控制等领域.嵌入式系统受其使用的硬件以及运行环境的 ...

  3. 数据库系统学习(九)-嵌入式SQL语言之基本技巧

    第九讲 嵌入式SQL语言之基本技巧 901 什么是嵌入式SQL语言 交互式SQL语言的局限性 嵌入式SQL语言 交互式和嵌入式语言的对比 高级语言中使用嵌入式语言需要解决的问题 902 程序与数据库连 ...

  4. 第14讲:嵌入式SQL语言(基本技巧)

    一.交互式SQL的局限 & 嵌入式SQL的必要性 专业人员(如DBA)可以熟练地运用交互式SQL语言,但普通用户却不是那么容易上手,所以需要通过数据库应用程序来使用数据库.编写一个可以与数据库 ...

  5. 移动平台3G手机网站前端开发布局技巧汇总

    移动平台3G手机网站前端开发布局技巧汇总 作者:前端开发-武方博   发布:2011-05-10 09:11   分类:移动开发   阅读:120,618 views   7条评论     您或许正在 ...

  6. 移动平台WEB前端开发技巧汇总(转)

    最近我很关注移动前端的知识,但做为一个UI设计师和web前端工作人员没有这个工作环境接触,做为门外汉,网上系统的知识也了了,一直有种雾里看花的感觉,见到本文,我自己是奉为经典.所以我分享之后又专门打笔 ...

  7. 嵌入式C语言不可不用的关键字

    1.static关键字 这个关键字前面也有提到,它的作用是强大的. 要对static关键字深入了解,首先需要掌握标准C程序的组成. 标准C程序一直由下列部分组成: 1)正文段——CPU执行的机器指令部 ...

  8. C语言 位操作

    c语言位操作中需要注意有: 位操作只针对整型和字符型数据 在右移操作中:对无符号数和有符号中的正数补 0:符号数中的负数,取决于所使用的系统:补 0 的称为“逻辑右移”,补 1 的称为“算术右移”. ...

  9. SQL入门(4): 嵌入式SQL语言

    本节讲述内容: 1.嵌入式SQL 语言概述 2.变量声明与数据库连接 3.数据集与游标 4.可滚动游标与数据库的增删改 5.状态捕捉以及错误处理机制 (一)嵌入式SQL语言 之前我们所学的都是交互式S ...

随机推荐

  1. 用深度优先搜索(DFS)解决多数图论问题

    前言 本文大概是作者对图论大部分内容的分析和总结吧,\(\text{OI}\)和语文能力有限,且部分说明和推导可能有错误和不足,希望能指出. 创作本文是为了提供彼此学习交流的机会,也算是作者在忙碌的中 ...

  2. JSONP安全防御要点

    严格安全地实现CSRF方式调用JSON文件:限制Referer.部署一次性token等. 严格安装JSON格式标准输出Content-Type及编码(Content-Type: application ...

  3. unity 截屏总结

    转载与https://www.cnblogs.com/MissLi/p/8005342.html 1.针对指定的相机进行截屏 此中方式要添加yield return waitfortheEndofFr ...

  4. Go语言入门:Hello world

    本文是「vangoleo的Go语言学习笔记」系列文章之一. 官网: http://www.vangoleo.com/go/go-hello-world/ 在上一篇文章你好,Go语言中,我们对Go语言的 ...

  5. 使用asp.net core 3.0 搭建智能小车2

    上一篇中我们把基本的运行环境搭建完成了,这一篇中,我们实战通过树莓派B+连接HC-SR04超声波测距传感器,用c# GPIO控制传感器完成距离测定,并将距离显示在网页上. 1.HC-SR04接线 传感 ...

  6. 学习笔记之javascript编写简单计算器

      感觉自己的的实力真的是有待提高,在编写计算器的过程中,出现了各种各样的问题,暴露了自己的基础不扎实,逻辑思维能力不够,学得知识不能运用到自己的demo中区.先介绍一些这个这个计算器的整体思路.大致 ...

  7. 一个自动管理项目的Makefile(C语言)

    Linux 是所有嵌入式软件工程师绕不过去的坎, makefile 是在Linux系统中绕不过去的坎. 花了几天时间初步学习和了解了makefile 的作用以及功能,并且制作了一个通用型的makefi ...

  8. GPS学习笔记

    预备知识 1. 卫星发送信号时,是以特定频率的电磁波为载波,调整信号到载波上的.多普勒效应就是,卫星和接收机是相对运动的,那么载波的频率会随运动距离发生改变.(<GPS测量与数据处理>p3 ...

  9. P3084 [USACO13OPEN]照片(差分约束)

    (已经有了简化版题面) 又秒了一次dp233 本来按照感觉瞎写了一发... 但还是老老实实列式子吧.... 对差分约束有了更深的理解 #include<cstdio> #include&l ...

  10. python——直接插入排序

    直接插入排序 直接插入排序原理 直接插入排序过程 代码 时间复杂度分析 排序稳定性