本帖最后由 dffzh 于 2025-5-1 15:55 编辑
#申请原创#
@21小跑堂
从事基于C语言的嵌入式产品开发时,在有些场景,特别是对实时性和运行速度有较高要求的场合,位操作运算符得到了广泛应用,特别是它们的组合应用,为什么呢?主要原因在于位操作运算符直接在计算机的二进制层面上进行操作,避免了复杂的类型转换和算术规则,能显著减少CPU的运算周期。这篇文章作者将通过一些实战的经验代码与大家分享一下C语言的位操作运算符的一些使用,希望可以加深大家对位操作运算符的理解和认知。
一、 先带大家看下有哪些位操作运算符 ? 所谓的位操作运算符,其实就是按二进制位进行运算的运算符,在C语言中主要包括: 位与运算符(&):即两个操作数都为1,结果才为1; 位或运算符(|):即两个操作数只要有一个为1,结果就为1; 按位取反运算符(~):即取反操作,~0为1,~1为0; 位异或运算符(^):即两个操作数不同时,结果才为1; 左移运算符(<<):操作数的所有位左移一定的位数; 右移运算符(>>):操作数的所有位右移一定的位数。 这里就不展开详细描述,相信从事嵌入式软件开发的朋友们都很熟悉!
二、 再以实际案例代码讲解应用 案例1: 软件需要读取7个数字输入信号,并通过总线转发到主机,但产品上由于硬件设计原因,没有按位bit0对应数字输入信号1,位bit1对应数字输入信号2,……位bit6对应数字输入信号7这样映射到输入检测芯片上;而是正好相反;因此软件上在读取到所有的数字输入信号后,当然就需要通过计算转换才能与实际一一对应。那既然是关于位操作的,自然而然就想到了通过位操作运算符来实现。 要实现的转换其实就是下面这个逻辑: bit0 与bit6互换; bit1 与bit5互换; bit2 与bit4互换; bit3不变。 那代码上如何实现呢? 作者是定义了一个API函数接口,函数入参为原始值,函数返回值为转换值,然后先通过位与运算符,左移运算符和右移运算符得到7个存放各个bit位值的临时变量值,其中bit3不用操作,然后将7个变量值通过位或运算符计算即可得到转换值,具体代码如下: uint8_t swap_low7bit(uint8_t value) { uint8_ttemp = 0; uint8_t a,b,c,d,e,f,g;
a= (value & 0x40) >> 6; //bit6 -> bit0 b= (value & 0x20) >> 4; //bit5 -> bit1 c= (value & 0x10) >> 2; //bit4 -> bit2 d= (value & 0x04) << 2; //bit2 -> bit4 e= (value & 0x02) << 4; //bit1 -> bit5 f= (value & 0x01) << 6; //bit0 -> bit6 g= value & 0x08; // bit3 temp= (a | b | c | d | e | f | g) & 0x7F; //only 7 di input returntemp; } 以上代码实现的应用功能,就使用了4种位操作运算符。 代码实现后,建议先在自己常用的编译环境里验证一下,作者用的是Visual C++6.0: 如上验证,代码正确。
案例2: 硬件上MCU通过菊花链的拓扑方式级联了4颗数字输入隔离芯片,用于检测32点数字输入信号,即每个芯片读取8点;起初硬件是按如下图设计: 根据硬件拓扑实现的代码如下: temp0 = di_data[0] | (di_data[1]<<8); temp1 = di_data[2] | (di_data[3]<<8); di_display_data = temp0 | (temp1 <<16); di_display_data也就是最终的32点数据信息,也是应用了位操作运算符。 但后面因为走线太长影响信号传输的原因,硬件改为如下设计了: 另外,32点输出控制的电路也进行了相同的修改。 那代码上如何修改呢? 为了能够方便维护,修改和迭代,作者是定义了一个API函数接口,函数入参为对应原硬件的值,函数返回值为对应新硬件的值。 实现原理主要是先通过位与运算符,左移运算符和右移运算符得到每个芯片即每个字节的数据,并存为32位数据类型,然后使用位或运算符计算即可得到新值,具体代码如下: //字节交换:最高字节和最低字节交换,中间两个字节互换 uint32_t SwapByteOf32bits(uint32_t value) { uint32_t data[4] = {0,0,0,0}; data[0]= (value & 0x000000FF)<<24; data[1]= (value & 0x0000FF00)<<8; data[2]= (value & 0x00FF0000)>>8; data[3]= (value & 0xFF000000)>>24; value = data[0] | data[1] | data[2] | data[3]; return value;
} 代码实现后,验证如下: 如上验证,代码正确。 调用方法如下即可: di_display_data =SwapByteOf32bits(di_display_data); di_display_data即为最终的32点数据信息。
案例3: 需要实现对读取的32个通道的数字输入信号的软件滤波功能,并且各个通道的状态互不影响,在滤波完成前也不能更新当前的通道状态,如果滤波期间,输入状态变化了,还需要重新开始滤波。 类似这种针对每个点位的应用,就要想到使用for循环+位操作运算符来实现。 第一步:判断状态是否发生改变,若状态改变,重设参数: 第二步:滤波完成后,更新数据: 以上是主要的代码片段,在这个案例里,位操作运算符的功能发挥地淋漓尽致!
三、 最后来个总结吧 以上通过实战案例和代码向大家展示了C语言的位操作运算符的一些应用场景,实现代码可能并非最优,但这不是最重要的,更重要的是希望通过这篇文章可以让大家对位操作运算符有一些更深层次的理解及其在嵌入式软件开发中的重要地位,特别是在对运算速度要求比较高的场合,大家能够想到使用位操作运算符来实现实际应用功能。
如果你有更经典的位操作运算符的应用案例和经验,欢迎来贴讨论和分享!
|
C语言基础知识-位操作详解,通过位操作,提高代码运行效率,节省空间。C语言开发必学。