CMP EMBEDDED.COM

Login | Register     Welcome Guest  
HOME DESIGN PRODUCTS COLUMNS E-LEARNING CONFERENCES CODE FORUMS/BLOGS NEWSLETTERS CONTACT FEATURES RSS RSS

An architecture for designing reusable embedded systems software, Part 2
Want to make your application software more reusable? Don't change the hardware, operating system, or your tools. Instead change the architectural framework within which you do your design. Part 2 in the three-part series shows building blocks for the portable code software structure.



Embedded.com

INTERFACE.H:
I/O interface macros

The final step in designing a highly portable architecture is to define the interface layer that contains the macros for importing/exporting variables to/from the core software. Consider the following example:


#define ssGet_sensor_signal()
(signed short int) (RAW_ANALOG_INPUT.SENSOR1)
#define ssGet_nvram_data ()
(NVRAM_STRUCTURE.DATA_REGISTER)
#define ssPut_nvram_data ( x )
(NVRAM_STRUCTURE.DATA_PORT
= (x) )

This interface is defined by Get and Put macros whose definitions are hardware, machine and/or architecture dependent. In this previous section of code, the ssGet_sensor_signal macro looks for a signed short data structure (RAW_ANALOG _INPUT) that is memory mapped to the A/D converters and picks out the SENSOR1 port of the A/D converter. By developing a set of import/export interface macros, the algorithm developer can code the core algorithm without being concerned about the origin for the signal. This method also lends itself easily to unit testing. When engineering is unit testing a software module on a PC, the interface for the signal sources are modified to reflect the simulated or real signals available for the test. Once the module is unit tested, the interface is written for the actual terminators of the system. The means, which is encapsulated by the macro, is of little interest once the interface is designed. What's important is the value of the sensor signal at a given time. Therefore, the interface in the main code would consist of Get and Put statements. When the hardware architecture changes, only the macro definition is modified to correspond to the change. The core software is completely void of any interface code with the exception of an interface header that links the outside real world to the inside micro-world.

The main types of macros defined in this interface header file consist of:

1. Reading input signals


#define Get_Veh_Lat_Accel()
( GET_ACCELERATION ( adtREG.ADR03, UW_Y_ACCEL_ZERO_POINT) )

#define Get_RC_HT_SENS() ( GET_HEIGHT (adtREG.ADR16, UW_HS_ZERO_POINT) ) #define Get_BatteryVoltage() ( GET_VOLTAGE (adtREG.ADR04, UW_VBAT_SCALE, UW_VBAT_ZERO_PT) )

In this case, the macro is used as a preprocessor definition that calls a function (i.e., GET_ACCELERATION(), GET_HEIGHT, or GET_VOLTAGE). These functions filter the input signal and remove any zero-point bias offsets. They're written specific to the requirements for processing the signals. The user interfaces to the signal in the software via the get_ macro. If a signal requires no processing, the microcontroller port may be accessed directly as in the following example:


#define Get_Accel_A2D_V()
( adtREG.ADR00 >> 6 )
// 10bit A/D MSB justified

In this case, the voltage is directly read from the 10-bit analog to digital converter (i.e., register ADR00 in the A to D converter), which uses a 16-bit data type that is most-significant-bit justified and requires to be shifted right by six bits. The next example interrogates a digital input to determine if the ignition state is on by masking out a bit in the Port B register.

#define Get_IgnitionState()
( FLAGS.IGNITION_OFF =
~( PORTB & PB0 ) )

If the register is changed, the macro is updated and the rest of the software is left untouched.

2. Driving/writing to outputs


#define Put_TurnOnAccelSensPower()
( PORTA |= PA7 )
#define Put_TurnOffAccelSensPower()
( PORTA &= ~PA7 )
#define Put_TurnOffECUPowerSupply()
( PORTB &= ~PB7 )
#define Put_TurnOnECUPowerSupply()
( PORTB |= PB7 )

In the examples above, the put statements define a bit function on a microcontroller port. The put macro is used to write/drive an output to a desired state in the core software. The put macro can take several forms including calling a function or writing a variable to a memory mapped I/O register.

3. Defining internal microcontroller interfaces


#define MAIN_LOOP_TIME_INTERRUPT (!(TIMER_FLAG & BIT1))

In this example, the I/O never reaches the outside world of the microcontroller. It's specific to an I/O signal within the microcontroller. The goal of the macro is to determine when the timer interrupt has occurred, which signals the start of a main loop timer. Another example would be to create a macro specific to the compiler for generating an assembly language op-code that would disable all interrupts.


#define Put_DisableAllInterrupts() (_asm("sei"))
// inhibit all maskable interrupts

Next in Part 3: Additional elements in the reusable system software architecture.

Dinu P. Madau is a Software Technical Fellow with Visteon. He has been developing software for embedded systems for over 22 years. He has an MSE in computer and electrical control systems engineering from Wayne State University and a BSE in computer engineering. Dinu has developed safety-critical software for anti-lock brakes, vehicle stability control, and suspension controls and is currently working in Advanced Global Technologies at Visteon developing driver awareness systems leveraging vision and radar technologies. He can be reached by e-mail at dmadau@visteon.com

1 | 2

Rate this article: Low High
Current rating
  • .
Embedded.com Career Center
Looking for a new job?
SEARCH JOBS

Browse all jobs

SPONSOR
RECENT JOB POSTINGS





 :