一、分析
這屆的真題,有點像第七屆的液位檢測。
這屆的題目開始,貌似比賽描述的功能,邏輯上變得更好梳理了。一開始就把大致的功能給你說明一遍,不像之前都是一塊一塊的說明。
1. 基本功能
1)測量競賽板上電位器 R37 輸出的模擬電壓信號,并通過 LCD 顯示
2)通過 LED 指示燈實現超出上限、低于下限的提醒功能
3)通過按鍵實現閾值范圍和輸出提醒指示燈的設置功能
4)通過 E2PROM 實現參數的斷電存儲功能
2. 顯示功能
顯示界面有兩個:數據顯示界面和參數配置界面
1)數據顯示界面
- 顯示 界面名稱、采集的實時電壓數據 和 狀態
- 電壓單位為 V,保留小數點后兩位
- 狀態(Status):超出上限(Upper)、低于下限(Lower)和正常(Normal)
2)參數配置界面
- 顯示:界面名稱、電壓上限、電壓下限;電壓超出上限 LD1,電壓低于下限 LD2 指示燈。
- 電壓上下限:0~3.3V,設備應具備錯誤設置的保護功能
偽代碼
typedef enum{Data_Disp,Set_Pama
}Lcd_State;
Lcd_State L_state = Data_Disp;typedef enum{Upper,Lower,Normal
} Volt_Status;
Volt_Status Status = Normal;float R37_Volt_Sum, R37_Volt, R37_Volt_AVE;
unsigned int choose_pama = 1;float Get_Volt(){//均值濾波for(int i = 0; i < 9; i++){R37_Volt = ADC_Get1();R37_Volt_Sum += R37_Volt;}//16位精度,分4096R37_Volt_Sum *= 3.3;R37_Volt_Sum /= 4096;R37_Volt_AVE = R37_Volt_AVE/10;return R37_Volt_AVE;
}
void Lcd_Proc(void){if(L_state == Data_Disp){R37_Volt_AVE = Get_Volt();//顯示 界面、電壓、狀態Lcd_Disp(Line0, "Main");Lcd_Disp(Line2, "Volt: %fV", R37_Volt_AVE);Lcd_Disp(Line3, "%s", Status);}else if(L_state == Set_Pama){i2c_read(Save_Volt_Max_Min_Led, 0, 4);Lcd_Disp(Line0, "Setting");if(choose_pama == 1) LCD_SetBackColor(Green);Lcd_Disp(Line1, "Max Volt: %f", Save_Volt_Max_Min_Led[0]);LCD_SetBackColor(White);Lcd_Disp(Line2, "Min Volt: %f", Save_Volt_Max_Min_Led[1]); Lcd_Disp(Line3, "Upper:LD%d",Save_Volt_Max_Min_Led[2]);Lcd_Disp(Line4, "Lower:LD%d",Save_Volt_Max_Min_Led[3]);}
}
3. 按鍵功能
1)B1:設置按鍵,用來切換2個顯示界面的。退出參數設置界面時,應將參數保存到 E2PROM
2)B2:選擇按鍵,被選中的需要高亮
3)B3:加? ? 按鍵,電壓增加 0.3V,LED序號+1
4)B3:減? ? 按鍵,電壓減少 0.3V,LED序號-1
LED 范圍為:1~8.
偽代碼
void Key_Proc(){if(L_state == Data_Disp){if(Key_down == 1){L_state = Set_Pama;}}else if(L_state == Set_Pama){switch (Key_down){case 1:LCD_Clear(White);i2c_write(Save_Volt_Max_Min_Led, 0 , 4);L_state = Data_Disp;break;case 2:if(++choose_pama == 5)choose_pama = 1;break;case 3:switch (choose_pama){case 1:Save_Volt_Max_Min_Led[0]+=3;if(Save_Volt_Max_Min_Led[0] >=33 && Save_Volt_Max_Min_Led[0] <40)Save_Volt_Max_Min_Led[0] = 33;break;case 2:if((Save_Volt_Max_Min_Led[1]+3)<Save_Volt_Max_Min_Led[0])Save_Volt_Max_Min_Led[1] += 3; break;case 3://到了8以后,再加就原地不動if(++Save_Volt_Max_Min_Led[2] >= 8)Save_Volt_Max_Min_Led[2] = 8;//如果相同if(Save_Volt_Max_Min_Led[2] == Save_Volt_Max_Min_Led[3]){if(Save_Volt_Max_Min_Led[3] == 8){Save_Volt_Max_Min_Led[2]=7;}else {Save_Volt_Max_Min_Led[2] += 1;}}break;case 4:if(++Save_Volt_Max_Min_Led[3] >= 8)Save_Volt_Max_Min_Led[2] = 8;if(Save_Volt_Max_Min_Led[2] == Save_Volt_Max_Min_Led[3]){if(Save_Volt_Max_Min_Led[2] == 8){Save_Volt_Max_Min_Led[3]=7;}else {Save_Volt_Max_Min_Led[3] += 1;}}break; }break;case 4:switch (choose_pama){case 1:if((Save_Volt_Max_Min_Led[1]+3) < Save_Volt_Max_Min_Led[0])Save_Volt_Max_Min_Led[0] -= 3;break;case 2:Save_Volt_Max_Min_Led[1] -= 3;if(Save_Volt_Max_Min_Led[1] >= 200)//補碼Save_Volt_Max_Min_Led[1] = 0;break;case 3:if(--Save_Volt_Max_Min_Led[2] == 0)Save_Volt_Max_Min_Led[2] = 1;if(Save_Volt_Max_Min_Led[2] == Save_Volt_Max_Min_Led[3]){if(Save_Volt_Max_Min_Led[3] == 1){Save_Volt_Max_Min_Led[2] = 2;}else {Save_Volt_Max_Min_Led[2] -= 1;}}break;case 4:if(--Save_Volt_Max_Min_Led[3] == 0)Save_Volt_Max_Min_Led[3] = 1;if(Save_Volt_Max_Min_Led[3] == Save_Volt_Max_Min_Led[2]){if(Save_Volt_Max_Min_Led[2] == 1){Save_Volt_Max_Min_Led[3] = 2;}else {Save_Volt_Max_Min_Led[3] -= 1;}}break; }}}
}
4.?LED 指示燈
1)R37 輸出電壓值超過電壓上限值時,上限提醒指示燈以 0.2 秒閃爍,下限指示燈熄滅。
2)R37 輸出電壓值低于電壓下限值時,下限提醒指示燈以 0.2 秒閃爍,上限指示燈熄滅。
3)R37 輸出電壓值介于上限和下限電壓之間時,全滅。
偽代碼
ucled = 0x0;
void Led_Proc(void){if(R37_Volt_AVE*10 >= Save_Volt_Max_Min_Led[0]){ ucled ^= (0x01 << (Save_Volt_Max_Min_Led[2]-1));Status = Upper;}else if(R37_Volt_AVE*10 <= Save_Volt_Max_Min_Led[1]){ucled ^= (0x01 << (Save_Volt_Max_Min_Led[3]-1));Status = Lower;}else if( Save_Volt_Max_Min_Led[1] <= R37_Volt_AVE <= Save_Volt_Max_Min_Led[0]){Led_Disp(0x00);Status = Normal;}}
5. 初始狀態
1)重新上電,從 E2PROM 載入各個參數。
2)默認指示燈 LD1 和 LD2
3)默認閾值 2.4V 和 1.2V
unsigned int Save_Volt_Max_Min_Led[4] = {24,12,1,2};
二、CubeMx
這次只用配置 Key、LED、ADC即可。打開賽點資源包,按照原理圖配置。
1. LED
2. Key
3. ADC
三、模版編寫
1. LED
void Led_Disp(uint8_t ucled){HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC, ucled << 8, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
2. Key
uint8_t Key_Scan(){uint8_t Key_val;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)==GPIO_PIN_RESET)Key_val = 1;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==GPIO_PIN_RESET)Key_val = 2;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2)==GPIO_PIN_RESET)Key_val = 3;if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==GPIO_PIN_RESET)Key_val = 4;return Key_val;
}
3. ADC
uint16_t Get_ADC2(){uint16_t adc = 0;HAL_ADC_Start(&hadc2);adc = HAL_ADC_GetValue(&hadc2);return adc;
}
4. I2C
void i2c_read(uint8_t* buf, uint8_t addr, uint8_t num){I2CStart();I2CSendByte(0xa0);I2CWaitAck();I2CSendByte(addr);I2CWaitAck();I2CStart();I2CSendByte(0xa1);I2CWaitAck();while(num--){*buf++ = I2CReceiveByte();if(num)I2CSendAck();elseI2CSendNotAck();}I2CStop();
}
void i2c_write(uint8_t* buf, uint8_t addr, uint8_t num){I2CStart();I2CSendByte(0xa0);I2CWaitAck();I2CSendByte(addr);I2CWaitAck();while(num--){I2CSendByte(*buf++);I2CWaitAck();}I2CStop();HAL_Delay(500);
}
四、完整代碼編寫
1. 全局變量
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "i2c_hal.h"
#include "lcd.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/*兩個界面*/
typedef enum{Data_Disp,Set_Pama
}Lcd_State;
Lcd_State L_state = Data_Disp;/*狀態*/
typedef enum{Upper,Lower,Normal
} Volt_Status;const char* StatusStrings[] = {"Upper","Lower","Nomal"
};
Volt_Status Status = Normal;
/* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
float R37_Volt, R37_Volt_Sum, R37_Volt_AVE;
uint8_t Lcd_Str_Disp[21];
uint8_t Save_Volt_Max_Min_Led[4] = {24,12,1,2};uint8_t Key_val, Key_up, Key_down, Key_old;
uint8_t choose_pama = 1;
__IO uint32_t uwTick_Set_Lcd = 0;
__IO uint32_t uwTick_Set_Key = 0;
__IO uint32_t uwTick_Set_Led = 0;uint8_t ucled = 0x0;
/* USER CODE END PD */
2. 主函數
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_ADC2_Init();/* USER CODE BEGIN 2 */LCD_Init();LCD_Clear(White);LCD_SetBackColor(White);LCD_SetBackColor(Blue);I2CInit();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */Lcd_Proc();Key_Proc();Led_Proc();/* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
3. 顯示功能
void Lcd_Proc(){if((uwTick - uwTick_Set_Lcd) < 100)return;uwTick_Set_Lcd = uwTick;if(L_state == Data_Disp){//R37_Volt_AVE = Get_Volt();R37_Volt_AVE = (((float)Get_ADC2())/4096)*3.3;//顯示:界面、電壓、狀態sprintf((char*) Lcd_Str_Disp, "Main");LCD_DisplayStringLine(Line0, Lcd_Str_Disp);sprintf((char*) Lcd_Str_Disp, "Volt: %4.2fV", R37_Volt_AVE);LCD_DisplayStringLine(Line2, Lcd_Str_Disp);sprintf((char*) Lcd_Str_Disp, "Status:%s", StatusStrings[Status]);LCD_DisplayStringLine(Line3, Lcd_Str_Disp);}else if(L_state == Set_Pama){//i2c_read(Save_Volt_Max_Min_Led, 0, 4);//Volt_Max_Min[0] = Save_Volt_Max_Min_Led[0];//Volt_Max_Min[1] = Save_Volt_Max_Min_Led[1];//ucled_Max = Save_Volt_Max_Min_Led[2];//ucled_Min = Save_Volt_Max_Min_Led[3];sprintf((char*) Lcd_Str_Disp, "Setting");LCD_DisplayStringLine(Line0, Lcd_Str_Disp);sprintf((char*) Lcd_Str_Disp, "Max Volt: %3.2f", (float)Save_Volt_Max_Min_Led[0]/10.0);if(choose_pama == 1) LCD_SetBackColor(Green);LCD_DisplayStringLine(Line1, Lcd_Str_Disp);LCD_SetBackColor(White);sprintf((char*) Lcd_Str_Disp, "Min Volt: %3.2f", (float)Save_Volt_Max_Min_Led[1]/10.0);if(choose_pama == 2) LCD_SetBackColor(Blue);LCD_DisplayStringLine(Line2, Lcd_Str_Disp);LCD_SetBackColor(White);sprintf((char*) Lcd_Str_Disp, "Upper:LD%1d", (uint8_t)Save_Volt_Max_Min_Led[2]);if(choose_pama == 3) LCD_SetBackColor(Blue);LCD_DisplayStringLine(Line3, Lcd_Str_Disp);LCD_SetBackColor(White);sprintf((char*) Lcd_Str_Disp, "Lower:LD%1d", (uint8_t)Save_Volt_Max_Min_Led[3]);if(choose_pama == 4) LCD_SetBackColor(Blue);LCD_DisplayStringLine(Line4, Lcd_Str_Disp);LCD_SetBackColor(White);}
}
4. 按鍵功能
void Key_Proc(){if((uwTick - uwTick_Set_Key) < 100)return;uwTick_Set_Key = uwTick;Key_val = Key_Scan();Key_down = Key_val & (Key_val ^ Key_old); Key_up = ~Key_val & (Key_val ^ Key_old);Key_old = Key_val;if(L_state == Data_Disp){if(Key_down == 1){LCD_Clear(White);L_state = Set_Pama;}}else if(L_state == Set_Pama){switch (Key_down){case 1:LCD_Clear(White);i2c_write(Save_Volt_Max_Min_Led, 0 , 4);L_state = Data_Disp;break;case 2:if(++choose_pama == 5)choose_pama = 1;break;case 3:switch(choose_pama){case 1:Save_Volt_Max_Min_Led[0]+=3;if(Save_Volt_Max_Min_Led[0] >=33 && Save_Volt_Max_Min_Led[0] <40)Save_Volt_Max_Min_Led[0] = 33;break;case 2:if((Save_Volt_Max_Min_Led[1]+3)<Save_Volt_Max_Min_Led[0]){Save_Volt_Max_Min_Led[1] += 3;}break;case 3://到了8以后,再加就原地不動if(++Save_Volt_Max_Min_Led[2] >= 8)Save_Volt_Max_Min_Led[2] = 8;//如果相同if(Save_Volt_Max_Min_Led[2] == Save_Volt_Max_Min_Led[3]){if(Save_Volt_Max_Min_Led[3] == 8){Save_Volt_Max_Min_Led[2]=7;}else {Save_Volt_Max_Min_Led[2] += 1;}}break;case 4:if(++Save_Volt_Max_Min_Led[3] >= 8)Save_Volt_Max_Min_Led[2] = 8;if(Save_Volt_Max_Min_Led[2] == Save_Volt_Max_Min_Led[3]){if(Save_Volt_Max_Min_Led[2] == 8){Save_Volt_Max_Min_Led[3]=7;}else {Save_Volt_Max_Min_Led[3] += 1;}}break;}break;case 4:switch(choose_pama){case 1:if((Save_Volt_Max_Min_Led[1]+3) < Save_Volt_Max_Min_Led[0])Save_Volt_Max_Min_Led[0] -= 3;break;case 2:Save_Volt_Max_Min_Led[1] -= 3;if(Save_Volt_Max_Min_Led[1] >= 200)//補碼Save_Volt_Max_Min_Led[1] = 0;break;case 3:if(--Save_Volt_Max_Min_Led[2] == 0){Save_Volt_Max_Min_Led[2] = 1;}if(Save_Volt_Max_Min_Led[2] == Save_Volt_Max_Min_Led[3]){if(Save_Volt_Max_Min_Led[3] == 1){Save_Volt_Max_Min_Led[2] = 2;}else {Save_Volt_Max_Min_Led[2] -= 1;}}break;case 4:if(--Save_Volt_Max_Min_Led[3] == 0){Save_Volt_Max_Min_Led[3] = 1;}if(Save_Volt_Max_Min_Led[3] == Save_Volt_Max_Min_Led[2]){if(Save_Volt_Max_Min_Led[2] == 1){Save_Volt_Max_Min_Led[3] = 2;}else {Save_Volt_Max_Min_Led[3] -= 1;}}break;}break;}}
}
5. LED 指示燈
void Led_Proc(){//ucled_Max = Save_Volt_Max_Min_Led[2];//ucled_Min = Save_Volt_Max_Min_Led[3];if((uwTick - uwTick_Set_Led) < 200)return;uwTick_Set_Led = uwTick;if(R37_Volt_AVE*10 >= Save_Volt_Max_Min_Led[0]){ucled ^= (0x01 << (Save_Volt_Max_Min_Led[2]-1));Status = Upper;}else if(R37_Volt_AVE*10 <= Save_Volt_Max_Min_Led[1]){ucled ^= (0x01 << (Save_Volt_Max_Min_Led[3]-1)); Status = Lower; }else if( (Save_Volt_Max_Min_Led[1] <= R37_Volt_AVE*10) && (R37_Volt_AVE*10 <= Save_Volt_Max_Min_Led[0])){ucled = 0x00;Status = Normal;}Led_Disp(ucled);
}
小結
這屆比賽,需要在數值邊界部分要考慮考慮。本屆難度一般。