LPCopen coding style and library design

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPCopen coding style and library design

445 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Strahlex on Fri Jan 24 02:45:47 MST 2014
Hello NXP developers,

I am working since approximately one and a half year with the NXP Cortex M3 MCUs and would like to share my opinion about LPCopen (and previous NXP libraries). Please do not take my advice personal, but consider it as information to make your libraries better and, therefore, creating better user experience for you customers. So will discuss a few things in general about the library design and with the ring buffer implementation as example. So let's start:
1. Create a uniform implementation for all LPC1xxx series. The fact that you have to provide different packages for different series and even MCUs actually means that your cross-series design is not good. I solved that in my library implementation by using a special driver level below the implementation level of the peripherals.
2. Use Git or any other version management system. It would make it easier for the community to collaborate.
3. Use ANSI C and ISO-C99 datatypes everywhere and never use int one embedded platforms. Here are some reasons why: http://embeddedgurus.com/stack-overflow/2009/07/efficient-c-tips-10-use-unsigned-integers/
4. Never use pointer arithmetic on embedded systems. It is hard to verify whether a pointer is correct or not and, furthermore, pointer arithmetic can give you unexpected results. In case of the ring buffer implementation I would recommend to switch to array indexing. It gave me absolute deterministic results compared to pointer arithmetic which can do unexspected things when memory is rare.
5. Use data type specific number formatting like for example 0u, 0xFFu, 0b1010, 1.2 and so on. It makes he code more readable (you can easily identify signed values) and makes shure the compiler knows which data type was meant.
6. Use enums to describe peripheral settings this makes the code more readable.
7. Make use of the fast data types defined in stdint.h (e.g. uint_fast16_t).
8. Avoid using one line ifs and loops always use braces to avoid issues.
9. Use a linter (MISRA C will show you most of the above issues).
10. Create module tests e.g. using CUnit.
11. Do code reviews a few months after the development (when you have forgotten how you implemented everything) to make sure your library design is sane and any developers coming after you will understand the code as well.

In general I would recommend reading this blog carefully, it describes a lot of flaws possible in embedded C development: http://embeddedgurus.com/stack-overflow/category/efficient-cc/

Regards
Strahlex
0 Kudos
4 Replies

387 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by rocketdawg on Mon Jan 27 09:07:52 MST 2014
Nice post.  Having read "clean code" several times I can agree with most.  I to prefer C99.
Much prefer C++ but, that is rather new in LPCExpresso free version (many thanks)
0 Kudos

387 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Strahlex on Sat Jan 25 16:58:57 MST 2014

Quote: MarcVonWindscooting

Designing libraries ist really difficult, much more difficult that creating programs. I believe there are only a few good libraries in this world. So few that most often I don't even consider using anything else than my own creation :bigsmile:


I know what you are talking about. In the embedded world a lot of libs exist but only few of the libs freely available are good. Well I actually come from the desktop C++ world and I really like the design of Qt. In my opinion one the most sane library designs I know.


Quote: MarcVonWindscooting

int/C99 types:
We're working on ARMs, that's why we know int = int32_t,...
I hate underscores and especially '_t' so I use Int32 instead int32_t because that's smoother typing.
However, if part of the code is meant to be shared with other platforms, say amd64, then yes I do prefer C99 types.


You are right. I have no idea why they put the _t behind it, but actually with code completion this is no problem at least for me. I have also created some typedefs with uint8 and so on, but since I read from the fast data-types I am actually thinking about switching back to the C90 data-types. For platform compatibility reasons I would recommend to use them everywhere possible and the fast data types are really great,


Quote: MarcVonWindscooting

What do you think about using gcc style -std=gnu99 which is C99 + allowing the following:
for (int i=0; ... ; ... ) { ... }


In my opinion C99 is needed because of:
- best structure initialization style (better than C++!):
struct MyDateAndTime date = {
  .day = 24,
  .month = 1,
  .year = 2014,
  .time = {
      .hour = 14,
      .min = 30,
  },
};

i.e. named initialization instead of positional.

- run-time size of local variables:
void complexTask(int n) {
  char buffer[n];  // size depends on parameter
  for (int i=sizeof buffer; i>=0; i--) { ... }   // sizeof calculates correct size of buffer
  ...
}


These are cool features of C99 and I would not program in 'C' without these. Programmer's life is hard enough!


With point 8 I was actually talking about constructs like this:
if (whatever)
   doSomething();
doAnotherthing();

I actually would call that bad design. There is an application note from IAR about constructs like this and why MISRA C does not allow them. It is actually, because you can easily oversee where the if starts and ends.

Btw. the runtime sizeof feature is new to me. Looks very promising.
0 Kudos

387 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MarcVonWindscooting on Sat Jan 25 06:41:52 MST 2014
Welcome Strahlex,

what a first post!

Designing libraries ist really difficult, much more difficult that creating programs. I believe there are only a few good libraries in this world. So few that most often I don't even consider using anything else than my own creation :bigsmile:

As I'm (obviously) building my own libs for LPCxxxx I have some comments on your suggestions:

int/C99 types:
We're working on ARMs, that's why we know int = int32_t,...
I hate underscores  and especially '_t' so I use Int32 instead int32_t because that's smoother typing.
However, if part of the code is meant to be shared with other platforms, say amd64, then yes I do prefer C99 types.

0u, 0xFFu : good advice. How much work was it to tidy up my code, when 'char' was suddenly an unsigned data type in gcc

'6.' should have been you number 1. (ONE). Every unnecessary #define should be rewarded one slap into the programmer's face.

Ouch! I do like: one line if else without braces or 'while(...) /* busy wait */;'  provided, the statements are short and simple.

What do you think about using gcc style -std=gnu99 which is C99 + allowing the following:
for (int i=0; ... ; ... ) { ... }


In my opinion C99 is needed because of:
- best structure initialization style (better than C++!):
struct MyDateAndTime date = {
  .day = 24,
  .month = 1,
  .year = 2014,
  .time = {
      .hour = 14,
      .min = 30,
  },
};

i.e. named initialization instead of positional.

- run-time size of local variables:
void complexTask(int n) {
  char buffer[n];  // size depends on parameter
  for (int i=sizeof buffer; i>=0; i--) { ... }   // sizeof calculates correct size of buffer
  ...
}


These are cool features of C99 and I would not program in 'C' without these. Programmer's life is hard enough!
0 Kudos

387 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lpcadmin on Fri Jan 24 14:51:00 MST 2014
Hi Strahlex,

Thank you for taking the time to provide constructive comments!  :) 
We certainly want to provide high quality, reliable code for our customers.  We will be continuing to improve the drivers and examples we provide and appreciate feedback. 

Best regards,
0 Kudos