《C嵌入式编程设计模式》---读书笔记(1)

时间:2022-10-23 19:35:31

第3章 访问硬件的设计模式

3.1 基本的硬件访问概念

    嵌入式系统最明显的特征是必须直接访问硬件,软件可访问的硬件可分为四种:基础设施、通信、传感器和致动器。

    基础设施硬件是指运行软件的计算机基础设施和设备,不仅包括CPU和内存,还包括存储设备、定时器、输入/输出设备、端口和中断等。

    通信硬件是指在不同的计算机设备之间用于建立连接的硬件。

    传感器和致动器是指用来检测和操纵物理单元的设备。

    使用位域对硬件发出命令或返回数据是非常常见的。位域是可访问的内存单元中连续的比特块(如字节或字),组合在一起对硬件有一定的语义。例如,一个8位的字节可能分为四个不同的域映射到硬件设备。

      0 0 0000 00

    这些位表示内存映射硬件设备中的信息如下:

    《C嵌入式编程设计模式》---读书笔记(1)

       位域是使用C语言中的位运算符操作的,其中包括:&(按位与)、|(按位或)、~(按位非)、^(按位异或)、>>(右移)、<<(左移)。通常在C语言编程中的习惯做法是使用#define为与运算和或运算创建位掩码,并且赋予它们有意义的名字,如下:    

#include <stdlib.h>
#include <stdio.h>
#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 <stdlib.h>
#include <stdio.h>

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;



3.2 硬件代理模式

    硬件代理模式(Hardware Proxy Pattern)创建软件单元负责访问硬件的一部分、硬件压缩封装以及编码实现。

3.2.1 抽象

    硬件代理模式使用类(或结构体)封装所有硬件设备访问,无论其硬件接口是怎样的。代理为客户提供接口,用来从设备中读取或写入数据,以及初始化、配置和关闭设备等。

3.2.2 问题

    通过提供位于客户和实际硬件之间的代理,解决了多个客户访问硬件所造成的各种问题,极大地限制了硬件改变的影响。同时,为了便于维护,设备使用的位编码、加密和压缩等细节将会通过硬件代理的内部私有方法来管理。

3.2.3 结构模式

    模式结构如图所示,模式中可能有多个客户,但每个被控设备仅有单一的硬件代理。代理包括公有和私有方法、封装函数和数据等。

《C嵌入式编程设计模式》---读书笔记(1)

3.2.4 协作角色

3.2.4.1 硬件设备

    该元素为具体硬件。

3.2.4.2 硬件代理

    该元素包含为当前设备制定的数据和函数,通常包括init()、configure()和disable()等。其他一些公有方法提供向设备发送或接受设备数据的功能。硬件代理类中的关键功能有(以上述图中为例):

    access()    此公有方法从设备中返回一个特殊值。

    congfigure()    此公有方法提供配置设备的方法。

    disable()    此公有方法提供设备安全关闭或禁用的方法。

    deviceAddr    此私有变量提供底层直接访问硬件,它的数据类型由具体硬件设备来确定。在一些事件中,硬件代理模式提供的公有方法完全隐藏代理如何连接到实际设备。客户不能直接访问这个变量。

    initialize()    此公有方法在第一次使用之前启动并初始化设备。

    marshal()    此私有方法从各种其他方法中获取参数,并且可以执行任何需要加密、压缩或设备发送数据所需的位包装的操作。

    mutate()    此公有方法向设备写入数据。

    unmarshal()    此私有方法执行任何需要从设备中获取数据的解包、加密或解压缩操作。

3.2.4.3 代理客户

    调用硬件代理提供的接口访问硬件设备。

3.2.7 相关模式

    该模式简单的实现不能实现任何线程安全性。他可以与临界模式、守卫调用模式或队列模式组合使用以提供线程安全性。为避免死锁,它可以与排序模式和同时锁定模式组合使用。

3.2.8 实例

    带有内存映射的马达系统,接口有16位宽。马达代理的作用是以硬件接口独立的方式提供服务来访问硬件。

    马达管理方法

  •     configure()    此方法设置马达的内存映射地址与旋转臂的长度。
  •     disable()    此方法关闭马达,保持设置的值原封不动。
  •     enable()     此方法使用目前设定的值启动马达。
  •     initialize()   此方法使用默认的设置值启动马达。    

    马达状态方法

  •     accessMotorDirection()    此方法方法返回当前马达的方向。
  •     accessMotorSpeed()    此方法返回马达的速度。    

    马达控制方法

  •     writeMotorSpeed()    此方法设置马达的速度并且调整旋转臂的长度。

    马达错误管理方法

  •     clearErrorStatus()    此方法清除所有的错误位。
  •      accessMotorState()    此方法返回错误状态。

    内部数据格式化方法(私有)

  •     marshal()    把客户数据格式转换为马达数据格式。
  •     unmarshal()    把马达数据格式转换为客户格式。