Follow our illustrated blog on Embedded Software Architecture

How to build a hybrid solar/wind energy harvester?

How procedural code evolves to object-oriented code

How procedural code evolves to object-oriented code, 8.0 out of 10 based on 1 rating
VN:F [1.9.22_1171]
Rating: 8.0/10 (1 vote cast)

Introduction

Most (real :-)) embedded software is written in C. Although C is a general-purpose procedural programming language, it is perfectly possible and useful to write object-oriented code in it. Object-orientation – well, GOOD object-orientation – allows for better code maintenance by managing code complexity.

Actually, there are two ways to manage software complexity:

  • abstractions, and
  • divide-and-conquer.

While divide-and-conquer is an algorithm design paradigm, thinking in OO (a programming paradigm) – in combination with strong domain knowledge – can help us to find and write good abstractions. But be warned, bad OO is sometimes worse, than no OO at all. In the following sections, we demonstrate how a small piece of pseudo-code evolves from procedural to object-oriented.

Bottom-up without abstraction

Consider the following pseudo-code snippet. The code blinks a GPIO LED called ‘LEDx’ when a device is ‘processing something’.

IF is_processing_something THEN
	state = read_bit_in_gpio_reg of LEDx
	IF state == 0 THEN
		set_bit_in_gpio_reg of LEDx
	ELSE
		clear_bit_in_gpio_reg of LEDx
	END
ELSE
	clear_bit_in_gpio_reg of LEDx
END

 

This code reads and manipulates bits in GPIO registers, and is clearly the result of bottom-up thinking without abstraction. Nothing wrong with that as long as it doesn’t get any more complex (unfortunately, most low-cost embedded systems do get more complicated but a rework/redesign is never done…).

Let us move on to the next level of complexity. Suppose we want to support active-high and active-low GPIO leds. The code then becomes:

IF is_processing_something THEN
	state = read_bit_in_gpio_reg of LEDx
	IF state == 0 THEN
		IF LEDx is active_high THEN
			set_bit_in_gpio_reg of LEDx
		ELSE
			clear_bit_in_gpio_reg of LEDx
	ELSE
		IF LEDx is active_high THEN
			clear_bit_in_gpio_reg of LEDx
		ELSE
			set_bit_in_gpio_reg of LEDx
	END
ELSE
	IF LEDx active_high THEN
		clear_bit_in_gpio_reg of LEDx
	ELSE
		set_bit_in_gpio_reg of LEDx
END

 

And this is where complexity starts: nested IFs and first signs of code duplication. The time to refactor this code is NOW. And therefor, we introduce the most fundamental abstraction: the ‘function’ :-).

The function

IF is_processing_something THEN
	IF is_on(LEDx) THEN
		led_off(LEDx)
	ELSE
		led_on(LEDx)
	END
ELSE
	led_off(LEDx)
END

 

Or even better:

IF is_processing_something THEN
	led_toggle(LEDx)
ELSE
	led_off(LEDx)
END

 

This is good application code! Only one level of abstraction, implementation details are hidden (active_high/low, gpio). The LED interface is such that the application developer can focus on the algorithm.

Some developers prefer to write application code before the functions are implemented. This approach is called top-down. The danger here is over-engineering (too much abstraction) or code that does not map very well on the lower software layers (bad abstraction, bad interfaces).

Evolving to object-orientation

Although controlling LEDs by GPIO is very common in embedded systems, there are other ways to implement the hardware e.g. pwm, LED drivers (e.g. ti tlc), DACs. But of course, as a user, you want to have a unified interface which supports multiple implementations (polymorphism). And this is exactly what the leds-class framework of the Linux kernel does.

A user can control leds via sysfs (the unified interface, see /sys/class/leds/<device>). Behind this interface a led framework takes care of different implementations. The core of the framework uses basic OO in order to be device-agnostic: it would be bad to have to change the core each time a new led device is added. Developers do not like to repeat ‘switch/case’-code like this:

FUNCTION led_off(LED led)
	SWITCH led_type(led)
		CASE LED_GPIO THEN do_gpio_stuff END
		CASE LED_PWM THEN do_led_pwm_stuff END
		...
	END
END

 

Maintenance becomes harder when these kind of switch/case statements come back repeatedly in the code (suppose a new device must be added…).

Back to the Linux led framework. A new led device can be added by implementing struct led_classdev (there are several examples available in drivers/leds directory). In this struct OO is achieved by function pointers (e.g. blink_set, brightness_set). The new device (struct led_classdev) registers itself in the core (include/linux/leds.h, leds-class.c, leds-core.c). The core only calls the function pointers and does not need to know about the underlying led hardware.

 

VN:F [1.9.22_1171]
Rating: 8.0/10 (1 vote cast)
Share
About rtos.be

rtos.be is a joined initiative from Gert Boddaert and Pieter Beyens.
More info: about us, our mission, contact us.

Speak Your Mind

*