
🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏
🪔本系列专栏 - 蓝桥杯嵌入式_勾栏听曲_0的博客
🍻欢迎大家 🏹 点赞👍 评论📨 收藏⭐️
📌个人主页 - 勾栏听曲_0的博客📝
🔑希望本文能对你有所帮助,如有不足请指正,共同进步吧🏆
🎇涉浅水者见虾,其颇深者察鱼鳖,其尤深者观蛟龙。📈
目录

功能概述
1)通过E2PROM完成商品库存数量以及商品单价的存储。
2)通过串口打印输出购买商品购买数量、总金额等信息。
3)依试题要求,通过按键,实现界面切换与控制功能。
4)依试题要求,通过LCD、LED完成数据显示和指示功能。
性能要求
1)按键响应时间:≤0.1秒。
2)指示灯动作响应时间:≤0.2秒。(条件触发后0.2秒内)
LCD显示界面
1)商品购买界面
在商品购买界面下,通过LCD显示界面名称(SHOP)、商品名称以及商品数量。

2)商品价格界面
在商品价格界面下,通过LCD显示界面名称(PRICE)、商品名称以及商品价格。

商品价格范围:1.0 - 2.0。保留小数点后1位有效数字。
3)库存信息界面
在库存信息界面下,通过LCD显示界面名称(REP)、商品名称以及当前库存数量。

4)LCD通用显示要求
显示背景色(BackColor):黑色
显示前景色(TextColor):白色
请严格按照图示2、3、4要求设计各个信息项的名称(区分字母大小写)和行列位置。
按键功能
1)B1:定义为界面切换按键,按下B1按键可以往复切换商品购买、商品价格、库存显示三个界面,切换模式如下图所示:

2)B2:定义为“商品X”。
在商品购买界面下,按下B2,商品X购买数量加1。购买数量调整模式:
0 1 2 3 … 商品X库存数量 0 1 2 …
在商品价格界面下,按下B2按键,商品X单价加0.1。商品单价调整模式:
1.0 1.1 … 2.0 1.0 1.1 …
在库存信息界面下,按下B2按键,商品X库存数量加1。
3)B3:定义为“商品Y”。
在商品购买界面下,按下B3,商品Y购买数量加1。购买数量调整模式:
0 1 2 3 … 商品X库存数量 0 1 2 …
在商品价格界面下,按下B3按键,商品Y单价加0.1。商品单价调整模式:
1.0 1.1 … 2.0 1.0 1.1 …
在库存信息界面下,按下B3按键,商品Y库存数量加 1。
4)B4:定义为“确认”按键。
在商品购买界面下,按下B4按键,确认购买信息,商品购买界面下的X、Y值重置为0,库存减少相应数量。
注意:
按键应进行有效的防抖处理,避免出现一次按下功能多次触发等情形。
按键动作不应影响数据采集过程和屏幕显示效果。
价格调整区间:1.0 - 2.0。
购买数量调整区间:0-商品当前库存数量。
E2PROM存储功能
通过竞赛平台上的 E2PROM (AT24CO2)保存商品库存数量和价格信息,存储位置要求如下:
商品X库存数量存储地址:E2PROM内部地址0
商品Y库存数量存储地址:E2PROM内部地址1
商品X单价存储地址:E2PROM内部地址2
商品Y单价存储地址:E2PROM内部地址3
**注意:
库存数量或价格发生变动时,数据写入到E2PROM中,无变化时不写入。
设备重新上电,能够从E2PROM相应地址中载入商品库存数量和价格。
严格按照试题要求的E2PROM地址写入并保存数据。
串口输出功能
使用竞赛板上的USB转串口功能完成以下要求,串口通信波特率设置为9600。
1)打印输出总价及购买信息
在商品购买界面下,B4按键按下后,设备串口输出购买商品数量和总价格。数据格式要求:
X:2,Y:2,Z:4.0
示例字符串表示购买了2个商品X,2个商品Y,总价为4.0元。总价保留小数点后1位有效数字,输出信息为ASCII 编码字符串。
2)查询当前单价信息
在任意界面下,通过串口调试助手,从 PC端向设备发送查询字符‘?’,设备返回当前各类商品单价。
X:1.0,Y:1.0
示例字符串表示商品X单价为1.0,商品Y为1.0。
商品价格保留小数点后1位有效数字,输出信息为ASCII编码字符串。
LED指示灯功能
1)LD1:在购买界面下,按下B4按键确认购买后,LD1点亮5秒后熄灭。
2)LD2:若商品X、Y库存数量均为0,指示灯LD2以0.1秒为间隔切换亮灭状
态。
3)LD3-LD8指示灯始终处于熄灭状态。
PWM输出功能
在商品购买界面下,通过B4按键确认购买信息后,5秒内通过PAl引脚输出频率为2KHz,占空比为30%的脉冲信号,其余时间频率不变,占空比为5%。
初始状态说明
请严格按照下列要求设计作品上电后的初始状态:
1)商品X:库存数量10,单价1.0。
2)商品Y:库存数量10,单价1.0。
3)上电后,处于商品购买界面,商品X、Y购买数量为0。
真题讲解系列文章重点关注顶层逻辑代码编写,各模块的代码编写大家可点击蓝桥杯嵌入式专题,里面有各个模块的详细解析与代码编写
首先观察硬件框图,同样的,除了老三样(LED,按键,LCD)以外,就是本届赛题的重点考试内容了。显然本次考察重点是串口通信与EEPROM。这里插一个题外话,将近几年的省赛真题都做了一遍的小伙伴一个都能发现,最常见的组合就是串口通信+其他,本届又考了EEPROM,大胆预测,2023年可能就是考串口通信加PWM或模数转换了。
回归正题,已经知道本届赛题考核的内容后,我们接着看功能要求中的功能概述。串口通信主要用到的是串口的发送,根据按键信号还发送商品购买数量,总金额,当然,往下仔细看,会发现串口还有一个功能要实现,就是接收串口数据,识别为"?"后,再通过串口发送商品单价。而EEPROM就是存储商品库存与商品单价了。其他比如购买,加库存等操作都是通过按键来实现的。
按键\EEPROM模块
按键的操作比较多,除了必有的界面切换,就是在特定界面下,商品的购买数量与单价的加与减。题目中的每个模块中的主要事项是一定要去仔细看的。还包括了按下购买按键后,使用串口发送数据。
赛题还要求,将商品的库存与商品单价存储到EEPROM中,并且在每次有数值改变时,就要将改变后的数值存入EEPROM中。而数值的改变都是通过按键实现的,因此我们直接在按键判断中,改变数值的后面加上EEPROM的写入,在每次程序运行之初读取即可完成这项功能。
void key_proc()
{
uint q = 0 ,w = 0; //记录B4按键按下时刻count的值,与0.1秒闪烁的计时
if(key[0].key_flag == 1)
{
view++;
if(view==3) view=0;
LCD_Clear(Black);
key[0].key_flag = 0;
}
if(key[1].key_flag == 1)
{
if(0 == view)
{
if(X_shop < X_rep) //购买数小于库存数
{
X_shop += 1;
}
}
if(1 == view)
{
X_price = X_price + 0.1;
if(X_price > 2.1) //价格在1.0到2.0之间
{
X_price = 1.0;
}
uchar x_p = X_price*10;
eeprom_write(2,x_p);
}
if(2 == view)
{
X_rep ++;
eeprom_write(0,X_rep);
}
key[1].key_flag = 0;
}
if(key[2].key_flag == 1)
{
if(0 == view)
{
if(Y_shop < Y_rep) //购买数小于库存数
{
Y_shop += 1;
}
}
if(1 == view)
{
Y_price = Y_price + 0.1;
if(Y_price > 2.1) //价格在1.0到2.0之间 //为什么是2.1呢? 因为double类型的变量,1.9+0.1,有可能等于2.000000001之类的值
{
Y_price = 1.0;
}
uchar y_p = (int)(Y_price*10);
eeprom_write(3,y_p);
/*xp.val = Y_price; //使用共用体将浮点数存储在EEPROM中
for(int i = 0; i < sizeof(float); i++)
{
eeprom_write(0x10 + i, xp.data[i]);
HAL_Delay(5);
}*/
}
if(2 == view)
{
Y_rep ++;
eeprom_write(1,Y_rep);
}
key[2].key_flag = 0;
}
if(key[3].key_flag == 1)
{
if(0 == view)
{
q = count; //记录按下的那一刻的数值
TurnOn_LED(1);
double z = X_shop*X_price + Y_shop*Y_price;
/*串口发送*/
char temp[20];
sprintf(temp,"X:%d , Y:%d , Z:%.2f\r\n",X_shop,Y_shop,z);
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50); //参数:串口号,要发送的字符,要发送的字符长度
X_rep -= X_shop;
Y_rep -= Y_shop;
eeprom_write(0,X_rep);
HAL_Delay(5);
eeprom_write(1,Y_rep);
X_shop=0,Y_shop=0;
LED_Disp(0x01);
__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,30); //设置占空比pa6_duty,然后通过定时器五秒后“30”恢复为“5”
falg_d = 1;
}
key[3].key_flag = 0;
}
if(q > count-4 && q < count) //一个循环后,也就是5秒之后
{
TurnOff_LED(1);
__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,5);
}
if(X_rep == 0 && Y_rep == 0)
{
w = count;
if(count-w == 10)
{
Toogle_LED(2); //翻转LED2的状态
}
}
else
{
TurnOff_LED(2);
}
}
串口模块
串口模块主要是接收串口发来的信息,并判断是否为“?”,是则发送商品单价。串口的另外一个功能在按键模块中实现。
void uart_rx_proc()
{
if(rx_pointer>0)
{
if(rx_pointer==1)
{
char temp[20];
sprintf(temp,"X:%.2f,Y:%.2f\n",X_price,Y_price);
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
}
else
{
char temp[20];
sprintf(temp,"Error");
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
}
rx_pointer=0;memset(rxdata,0,30);
}
}
LCD模块
显示各种参数变量,要注意每一行每一列的显示都要与赛题中的位置一样。
void disp_proc()
{
if(view==0)
{
char text[30];
sprintf(text," SHOP ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text," X:%d ",X_shop);
LCD_DisplayStringLine(Line3, (uint8_t *)text);
sprintf(text," Y:%d ",Y_shop);
LCD_DisplayStringLine(Line4, (uint8_t *)text);
}
else if(view==1)
{
char text[30];
sprintf(text," PRICE ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text," X:%.2f ",X_price);
LCD_DisplayStringLine(Line3, (uint8_t *)text);
sprintf(text," Y:%.2f ",Y_price);
LCD_DisplayStringLine(Line4, (uint8_t *)text);
}
else if(view==2)
{
char text[30];
sprintf(text," REP ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text," X:%d ",X_rep);
LCD_DisplayStringLine(Line3, (uint8_t *)text);
sprintf(text," Y:%d ",Y_rep);
LCD_DisplayStringLine(Line4, (uint8_t *)text);
}
}
中断模块
中断模块中包含按键的扫描,PWM占空比改变,库存为零后的LED灯闪烁与确认购买后LED灯5秒长亮。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定时器回调函数
{
if(htim->Instance==TIM3) //判断是定时器3 //分频系数:80,重装载值:1000 //也就是80Mhz的晶振,经过这些数值,这个定时器1秒种会响应100(80M/80/1000)次
{
//HAL_UART_RxCpltCallback(&huart1);
key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(int i = 0;i < 4; i++ )
{
switch (key[i].judge_sta)
{
case 0:
{
if(key[i].key_sta==0)
{
key[i].judge_sta = 1; //第一次判断是否按下
key[i].key_time = 0;
}
}
break;
case 1:
{
if(key[i].key_sta==0) //进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖
{
key[i].judge_sta = 2;
}
else
key[i].judge_sta = 0;
}
break;
case 2:
{
if(key[i].key_sta==1) //判断是否松手
{
if(key[i].key_time < 100)
{
key[i].key_flag = 1;
}
key[i].judge_sta = 0;
}
else
{
key[i].key_time++;
if(key[i].key_time > 100) //一次扫描10毫秒,100次1000毫秒,就是判断是否长按超过1000毫秒//未松手时,就会执行相应反应
{
key[i].long_flag = 1;
}
}
}
break;
}
}
if(falg_d > 0)
{
falg_d ++;
LED_Disp(0x01);
if(falg_d > 500) //5秒后
{
falg_d = 0;
__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,5); //设置占空比pa6_duty
//LED_Dispm(0x01);
LED_mie();
}
}
if(0 == X_rep && 0 == Y_rep)
{
s ++; //0.1秒的循环位
if(s == 10)
LED_Disp(0x02);
if(s == 20)
{
s = 0;
//LED_Dispm(0x02);
LED_mie();
}
}
count ++;
if(count == 500) //每5秒后重新计时
{
count = 0;
}
}
}
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源
嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来
文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g
打印1:defsum(i)i=i+[2]end$x=[1]sum($x)print$x打印12:defsum(i)i.push(2)end$x=[1]sum($x)print$x后者是修改全局变量$x。为什么它在第二个例子中被修改而不是在第一个例子中?类Array的任何方法(不仅是push)都会发生这种情况吗? 最佳答案 变量范围在这里无关紧要。在第一段代码中,您仅使用赋值运算符=为变量i赋值,而在第二段代码中,您正在修改$x(也称为i)使用破坏性方法push。赋值从不修改任何对象。它只是提供一个名称来引用一个对象。方法要么是破坏性