Troubleshooting Common Peripheral Bugs in Embedded C
Temperature Sensor Data Type Mismatch
Ensure the receiving variable type exactly matches the return type of the temperature acquisition function. A type mismatch, such as assigning an unsigned 32-bit return value to a smaller data type, causes data truncation and erratic readings.
uint32_t thermal_value;
uint32_t read_ds18b20(void)
{
uint8_t lsb, msb;
uint32_t raw_temp;
reset_onewire();
write_onewire(0xCC);
write_onewire(0x44);
delay_us(200);
reset_onewire();
write_onewire(0xCC);
write_onewire(0xBE);
lsb = read_onewire();
msb = read_onewire();
raw_temp = msb & 0x0F;
raw_temp <<= 8;
raw_temp |= lsb;
raw_temp *= 625;
return raw_temp;
}
UART Reception Failure with Timer 2 Baud Rate Generator
When utilizing automated tools to generate baud rate configurations, the system clock frequency defined in the tool must precisely match the actual hardware clock frequency used during programming. Additionally, both the global interrupt enable and the specific serial port interrupt enable must be activated manually after initialization.
void config_uart1_9600(void) // 9600bps @ 12.000MHz
{
SCON = 0x50; // Mode 1, 8-bit, variable baud rate
AUXR |= 0x01; // Timer 2 as baud rate source for UART1
AUXR |= 0x04; // Timer 2 in 1T mode
T2L = 0xC7; // Low byte of timer reload
T2H = 0xFE; // High byte of timer reload
AUXR |= 0x10; // Start Timer 2
EA = 1; // Enable global interrupts
ES = 1; // Enable UART1 interrupt
}
Button State Logic Flaws
When transitioning from a switch-case structure to an if-else chain for key processing, omitting an explicit check for a valid key press allows the idle state (key value 0) to incorrectly trigger conditions. Always validate that the key value is non-zero before evaluating specific key mappings.
void handle_key_input(void)
{
if ((key_pressed == 11) && (ui_state == 1))
{
display_buf[0] = 20; display_buf[1] = 20;
display_buf[2] = 20; display_buf[3] = 20;
action_flag = 1; ui_state = 2;
}
else if ((key_pressed != 0) && (ui_state == 2))
{
activate_led(action_flag);
action_flag++;
}
}
Dynamic LED Blink Frequency Adjustment
When modifying a timer-based blink frequency dynamically, using an equality check (==) for the counter is error-prone. If the target threshold is reduced (e.g., from 400 to 200) while the counter has already surpassed the new threshold, the condition will never be met. Utilizing a greater-than-or-equal-to (>=) operator prevents the counter from stalling.
if (++tick_counter >= blink_threshold)
{
refresh_flag = 1;
tick_counter = 0;
}
Counter Variable Overflow in Blink Logic
If a blink mechanism appears completely inactive despite correct logic, verify the counter data types. Copying and modifying variable declarations often leads to using 8-bit integers (uint8_t) for counters that must reach values like 500, causing an overflow before the threshold is ever reached. Upgrading the counter to 16-bit (uint16_t) resolves the issue.
// Incorrect: uint8_t sec_tick;
uint16_t sec_tick;
bool sec_refresh;
void timer_task_handler(void)
{
if (++seg_tick == 50) { seg_refresh = 1; seg_tick = 0; }
if (++ds_tick == 100) { ds_refresh = 1; ds_tick = 0; }
if (++temp_tick == 100) { temp_refresh = 1; temp_tick = 0; }
if (++sec_tick == 500) { sec_refresh = 1; sec_tick = 0; }
}
Optimizing State Toggle Logic
State toggles can be streamlined by inverting the flag directly within the timer interrupt service routine, eliminating the need for a secondary check in the main processing loop.
// Original approach requiring main loop evaluation
void timer_isr(void) { if (++tick == 500) { event_flag = 1; tick = 0; } }
void main_loop(void) { if (event_flag) { event_flag = 0; state = !state; } }
// Streamlined approach
void timer_isr(void) { if (++tick == 500) { state = !state; tick = 0; } }
Leading Zero Suppression on Seven-Segment Displays
When displaying sensor data like ultrasonic distance, leading zeros on unoccupied digits should be blanked out. Conditional logic must replace the hundreds digit with a blank code when the value drops below 100, rather than displaying a zero.
if (distance_val >= 100)
{
dig_buf[5] = distance_val / 100;
dig_buf[6] = (distance_val % 100) / 10;
dig_buf[7] = distance_val % 10;
}
else
{
dig_buf[5] = DISPLAY_BLANK;
dig_buf[6] = (distance_val % 100) / 10;
dig_buf[7] = distance_val % 10;
}
Transmitting Numeric Data via UART in Text Mode
Sending numerical values over serial requires converting each digit to its ASCII representation. Direct transmission of integers results in unreadable characters. Digits must be mathematically extracted, added to the ASCII offset of 48 (or '0'), and transmitted sequentially as characters.
void uart_transmit_byte(uint8_t data_byte)
{
SBUF = data_byte;
while (TI == 0);
TI = 0;
}
void uart_send_string(uint8_t *ptr)
{
while (*ptr != '\0')
{
uart_transmit_byte(*ptr);
ptr++;
}
}
// Transmitting distance value
if (distance_val < 100)
{
char d1 = (distance_val / 10) + '0';
char d2 = (distance_val % 10) + '0';
uart_send_string("Distance:");
uart_transmit_byte(d1);
uart_transmit_byte(d2);
uart_send_string("cm");
}
else
{
char d1 = (distance_val / 100) + '0';
char d2 = ((distance_val % 100) / 10) + '0';
char d3 = (distance_val % 10) + '0';
uart_send_string("Distance:");
uart_transmit_byte(d1);
uart_transmit_byte(d2);
uart_transmit_byte(d3);
uart_send_string("cm");
}