Practical Techniques for Common Problem Models in Lanqiao Cup Microcontroller Programming

Standardized Code Patterns for Varible Modification

1. Toggling Between 0 and 1

Use the XOR operator to switch a variable between 0 and 1 each time the code executes.

DisplayModeFlag ^= 1;

2. Cycling Through a Range of Values

Increment a variable and reset it to the starting value once it exceeds an upper limit.

if (++DisplayModeFlag == LIMIT_VALUE + 1) {
    DisplayModeFlag = 0;
}

3. Incrementing and Decrementing with Bounds Checking

Implement bounds checking to prevent parameters from exceeding defined minimum and maximum values.

case KEY_DECREMENT:
    if (DisplayModeFlag == 1) { // In temperature parameter setting mode
        if (--TempParam == 0xFF) { // Underflow check, reset to 0
            TempParam = 0;
        }
    }
    break;
case KEY_INCREMENT:
    if (DisplayModeFlag == 1) {
        if (++TempParam == 100) { // Overflow check, reset to 99
            TempParam = 99;
        }
    }
    break;

Seven-Segment Display Management

1. Multi-Mode Interface Display and Switching

Display Logic

Define a display mode variable (SegMode) and update the display buffer (SegBuffer) accordingly in the processing function.

unsigned char SegMode; // Display mode flag
float CurrentTemp; // Temperature reading
unsigned char TempSetpoint = 25; // Temperature setpoint
float OutputVoltage; // DA output voltage

void Seg_UpdateDisplay() {
    static unsigned char slow_counter = 0;
    if (slow_counter) return;
    slow_counter = 1; // Display refresh rate control

    // Update sensor readings here
    CurrentTemp = Read_Temperature();

    switch (SegMode) {
        case 0: // Temperature display view
            SegBuffer[0] = 11; // 'C' symbol
            SegBuffer[4] = (unsigned char)CurrentTemp / 10 % 10;
            SegBuffer[5] = (unsigned char)CurrentTemp % 10;
            SegBuffer[6] = (unsigned int)(CurrentTemp * 100) / 10 % 10;
            SegBuffer[7] = (unsigned int)(CurrentTemp * 100) % 10;
            DecimalPoint[5] = 1; // Enable decimal point
            break;
        case 1: // Parameter setting view
            SegBuffer[0] = 12; // 'P' symbol
            SegBuffer[4] = SegBuffer[5] = 10; // Blank digits 5 and 6
            SegBuffer[6] = TempSetpoint / 10 % 10;
            SegBuffer[7] = TempSetpoint % 10;
            DecimalPoint[5] = 0; // Disable decimal point
            break;
        case 2: // DA output view
            SegBuffer[0] = 13; // 'A' symbol
            SegBuffer[5] = (unsigned char)OutputVoltage;
            SegBuffer[6] = (unsigned int)(OutputVoltage * 100) / 10 % 10;
            SegBuffer[7] = (unsigned int)(OutputVoltage * 100) % 10;
            DecimalPoint[5] = 1;
            break;
    }
}

Mode Switching

Change the display mode in the key processing routine.

For two modes only (0 and 1):

case KEY_MODE_SWITCH:
    SegMode ^= 1; // Toggle between modes
    break;

For three or more modes:

case KEY_MODE_SWITCH:
    if (++SegMode == 3) {
        SegMode = 0; // Cycle through modes 0, 1, 2
    }
    break;

2. Flashing Display for Selected Parameters

Define a blinking flag and an array to store parameters. Use a timer interrupt to toggle the flag periodically and conditionally blank the corresponding display segment.

bit BlinkFlag; // Blinking control flag
unsigned char ParamData[2] = {30, 20}; // Upper and lower limits
unsigned char SelectedParamIndex; // Currently selected parameter index (0 or 1)

// Inside display function:
case 1: // Parameter setting view
    SegBuffer[0] = 12; // 'P'
    SegBuffer[3] = ParamData[0] / 10 % 10;
    SegBuffer[4] = ParamData[0] % 10;
    SegBuffer[5] = 13; // '-'
    SegBuffer[6] = ParamData[1] / 10 % 10;
    SegBuffer[7] = ParamData[1] % 10;

    if (SelectedParamIndex == 0) {
        // Flash the upper limit digits
        SegBuffer[3] = BlinkFlag ? (ParamData[0] / 10 % 10) : 10; // 10 means blank
        SegBuffer[4] = BlinkFlag ? (ParamData[0] % 10) : 10;
    } else {
        // Flash the lower limit digits
        SegBuffer[6] = BlinkFlag ? (ParamData[1] / 10 % 10) : 10;
        SegBuffer[7] = BlinkFlag ? (ParamData[1] % 10) : 10;
    }
    break;

// In Timer ISR, toggle the flag every 500ms
void Timer0_ISR(void) interrupt 1 {
    static unsigned int ms_counter = 0;
    // ... other timer code
    if (++ms_counter == 500) {
        ms_counter = 0;
        BlinkFlag ^= 1;
    }
}

A concise alternative for managing the blanking segment:

SegBuffer[3] = 10 + (unsigned char)BlinkFlag; // Alternates between 10 (blank) and 11 (dash)

3. Parameter Setting and Saving

Use separate variables for display (editable) and control (actual value). Transfer values between them when entering or exiting the setting mode.

unsigned char TempDisplayValue; // For display and editing
unsigned char TempControlValue = 25; // For actual control, initialized

// In key handler for mode switch:
case KEY_MODE_SWITCH:
    if (++SegMode == 3) SegMode = 0;
    if (SegMode == 1) { // Entering setting mode from display
        TempDisplayValue = TempControlValue; // Load current value for editing
    }
    if (SegMode == 2) { // Exiting setting mode
        TempControlValue = TempDisplayValue; // Save edited value
    }
    break;

For parameter validation upon exit:

case KEY_MODE_SWITCH:
    SegMode ^= 1; // Toggle between two modes
    if (SegMode == 1) { // Enter setting mode
        DisplayArray[0] = ControlArray[0]; // Copy control values to display
        DisplayArray[1] = ControlArray[1];
        ParamIndex = 1; // Reset selection pointer
    } else { // Exit setting mode
        if (DisplayArray[0] > DisplayArray[1]) { // Validate upper > lower
            ControlArray[0] = DisplayArray[0]; // Save values
            ControlArray[1] = DisplayArray[1];
            ValidationFlag = 0; // Set OK flag
        } else {
            ValidationFlag = 1; // Set error flag
        }
    }
    break;

4. Blanking Leading Zeros

Iterate through higher-order display segments and set them to blank (10) if they are zero, ensuring at least the last digit remains visible.

unsigned char idx = 3; // Start from digit 3
while (SegBuffer[idx] == 0) {
    SegBuffer[idx] = 10; // Blank this digit
    if (++idx == 7) break; // Stop before the last digit to avoid blanking everything
}

Key Press Handling with Long-Press Detection

Detect short and long presses by measuring key hold time in a timer interrupt. For example, to enable rapid increment/decrement after a 500ms long press.

unsigned int KeyHoldTimer;
bit KeyHoldActiveFlag;

// In key scanning logic:
if (DisplayModeFlag == 1) { // Only active in a specific mode
    if (KeyPressEvent == KEY_INC || KeyPressEvent == KEY_DEC) {
        KeyHoldActiveFlag = 1; // Start timing on press
    }
}

if (KeyHoldTimer < 500) { // Short press handling
    if (KeyReleaseEvent == KEY_INC) {
        KeyHoldActiveFlag = 0; KeyHoldTimer = 0;
        // Perform single increment
        if (++ParamData[SelectedParamIndex] == UPPER_LIMIT) ParamData[SelectedParamIndex] = LOWER_LIMIT;
    }
    // ... similar for decrement
} else { // Long press handling
    if (KeyStillPressed == KEY_INC) {
        // Perform rapid increment (triggered each key scan cycle)
        if (++ParamData[SelectedParamIndex] == UPPER_LIMIT) ParamData[SelectedParamIndex] = LOWER_LIMIT;
    }
    // ... similar for decrement
    if (KeyReleaseEvent == KEY_INC || KeyReleaseEvent == KEY_DEC) {
        KeyHoldActiveFlag = 0; KeyHoldTimer = 0; // Reset on release
    }
}

// In Timer ISR:
if (KeyHoldActiveFlag == 1) {
    if (++KeyHoldTimer > 600) KeyHoldTimer = 600; // Cap the timer
}

Example for a 1-second long press to clear and save a counter:

if (DisplayModeFlag == 1) { // In humidity mode
    if (KeyPressEvent == KEY_7) KeyHoldActiveFlag = 1; // Start timing on press
    if (KeyReleaseEvent == KEY_7) { // On release
        KeyHoldActiveFlag = 0;
        if (KeyHoldTimer > 1000) { // Long press action
            CounterValue = 0;
            EEPROM_SaveByte(0, CounterValue); // Save to EEPROM
        }
        KeyHoldTimer = 0; // Reset timer
    }
}

LED Control Patterns

1. Exclusive Mode Indicators

Light only the LED corresponding to the current mode.

unsigned char i;
for (i = 0; i < 3; i++) {
    LedState[LED1 + i] = (i == DisplayModeFlag); // Only one LED is on
}

2. Conditional LED Activation

Turn on an LED based on a logical expression derived from sensor or state data.

// Example: Turn on LED2 if voltage is between 1.5V-2.5V or above 3.5V
LedState[LED2] = ((SensorVoltage >= 1.5 && SensorVoltage < 2.5) || (SensorVoltage >= 3.5));

3. Global LED Deactivation Based on Flag

Turn off all LEDs when a specific condition is met.

if (LedEnableFlag == 0) { // LEDs disabled
    for (i = 0; i < 8; i++) {
        LedState[i] = 0; // Turn off all LEDs
    }
}

4. Single LED Blinking

Blink a specific LED by AND-ing an enable flag with a toggling blink flag.

LedState[LED0] = LedEnableFlag & LedBlinkFlag;
// If LedEnableFlag is 1, LedState toggles with LedBlinkFlag.

Timer-Based Event Scheduling

1. Generating a 1-Second Toggle Pulse

Use a millisecodn counter in a timer interrupt to toggle a flag every second.

// In Timer ISR (called every 1ms):
static unsigned int ms_accumulator = 0;
if (++ms_accumulator == 1000) {
    ms_accumulator = 0;
    DisplayBlinkFlag ^= 1;
    LedBlinkFlag ^= 1;
}

2. Counting Seconds in a Specific Mode

Accumulate seconds only when a particular mode is active.

if (DisplayModeFlag == 1) { // In counting mode
    static unsigned int ms_tick = 0;
    if (++ms_tick == 1000) {
        ms_tick = 0;
        SecondsElapsed++; // Increment second counter
    }
}

Tags: microcontroller lanqiao-cup embedded-systems c-programming seven-segment-display

Posted on Fri, 08 May 2026 01:57:47 +0000 by wisewood