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
}
}