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


前言

相关说明:

开发板:CT117E-M4(STM32G431RB 蓝桥杯嵌入式比赛板)
开发环境: CubeMX+Keil5
涉及题目:STM32ADC–基于HAL库(第十届蓝桥杯嵌入式省赛-研究生组)
题目难点:LCD与LED冲突。LED3在作为Upper LED,当前电压超出阈值时闪烁时仍存在问题,其他皆正常,用的是新板,不确定是否与板子有关。


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

一、CubeMX配置(第十届蓝桥杯嵌入式省赛完整版)

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

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

3.GPIO:在这里插入图片描述

4.ADC(除图中配置,其他默认即可):在这里插入图片描述

5.I2C(存储电压值、LED灯):在这里插入图片描述

6.TIM6(0.2s中断):在这里插入图片描述
在这里插入图片描述

二、代码相关定义、声明

1.函数声明

void EEPROM_WriteBuff(uint8_t addr,uint8_t *writeBuff,uint32_t numToWrite);	//EEPROM写数组
void EEPROM_ReadBuff(uint8_t addr,uint8_t *readBuff,uint32_t numToRead);	//EEPROM读数组
double ADC_GetValue(void);		//获取ADC的值
void KEY_Scan(void);			//按键扫描
void Setting_Mode(void);		//设置模式	
void Color_change(void);		//选中行高亮
void Data_change(uint8_t mode);	//数据更改(max/min/led)
void Data_read(void);			//从EEPROM中读取数据
void Data_save(void);			//数据保存
char* GET_Statue(void);			//获取当前状态(Upper/Lower/Normal)
void LCD_fresh(void);			//LCD更新
void LCD_Init_Show(void);		//LCD初始化
void LED_refresh(void);			//LED更新

2.宏定义

#define LED_GPIO_PORT   GPIOC
#define LED1_GPIO_PIN	GPIO_PIN_8
#define LED2_GPIO_PIN	GPIO_PIN_9
#define LED3_GPIO_PIN	GPIO_PIN_10
#define LED4_GPIO_PIN	GPIO_PIN_11
#define LED5_GPIO_PIN	GPIO_PIN_12
#define LED6_GPIO_PIN	GPIO_PIN_13
#define LED7_GPIO_PIN	GPIO_PIN_14
#define LED8_GPIO_PIN	GPIO_PIN_15

#define ON 	GPIO_PIN_RESET
#define OFF	GPIO_PIN_SET
#define TOG	2

#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 LED1_TOG()	HAL_GPIO_TogglePin(LED_GPIO_PORT,LED1_GPIO_PIN)
#define LED2_TOG()	HAL_GPIO_TogglePin(LED_GPIO_PORT,LED2_GPIO_PIN)
#define LED3_TOG()	HAL_GPIO_TogglePin(LED_GPIO_PORT,LED3_GPIO_PIN)
#define LED4_TOG()	HAL_GPIO_TogglePin(LED_GPIO_PORT,LED4_GPIO_PIN)
#define LED5_TOG()	HAL_GPIO_TogglePin(LED_GPIO_PORT,LED5_GPIO_PIN)
#define LED6_TOG()	HAL_GPIO_TogglePin(LED_GPIO_PORT,LED6_GPIO_PIN)
#define LED7_TOG()	HAL_GPIO_TogglePin(LED_GPIO_PORT,LED7_GPIO_PIN)
#define LED8_TOG()	HAL_GPIO_TogglePin(LED_GPIO_PORT,LED8_GPIO_PIN)

#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

#define max 	2//max数据对应行 下三个同理
#define min 	4
#define upper	6
#define lower 	8

#define add   1//加模式
#define sub	  0//减模式

3.变量定义

double writeBuff[1];			//EEPROM写数据数组
double readBuff[1];				//EEPROM读数据数组

uint8_t v_maxAddr=0X08;			//EEPROM存储上限电压的地址
uint8_t v_minAddr=0X00;			//EEPROM存储下限电压的地址
double v_max;					//上限电压
double v_min;					//下限电压
double v_now;					//当前电压
double v_step=0.3;				//电压每次改变值
double v_upper=3.3;				//电压更改最大值
double v_lower=0;				//电压更改最小值

uint8_t led_upAddr=0X10;	//EEPROM存储Upper LED的地址
uint8_t led_lowAddr=0X18;	//EEPROM存储Lower LED的地址
int led_up;					//Upper对应LED
int led_low;				//Lower对应LED
int led_max=8;				//最大为LED8
int led_min=1;				//最小为LED1
int led_step=1;				//LED每次改变值
int led_statue=1;			//LED灯状态(0低于下限/1正常状态/2高于上限)
int led_change=0;			//LED灯状态改变标志位
uint8_t led_close[9];		//LED灯关闭数组(为1则下标对应的LED灯关闭)

uint8_t page=1;				//LCD页面(1/2)
uint8_t line_now=48;		//当前行对应坐标
uint8_t line_old=48;		//初始行对应坐标
uint8_t line_end=192;		//结束行对应坐标
uint8_t line_setp=48;		//每行坐标差

uint8_t lcd_fresh_num=0;	//主循环中LCD更新标志(0.4s更新一次,减小LCD更新对LED的影响)

char str[30];				//组合字符串

三、主要函数

1.按键扫描

主循环中只有一个按键有效。

void KEY_Scan()
{
    
    
	//KEY1 界面切换
	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);
			page=2;//LCD页改变
			LCD_fresh();//LCD更新
			Setting_Mode();//进入设置模式
			led_change=1;//LED更新标志,使新用新设定的LED
		}
	}
}

2.设置模式

相比于之前,本次设置模式函数的编写降低了程序耦合度,将数据的增加,减小和保存都写成一个独立的函数来调用。

void Setting_Mode()//设置模式
{
    
    
	uint8_t i;
	line_now=line_old;//当前行更新
	uint8_t lcd_change=0;//LCD更新标志位
	Color_change();//当前行颜色翻转,上一行颜色恢复
	while(1)
	{
    
    
		if(lcd_change)
		{
    
    
			lcd_change=0;
			Color_change();//当前行颜色翻转,上一行颜色恢复
		}
		//KEY1 界面切换
		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);
				Data_save();//将数据存储至EEPROM
				for(i=1;i<9;i++)//LED关闭数组重置
				{
    
    
					led_close[i]=1;
				}
				led_close[led_up]=0;//将新设定的Upper LED对应下标改为0
				led_close[led_low]=0;//将新设定的Lower LED对应下标改为0
				page=1;//LCD显示页改变
				LCD_fresh();//LCD更新
				break;//退出循环
			}
		}
		
		//KEY2 选择按键
		else if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)
		{
    
    
			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_setp;//行增加
				if(line_now>line_end)//如果当前行大于末尾行则回到初始行
					line_now=line_old;
				lcd_change=1;//LCD改变标志位置1
			}
		}
		
		//KEY3 ++
		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);//当前数据以增模式改变
				lcd_change=1;//LCD改变标志位置1
			}
		}
		
		//KEY4 --
		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);//当前行数据以减模式改变
				lcd_change=1;//LCD改变标志位置1
			}
		}
	v_now=ADC_GetValue();//获取R37的电压值
	LED_refresh();//LED动态更新
	}//while
}

3.获取R37电压值

使用ADC时需注意,每次GetValue之前都要调用一次Start函数。

double ADC_GetValue()
{
    
    
	double	count;
	HAL_ADC_Start(&hadc2);
	count=HAL_ADC_GetValue(&hadc2);
	return count*3.3/4096;
}

4.选中行高亮

函数中str保存当前行显示字符串,str2保存上一行显示字符串,首行数据的上一行是末尾行。

void Color_change()
{
    
    
	char str2[30];
	if(line_now==line_old+line_setp*0)
	{
    
    
		sprintf(str,"   Max Volt:%.2fV       ",v_max);
		sprintf(str2,"   Lower:LD%d           ",led_low);
	}
	else if(line_now==line_old+line_setp*1)
	{
    
    
		sprintf(str,"   Min Volt:%.2fV       ",v_min);
		sprintf(str2,"   Max Volt:%.2fV       ",v_max);
	}
	else if(line_now==line_old+line_setp*2)
	{
    
    
		sprintf(str,"   Upper:LD%d       ",led_up);
		sprintf(str2,"   Min Volt:%.2fV       ",v_min);
	}
	else if(line_now==line_old+line_setp*3)
	{
    
    
		sprintf(str,"   Lower:LD%d           ",led_low);
		sprintf(str2,"   Upper:LD%d       ",led_up);
	}
	
	LCD_SetTextColor(Blue);//上一行颜色恢复
	LCD_SetBackColor(White);
	if(line_now==line_old)
		LCD_DisplayStringLine(line_end,(unsigned char*)str2);
	else
		LCD_DisplayStringLine(line_now-line_setp,(unsigned char*)str2);
	
	LCD_SetTextColor(White);//当前颜色翻转
	LCD_SetBackColor(Blue);
	LCD_DisplayStringLine(line_now,(unsigned char*)str);
}

5.LED灯关闭

LED关闭数组中,若值为1,则下标对应LED灯关闭。

void LED_AllClose()//LED关闭数组中,若值为1,则下标对应LED灯关闭
{
    
    
	uint8_t i;
	for(i=1;i<9;i++)
	{
    
    
		if(led_close[i]==1)
		{
    
    
			switch(i)
			{
    
    
				case 1:
					LED1(OFF);
					break;
				
				case 2:
					LED2(OFF);
					break;
				
				case 3:
					LED3(OFF);
					break;
				
				case 4:
					LED4(OFF);
					break;
				
				case 5:
					LED5(OFF);
					break;
				
				case 6:
					LED6(OFF);
					break;
				
				case 7:
					LED7(OFF);
					break;
				
				case 8:
					LED8(OFF);
					break;
			}
		}
	}
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

6.Main函数

int main(void)
{
    
    
	uint8_t i;
  /* 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();
  I2CInit();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */
	LCD_Init();
	HAL_ADC_Start(&hadc2);//开启ADC2
	LCD_Init_Show();//LCD开机界面显示
	TIM6->SR=0;//中断标志位置0
	Data_read();//从EEPROM中读取数据
	
	for(i=1;i<9;i++)//初始化LED关闭数组
	{
    
    
		led_close[i]=1;
	}
	led_close[led_up]=0;//开启Upper LED
	led_close[led_low]=0;//开启Lower LED
	LED_AllClose();//其余灯全部关闭
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    
    
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
		KEY_Scan();//按键扫描
		v_now=ADC_GetValue();//获取ADC的值
		if(led_statue==2 ||led_statue==0)//LED状态为闪烁(Upper/Lower)
		{
    
    
			if(lcd_fresh_num>1)//每0.4s刷新一次LCD
			{
    
    
				lcd_fresh_num=0;
				LCD_fresh();
			}
		}
		else//LED为常亮
		{
    
    
			LCD_fresh();
		}
		LED_refresh();//LED更新
  }
  /* USER CODE END 3 */
}

四、实验结果


Max==Min bug已修复

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

在这里插入图片描述


总结

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

猜你喜欢

转载自blog.csdn.net/Octopus1633/article/details/123604441