寄存器位域、位操作等示例

嵌入式中,与外设打交道较多,其实大部分的设置是直接操作寄存器的,而寄存器又被当成一个内存地址来使用。每个寄存器有不同的字段,表示不同的含义。常常使用的方法有移位和位域。我一般常用的移位,这是当年搞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日