找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1924|回复: 34
收起左侧

C语言如何将多个位变量转换成一个变量来存储

  [复制链接]
ID:34149 发表于 2024-1-17 12:19 | 显示全部楼层 |阅读模式
平时利用多个按键,往往都是一个按键一个位变量表示,在单片机C语言中要么是0,要么是1,就好像按键一样,
程序中为了判断状态,要写很多语句,特别是多个按键的情况,占用很多程序空间。
为了计算方便,利用存储空间,有没有什么好办法把他们集中在一起组成一个单字节的,判断这个数值的内容,就能方便的
判断出多个位变量的状态。
比如有6个独立按键,占用了分散的不同的IO口,每个按键都要用到短按、长按、组合等控制功能,如果每个按键都写一次
这样子的判断,那么将有6个这样子的似乎重复的语句,各种变量,大量占据存储空间。

想请教各位老师,如何利用程序强大的运算功能,高效利用程序储存空间,
有什么好的算法使这6个位变量通过哪种方法组合成一个单字节的变量。


回复

使用道具 举报

ID:895440 发表于 2024-1-17 14:46 | 显示全部楼层
用bit类型
一个bit变量只占一位
回复

使用道具 举报

ID:458247 发表于 2024-1-17 14:48 | 显示全部楼层
支持位带操作的单片机可以用结构体位域写法
回复

使用道具 举报

ID:883242 发表于 2024-1-17 16:08 | 显示全部楼层
不同平台做法完全不一样。

不支持位操作的单片机以及编译器,只能用“位域”来实现,具体使用方法自己搜。

支持位操作的单片机及编译器查手册,以Keil C51为例,定义字节变量的时候加上bdata属性,然后用sbit定义就行了。

  1. unsigned char bdata datum;
  2. sbit bd0=datum^0;
  3. sbit bd1=datum^1;
复制代码
回复

使用道具 举报

ID:883242 发表于 2024-1-17 19:44 | 显示全部楼层
支持bit-band的ARM比如cortex-m3可以这么写,不过不建议,bit-band访问1位需要32位地址,如果不是特殊功能寄存器,只是通用内存,这生意太不划算了。
  1. #define DEVICE_REG0 ((volatile unsigned long *) (0x40000000))
  2. #define DEVICE_REG0_BIT0 ((volatile unsigned long *) (0x42000000))
  3. #define DEVICE_REG0_BIT1 ((volatile unsigned long *) (0x42000004))
复制代码
回复

使用道具 举报

ID:34149 发表于 2024-1-17 22:23 | 显示全部楼层
总共6个按键位变量,我把它合在一起了,成了一个字节,只是想让它在后面的判断和调用方便。没想到还有点难处理。有了返回值,判断起来就方便多了,组合判断非常有优势。
unsigned char KeyScan(unsigned char KeyFlag)
{
        static unsigned char da=0xff;
        if(KeyFlag==1)
        {
                if(key1==0)da &= 0xfe;else da |= 0x01;
                if(key2==0)da &= 0xfd;else da |= 0x02;
                if(key3==0)da &= 0xfb;else da |= 0x04;
                if(key4==0)da &= 0xf7;else da |= 0x08;
                if(key5==0)da &= 0xef;else da |= 0x16;
                if(key6==0)da &= 0xdf;else da |= 0x32;
         }
         return da;
}
回复

使用道具 举报

ID:883242 发表于 2024-1-18 01:43 | 显示全部楼层
dyx811 发表于 2024-1-17 22:23
总共6个按键位变量,我把它合在一起了,成了一个字节,只是想让它在后面的判断和调用方便。没想到还有点难 ...

你这代码转换成机器码,执行效率跟我在地板发的代码差了十万八千里,好好学习吧。
回复

使用道具 举报

ID:213173 发表于 2024-1-18 08:26 | 显示全部楼层
dyx811 发表于 2024-1-17 22:23
总共6个按键位变量,我把它合在一起了,成了一个字节,只是想让它在后面的判断和调用方便。没想到还有点难 ...

假设6个按键分别接P2.3、2.4,P1.1~1.4
#define KeyFlag P2<<1&0x30|P1>>1&0x0f

unsigned char KeyScan(unsigned char da)
{
        switch(da)
        {
                case 0x3e: da=1; break;
                case 0x3d: da=2; break;
                case 0x3b: da=3; break;
                case 0x37: da=4; break;
                case 0x2f: da=5; break;
                case 0x1f: da=6; break;
                default:   da=0; break;
        }       
        return da;
}
回复

使用道具 举报

ID:1073939 发表于 2024-1-18 08:40 | 显示全部楼层
dyx811 发表于 2024-1-17 22:23
总共6个按键位变量,我把它合在一起了,成了一个字节,只是想让它在后面的判断和调用方便。没想到还有点难 ...

你这段代码可优化。节省一个全局变量,代码也可简短些。

  1. unsigned char KeyScan(unsigned char KeyFlag)
  2. {
  3.         unsigned char da=0xff;
  4.         if(KeyFlag==1)
  5.         {
  6.                 if(key1==0)da &= 0xfe;
  7.                 if(key2==0)da &= 0xfd;
  8.                 if(key3==0)da &= 0xfb;
  9.                 if(key4==0)da &= 0xf7;
  10.                 if(key5==0)da &= 0xef;
  11.                 if(key6==0)da &= 0xdf;
  12.          }
  13.          return da;
  14. }
复制代码


回复

使用道具 举报

ID:458247 发表于 2024-1-18 08:57 | 显示全部楼层
wulin 发表于 2024-1-18 08:26
假设6个按键分别接P2.3、2.4,P1.1~1.4
#define KeyFlag P21&0x0f

typedef struct
{
    unsigned int key1 : 1;
    unsigned int key2 : 1;
    unsigned int key3 : 1;
    unsigned int key4 : 1;
    unsigned int key5 : 1;
    unsigned int key6 : 1;
}TsMAIN_h_KeyValueStatType;

typedef union
{
    TsMAIN_h_KeyValueStatType keyStat;
    unsigned char             keyValue;
}TuMAIN_h_KeyValueUnionType;

TuMAIN_h_KeyValueUnionType SsMAIN_h_KeyValue;

unsigned char GetKeyValue( unsigned char KeyFlag )
{
    if( 1== KeyFlag )
    {
        SsMAIN_h_KeyValue.keyStat.key1 = (!key1) ? 0 : 1;
        SsMAIN_h_KeyValue.keyStat.key2 = (!key2) ? 0 : 1;
        SsMAIN_h_KeyValue.keyStat.key3 = (!key3) ? 0 : 1;
        SsMAIN_h_KeyValue.keyStat.key4 = (!key4) ? 0 : 1;
        SsMAIN_h_KeyValue.keyStat.key5 = (!key5) ? 0 : 1;
        SsMAIN_h_KeyValue.keyStat.key6 = (!key6) ? 0 : 1;
    }
}
回复

使用道具 举报

ID:458247 发表于 2024-1-18 09:19 | 显示全部楼层
yzw846562238 发表于 2024-1-18 08:57
typedef struct
{
    unsigned int key1 : 1;

return SsMAIN_h_KeyValue.KeyValue;
回复

使用道具 举报

ID:883242 发表于 2024-1-18 14:28 | 显示全部楼层
yzw846562238 发表于 2024-1-18 08:57
typedef struct
{
    unsigned int key1 : 1;

这算是个通用的做法,在有特殊位寻址功能的平台上,比如51的效率就非常差劲了。

加强知识的学习吧。
回复

使用道具 举报

ID:485350 发表于 2024-1-18 15:34 | 显示全部楼层
void Key_Scan()                //按键检测 1ms调用
{
        u8 i;
        u8 Loop_Bit = 0x01;
    u8 Key = 0;
       
        if(KEY1_IO == 0)
        {
                Key |= KEY_BIG;
    }
   
    if(KEY2_IO == 0)
        {
                Key |= KEY_SMALL;
    }
   
        for(i = 0; i < KEYNUM; i++)
        {
                if((Key_Lock & Loop_Bit) == 0)                                                        //唯一按键
                {
                        if(Key & Loop_Bit)
                        {
                                if(++Key_Count[i] >= 50)                        //消抖50ms
                                {
                                        Key_Count[i] = 0;
                                        Key_Down |= Loop_Bit;        //按键触发
                                        Key_Lock |= Loop_Bit;                        //按键锁住         
                                }
                        }
                        else
                        {
                                Key_Count[i] = 0;
                        }
                }
                else
                {
                        if((Key & Loop_Bit) == 0)
                        {
                                if(++Key_Count[i] >= 40)                        //消抖50ms
                                {
                                        Key_Count[i] = 0;
                                        Key_Lock &= ~Loop_Bit;                        //按键锁解除
                                        Key_Up |= Loop_Bit;
                                }
                        }
                        else
                        {
                                Key_Count[i] = 0;
                        }
                }
                Loop_Bit <<= 1;
        }
}
回复

使用道具 举报

ID:34149 发表于 2024-1-18 17:22 | 显示全部楼层
ydatou 发表于 2024-1-18 08:40
你这段代码可优化。节省一个全局变量,代码也可简短些。

利用了静态变量可以不用每次都初始化da,这样子不能做多键同时按下的准确判断。
回复

使用道具 举报

ID:34149 发表于 2024-1-18 17:35 | 显示全部楼层
yzw846562238 发表于 2024-1-18 09:19
return SsMAIN_h_KeyValue.KeyValue;

差点以为结构体可以自动返回呢,不过这种写法看的头晕。变量名字好长好长。但是非常的新颖!
现在就去验证该语句对空间占用,机器周期耗费情况。
回复

使用道具 举报

ID:34149 发表于 2024-1-18 17:57 | 显示全部楼层
wulin 发表于 2024-1-18 08:26
假设6个按键分别接P2.3、2.4,P1.1~1.4
#define KeyFlag P21&0x0f

这个还没有看得懂。同时按住两个按键返回值是如何变化的呢 ?
回复

使用道具 举报

ID:34149 发表于 2024-1-18 18:02 | 显示全部楼层
Hephaestus 发表于 2024-1-18 01:43
你这代码转换成机器码,执行效率跟我在地板发的代码差了十万八千里,好好学习吧。

一定好好学习。主要是高级点的单片机还没有咋个会呢。你发的代码是带指针的吗?一时半会还理解不透。

事实上,我是想把一堆的位变量,(我举的例子是6个,来自于不同的地方),放在一个字节里面,用8位分别代表其中各位的状态,方便后期仅对该字节的判断就知道哪些位有效,哪些组合满足要求,同时还有滤波,长按的判断等等。主要目的是不用分别对每个位变量判断短按长按等等。
回复

使用道具 举报

ID:34149 发表于 2024-1-18 19:53 | 显示全部楼层
18680365301 发表于 2024-1-18 15:34
void Key_Scan()                //按键检测 1ms调用
{
        u8 i;

这是咋玩的啊,开头几句就卡壳了
回复

使用道具 举报

ID:883242 发表于 2024-1-18 22:27 | 显示全部楼层
dyx811 发表于 2024-1-18 18:02
一定好好学习。主要是高级点的单片机还没有咋个会呢。你发的代码是带指针的吗?一时半会还理解不透。

...

8051内存地址20h~27h这8个字节每位都是可以直接访问的,具体使用方法参加我在地板位的代码。
回复

使用道具 举报

ID:624769 发表于 2024-1-18 23:05 | 显示全部楼层
dyx811 发表于 2024-1-17 22:23
总共6个按键位变量,我把它合在一起了,成了一个字节,只是想让它在后面的判断和调用方便。没想到还有点难 ...

sbit Temp1 = ACC^0;
sbit Temp2 = ACC^1;
sbit Temp3 = ACC^2;
sbit Temp4 = ACC^3;
sbit Temp5 = ACC^4;
sbit Temp6 = ACC^5;

unsigned char KeyScan(unsigned char KeyFlag)
{
        ACC = 0xff;
        if(KeyFlag==1)
        {
                Temp1 = key1;
                Temp2 = key2;
                Temp3 = key3;
                Temp4 = key4;
                Temp5 = key5;
                Temp6 = key6;
         }
         return ACC;
}
回复

使用道具 举报

ID:883242 发表于 2024-1-18 23:59 | 显示全部楼层
188610329 发表于 2024-1-18 23:05
sbit Temp1 = ACC^0;
sbit Temp2 = ACC^1;
sbit Temp3 = ACC^2;

你的代码太想当然了。

        ACC = 0xff;
        if(KeyFlag==1)
后面这一句执行的时候,前面那一句的结果已经被破坏掉了。
回复

使用道具 举报

ID:1073939 发表于 2024-1-19 14:29 | 显示全部楼层
dyx811 发表于 2024-1-18 17:22
利用了静态变量可以不用每次都初始化da,这样子不能做多键同时按下的准确判断。

请仔细分析下我给你的优化代码。
1:因为有这变量的初始换,才省了你代码中的一堆else。
2:临时变量和全局变量相比,代码空间更小,执行速度更快。计算越复杂,用临时变量优化效果越明显。你就是一定要使用全局变量,也应该先用临时变量获得计算结果,之后赋值给全局变量。
3.静态变量是全局变量的一种,但限制了使用范围。从你的代码看,使用静态变量无意义。
回复

使用道具 举报

ID:34149 发表于 2024-1-19 15:33 | 显示全部楼层
ydatou 发表于 2024-1-19 14:29
请仔细分析下我给你的优化代码。
1:因为有这变量的初始换,才省了你代码中的一堆else。
2:临时变量和 ...

谢谢指点,突然恍然大悟了!!我居然把把&运算算错了。。。光想着怎么复原该Bit位了。
这样子确实精简了好多代码。非常感谢。
回复

使用道具 举报

ID:883242 发表于 2024-1-19 16:23 | 显示全部楼层
dyx811 发表于 2024-1-19 15:33
谢谢指点,突然恍然大悟了!!我居然把把&运算算错了。。。光想着怎么复原该Bit位了。
这样子确实精简了 ...

都不对。

代码是否被简化,以c语言的简洁程度来判断是错误的。

只有汇编码,也就单片机执行的机器码,变简洁了,才是真的优化。

你们搞的都是假优化。

评分

参与人数 1黑币 +5 收起 理由
dzbj + 5 赞一个!

查看全部评分

回复

使用道具 举报

ID:1073939 发表于 2024-1-19 16:36 | 显示全部楼层
ydatou 发表于 2024-1-19 14:29
请仔细分析下我给你的优化代码。
1:因为有这变量的初始换,才省了你代码中的一堆else。
2:临时变量和 ...

再看了下楼主代码,当KeyFlag 不为1时,要返回上一次的key值,这确实需要一个全局变量,我给的优化代码没做到这点。
修改如下
  1. unsigned char KeyScan(unsigned char KeyFlag)
  2. {
  3.         static unsigned char old=0xff;
  4.         if (KeyFlag == 1)
  5.         {
  6.                 unsigned char da = 0xff;
  7.                 if (key1 == 0)
  8.                         da &= 0xfe;
  9.                 if (key2 == 0)
  10.                         da &= 0xfd;
  11.                 if (key3 == 0)
  12.                         da &= 0xfb;
  13.                 if (key4 == 0)
  14.                         da &= 0xf7;
  15.                 if (key5 == 0)
  16.                         da &= 0xef;
  17.                 if (key6 == 0)
  18.                         da &= 0xdf;
  19.                 old=da;
  20.         }
  21.         return old;
  22. }
复制代码


假如是使用51MCU,使用位变量更好,如下
  1. unsigned char bdata gKey = 0xff;
  2. sbit bKey1 = gKey ^ 0;
  3. sbit bKey2 = gKey ^ 1;
  4. sbit bKey3 = gKey ^ 2;
  5. sbit bKey4 = gKey ^ 3;
  6. sbit bKey5 = gKey ^ 4;
  7. sbit bKey6 = gKey ^ 5;
  8. unsigned char KeyScan(unsigned char KeyFlag)
  9. {
  10.         if (KeyFlag == 1)
  11.         {
  12.                 bKey1 = key1;
  13.                 bKey2 = key2;
  14.                 bKey3 = key3;
  15.                 bKey4 = key4;
  16.                 bKey5 = key5;
  17.                 bKey6 = key6;
  18.         }
  19.         return gKey;
  20. }
复制代码


回复

使用道具 举报

ID:34149 发表于 2024-1-20 21:22 | 显示全部楼层
ydatou 发表于 2024-1-19 16:36
再看了下楼主代码,当KeyFlag 不为1时,要返回上一次的key值,这确实需要一个全局变量,我给的优化代码没 ...

bdta太好用了!不光完全实现了一堆位变量的整合,经过实验,不光实现了既定要求,程序上还节省了大量空间。
我采用了  ydatou 大哥的编程思路,简单,高效!
在此,感谢各位大佬们的热心帮助!
期望大家在单片机领域有更深的造诣,在编程道路上技术越来越精湛!
祝愿各位在龙年   {“龙腾虎跃!心想事成!”}

回复

使用道具 举报

ID:883242 发表于 2024-1-20 21:38 | 显示全部楼层
dyx811 发表于 2024-1-20 21:22
bdta太好用了!不光完全实现了一堆位变量的整合,经过实验,不光实现了既定要求,程序上还节省了大量空间 ...

我早就告诉你了你不看。
回复

使用道具 举报

ID:196791 发表于 2024-1-21 00:34 来自手机 | 显示全部楼层
学习各大佬的编程思路,深受启发
回复

使用道具 举报

ID:1101997 发表于 2024-1-21 08:31 | 显示全部楼层
我们追求效率肯定是没有错的。对于现在的处理器来说,内存空间和运行速度基本上都是能满足一般运行要求的,对于项目来说,可靠健壮的代码和清晰明了的结构,是当前需要重点关注的。
回复

使用道具 举报

ID:1101997 发表于 2024-1-21 08:34 | 显示全部楼层
我们追求效率肯定是没有错的。
对于现在的处理器来说,内存空间和运行速度基本上都是能满足一般运行要求的,对于项目来说,可靠健壮的代码和清晰明了的文件架构,也许我们需要更重点关注的。

回复

使用道具 举报

ID:420836 发表于 2024-1-21 09:06 | 显示全部楼层
一个字节由 8 位组成。 字节的按位操作广泛用于数字变量,例如开关状态、标志和数字输出。
回复

使用道具 举报

ID:883242 发表于 2024-1-21 12:31 | 显示全部楼层
TTQ001 发表于 2024-1-21 09:06
一个字节由 8 位组成。 字节的按位操作广泛用于数字变量,例如开关状态、标志和数字输出。

字节的概念多了去了,早年计算机一个字节6、7、8位的都有,TI C2000系列DSP一个字节16位,不存在8位操作,你还是见识的太少了。
回复

使用道具 举报

ID:34149 发表于 2024-1-21 18:12 | 显示全部楼层
Hephaestus 发表于 2024-1-20 21:38
我早就告诉你了你不看。

我看了,当时没有想到能这么用。主要看来还是学习少了!
以后定向大佬们学习!请受小弟一拜!!!!
回复

使用道具 举报

ID:849913 发表于 2024-2-9 22:58 | 显示全部楼层
可以 用一个8位的io端口啊 用6位 写程序时判断io端口的状态 如P1=0xc0 就是“”11000000“” 就是一起按了6个按键
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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