嵌入式中,与外设打交道较多,其实大部分的设置是直接操作寄存器的,而寄存器又被当成一个内存地址来使用。每个寄存器有不同的字段,表示不同的含义。常常使用的方法有移位和位域。我一般常用的移位,这是当年搞AVR单片机时留下的纪念,我也自认为移位方式十分好用。另外的位域,其实我没实际使用过。
本文就虚拟一个寄存器(其实是一个变量而已)及其字段,当然,仅具有演示意义,不代表实际的场合。
使用位操作时,就是通过左移、右移来组装不同字段的数据,在移位时一定要记得使用掩码,否则数据有可能会错乱。见代码的test_1函数示例。
使用位域时,就是设置好各个字段的位域,然后一一赋值,再利用联合体的特性来对寄存器赋值,位域最好用联合体,方便很多。见代码的test_2函数示例。
两种方法各有优点,看个人取舍吧。我是偏向使用移位的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| #include <stdio.h> /* 测试结果: struct test register_value: 0x0000fead enable: 1 type: 6 id: a value: fe union test register_value: 0x0000fead enable: 0 type: 6 id: a value: fe */ /** 虚拟的寄存器,各字段: 0: enable 1~3: type 4~7: id 8~15:value 16~31: reserve */ unsigned int register_value = 0; // 结构体式 typedef struct { unsigned int enable; unsigned int type; unsigned int id; unsigned int value; }DEV_IO_ARG; int test_1(void) { DEV_IO_ARG io_arg_tmp; DEV_IO_ARG *io_arg = &io_arg_tmp; // 组装 io_arg->enable = 0x1; io_arg->type = 0x6; io_arg->id = 0xa; io_arg->value = 0xfe; register_value = (io_arg->value << 8) | (io_arg->id << 4) | \ (io_arg->type << 1) | (io_arg->enable << 0); printf("register_value: 0x%08x\n", register_value); // 分解 io_arg->enable = (register_value >> 0) & 0x01; io_arg->type = (register_value >> 1) & 0x07; io_arg->id = (register_value >> 4) & 0x0f; io_arg->value = (register_value >> 8) & 0xff; printf("enable: %x type: %x id: %x value: %x\n", io_arg->enable, io_arg->type, io_arg->id, io_arg->value); } // 联合体式 typedef struct { unsigned int enable: 1; unsigned int type: 3; unsigned int id: 4; unsigned int value: 8; unsigned int reserve: 16; }DEV_IO_ARG_1; typedef union { unsigned int tmp; DEV_IO_ARG_1 arg; }DEV_IO_ARG_UNION;
void test_2(void) { DEV_IO_ARG_UNION io_arg_tmp = {0}; io_arg_tmp.arg.enable = 1; io_arg_tmp.arg.type = 0x6; io_arg_tmp.arg.id = 0xa; io_arg_tmp.arg.value = 0xfe; register_value = 0; register_value = io_arg_tmp.tmp; printf("register_value: 0x%08x\n", register_value); // 前面enable为1,这里减去1,理论上enable应该为0。 io_arg_tmp.tmp = register_value - 1; printf("enable: %x type: %x id: %x value: %x\n", io_arg_tmp.arg.enable, io_arg_tmp.arg.type, io_arg_tmp.arg.id, io_arg_tmp.arg.value); } int main(void) { printf("struct test\n"); test_1(); printf("union test\n"); test_2(); }
|
注:本文内容简单,起因也很简单,公司某部门某些人员可能没搞过嵌入式,没见过位域的用法,但又要搞那一堆代码,不懂了,就问我们部门的人。该同事(和我十分好的那种)和我聊了,于是就有此文。有时想想,想不明白凭什么别人不懂的问题就可以找我们,我们不懂的问题就要自己查。
李迟记于2014年6月7日