Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Alternative to boolean type for embedded C (ATmega2560)
I'm working on writing drivers for the peripherals in my ATmega2560 microcontroller. One of these is a USART which has the option of being double speed or normal speed. If this was C for the PC I would use <stdbool.h> to create a boolean member of the USART struct and write my header and source file as shown below.
//usart.h
#ifndef USART_H
#define USART_H
#include <stdbool.h>
typedef enum { USART_PARITY_NONE, USART_PARITY_EVEN, USART_PARITY_ODD } usart_parity_t;
typedef enum { USART_STOP_1, USART_STOP_2 } usart_stopbits_t;
typedef enum { USART_BITS_5, USART_BITS_6, USART_BITS_7, USART_BITS_8} usart_databits_t;
typedef struct
{
usart_parity_t parity;
usart_stopbits_t stopbits;
usart_databits_t databits;
bool doublespeed;
} usart_t;
//Prototype functions below
#endif
//usart.c
#include <stdint.h>
#include <stdbool.h>
#include "usart.h"
static void set_baud(uint32_t baud, bool doublespeed)
{
if(doublespeed)
{
//Setup usart for double speed
}
else
{
//Setup usart for normal speed
}
//do more stuff
}
However, <stdbool.h> is not provided by the C library implementation available for the ATmega2560.
Question: How do I handle this in my code? Should I use uint8_t doublespeed instead? Are there any consequences of doing this?
2 answers
This is initialization code, so speed and efficiency probably don't matter much. Therefore, optimize for clarity. I see two options:
- Define your own Boolean type. Make it whatever is easy for the processor to handle. That could be an 8 bit unsigned integer, or possibly a bit type if the processor can handle those simply. Wasting 7 bits of an 8-bit byte is no big deal here.
Check how the compiler passes parameters to subroutines. If the 8-bit boolean gets expanded into a 16 bit value, then you might as well define it as a 16 bit value in the first place.
- Define another enum. It could be usart_speed_t, with values for normal and double speed.
Run time or build time?
You should also ask yourself whether this is something you really need at run time. I've done many microcontroller projects that used a UART, and the vast majority of the time the protocol is fixed for the particular project. I do the setup calculations in the preprocessor, and only the results get written to the hardware registers at run time during initialization.
My standard UART handler is highly build-time configurable, and only includes routines for changing configuration, like baud rate, if specifically enabled in a particular project.
Normal/double speed hidden?
Another issue you should consider is whether normal versus double speed should be visible at the application level in the first place.
This is really a detail of the hardware implementation. I'm not familiar with the ATmega peripherals, but on other microcontrollers similar configuration options allows accessing higher baud rates than you would get by dividing the available clock by 16. The down side is that there is less resolution available for detecting when the start bit starts, and therefore when to sample the remaining bits. This means you want to use normal mode when possible, and double speed mode when the baud rate can only be achieved that way.
If this is what your normal/double speed mode is, then it is part of the overall baud rate setup. Again, baud rate is often fixed for a project and therefore should be calculated at build time. If you do need to change the baud rate at run time, then that code should decide the normal/double speed mode under the hood.
The system clock speed is one of the constants that should be available at build time. That and the desired baud rate is all that should be needed to set up the clocking to the UART. You can do things in the preprocessor you wouldn't want to do at run time, like search the possible combinations of various settings to find the most optimal one. The run time code just loads the resulting fixed values into the control registers.
Calculating the baud rate setup at run time can be rather awkward on small micros. You need to do wide integer division, or possibly software floating point.
For example, here is a snippet from my standard UART module for a dsPIC:
; Subroutine UART_BAUD_SET
;
; Set the baud rate from the value in W1:W0. The closest attainable baud rate
; will be set and this value saved in the global variable UART_BAUD.
;
/if runtime_baud then
/var new ii integer
glbsub uart[chars uname]_baud_set, regf0 | regf1 | regf2 | regf3
;
; The baud rate register value is:
;
; freq_inst
; Ubrg = --------- - 1
; 16 * BAUD
;
mov #-4, w2 ;pass number of fraction bits
gcall fp32f_fltu ;convert BAUD*16 to floating point in W1:W0
mov.d w0, w2 ;save BAUD*16 in W3:W2
fpload w0, [v freq_inst] ;get FREQ_INST into W1:W0
gcall fp32f_div ;do the divide
fpload w2, -.5 ;subtract 1, but add 1/2 for rounding
gcall fp32f_add
mov #0, w2 ;pass number of fraction bits
gcall fp32f_fixu ;convert FP in W1:W0 to integer
cp0 w1
skip_z ;no overflow ?
mov #0xFFFF, w0 ;overflow, use maximum possible value
;
; The new Ubrg register value for the selected baud rate is in W0.
;
; Set the new baud rate, but also reset the UART and the software FIFOs.
;
mcall uart[chars uname]_off ;turn the UART off
mov w0, Ubrg ;set the new baud rate
mcall uart[chars uname]_on ;turn the UART back on
;
; Compute the actual new baud rate and save it in the global variable
; UART_BAUD. The baud rate is:
;
; freq_inst
; BAUD = ------------
; 16(Ubrg + 1)
;
mov Ubrg, w0 ;get baud rate register value into W1:W0
mov #0, w1
add #1, w0 ;add 1
addc #0, w1
mov #-4, w2 ;pass number of fraction bits in W1:W0
gcall fp32f_fltu ;make floating point 16(Ubrg - 1)
mov.d w0, w2 ;save it in W3:W2
fpload w0, [v freq_inst] ;get the numerator into W1:W0
gcall fp32f_div ;compute FP baud rate in W1:W0
fpload w2, .5 ;add 1/2 for rounding
gcall fp32f_add
mov #0, w2 ;pass number of fraction bits to make
gcall fp32f_fixu ;make integer baud rate in W1:W0
mov w0, uart[chars uname]_baud+0 ;save result in global variable
mov w1, uart[chars uname]_baud+2
leaverest
/del ii
/endif
A few things to note:
- Lines that start with "/" are preprocessor commands. Preprocessor functions are "[...]", which are replaced with their expansions before the assembler gets the code.
The preprocessor is capable of floating point math and is essentially a full interpreted language on its own. It is described at www.embedinc.com/pic/prepic.htm, and is based on the Embed scripter described at www.embedinc.com/pic/escr.
- This subroutine only exists when the preprocessor switch runtime_baud is true.
- The preprocessor constant freq_inst is the instruction clock frequency, and is used to compute the baud rate setup. This constant is set early in the build process, and is assumed by many of my standard "library" modules.
- This is a 16 bit machine, and even that's not good enough for computing the baud rate setup. The baud rate is passed in a 32 bit call argument (registers W1 and W0 used together). In this case, the calculations are done in software floating point because it was easier than managing the wide dynamic range of the values in 32 bit integers. The fp32f_xxx routines perform the floating point calculations.
You can see all the gory details in the GitHub repository https://github.com/EmbedInc/dspic. Look for files with "uart" in their names. This snippet was taken from uart.ins.dspic.
0 comment threads
Old C90 compilers do not provide stdbool.h, nor do they provide stdint.h. If you have one of them but not the other, then your compiler is dysfunctional somehow.
Here's from an old production code header of mine that is used with old crappy compilers for 8/16 bit targets:
(it happens to add BOOL TRUE FALSE also as they were common in C90, but you probably don't need those)
#ifdef __STDC_VERSION__
#if (__STDC_VERSION__ >= 199901L) /* C99 or later? */
#include <stdint.h>
#include <stdbool.h>
typedef bool BOOL;
#ifndef FALSE
#define FALSE false
#define TRUE true
#endif
#else
#define C90_COMPILER
#endif /* #if (__STDC_VERSION__ >= 199901L) */
#else
#define C90_COMPILER
#endif /* __STDC_VERSION__ */
#ifdef C90_COMPILER
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;
typedef signed char int8_t;
typedef signed int int16_t;
typedef signed long int32_t;
#ifndef BOOL
#ifndef FALSE
#define FALSE 0u
#define false 0u
#define TRUE 1u
#define true 1u
#endif
typedef uint8_t BOOL;
typedef uint8_t bool;
#endif
#endif /* C90_COMPILER */
The explanation is that the constant __STDC_VERSION__ was added in ISO C 9899:1990 + Amd1 1995, aka "C95". Neither the original C90 or C95 had support for the required headers however - in case either of those standards are used, this code defines a placeholder token C90_COMPILER.
Then in case of C90, define all common types from stdint.h and stdbool.h. You'd define bool as uint8_t indeed. You could just ignore all of this and use uint8_t with value 1 or 0 too, but that's less self-explanatory and means that you are still in the situation where you can't use semi-modern libs that come with the type bool.
Also for MISRA C compliance you typically need to tell the MISRA checker what your Boolean type is called and this was the case even with old MISRA C:2004. All MISRA versions strongly encourage using stdint.h types or equivalents.

2 comment threads