2015年第六届蓝桥杯—电压测量监控设备

该文详细介绍了使用STM32CubeMX配置的RTC时钟、ADC电压采集、EEPROM存储以及UART串行通信在一款电压测量监控设备中的实现。通过RTC获取时间,ADC采集电压,EEPROM存储参数,以及UART进行数据传输。此外,还涉及了按键操作和LCD显示。该实现是针对2015年第六届蓝桥杯嵌入式竞赛的一个项目。
摘要由CSDN通过智能技术生成

2015年第六届蓝桥杯—电压测量监控设备

功能简述

在这里插入图片描述

任务主体由LCD显示、USART串口通信、LED灯阵显示、E2PROM、Key、RTC、ADC等功能模块组成。完成电压采集并定时上报的功能

设计任务及代码实现

RTC时钟

STM32CubeMX配置

在这里插入图片描述

代码实现

//rtc时钟相关定义
RTC_TimeTypeDef Time;
RTC_TimeTypeDef Setting_Time;
RTC_DateTypeDef Data;

void Get_Time(void)
{
	HAL_RTC_GetTime(&hrtc, &Time, RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc, &Data, RTC_FORMAT_BIN);
}

注意事项:此处一定要先获取RTC时间,再获取日期。如果两者相反,则有可能出现程序运行卡顿。

ADC采集

STM32CubeMX配置

由题意并结合G431芯片参考手册,选用ADC2的IN15。在STM32CubeMX配置如下
在这里插入图片描述

代码实现

  • 此处采用12通道的ADC电压采集,采集电压的范围为0-3.3V。通过计算ADC的分辨率既可以得到采集后的Value值
void Adc(void)
{
	HAL_ADC_Start(&hadc2);
	Value = (HAL_ADC_GetValue(&hadc2)*3.3)/4096;
}

EEPROM存储k值(在EEPROM中对于浮点数进行存储)

说明:此处EEPROM采用软件IIC,在STM32CubeMX中不需要进行配置

代码实现

定义共用体用来存储浮点数

typedef union EEPROM_Float
{
	float a;
	uint8_t b[4];
}float_data;

软件IIC的读写操作函数

//i2c相关操作
void iic_24c02_write(uint8_t *pucBuf , uint8_t ucAddr , uint8_t ucNum)//写操作
{
	//pucbuf代表存入数据的地址或者数组;ucAddr代表字节开始位置;ucNum代表存入的字节数
	
	I2CStart();
	I2CSendByte(0xa0);//0xa0为写操作时候的设备地址
	I2CWaitAck(); //等待完成
	
	I2CSendByte(ucAddr);//发送要存入数据的地址如0代表第一个字节存入第0号位;1代表第1个字节存入1号位
	I2CWaitAck();//等待完成
	
	while(ucNum--)//相当于一个字节一个字节去存储
	{
		I2CSendByte(*pucBuf++);
		I2CWaitAck();
	}
	I2CStop();
	HAL_Delay(50);//防止在短时间内多次存入数据
}

void iic_24c02_read(uint8_t *pucBuf , uint8_t ucAddr , uint8_t ucNum)//读操作指令
{
	//ucNum为想要读取的自己数,pucbuf为读取出来的数据存入的目标数组
	I2CStart();
	I2CSendByte(0xa0);//0xa0为写操作时候的设备地址
	I2CWaitAck(); //等待完成
	
	I2CSendByte(ucAddr);//发送要存入数据的地址如0代表第一个字节存入第0号位;1代表第1个字节存入1号位
	I2CWaitAck();//等待完成
	
	I2CStart();
	I2CSendByte(0xa1);//0xa1为读操作时候的设备地址
	I2CWaitAck(); //等待完成
	
	while(ucNum--)
	{
		*pucBuf++ = I2CReceiveByte();
		if(ucNum)
			I2CSendAck();
		else
			I2CSendNotAck();
	}
	I2CStop();
}

调用软件IIC的存储函数对于k值进行存储

float_data float_write;
void EEPROM_Store(void)
{
	float_write.a = k;
	iic_24c02_write(float_write.b , 0 , 4);
}

UART串行通讯设计

STM32CubeMX配置

为了方便数据的收发,此处采用IDLE+DMA的方式进行串口通信

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

代码实现

  1. UART变量定义
//UART串口变量
uint8_t Rx_Buf[1000],Rx_Buf_Temp[1000];
uint16_t Rx_Size=1000;
  1. 串口中断回调函数及字符处理
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if(huart == &huart1)
	{
		if(Size == 6)
		{
			memcpy(Rx_Buf,Rx_Buf_Temp,Size);
			if(Rx_Buf[0]=='k' && Rx_Buf[1]=='0' && Rx_Buf[2]=='.'){
				k = 1.*(Rx_Buf[3]-48)*0.1;
				sprintf(str4,"ok \r\n");
				HAL_UART_Transmit_IT(&huart1, str4, sizeof(str4));
			}
		}
		HAL_UARTEx_ReceiveToIdle_DMA(&huart1, Rx_Buf_Temp, Rx_Size);
	}
}
  1. main函数中打开空闲中断
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, Rx_Buf_Temp, Rx_Size);

按键操作

代码实现

  1. 按键扫描函数
void Key_Scan(void)
{
	if(HAL_GPIO_ReadPin(KEY_1_GPIO_Port,KEY_1_Pin) == 0)
	{
		HAL_Delay(10);
		if(HAL_GPIO_ReadPin(KEY_1_GPIO_Port,KEY_1_Pin) == 0)
		{
			if(LED_ON_State == 0) LED_ON_State = 1;
			else                  LED_ON_State = 0;
		}
	}
	
	if(HAL_GPIO_ReadPin(KEY_2_GPIO_Port,KEY_2_Pin) == 0)
	{
		HAL_Delay(10);
		if(HAL_GPIO_ReadPin(KEY_2_GPIO_Port,KEY_2_Pin) == 0)
		{
			if(LCD_State ==0)   LCD_State =1;
			else                LCD_State =0; 
		}
	}
	
	if(HAL_GPIO_ReadPin(KEY_3_GPIO_Port,KEY_3_Pin) == 0)
	{
		HAL_Delay(10);
		if(HAL_GPIO_ReadPin(KEY_3_GPIO_Port,KEY_3_Pin) == 0)
		{
			if(Setting_State<3)
				Setting_State++;
			if(Setting_State == 3)
				Setting_State =0;
		}
	}
	
	if(HAL_GPIO_ReadPin(KEY_4_GPIO_Port,KEY_4_Pin) == 0)
	{
		HAL_Delay(10);
		if(HAL_GPIO_ReadPin(KEY_4_GPIO_Port,KEY_4_Pin) == 0)
		{
			if(Setting_State == 0)//设置秒
			{
				Setting_Time.Seconds++;
				if(Setting_Time.Seconds == 60)
					Setting_Time.Seconds = 0;
			}
			else if(Setting_State == 1)
			{
				Setting_Time.Minutes++;
				if(Setting_Time.Minutes == 60)
					Setting_Time.Minutes = 0;
			}
			else if(Setting_State == 2)
			{
				Setting_Time.Hours++;
				if(Setting_Time.Hours == 24)
					Setting_Time.Hours =0;
			}
		}
	}
}

LCD显示(直接调用LCD显示库函数即可)

说明:

  • 为了达到屏幕效果切换,在LCD显示界面设定了不同操作界面的状态;同时为了达到很好的显示效果,不造成LCD卡顿,此处通过输入空白行来达到清屏效果(没有使用clear函数)
void LCD_Show(void)
{
	if(LCD_State == 0){ //显示界面1
		sprintf(str2,"    V1: %.2f", Value);
		LCD_DisplayStringLine(Line1, str2);
		sprintf(str2,"    K:%.1f",k);
		LCD_DisplayStringLine(Line2, str2);
		if(LED_ON_State == 1)  sprintf(str2,"    LED:ON       ");
		else                   sprintf(str2,"    LED:OFF      ");
		LCD_DisplayStringLine(Line3, str2);
		sprintf(str1,"    T:%02d-%02d-%02d",Time.Hours,Time.Minutes,Time.Seconds);
		LCD_DisplayStringLine(Line4, str1);
		sprintf(str2,"                                  ");
		LCD_DisplayStringLine(Line6, str2);
	}
	else if(LCD_State == 1)
	{
		sprintf(str2,"                         ");
		LCD_DisplayStringLine(Line1, str2);
		LCD_DisplayStringLine(Line2, str2);
		LCD_DisplayStringLine(Line4, str2);
		sprintf(str2,"       Setting           ");
		LCD_DisplayStringLine(Line3, str2);
		sprintf(str1,"    T:%02d-%02d-%02d",Setting_Time.Hours,Setting_Time.Minutes,Setting_Time.Seconds);
		LCD_DisplayStringLine(Line6, str1);
	}
	
}

LED显示函数

说明:

  • STM32G431由于LCD与LED有引脚公用,所以每次对LED灯进行操作的时候需要对于锁存器进行使能与失能

代码实现

  1. LED开关灯函数
void LEDx_ON(uint16_t n)
{
	LED_ALL&=(0xFEFF<<(n-1))|(0xFEFF>>(16-(n-1)));
	GPIOC->ODR=LED_ALL;
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

void LEDx_OFF(uint16_t n)
{
	LED_ALL|=(0x0100<<(n-1))|(0x0100>>(16-(n-1)));
	GPIOC->ODR=LED_ALL;
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

void LED_All_Close(void)
{
		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, GPIO_PIN_SET);
		
		HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
  1. LED闪烁函数
void Led1_Toggle(void)
{
	if(LED_State==0)
	{
		LEDx_ON(1);
		LED_State=1;
	}
	else
	{
		LEDx_OFF(1);
		LED_State=0;
	}
}

总工程设计

代码实现

  1. 变量定义
//rtc时钟相关定义
RTC_TimeTypeDef Time;
RTC_TimeTypeDef Setting_Time;
RTC_DateTypeDef Data;
//LCD显示相关定义
unsigned char str1[30],str2[30];
uint8_t LCD_State =0;//控制显示界面
uint8_t Setting_State = 0;//0修改秒 1修改分钟,2修改小时
//Adc相关定义
float k = 0.5;
float Value=0;
//LED闪烁状态变量
uint8_t LED_Option = 1;
uint8_t LED_State = 0; // 控制LED灯电平翻转达到闪烁
uint8_t LED_ON_State = 0; // LED状态标志位,1ON,0 OFF
//UART串口变量
uint8_t Rx_Buf[1000],Rx_Buf_Temp[1000];
uint16_t Rx_Size=1000;
unsigned char str4[30],str3[30],temp[30];
uint8_t Appear_Flag =0;//0代表上报完成,1代表上报未完成。防止上报多次
  1. 功能初始化
  //LCD初始化
  LCD_Init();
  LCD_Clear(White);
  //ADC初始化
  HAL_ADC_Init(&hadc2);
  //uwTick变量设定
  uint32_t Time_uwTick = 0;
  uint32_t LED_uwTick = 0;
  uint32_t LCD_uwTick = 0;
  uint32_t Key_uwTick = 0;
  //USART串口悬起
  HAL_UARTEx_ReceiveToIdle_DMA(&huart1, Rx_Buf_Temp, Rx_Size);
  //LED初始化
  LED_All_Close();
  //EEPROM初始化
  I2CInit();
  1. 主循环
while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  if(uwTick - Time_uwTick>100)
	  {
		  Time_uwTick = uwTick;
		  Get_Time();
		  Adc();		
		  EEPROM_Store();
		  if(Value>3.3*k) LED_Option=1;
		  else{           
			  LED_Option=0;
			  LEDx_OFF(1);
		  }
		  Reporting();
	  }
	  if(LED_Option==1 && LED_ON_State == 1)
	  {
		  if(uwTick-LED_uwTick>200)
		  {
			  LED_uwTick=uwTick;
			  Led1_Toggle();
		  }
	  }
	  if(uwTick - LCD_uwTick >100)
	  {
		  LCD_uwTick = uwTick;
		  LCD_Show();
	  }
	  
	  if(uwTick - Key_uwTick>200)
	  {
		  Key_uwTick = uwTick;
		  Key_Scan();
	  }
  }

注意事项

  1. 在使用IIC总线操作EEPROM的时候,一定要在main函数中使用I2CInit(); 函数。不然会导致数据无法存入EEPROM中
  2. 由于按键采用非阻塞式扫描,需要将按键的扫描周期拉长,方式由于扫描周期过短而造成效果不佳
  3. 工程文件详见

GitHub - dongjieHuo/STM32G431RBT6_ADC: 本项目为第六届蓝桥杯嵌入式省赛赛题,开发板采用STM32G431RBT6,采用C语言编写

第一次在CSDN中发表文章,求一键三连~~ 之后可能为大家带来蓝桥杯嵌入式真题讲解及CoppeliaSim的机器人仿真~~

本篇文章就到这里,谢谢大家~ 如有错误请批评指正

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值