网站是做响应式还是自适应的好,青岛城乡建筑设计院有限公司,宁夏建设工程交易中心网站,手机怎么看网页源代码前面#xff0c;我们对点灯代码进行了第一次优化#xff0c;效果如下 尽管第一次优化以后代码可读性确实高了不少#xff0c;也看起来更加简洁#xff0c;但是#xff0c;这里仍旧存在一个很严重的问题#xff1a;就在每一个表达式右边#xff0c;我们给寄存器的数据赋值…前面我们对点灯代码进行了第一次优化效果如下 尽管第一次优化以后代码可读性确实高了不少也看起来更加简洁但是这里仍旧存在一个很严重的问题就在每一个表达式右边我们给寄存器的数据赋值的操作。
我们每一个操作都是直接整体全部赋值与自己不相关的位直接就用默认的0或1处理了这样就会造成一个问题原来其他位可能被赋予特定值的现在因为整体赋值而被修改这样是很不合适的。 我们更希望的是在修改我们期望的特定位的数据以后仍不改变其他位的值。那么怎么办呢这时候我们就要利用C语言中的位运算操作了。
一、位运算在代码中的用法
考虑到我们可能长时间没有使用位运算所以这里放置一段代码供我们去回忆一下 #include stdio.h #include stdlib.h void printfBinary(char * str, uint32_t num) { char buffer[33]; itoa(num, buffer, 2); // 把result转成2进制字符串 printf(%s (%s)2 \n, str, buffer); } int main() { /* 左移 81 10001 10000*/ printfBinary(8 1, 8 1); /* 右移 81 10001 100*/ printfBinary(8 1, 8 1); /* 按位或 8|7 1000|0111 1111 */ printfBinary(8 | 7, 8 | 7); /* 按位或 87 10000111 0000 */ printfBinary(8 7, 8 7); /* 按位取反 ~8 ~1000 0111 */ printfBinary(~8, ~8); /* 把某位置 1 (0 位 1位 ...) 比如把 num 的第 2 位置 1 1. 得到一个数第 2 位是 1 其他都为 0 a 0000 0100 是由 12 得到 2. 让 num | a */ printfBinary(8置第 2 位为 1 , 8 | (1 2)); /* 把连续的多位同时置 1 (0 位 1位 ...) 比如把 num 的第 1和2 位置 1 1 a 3 1 2. num | a */ printfBinary(8置第 1和2 位为 1 , 8 | (3 1)); /* 把某位置 0 (0位 1位 ...) 比如把 num 的第 2 位置 0 1. 得到一个数第 2 位是 0 其他都为 1 a 1111 1011 是由 ~(12) 得到 2. 让 num a */ printfBinary(7置第 2 位为 0 , 7 ~(1 2)); /* 把连续多位同时置 0 (0位 1位 ...) 比如把 num 的第 1和2 位置 0 1. a ~(31) 2. 让 num a */ printfBinary(7置第 1和2 位为 0 , 7 ~(3 1)); /* 把连续的多位同时置位 101 (二进制) 比如把 num 的第 1,2,3 位置为 101 1. num的 1,2,3位置为0 num ~(71) 2. num | (5 1); (5 101) */ unsigned char num 13; num ~(7 1); num | 5 1; printfBinary(13, 13); printfBinary(13的123位置为101 , num); } 通过这段代码可以发现如果我们想要把特定位置为1就要利用或运算。比如1000我们要让他第1位变为1这里说的第几位是以0开头则要用或运算即1000 | 0010然后每一位对应做或运算1|0-10|0-00|1-10|0-0即变成1010 很明显这样就可以实现了。同时这里用的0010可以借助移位得到即11。整理一下就是1000 | (11)这里总结一个方法置1位或位1余0如果要把特定位置1就要让寄存器数据与一个数n做或运算然后这个数n的取法就是让对应特定位的地方为1其余给0即可
同理如果我们想要让特定位变成0则就要做与运算。比如0011要让第三位变成1就要对其进行与操作与谁做呢就是与1011或即0011101101-000-111-1,11-1这样结果就是0111很明显实现了我们想要的操作。当然这个1011我们不好去找所以这里还要用到取反的操作~我们将100取反就得到了1011同时这个100也可以用移位得到即12。整理一下就是0011 ~(12)。实际上就是为了让除了特定位以外的位全部置1这样才好实现当然了这是一个技巧记得了就好。所以总结一个方法置0位与位0余1如果要把特定位置0就要让寄存器数据与一个数n做与运算然后这个数n的取法就是让对应特定位的地方为0其余给1即可这个过程我们也会用取反实现
由此可以看出利用位运算即可实现在不改变其他位的情况下修改特定位的值了这样做能够更加精确化的修改寄存器中的数据而不影响其他位的值的情况更加合理了。
二、利用位运算优化代码
好前面我们对位运算进行了大致的回忆和使用方法的总结现在我们就来对代码进行优化。主要就是对表达式右边进行修改。
以点亮第一个LED灯为例 第一开启时钟的时候我们是要将寄存器数据二进制位的第二位置为1其他不变就是100。那么我们想想前面我们说置1位或位1余0好那就是对原数据或一个...0100就行同时100也可以写成12故代码修改后
// 开启时钟 第二位置为1其他不变
RCC-APB2ENR | (12);
第二配置GPIOA的工作模式部分 我们是让PA0端口变成最大速度的推挽输出模式所以要让前四位变成0011。这时候就遇到一个问题我们之前讲的都是修改一位那么现在要修改四位要怎么办呢诶其实一样的我们一位一位的修改不就好了。
从高向低位修改首先看第3位我们要置0因为置0位与 位0余1所以我们要让原数据与一个...110111同时这个110111是由1000取反得到的且1000是13得到因此代码可以写成这样 ~(13)
同理再看第二位也是要置0那么由于置0位与 位0余1所以我们要让原数据与一个...111011同时111011是由100取反得到且100可以表示成12因此代码可以写成 ~(12)
同理再看第1位这时候要置为1那么由于置1位或 位1余0所以我们要让原数据或一个10且10可以表示成11所以代码可以写成 | (11)
同理再看第0位也是要置1那么由于置1位或 位1余0因此我们让原数据或一个1即可1也能写成10所以保证与前三段代码格式一致这里就改成 | (10)
// 设置GPIOA的工作模式
GPIOA-CRL ~(13) // 第三位置0
GPIOA-CRL ~(12) // 第二位置0
GPIOA-CRL | (11) // 第一位置1
GPIOA-CRL | (10) // 第0位置1 第三设置端口高低电平 因为我们要让LED-1灯亮又连接LED-1灯的端口是PA0所以我们只需修改端口输出寄存器中的数据的二进制第0位的值为0就好了。要让第0位置0由于置0位与 位0余1所以我们让原数据与一个...1110就好由于...1110可以由1取反得到所以代码可以写成 ~(10)
// 设置PA0为低电平
GPIOA-ODR ~(10); // 第0位置0 总的二次优化代码如下
#includestm32f10x.hint main(void)
{// 开启时钟 RCC-APB2ENR | (12);// 设置GPIOA的工作模式GPIOA-CRL ~(13);GPIOA-CRL ~(12);GPIOA-CRL | (11);GPIOA-CRL | (10);// 设置PA0为低电平GPIOA-ODR ~(10);// 死循环保持状态while(1){}
}
三、测试优化代码
接下来就时在keil中测试了
点击编译运行下载 无错误
观察现象很明显黄灯确实亮了说明本次优化代码没有问题 四、结语
通过本次优化代码我们再次加深了对位运算的理解也了解到了其一个应用场景
这里实际上还有一个小地方可以简化代码就是设置GPIOA工作模式地方我们发现四段代码两两类似其实咱可以两两合并只需要用一个或就可以即
// 设置GPIOA的工作模式
GPIOA-CRL ~(13|12)
GPIOA-CRL | (11|10) 这样其实也是同样的意思实际上就是利用或运算同时对两位进行修改我们可自行尝试运行一下效果是一样的 OK这就是第二次的代码优化了继续加油拜拜