原題展示
原題分析
模擬賽1的題目中需要的準(zhǔn)備的知識(shí)點(diǎn)不多,其中只用到了串口、LCD、LED、按鍵、定時(shí)器的PWM輸出、以及ADC等幾個(gè)模塊,題目要求也簡(jiǎn)單詳細(xì)并且數(shù)量不多,非常適合入門比賽,以及整合自己比賽的模塊。
與模擬賽2相比,當(dāng)然是模擬賽2的試題比較難啦,雖然需要的模塊差不多,但是模擬賽2的功能要求相對(duì)較多、較為復(fù)雜。
與省賽相比嘛,只能說(shuō)省賽的功能要求更多、功能更加復(fù)雜,其余的需要大家自己體會(huì)。??????
題解
LED相關(guān)
通過(guò)查詢產(chǎn)品手冊(cè)知,LED的引腳為PC8~PC15,外加鎖存器74HC573需要用到的引腳PD2。(由于題目要求除題目要求需要使用的LED外其他LED都處于熄滅狀態(tài),此處特意將所有的LED都初始化以便于管理其他的LED燈)
CubeMX配置:
代碼樣例
由于G431的所有LED都跟鎖存器74HC573連接,因此每次更改LED狀態(tài)時(shí)都需要先打開鎖存器,寫入數(shù)據(jù)后再關(guān)閉鎖存器。
/*****************************************************
* 函數(shù)功能:改變所有LED的狀態(tài)
* 函數(shù)參數(shù):
* char LEDSTATE: 0-表示關(guān)閉 1-表示打開
* 函數(shù)返回值:無(wú)
******************************************************/
void changeAllLedByStateNumber(char LEDSTATE)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
//打開鎖存器 準(zhǔn)備寫入數(shù)據(jù)
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
//關(guān)閉鎖存器 鎖存器的作用為 使得鎖存器輸出端的電平一直維持在一個(gè)固定的狀態(tài)
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
/*****************************************************
* 函數(shù)功能:根據(jù)LED的位置打開或者是關(guān)閉LED
* 函數(shù)參數(shù):
* uint16_t LEDLOCATION:需要操作LED的位置
* char LEDSTATE: 0-表示關(guān)閉 1-表示打開
* 函數(shù)返回值:無(wú)
******************************************************/
void changeLedStateByLocation(uint16_t LEDLOCATION,char LEDSTATE)
{
HAL_GPIO_WritePin(GPIOC,LEDLOCATION,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
試題要求的LED顯示其條件都比較單一,在滿足點(diǎn)亮條件時(shí)直接點(diǎn)亮,否則,就直接熄滅即可,至于閃爍的周期控制,可以借助與sysTick中斷實(shí)現(xiàn),如果就因此再開一個(gè)定時(shí)器就有點(diǎn)浪費(fèi)資源了。(雖然小編以前經(jīng)常這樣子干??????)
小編寫的LED工作邏輯函數(shù):
/***************************************
* 函數(shù)功能:LED顯示邏輯函數(shù)
* 函數(shù)參數(shù):無(wú)
* 函數(shù)返回值:無(wú)
***************************************/
static void ledWork(void)
{
// 外部聲明的計(jì)數(shù)值
extern uint16_t count;
// 數(shù)據(jù)界面
if(LCDmod == 0 && HAL_GetTick()%90==0)
{
changeLedStateByLocation(LED1,1);
changeLedStateByLocation(LED2,0);
}
// 參數(shù)界面
else if(LCDmod == 1 && HAL_GetTick()%90==0)
{
changeLedStateByLocation(LED1,0);
changeLedStateByLocation(LED2,1);
}
// r37 > vTemp
if(r37 > VTemp)
{
if(count >= 100)
{
rollbackLedByLocation(LED3);
count = 0;
}
}
else
changeLedStateByLocation(LED3,0);
}
這邊不能夠直接使用HAL_GetTick()
函數(shù)計(jì)時(shí),因?yàn)槭褂迷摵瘮?shù)計(jì)時(shí)會(huì)LED閃爍時(shí)出現(xiàn)頻率不一致的情況。
此處,特意使用count
來(lái)計(jì)數(shù)定時(shí),該變量是放置在一個(gè)1ms觸發(fā)一次的定時(shí)器中。
LCD相關(guān)
樣例代碼
由于LCD的相關(guān)代碼在官方給的比賽資源數(shù)據(jù)包中存在,因此,可以直接調(diào)用資源包中的.c、.h文件來(lái)完成LCD的相關(guān)初始化以及顯示。這是一個(gè)簡(jiǎn)單的LCD初始化函數(shù),其功能是將LCD顯示屏初始化為一個(gè)背景色為黑色、字體顏色為白色的屏幕,具體代碼如下:
/******************************************************************************
* 函數(shù)功能:LCD初始化
* 函數(shù)參數(shù):無(wú)
* 函數(shù)返回值:無(wú)
*******************************************************************************/
void lcdInit(void)
{
//HAL庫(kù)的初始化
LCD_Init();
//設(shè)置LCD的背景色
LCD_Clear(Black);
//設(shè)置LCD字體顏色
LCD_SetTextColor(White);
//設(shè)置LCD字體的背景色
LCD_SetBackColor(Black);
}
在顯示時(shí),可以借助于sprintf()
函數(shù)將需要顯示的數(shù)據(jù)格式成一個(gè)字符串,再在LCD上顯示這個(gè)字符串。
char temp[20];
LCD_DisplayStringLine(Line1,(u8*) " DATA ");
sprintf(temp," VR37:%4.2fV ",r37);
為了操作LED與LCD顯示方便,不讓其相互干擾,小編這里對(duì)LCD進(jìn)行了部分源碼改寫,使得每次LCD顯示時(shí)不改變LED的顯示狀態(tài),具體的方法各位可以點(diǎn)擊查看【藍(lán)橋杯】一文解決藍(lán)橋杯嵌入式開發(fā)板(STM32G431RBT6)LCD與LED顯示沖突問(wèn)題,并講述LCD翻轉(zhuǎn)顯示。
下面附上小編完成模擬賽的LCD部分的詳細(xì)代碼:
/**
* @Name lcdDisplay
* @brief LCD顯示數(shù)據(jù)
* @param char mod:顯示模式 可以切換顯示數(shù)據(jù)
* @retval None
* @author 黑心蘿卜三條杠
* @Data 2023-04-02
**/
static void lcdDisplay()
{
char temp[20];
// 數(shù)據(jù)顯示界面
if(LCDmod == 0)
{
LCD_DisplayStringLine(Line1,(u8*) " DATA ");
sprintf(temp," VR37:%4.2fV ",r37);
LCD_DisplayStringLine(Line3,(u8*)temp);
sprintf(temp," PA7:%dHz ",Pa7Frd);
LCD_DisplayStringLine(Line5,(u8*)temp);
}
// 參數(shù)顯示界面
else if(LCDmod == 1)
{
LCD_ClearLine(Line1);
LCD_DisplayStringLine(Line3,(u8*)" PARA ");
sprintf(temp," VP1:%3.1fV ",VTemp);
LCD_DisplayStringLine(Line5,(u8*)temp);
}
}
(是不是非常簡(jiǎn)單粗暴。哈哈哈哈)
按鍵相關(guān)
????通過(guò)查詢產(chǎn)品手冊(cè)知,開發(fā)板上的四個(gè)按鍵引腳為PB0~PB2、PA0。
CubeMX配置
樣例代碼
由于主板上的按鍵數(shù)量較少,因此小編這里的按鍵讀取操作相對(duì)簡(jiǎn)單粗暴,其實(shí)現(xiàn)步驟為:
- 步驟一:判斷按鍵是否按下以及按鍵鎖是否打開,在兩者同時(shí)滿足的情況下進(jìn)入下一步;
- 步驟二:關(guān)閉按鍵鎖并且延時(shí)10ms,實(shí)現(xiàn)按鍵的延時(shí)消抖;
- 步驟三:再次讀取每個(gè)按鍵的值,判斷按鍵按下的位置;
- 步驟四:讀取每個(gè)按鍵的狀態(tài),如果都處于松開狀態(tài)就打開按鍵鎖;
具體代碼實(shí)現(xiàn):
/*********************************************
* 函數(shù)功能:按鍵掃描 含按鍵消抖 無(wú)長(zhǎng)按短按設(shè)計(jì)
* 函數(shù)參數(shù):無(wú)
* 函數(shù)返回值:按鍵的位置
* 返回值說(shuō)明:B1-1 B2-2 B3-3 B4-4
*********************************************/
unsigned char scanKey(void)
{
//按鍵鎖
static unsigned char keyLock = 1;
//記錄按鍵消抖時(shí)間
// static uint16_t keyCount = 0;
//按鍵按下
if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET
|| HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET || HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET)
&& keyLock == 1){
//給按鍵上鎖 避免多次觸發(fā)按鍵
keyLock = 0;
//按鍵消抖 這里最好不要使用延時(shí)函數(shù)進(jìn)行消抖 會(huì)影響系統(tǒng)的實(shí)時(shí)性
// if(++keyCount % 10 < 5) return 0;
// if(HAL_GetTick()%15 < 10) return 0;
HAL_Delay(10);
//按鍵B1
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET){
return 1;
}
//按鍵B2
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET){
return 2;
}
//按鍵B3
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET){
return 3;
}
//按鍵B4
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET){
return 4;
}
}
//按鍵松開
if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == SET && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == SET
&& HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == SET && HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == SET)
&& keyLock == 0){
//開鎖
keyLock = 1;
}
return 0;
}
調(diào)用上述函數(shù)后,即可判斷每次按鍵按下的位置,之后的按鍵邏輯函數(shù)就相對(duì)簡(jiǎn)單了,大家一起來(lái)看看吧!
/***************************************
* 函數(shù)功能:按鍵邏輯函數(shù)
* 函數(shù)參數(shù):無(wú)
* 函數(shù)返回值:無(wú)
***************************************/
static void keyPro(void)
{
// 按鍵掃描
unsigned char keyRising = scanKey();
switch(keyRising)
{
// B1 切換顯示界面
case 1:
LCDmod ^= 1;
break;
// B2 參數(shù)界面下增加電壓參數(shù)
case 2:
if(LCDmod)
{
VTemp += 0.3f;
if(VTemp > 3.3f) VTemp = 0.0f;
}
break;
// B3 數(shù)據(jù)界面下調(diào)節(jié)頻率
case 3:
if(!LCDmod)
{
Pa7Frd += 1000;
if(Pa7Frd > 10000) Pa7Frd = 1000;
}
break;
// 其他
default:
break;
}
}
定時(shí)器相關(guān)
CubeMX配置
本屆試題定時(shí)器的功能為PWM輸入,PWM輸出時(shí),如果大家不需要改變其占空比或者是頻率,那么大家就可以不用再管理這些個(gè)定時(shí)器了。
大家一起來(lái)看看定時(shí)器的PWM輸出的配置吧!
樣例代碼
模擬題中,需要大家能夠改變PWM輸出的頻率或占空比,大家對(duì)這個(gè)也非常感興趣吧,那么大家一起來(lái)看看吧!??????
/***************************************
* 函數(shù)功能:按鍵邏輯函數(shù)
* 函數(shù)參數(shù):無(wú)
* 函數(shù)返回值:無(wú)
***************************************/
static void changePWMData(void)
{
// 頻率發(fā)生了更新 應(yīng)該更新定時(shí)器頻率
if(Pa7Frd != oldPa7Frd)
{
__HAL_TIM_SetAutoreload(&htim3,1000000/Pa7Frd-1);
HAL_TIM_GenerateEvent(&htim3,TIM_EVENTSOURCE_UPDATE);
oldPa7Frd = Pa7Frd;
}
// 占空比發(fā)生了更新 應(yīng)該更新定時(shí)器占空比
if(Pa7Duty != oldPa7Duty || Pa7Frd != oldPa7Frd)
{
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,(int)((Pa7Duty*1.0/100.0)*(1000000/Pa7Frd)+0.5));
oldPa7Duty = Pa7Duty;
}
}
哦!對(duì)了,大家在使用定時(shí)器前還需要使用函數(shù)開啟定時(shí)器的PWM功能嗷(其實(shí)這里我更喜歡說(shuō)初始化) HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
還有一個(gè)重要的東西,小編在更改PWM周期后更新了定時(shí)器,因?yàn)樾【幇l(fā)現(xiàn)不更新改變定時(shí)器輸出PWM的頻率是沒(méi)啥用的。這里的更新有兩種方法:
- 方法一:直接更改定時(shí)器的寄存器——
TIM2->EGR = TIM_EGR_UG
; - 方法二:使用函數(shù)更新,這里我們使用的觸發(fā)源為計(jì)時(shí)器更新事件——
HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_UPDATE)
串口相關(guān)
CubeMX配置
????配置時(shí)一定一定記得改引腳!?。?/strong>
樣例代碼
本程序中小編使用的是中斷接收PC發(fā)送的數(shù)據(jù)其函數(shù)原型為:
// 函數(shù)原型:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
// 參數(shù)解析:
UART_HandleTypeDef *huart:串口通道;
uint8_t *pData:存放數(shù)據(jù)的buff;
uint16_t Size:一次接收數(shù)據(jù)的長(zhǎng)度
在使用時(shí)還需要使用該函數(shù)“中斷初始化”,否則不能夠進(jìn)入中斷接收數(shù)據(jù);
下面就是一個(gè)串口接收定長(zhǎng)數(shù)據(jù)的demo:
/**********************************************串口相關(guān)************************************/
//定義一個(gè)串口信息的結(jié)構(gòu)
uint8_t _ucRxbuff[1];
/***使用HAL_UART_Receive_IT中斷接收數(shù)據(jù) 每次接收完成數(shù)據(jù)后就會(huì)執(zhí)行該函數(shù)***/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1){
// 重新使能中斷
HAL_UART_Receive_IT(huart,(uint8_t *)&_ucRxbuff,sizeof(_ucRxbuff));
}
}
本屆試題要求的是定長(zhǎng)數(shù)據(jù),因此我們只要使用HAL_UART_Receive_IT(huart,(uint8_t *)&_ucRxbuff,sizeof(_ucRxbuff));
觸發(fā)中斷即可,不需要改變串口接收數(shù)據(jù)的長(zhǎng)度。
題中要求串口功能不僅僅是接收數(shù)據(jù)這么簡(jiǎn)單,其還需要能夠解析串口接收的數(shù)據(jù),并且以此為指令將合適的結(jié)果發(fā)送給PC。下面就是小編寫的一個(gè)簡(jiǎn)單的數(shù)據(jù)處理demo:
/**
* @Name usartPro
* @brief 串口處理邏輯函數(shù)
* @param None
* @retval None
* @author 黑心蘿卜三條杠
* @Data 2023-04-02
**/
static void usartPro(void)
{
#if 0
/************* 串口接收一位數(shù)據(jù)*********/
// 判斷是否接收到串口數(shù)據(jù)
if(_ucRxbuff[0] == '0' || strlen((char*)_ucRxbuff) == 0) return ;
// 判斷是否接收到串口數(shù)據(jù)
if('0'<_ucRxbuff[0] && _ucRxbuff[0]<='9')
Pa7Duty = (_ucRxbuff[0] - '0')*10;
// 其他情況發(fā)送錯(cuò)誤信息
else
HAL_UART_Transmit_IT(&huart1,(u8*)"error\r\n",sizeof((char*)"error"));
// 清空有效值
_ucRxbuff[0] = '0';
#else
/************* 串口接收多位數(shù)據(jù)*********/
// 判斷是否接收到串口數(shù)據(jù)
if(strlen((char*)ucRxbuff) == 0) return ;
// 判斷是否接收到串口數(shù)據(jù)長(zhǎng)度是否合理 串口數(shù)據(jù)格式是否正確
else if(strlen((char*)ucRxbuff) == 1 && '0'<ucRxbuff[0] && ucRxbuff[0]<='9')
Pa7Duty = (ucRxbuff[0] - '0')*10;
else
HAL_UART_Transmit_IT(&huart1,(u8*)"error\r\n",sizeof((char*)"error\r\n"));
memset(ucRxbuff,0,sizeof((char*)ucRxbuff));
lenBuff = 0;
#endif
}
文章福利
下邊是小編個(gè)人整理出來(lái)免費(fèi)的藍(lán)橋杯嵌入式福利,有需要的童鞋可以自取喲!??????
省賽:
- 【藍(lán)橋杯嵌入式】第十一屆藍(lán)橋杯嵌入式省賽(第二場(chǎng))程序設(shè)計(jì)試題及其題解
- 【藍(lán)橋杯嵌入式】第十二屆藍(lán)橋杯嵌入式省賽程序設(shè)計(jì)試題以及詳細(xì)題解
- 【藍(lán)橋杯嵌入式】第十三屆藍(lán)橋杯嵌入式省賽程序設(shè)計(jì)試題及其詳細(xì)題解
- 【藍(lán)橋杯嵌入式】第十三屆藍(lán)橋杯嵌入式省賽(第二場(chǎng))程序設(shè)計(jì)試題及其題解
- 【藍(lán)橋杯嵌入式】第十三屆藍(lán)橋杯嵌入式省賽客觀題以及詳細(xì)題解
- 【藍(lán)橋杯嵌入式】第十二屆藍(lán)橋杯嵌入式省賽客觀題及詳細(xì)題解
國(guó)賽:
- 【藍(lán)橋杯嵌入式】第十二屆藍(lán)橋杯嵌入式國(guó)賽程序設(shè)計(jì)試題以及詳細(xì)題解
- 【藍(lán)橋杯嵌入式】第十三屆藍(lán)橋杯嵌入式國(guó)賽客觀題以及詳細(xì)題解
- 【藍(lán)橋杯嵌入式】第十二屆藍(lán)橋杯嵌入式國(guó)賽客觀題及詳細(xì)題解
其他:
- 【藍(lán)橋杯嵌入式】第十四屆藍(lán)橋杯嵌入式[模擬賽1]客觀題及詳細(xì)題解
- 【藍(lán)橋杯嵌入式】第十四屆藍(lán)橋杯嵌入式[模擬賽2]客觀題及詳細(xì)題解
- 【藍(lán)橋杯嵌入式】第十四屆藍(lán)橋杯嵌入式[模擬賽2]程序設(shè)計(jì)試題及詳細(xì)題解
- 【藍(lán)橋杯】一文解決藍(lán)橋杯嵌入式開發(fā)板(STM32G431RBT6)LCD與LED顯示沖突問(wèn)題,并講述LCD翻轉(zhuǎn)顯示
這是小編自創(chuàng)的嵌入式交流群Q:726128226,歡迎各位大佬加入交流喲!??????文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-412432.html
也歡迎大家留言或私信交流,共同進(jìn)步喲!??????文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-412432.html
到了這里,關(guān)于【藍(lán)橋杯嵌入式】第十四屆藍(lán)橋杯嵌入式[模擬賽1]程序設(shè)計(jì)試題及詳細(xì)題解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!