> 文档中心 > STM32LED--基于HAL库(第七届蓝桥杯嵌入式省赛)

STM32LED--基于HAL库(第七届蓝桥杯嵌入式省赛)

文章目录

  • 前言
  • 一、CubeMX配置(第十三届模拟题完整版)
  • 二、代码相关定义、声明
    • 1.函数声明
    • 2.宏定义
    • 3.变量定义
  • 三、主要函数
    • 1.按键相关函数
    • 2.换行
    • 3.更改数据
    • 4.检测液位等级是否改变
    • 5.串口接收中断
    • 6.定时器中断
    • 7.LED关闭函数
    • 8.Main函数
  • 四、实验结果
  • 五、源码(转载请注明出处)
  • 总结

前言

相关说明:

开发板:CT117E-M4(STM32G431RB 蓝桥杯嵌入式比赛板)
开发环境: CubeMX+Keil5
涉及题目:第七届蓝桥杯嵌入式省赛
题目难点:LCD与LED冲突处理,保证LED正常闪烁频率
(本次项目编写运用了模块化编程,更有利于代码阅读与理解。)


CubeMX配置、主要函数代码及说明:

一、CubeMX配置(第十三届模拟题完整版)

1.使能外部高速时钟:在这里插入图片描述

2.配置时钟树:在这里插入图片描述
3.GPIO:

在这里插入图片描述

4.ADC(检测R37电压值):在这里插入图片描述

5.TIM6:
在这里插入图片描述

6.I2C:在这里插入图片描述

7.USART:在这里插入图片描述

二、代码相关定义、声明

1.函数声明

main.cvoid EEPROM_WriteBuff(uint8_t addr,uint8_t *writeBuff,uint16_t numByteToWrite);//EEPROM写数组函数void EEPROM_ReadBuff(uint8_t addr,uint8_t *readBuff,uint16_t numByteToRead);//EEPROM读数组函数void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);//定时器中断函数void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口接收中断void LCD_Init_Show();//LCD初始化显示界面void LCD_Refresh(uint8_t page);//LCD更新void Level_change();//检测Level是否改变void Data_Read();//数据读取void Data_Save();//数据保存gpio.hvoid KEY_Scan(void);//按键扫描void LED_AllClose(uint8_t *LED_Close);//LED关闭函数adc.hdouble ADC_GetValue(void);//获取R37电压值

2.宏定义

gpio.h#define LED_GPIO_PORT GPIOC#define LED1_GPIO_PINGPIO_PIN_8#define LED2_GPIO_PINGPIO_PIN_9#define LED3_GPIO_PINGPIO_PIN_10#define LED4_GPIO_PINGPIO_PIN_11#define LED5_GPIO_PINGPIO_PIN_12#define LED6_GPIO_PINGPIO_PIN_13#define LED7_GPIO_PINGPIO_PIN_14#define LED8_GPIO_PINGPIO_PIN_15#defineON GPIO_PIN_RESET#define OFFGPIO_PIN_SET#define LED1(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED1_GPIO_PIN,a)#define LED2(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED2_GPIO_PIN,a)#define LED3(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED3_GPIO_PIN,a)#define LED4(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED4_GPIO_PIN,a)#define LED5(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED5_GPIO_PIN,a)#define LED6(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED6_GPIO_PIN,a)#define LED7(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED7_GPIO_PIN,a)#define LED8(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED8_GPIO_PIN,a)#define KEY1_GPIO_PORT GPIOB#define KEY1_GPIO_PIN  GPIO_PIN_0#define KEY2_GPIO_PORT GPIOB#define KEY2_GPIO_PIN  GPIO_PIN_1#define KEY3_GPIO_PORT GPIOB#define KEY3_GPIO_PIN  GPIO_PIN_2#define KEY4_GPIO_PORT GPIOA#define KEY4_GPIO_PIN  GPIO_PIN_0

3.变量定义

main.cuint8_t Threshold_1;//阈值1uint8_t Threshold_2;//阈值2uint8_t Threshold_3;//阈值3uint8_t Threshold_step=5;//阈值每次改变值uint8_t Threshold_max=95;//阈值更改上限值uint8_t Threshold_min=5;//阈值更改下限值uint8_t EE_T1_addr=0x00;//T1在EEPROM中的存储地址uint8_t EE_T2_addr=0x08;//T2在EEPROM中的存储地址uint8_t EE_T3_addr=0x10;//T3在EEPROM中的存储地址double v_value; //R37电压值int heigh;//液位高度uint8_t level;//液位等级uint8_t level_old;//上一次液位等级uint8_t recDat;//串口接收的数据uint8_t LED3_Flag=0;//LED3闪烁标志位uint8_t LED2_Flag;//LED2闪烁标志位uint8_t interNum=0;//进入中断次数 每次为0.1s 10次为1suint8_t LED2_intNum=0;//LED2状态改变次数 每0.1s反转一次 到达10则完成5次闪烁uint8_t LED3_intNum=0;//LED3状态改变次数 每0.1s反转一次 到达10则完成5次闪烁char str[30];//用于组合字符串uint8_t LED_Close[4]={0,0,1,1};//LED灯关闭数组 值为1 则下标对应LED关闭gpio.cuint8_t  line_now=96;//当前行对应坐标 下面同理const uint8_t line_step=48;const uint8_t line_beg=96;const uint8_t line_end=192;const uint8_t add=1;const uint8_t sub=2;extern uint8_t Threshold_1;extern uint8_t Threshold_2;extern uint8_t Threshold_3;extern uint8_t Threshold_step;extern uint8_t Threshold_max;extern uint8_t Threshold_min;extern uint8_t LED_Close[4];extern uint8_t interNum;

三、主要函数

1.按键相关函数

将按键按下后执行的操作封装为一个个独立的函数进行调用,降低函数间的耦合度。

gpio.c/* KEY */void Setting_Mode()//设置模式{line_now=line_beg;Line_change();while(1){LED_AllClose(LED_Close);//更新LEDif(interNum>9)//到达1s后重置中断次数标志位interNum=0;if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)//save data{HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);Data_Save();//保存数据LCD_Refresh(1);//LCD更新为第一页break;//退出循环}}else if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)//change line{HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET);line_now+=line_step;//行增加if(line_now>line_end)//若大于结尾行则回到起始行line_now=line_beg;Line_change();//更新显示}}else if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)//++{HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET);Data_change(add);//在加模式更改数据Line_change();//更新显示}}else if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)//--{HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET);Data_change(sub);//在减模式更改数据Line_change();//更新显示}}}}void KEY_Scan()//按键扫描{if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);LCD_Refresh(2);//LCD更新为第二页Setting_Mode();//进入设置模式}}}

2.换行

当前行高亮,前一行恢复。

gpio.cvoid Line_change(){char str1[30];char str2[30];switch(line_now){case line_beg+0*line_step: sprintf(str1,"  Threshold 1:%dcm  ",Threshold_1);//当前行sprintf(str2,"  Threshold 3:%dcm  ",Threshold_3);//上一行break;case line_beg+1*line_step: sprintf(str1,"  Threshold 2:%dcm  ",Threshold_2);sprintf(str2,"  Threshold 1:%dcm  ",Threshold_1);break;case line_beg+2*line_step: sprintf(str1,"  Threshold 3:%dcm  ",Threshold_3);sprintf(str2,"  Threshold 2:%dcm  ",Threshold_2);break;}LCD_SetBackColor(White);LCD_SetTextColor(Black);LCD_DisplayStringLine(line_now,(unsigned char*)str1);//当前行高亮LCD_SetBackColor(Black);LCD_SetTextColor(White);if(line_now==line_beg)//第一行的上一行为最后一行LCD_DisplayStringLine(line_end,(unsigned char*)str2);//上一行恢复elseLCD_DisplayStringLine(line_now-line_step,(unsigned char*)str2);}

3.更改数据

mode判断加模式还是减模式,switch判断是哪种数据要被改变。

gpio.cvoid Data_change(uint8_t mode){switch(line_now){case line_beg+0*line_step: if(mode==add){if(Threshold_1+Threshold_step!=Threshold_2)//增加后不可与第二阈值相等Threshold_1+=Threshold_step;}else{Threshold_1-=Threshold_step;if(Threshold_1<Threshold_min)//减小后不可小于最低阈值Threshold_1=Threshold_min;}break;case line_beg+1*line_step:if(mode==add){if(Threshold_2+Threshold_step!=Threshold_3)//增加后不可与第三阈值相等Threshold_2+=Threshold_step;}else{if(Threshold_2-Threshold_step!=Threshold_1)//减小后不可与第一阈值相等Threshold_2-=Threshold_step;}break;case line_beg+2*line_step:if(mode==add){Threshold_3+=Threshold_step;if(Threshold_3>Threshold_max)//增加后不可大于最高阈值Threshold_3=Threshold_max;}else{if(Threshold_3-Threshold_step!=Threshold_2)//减小后不可与第二阈值相等Threshold_3-=Threshold_step;}break;}}

4.检测液位等级是否改变

随电压值、阈值变化而变化,与电压值一样每一秒检测一次即可。

main.cvoid Level_change()//检测Level是否改变{static uint8_t first=1;//第一次进入函数标志位(开机初始化时调用)level_old=level;if(heigh<=Threshold_1){level=0;}else if(Threshold_1<heigh && heigh<=Threshold_2){level=1;}else if(Threshold_2<heigh && heigh<=Threshold_3){level=2;}else if(Threshold_3<heigh){level=3;}if(first)//第一次认为无改变{first=0;level_old=level;}else if(level!=level_old)//如果发生改变{LED2_Flag=1;//LED2开启闪烁LED2_intNum=0;//重置LED2状态改变次数if(level>level_old)printf("a:H%d+L%d+U\r\n",heigh,level);elseprintf("a:H%d+L%d+D\r\n",heigh,level);}}

5.串口接收中断

接收到数据后开启LED3闪烁,并判断接收的指令进行反馈。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口接收中断{LED3_Flag=1;//LED3开启闪烁LED3_intNum=0;//重置LED3状态改变次数if(recDat=='C') //如果接收的数据为C{printf("C:H%d+L%d\r\n",heigh,level);}else if(recDat=='S')//如果接收的数据为S{printf("S:TL%d+TM%d+TH%d\r\n",Threshold_1,Threshold_2,Threshold_3);}HAL_TIM_Base_Start_IT(&htim6);HAL_UART_Receive_IT(&huart1,&recDat,1);}

6.定时器中断

控制LED状态

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器中断函数{interNum++;if(interNum>9)//到达10则重置{if(LED_Close[1]==1)LED_Close[1]=0;elseLED_Close[1]=1;}if(LED2_Flag)//LED2开启闪烁{LED2_intNum++;if(LED_Close[2]==1)LED_Close[2]=0;elseLED_Close[2]=1;HAL_GPIO_TogglePin(LED_GPIO_PORT,LED2_GPIO_PIN);if(LED2_intNum>9){LED2_Flag=0;LED2_intNum=0;LED_Close[2]=1;}}if(LED3_Flag)//LED3开启闪烁{LED3_intNum++;if(LED_Close[3]==1)LED_Close[3]=0;elseLED_Close[3]=1;HAL_GPIO_TogglePin(LED_GPIO_PORT,LED3_GPIO_PIN);if(LED3_intNum>9){LED3_Flag=0;LED3_intNum=0;LED_Close[3]=1;}}}

7.LED关闭函数

本题6(定时器中断)+7(LED关闭函数)即可实现闪烁频率的精准控制,6控制灯状态为灭还是亮,保存在数组里,LCD就影响不了,再调用7判断如果数组中值为1,则下标对应LED关闭,否则为开启,理解了逻辑很好写。

void LED_AllClose(uint8_t *LED_Close)//LED_Close数组中值为1 则下标对应LED关闭{uint8_t i;LED1(ON);//默认开启LED1~LED3LED2(ON);LED3(ON);for(i=1;i<4;i++){if(LED_Close[i])//若值为1则关闭 值为0则亮 通过修改数组值达到闪烁{switch(i){case 1:LED1(OFF);break;case 2:LED2(OFF);break;case 3:LED3(OFF);break;}}}LED4(OFF);LED5(OFF);LED6(OFF);LED7(OFF);LED8(OFF);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);}

8.Main函数

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();  MX_TIM6_Init();  MX_USART1_UART_Init();  I2CInit();  /* USER CODE BEGIN 2 */LCD_Init();//LCD初始化HAL_ADC_Start(&hadc2);//ADC开启Data_Read();//从EEPROM中读取数据v_value=ADC_GetValue();//获取R37电压值heigh=v_value/3.3*100;//计算当前液位高度Level_change();//液位等级更新LCD_Init_Show();//LCD显示初始化 将以上数据进行显示TIM6->SR=0;//定时器更新标记清零TIM6->CNT=0;//重置计数值HAL_TIM_Base_Start_IT(&htim6);//开启定时器HAL_UART_Receive_IT(&huart1,&recDat,1);//开启串口接收中断  /* USER CODE END 2 */  /* Infinite loop */  /* USER CODE BEGIN WHILE */  while (1)  {    /* USER CODE END WHILE */    /* USER CODE BEGIN 3 */KEY_Scan();//按键扫描if(interNum>9)//如果进入10次中断 每次0.1s 即1s{interNum=0;//重置进入中断次数标记位v_value=ADC_GetValue();//更新R37电压值heigh=v_value/3.3*100;//更新液位高度Level_change();//更新液位等级LCD_Refresh(1);//更新LCD显示}LED_AllClose(LED_Close);//实时检测是否需要更新LED状态 达到准确无误的闪烁频率  }  /* USER CODE END 3 */}

四、实验结果

五、源码(转载请注明出处)

STM32LED--基于HAL库(第七届蓝桥杯嵌入式省赛)


总结

以上就是全部内容,如有错误请批评指正。