Mostly Asked Basic Embedded C Programs In Interviews

The foundation of today’s technologically advanced society is embedded C programming. Understanding basic programs is essential for success in job interviews for embedded software positions. This article examines frequently asked questions about Embedded C programs, including those involving timers, interrupts, serial connection, and LED control. In order to help you succeed in interviews and in the field of embedded systems, we will dissect each program and offer code samples. Join us on this adventure as we unravel the fundamental ideas and problems of embedded programming.

1.Blinking an LED

Write a C program to blink an LED connected to GPIO pin.

#include <stdint.h>
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
    // Set the DDR register for the LED pin as an output
    DDRB |= (1 << PB0);
    
    while (1) {
        // Toggle the LED state
        PORTB ^= (1 << PB0);
        _delay_ms(1000); // Delay for 1 second
    }
    return 0;
}

2.UART Communication

Implement a simple UART (serial) communication program to send and receive characters.

Code: Atmega Series

#include <avr/io.h>
#include <util/delay.h>

void UART_init(unsigned int baud) {
    // Set baud rate
    UBRRH = (unsigned char)(baud >> 8);
    UBRRL = (unsigned char)baud;
    // Enable transmitter and receiver
    UCSRB = (1 << TXEN) | (1 << RXEN);
    // Set frame format: 8 data, 1 stop bit
    UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
}

void UART_transmit(unsigned char data) {
    // Wait for empty transmit buffer
    while (!(UCSRA & (1 << UDRE)));
    // Put data into buffer, send the data
    UDR = data;
}

unsigned char UART_receive() {
    // Wait for data to be received
    while (!(UCSRA & (1 << RXC)));
    // Get and return received data from buffer
    return UDR;
}

int main(void) {
    unsigned char data;
    UART_init(9600);
    
    while (1) {
        data = UART_receive();
        UART_transmit(data);
    }
    return 0;
}

3.RTOS Task Creation

Implement two tasks, TaskA and TaskB, using an RTOS (e.g., FreeRTOS) to toggle two LEDs.

Code: Atmega Series

#include "FreeRTOS.h"
#include "task.h"
#include "portable.h"

#define LED1_PIN (1 << 0)
#define LED2_PIN (1 << 1)

void TaskA(void* pvParameters) {
    while (1) {
        // Toggle LED1
        PORTB ^= LED1_PIN;
        vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay for 1 second
    }
}

void TaskB(void* pvParameters) {
    while (1) {
        // Toggle LED2
        PORTB ^= LED2_PIN;
        vTaskDelay(500 / portTICK_PERIOD_MS); // Delay for 500ms
    }
}

int main(void) {
    DDRC = LED1_PIN | LED2_PIN;
    xTaskCreate(TaskA, "TaskA", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
    xTaskCreate(TaskB, "TaskB", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
    vTaskStartScheduler();
    return 0;
}

4. Interrupt Handling

Write a program to handle an external interrupt when a button is pressed.

Code: Atemga Series

#include <avr/io.h>
#include <avr/interrupt.h>

#define BUTTON_PIN (1 << 2)

ISR(INT0_vect) {
    // Handle the interrupt here (e.g., toggle an LED)
    PORTB ^= (1 << PB0);
}

void init_interrupt() {
    // Set BUTTON_PIN as input
    DDRC &= ~BUTTON_PIN;
    // Enable external interrupt INT0
    GICR |= (1 << INT0);
    // Trigger INT0 on falling edge
    MCUCR |= (1 << ISC01);
    MCUCR &= ~(1 << ISC00);
    // Enable global interrupt
    sei();
}

int main(void) {
    // Set PB0 as output for LED
    DDRB |= (1 << PB0);
    init_interrupt();
    
    while (1) {
        // Main loop
    }
    return 0;
}

5. SPI Communication

SPI Initialization and Data Transmission (Master):

#include <avr/io.h>
#include <util/delay.h>

#define SS   PB4
#define MOSI PB5
#define MISO PB6
#define SCK  PB7

void SPI_MasterInit() {
    // Set MOSI, SCK, and SS as output
    DDRB |= (1 << MOSI) | (1 << SCK) | (1 << SS);
    
    // Enable SPI, Master mode, set clock rate fck/16
    SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
}

void SPI_Transmit(char data) {
    // Start transmission
    SPDR = data;
    
    // Wait for transmission complete
    while (!(SPSR & (1 << SPIF)));
}

int main(void) {
    SPI_MasterInit();
    while (1) {
        // Send data
        SPI_Transmit(0x55);
        _delay_ms(1000); // Delay for 1 second
    }
    return 0;
}

SPI Data Reception (Slave):

#include <avr/io.h>
#include <avr/interrupt.h>

#define MISO PB6

void SPI_SlaveInit() {
    // Set MISO as output
    DDRB |= (1 << MISO);
    
    // Enable SPI and SPI interrupt
    SPCR = (1 << SPE) | (1 << SPIE);
    
    // Enable global interrupt
    sei();
}

int main(void) {
    SPI_SlaveInit();
    while (1) {
        // Main loop
    }
    return 0;
}

ISR(SPI_STC_vect) {
    // Data received, read SPDR
    char receivedData = SPDR;
    // Process received data
}

6. I2C Communication

I2C Master Sending Data:

#include <avr/io.h>
#include <util/twi.h>
#include <util/delay.h>

#define SLAVE_ADDRESS 0x50

void I2C_Init() {
    // Set bit rate
    TWBR = 0x20; // Bit rate = 32
    // Enable TWI, generate start condition, clear interrupt flag
    TWCR = (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);
    
    // Wait until TWI finish its current job (start condition)
    while (!(TWCR & (1 << TWINT)));
}

void I2C_Start() {
    // Generate start condition
    TWCR = (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);
    // Wait until TWI finish its current job (start condition)
    while (!(TWCR & (1 << TWINT)));
}

void I2C_Stop() {
    // Generate stop condition
    TWCR = (1 << TWSTO) | (1 << TWINT) | (1 << TWEN);
    // Wait until stop condition execution
    while (TWCR & (1 << TWSTO));
}

void I2C_Write(char data) {
    // Copy data in TWI data register
    TWDR = data;
    // Enable TWI, clear interrupt flag
    TWCR = (1 << TWEN) | (1 << TWINT);
    // Wait until TWI finish its current job (Write operation)
    while (!(TWCR & (1 << TWINT)));
}

int main(void) {
    I2C_Init();
    while (1) {
        // Send data to the I2C slave
        I2C_Start();
        I2C_Write(SLAVE_ADDRESS << 1); // Address + Write
        I2C_Write(0x55); // Data to send
        I2C_Stop();
        _delay_ms(1000); // Delay for 1 second
    }
    return 0;
}

I2C Slave Receiving Data:

#include <avr/io.h>
#include <util/twi.h>
#include <avr/interrupt.h>

#define SLAVE_ADDRESS 0x50

void I2C_Init() {
    // Set slave address and enable acknowledgment
    TWAR = (SLAVE_ADDRESS << 1);
    // Enable TWI, generate acknowledgment, clear interrupt flag
    TWCR = (1 << TWEN) | (1 << TWEA) | (1 << TWINT);
    
    // Enable global interrupt
    sei();
}

int main(void) {
    I2C_Init();
    while (1) {
        // Main loop
    }
    return 0;
}

ISR(TWI_vect) {
    // TWI status register, mask the prescaler bits (TWPS0, TWPS1)
    uint8_t status = TWSR & 0xF8;

    switch (status) {
        case TW_SR_SLA_ACK: // SLA+W received, ACK returned
        case TW_SR_ARB_LOST_SLA_ACK: // Arbitration lost, SLA+W received, ACK returned
            // Receive data and do something with it
            uint8_t receivedData = TWDR;
            // Process received data
            // ...
            
            // Enable acknowledgment
            TWCR = (1 << TWEN) | (1 << TWEA) | (1 << TWINT);
            break;
            
        case TW_ST_SLA_ACK: // SLA+R received, ACK returned
            // Send data to the master
            TWDR = 0xAA; // Data to send
            // Enable acknowledgment
            TWCR = (1 << TWEN) | (1 << TWEA) | (1 << TWINT);
            break;
            
        default:
            // Handle other cases if necessary
            break;
    }
}

More Posts Related To Programming:

Leave a Comment