文章目录
前言
为了减少篇幅,各模块的初始化均在模块配置中,可以随时去翻看博客,还有function里面的函数,将不再罗列出来,只是截图展示。
一、题目
设计一个电压测量监控设备,定时通过串口向 PC 机发送电压值,通过串口接收系统配置参数并保存到 E2PROM 中。设备硬件部分主要由电源部分、控制器单元、串口部分、存储单元组成,系统框图如图 1 所示 :
设计任务及要求
1. RTC 实时时钟
使用 STM32 内部 RTC 完成相关功能,设备上电后,时间初始化为 23 时 59 分 55 秒,默认定时上报电压时间为 0 时 0 点 0 分
2. ADC 测量功能
设备采集电位器 R37 输出的电压信号 V1,并通过 LCD 显示。当 V1>VDD*k 时,指示灯LD1 以 0.2 秒为间隔闪烁,闪烁功能可以通过按键关闭;VDD为 3.3V;k 默认值为 0.1,保存在 E2PROM 中并可以通过串口修改配置。
3. 串行功能
3.1 设定 k 值,可设置范围 0.1 ~ 0.9
格式:【命令类型】【数值】【命令结束标志】
举例:
“k0.1\n”
设置 k 值为 0.1;
设备接收到命令执行后,回复“ok\n”。
3.2 定时上报电压 V1
格式:【V1 电压值】+【k 值】+【时间】【命令结束标志】
举例:
“2.21+0.1+123030\n”
12 时 30 分 30 秒上报电压值为 2.21V,k 值为 0.1
说明:串口设定 9600 波特,数据位 8,停止位 1,无校验位;没有发送或发送错误的控制命令时,设备不做回应。
4. LCD 显示
设备上电默认通过 LCD 显示电位器输出电压 V1(保留小数点后两位有效数字)、k 值、指示灯闪烁报警功能状态和系统时间,显示界面如图 1 所示:
5. 按键功能
“B1”按键设定为“功能”按键,打开/关闭指示灯闪烁报警功能,默认为打开状态;
“B2”按键设定为“设置”按键,设置设备自动上报电压时间,按下 B2 后,LCD 显示界面如图 2 所示,此时通过按键 B3 切换选择时、分、秒,通过按键 B4 进行调整,完成调整后,按下 B2 按键,更新自动上报时间,并返回图 1 所示的 LCD 显示界面。
二、模块初始化以及功能分析
1.模块的初始化
需要用的模块:LCD、IIC、四个按键、LED、ADC(PB15)、USART1
2.模块功能分析
LCD:显示-----》Display();
IIC:读取和存储信息到EEPROM-----》EEPROM_Write();EEPROM_Read();
按键:对参数进行调整-----》KEY_Handle();KEY_Scan();
LED:提示作用-----》LED();
ADC:读取电压值-----》Get_ADC();
USART1:与PC机交互-----》HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
三、函数实现
1.void Display(void);
当mode = 0时显示页面1;
当mode = 1时显示页面2:
Alarm_flag是打开/关闭指示灯闪烁报警功能
void Display(void)
{
if(mode == 0)
{
sprintf((char *)str," V1: %.2fV",Adc);
LCD_DisplayStringLine(Line1,str);
sprintf((char *)str," k: %.1f",k);
LCD_DisplayStringLine(Line3,str);
if(Alarm_flag == 1)
sprintf((char *)str," LED: ON");
else
sprintf((char *)str," LED: OFF");
LCD_DisplayStringLine(Line5,str);
sprintf((char *)str," T:%.2d-%.2d-%.2d",Time[0],Time[1],Time[2]);
LCD_DisplayStringLine(Line7,str);
sprintf((char *)str," 1");
LCD_DisplayStringLine(Line9,str);
}
else if(mode == 1)
{
sprintf((char *)str," Setting");
LCD_DisplayStringLine(Line1,str);
sprintf((char *)str," %.2d-%.2d-%.2d",Time_Set[0],Time_Set[1],Time_Set[2]);
LCD_DisplayStringLine(Line5,str);
sprintf((char *)str," 2");
LCD_DisplayStringLine(Line9,str);
}
}
2.void EEPROM_Read(void);void EEPROM_Write(void);
Time为初始时间的时分秒,Time_Set为上报电压时间
k为系数(float)
IIC的读写函数在模块配置iic那一小节
void EEPROM_Write(void)
{
IIC_Write(0x00,Time[0]);HAL_Delay(10);
IIC_Write(0x01,Time[1]);HAL_Delay(10);
IIC_Write(0x02,Time[2]);HAL_Delay(10);
IIC_Write(0x03,Time_Set[0]);HAL_Delay(10);
IIC_Write(0x04,Time_Set[1]);HAL_Delay(10);
IIC_Write(0x05,Time_Set[2]);HAL_Delay(10);
IIC_Write(0x06,k*10);HAL_Delay(10);
}
void EEPROM_Read(void)
{
Time[0] = IIC_Read(0x00);HAL_Delay(10);
Time[1] = IIC_Read(0x01);HAL_Delay(10);
Time[2] = IIC_Read(0x02);HAL_Delay(10);
Time_Set[0] = IIC_Read(0x03);HAL_Delay(10);
Time_Set[1] = IIC_Read(0x04);HAL_Delay(10);
Time_Set[2] = IIC_Read(0x05);HAL_Delay(10);
k = IIC_Read(0x06)/10.0;HAL_Delay(10);
}
3.uint8_t KEY_Scan(uint8_t mode);
uint8_t KEY_Scan(uint8_t mode)
{
static uint8_t flag=1;
if(mode) flag = 1;
if(flag &&(KEY_B1 == 0 || KEY_B2 == 0 || KEY_B3 == 0 || KEY_B4== 0 ))
{
HAL_Delay(10);
flag = 0;
if (KEY_B1 == 0) return B1_Press;
else if (KEY_B2 == 0) return B2_Press;
else if (KEY_B3 == 0) return B3_Press;
else if (KEY_B4 == 0) return B4_Press;
}else if(KEY_B1 == KEY_B2 == KEY_B3 == KEY_B4 == 1) flag = 1;
return 0;
}
4.void KEY_Handle(uint8_t key);
在B1按下时打开/关闭指示灯闪烁报警功能(Alarm_flag变化)
在B2按下时显示页面切换(mode变化),切换回去时将参数写入EEPROM
在B3按下时切换参数选择的地址(location变化)
在B4按下时对应加1
void KEY_Handle(uint8_t key)
{
if(key == B1_Press)
{
LCD_ClearLine(Line5);
Alarm_flag = !Alarm_flag;
}
else if(key == B2_Press)
{
LCD_Clear(White);
if(mode == 0) mode = 1;
else if(mode == 1){
mode = 0;
EEPROM_Write();
}
}
else if(key == B3_Press)
{
if(mode == 1)
{
location++;
location = location % 4;
}
}
else if(key == B4_Press)
{
Time_Set[location - 1] = Time_Set[location - 1] + 1;
Time_Set[0] = Time_Set[0] % 24;
Time_Set[1] = Time_Set[1] % 60;
Time_Set[2] = Time_Set[2] % 60;
}
}
5.uint16_t Get_ADC(void);
读取ADC,Get_ADC()*3.3/4096才是电压值
uint16_t Get_ADC(void)
{
uint16_t temp = 0 ;
HAL_ADC_Start(&hadc2);
temp = HAL_ADC_GetValue(&hadc2);
HAL_ADC_Stop(&hadc2);
return temp;
}
6.void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
接收到4个字符进入中断。判断后改变k的值。
memset是string中的字符串清空函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
char arr[20];
if(Buffer[3] >= '0' && Buffer[3] <= '9')
{
k = (float)(Buffer[3]-'0')/10;
sprintf(arr,"OK\r\n");
HAL_UART_Transmit(&huart1,(unsigned char *)arr,strlen(arr),50);
}
memset(Buffer,0,sizeof(Buffer));
}
7.int main(void);
初始化;
LCD_Init();
I2CInit();
LCD_Clear(White);
LCD_SetTextColor(Black);
IIC_Write(0x00,23);HAL_Delay(10);
IIC_Write(0x01,59);HAL_Delay(10);
IIC_Write(0x02,55);HAL_Delay(10);
IIC_Write(0x03,00);HAL_Delay(10);
IIC_Write(0x04,00);HAL_Delay(10);
IIC_Write(0x05,00);HAL_Delay(10);
IIC_Write(0x06,01);HAL_Delay(10);
EEPROM_Read();
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);
HAL_Delay(100);
while(1);
Display();
HAL_UART_Receive_IT(&huart1,Buffer,4);
key = KEY_Scan(0);
KEY_Handle(key);
if(Time[0] == Time_Set[0] && Time[1] == Time_Set[1] && Time[2] == Time_Set[2])
{
sprintf(arr,"%.2f+%.1f+%.2d%.2d%.2d\r\n",Adc,k,Time_Set[0],Time_Set[1],Time_Set[2]);
HAL_UART_Transmit(&huart1,(unsigned char *)arr,strlen(arr),50);
HAL_Delay(1000);
}
if(Count == 1)
{
Adc = Get_ADC()*3.3/4096;
HAL_RTC_GetTime(&hrtc, &T, RTC_FORMAT_BIN);
Time[0] = T.Hours;
Time[1] = T.Minutes;
Time[2] = T.Seconds;
HAL_RTC_GetDate(&hrtc, &D, RTC_FORMAT_BIN);
}
if((Adc>(float)3.3*k) && (num == Alarm_flag == 1))
{
if(i++%2)
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15
|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);
}
else
{
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);
}
}
8.void SysTick_Handler(void);
上面的num和count在哪变了,在哪计时呢?
就在滴答定时器里面咯。
大家打开stm32g4xx_it.c
if(j++ >= 200)
{
num = 1;
j = 0;
}
if(i++ >= 1000)
{
Count = 1;
i = 0;
}
j是0.2s的计时,i是1s的计时
总结
以上就是本次模拟题的程序设计部分(变量定义部分,本人没有罗列出来嗷),在代码简洁方面本人做的不是很好,如有更好的设计方法可以在评论区留言。互相学习。👍
还等什么,赶快自己写一下,测试一下代码。嘿嘿