While writing codes, a lot of times I need to do some math calculation which I did on paper/calculator only to get some constant values (e.g. hardware register settings, etc.). Either you realize it or not, these kind of calculation tasks can be really simply done using the C preprocessor to get the job done.
Let me take a simple example: while trying to write some basic UART application on the MSP-EXP430G2 Launchpad kit and the on-board MSP430G2553 microcontroller, I need to program the on-chip USCI module which is used for the UART communication. As you might expect, for setting a hardware UART module the main task would be to get the baud rate setting values. Looking into the MSP430G2553 Device Family User’s Guide and referring to this wiki, the basic idea is quite simple:
Based on the input clock frequency and the desired baud rate, we need to calculate the integer and fraction part of the so called division factor N, which is none other than N=InputFreq/BaudRate. The integer part of this number is then used as the main baud rate register UCBRx while the fractional part becomes the input for calculating the value of the modulation register (UCBRSx bits if UCOS16=0 or UCBRFx bits if UCOS15=1).
So here is the thing: You can try to calculate this on a paper and write in hardcoded in your code, or you can try to use the C preprocessor to calculate it for you during compilation. The following is an example on how to do it in the way which I call smartly using the C preprocessor while the full source code and project on Code Composer Studio for UART echo application can be found here.
The smart calculation of the USCI baud rate setting is basically implemented in the usci_settings.h header file. In this file, the user needs to only define the used input clock frequency and the desired baud rate such as follows:
#define USCI_INPUT_CLK (8000000UL) // in Hz #define USCI_BAUD_RATE (9600)
The rest of the code in the header file will then automatically calculate the baud rate setting based on both inputs above (input clock frequency and descried baud rate). The implementation is done in the following part of the header file:
#define USCI_DIV_INT (USCI_INPUT_CLK/USCI_BAUD_RATE) #define USCI_BR0_VAL (USCI_DIV_INT & 0x00FF) #define USCI_BR1_VAL ((USCI_DIV_INT >> 8) & 0xFF) #define USCI_DIV_FRAC_NUMERATOR (USCI_INPUT_CLK - (USCI_DIV_INT*USCI_BAUD_RATE)) #define USCI_DIV_FRAC_NUM_X_8 (USCI_DIV_FRAC_NUMERATOR*8) #define USCI_DIV_FRAC_X_8 (USCI_DIV_FRAC_NUM_X_8/USCI_BAUD_RATE) #if (((USCI_DIV_FRAC_NUM_X_8-(USCI_DIV_FRAC_X_8*USCI_BAUD_RATE))*10)/USCI_BAUD_RATE < 5) #define USCI_BRS_VAL (USCI_DIV_FRAC_X_8<< 1) #else #define USCI_BRS_VAL ((USCI_DIV_FRAC_X_8+1)<< 1) #endif
Based on the short description above on how to calculate the baud rate setting, first we calculate the integer part of the division factor N=InputFreq/BaudRate. This is done at line 1, and used to simply calculate the UCBR0 and UCBR1 registers at line 2 and 3 which shall contain the integer part of division factor N.
Then comes the interesting part: we need to calculate the fraction part of N. So first I try to calculate the numerator of the fraction part (USCI_DIV_FRAC_NUMERATOR) at line 5 by simply calculating the difference of the input clock with the integer part of N times the baud rate.
Since the UCBRSx bits is basically the fraction part of N times 8, and because the preprocessor can work mostly with integers, we need to first times the USCI_DIV_FRAC_NUMERATOR with 8 at line 6 resulting in USCI_DIV_FRAC_NUM_X_8, then after that dividing it back with the baud rate at line 7 resulting at USCI_DIV_FRAC_X_8. Lines 9-13 are the implementation of rounding function.
Finally, the calculation result can be used by the application code in form of the USCI_BR0_VAL, USCI_BR1_VAL, and USCI_BRS_VAL which are defined as the register values.
// setup USCI UART registers UCA0CTL1 |= UCSSEL_2 + UCSWRST; UCA0BR0 = USCI_BR0_VAL; UCA0BR1 = USCI_BR1_VAL; UCA0MCTL = USCI_BRS_VAL; UCA0CTL1 &= ~UCSWRST;
If necessary, the user shall only need to change the USCI_INPUT_CLK and USCI_BAUD_RATE values and recompile the code while the C preprocessor in the header file will then calculate new values of the hardware registers.
One important notice for this implementation is that the code will only to do the calculation for the “Low-Frequency Baud-Rate Mode Setting (UCOS16=0). The implementation of flexible calculation between the “Low-Frequency Baud-Rate Mode Setting” and “Oversampling Baud-Rate Mode Setting” is of course possible, but not yet covered in the current version.
So now you know that your C preprocessor can do a lot of calculation job for you and make your code simpler, if you know how to write your code smartly.