Memory-mapped I/O lets a program talk to hardware devices by reading and writing special memory addresses. This makes hardware control simple and fast.
Memory-mapped I/O concept in Embedded C
volatile uint32_t * const DEVICE_REGISTER = (volatile uint32_t *)0x40000000; // Read value uint32_t value = *DEVICE_REGISTER; // Write value *DEVICE_REGISTER = 0x12345678;
volatile tells the compiler the value can change anytime, so it must always read from memory.
The address (like 0x40000000) is the hardware register location.
volatile uint8_t * const LED_CONTROL = (volatile uint8_t *)0x50000000; *LED_CONTROL = 0x01; // Turn on LED
volatile uint16_t * const BUTTON_STATUS = (volatile uint16_t *)0x50000010;
uint16_t status = *BUTTON_STATUS; // Read button statevolatile uint32_t * const UART_DATA = (volatile uint32_t *)0x40001000; *UART_DATA = 'A'; // Send character 'A' via UART
This program writes a value to a memory-mapped register and reads it back. It then prints the value to show how memory-mapped I/O works.
#include <stdint.h> #include <stdio.h> // Simulated hardware register address volatile uint32_t * const SIMULATED_REGISTER = (volatile uint32_t *)0x20000000; int main() { // Write a value to the hardware register *SIMULATED_REGISTER = 0xABCD1234; // Read back the value uint32_t val = *SIMULATED_REGISTER; // Print the value printf("Register value: 0x%X\n", val); return 0; }
Always use volatile for memory-mapped registers to prevent compiler optimizations that skip actual memory access.
Memory addresses must match the hardware documentation exactly.
Accessing wrong addresses can cause crashes or unexpected behavior.
Memory-mapped I/O uses special memory addresses to control hardware devices.
Use pointers with volatile to read/write hardware registers safely.
This method makes hardware control simple and fast in embedded systems.
Practice
What is the main purpose of memory-mapped I/O in embedded C?
Solution
Step 1: Understand memory-mapped I/O concept
Memory-mapped I/O assigns special memory addresses to hardware devices so the CPU can control them by reading/writing these addresses.Step 2: Differentiate from other memory uses
Unlike dynamic memory allocation or file/network I/O, memory-mapped I/O directly interfaces with hardware registers.Final Answer:
To control hardware devices using specific memory addresses -> Option DQuick Check:
Memory-mapped I/O = hardware control via memory [OK]
- Confusing memory-mapped I/O with file or network I/O
- Thinking it allocates memory for variables
- Assuming it manages software protocols
Which of the following is the correct way to declare a pointer for a memory-mapped hardware register at address 0x40021000 in embedded C?
Solution
Step 1: Use volatile keyword for hardware registers
Hardware registers can change anytime, so pointer must be declared volatile to prevent compiler optimizations.Step 2: Correct pointer type and casting
The pointer should be of type volatile int* and cast the address to (volatile int *) to access the register correctly.Final Answer:
volatile int *reg = (volatile int *)0x40021000; -> Option BQuick Check:
Use volatile pointer with correct cast [OK]
- Omitting volatile keyword
- Assigning address to int variable instead of pointer
- Wrong casting or missing pointer declaration
Consider the following code snippet in embedded C:
volatile unsigned int *LED_REG = (volatile unsigned int *)0x50000000; *LED_REG = 0xFF; unsigned int value = *LED_REG;
What is the value of value after execution?
Solution
Step 1: Write 0xFF to hardware register
The statement *LED_REG = 0xFF; writes the value 0xFF to the memory address 0x50000000.Step 2: Read back the value from the same register
The next line reads the value from the same address, so value will be 0xFF as written.Final Answer:
0xFF -> Option CQuick Check:
Write then read same register = same value [OK]
- Assuming value resets to 0
- Thinking reading hardware register causes error
- Ignoring volatile keyword effects
Identify the error in the following embedded C code snippet for memory-mapped I/O:
unsigned int *TIMER_REG = (unsigned int *)0x60000000; *TIMER_REG = 0x01;
Solution
Step 1: Check pointer declaration for hardware register
Hardware registers must be accessed via volatile pointers to prevent compiler optimizations that may skip reads/writes.Step 2: Identify missing volatile keyword
The pointer is declared as unsigned int * without volatile, which is incorrect for memory-mapped I/O.Final Answer:
Missing volatile keyword for pointer declaration -> Option AQuick Check:
Hardware pointers need volatile keyword [OK]
- Ignoring volatile keyword
- Confusing pointer type with variable type
- Thinking casting syntax is wrong
You want to toggle a hardware LED connected to a memory-mapped register at address 0x70000000. The LED turns on when bit 0 is set to 1 and off when it is 0. Which code snippet correctly toggles the LED state without affecting other bits?
Solution
Step 1: Use volatile pointer to access hardware register
Declare LED as volatile unsigned int pointer to ensure proper hardware access and prevent compiler optimization.Step 2: Toggle bit 0 without affecting other bits
Use XOR (^ 0x01) on *LED to flip bit 0 while leaving other bits unchanged.Step 3: Check other options for errors
volatile unsigned int *LED = (volatile unsigned int *)0x70000000; *LED = 0x01; overwrites all bits, C lacks volatile, D treats LED as variable not pointer.Final Answer:
volatile unsigned int *LED = (volatile unsigned int *)0x70000000; *LED = *LED ^ 0x01; -> Option AQuick Check:
Toggle bit with XOR on volatile pointer [OK]
- Overwriting entire register instead of toggling bit
- Missing volatile keyword causing optimization issues
- Using variable instead of pointer for hardware address
Ready to prove it?
Pass the test and this pattern moves unknown
