打印

按字节保存多种不同类型数据时..我的做法和大家共享

[复制链接]
2193|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zq1987731|  楼主 | 2008-12-6 19:17 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    好多时候大家可能碰上要将一些比较古怪的自定义数据结构按字节放入一些片外器件里的情况,比如I2C总线的FLASH。
举个例子先:
============================================下面先定义一个要用到的结构============================================
    typedef unsigned char       UINT8;   
    typedef unsigned short int  UINT16;  
    typedef unsigned long int   UINT32;  
    typedef signed char         INT8;   
    typedef signed short int    INT6;  
    typedef signed long int     INT32;

    typedef struct
    {
        UINT8  D0[12];
        UINT16 D1[34];
        UINT32 D2[56]
        UINT32 D3 : 8;
        UINT32 D4 : 2;
        UINT32 D5 : 2;
        UINT32 D6 : 8;
        UINT32 D7 : 12; 
        INT8   D8[560];
        INT16  D9[126];
        INT32  D10[11];      
    }EXP_Struct_1;

============================================然后用上面的结构定义需要保存的内容============================================
    EXP_Struct_1 Save;

    这时候save结构体中假设已经有了需要进行存储的数据,只需要将相关数据全部转化为字节然后就能存储的时候,可以采用两种方法:
1,强制类型转换
memcpy( Buffer, (UINT8 *)&Save, sizeof(Save) );

memcpy是库函数,或自己实现的函数,功能是将(uint8 *)&Save为首地址,需拷贝的长度为sizeof(Save)的内容拷贝到Buffer中
函数原型void *memcpy(void *dest, const void *src, size_t n);

2,共用体的转换(利用其共享内存空间的特性)
typedef union
{
    EXP_Struct_1 Struct_Type;
    UINT8 Mem_Type[/*注意*/]; 
}TEMP_UNION;
/*方括号内填入的大小就是sizeof(Save)的大小*/

然后可以编写一个“发送”函数,在函数中定义并赋值:
TEMP_UNION Temp;
Temp.Struct_Type = Save;
    此时Temp.Mem_Type[]中就是你需要存储的结构体,这时候已经是uint8数组了...发送起来自然就可以很方便得用数组下标++,或者Temp.Mem_Type(该数组首地址)作为指针++的方式,然后发送完毕,作为auto变量存在的TEMP_UNION共用体Temp会被自动销毁(因为这是在“发送”函数内部定义的),所以绝对绿色+环保。

    我现在用的是第二种方法...毕竟第二种方法较第一种来得更直观,下面以最近的应用再举个例子:
    有这么个中断驱动扫描按键的应用,按一定时间的周期扫描24个按键的状态,按键为74HC165级联方式读入,按键模式有常开、常闭以及类似于拨码开关的类型,也就是所谓“没有按键”的时候,读入的并不是全1或全0,而是一个1及0组合的定值,读入的并不是“有无按键”而是“按键状态改变”
    我在处理的时候设定为定时器触发,每10ms一次,在中断中读入74HC165的值,按键值类型是这样定义的(由于使用32位MCU,为效率考虑,变量定义我都是按32位为主):
    typedef struct
    {   // Input Data
        UINT32 X027 : 1;        // LSB         
        UINT32 X026 : 1;
        UINT32 X025 : 1;
        UINT32 X024 : 1;
        UINT32 X023 : 1;
        UINT32 X022 : 1;
        UINT32 X021 : 1;
        UINT32 X020 : 1;
        UINT32 X017 : 1;
        UINT32 X016 : 1;
        UINT32 X015 : 1;
        UINT32 X014 : 1;
        UINT32 X013 : 1;
        UINT32 X012 : 1;
        UINT32 X011 : 1;
        UINT32 X010 : 1;
        UINT32 X007 : 1;
        UINT32 X006 : 1;
        UINT32 X005 : 1;
        UINT32 X004 : 1;
        UINT32 X003 : 1;
        UINT32 X002 : 1;
        UINT32 X001 : 1;
        UINT32 X000 : 1;  
        // Unused
        UINT32 UN07 : 1;
        UINT32 UN06 : 1;
        UINT32 UN05 : 1;
        UINT32 UN04 : 1;
        UINT32 UN03 : 1;
        UINT32 UN02 : 1;
        UINT32 UN01 : 1;
        UINT32 UN00 : 1;        // MSB  
    }Digital_Input_Struct; 

// 按键结构体声明(定义+赋值部分省略)
    extern volatile Digital_Input_Struct    DIN;
   
    这样的结构是为了使用起来方便,读入第一个按键时只需要判定DIN.X000为0和1就能知道目前按键的状态,且按键为10ms周期性扫描,通过一个可设定的读入次数变量可以做到读入X次均为1(0),该按键当前状态就为1(0),作消抖之用。下面定义了一个转换格式用的共用体:
    typedef union
    {
        Digital_Input_Struct    DIN_Struct;
        UINT32                  UINT32_TYPE;     
    }Digital_Input_Union; 
// 按键共用体声明(作为缓存,判定时用,定义+赋值部分省略)
    extern volatile Digital_Input_Union    DIN_Temp[/*设定大小*/];

    我在中断中要比较有无按键按下,只需要调用165读取函数,该函数自动将24个读入数据依次赋值给作缓存用的DIN_Temp[0]的X000~X007,X010~X017(8进制序号表示),X020~X027这24个成员中,【注意:在此处是以DIN_Temp[0].DIN_Struct.X000~DIN_Temp[0].DIN_Struct.X027作为被赋值对象这样的方式调用的】
    然后在中断程序中,我只需要判定DIN_Temp[0].UINT32_TYPE(这一次的按键状态)以及DIN_Temp[1].UINT32_TYPE(上一次的按键状态)DIN_Temp[2].UINT32_TYPE(上上次的按键状态),以此类推...因为结构体整体无法比大小,各个成员拿来比,在我这个应用中会显得很麻烦,但共用体将结构体转化成的UINT32型自然就可以了,最后假设需要确认4次即认定按键状态改变的话【即DIN_Temp[0].UINT32_TYPE~DIN_Temp[3].UINT32_TYPE这4个周期扫到的按键值相等,且和DIN_Temp[4].UINT32_TYPE及之后几个值不等】就认定按键的值被改变了,只需要将这个按键值以如此方式赋值:
DIN = DIN_Temp[0].DIN_Struct; //【结构体赋值给结构体,这类型要多匹配有多匹配】
============================================下面是总结============================================
    肯定还有很多好用的方法,希望大家也能拿来共享一下让本菜鸟开开眼界...- -

相关帖子

沙发
xwj| | 2008-12-6 19:35 | 只看该作者

不错,用union和结构是正解,但一定要先理解编译器的数据存

使用特权

评论回复
板凳
zq1987731|  楼主 | 2008-12-6 19:46 | 只看该作者

对了...说到这里我想起来了....

请大家注意我按键结构定义,其中有两个不起眼的注释:
// MSB
// LSB
这个结构作为UINT32读出来的时候就是按这个顺序的(RealView Compiler)这个不同的编译器有不同的存储方式安排,但如果只是要打包成UINT8然后保存,从器件中读回时也是以UINT8读回进结构体中再以结构体成员方式调用的话,这个保存顺序就显得可以忽略了,就好比有的FLASH,为了保护内部数据就采用了数据线乱连的方式,但事实上怎么进去的就怎么出来...但如果希望读出的数据也要按你所希望的格式排列,那就需要像我一样了,按键的结构我就是按编译器认定的顺序来排列的:
(这么做的缺点在于不同编译器间移植时需要修改,当然如果你一直用同一种编译器,那就无所谓了)

使用特权

评论回复
地板
xhtxzxw| | 2008-12-6 20:13 | 只看该作者

嘿嘿

如果仅仅为了"按字节访问"的话,需要这样的"显式"定义吗?
似乎,这样做更多的是"给人看"的,而不是"给compiler看"的?

anytype somevar;
char * pchar;

... ...
pchar = (char *)&somevar;
... ...
//此后,凡是涉及到对somevar的"按字节访问",用这个pchar就行了.

使用特权

评论回复
5
ayb_ice| | 2008-12-6 20:52 | 只看该作者

直接用强制类型更方便

使用特权

评论回复
6
xwj| | 2008-12-6 20:57 | 只看该作者

是的,只为整个保存的话,直接用强制类型指针最方便

但除非是整个保存,否则可读性一般都会很差
有时候可能连自己都要想半天:-)

使用特权

评论回复
7
zq1987731|  楼主 | 2008-12-6 21:04 | 只看该作者

如果只是搬运数据的话,确实强制类型转换足以...

    我用union主要是为了某些特殊用途的方便,比如我所用到的“按键状态比较”...
不然的话用如下判定加在if()里...估计除了【软件代码按行计报酬】之外的人都会觉得有些麻烦的吧...
Temp1.X000 == Temp2.X000 &&
Temp1.X001 == Temp2.X001 &&
//....... 
Temp1.X027 == Temp2.X027

    另外强制类型转换的指针一重的还好,要是同时有多个多重指针一起用,就比较容易出些小错误,为了减少出错概率和便于维护考虑我才用union的..
    况且强制类型转换以及union方法的编译效率是一样的,因为对编译器而言这两类方法是一个意思,无非就是从固定地址空间取数据而已...
    

使用特权

评论回复
8
ayb_ice| | 2008-12-6 21:10 | 只看该作者

可以用宏定义提高可读性

#define ps16(a) ((int*)&(a))[0]

使用特权

评论回复
9
zq1987731|  楼主 | 2008-12-6 21:24 | 只看该作者

宏定义的方法确实不错..

    我本来也考虑过用宏定义,但因为软件规模稍微大了点导致宏定义多达上千个...而我又没做什么详细文字记录,最后乱成一团..为了防止宏定义互相冲突,就不敢往里胡乱追加了..这个纯属个人失误...

使用特权

评论回复
10
ayb_ice| | 2008-12-6 21:55 | 只看该作者

我的项目有个MACRO.H头文件

全部打印出来也大概有十几页

使用特权

评论回复
11
原野之狼| | 2008-12-6 22:32 | 只看该作者

不错,学习~

有个疑问,结构体能直接拷贝么?

使用特权

评论回复
12
zq1987731|  楼主 | 2008-12-7 11:08 | 只看该作者

这个啊..

    结构体直接拷贝的话,如果是拷贝给相同结构定义的结构体,那自然是没问题。
    若是要拷贝到一维字节型(8位整型)数组...这个“直接”就麻烦了,也就是要把结构中的成员都转化为字节型,假设结构体中有个成员:32位整型数组X[13],那么首先要将它转化为8位整型数组X[52],然后再追加到目标数组后面,然后再处理下个成员...简直就是——吃力不讨好

使用特权

评论回复
13
xhtxzxw| | 2008-12-8 22:00 | 只看该作者

嘿嘿

没什么复杂的.

typedef struct
{
    ... ...
}structA;                  //你所定义的结构
structA the_struct_var;    //这种结构的变量

char buf[sizeof(structA)]; //准备存储结构变量中各个字节的字符形数组
char * p;                  //定义一个字符指针

... ...

p = (char *)&the_struct_var;          //用字符指针指向这个结构变量
for(i=0; i<sizeof(structA);i++)       //一个字节一个字节的复制
    buf = *p++;

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

95

主题

759

帖子

3

粉丝