datasheet

STM32学习---位带操作总结

2019-07-12来源: eefocus关键字:stm32  位带操作  GPIO

*简介:*在计算机中所有的数据都是以二进制的形式储存的。位运算其实就是直接对在内存中的二进制数进行操作,因此处理数据的速度非常快。

在实际编程中,如果能巧妙运用位操作,完全可以达到四两拨千金的效果,正是因为这些优点,所以位操作的应用非常广泛,同时掌握位带操作对于我们理解STM32的原理非常有用。


**


*过渡:***为了方便大家理解,有必要补充一些基础知识。

1.计算中的符号位: 计算机的符号位就是在处理二进制数据时,专门规定有一位,是用来确定数据的正负,符号位是1表示负数,是0表示正数。当然这里说的是有符号数,这个符号位通常是数据的最高位,如8位数据,左边第一位是符号位,后面7位用来表示数据大小。

2.补码: 注意,此处的“==”是相等的意思。

在机器的世界里:正数的最高位是符号位0,负数的最高位是符号位1。

对于正数:反码 ==补码 ==原码

对于负数:反码 ==除符号位以外的各位数取反

补码 ==反码+1

原码 ==(补码-1)后的反码 ==补码的反码+1

如:-15的二进制

<1>先取-15的原码:1000 1111

<2>得反码:1111 0000

<3>得补码:1111 0001

可见,-15在计算机里的二进制表达式就是1111 0001

16进制为:0xF1

3.二进制数右移:

把一个二进制数右移N位,规则为:

除符号位外,全部右移N位,如果数字是一个无符号位数值,则用0填补最左边的N位;如果数字是一个有符号位数值,则用1填补最左边的N位

也就是说,如果数字原先是一个正数,则右移之后在最左边补N个0;如果数字原先是个负数,则右移之后在最左边补N个1

如:-15=1111 0001

右移二位,最高位由符号位填充将得到 1111 1100即-4


总结:

一、位操作基础知识,位操作符的应用规则表及使用要点

二、位操作符的技巧,包括判断奇偶、交换两数、变换符号、求绝对值

位操作符还有许多应用,但对于学习STM32,理解这些知识应该足够了。


一、位操作的基础知识

基本的位操作符有与、或、异或、取反、左移、右移这6种,它们的运算规则如下所示:




符号 运算规则

&(与) 两个都为1时,结果才为1

l(或) 两个位都为0时,结果才为0

^(异或) 两个位相同为0,相异为1

~(取反) 0变1,1变0

<< (左移) 各二进制位全部左移若干位,高位丢弃,低位补0

>>(右移) 各二进制位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

注意以下几点:

1、在这六种操作符中,只有取反是单目操作符,其他5种都是双目操作符。

2、位操作只能用于整型数据,对float和double类型进行位操作会被编译器报错。

3、对于移位操作,在微软的VC6.0和VS2008编译器都是采取算术称位即算术移位操作,算术移位是相对于逻辑移位,它们在左移操作中都一样,低位补0即可,但在右移中逻辑移位的高位补0而算术移位的高位是补符号位。

如下面代码会输出-4和3。


 int a = -15, b = 15;

 printf("%d %dn", a >> 2, b >> 2);


因为15=0000 1111(二进制),右移两位,最高位由符号位填充将得到0000 0011即3。 -15=1111 0001(二进制),右移两位,最高位由符号位填充将得到1111 1100 即-4。

4、位操作符的运算优先级比较低,因而尽量使用括号来确保运算顺序,否则可能得到名莫名其妙的结果。

比如说要得到像1,3,5,9这些2^i+1的数字。写成int a=1<

5、另外位操作还有一些复合操作符,如&=、|=、 ^=、<<=、>>=。

二、位操作符的技巧

下面对位操作的一些常见应用作个总结,有判断奇偶、交换两数、变换符号及求绝对值。这些小技巧应用易记,应当熟练掌握。

1、判断奇偶

只要根据最末位是1还是0来决定 ,为0就是偶数,为1就是奇数。因此可以用if ((a&1)==0)代替if(a%2 ==0)来判断a是不是偶数。

下面程序将输出0到100之间的所有奇数。


for (i=0;i<100;++i)

 if(i&1)

  printf ("%d",i);

  putchar("n");


分析:if(i&1)表示如果i是奇数,则执行if中函数,否则不执行

i&1,按位与取运算,取二进制整数i的最低位,如果最低位是1则得1,如果最低位是0则得0。奇数i的最低位是1,偶数i的最低位是0

如:i(二进制)&1

(0) 0000 0000&1得0 (偶数)

(1)0000 0001&1得1(奇数)

(2)0000 0010&1得0 (偶数)


2、交换两数

一般的写法是:



    

void Swap(int &a, int &b)

{

if (a != b)

{

int c = a;

a = b;

b = c;



可以用位操作来实现交换两数而不用第三方变量:



    void Swap (int &a,int &b)

    {

         if (a != b)

         {

             a ^= b;

             b ^= a;

             a ^= b;

         }

}



分析: 设 a= 1111 000

b= 1100 1100

则由 a ^= b 知 a = a ^ b

即 a = 0011 1101 <1>

由 b ^= a 知 b = b ^ a

{ b = 1100 1100

{ a = 0011 1101

则 b = 1111 0001 <2>

第3步, a ^= b 即 a = a ^ b

又 { a = 0011 1101

{ b = 1111 0001

则 a = 1100 1100 <3>

a与b的值实现调换。


3、变换符号

变换符号就是 正数变成负数,负数变成正数。

如:对于-11和11,可以通过下面的变换方法将-11变成11。

1111 0101 (-11的二进制)- - -<取反> 0000 1010 (二进制)

- - -<加1> 0000 1011 (11的二进制)

同理,可以将11变成-11。

0000 1011 (11的二进制)- - -<取反> 1111 0100 (二进制)

- - -<加1> 1111 0101 (-11的二进制)

因此变换符号只需要取反后加1即可。

完整代码如下:


#include

int SignReversal(int a)

{

 return ~a + 1;

}

int main()

{

 printf("对整数变换符号 --- by MoreWindows( http://blog.csdn.net/MoreWindows )  ---nn");

 int a = 7, b = -12345;

 printf("%d  %dn", SignReversal(a), SignReversal(b));

 return 0;

}


4、求绝对值

位操作也可以用来求绝对值,对于负数可以通过对其取反后加1来得到正数。对于-6可以这样:

1111 1010 (-6的二进制)- - -<取反> 0000 0101(二进制)

- - -<加1> 0000 0110 (6的二进制)

来得到6。


因此先移位来取符号位,int i = a >> 31;要注意如果a为正数,i等于0,为负数,i等于-1。然后对i进行判断——如果i等于0,直接返回。否之,返回~a+1。

完整代码如下:


int my_abs(int a)

{

 int i = a >> 31;

 return i == 0 ? a : (~a + 1);

}


现在再分析下。对于任何数,与0异或都会保持不变,与-1即0xFFFFFFFF异或就相当于取反。因此,a与i异或后再减i(因为i为0或-1,所以减i即是要么加0要么加1)也可以得到绝对值。

所以可以对上面代码优化下:


int my_abs(int a)

{

 int i = a >> 31;

 return ((a ^ i) - i);

}


注意,上述这种方法没用任何判断表达式,故推荐使用。

分析: 在C语言中" ? : “是什么意思?

是条件运算符。条件运算符是C语言中唯一的三目三目运算符,就是说它有三个运算对象。条件运算符的形式是” ? : ",由它构成的表达式称为条件表达式。

形式为:表达式1 ?表达式2 :表达式3

运算功能是:先计算表达式1的值,若值为非0,则计算表达式2的值,

并将表达式2的值作为整个条件表达式的结果;

若计算表达式1的值为0,则计算表达式3的值,

并将表达式3的值作为整个条件表达式的结果。

比如,有以下表达式(a > b)? a + b : a - b

a = 8, b = 4时,计算 a + b = 12,所以表达式结果为12;

a = 4, b = 8时,计算 a - b = -4 ,所以表达式结果为 -4。


关键字:stm32  位带操作  GPIO

编辑:什么鱼 引用地址:http://www.eeworld.com.cn/mcu/ic467546.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32时钟系统以及配置及源码分析
下一篇:STM32 IO口位带操作

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM32开发笔记71: 解决FreeRTOS任务的内存分配问题

单片机型号:STM32F091RCT6在使用FreeRTOS进行程序设计时,遇到任务不能运行的问题,具体程序如下: DebugOutput("启动USB通讯线程...rnrn"); osThreadDef(usbTask, StartUsbTask, osPriorityNormal, 0, 128); usbTaskHandle = osThreadCreate(osThread(usbTask), NULL); DebugOutput("启动雷达红外通讯线程...rnrn"); osThreadDef(irdaTask, St
发表于 2019-07-13

STM32开发笔记72: 使用命名空间解决类名冲突问题

单片机型号:STM32L053R8T6在程序设计中,使用了两个类,这两个类都有引脚定义并同名,程序如下:#ifndef E32_400T20S_H_#define E32_400T20S_H_ #include "io.h"#include "mini_uart.h" #ifdef __cplusplusextern "C"{ class CM0:public CIO_Output{public: CM0(void);}; class CM1:public CIO_Output{public: CM1(void);}; 
发表于 2019-07-13

STM32开发笔记73: C++中子类调用父类同名函数的处理方法

单片机型号:STM32L053R8T61、问题父类有1方法:Enable_RS485,如下所示:class CUart{public: uint8_t u8_UartNumber; //端口号1-8 uint32_t u32_BaudRate; //波特率 uint8_t u8_Parity; //效验位 CC0 C0; //485控制引脚C0 CC1 C1; //485控制引脚C1 UART_HandleTypeDef hUART; uint8_t u8_UartReceiveBuffer[1];public: CUart(uint8_t
发表于 2019-07-13

STM32开发笔记74: STM32L0低功耗唤醒后的时钟选择

本文介绍STM32L0系列单片机低功耗唤醒后的时钟选择。参看已有的低功耗例程,发现都使能了HSI时钟,一致没有深究其中的具体原因,今天把它搞明白了,现记录如下:先看一下,使能低功耗的函数:void CTarget::EnableLowPower(void){ HAL_PWREx_EnableUltraLowPower(); HAL_PWREx_EnableFastWakeUp(); __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI); DisableAllIO();}第1句使能超低功耗,第2句使能快速唤醒,第3句选择唤醒后的主始终,第4句将所有IO引脚设置为低功耗状态
发表于 2019-07-13

STM32开发笔记75: 使用STM32CubeMX点亮一个LED

今天调试在自己的程序框架下调试RTC始终不成功,只要初始化RTC就进入死机状态。现在重温一下STM32CubeMX的使用方法,看STM32CubeMX生成的程序是否有RTC初始化不成功的问题。本日志从工程的建立讲到点亮一个LED。1、启动STM32CubeMX,我现在使用的版本是5.2.1。2、File-New Project,选择相应的芯片类型。3、双击相应的芯片类型后,进入配置界面。进行SYS配置,选中Debug Serial Wire,由于我习惯于使用FreeRTOS所以在我的项目中Timebase Source都选择定时器。4、进行RCC设置。5、时钟设置如下:6、在芯片引脚图中,将连接LED的引脚设置
发表于 2019-07-13
STM32开发笔记75: 使用STM32CubeMX点亮一个LED

STM32开发笔记76: 初始化RTC后死机的原因

项目开发中只要初始化RTC,则系统死机。其初始化步骤可参考日志:STM32开发笔记44:RTC驱动程序的移植。按照日志STM32开发笔记75: 使用STM32CubeMX点亮一个LED使用STM32CubeMX直接生成程序则运行正常。分析原因在于,少移植了2个函数:HAL_RTC_MspInit和HAL_RTC_MspDeInit。这两个函数的实现非常简单,可以靠STM32CubeMX直接生成。void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc){  __HAL_RCC_RTC_ENABLE();   HAL_NVIC_SetPriority(RTC_IRQn
发表于 2019-07-13

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved