Writing to a hardware register in Embedded C - Time & Space Complexity
When writing to a hardware register, it's important to know how the time needed changes as the program runs.
We want to see how the number of write operations grows with the input size.
Analyze the time complexity of the following code snippet.
volatile uint32_t *REG = (volatile uint32_t *)0x40021000;
void write_values(uint32_t *values, int n) {
for (int i = 0; i < n; i++) {
*REG = values[i];
}
}
This code writes each value from an array to a hardware register one by one.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Writing a value to the hardware register inside the loop.
- How many times: Exactly once for each element in the input array, so n times.
Each new value in the input array causes one write operation to the register.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 writes |
| 100 | 100 writes |
| 1000 | 1000 writes |
Pattern observation: The number of writes grows directly with the number of input values.
Time Complexity: O(n)
This means the time to complete writing grows in direct proportion to the number of values.
[X] Wrong: "Writing to a hardware register is always constant time no matter how many values."
[OK] Correct: Each write operation takes time, so writing multiple values adds up linearly.
Understanding how loops affect time helps you explain how embedded code interacts with hardware efficiently.
"What if we added a nested loop writing the same values multiple times? How would the time complexity change?"
Practice
volatile keyword when writing to a hardware register in embedded C?Solution
Step 1: Understand the role of
Thevolatilevolatilekeyword tells the compiler that the variable's value can change at any time, outside the program's control.Step 2: Prevent compiler optimization on hardware registers
Hardware registers can change independently, so the compiler must not optimize away reads or writes to them.Final Answer:
To tell the compiler the value can change unexpectedly and prevent optimization -> Option AQuick Check:
volatile prevents optimization for hardware registers [OK]
- Thinking volatile makes variable read-only
- Assuming volatile allocates memory
- Using volatile to initialize values
Solution
Step 1: Use pointer casting with volatile
To write to a hardware register, cast the address to a pointer to volatile unsigned int.Step 2: Dereference pointer and assign value
Use the dereference operator (*) to write the value 0xFF to the address.Final Answer:
*((volatile unsigned int *)0x40021000) = 0xFF; -> Option DQuick Check:
Pointer cast + dereference = correct syntax [OK]
- Trying to assign value directly to address
- Missing volatile keyword
- Incorrect pointer syntax
volatile unsigned char *reg = (volatile unsigned char *)0x50000000; *reg = 0xAA; unsigned char val = *reg;
Solution
Step 1: Write 0xAA to the hardware register
The pointerregpoints to the register address; writing 0xAA stores this value in the register.Step 2: Read back the value from the register
Reading from*regreturns the stored value 0xAA, which is assigned toval.Final Answer:
val will be 0xAA -> Option AQuick Check:
Write then read returns stored value [OK]
- Assuming register value does not change after write
- Confusing pointer dereference with variable assignment
- Expecting compilation error due to volatile
unsigned int *reg = (unsigned int *)0x60000000; *reg = 0x1F;
Solution
Step 1: Check pointer declaration for hardware register
The pointer should be declared asvolatileto prevent compiler optimizations on hardware registers.Step 2: Understand impact of missing volatile
Withoutvolatile, compiler may optimize away reads/writes, causing incorrect hardware behavior.Final Answer:
Missing volatile keyword in pointer declaration -> Option BQuick Check:
Hardware register pointers must be volatile [OK]
- Ignoring volatile keyword
- Thinking address format is wrong
- Assuming data type must be different
Solution
Step 1: Understand toggling a bit
Toggling means flipping the bit: if 0 then 1, if 1 then 0. This is done using XOR (^=) with a mask.Step 2: Apply XOR with bit mask
Using^= (1 << 3)flips only bit 3 without changing other bits.Final Answer:
*((volatile unsigned int *)0x70000000) ^= (1 << 3); -> Option CQuick Check:
XOR toggles bits, OR sets bits, AND clears bits [OK]
- Using OR (|=) which only sets bits
- Using AND (&=) which clears bits
- Assigning mask directly overwrites all bits
Ready to prove it?
Pass the test and this pattern moves unknown
