温馨提示:
真题演练分为模拟篇和研究篇。本专栏的主要作用是记录我的备赛过程,我打算先自己做一遍,把遇到的问题和不同之处记录到演练篇,然后再返回来仔细研究一下,找到最佳的解题方法记录到研究篇。题目在:https://pan.quark.cn/s/fc121e80dbff
本文可能不会重复之前提到的内容,如需查看,请参考附录
【蓝桥杯嵌入式】附录
目录
演练时发现的问题
未解决的问题
题目分析与计划
设计规范
浏览题目
详细阅读与分析
整体流程设计
一、外设配置与代码框分析
二、软件配置
按照分析配置外设:
TIM
三、程序功能实现
主循环代码
各个按键功能代码
功能函数代码
stm32g4xx_it.c中 代码
四、保存结果
演练时发现的问题
演练用时:4小时10分
完成度:“设备重新上电,能够从 E2PROM 相应地址中载入商品库存数量和价格。”功能未实现,原因是不会将char类型数据转换为其他类型数据。
- 串口
- 接收缓冲区的大小需要用sizeof()来确定,一般题目设置为定值即可。
- 发送一般需要加换行符,所以发送缓冲区的大小需要用strlen()来确定。
- 发送缓冲区要设置为一个有足够大小的确定的数组。
未解决的问题
题目要求初始化,又要求复位后可以从存储芯片中读取价格和库存,但是写入的是char类型的数据,所以数据类型转化是一个问题,之前没有考虑过读取各种数据的方法。需要专门学习一下。
普通的
题目分析与计划
设计规范
我自己设计的时候没有章法,从而设计的有点潦草,浪费了很多时间。设计的时候要冷静、全面、要遵守一个确定的顺序。
现在我要确定一个章法,这是第一版,以后更新的版本请在附录中查看。
- 浏览一遍题目,有一个整体印象。
- 仔细读题目,综合考虑如何配置外设和实现功能。
- 确定需要的外设和功能。约定圆形为外设分析、三角型为功能分析,图形中的数字来区分不同的模块。
- 设计流程,主循环前做什么、主循环中做什么。
- 确定主循环前打开需要的外设
- 根据题目一步一步的设计流程,不要遗漏,字不要写的太大,而且要留有足够的空间用来修改。
- 流程包括一条主线任务,调用函数就是支线任务。
- 同时在另一张草稿纸上写下需要的变量,按照外设或题目中的信息命名,要有特征性和区分度。
- 这样设计下来最多1小时,按照设计配置外设和写程序最多2小时,检查和优化1小时,最多4个小时就完成了。
-
全局变量定义:
经验规则:
标志变量:uint8_t ,只取个位数或0,1
与计算有关或者需要显示:uint32_t
浮点型:float
浏览题目
得到的信息:①串口、②I2C和存储芯片、③KEY4个、④LED2个、⑤LCD3种界面、⑥PWM输出。
详细阅读与分析
硬件要求:
⑥PWM输出
PA1、频率为2k、占空比默认5%,可以调到30%
所以PCBTimer设置为100MHz,预分频系数设置为499,周期设置为99。
功能要求:
常用功能:4个按键的响应功能、LED控制功能
三角形1:更新库存功能
三角形2:更新价格功能
三角形3:LED2闪烁功能
三角形4:串口接收并反馈功能
需要的定时器的功能:
三角形1用到定时器:TIM6
三角形3用到定时器:TIM7
整体流程设计
自己写设计流程草稿的时候要按照自己的习惯简写,我这里是为了方便描述才写这么多的。
- 启动PWM输出、串口接收、存储芯片相关的数据初始化。
- 主循环中轮询检测按键
- (支线)按键1的响应:按键B1按下一次就切换一种模式,设置标志位B1_key=1/2/3来区分模式;
- 当B1_key=1时为SHOP模式;
- 按下B2按键,X对应加1
- 按下B3按键,Y对应加1
- 按下B4按键,执行三角形1:更新库存功能
- (支线)三角形1:更新库存功能
- 如果有库存,就把LD1点亮标志LD1_f置1
- PWM占空比改为30%,打开TIM6
- TIM6:5s后关闭TIM6,LD1点亮标志LD1_f置0,PWM占空比改为5%
- 判断库存是否改变,若改变就更新存储芯片的数据
- 串口发送数据
- SHOP_X、SHOP_Y的值清零
- (支线)三角形1:更新库存功能
- 当B1_key=2时为PRICE模式;
- 按下B2按键,X对应加0.1
- 按下B3按键,Y对应加0.1
- 当B1_key=3时为REP模式;
- 执行三角形2:更新价格功能
- (支线)三角形2:更新价格功能
- 判断价格是否改变,若改变就更新存储芯片的数据
- (支线)三角形2:更新价格功能
- 按下B2按键,X对应加1
- 按下B3按键,Y对应加1
- 其他支线
- (支线)三角形3:LED2闪烁功能
- 轮询检测,若REP_X和REP_Y都为0,而且LD2没有闪烁,就打开TIM7;否则关闭定时器。
- TIM7:定时100ms,超时就改变LD2状态。
- 轮询检测,若REP_X和REP_Y都为0,而且LD2没有闪烁,就打开TIM7;否则关闭定时器。
- (支线)三角形4:串口接收并反馈功能
- 中断服务函数中,判断指令是否为“?”,若是,则发送数据。
- (支线)三角形3:LED2闪烁功能
一、外设配置与代码框分析
LED:锁存器操作包装函数,直接在main.c实现
void LED_switch(uint16_t GPIO_Pin,GPIO_PinState PinState)
{
HAL_GPIO_WritePin(LED_LE_GPIO_Port,LED_LE_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,LED1_Pin|LED2_Pin|LED3_Pin|LED4_Pin|LED5_Pin|LED6_Pin|LED7_Pin|LED8_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_Pin,PinState);
HAL_GPIO_WritePin(LED_LE_GPIO_Port,LED_LE_Pin,GPIO_PIN_RESET);
}
KEY:扫描函数
按键扫描函数框架:所有的按键引发事件的操作都在这里实现
//按键1响应函数
void B1Fun()
{
}
//按键2响应函数
void B2Fun()
{
}
//按键3响应函数
void B3Fun()
{
}
//按键4响应函数
void B4Fun()
{
}
//按键扫描
void KeyScan()
{
uint32_t keyDelay=50;
if(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin)==GPIO_PIN_RESET)
{
HAL_Delay(keyDelay);
if(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin)==GPIO_PIN_RESET)
{
B1Fun();
}
}
if(HAL_GPIO_ReadPin(B2_GPIO_Port,B2_Pin)==GPIO_PIN_RESET)
{
HAL_Delay(keyDelay);
if(HAL_GPIO_ReadPin(B2_GPIO_Port,B2_Pin)==GPIO_PIN_RESET)
{
B2Fun();
}
}
if(HAL_GPIO_ReadPin(B3_GPIO_Port,B3_Pin)==GPIO_PIN_RESET)
{
HAL_Delay(keyDelay);
if(HAL_GPIO_ReadPin(B3_GPIO_Port,B3_Pin)==GPIO_PIN_RESET)
{
B3Fun();
}
}
if(HAL_GPIO_ReadPin(B4_GPIO_Port,B4_Pin)==GPIO_PIN_RESET)
{
HAL_Delay(keyDelay);
if(HAL_GPIO_ReadPin(B4_GPIO_Port,B4_Pin)==GPIO_PIN_RESET)
{
B4Fun();
}
}
}
LCD:各种界面的设计,在主循环中实现
PWM输出:
- 通过设置 Prescaler 来设置定时器频率
- 通过设置 Counter Period 来设置周期(计数值)
- 通过设置 Pulse 来设置占空比
- 通过设置 CH Polarity 来设置极性
- PWM波的频率设置计算公式:
预分频系数 = (APB1 Timer/PWM波的频率 )-1;
周期 = 定时器频率/PWM波的频率
预分频系数 = (APB1 Timer/定时器频率 )-1;
- 根据题目确定配置,如果要配置%的占空比,周期就设置为100的倍数。然后根据题目和便于计算原则,设置预分频系数。
- 在程序中修改预分频系数、周期、占空比的函数如下
__HAL_TIM_SET_COUNTER() //设置计数值,即周期 __HAL_TIM_SET_PRESCALER() //设置预分频系数 __HAL_TIM_SET_COMPARE(设备句柄地址,通道号,脉宽值); //设置占空比
二、软件配置
参考附录的内容,建立名为“ ”的项目。
按照分析配置外设:
时钟配置:
LED
KEY
PWM输出
TIM
TIM6
TIM7
生成项目文件后,打开MDK;
导入LCD和I2C驱动程序文件,编译。
三、程序功能实现
代码规则如下:
【蓝桥杯嵌入式】五、省赛一条龙(一)全部功能总结汇总-CSDN博客
主循环代码
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
LCD_Init();
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
LCD_Clear(Black);
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM2_Init();
MX_TIM6_Init();
MX_TIM7_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
HAL_TIM_Base_Start(&htim2);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
HAL_UART_Receive_DMA(&huart1,rxBuf,1);
while (1)
{
KEYscan();
LD2_fun();
if(B1_key==1)
{
LED_fun();
LCD_DisplayStringLine(Line2,(uint8_t*)" SHOP");
sprintf(LCD_str," X:%d",SHOP_X);
LCD_DisplayStringLine(Line4,(uint8_t*)LCD_str);
sprintf(LCD_str," Y:%d",SHOP_Y);
LCD_DisplayStringLine(Line5,(uint8_t*)LCD_str);
}
else if(B1_key==2)
{
LED_fun();
LCD_DisplayStringLine(Line2,(uint8_t*)" PRICE");
sprintf(LCD_str," X:%.1f",PRICE_X);
LCD_DisplayStringLine(Line4,(uint8_t*)LCD_str);
sprintf(LCD_str," Y:%.1f",PRICE_Y);
LCD_DisplayStringLine(Line5,(uint8_t*)LCD_str);
}
else if(B1_key==3)
{
LED_fun();
LCD_DisplayStringLine(Line2,(uint8_t*)" REP");
sprintf(LCD_str," X:%d",REP_X);
LCD_DisplayStringLine(Line4,(uint8_t*)LCD_str);
sprintf(LCD_str," Y:%d",REP_Y);
LCD_DisplayStringLine(Line5,(uint8_t*)LCD_str);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
各个按键功能代码
//按键响应功能
void B1_fun()
{
B1_key++;
LCD_Clear(Black);
if(B1_key==3)
{
changePRICE();
}
if(B1_key>3)
{
B1_key=1;
}
}
void B2_fun()
{
if(B1_key==1)
{
SHOP_X++;
if(SHOP_X>REP_X)
{
LCD_Clear(Black);
SHOP_X=0;
}
}
else if(B1_key==2)
{
PRICE_X+=0.1;
if(PRICE_X>2.0)
PRICE_X=1.0;
}
else if(B1_key==3)
REP_X++;
}
void B3_fun()
{
if(B1_key==1)
{
SHOP_Y++;
if(SHOP_Y>REP_Y)
{
LCD_Clear(Black);
SHOP_Y=0;
}
}
else if(B1_key==2)
{
PRICE_Y+=0.1;
if(PRICE_Y>2.0)
PRICE_Y=1.0;
}
else if(B1_key==3)
REP_Y++;
}
void B4_fun()
{
//三角形1:更新库存功能
if(B1_key==1)
{
if(REP_X!=0||REP_Y!=0)
LD1_f=1;
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,29);
HAL_TIM_Base_Start_IT(&htim6);
REP_X-=SHOP_X;
REP_Y-=SHOP_Y;
if(oldREP_X!=REP_X||oldREP_Y!=REP_Y)
{
oldREP_X=REP_X;
oldREP_Y=REP_Y;
rem();
SHOP_X=SHOP_X;
SHOP_Y=SHOP_Y;
}
sprintf(txBuf,"X:%d,Y:%d,Z:%0.1f\n",SHOP_X,SHOP_Y,(SHOP_X*PRICE_X+SHOP_Y*PRICE_Y));
HAL_UART_Transmit_DMA(&huart1,(uint8_t*)txBuf,strlen(txBuf));
SHOP_X=0;
SHOP_Y=0;
LCD_Clear(Black);
}
}
功能函数代码
//存入数据
void rem()
{
sprintf((char*)REP_Xbuf,"%d",REP_X);
sprintf((char*)REP_Ybuf,"%d",REP_Y);
sprintf((char*)PRICE_Xbuf,"%.1f",PRICE_X);
sprintf((char*)PRICE_Ybuf,"%.1f",PRICE_Y);
iic_24c02_write(REP_Xbuf,0,sizeof(REP_Xbuf));
iic_24c02_write(REP_Ybuf,1,sizeof(REP_Ybuf));
iic_24c02_write(PRICE_Xbuf,2,sizeof(PRICE_Xbuf));
iic_24c02_write(PRICE_Ybuf,3,sizeof(PRICE_Ybuf));
}
//更新价格
void changePRICE()
{
if(oldPRICE_X!=PRICE_X||oldPRICE_Y!=PRICE_Y)
{
oldPRICE_X=PRICE_X;
oldPRICE_Y=PRICE_Y;
rem();
}
}
//LD2闪烁
void LD2_fun()
{
if(REP_X==0&&REP_Y==0)
{
HAL_TIM_Base_Start_IT(&htim7);
}
else
{
HAL_TIM_Base_Stop_IT(&htim7);
}
}
//LED控制
void LED_fun()
{
if(LD1_f==1)
{
LED_switch(LED1_Pin,GPIO_PIN_RESET);
}
else
LED_switch(LED1_Pin|LED2_Pin|LED3_Pin|LED4_Pin|LED5_Pin|LED6_Pin|LED7_Pin|LED8_Pin,GPIO_PIN_SET);
}
stm32g4xx_it.c中 代码
void DMA1_Channel1_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
/* USER CODE END DMA1_Channel1_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart1_rx);
/* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
if(rxBuf[0]=='?')
{
sprintf(txBuf,"X:%.1f,Y:%.1f\n",PRICE_X,PRICE_Y);
HAL_UART_Transmit_DMA(&huart1,(uint8_t*)txBuf,strlen(txBuf));
}
/* USER CODE END DMA1_Channel1_IRQn 1 */
}
void TIM6_DAC_IRQHandler(void)
{
/* USER CODE BEGIN TIM6_DAC_IRQn 0 */
/* USER CODE END TIM6_DAC_IRQn 0 */
HAL_TIM_IRQHandler(&htim6);
/* USER CODE BEGIN TIM6_DAC_IRQn 1 */
HAL_TIM_Base_Stop_IT(&htim6);
LD1_f=0;
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,4);
/* USER CODE END TIM6_DAC_IRQn 1 */
}
void TIM7_IRQHandler(void)
{
/* USER CODE BEGIN TIM7_IRQn 0 */
/* USER CODE END TIM7_IRQn 0 */
HAL_TIM_IRQHandler(&htim7);
/* USER CODE BEGIN TIM7_IRQn 1 */
LED_switch(LED1_Pin|LED2_Pin|LED3_Pin|LED4_Pin|LED5_Pin|LED6_Pin|LED7_Pin|LED8_Pin,GPIO_PIN_SET);
HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
HAL_GPIO_WritePin(LED_LE_GPIO_Port,LED_LE_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED_LE_GPIO_Port,LED_LE_Pin,GPIO_PIN_RESET);
if(LD1_f==1)
LED_switch(LED1_Pin,GPIO_PIN_RESET);
/* USER CODE END TIM7_IRQn 1 */
}
四、保存结果
打包Core文件中所有文件,还有Gxxxxxxxx.hex文件。