Hardware/Peripheral Device Design Pattern

来源:百度文库 编辑:神马文学网 时间:2024/04/20 11:01:17
Hardware Device Design Pattern
We have already looked at classes that provide a high level interface to theunderlying hardware (e.g.Serial Port).Here we will look at the design of classes corresponding to the individualdevices. The main objective is to keep all register programming information atone place.
Intent
The Hardware Device Design Pattern encapsulates the actual hardware devicebeing programmed. The main idea is to encapsulate device register programmingand bit manipulation into a single class dealing with the device.
Also Known As
Device
Hardware Interface
Peripheral Interface
Peripheral
Motivation
Very often the lowest level of code that interfaces with the hardware isdifficult to understand and maintain. One of the main reasons for this is the idiosyncrasiesof register level programming model of hardware devices.  Very oftendevices require registers to be accessed in a certain sequence. Defining a classto represent the device can go a long way in simplifying the code by decouplingthe low level code and register manipulation.
Another motivation for this design pattern is skill sets. Often details aboutintricacies of register programming in devices are understood only by thepersons familiar with the hardware design. Many times other low level code mightbe written by software engineers with just basic understanding of hardware.
Also note that separating the device programming and logic simplifies portingof the code to a different hardware platform.
Applicability
This pattern can be used to represent any hardware device.
Structure
The structure of class in this design pattern largely depends upon theregister programming model of the device being programmed. In most cases, thisdesign pattern would be implemented as a single class representing the device.In case of complex devices, the device might be modeled as a main device classand other subclasses modeling different parts of the device.
Participants
This design pattern generally interfaces with other classes that need toaccess hardware registers.
Collaboration
Collaboration between the device class and other classes would largely dependupon the purpose for which the device is being used. For example, a hardwaredevice that provides timers, serial ports and a DMA controller might be modeledjust as a serial device if other device functionality is not being used.
Consequences
The device involved in low level hardware programming is simplified asdetails about register manipulation have been hidden within the class. Thus thecode accessing the hardware device can focus on the logic of the operation beingperformed. As noted earlier, porting of hardware dependent software is alsosimplified.
Implementation
We will study the implementation of this pattern by working with an imaginaryserial device with the following register set:
Status Register (STAT): This read only register contains the following status bits: Bit 0: Transmit Buffer Has Empty Space
Bit 1: Receive Buffer Has Data
Bit 2: Transmit under run
Bit 3: Receive overrun
Action Register (ACT): Bits in this write only register correspond to the bits in the status register. A condition in the status register can be cleared by writing the corresponding bit as 1. Note that bit 0 automatically gets cleared when writes are performed to the transmit buffer. Bit 1 is cleared automatically when reads are performed from the receive buffer. Bit 2 and 3 however need to be cleared explicitly.
Transmit Buffer (TXBUF): Write only buffer in which bytes meant for transmission should be written.
Receive Buffer (RXBUF): Read only buffer in which received bytes are stored.
Sample Code and Usage
Here is the code for the Serial_Device class:
Serial_Device
class Serial_Device { enum Register_Offsets { STAT_REG_OFFSET = 0, ACT_REG_OFFSET = 0, TXBUF_OFFSET = 1, RXBUF_OFFSET = 2 }; enum Status_Register_Bits { TX_EMPTY, RX_DATA, TX_UNDERRUN, RX_OVERRUN }; const long m_status_Register; const long m_action_Register; const long m_transmit_Register; const long m_receive_Register; public: Serial_Device(long baseAddress) : m_status_Register(baseAddress + STAT_REG_OFFSET), m_action_Register(baseAddress + ACT_REG_OFFSET), m_transmit_Register(baseAddress + TXBUF_OFFSET), m_receive_Register(baseAddress + RXBUF_OFFSET) { } // Use this method to determine if a transmit interrupt might be // pending. bool Transmitter_Has_Space() const { return ((io_read(m_status_Register) & TX_EMPTY) == TX_EMPTY); } // This method returns true if a receive interrupt is pending bool Receiver_Has_Data() const { return ((io_read(m_status_Register) & RX_DATA) == RX_DATA); } // Returns true if transmit error interrupt is active bool Transmitter_Has_Error() const { return ((io_read(m_status_Register) & TX_UNDERRUN) == TX_UNDERRUN); } // Returns true if receive error interrupt is active bool Receiver_Has_Error() const { return ((io_read(m_status_Register) & RX_OVERRUN) == RX_OVERRUN); } // Clear all the error conditions void Clear_Errors() const { io_write(m_action_Register, TX_UNDERRUN | TX_OVERRUN); } // Write_Data transmits the specified number of bytes. All bytes // may not be transmitted due to transmit buffer space. The total // number of transmitted bytes is returned. int Write_Data(const char *pData, int byteCount) const { // Keep writing transmit bytes until all the bytes // have been transmitted or transmit buffer has no space for (int txCount=0; i < byteCount; txCount++) { if (!Transmitter_Has_Space()) { break; } // Write the byte to the transmit buffer for transmission io_write(m_transmit_Register, pData[txCount]); } // Return the count of transmitted bytes return txCount; } // Read_Data reads the bytes from the device. The maximum number // of bytes to be read can be specified. The actual number // of received bytes is returned. int Read_Data(char *pData, int byteLimit) const { // Keep reading received bytes until all the received bytes // have been copied or specified limit has been reached for (int rxCount=0; i < byteLimit; rxCount++) { if (!Receiver_Has_Data()) { break; } pData[rxCount] = io_read(m_receive_Register); } // Return count of received bytes return rxCount; } };
Known Uses
This pattern is used to decouple the logical device handling and deviceregister manipulation.
Related Patterns
Serial Port Design Pattern
High Speed Serial Port Design Pattern