Virtual Coffee with a Firmware Engineer

firmware

Nowadays, most electronic devices out there require some form of intelligence or decision making. Sometimes very talented electronic engineers can make this using purely analogue and digital electronics together and substantially reduce the costs of the project. However, it is more than common to find that almost all electronic products have a microcontroller inside. These little computers introduce the possibility of measuring analogue signals so they can be processed, enable communication between peripherals, and execute a code taking into account the inputs of your system, just to name a few things.

But as always, it all comes with a cost. Introducing an MCU into your product also increases its complexity and likelihood of failing. It also introduces into the project a discipline of its own: Firmware Engineering. Although most electronic engineers know a bit of C programming and can write some simple code, a lot of them do not consider themselves programmers and fall short when a proper code needs to be written (I am talking about myself of course:) ). Because of that reason, normally in medium to big size companies you have a clear line dividing between HW development and SW development, as you need specialists on both sides to produce a quality product. Don’t get me wrong, there are some electronic engineers out there that are equally good at HW and FW design, these people are just incredible in my opinion.

In this article, 3 Firmware Engineers and App developers from different industries will be interviewed with different basic questions about Firmware design so you can expand your knowledge and understanding of the topic.

Programming tips according to Niall, Firmware Engineer and Mobile Application Developer in the Audio industry

What is the first thing you do when you have to write a new program? You dive right into coding or is there some “preparation” beforehand?

When starting out, I certainly tended to dive in and start typing. Maybe you have to go through that to learn. After gaining some experience I started to plan more. It saves time in the long run and usually results in better code. Hence, it’s my longest answer by far!

Specification

Write out a spec, or at least carefully think about what you want your project to do:

  • How many buttons do I need to create an intuitive product?
  • What meaningful information can I display? Do I have LEDs? An LCD? Network Connection?

Ideally, the programmer shouldn’t produce the spec. Programmers are often biased to what’s easy. Another person writing the specification can produce a better product.

Modes of operation

It’s good to identify different states that your project will run in.

You’ll typically have at least two states: Standby and Running. Write out the requirements and constraints for these states.

Standby: Can I wake up the microcontroller periodically and listen for ON signals and still meet low power requirements?

Running: Poll buttons with non-block debounce; when flag pin goes low, read data from a sensor; When sensor value exceeds a threshold, SPI write to character display.

In the past, I’ve sometimes created Init and Shutdown states to configure peripherals when transitioning to and from standby. Stay simple though, don’t go too mad creating states for every little aspect of operation!

UI

From the spec think about how you’re going to handle your UI in code.

  • Can I get away with a few switch statements?
  • Do I have a maze of twisty menus? Maybe I need to use linked lists.
  • Am I driving an LCD/ OLED? Do I need a graphics library?
  • Do some inputs need ADCs? Have I got sufficient resolution?

Performance

  • Identify bottlenecks
  • Determine what signals need servicing immediately, usually, these will be handled by interrupts.
  • How can I reduce execution time blocked by interrupts?
  • Can I use DMA to free up resource?
  • What can be dealt with less rigidly? Button reading for example

Licensing

  • Do I need to fully/ partly open source my project if I incorporate some GPL/LGPL code?
  • Do I have permission to use this font in an embedded context?

Plan of attack

Work out what needs doing and in what order. You could use a Gantt chart, but a list will do. If you’re lucky enough to have Jira, plan tasks, sub-tasks and sprints, but let’s not go there!

What is your most useful way to name variables and subroutines?

Your code should be self-commenting where possible i.e. give meaningful names to your variables and functions.

If a variable indexes an array, it should have index in its name.

If a variable represents an enumerated type, it should have type in its name.

If a function returns a string via a const char * for a given type, call it something like nameFromType(eThing ThingType).

It’s common practice to prefix variables according to their scope…

One might prefix global variables with a ‘g’, for example gState.

A variable with file scope is sometimes prefixed with an ‘m’, for example mRetryCount.

Sometimes it’s descriptive to prefix a pointer with a lower case ‘p’, pBuf. “fp” for a file pointer.

It’s quite common to prefix typedefs according to their type. ‘s’ for struct, ‘u’ for union, ‘e’ for enum

Can you tell us the structure of your average main.c page?

Usually, there’s some vital port configuration that needs to be done ASAP.

After that, very little, maybe the main loop containing the main state machine.

Software_structure

Do you normally do all your code in your main.c or have separate files? What’s the better approach?

Separate files.

Logically spread your code over files for re-use in other projects. For example, all your SPI or I2C handling can be placed in .c and .h files.

Register maps and config for SPI/ I2C peripherals can be put in dedicated headers per device.

Button reading, scheduled tasks, parsers, etc should all go in their own files.

What’s the most common reason your code will not compile?

Missing semicolon.

Is there a process or method to follow when debugging a code that doesn’t work?

It depends how familiar you are with the code you’re working with.

If you know it well, quite often you’ll know where to look when something strange happens.

If you’re less familiar with it, a debugger is indispensable. If you’re lucky, stopping the debugger allows you to step back through the stack and work out how you got there. If you’re unlucky and the program counter has gone for a walk, you might need to go through a process of elimination to determine the exact point of failure.

It’s nice to use a spare serial port to sprintf diagnostic info down. You can view the trace in PuTTY. It’s often more useful than a debugger since you can see live information without stopping the program.

Programming tips according to Ade, Firmware Engineer and Mobile Application Developer in the Secure Communications Industry

What is the first thing you do when you have to write a new program? You dive right into coding or is there some “preparation” beforehand?

Clarify – that’s how I always start. If I don’t understand the idea and intention of the project or “thing”, then it’s harder to come up with creative solutions and develop with a bigger picture in mind. What this means is I like to start any new program with as much information as possible so I feel more equipped to develop more confidently, rapidly and with creativity. It’s like starting an adventure in a potentially uncharted territory and learning as much as possible about the terrain, the weather, the season, wild animals etc, before starting the journey. The more I know, the less risky and more informed I am.

What is your most useful way to name variables and subroutines?

If a colleague cannot immediately look at the variable or subroutine and get a rough idea of what it does from the name, then you should probably change the name to make it more readable and relevant. Also, consistency is important in your naming, and so is general practice, which you can get from reading other people’s code (open source code).

Can you tell us the structure of your average main.c page?

When I used to program in C (I’m now an Android app developer), the structure of the main.c file was mostly dependent on the size of the project, but essentially I structured it as the entry point of the program. It would perform the initialisations that the program needs to run and that was it. The responsibility of individual functional blocks of the program is then in effect self-contained.

Do you normally do all your code in your main.c or have separate files? What’s the better approach?

Imagine you walked into a library and asked for a book titled “The art of programming”. You’re then given 2 options: a 500-page book OR the exact contents of the 500-page book in one very, very, very long piece of paper i.e. a scroll – which would you prefer to read? Now imagine if you’re also the writer, what style would you think was more convenient and that your readers would enjoy more? I think you get the idea 🙂

What’s the most common reason your code will not compile?

Disregarding linker issues, the most common reasons I’ve found are usually reference related…or more simply put, human errors.

Is there a process or method to follow when debugging a code that doesn’t work?

Yes, my first question is always “is it repeatable?” – you’ll save many hours with just this one question. If it is, great, I can trace the code sequence to better isolate and identify the possible causes and find a solution. Otherwise, if it isn’t repeatable, then I cast a wider net to gather as much information as possible by doing a LOT of logging and also experimenting with an if this then that approach.

Programming tips according to Miguel Angel, Firmware Engineer in the Automotive industry

What is the first thing you do when you have to write a new program? You dive right into coding or is there some “preparation” beforehand?

When I have to do a program the first thing I do is to have everything clear, so a handwritten diagram is usually the fastest way to clarify your ideas. The second thing is to split your program tasks into different software components which each one interconnects with each other.

Usually, the first draft or design increases in complexity as you try to add new features. If you think your program is growing in complexity my suggestion would be to STOP coding and redesign it again. Your experience based on the last thing you coded will make you code a SIMPLER and more structured program.

What is your most useful way to name variables and subroutines?

As in the first question I suggested to separate the program tasks into different software components, the most useful way to name your functions and variables is appending software component name at the beginning of your variable/function. In this way, you will know the related function task/functionality by just having a quick look at the name. The other thing to take into account is trying to describe the function/variable action within the name by using fewer words as possible.

Can you tell us the structure of your average main.c page?

The structure of my average main.c page is always the following:
  • First – Initialization of all the peripherals, clocks and software components.
  • Second – I execute everything in a super loop ( while(1){ /* tasks */ } )
  • As you can see it’s very simple, your main program should not be very large, everything should be handled in the different software components.

Do you normally do all your code in your main.c or have separate files? What’s the better approach?

Separating the code in different files is the best way to have a clean and readable code, so I usually write my program by separating all the tasks/functionalities into different files and folders. Doing it in this way you will have more re-usability of your code into different programs.

What’s the most common reason your code will not compile?

The most common faults of having your code not compiling are the following:
  • Missing semicolon “;”. Every statement should end with a semicolon, It’s common to miss using the semicolon at the end of the statement (this is a usual error on your first ANSI C programs).
  • Undeclared variable. In large code, is usual to use a variable to hold intermediate results and forget the declaration of this variable.
  • Mismatch of parenthesis. If you place a function with arguments inside if-else statements is common to have a mismatch in the parenthesis.

Is there a process or method to follow when debugging a code that doesn’t work?

If your code does not work, do not worry, doing step-by-step debugging you will know the root cause. The first thing to check is the flow execution of your code, place a breakpoint in the main function and verify the code is executed in the order you have programmed. Verify all the peripherals and variables have been properly initialized, if all those checks passed go directly to the non-working task or function. Debugging an embedded system a part of a debugger an oscilloscope is also needed, as you’re interfacing with external hardware outside the microcontroller. If the issue you’re trying to debug is reproducible (it occurs every time) you’re lucky, the most difficult errors are those which are difficult to reproduce.

About Miguel

Miguel is an Electronics Engineer currently working as a Software Engineer in an Automotive company. He has participated in some open source projects such as Mooltipass (hardware password keeper) and co-developed his own password keeper using an attiny85 called Memtype (currently working in a PRO version with OLED display).

Memtype review by N-O-D-E:

Final word

This article has provided valuable and practical information to any electronics engineer out there that needs to start its first MCU programme or wants to improve its coding skills. Firmware design is a discipline of its own that can only be improved with practice and writing code. Hopefully, you now have a general understanding and a template you can use to start in your next Firmware development for your electronic product.

Posted in: Product Development

Leave a Reply