在上一章(【蓝桥杯嵌入式】记录一次模拟赛(上)-CSDN博客)中,我们完成了B2、B3按键的第一功能:触发ADC采集。
七、B2按键第二功能:参数标准选项
又是熟悉的循环切换的模式,继续用求模的方法。首先,我们用一个数组来存储这些标准参数(上、下限),那么按键B2第二功能就是在切换数组的当前索引,这样就可以修改对应的参数。数组的类型选择用整型,原因同上一章中讲到的浮点型的精度问题,在整个做题过程中,大多数涉及显示为小数的数据存储、计算时都使用整型,显示格式化、串口信息格式化时才转化为浮点型。
unsigned int standard_goods[4] = {220, 120, 300, 140}; // 产品标准参数数组:R37上限,R37下限,R38上限,R38下限
unsigned char standard_key = 0; // 产品标准参数数组索引
按照选择的顺序,我们定义数组中元素对应R37上、下限,R38上下限
用变量standard_key作为数组索引,那么B2第二功能就是修改standard_key。
只需要在原来的基础上加上对standard_key求模的部分即可。
// 按键B2的功能实现
void function_B2()
{
if (lcd_mode == 0)
{
// 采集R37电压
unsigned int adc_dat; // ADC采集到的数据
char line_disp[21]; // 存储LCD行显示内容的数组,每行最多显示20个字符
// 启动ADC转换
HAL_ADC_Start(&hadc2);
// 等待ADC转换完成,超时时间为100毫秒
if (HAL_ADC_PollForConversion(&hadc2, 100) == HAL_OK)
{
// 读取ADC转换后的数据
adc_dat = HAL_ADC_GetValue(&hadc2);
// 计算R37电阻的电压值(0-3.3V)
adc_R37 = (adc_dat / 4095.0) * 330;
// 格式化R37电压值为字符串,保留两位小数
sprintf(line_disp, " R37:%.2fV ", (float)(adc_R37 / 100.0));
// 在LCD的第三行显示R37电压值
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp);
// 增加总的R37检测量计数
all_r37++;
// 停止ADC转换
HAL_ADC_Stop(&hadc2);
}
}
else if (lcd_mode == 1)
{
standard_key = (standard_key + 1) % 4; // 切换标准参数索引
}
}
八、B3、B4第二功能:修改标准上下限
这里的加减上下限同样也使用加一、求模的方法实现。只是需要注意区分出上下限(用不同的范围)。(索引0、2表示上限,索引1、3表示下限)
加减完的结果应当实时显示在界面上:
case 1:
// 标准设置界面
// "12345678901234567890"
LCD_DisplayStringLine(Line1, (unsigned char *)" STANDARD "); // 第一行显示:标准设置标题
sprintf(line_disp, " SR37:%.1f-%.1f ", (float)(standard_goods[1] / 100.0), (float)(standard_goods[0] / 100.0));
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp); // 第三行显示:R37标准范围
sprintf(line_disp, " SR38:%.1f-%.1f ", (float)(standard_goods[3] / 100.0), (float)(standard_goods[2] / 100.0));
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 第四行显示:R38标准范围
break;
在lcd_disp()函数中补全标准设置界面的代码,记得将数组元素转为合适的浮点型,才能使用“%.1f”。
B3、B4第二功能部分的代码:
// 按键B3的功能实现
void function_B3()
{
if (lcd_mode == 0)
{
// 采集R38电压
unsigned int adc_dat;
char line_disp[21];
HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
{
adc_dat = HAL_ADC_GetValue(&hadc1);
adc_R38 = (adc_dat / 4095.0) * 330;
sprintf(line_disp, " R38:%.2fV ", (float)(adc_R38 / 100.0));
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 显示R38电压值
HAL_ADC_Stop(&hadc1);
}
}
else if (lcd_mode == 1)
{
// 标准设置界面下按键B3的功能实现
if ((standard_key == 0) || (standard_key == 2))
{
// 调整SR37的标准值
if (standard_goods[standard_key] == 300)
{
standard_goods[standard_key] = 220;
}
else
{
standard_goods[standard_key] += 20;
}
}
else if ((standard_key == 1) || (standard_key == 3))
{
// 调整SR38的标准值
if (standard_goods[standard_key] == 200)
{
standard_goods[standard_key] = 120;
}
else
{
standard_goods[standard_key] += 20;
}
}
// 更新LCD显示
char line_disp[21];
sprintf(line_disp, " SR37:%.1f-%.1f ", (float)(standard_goods[1] / 100.0), (float)(standard_goods[0] / 100.0));
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp); // 显示调整后的SR37标准范围
sprintf(line_disp, " SR38:%.1f-%.1f ", (float)(standard_goods[3] / 100.0), (float)(standard_goods[2] / 100.0));
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 显示调整后的SR38标准范围
}
}
// 按键B4的功能实现
void function_B4()
{
if (lcd_mode == 1)
{
// 标准设置界面下按键B4的功能实现
if ((standard_key == 0) || (standard_key == 2))
{
// 调整SR37的标准值
if (standard_goods[standard_key] == 220)
{
standard_goods[standard_key] = 300;
}
else
{
standard_goods[standard_key] -= 20;
}
}
else if ((standard_key == 1) || (standard_key == 3))
{
// 调整SR38的标准值
if (standard_goods[standard_key] == 120)
{
standard_goods[standard_key] = 200;
}
else
{
standard_goods[standard_key] -= 20;
}
}
// 更新LCD显示
char line_disp[21];
sprintf(line_disp, " SR37:%.1f-%.1f ", (float)(standard_goods[1] / 100.0), (float)(standard_goods[0] / 100.0));
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp); // 显示调整后的SR37标准范围
sprintf(line_disp, " SR38:%.1f-%.1f ", (float)(standard_goods[3] / 100.0), (float)(standard_goods[2] / 100.0));
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 显示调整后的SR38标准范围
}
}
关于标准参数数组索引standard_key还有一点需要注意,每次重新进入标准设置界面时,应都是从R37上限开始,也即索引要在切换界面时重置,我把这个操作加在了B1的按键功能(毕竟它就是界面切换键)。
// 按键B1的功能实现:界面翻转
void function_B1()
{
lcd_mode = (lcd_mode + 1) % 3; // 切换LCD界面模式
lcd_disp(); // 更新LCD显示
standard_key = 0; // 重置标准参数索引
}
九、B4第一功能:清空合格率
合格率的公式:
对应的共有相关的六个参数:
float pass_pr37 = 0.0; // R37产品合格率,50.0表示50.0%
float pass_pr38 = 0.0; // R38产品合格率,以百分比表示
unsigned int pass_r37 = 0; // R37产品合格数量
unsigned int all_r37 = 0; // R37产品总检测量
unsigned int pass_r38 = 0; // R38产品合格数量
unsigned int all_r38 = 0; // R38产品总检测量
B4第一功能就是在合格率界面下按下时清零这六个参数,同时对显示进行更新:
if (lcd_mode == 2)
{
// 合格率界面下按键B4的功能实现:清零合格率
pass_pr37 = 0;
pass_r37 = 0;
all_r37 = 0;
pass_pr38 = 0;
pass_r38 = 0;
all_r38 = 0;
lcd_disp(); // 更新LCD显示
}
lcd_disp()中合格率界面部分:
case 2:
// 合格率界面
// "12345678901234567890"
LCD_DisplayStringLine(Line1, (unsigned char *)" PASS "); // 第一行显示:合格率标题
sprintf(line_disp, " PR37:%4.1f%% ", pass_pr37);
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp); // 第三行显示:R37合格率
sprintf(line_disp, " PR38:%4.1f%% ", pass_pr38);
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 第四行显示:R38合格率
十、LED灯部分:LD3~LD5
我们把串口部分放最后先看LED部分:
界面显示灯:LD3~LD5
每次切换界面会用到lcd_disp()(LCD界面显示函数),所以我们把这一部分放在这里:
假设我们从产品界面切到标准界面,LED灯情况:
产品界面:○○○○○●○○(○表灭,●表亮,从左到右是LD8,LD7,···,LD1)
标准界面:○○○○●○○○(○表灭,●表亮,从左到右是LD8,LD7,···,LD1)
那么就需要把LED的显示参数led_addr的第3位置0(位与操作),第4位置1(位或操作):
求换过程:(原led_addr = 0x04)
led_addr &= 0xe3;//清除LD3-LD5 led_addr |= 0x10;
切换后,led_addr = 0x08
完整的lcd_disp():
void lcd_disp()
{
char line_disp[21]; // 存储LCD行显示内容的数组,每行最多显示20个字符
switch (lcd_mode)
{
case 0:
// 产品参数界面
// "12345678901234567890"
LCD_DisplayStringLine(Line1, (unsigned char *)" GOODS "); // 第一行显示:商品信息标题
sprintf(line_disp, " R37:%.2fV ", (float)(adc_R37 / 100.0));
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp); // 第三行显示:R37电压值
sprintf(line_disp, " R38:%.2fV ", (float)(adc_R38 / 100.0));
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 第四行显示:R38电压值
//在产品参数界面亮LD3
led_addr &= 0xe3;//清除LD3-LD5
led_addr |= 0x04;
led_disp(led_addr); // 更新LED显示
break;
case 1:
// 标准设置界面
// "12345678901234567890"
LCD_DisplayStringLine(Line1, (unsigned char *)" STANDARD "); // 第一行显示:标准设置标题
sprintf(line_disp, " SR37:%.1f-%.1f ", (float)(standard_goods[1] / 100.0), (float)(standard_goods[0] / 100.0));
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp); // 第三行显示:R37标准范围
sprintf(line_disp, " SR38:%.1f-%.1f ", (float)(standard_goods[3] / 100.0), (float)(standard_goods[2] / 100.0));
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 第四行显示:R38标准范围
//在标准设置界面亮LD4
led_addr &= 0xe3;//清除LD3-LD5
led_addr |= 0x08;
led_disp(led_addr); // 更新LED显示
break;
case 2:
// 合格率界面
// "12345678901234567890"
LCD_DisplayStringLine(Line1, (unsigned char *)" PASS "); // 第一行显示:合格率标题
sprintf(line_disp, " PR37:%4.1f%% ", pass_pr37);
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp); // 第三行显示:R37合格率
sprintf(line_disp, " PR38:%4.1f%% ", pass_pr38);
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 第四行显示:R38合格率
//在合格率界面亮LD5
led_addr &= 0xe3;//清除LD3-LD5
led_addr |= 0x10;
led_disp(led_addr); // 更新LED显示
break;
default:
break;
}
}
十一、标准判断功能
在B2、B3按键的第一功能中,不仅是检测,还需要判断是否合格,计算合格率。
这一部分实现如下:
采集完数据,检测总数加一
判断是否在范围内(if语句和逻辑运算符号&&),是的话合格数加一,否则不加
最后结合更新后的数据更新合格率
// 按键B2的功能实现
void function_B2()
{
if (lcd_mode == 0)
{
// 采集R37电压
unsigned int adc_dat; // ADC采集到的数据
char line_disp[21]; // 存储LCD行显示内容的数组,每行最多显示20个字符
// 启动ADC转换
HAL_ADC_Start(&hadc2);
// 等待ADC转换完成,超时时间为100毫秒
if (HAL_ADC_PollForConversion(&hadc2, 100) == HAL_OK)
{
// 读取ADC转换后的数据
adc_dat = HAL_ADC_GetValue(&hadc2);
// 计算R37电阻的电压值(0-3.3V)
adc_R37 = (adc_dat / 4095.0) * 330;
// 格式化R37电压值为字符串,保留两位小数
sprintf(line_disp, " R37:%.2fV ", (float)(adc_R37 / 100.0));
// 在LCD的第三行显示R37电压值
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp);
// 增加总的R37检测量计数
all_r37++;
// 停止ADC转换
HAL_ADC_Stop(&hadc2);
// 检查R37电压值是否在标准范围内
if ((adc_R37 >= standard_goods[1]) && (adc_R37 <= standard_goods[0]))
{
// R37电压值在标准范围内,设置LED亮
pass_led_flag7 = 7;
pass_r37++;
led_addr |= 0x01; // 设置LED地址
led_disp(led_addr); // 更新LED显示
}
}
// 计算R37合格率(通过的R37电阻数量除以总检测量,然后乘以100)
pass_pr37 = pass_r37 / (float)(all_r37) * 100;
}
else if (lcd_mode == 1)
{
standard_key = (standard_key + 1) % 4; // 切换标准参数索引
}
}
十二、标准改变时,对应合格率清零
在题目中,有产品标准改变时对应的合格率清零的要求:
这一点的话,我们只需要在B3、B4按键进行标准修改时,判断下修改的标准是R37还是R38的,然后清零相应合格率、总数、合格数。
B3、B4的最终代码:
// 按键B3的功能实现
void function_B3()
{
if (lcd_mode == 0)
{
// 采集R38电压
unsigned int adc_dat;
char line_disp[21];
HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
{
adc_dat = HAL_ADC_GetValue(&hadc1);
adc_R38 = (adc_dat / 4095.0) * 330;
sprintf(line_disp, " R38:%.2fV ", (float)(adc_R38 / 100.0));
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 显示R38电压值
all_r38++;
HAL_ADC_Stop(&hadc1);
if ((adc_R38 >= standard_goods[3]) && (adc_R38 <= standard_goods[2]))
{
// R38电压值在标准范围内,设置LED亮
pass_led_flag8 = 8;
pass_r38++;
led_addr |= 0x02;
led_disp(led_addr);
}
}
pass_pr38 = pass_r38 / (float)(all_r38) * 100; // 计算R38合格率
}
else if (lcd_mode == 1)
{
// 标准设置界面下按键B3的功能实现
if ((standard_key == 0) || (standard_key == 2))
{
// 调整SR37的标准值
if (standard_goods[standard_key] == 300)
{
standard_goods[standard_key] = 220;
}
else
{
standard_goods[standard_key] += 20;
}
}
else if ((standard_key == 1) || (standard_key == 3))
{
// 调整SR38的标准值
if (standard_goods[standard_key] == 200)
{
standard_goods[standard_key] = 120;
}
else
{
standard_goods[standard_key] += 20;
}
}
// 根据调整后的标准值重新计算合格率
if (standard_key <= 1)
{
pass_pr37 = 0;
pass_r37 = 0;
all_r37 = 0;
}
else
{
pass_pr38 = 0;
pass_r38 = 0;
all_r38 = 0;
}
// 更新LCD显示
char line_disp[21];
sprintf(line_disp, " SR37:%.1f-%.1f ", (float)(standard_goods[1] / 100.0), (float)(standard_goods[0] / 100.0));
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp); // 显示调整后的SR37标准范围
sprintf(line_disp, " SR38:%.1f-%.1f ", (float)(standard_goods[3] / 100.0), (float)(standard_goods[2] / 100.0));
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 显示调整后的SR38标准范围
}
}
// 按键B4的功能实现
void function_B4()
{
if (lcd_mode == 2)
{
// 合格率界面下按键B4的功能实现:清零合格率
pass_pr37 = 0;
pass_r37 = 0;
all_r37 = 0;
pass_pr38 = 0;
pass_r38 = 0;
all_r38 = 0;
lcd_disp(); // 更新LCD显示
}
else if (lcd_mode == 1)
{
// 标准设置界面下按键B4的功能实现
if ((standard_key == 0) || (standard_key == 2))
{
// 调整SR37的标准值
if (standard_goods[standard_key] == 220)
{
standard_goods[standard_key] = 300;
}
else
{
standard_goods[standard_key] -= 20;
}
}
else if ((standard_key == 1) || (standard_key == 3))
{
// 调整SR38的标准值
if (standard_goods[standard_key] == 120)
{
standard_goods[standard_key] = 200;
}
else
{
standard_goods[standard_key] -= 20;
}
}
// 根据调整后的标准值重新计算合格率
if (standard_key <= 1)
{
pass_pr37 = 0;
pass_r37 = 0;
all_r37 = 0;
}
else
{
pass_pr38 = 0;
pass_r38 = 0;
all_r38 = 0;
}
// 更新LCD显示
char line_disp[21];
sprintf(line_disp, " SR37:%.1f-%.1f ", (float)(standard_goods[1] / 100.0), (float)(standard_goods[0] / 100.0));
LCD_DisplayStringLine(Line3, (unsigned char *)line_disp); // 显示调整后的SR37标准范围
sprintf(line_disp, " SR38:%.1f-%.1f ", (float)(standard_goods[3] / 100.0), (float)(standard_goods[2] / 100.0));
LCD_DisplayStringLine(Line4, (unsigned char *)line_disp); // 显示调整后的SR38标准范围
}
}
到了这里整个题目完成了80%,在下一章节中,我们会完成点灯1s后熄灭、串口收发的任务,敬请期待。