Using Structs and Enums in Embedded C for a PIC Microcontroller

Structs (struct) and Enums (enum) are powerful features in Embedded C programming that allow better organization of data.

In PIC microcontrollers, these help in managing sensor data, configurations, registers, and peripheral settings efficiently.

This tutorial will cover:

  1. What are Structs and Enums?
  2. How to use Structs in Embedded C?
  3. How to use Enums for better readability?
  4. Real-world Embedded C examples using PIC microcontrollers

What are Structs (struct) and Enums (enum)?

Structs (struct)

A struct (structure) is a user-defined data type that groups multiple related variables of different data types under one name.

Use Case in Embedded Systems:

  • Storing sensor data (temperature, pressure, humidity).
  • Managing I/O pin configurations.
  • Handling UART communication settings.

Enums (enum)

An enum (enumeration) is a user-defined data type that assigns meaningful names to integer constants.

Use Case in Embedded Systems:

  • Defining state machines.
  • Representing error codes.
  • Managing peripheral modes (ADC, PWM, UART baud rates).

How to Use Structs in Embedded C for PIC?

Declaring and Using a Struct

#include <xc.h>

typedef struct {
    unsigned int temperature;
    unsigned int humidity;
    unsigned int pressure;
} SensorData;

void main() {
    SensorData sensor;  // Declare a struct variable

    sensor.temperature = 25;
    sensor.humidity = 50;
    sensor.pressure = 1000;

    while(1);
}

Key Points:

  • typedef struct { … } SensorData; creates a new data type.
  • sensor.temperature = 25; assigns values.

Using Structs to Group Peripheral Registers

Structs are useful when working with hardware registers in PIC.

Example: Struct for PORT Configuration

#include <xc.h>

typedef struct {
    unsigned char TRIS;  // Direction Register
    unsigned char PORT;  // Input/Output Register
    unsigned char LAT;   // Latch Register
} GPIO;

#define LED_PIN  PORTBbits.RB0

void main() {
    GPIO portB;
    
    portB.TRIS = 0x00;  // Set PORTB as output
    portB.PORT = 0x01;  // Set RB0 HIGH

    while(1);
}

Why Use Structs?

  • Easier to manage and understand register groups.
  • Reduces code complexity.

Passing Structs to Functions

Functions can take structs as parameters.

Example: Function to Update Sensor Data

#include <xc.h>

typedef struct {
    unsigned int temperature;
    unsigned int humidity;
} SensorData;

void updateSensor(SensorData *data) {
    data->temperature = 30;  // Modify struct members
    data->humidity = 55;
}

void main() {
    SensorData sensor;
    updateSensor(&sensor);  // Pass struct by reference

    while(1);
}

Key Points:

  • updateSensor(SensorData *data) receives pointer to struct.
  • data->temperature = 30; modifies values inside the function.

Structs for UART Communication Settings

Structs are great for managing UART settings.

Example: Struct for UART Configuration

#include <xc.h>

typedef struct {
    unsigned long baudRate;
    unsigned char parity;
    unsigned char stopBits;
} UART_Config;

void initUART(UART_Config config) {
    // Set baud rate
    SPBRG = (_XTAL_FREQ / (16 * config.baudRate)) - 1;
    
    // Configure parity and stop bits
    TXSTA = (config.stopBits << 3) | (config.parity << 4);
}

void main() {
    UART_Config uart1 = {9600, 0, 1};  // 9600 baud, No Parity, 1 Stop Bit
    initUART(uart1);

    while(1);
}

Advantages:

  • Makes code modular and reusable.
  • Easy to change UART settings without modifying multiple lines.

How to Use Enums in Embedded C for PIC?

Enums allow meaningful names instead of raw numbers.

Example: Enum for LED States

#include <xc.h>

typedef enum {
    LED_OFF,
    LED_ON
} LED_State;

void main() {
    LED_State led = LED_OFF;
    
    while(1) {
        if (led == LED_OFF) {
            PORTBbits.RB0 = 0;  // Turn LED OFF
        } else {
            PORTBbits.RB0 = 1;  // Turn LED ON
        }
    }
}

Key Benefits:

  • Improves code readability (LED_OFF vs 0).
  • Avoids magic numbers.

Using Enums for Error Codes

Enums can be used for error handling.

Example: Enum for ADC Errors

#include <xc.h>

typedef enum {
    ADC_SUCCESS = 0,
    ADC_OVERFLOW = 1,
    ADC_TIMEOUT = 2
} ADC_Status;

ADC_Status readADC() {
    // Simulated ADC Read
    return ADC_SUCCESS;
}

void main() {
    ADC_Status status = readADC();
    
    if (status == ADC_OVERFLOW) {
        // Handle overflow error
    }

    while(1);
}

Why Use Enums for Errors?

  • Makes error handling clear.
  • Avoids using raw numbers (if(status == 1) is less readable).

Using Enums for State Machines

State machines are common in embedded systems (e.g., button debounce, motor control).

Example: Enum for Button Press State

#include <xc.h>

typedef enum {
    BUTTON_RELEASED,
    BUTTON_PRESSED,
    BUTTON_HELD
} ButtonState;

ButtonState checkButton() {
    if (PORTBbits.RB1 == 1)
        return BUTTON_PRESSED;
    else
        return BUTTON_RELEASED;
}

void main() {
    ButtonState state = BUTTON_RELEASED;

    while(1) {
        state = checkButton();
        
        if (state == BUTTON_PRESSED) {
            PORTBbits.RB0 = 1;  // Turn LED ON
        } else {
            PORTBbits.RB0 = 0;  // Turn LED OFF
        }
    }
}

Advantages of Enums in State Machines:

  • Makes code readable (BUTTON_PRESSED vs 1).
  • Helps in debugging.

Best Practices for Using Structs and Enums in Embedded C

Use typedef struct for organizing related variables.
Use pointer to struct (*ptr) for function arguments to save memory.
Group hardware registers using structs for better readability.
Use enums instead of raw numbers (LED_ON vs 1).
Use switch-case with enums for state machines.

Summary

Concept Usage Example
Structs (struct) Group related data typedef struct { int temp; } Sensor;
Pointer to Struct Efficient memory handling Sensor *ptr = &sensor;
Enums (enum) Define readable constants typedef enum { LED_ON, LED_OFF } LED_State;
Enums in State Machine Manage system states typedef enum { IDLE, RUNNING } State;

Structs (struct) and Enums (enum) improve code structure, readability, and efficiency in programming. By grouping related data and using readable constants, you can write cleaner, more maintainable code.

Related posts

Using Macros and Preprocessor Directives in C for a PIC Microcontroller

Understanding Pointers and Memory Management in Embedded C for PIC Microcontrollers

Generating PWM Signals with PIC CCP Module Tutorial

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Read More