cancel
Showing results for 
Search instead for 
Did you mean: 

Design guidelines for embedded applications

magene
Senior II

I've been doing a lot of embedded coding lately. I'm not a very experienced developer and I have been trying to develop a set of design guidelines to help me up the learning curve.  I'd appreciate hearing what experienced embedded developers use for guidelines. 

The SOLID principles make a lot of sense to me but I've added a few more of my own.  Any comments on these will also be appreciated. If they're dumb, I'll be happy to modify or drop them.

  1. My main goal it to make sure the code is reasonably easy to understand by a competent but not necessarily expert C/C++ developer.
  2. For most embedded applications, the code should be non-blocking.  For example, all my UART drivers use DMA at this point. If I run out of DMA channels, I can use IT transfers to maintain a non-blocking approach.
  3. I'd prefer to use straight C. However, I have a basic understanding of Object Oriented programming.  The other developers who may work with me on this project have an ever better understanding of OOP. But nobody sees OOP as the answer to all our needs.  So, I'll use a C++ class if it make the code easier to understand or reduces cut and paste or some other good reason.
  4. Functions shouldn't be more than a screen full of lines.
  5. Files shouldn't be more than a couple of 100 lines long.
  6. Functions should be loosely coupled.  Loosely coupled to me means for example: GPS receiver code shouldn't need to know very much, better yet nothing at all, about the details of the UART or I2C or whatever port it's using to communicate.
  7. I'm trying to stick to the STM low level drivers instead of the HAL drivers.

 

4 REPLIES 4
Johi
Senior III

I have been working in C and C++ since 1996. So some years of experience I have, in professional and in a non professional context. I do not consider myself a specialist. If you are part of a team, follow your team to make sure you keep being part of the team. If your objective is to learn, buy a board, read the RMXXX datasheets and start experimenting with the board. If you feel ok with your design rules, follow them, if they are in the way of your learning process, find alternatives.   

TDK
Guru

2. DMA and IT is considerably more complicated from a code standpoint than blocking functions. Sometimes blocking is just fine.

3. C vs C++ is generally an individual choice. Use the one you're more comfortable with. They are both effective. C is generally preferred for low level code.

4 and 5. Was never a fan of blind guidelines on function lengths. Sometimes long functions are warranted.

6. Over-generalization is a common pitfall. The I2C driver for a device often needs to be different than the SPI driver. Separating the communication method from the driver often leads to more obfuscation.

7. Use HAL or register-level access. LL adds a level of obfuscation without providing the usefulness of generalization (as HAL provides).

General rules are fine. Keeping a consistent style (indentation, naming conventions, whitespace choices) helps quite a bit.

Edit: also consider adding "no dynamic memory allocation" to the design guideline. In my opinion, that's one of the most important things to ensure code quality.

If you feel a post has answered your question, please click "Accept as Solution".
Andrew Neil
Evangelist III

7: Any particular reason why you're "trying to stick to the STM low level drivers instead of the HAL drivers" ?

What do you see as the advantage to that approach?

magene
Senior II

@Andrew Neil  - That's a good question, @TDK also mentioned sticking to HAL or register level access. How I got to using the LL drivers is kind of a long, painful story that I'll try to keep short-ish.  I started off my embedded programming learning curve a few years ago using HAL and quickly got very frustrated trying to figure out all the things HAL is doing "for me" under the hood and all the files I had to look at to figure out what it was doing.  For example, if you're using HAL my recollection is if you want to find out something simple like what pins are being used for RX and TX in a UART, you have to look at HAL_init(), msp_init, msp_UART_init and probably several other places.  It gets even more complicated when I started using DMA. I asked lots of questions on this forum and many of the members who were super helpful getting me up the learning curve were pretty negative about HAL.  I eventually figured out how to use HAL reasonably well but using HAL forced me to structure my program according to the way HAL is structured rather than the way I wanted to structure my program which was always an irritation.  A few months ago, I got some time to spend investigating programming at the register level and using LL drivers.  Here's a couple of reasons why I chose the LL driver approach. 

Reason 1:

 

LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_8);

 

is much more readable to me than the equivalent register level command

 

ATOMIC_MODIFY_REG(USARTx->CR3, USART_CR3_TXFTCFG, Threshold << USART_CR3_TXFTCFG_Pos);

 

Reason 2:

I've found that using CubeMX (either stand alone or within CubeIDE) is a very effective way to get the peripheral driver initialization code I need.  I was very pleased to discover that I can tell CubeMX to use the LL drivers instead of HAL drivers and I can tell pretty easily what's going on in the code it generates with LL drivers. 

Now I can structure my programs in a way that makes sense to me.  For example, my most recent program has a separate file for each of the 8 uarts (or usarts) with all the configuration details (baud rate, RX and TX pins, DMA stream numbers, etc.) and all the IRQ and call back functions specific to that uart. And a single file with all the functions I've written that are common to all UARTS like startDMAXfer, startITXfer and so on. So, if I want to change the functionality of the Character Match callback function for UART4, I only have to look in the UART4.cpp file. I've been off and mostly running with LL drivers ever since I figure out how to do this.  OK, maybe not running but at least not limping too badly.