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: