C Programming for Embedded Systems – Comprehensive Class
Notes
---
1. Introduction
C programming has been the backbone of embedded systems development for decades. It offers:
- **Direct hardware access** through pointers and memory-mapped I/O.
- **Efficiency** in both execution speed and memory usage.
- **Portability** across microcontrollers with minimal changes.
Embedded systems demand **deterministic behavior**, minimal overhead, and often run without an
operating system. C enables developers to meet these requirements.
---
2. Embedded C vs Standard C
| Feature | Standard C | Embedded C |
|---------------------|-------------------------------------|-------------------------------------|
| Memory model | General-purpose | Limited RAM/ROM |
| Libraries | Standard C libraries | MCU-specific libraries |
| Input/Output | File I/O | Hardware I/O |
| Target | Desktop/Server | Microcontrollers, SoCs |
Embedded C also adds MCU-specific extensions, e.g. `__interrupt`, `__io`.
---
3. Pointers in Embedded Systems
Pointers are critical for:
- **Direct register access**
- **DMA buffer management**
- **Dynamic memory (if available)**
**Example: Accessing a peripheral register**
```c
#define GPIO_PORTA (*(volatile unsigned int*)0x40004000)
void toggle_pin(void) {
GPIO_PORTA ^= (1 << 5);
```
**Explanation**:
- `volatile` prevents compiler optimization of hardware registers.
- Direct memory address mapping allows low-level control.
---
4. Structures and Bit-fields
Structures group related data, and bit-fields allow memory-efficient representation.
**Example: GPIO register layout**
```c
typedef struct {
unsigned int MODE : 2;
unsigned int SPEED : 2;
unsigned int PUPD : 2;
unsigned int OTYPE : 1;
unsigned int : 25; // Reserved bits
} GPIO_Config_t;
GPIO_Config_t *gpioA = (GPIO_Config_t*)0x40020000;
```
---
5. Memory Management in Embedded C
### 5.1 Static vs Dynamic Allocation
- **Static allocation** preferred in embedded systems to avoid heap fragmentation.
- Dynamic allocation (`malloc`, `free`) is often avoided in safety-critical systems.
### 5.2 Memory Segments
- **Text segment** – program instructions (Flash)
- **Data segment** – initialized variables (RAM)
- **BSS segment** – uninitialized variables (RAM)
- **Stack** – function calls, local variables
- **Heap** – dynamic memory
**Diagram Placeholder: Memory Layout of an Embedded Application**
---
6. Hardware Access with Memory-Mapped I/O
Microcontrollers map peripherals into the address space.
```c
#define UART0_DR (*(volatile unsigned int*)0x4000C000)
void uart_send(char c) {
UART0_DR = c;
```
---
7. Interrupts in Embedded C
Interrupts handle asynchronous events.
**Example: Timer ISR**
```c
void __attribute__((interrupt)) TIMER1_ISR(void) {
// Clear interrupt flag
TIMER1->STATUS = 0;
// Perform periodic task
}
```
**Best Practices**:
- Keep ISRs short
- Avoid heavy computation
- Use flags for main loop processing
---
8. Embedded C Best Practices
1. **Use volatile for hardware registers**
2. **Minimize dynamic memory allocation**
3. **Modularize code** for maintainability
4. **Optimize for power and performance**
5. **Comment hardware-specific code clearly**
---
9. Example: Blinking an LED (Bare Metal)
```c
#define LED_PIN (1 << 3)
#define GPIO_OUT (*(volatile unsigned int*)0x50000000)
void delay(unsigned int count) {
while(count--);
int main(void) {
while(1) {
GPIO_OUT ^= LED_PIN;
delay(100000);
```
---
10. Common Interview Questions
1. Explain `volatile` in embedded C.
2. Difference between ISR and normal function.
3. Why avoid dynamic memory allocation?
4. How to map a peripheral register in C?
---
11. Practice Problems
1. Write a program to configure UART at 9600 baud.
2. Implement a software debouncing function for a push button.
3. Create a struct with bit-fields to represent an ADC control register.
---
12. References
- "The C Programming Language" by Kernighan & Ritchie
- ARM Cortex-M Technical Reference Manual
- MISRA C Guidelines
---
**End of Notes – Prepared for Educational Use**