Microcontroller Embedded C Programming Lecture 184| Modifying led toggle exercise with macros

  • Post author:
  • Post category:Blog

 

Modifying led toggle exercise with macros

 

In C, macros are used to define reusable pieces of code or constants that are substituted directly into the code during preprocessing. Macros are preprocessor directives and are typically defined using the #define directive.

In this article, we will enhance our previous program, led_toggle_bitfields, by incorporating macros to make the code more organized and readable.  Macros are a powerful tool for simplifying code and abstracting away complex values or addresses.

Let’s modify the below code and let’s see where and all we can make use of macros here. 

#include "main.h"

int main(void)
{
   RCC_AHB1ENR_t volatile *const pClkCtrlReg = (RCC_AHB1ENR_t*) 0x40023830;
   GPIOx_MODE_t volatile *const pPortDModeReg = (GPIOx_MODE_t*) 0x40020C00;
   GPIOx_ODR_t volatile *const pPortDOutReg = (GPIOx_ODR_t*) 0x40020C14;

   //1. enable the clock for GPOID peripheral in the AHB1ENR (SET the 3rd bit position)
   pClkCtrlReg->gpiod_en = 1;

   //2. configure the mode of the IO pin as output
   pPortDModeReg->pin_12 = 1;

   while(1)
   {
      //Set 12th bit of the output data register to make I/O pin-12 as HIGH
      pPortDOutReg->pin_12 = 1;

      //introduce small human observable delay
     //This loop executes for 300K times
     for(uint32_t i=0 ; i < 300000 ; i++ );

     //Reset 12th bit of the output data register to make I/O pin-12 as LOW
     pPortDOutReg->pin_12 = 0;

    for(uint32_t i=0 ; i < 300000 ; i++ );
   }

   for(;;);
}

led_toggle_bitfields program code

Define a Macro:

To define a macro, use the #define directive followed by the macro name and its replacement value.

The defined macros are shown in Figure 1 and the Modified code is below code snippet. 

 

Here, there is an address represented as (RCC_AHB1ENR_t*) 0x40023830. To hide this number, a macro can be used. This can be achieved by creating macros in main.h.

The number 0x40023830 is the register address of the AHB1_ENR register. A macro named ADDR_REG_AHB1ENR can be created with the value ((RCC_AHB1ENR_t*) 0x40023830) to represent this address. 

#define ADDR_REG_AHB1ENR    (   (RCC_AHB1ENR_t*)    0x40023830)

Similar to ADDR_REG_AHB1ENR, two more macros can be created for GPIOD_MODE and GPIOD_OD, named ADDR_REG_GPIOD_MODE and ADDR_REG_GPIOD_OD respectively with their corresponding values.

#define ADDR_REG_GPIOD_MODE   (  (GPIOx_MODE_t*)   0x40020C00)

#define ADDR_REG_GPIOD_OD          (  (GPIOx_ODR_t*)      0x40020C14) 

According to our guideline for writing macros, a macro value must be inside parentheses. That’s why let’s use the parentheses here. 

Figure 2. Some macros in main.h
Figure 1. Some macros in main.h

 

 

#include "main.h"

int main(void)
{
   RCC_AHB1ENR_t volatile *const pClkCtrlReg   = ADDR_REG_AHB1ENR;;
   GPIOx_MODE_t  volatile *const pPortDModeReg = ADDR_REG_GPIOD_MODE; 
   GPIOx_ODR_t   volatile *const pPortDOutReg  = ADDR_REG_GPIOD_OD;

   //1. enable the clock for GPOID peripheral in the AHB1ENR (SET the 3rd bit position)
   pClkCtrlReg->gpiod_en = CLOCK_ENABLE;

   //2. configure the mode of the IO pin as output
   pPortDModeReg->pin_12 = MODE_CONF_OUTPUT; 

  while(1)
  {
  //Set 12th bit of the output data register to make I/O pin-12 as HIGH
  pPortDOutReg->pin_12 = PIN_STATE_HIGH;

  //introduce small human observable delay
  //This loop executes for 300K times
  for(uint32_t i=0 ; i < DELAY_COUNT ; i++ );

  //Reset 12th bit of the output data register to make I/O pin-12 as LOW
  pPortDOutReg->pin_12 = PIN_STATE_LOW;

  for(uint32_t i=0 ; i < DELAY_COUNT ; i++ );
  }

 for(;;);
}

Modified code in main.c

1. Enable the clock for the GPIOD peripheral in the AHB1ENR(SET the 3rd-bit position)

pClkCtrlReg-> gpiod_en = 1;

Here, 1 means enable. We can create a meaningful macro named CLOCK_ENABLE for 1. 

pClkCtrlReg -> gpiod_en = CLOCK_ENABLE; 

And define it as #define CLOCK_ENABLE (1); 

When someone sees this code, then it is very clear to them that you are doing some ClOCK_ENABLE thing here.

 

2. Configure the mode of the IO pin as output

Similarly, we can create a macro named MODE_CONF_OUTPUT for configuring the mode of the IO pin as output 

pPortDMODEReg -> pin_12 = MODE_CONF_OUTPUT;

And define it as #define MODE_CONF_OUTPUT (1)  

 

  • Set 12th bit of the output data register to make I/O pin-12 as HIGH. 

We can create a macro named PIN_STATE_HIGH and define it as #define PIN_STATE_HIGH (1)

 

  • 12th bit of the output data register to make I/O pin-12 as LOW

we can create a macro named PIN_STATE_LOW and define it as #define PIN_STATE_LOW (0).

 

For the number 300000, we can create a macro named DELAY_COUNT and define it as #define DELAY_COUNT (300000UL). By adding ‘u’ and ‘l’ characters along with the number, we are telling the compiler to treat this number as an unsigned long integer.  

Please note that according to our macro writing guidelines, all the things should be in parentheses. That is always better practice. 

Now someone can easily understand what’s going on here. Still, you can use more macros to make it more readable.

 

FastBit Embedded Brain Academy Courses

Click here: https://fastbitlab.com/course1

 

FastBitLab

The FastBit Embedded Brain Academy uses the power of internet to bring the online courses related to the field of embedded system programming, Real time operating system, Embedded Linux systems, etc at your finger tip with very low cost. Backed with strong experience of industry, we have produced lots of courses with the customer enrolment over 3000+ across 100+ countries.