嵌入式系统最明显的特征是必须直接访问硬件,软件可访问的硬件可分为四种:基础设施、通信、传感器和致动器。
基础设施硬件是指运行软件的计算机基础设施和设备,不仅包括CPU和内存,还包括存储设备、定时器、输入/输出设备、端口和中断等。
通信硬件是指在不同的计算机设备之间用于建立连接的硬件。
传感器和致动器是指用来检测和操纵物理单元的设备。
使用位域对硬件发出命令或返回数据是非常常见的。位域是可访问的内存单元中连续的比特块(如字节或字),组合在一起对硬件有一定的语义。例如,一个8位的字节可能分为四个不同的域映射到硬件设备。
0 0 0000 00
这些位表示内存映射硬件设备中的信息如下:
位域是使用C语言中的位运算符操作的,其中包括:&(按位与)、|(按位或)、~(按位非)、^(按位异或)、>>(右移)、<<(左移)。通常在C语言编程中的习惯做法是使用#define为与运算和或运算创建位掩码,并且赋予它们有意义的名字,如下:
#include
#include
#define TURN_OFF (0x00)
#define INITIALIZE (0x61)
#define RUN (0x69)
#define CHECK_ERROR (0x02)
#define DEVICE_ADDRESS (0x01FFAFD0)
void emergencyShutDown(void){
printf("OMG We're all gonna die!\n");
}
int main() {
unsigned char* pDevice;
pDevice = (unsigned char *)DEVICE_ADDRESS; // pt to device
// for testing you can replace the above line with
// pDevice = malloc(1);
*pDevice = 0xFF; // start with all bits on
printf ("Device bits %X\n", *pDevice);
*pDevice = *pDevice & INITIALIZE; // and the bits into
printf ("Device bits %X\n", *pDevice);
if (*pDevice & CHECK_ERROR) { // system fail bit on?
emergencyShutDown();
abort();
} else {
*pDevice = *pDevice & RUN;
printf ("Device bits %X\n", *pDevice);
};
return 0;
};
左移和右移操作对于孤立的设置以及测试特殊的位非常有用,并且在串行位数据处理中也有用。如表达式“1<<3”设置3位得到值8,代码如下:
#define CHECKERROR (1<<3)
C语言中的位域提供了另一种方法来表示设备接口位映射域。这个语法用结构体中的字段表示可变长度位域。: 运算符在定义中分离字段的长度和名字,代码如下:
#include
#include
int main() {
typedef struct _statusBits {
unsigned enable : 1;
unsigned errorStatus : 1;
unsigned motorSpeed : 4;
unsigned LEDColor : 2;
} statusBits;
statusBits status;
printf("size = %d\n",sizeof(status));
status.enable = 1;
status.errorStatus = 0;
status.motorSpeed = 3;
status.LEDColor = 2;
if (status.enable) printf("Enabled\n");
else printf ("Disabled\n");
if (status.errorStatus) printf("ERROR!\n");
else printf("No error\n");
printf ("Motor speed %d\n",status.motorSpeed);
printf ("Color %d\n",status.LEDColor);
return 0;
};
C语言中的位域有两个问题。首先,位序有编译器的处理器的依赖性;其次,编译器可能会强制字节填充规则。如上述代码中,在GUN C编译器返回状态长度为4个字节,即使一个无符号的字符的大小仅为1字节。而且,因为大多数CPU必须每次写一个字节或字,位域有可能不在一个原子步骤内写入,如果不同的位域使用单独的互斥信号,将导致线程安全问题。
另一个使用位域的潜在问题是,不可能在标量和用户自定义的结构体之间强制转换。因此,以下做法不被允许:
unsigned char f;
f = 0xF0;
status = (statusBits)f;
硬件代理模式(Hardware Proxy Pattern)创建软件单元负责访问硬件的一部分、硬件压缩封装以及编码实现。
硬件代理模式使用类(或结构体)封装所有硬件设备访问,无论其硬件接口是怎样的。代理为客户提供接口,用来从设备中读取或写入数据,以及初始化、配置和关闭设备等。
通过提供位于客户和实际硬件之间的代理,解决了多个客户访问硬件所造成的各种问题,极大地限制了硬件改变的影响。同时,为了便于维护,设备使用的位编码、加密和压缩等细节将会通过硬件代理的内部私有方法来管理。
模式结构如图所示,模式中可能有多个客户,但每个被控设备仅有单一的硬件代理。代理包括公有和私有方法、封装函数和数据等。
该元素为具体硬件。
该元素包含为当前设备制定的数据和函数,通常包括init()、configure()和disable()等。其他一些公有方法提供向设备发送或接受设备数据的功能。硬件代理类中的关键功能有(以上述图中为例):
access() 此公有方法从设备中返回一个特殊值。
congfigure() 此公有方法提供配置设备的方法。
disable() 此公有方法提供设备安全关闭或禁用的方法。
deviceAddr 此私有变量提供底层直接访问硬件,它的数据类型由具体硬件设备来确定。在一些事件中,硬件代理模式提供的公有方法完全隐藏代理如何连接到实际设备。客户不能直接访问这个变量。
initialize() 此公有方法在第一次使用之前启动并初始化设备。
marshal() 此私有方法从各种其他方法中获取参数,并且可以执行任何需要加密、压缩或设备发送数据所需的位包装的操作。
mutate() 此公有方法向设备写入数据。
unmarshal() 此私有方法执行任何需要从设备中获取数据的解包、加密或解压缩操作。
调用硬件代理提供的接口访问硬件设备。
该模式简单的实现不能实现任何线程安全性。他可以与临界模式、守卫调用模式或队列模式组合使用以提供线程安全性。为避免死锁,它可以与排序模式和同时锁定模式组合使用。
带有内存映射的马达系统,接口有16位宽。马达代理的作用是以硬件接口独立的方式提供服务来访问硬件。
马达管理方法
马达状态方法
马达控制方法
马达错误管理方法
内部数据格式化方法(私有)