文章目录

模拟赛1的题目中需要的准备的知识点不多,其中只用到了串口、LCD、LED、按键、定时器的PWM输出、以及ADC等几个模块,题目要求也简单详细并且数量不多,非常适合入门比赛,以及整合自己比赛的模块。
与模拟赛2相比,当然是模拟赛2的试题比较难啦,虽然需要的模块差不多,但是模拟赛2的功能要求相对较多、较为复杂。
与省赛相比嘛,只能说省赛的功能要求更多、功能更加复杂,其余的需要大家自己体会。😜😜😜
通过查询产品手册知,LED的引脚为PC8~PC15,外加锁存器74HC573需要用到的引脚PD2。(由于题目要求除题目要求需要使用的LED外其他LED都处于熄灭状态,此处特意将所有的LED都初始化以便于管理其他的LED灯)
CubeMX配置:

代码样例
由于G431的所有LED都跟锁存器74HC573连接,因此每次更改LED状态时都需要先打开锁存器,写入数据后再关闭锁存器。
/*****************************************************
* 函数功能:改变所有LED的状态
* 函数参数:
* char LEDSTATE: 0-表示关闭 1-表示打开
* 函数返回值:无
******************************************************/
void changeAllLedByStateNumber(char LEDSTATE)
{
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,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
//打开锁存器 准备写入数据
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
//关闭锁存器 锁存器的作用为 使得锁存器输出端的电平一直维持在一个固定的状态
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
/*****************************************************
* 函数功能:根据LED的位置打开或者是关闭LED
* 函数参数:
* uint16_t LEDLOCATION:需要操作LED的位置
* char LEDSTATE: 0-表示关闭 1-表示打开
* 函数返回值:无
******************************************************/
void changeLedStateByLocation(uint16_t LEDLOCATION,char LEDSTATE)
{
HAL_GPIO_WritePin(GPIOC,LEDLOCATION,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
试题要求的LED显示其条件都比较单一,在满足点亮条件时直接点亮,否则,就直接熄灭即可,至于闪烁的周期控制,可以借助与sysTick中断实现,如果就因此再开一个定时器就有点浪费资源了。(虽然小编以前经常这样子干🤣🤣🤣)
小编写的LED工作逻辑函数:
/***************************************
* 函数功能:LED显示逻辑函数
* 函数参数:无
* 函数返回值:无
***************************************/
static void ledWork(void)
{
// 外部声明的计数值
extern uint16_t count;
// 数据界面
if(LCDmod == 0 && HAL_GetTick()%90==0)
{
changeLedStateByLocation(LED1,1);
changeLedStateByLocation(LED2,0);
}
// 参数界面
else if(LCDmod == 1 && HAL_GetTick()%90==0)
{
changeLedStateByLocation(LED1,0);
changeLedStateByLocation(LED2,1);
}
// r37 > vTemp
if(r37 > VTemp)
{
if(count >= 100)
{
rollbackLedByLocation(LED3);
count = 0;
}
}
else
changeLedStateByLocation(LED3,0);
}
这边不能够直接使用HAL_GetTick()函数计时,因为使用该函数计时会LED闪烁时出现频率不一致的情况。
此处,特意使用count来计数定时,该变量是放置在一个1ms触发一次的定时器中。
样例代码
由于LCD的相关代码在官方给的比赛资源数据包中存在,因此,可以直接调用资源包中的.c、.h文件来完成LCD的相关初始化以及显示。这是一个简单的LCD初始化函数,其功能是将LCD显示屏初始化为一个背景色为黑色、字体颜色为白色的屏幕,具体代码如下:
/******************************************************************************
* 函数功能:LCD初始化
* 函数参数:无
* 函数返回值:无
*******************************************************************************/
void lcdInit(void)
{
//HAL库的初始化
LCD_Init();
//设置LCD的背景色
LCD_Clear(Black);
//设置LCD字体颜色
LCD_SetTextColor(White);
//设置LCD字体的背景色
LCD_SetBackColor(Black);
}
在显示时,可以借助于sprintf()函数将需要显示的数据格式成一个字符串,再在LCD上显示这个字符串。
char temp[20];
LCD_DisplayStringLine(Line1,(u8*) " DATA ");
sprintf(temp," VR37:%4.2fV ",r37);
为了操作LED与LCD显示方便,不让其相互干扰,小编这里对LCD进行了部分源码改写,使得每次LCD显示时不改变LED的显示状态,具体的方法各位可以点击查看【蓝桥杯】一文解决蓝桥杯嵌入式开发板(STM32G431RBT6)LCD与LED显示冲突问题,并讲述LCD翻转显示。
下面附上小编完成模拟赛的LCD部分的详细代码:
/**
* @Name lcdDisplay
* @brief LCD显示数据
* @param char mod:显示模式 可以切换显示数据
* @retval None
* @author 黑心萝卜三条杠
* @Data 2023-04-02
**/
static void lcdDisplay()
{
char temp[20];
// 数据显示界面
if(LCDmod == 0)
{
LCD_DisplayStringLine(Line1,(u8*) " DATA ");
sprintf(temp," VR37:%4.2fV ",r37);
LCD_DisplayStringLine(Line3,(u8*)temp);
sprintf(temp," PA7:%dHz ",Pa7Frd);
LCD_DisplayStringLine(Line5,(u8*)temp);
}
// 参数显示界面
else if(LCDmod == 1)
{
LCD_ClearLine(Line1);
LCD_DisplayStringLine(Line3,(u8*)" PARA ");
sprintf(temp," VP1:%3.1fV ",VTemp);
LCD_DisplayStringLine(Line5,(u8*)temp);
}
}
(是不是非常简单粗暴。哈哈哈哈)
通过查询产品手册知,开发板上的四个按键引脚为PB0~PB2、PA0。
CubeMX配置

样例代码
由于主板上的按键数量较少,因此小编这里的按键读取操作相对简单粗暴,其实现步骤为:
- 步骤一:判断按键是否按下以及按键锁是否打开,在两者同时满足的情况下进入下一步;
- 步骤二:关闭按键锁并且延时10ms,实现按键的延时消抖;
- 步骤三:再次读取每个按键的值,判断按键按下的位置;
- 步骤四:读取每个按键的状态,如果都处于松开状态就打开按键锁;
具体代码实现:
/*********************************************
* 函数功能:按键扫描 含按键消抖 无长按短按设计
* 函数参数:无
* 函数返回值:按键的位置
* 返回值说明:B1-1 B2-2 B3-3 B4-4
*********************************************/
unsigned char scanKey(void)
{
//按键锁
static unsigned char keyLock = 1;
//记录按键消抖时间
// static uint16_t keyCount = 0;
//按键按下
if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET
|| HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET || HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET)
&& keyLock == 1){
//给按键上锁 避免多次触发按键
keyLock = 0;
//按键消抖 这里最好不要使用延时函数进行消抖 会影响系统的实时性
// if(++keyCount % 10 < 5) return 0;
// if(HAL_GetTick()%15 < 10) return 0;
HAL_Delay(10);
//按键B1
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET){
return 1;
}
//按键B2
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET){
return 2;
}
//按键B3
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET){
return 3;
}
//按键B4
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET){
return 4;
}
}
//按键松开
if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == SET && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == SET
&& HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == SET && HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == SET)
&& keyLock == 0){
//开锁
keyLock = 1;
}
return 0;
}
调用上述函数后,即可判断每次按键按下的位置,之后的按键逻辑函数就相对简单了,大家一起来看看吧!
/***************************************
* 函数功能:按键逻辑函数
* 函数参数:无
* 函数返回值:无
***************************************/
static void keyPro(void)
{
// 按键扫描
unsigned char keyRising = scanKey();
switch(keyRising)
{
// B1 切换显示界面
case 1:
LCDmod ^= 1;
break;
// B2 参数界面下增加电压参数
case 2:
if(LCDmod)
{
VTemp += 0.3f;
if(VTemp > 3.3f) VTemp = 0.0f;
}
break;
// B3 数据界面下调节频率
case 3:
if(!LCDmod)
{
Pa7Frd += 1000;
if(Pa7Frd > 10000) Pa7Frd = 1000;
}
break;
// 其他
default:
break;
}
}
CubeMX配置
本届试题定时器的功能为PWM输入,PWM输出时,如果大家不需要改变其占空比或者是频率,那么大家就可以不用再管理这些个定时器了。
大家一起来看看定时器的PWM输出的配置吧!

样例代码
模拟题中,需要大家能够改变PWM输出的频率或占空比,大家对这个也非常感兴趣吧,那么大家一起来看看吧!😉😉😉
/***************************************
* 函数功能:按键逻辑函数
* 函数参数:无
* 函数返回值:无
***************************************/
static void changePWMData(void)
{
// 频率发生了更新 应该更新定时器频率
if(Pa7Frd != oldPa7Frd)
{
__HAL_TIM_SetAutoreload(&htim3,1000000/Pa7Frd-1);
HAL_TIM_GenerateEvent(&htim3,TIM_EVENTSOURCE_UPDATE);
oldPa7Frd = Pa7Frd;
}
// 占空比发生了更新 应该更新定时器占空比
if(Pa7Duty != oldPa7Duty || Pa7Frd != oldPa7Frd)
{
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,(int)((Pa7Duty*1.0/100.0)*(1000000/Pa7Frd)+0.5));
oldPa7Duty = Pa7Duty;
}
}
哦!对了,大家在使用定时器前还需要使用函数开启定时器的PWM功能嗷(其实这里我更喜欢说初始化) HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
还有一个重要的东西,小编在更改PWM周期后更新了定时器,因为小编发现不更新改变定时器输出PWM的频率是没啥用的。这里的更新有两种方法:
TIM2->EGR = TIM_EGR_UG;HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_UPDATE)CubeMX配置
配置时一定一定记得改引脚!!!
样例代码
本程序中小编使用的是中断接收PC发送的数据其函数原型为:
// 函数原型:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
// 参数解析:
UART_HandleTypeDef *huart:串口通道;
uint8_t *pData:存放数据的buff;
uint16_t Size:一次接收数据的长度
在使用时还需要使用该函数“中断初始化”,否则不能够进入中断接收数据;
下面就是一个串口接收定长数据的demo:
/**********************************************串口相关************************************/
//定义一个串口信息的结构
uint8_t _ucRxbuff[1];
/***使用HAL_UART_Receive_IT中断接收数据 每次接收完成数据后就会执行该函数***/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1){
// 重新使能中断
HAL_UART_Receive_IT(huart,(uint8_t *)&_ucRxbuff,sizeof(_ucRxbuff));
}
}
本届试题要求的是定长数据,因此我们只要使用HAL_UART_Receive_IT(huart,(uint8_t *)&_ucRxbuff,sizeof(_ucRxbuff)); 触发中断即可,不需要改变串口接收数据的长度。
题中要求串口功能不仅仅是接收数据这么简单,其还需要能够解析串口接收的数据,并且以此为指令将合适的结果发送给PC。下面就是小编写的一个简单的数据处理demo:
/**
* @Name usartPro
* @brief 串口处理逻辑函数
* @param None
* @retval None
* @author 黑心萝卜三条杠
* @Data 2023-04-02
**/
static void usartPro(void)
{
#if 0
/************* 串口接收一位数据*********/
// 判断是否接收到串口数据
if(_ucRxbuff[0] == '0' || strlen((char*)_ucRxbuff) == 0) return ;
// 判断是否接收到串口数据
if('0'<_ucRxbuff[0] && _ucRxbuff[0]<='9')
Pa7Duty = (_ucRxbuff[0] - '0')*10;
// 其他情况发送错误信息
else
HAL_UART_Transmit_IT(&huart1,(u8*)"error\r\n",sizeof((char*)"error"));
// 清空有效值
_ucRxbuff[0] = '0';
#else
/************* 串口接收多位数据*********/
// 判断是否接收到串口数据
if(strlen((char*)ucRxbuff) == 0) return ;
// 判断是否接收到串口数据长度是否合理 串口数据格式是否正确
else if(strlen((char*)ucRxbuff) == 1 && '0'<ucRxbuff[0] && ucRxbuff[0]<='9')
Pa7Duty = (ucRxbuff[0] - '0')*10;
else
HAL_UART_Transmit_IT(&huart1,(u8*)"error\r\n",sizeof((char*)"error\r\n"));
memset(ucRxbuff,0,sizeof((char*)ucRxbuff));
lenBuff = 0;
#endif
}
下边是小编个人整理出来免费的蓝桥杯嵌入式福利,有需要的童鞋可以自取哟!🤤🤤🤤
省赛:
国赛:
其他:
这是小编自创的嵌入式交流群Q:726128226,欢迎各位大佬加入交流哟!😁😁😁
也欢迎大家留言或私信交流,共同进步哟!😉😉😉
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
目录第1题连续问题分析:解法:第2题分组问题分析:解法:第3题间隔连续问题分析:解法:第4题打折日期交叉问题分析:解法:第5题同时在线问题分析:解法:第1题连续问题如下数据为蚂蚁森林中用户领取的减少碳排放量iddtlowcarbon10012021-12-1212310022021-12-124510012021-12-134310012021-12-134510012021-12-132310022021-12-144510012021-12-1423010022021-12-154510012021-12-1523.......找出连续3天及以上减少碳排放量在100以上的用户分析:遇到这类
假设我在Store的模型中有这个非常简单的方法:defgeocode_addressloc=Store.geocode(address)self.lat=loc.latself.lng=loc.lngend如果我想编写一些不受地理编码服务影响的测试脚本,这些脚本可能已关闭、有限制或取决于我的互联网连接,我该如何模拟地理编码服务?如果我可以将地理编码对象传递到该方法中,那将很容易,但我不知道在这种情况下该怎么做。谢谢!特里斯坦 最佳答案 使用内置模拟和stub的rspecs,你可以做这样的事情:setupdo@subject=MyCl
在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定
我有一个gem,它有一个根据Rails.env的不同行为的方法:defself.envifdefined?(Rails)Rails.envelsif...现在我想编写一个规范来测试这个代码路径。目前我是这样做的:Kernel.const_set(:Rails,nil)Rails.should_receive(:env).and_return('production')...没关系,只是感觉很丑。另一种方法是在spec_helper中声明:moduleRails;end而且效果也很好。但也许有更好的方法?理想情况下,这应该有效:rails=double('Rails')rails.sho
我有一个rspec模拟对象,一个值赋给了属性。我正在努力在我的rspec测试中满足这种期望。只是想知道语法是什么?代码:defcreate@new_campaign=AdCampaign.new(params[:new_campaign])@new_campaign.creationDate="#{Time.now.year}/#{Time.now.mon}/#{Time.now.day}"if@new_campaign.saveflash[:status]="Success"elseflash[:status]="Failed"endend测试it"shouldabletocreat
我正在尝试测试命令行工具的输出。如何使用rspec来“伪造”命令行调用?执行以下操作不起作用:it"shouldcallthecommandlineandreturn'text'"do@p=Pig.new@p.should_receive(:run).with('my_command_line_tool_call').and_return('resulttext')end如何创建stub? 最佳答案 使用newmessageexpectationsyntax:规范/虚拟规范.rbrequire"dummy"describeDummy
目录前言: 一、ASC分析代码实现二、 卡片分析代码实现三、 直线分析代码实现四、货物摆放分析代码实现小结:前言: 在刷题的过程中,发现蓝桥杯的题目和力扣的差别很大。让人有一种不一样的感觉,蓝桥杯题目偏向对于实际问题用编程去的解决,而力扣给人感觉很锻炼自己的编程思维,逻辑能力。两者结合去刷,相信会有不一样的收获。 一、ASC 已知大写字母A的ASCII码为65,请问大写字母L的ASCII码是多少?分析 这道题目看上去很简单,我们需确定自己计算的准确,所以我建议用编程去解决。代码实现publicclassTest8{publicstaticvoidmain(String[]args){Sy
我有一个或多或少这样的场景classAdefinitialize(&block)b=B.new(&block)endend我正在对A类进行单元测试,我想知道B#new是否正在接收传递给A#new的block。我使用Mocha作为模拟框架。这可能吗? 最佳答案 我用Mocha和RSpec都试过了,虽然我可以通过测试,但行为不正确。从我的实验中,我得出结论,验证block是否已通过是不可能的。问题:为什么要传递一个block作为参数?block将用于什么目的?什么时候调用?也许这确实是您应该用类似的东西测试的行为:classBlockP
我目前正在将一种算法从Java转换为Ruby,但由于Ruby中缺少整数溢出,我遇到了一些障碍。假设我的值为2663860877,它大于最大整数2147483648。在Java中,它环绕,我应该得到-1631106419。我找到了这段代码,但它似乎不起作用:defforce_overflow(i)ifi2147483647i&0xffffffffelseiendend并且'ing变量不会像您期望的那样强制它为负。 最佳答案 假设32位整数具有二进制补码负数,这应该可行:defforce_overflow_signed(i)force_