By the end of this chapter, you will be able to:
Mastering these skills ensures you can confidently install and program controllers, making you a valuable asset in the field of microcontroller and microprocessor technology.
Programming microcontrollers and microprocessors is a critical skill for biomedical engineers working in Kenya’s healthcare technology sector. Devices such as patient monitors, infusion pumps, and diagnostic equipment rely on embedded controllers programmed for reliability and precision. This chapter equips students with foundational knowledge and practical skills to develop, design, and implement controller programs tailored to biomedical applications, ensuring safe and effective device operation in clinical and research settings.
Hardware programming means writing the code that runs directly on the microcontroller or microprocessor inside a medical device and controls its inputs and outputs. The program reads sensors, makes decisions and drives actuators within strict time limits, so it has to be reliable and easy to test. This topic gives the terms, the basic programming concepts and the methods used to design a controller program before it is written.
A programmer who works on a device must use the terms of the field correctly. They give a common language for design, testing and fault-finding.
The instruction set is the complete collection of commands that a microcontroller or microprocessor can execute. It defines the operations available, such as arithmetic, logic, data transfer and control of the program flow. When a heart rate monitor is programmed, the instruction set decides which operations are used to process the sensor data and produce the output.
Firmware is the software stored in a device's non-volatile memory that controls the hardware. Unlike general software, it is tied closely to the hardware and is usually stored in ROM or Flash memory. In a device such as an insulin pump, the firmware controls the timing and the amount of each dose.
Registers are small, fast storage locations inside the processor that hold data while instructions are carried out. They give quick access to operands and results. In an ECG machine, registers hold intermediate signal values during filtering and analysis.
An interrupt is a signal that temporarily stops the program so that an urgent event can be served, such as a sensor input or a hardware fault. Interrupt handling is important in devices where a late response can affect patient safety, such as an alarm on a monitoring system.
Input/output (I/O) ports are the connections through which a microcontroller communicates with sensors, actuators and other devices. Correct programming of the ports lets a device collect physiological data and control parts such as pumps and valves.
A controller program is built from a small number of concepts that decide how instructions are organised and carried out. Understanding them helps a technician to read firmware and to write programs that are efficient, reliable and easy to maintain.
A program normally runs its instructions one after another, unless a control statement changes the order. In a blood glucose meter the steps must follow a fixed order: apply the test strip, measure, calculate and display, or the reading would be wrong.
Control structures such as loops, conditions and switches let a program make decisions and repeat actions. A ventilator controller may use a loop that samples the oxygen level continuously and a condition that changes the airflow when the level moves outside the set limits.
A variable stores a value that the program uses, and its data type, such as integer, floating-point number or Boolean, decides what the value can be and how much memory it needs. A sensor reading arrives as an integer from the ADC and may be converted into a temperature value in degrees Celsius before it is used.
Many hardware tasks must happen at set times or in a set time order. A pacemaker's program, for instance, must time its pulses accurately against the heart's rhythm. Programs use timers and delay routines for this, and a delay that blocks the program stops it doing anything else while it waits.
Microcontrollers have little RAM and little program memory, so a programmer chooses data types of the right size, avoids unnecessary copies of data and checks that the program cannot write beyond the end of a data area. A portable ultrasound scanner with limited memory shows why this care is needed.
A design method is a planned way of deciding the structure of a program before it is coded. A good method makes the program clear, easy to test and easy to change when the device is updated. The methods below are not exclusive: a real firmware design usually mixes several, for example a top-down breakdown whose parts are modules driven by events.
| Method | Starting point | Typical use in a controller program |
|---|---|---|
| Top-Down Design | The overall function | Breaking a device into subsystems |
| Bottom-Up Design | The hardware and low-level routines | Testing drivers on new hardware first |
| Modular Design | Separate units with set interfaces | Reusing a routine in several devices |
| Structured Programming | Sequence, selection and repetition | Keeping code readable and testable |
| Object-Oriented Design (OOD) | Objects that hold data and actions | Representing a sensor or a pump |
| Event-Driven Design | Events and states | Responding to buttons, timers and alarms |
| Data-Driven Design | Tables and settings | Calibration and alarm limits |
| Functional Design | Functions without side effects | Filters and calculations |
| Component-Based Design | Ready-made components | Using drivers and communication software |
Top-down design starts with the overall function of the system and breaks it down step by step into smaller parts until each part is simple enough to code. This hierarchical approach helps engineers develop complex programs, such as the controller of a dialysis machine, by treating subsystems such as fluid control, alarms and the user interface one at a time. It gives a clear overview first, but the lower-level details are not tested until late, so a fault in a low-level routine may show up only then.
Bottom-up design starts with the lowest-level routines and builds upwards. The programmer first writes and tests the routines that talk to the hardware, such as reading a thermistor through the ADC or sending a byte through a UART, and then combines them into larger functions. This approach is natural when a new board is being brought into use or when the hardware has limits that the programmer must discover. In an incubator controller, the temperature reading routine would be written and checked first, and the control logic built on top of it. The risk is that the finished low-level pieces may not fit together as the whole system needs.
Modular design divides the program into independent modules, each with one job and a defined interface of the functions it offers. The inside of a module is hidden from the rest of the program. Modules are tested separately, can be reused and can be assigned to different people. For example, a sensor calibration module can be reused in several biomedical devices, which saves development time. A good module does one task well and depends on as few other modules as possible.
Structured programming builds programs from three basic structures: sequence, selection (if and switch) and repetition (loops). It avoids jumping about the program without structure, and it breaks the work into functions with one clear entry and exit. The result is a program that can be read from top to bottom and checked step by step. The structures themselves are the control structures introduced under Programming concepts, and structured programming is the discipline of using only them.
Object-oriented design describes the system as objects, each holding its own data and the actions that work on that data. An infusion pump program might have a pump object that stores the rate and the volume delivered and offers actions such as start, stop and set rate, and a separate sensor object for the drop detector. The data of an object is changed only through its own actions, which prevents other parts of the program from damaging it. OOD can be used with the languages named under Types of hardware programming languages, but its ideas of objects can also be applied in plain C using structures and functions.
In event-driven design the program is planned around events, such as a button press, a timer reaching its value or a sensor having a new reading, and around the states the device can be in. The designer lists the events and the states and decides what the device does for each event in each state, usually on a state diagram. A syringe pump might have the states idle, running, paused and alarm, with events such as start button, empty syringe and occlusion. The design suits devices that mostly wait for something to happen, and it fits well with interrupts. The way such a design is coded is covered under Event-Driven Programming.
In data-driven design the behaviour of the program is controlled by data held in tables or settings, not by code written for each case. For example, a table of thermistor readings and the matching temperatures lets the program look up a temperature without a long calculation, and a table of alarm limits for each patient type lets the same program serve different uses. A change to the data changes the behaviour without rewriting the program. The data must be checked carefully, because a wrong entry in a table affects every reading that uses it.
Functional design builds the program from functions that take inputs and give outputs without changing anything else, so the same input always gives the same output. A function that takes ten ADC samples and returns their average is an example. Such functions are easy to test, because a test only needs the input and the expected output. In firmware, the parts that change the hardware are kept separate from these calculation functions. Languages built around this idea are described under Functional Programming Languages.
Component-based design builds the system from ready-made components, each with a defined interface, such as a driver for a display, a communication software package or a real-time operating system. The programmer chooses and connects components instead of writing everything. A component can be replaced by another with the same interface, for example a new temperature sensor driver, without changing the rest of the program. The designer must check that each component suits the device, is documented and has been tested, because a fault in a component becomes a fault in the device.
A flowchart is a diagram of the program logic that uses symbols for steps and decisions. A flowchart of an infusion pump controller shows decision points and the order of the steps, so the design can be checked and discussed before coding begins.
Pseudocode is the logic of a program written in plain language or in simplified code. It bridges the gap between the design and the real code. For example, the logic of an alarm routine might be written like this:
read temperature
if temperature > upper limit
sound alarm
else if temperature < lower limit
sound alarm
else
switch alarm off
Iterative development improves the program through repeated cycles of writing, testing and correcting. It is important in safety-critical functions, where each change to a device such as a ventilator must be tested again before it is released.
Create a free account to open more of this chapter.
Free: practical guides, quick cards, workplace scenarios and more.
Create a free accountThis chapter provides a comprehensive overview of hardware programming, beginning with essential terms and programming concepts that form the foundation for effective program design. Various program design methods are explored, including top-down, bottom-up, modular, structured, object-oriented, event-driven, data-driven, functional, and component-based approaches, each offering unique advantages in organizing code for microcontrollers and microprocessors. The discussion then shifts to hardware programming languages, highlighting object-oriented, functional, and scripting languages suitable for different programming needs. Several hardware programming methods are detailed, such as bare-metal, register-level, interrupt-driven, real-time, in-circuit, in-system, event-driven, hardware abstraction layer programming, and techniques for low-power and sleep mode operation. The chapter also covers critical aspects of program development, including memory addressing, functions, classes, structures, and control flow constructs like loops and conditionals. Compilation and debugging processes are explained step-by-step, from preprocessing to generating binary and hex files, ensuring that programs are correctly built and tested. Finally, the importance of thorough program documentation, inspection, and rigorous testing, including electrical and functionality tests, calibration, and response evaluation, is emphasized, along with the preparation of commissioning documents such as safety documentation and handover reports to ensure smooth project completion.
At the start of this chapter we promised you would be able to:
Tick each one you can genuinely do.
So, are you there yet?
You're competent when you can confidently do 50% or more of what this chapter promised.
Sign in to record how you're doing.