news 2026/7/24 21:04:19

单片机开发进阶技巧:(一)单变量承担多个标志位及计数功能

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
单片机开发进阶技巧:(一)单变量承担多个标志位及计数功能
下一篇
如何判断一个浮点型运算结果是否为整数

目 录

  • 单变量承担多个标志位及计数功能
    • 1)判断某个位是 1 还是 0
    • 2)将某一位置 1 或置 0
    • 3)低位计数,高位判断
    • 4)应用案例
      • ① 基础案例
      • ② 进阶案例
      • ③ 补充案例

单变量承担多个标志位及计数功能

使用一个变量,来承担多个标志位的功能,即每一位都可以充当标志位,也可以包含计数功能。

  • 好处:节省空间,让代码看起来高级,集成度会变高。

  • 缺点:代码可读性差,不易理解。

定义一个 8 位整型变量uint8_t flag,则有:

1)判断某个位是 1 还是 0

/*判断位0*/if((flag&0x01)==0)// 位0为0if((flag&0x01)==0x01)// 位0为1/*判断位1*/if((flag&0x02)==0)// 位1为0if((flag&0x02)==0x02)// 位1为1/*判断位2*/if((flag&0x04)==0)// 位2为0if((flag&0x04)==0x04)// 位2为1/*判断位3*/if((flag&0x08)==0)// 位3为0if((flag&0x08)==0x08)// 位3为1/*判断位4*/if((flag&0x10)==0)// 位4为0if((flag&0x10)==0x10)// 位4为1/*判断位5*/if((flag&0x20)==0)// 位5为0if((flag&0x20)==0x20)// 位5为1/*判断位6*/if((flag&0x40)==0)// 位6为0if((flag&0x40)==0x40)// 位6为1/*判断位7*/if((flag&0x80)==0)// 位7为0if((flag&0x80)==0x80)// 位7为1

2)将某一位置 1 或置 0

/*位0置1或置0*/flag|=0X01;// 位0置1flag&=0X7F;// 位0置0,等效于 flag &= ~0X01;/*位1置1或置0*/flag|=0X02;// 位1置1flag&=0XFD;// 位1置0,等效于 flag &= ~0X02;/*位2置1或置0*/flag|=0X04;// 位2置1flag&=0XFB;// 位2置0,等效于 flag &= ~0X04;/*位3置1或置0*/flag|=0X08;// 位3置1flag&=0XF7;// 位3置0,等效于 flag &= ~0X08;/*位4置1或置0*/flag|=0X10;// 位4置1flag&=0XEF;// 位4置0,等效于 flag &= ~0X10;/*位5置1或置0*/flag|=0X20;// 位5置1flag&=0XDF;// 位5置0,等效于 flag &= ~0X20;/*位6置1或置0*/flag|=0X40;// 位6置1flag&=0XBF;// 位6置0,等效于 flag &= ~0X40;/*位7置1或置0*/flag|=0X80;// 位1置1flag&=0X7F;// 位1置0,等效于 flag &= ~0X80;

3)低位计数,高位判断

在实际项目中,常将一个变量的高几位用作状态标志位低几位用作计数值或索引。例如:

  • 使用uint8_t flag
    • bit7、bit6:作为布尔标志(如TX_READYRX_COMPLETE
    • bit5 ~ bit0(共6位):用于计数(范围 0~63)

此外,如果计数位数不够,可以定义一个 16 位的变量。

4)应用案例

实际工程当中,我们会用位操作符来代替0x01这些十六进制:

0x01=1<<0;0x02=1<<1;0x04=1<<2;0x08=1<<3;0x10=1<<4;0x20=1<<5;0x40=1<<6;0x80=1<<7;

还可以进一步将其定义为宏:

#defineVALUE1(1<<7)#defineVALUE2(1<<6)#defineVALUE3(1<<5)

① 基础案例

串口通信:

uint8_tuart_status=0;#defineUART_TX_BUSY(1<<7)#defineUART_RX_VALID(1<<6)#defineUART_ERR_FLAG(1<<5)#defineUART_RX_COUNT0x1F// 低5位存接收字节数(最多31字节)// 接收到1个字节uart_status=(uart_status&~UART_RX_COUNT)|((rx_cnt+1)&UART_RX_COUNT);uart_status|=UART_RX_VALID;// 发送中uart_status|=UART_TX_BUSY;// 发送完成uart_status&=~UART_TX_BUSY;

② 进阶案例

我们可以进一步将 “判断某个位是1还是0” 和 “将某一位置1或置0” 封装成宏形式的函数(如果编译器报错了,就把 1U 后面的 U 删了):

#defineSET_BIT(var,bit)((var)|=(1U<<(bit)))// 将变量的某一位置1#defineCLR_BIT(var,bit)((var)&=~(1U<<(bit)))// 将变量的某一位置0#defineIS_SET(var,bit)(((var)&(1U<<(bit)))!=0)// 判断变量的某一位是否为1,返回1则是1,返回0则是0

则进阶版的串口通讯实例如下:

#include<stdint.h>// ---------- 位操作宏 ------------------------------#defineSET_BIT(var,bit)((var)|=(1U<<(bit)))// 置1#defineCLR_BIT(var,bit)((var)&=~(1U<<(bit)))// 清0#defineIS_SET(var,bit)(((var)&(1U<<(bit)))!=0)// 判断是否为1// ---------- UART 状态位定义 -----------------------#defineUART_TX_BUSY_BIT7// bit7: 发送忙标志#defineUART_RX_VALID_BIT6// bit6: 接收数据有效#defineUART_ERR_FLAG_BIT5// bit5: 错误标志#defineUART_RX_COUNT_MASK0x1F// 低5位(bit4~bit0):接收字节数(0~31)// 状态变量uint8_tuart_status=0;// ---------- 使用示例 ------------------------------// 1. 开始发送数据(置位 TX_BUSY)SET_BIT(uart_status,UART_TX_BUSY_BIT);// 2. 发送完成(清除 TX_BUSY)CLR_BIT(uart_status,UART_TX_BUSY_BIT);// 3. 接收到一个字节(在 RX 中断中调用){uint8_tcurrent_count=uart_status&UART_RX_COUNT_MASK;// 读取当前计数值if(current_count<31){// 计数+1,并更新到低5位(先清空再写入)uart_status=(uart_status&~UART_RX_COUNT_MASK)|(current_count+1);}SET_BIT(uart_status,UART_RX_VALID_BIT);// 标记有新数据}// 4. 主循环中检查是否有有效数据if(IS_SET(uart_status,UART_RX_VALID_BIT)){uint8_trx_bytes=uart_status&UART_RX_COUNT_MASK;// 获取接收字节数// TODO: 处理接收到的 rx_bytes 个字节数据// ...// 处理完毕,清除有效标志(计数不清零,由下次接收覆盖)CLR_BIT(uart_status,UART_RX_VALID_BIT);}// 5. 出现通信错误(如帧错误、溢出等)SET_BIT(uart_status,UART_ERR_FLAG_BIT);// 6. 检查是否有错误(可选)if(IS_SET(uart_status,UART_ERR_FLAG_BIT)){// TODO: 错误处理CLR_BIT(uart_status,UART_ERR_FLAG_BIT);// 清除错误标志}

③ 补充案例

下面是正点原子通用定时器输入捕获例程中的一个函数,就使用的这个方式。

/** * @brief 溢出中断服务函数底层回调函数 * @others 用来记录定时器溢出次数,此函数会被HAL_TIM_IRQHandler()调用 */voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim){if((TIM5CH1_CAPTURE_STA&0X80)==0)// 还未成功捕获{if(TIM5CH1_CAPTURE_STA&0X40)// 已经捕获到高电平(上升沿)了{if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)// 高电平太长了(计数计满了){TIM5CH1_CAPTURE_STA|=0X80;// 标记成功捕获了一次TIM5CH1_CAPTURE_VAL=0XFFFF;}else// 计数TIM5CH1_CAPTURE_STA++;}}}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/24 21:03:44

【高阶工控感知】强腐蚀与极窄接管工况下的物位测量破局:基于 FMCW 调频连续波距离估计与信号跟踪算法的 C++ 实战,兼论“雷达液位计生产厂家推荐”与“工业自动化仪表应用厂家”选型硬指标

各位 CSDN 的工业自动化工程师、嵌入式系统架构师、IIoT 边缘计算开发者以及工控仪表领域的同仁们&#xff0c;大家好&#xff01;在现代流程工业&#xff08;Process Industry&#xff09;如精细化工、冶金采矿、医药制造及污水处理中&#xff0c;物位&#xff08;液位、料位&…

作者头像 李华
网站建设 2026/7/24 21:01:23

多线程视频处理管线优化实战:锁粒度、唤醒机制与内存复用

在实时视频处理系统中&#xff0c;每毫秒的延迟都可能影响用户体验。最近我在对一个视频处理模块进行性能调优时&#xff0c;经历了一次典型的端到端延迟优化过程——最终将处理管线延迟降低了约 30ms&#xff0c;CPU 占用也更加平滑。本文完整记录这次优化的思路与细节&#x…

作者头像 李华
网站建设 2026/7/24 20:59:42

opencode 源码拆解:run 命令从 CLI 到 Agent Loop 的三层路由设计

如果你要设计一个能从终端命令一路走到 AI Agent 循环的架构——用户敲一行 opencode run "fix this bug"&#xff0c;你的系统怎么把这个字符串变成一次完整的 LLM 推理&#xff1f; 你可能会想&#xff1a;这有什么难的&#xff1f;process.argv 解析一下&#xf…

作者头像 李华
网站建设 2026/7/24 20:59:04

终极鸣潮工具箱WaveTools完整指南:3步解锁120FPS高帧率与抽卡分析

终极鸣潮工具箱WaveTools完整指南&#xff1a;3步解锁120FPS高帧率与抽卡分析 【免费下载链接】WaveTools &#x1f9f0;鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools 还在为《鸣潮》游戏的60FPS限制感到困扰吗&#xff1f;想要让你的高端显卡发挥…

作者头像 李华