搜索
bottom↓
回复: 6

C语言的高级应用-前导置零法

[复制链接]

出0入234汤圆

发表于 2021-11-12 15:48:11 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2022-1-7 17:36 编辑

以下文章来源于:公众号:开源电子网,读取更多技术文章,请扫码关注

讨论发帖图.png



C语言的高级应用-前导置零法

       虽然C语言基础知识很简单,但是如果我们不学习C语言高级玩法,那么我们还是入门级的程序员,例如一个功能自己使用100行实现,到处if else语句等等,别人50行代码完成功能实现,代码格式还那么精简,对于企业来讲,精简的代码有利于维护以及查找bug简单,如果我们不优化自己的代码,那么后期维护性是非常吃力的。


一、本节课的目标      

       本节课,我们使用计数前导零_CLZ(x)来制作一个管理事件代码,计数前导零有两种方法,一种计算高位最高优先级的事件,第二种计算最低优先级的事件。


二、前导置零方法

       计数前导零是对位列表的一种操作,它从最高有效位开始计算第一个位之前存在多少个零位,对于C语言的unsigned int 一般是32位的,例如0000 0000 0000 0000 0000 0000 0000 0000的32位,如果我们计算 unsigned int x  =  0x5fffffff使用_CLZ(x)计算前导零为1,如图所示:


          C语言的高级应用-前导置零法(1)416.png


       因为0x5fffffff转成二进制位0101 1111 1111 1111 1111 1111 1111 1111,而最高位的零的个数为1个,所以_CLZ(x)计算前导零为1,这个前导零一般在FreeRTOS操作系统的RTOS就绪链表查找最高优先级,数值越高,优先级越高,


       从上述图可知,我们可不可以从最低位查找0的个数,那么我们先分析问题:

       1,首先我们把低位和高位替换位置,例如0x5ffffff1转成0x1ffffff5。


C语言的高级应用-前导置零法(1)631.png



C语言的高级应用-前导置零法(1)633.png



       2, 然后我们进行CLZ计数前导零,从上图可知,计算得出为3。

       实现源码:

       前面我们一讲讲解了两步奏,先低位替换成高位,然后使用CLZ计数前导零,如以下源码所示:

  1. <font size="4">__asm int os_ffs(int value)</font>

  2. <font size="4">{</font>

  3. <font size="4">    CMP     R0, #0x00     /* R0是否为0 */</font>

  4. <font size="4">    BEQ     exit</font>



  5. <font size="4">    RBIT    R0, R0        /* 低位和高位替换 */</font>

  6. <font size="4">    CLZ     R0, R0        /* 计算前导零指令 */</font>



  7. <font size="4">exit</font>

  8. <font size="4">    BX      LR            /* R0为0返回 */</font>

  9. <font size="4">}</font>
复制代码

       第三行CMP     R0, #0x00主要判断R0(value)是否为零,如果为零则运行exit以后的代码,然后调用RBIT    R0, R0   /* 低位和高位替换 */,最后计算前导零指令。


三、应用场景


方法1:
       我们使用LVGL为例,如果我们制作一个图标的界面,我们不可能每个图标使用一个回调函数,这样造成代码量太大,无法做到精简,那么我们就让他们共用一个回调函数,那么我们怎么区分它们那个触发了事件了呢,这个是一个重点的问题,那么小编是这样写的,如以下源码所示:

       1, 定义一个32位的obj_readly_list变量,如果数据结构学习不错的,也可以使用链表形式。

       2, 当我们按下某一个图标时,那么我们触发某个事件,例如该图标人为设定事件1,那么obj_readly_list就等于0x00000002(对应以下代码的第一步)

       3, 这个变量lv_bit = (31UL - ( unsigned char )__clz( ( obj_readly_list ) ) );(对应第二步)计算前导置零得出31-__clz(0x00000002) = 31 – 30 = 1,那么表示事件1被触发了,然后对obj_readly_list清零操作(对应第三步)。

       4, 最后我们switch case语句进行判断事件1(lv_bit为1)对应的动作。

  1. <font size="4">void lv_event_cb(lv_event_t *event)</font>

  2. <font size="4">{</font>

  3. <font size="4">    lv_event_code_t code = lv_event_get_code(event);</font>

  4. <font size="4">    lv_obj_t * obj = lv_event_get_target(event);</font>

  5. <font size="4">    if (code == LV_EVENT_CLICKED)</font>

  6. <font size="4">    {</font>

  7. <font size="4">        for (int i = 0;i < app_mun;i ++)</font>

  8. <font size="4">        {</font>

  9. <font size="4">            if (obj == lv_obj_app)</font>

  10. <font size="4">            {</font>

  11. <font size="4">                /* 第一步:obj就绪表位置1 */</font>

  12. <font size="4">                obj_readly_list |= 1 << i; </font>

  13. <font size="4">            }</font>

  14. <font size="4">        }</font>

  15. <font size="4">        /* 第二步:计算前导指令 */ </font>

  16. <font size="4">        lv_bit = (31UL -  ( unsigned char )</font>

  17. <font size="4">                             __clz( ( obj_readly_list ) ) );</font>

  18. <font size="4">        /* 第三步:该位清零就绪表 */</font>

  19. <font size="4">        obj_readly_list &= ~(1 << lv_bit);</font>

  20. <font size="4">         /* 根据该位做相应的函数 */</font>

  21. <font size="4">        switch(lv_bit)                  </font>

  22. <font size="4">        {</font>



  23. <font size="4">            case 0:</font>

  24. <font size="4">              /* 对象0处理的任务 */</font>

  25. <font size="4">            break;</font>

  26. <font size="4">            case 1:</font>

  27. <font size="4">              /* 对象1处理的任务 */</font>

  28. <font size="4">              break;</font>

  29. <font size="4">            case 2:</font>

  30. <font size="4">              /* 对象2处理的任务 */</font>

  31. <font size="4">              break;</font>

  32. <font size="4">            case 3:</font>

  33. <font size="4">              /* 对象3处理的任务 */</font>

  34. <font size="4">              break;</font>

  35. <font size="4">            case 4:</font>

  36. <font size="4">              /* 对象4处理的任务 */</font>

  37. <font size="4">              break;</font>

  38. <font size="4">            default:</font>

  39. <font size="4">              break;</font>

  40. <font size="4">        }</font>

  41. <font size="4">    }</font>

  42. <font size="4">}</font>
复制代码


方法2:
       我们主要调用前面我们讲解的os_ffs()进行计算,所以lv_bit也会得到某个事件触发的值。

  1. <font size="4">void lv_event_cb(lv_event_t *event)</font>

  2. <font size="4">{</font>

  3. <font size="4">    lv_event_code_t code = lv_event_get_code(event);</font>

  4. <font size="4">    lv_obj_t * obj = lv_event_get_target(event);</font>

  5. <font size="4">    if (code == LV_EVENT_CLICKED)</font>

  6. <font size="4">    {</font>

  7. <font size="4">        for (int i = 0;i < app_mun;i ++)</font>

  8. <font size="4">        {</font>

  9. <font size="4">            if (obj == lv_obj_app)</font>

  10. <font size="4">            {</font>

  11. <font size="4">                /* 第一步:obj就绪表位置1 */</font>

  12. <font size="4">                obj_readly_list |= 1 << i; </font>

  13. <font size="4">            }</font>

  14. <font size="4">        }</font>

  15. <font size="4">        /* 第二步:计算前导指令 */ </font>

  16. <font size="4">        lv_bit = os_ffs( obj_readly_list );</font>

  17. <font size="4">        /* 第三步:该位清零就绪表 */</font>

  18. <font size="4">        obj_readly_list &= ~(1 << lv_bit);</font>

  19. <font size="4">         /* 根据该位做相应的函数 */</font>

  20. <font size="4">        switch(lv_bit)                  </font>

  21. <font size="4">        {</font>



  22. <font size="4">            case 0:</font>

  23. <font size="4">              /* 对象0处理的任务 */</font>

  24. <font size="4">            break;</font>

  25. <font size="4">            case 1:</font>

  26. <font size="4">              /* 对象1处理的任务 */</font>

  27. <font size="4">              break;</font>

  28. <font size="4">            case 2:</font>

  29. <font size="4">              /* 对象2处理的任务 */</font>

  30. <font size="4">              break;</font>

  31. <font size="4">            case 3:</font>

  32. <font size="4">              /* 对象3处理的任务 */</font>

  33. <font size="4">              break;</font>

  34. <font size="4">            case 4:</font>

  35. <font size="4">              /* 对象4处理的任务 */</font>

  36. <font size="4">              break;</font>

  37. <font size="4">            default:</font>

  38. <font size="4">              break;</font>

  39. <font size="4">        }</font>

  40. <font size="4">    }</font>

  41. <font size="4">}</font>
复制代码

       经过上面的学习,相信大家对于两种前导置零的方法有一定的了解,本文详细介绍了前导置零到底是什么,如果是一个c语言初学者,那么就把最基本的知识好好理解,熟能生巧,笔者建议初学者学习C语言的基础知识同时,可以去学习数据结构c语言版第二版 (严蔚敏)。






出0入0汤圆

发表于 2021-11-12 16:27:53 | 显示全部楼层
我觉得还是简单粗暴些好,flash、主频足够的情况下,结构、语法简单反而容易找bug。

出0入0汤圆

发表于 2021-11-12 16:45:06 | 显示全部楼层
看不懂什么玩意儿。

出0入117汤圆

发表于 2021-11-12 22:26:15 来自手机 | 显示全部楼层
嵌入的汇编算哪门子c应用?不是arm指令集就不配用C了?

出0入4汤圆

发表于 2021-11-13 08:52:34 | 显示全部楼层
排版整理下,太乱了

出0入8汤圆

发表于 2021-11-13 10:51:27 | 显示全部楼层
case分支里不还是要回调函数

出0入0汤圆

发表于 2022-3-3 09:30:54 | 显示全部楼层
真应该多多学习,看完感觉明白一些
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子论坛 ( 公安交互式论坛备案:44190002001997 粤ICP备09047143号 )

GMT+8, 2022-7-2 05:03

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表