Bird
Raised Fist0
Embedded Cprogramming~10 mins

Memory-mapped I/O concept in Embedded C - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Memory-mapped I/O concept
CPU wants to read/write
↓
Access address
↓
Is address in RAM?
Yes→Read/Write RAM
No↓
Is address in I/O range?
Yes→Access device register
No↓
Invalid address
CPU uses normal memory instructions to access RAM or device registers by address; addresses in I/O range connect to hardware devices.
Execution Sample
Embedded C
volatile unsigned int *LED = (volatile unsigned int *)0x40000000;
*LED = 0x1;
unsigned int val = *LED;
Write 1 to LED device register at address 0x40000000, then read back its value.
Execution Table
StepActionAddress AccessedValue Written/ReadResult
1Set pointer LED to address 0x400000000x40000000N/APointer LED points to device register
2Write value 0x1 to *LED0x400000000x1LED device register updated to 1 (turn on LED)
3Read value from *LED0x40000000N/AValue read is 0x1 (LED state)
💡 All memory accesses completed; device register at 0x40000000 used for I/O
Variable Tracker
VariableStartAfter Step 1After Step 2After Step 3
LED (pointer)undefined0x400000000x400000000x40000000
*LED (device register)unknownunknown0x10x1
valundefinedundefinedundefined0x1
Key Moments - 3 Insights
Why do we use a pointer to a fixed address for device access?
Because device registers are mapped to specific memory addresses, using a pointer lets CPU read/write them like normal memory (see execution_table step 1).
What does 'volatile' mean in the pointer declaration?
'volatile' tells the compiler not to optimize away reads/writes because device registers can change independently (see execution_table steps 2 and 3).
Why does reading from *LED return the value we wrote?
Because the device register at that address holds the current state, so reading it returns the last written value (see execution_table step 3).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, what value is written to the device register at step 2?
A0x1
B0x0
Cundefined
D0x40000000
💡 Hint
Check the 'Value Written/Read' column at step 2 in execution_table
At which step does the variable 'val' get assigned a value?
AStep 1
BStep 3
CStep 2
DNever
💡 Hint
Look at variable_tracker for 'val' changes and execution_table step 3
If the address 0x40000000 was not in the I/O range, what would happen?
ACPU ignores the access
BCPU reads/writes normal RAM
CCPU triggers invalid address error
DCPU writes to device anyway
💡 Hint
Refer to concept_flow: after No to 'Is address in RAM?', No to 'Is address in I/O range?' leads to [Invalid address]
Concept Snapshot
Memory-mapped I/O uses normal memory addresses to access device registers.
CPU reads/writes device by accessing special addresses.
Use pointers to fixed addresses for devices.
Declare pointers volatile to prevent optimization.
Reading/writing device registers uses normal memory instructions.
Full Transcript
Memory-mapped I/O means the CPU uses normal memory instructions to talk to devices by reading and writing special memory addresses. The CPU checks if the address is RAM or device. If device, it accesses hardware registers. In the example, a pointer LED points to address 0x40000000, which is a device register. Writing 0x1 to *LED turns on the LED. Reading from *LED gets the current LED state. The volatile keyword prevents compiler optimizations that could skip these important reads and writes. This way, device control looks like normal memory access but actually controls hardware.

Practice

(1/5)
1.

What is the main purpose of memory-mapped I/O in embedded C?

easy
A. To perform file input/output operations
B. To allocate dynamic memory for variables
C. To manage network communication protocols
D. To control hardware devices using specific memory addresses

Solution

  1. 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.
  2. 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.
  3. Final Answer:

    To control hardware devices using specific memory addresses -> Option D
  4. Quick Check:

    Memory-mapped I/O = hardware control via memory [OK]
Hint: Memory-mapped I/O means hardware control via memory addresses [OK]
Common Mistakes:
  • Confusing memory-mapped I/O with file or network I/O
  • Thinking it allocates memory for variables
  • Assuming it manages software protocols
2.

Which of the following is the correct way to declare a pointer for a memory-mapped hardware register at address 0x40021000 in embedded C?

easy
A. int *reg = (int *)0x40021000;
B. volatile int *reg = (volatile int *)0x40021000;
C. volatile int reg = 0x40021000;
D. int reg = (int *)0x40021000;

Solution

  1. Step 1: Use volatile keyword for hardware registers

    Hardware registers can change anytime, so pointer must be declared volatile to prevent compiler optimizations.
  2. 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.
  3. Final Answer:

    volatile int *reg = (volatile int *)0x40021000; -> Option B
  4. Quick Check:

    Use volatile pointer with correct cast [OK]
Hint: Always use volatile pointer cast for hardware addresses [OK]
Common Mistakes:
  • Omitting volatile keyword
  • Assigning address to int variable instead of pointer
  • Wrong casting or missing pointer declaration
3.

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?

medium
A. 0x00
B. Compiler error
C. 0xFF
D. Undefined behavior

Solution

  1. Step 1: Write 0xFF to hardware register

    The statement *LED_REG = 0xFF; writes the value 0xFF to the memory address 0x50000000.
  2. 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.
  3. Final Answer:

    0xFF -> Option C
  4. Quick Check:

    Write then read same register = same value [OK]
Hint: Read returns last written value to hardware register [OK]
Common Mistakes:
  • Assuming value resets to 0
  • Thinking reading hardware register causes error
  • Ignoring volatile keyword effects
4.

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;
medium
A. Missing volatile keyword for pointer declaration
B. Incorrect address casting syntax
C. Pointer should be declared as int, not unsigned int
D. Cannot assign value directly to pointer

Solution

  1. 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.
  2. Step 2: Identify missing volatile keyword

    The pointer is declared as unsigned int * without volatile, which is incorrect for memory-mapped I/O.
  3. Final Answer:

    Missing volatile keyword for pointer declaration -> Option A
  4. Quick Check:

    Hardware pointers need volatile keyword [OK]
Hint: Always use volatile for hardware register pointers [OK]
Common Mistakes:
  • Ignoring volatile keyword
  • Confusing pointer type with variable type
  • Thinking casting syntax is wrong
5.

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?

hard
A. volatile unsigned int *LED = (volatile unsigned int *)0x70000000; *LED = *LED ^ 0x01;
B. volatile unsigned int *LED = (volatile unsigned int *)0x70000000; *LED = 0x01;
C. unsigned int *LED = (unsigned int *)0x70000000; *LED |= 0x01;
D. volatile unsigned int LED = 0x70000000; LED ^= 0x01;

Solution

  1. 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.
  2. Step 2: Toggle bit 0 without affecting other bits

    Use XOR (^ 0x01) on *LED to flip bit 0 while leaving other bits unchanged.
  3. 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.
  4. Final Answer:

    volatile unsigned int *LED = (volatile unsigned int *)0x70000000; *LED = *LED ^ 0x01; -> Option A
  5. Quick Check:

    Toggle bit with XOR on volatile pointer [OK]
Hint: Use XOR on volatile pointer to toggle bits safely [OK]
Common Mistakes:
  • Overwriting entire register instead of toggling bit
  • Missing volatile keyword causing optimization issues
  • Using variable instead of pointer for hardware address
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard