第十二届蓝桥杯嵌入式国赛(赛后总结)
Posted ORI2333
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十二届蓝桥杯嵌入式国赛(赛后总结)相关的知识,希望对你有一定的参考价值。
前言
笔者今年是第一次参加蓝桥杯,赛道为嵌入式设计,目前取得了国二的成绩,虽说不是最好,但从中学到了许多,收获了许多。今年我所使用的是STM32G431, 用的HAL库,这也是我第一次学习使用HAL库。参加本次比赛前,我已经学习了快一年的STM32,但注重在项目开发方面,参加此比赛是为了进一步巩固自己的32基础,同时参加比赛也认识了一些优秀的大佬,在此分享我个人的一些经验。
对于此次比赛总结:
对于省赛,我花了大大概两周的时间,三天左右学习了HAL库,之后将省赛所用的模块单独用HAL写了一遍,然后就去做往年省赛题,我做了四套题,感觉省赛难度不大,无非是堆函数,这些函数只要你能理解自个能敲出来,那都是没问题的,重要的考点在于你解决问题的逻辑,这个很难短时间突击,只能靠平时的积累。而且不要想象省赛那么难,其实我第一次看到省赛题时感觉也有点小挑战,但省一并不需要完完全全实现,只要实现70%就可了,全部实现是一定能获奖的。
对于国赛,期间各种比赛交错,计算机设计大赛、互联网+等等,还有学校的一些乱七八糟的事物,耽误了很多,国赛只准备了一周时间,和国赛一样,把可能考的模块单独写一遍,再做往年题,但我只做了两套。国赛期间,因为我的捕获函数开启了HAL_TIM_IC_Start(&htim3, TIM_CHANNEL_2)
,而不是HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);
,两个字母之差,这个问题我找了快两个小时,他们的寄存器都有数据,但一个能进回调函数,一个不能进,因为这里耽误,导致最后一个功能没有完成,失之锅一。这里说下,国一的水平基本上是全部功能实现的水平。对于客观题只能靠平时积累和往年题。
对于其比赛大家一定要懂得是,这个比赛是一个类似于算法题的考试,单位时间内尽全力解题。而且比赛成绩的判定是看你的.axf文件,也就是看现象看结果,所以实现过程不要过度追求完美、复杂,能用就行,不出问题就行。我准备的单一功能实现代码就是追求能用就行,确保到比赛时我不会写错,能够以最短的时间完成符合的现象。就比如串口接收,我平时做项目会用DMA,但比赛我不会用,因为用这个会增加我的复杂度,进而错误率也会增大,所以在准备能简单就简单。
在我看来,蓝桥杯嵌入式适用于初学者学习STM32、已有经验者巩固基础、往年单片机组晋升的同学来参加,但不要把所有的精力都放在这一个比赛上,要结合自身当前情况,将比赛融入自身的学习路线中,比赛是为自个服务的,而不能为了比赛而比赛。
以下是我国赛准备期间单一功能的实现代码,大家可以收藏看看,这些代码可能不是最优解,但在我看来是最快最简单最粗暴解,很容易在比赛中套用。有些不懂可以私聊哈,相互学习!
串口通信
重写printf函数
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
return(ch);
}
中断接收字符串函数
使用前一定开启中断
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
void USART1_IRQHandler(void)
{
static u8 index = 0;
if(__HAL_UART_GET_FLAG( &huart1, UART_FLAG_RXNE ) != RESET)
{
ch=( uint16_t)READ_REG(huart1.Instance->RDR);
rxbuff[index] = ch;
if(rxbuff[index] == '\\n')
{
rxbuff[index] = '\\0';//将\\n字符清零
rxbuff[index-1] = '\\0';//将\\r字符清零
index = 0;//数组下标放到最前面
strcpy(temp,rxbuff); //拷贝函数,将rxbuff送给temp
memset(rxbuff,0,sizeof(rxbuff)); //将rxbuff清0
return ;//接收结束直接返回
}
index++;
}
}
IIC EPROM
Cube设置
将PB6、PB7模式调节为Output Push Pull
,No pull-up and no pull-down
,速度High
读取函数
uint8_t M24C02_Read(unsigned char address)
{
unsigned char val;
I2CStart(); //IIC开始
I2CSendByte(0xa0); //测试状态
I2CWaitAck(); //等待相应
I2CSendByte(address); //发送要读取的地址
I2CWaitAck(); //等待相应
I2CStart();
I2CSendByte(0xa1); //告诉24C02要读数据
I2CWaitAck(); //等待相应
val = I2CReceiveByte(); //返回要读取的值
I2CWaitAck(); //等待相应
I2CStop(); //停止
return val;
}
发送函数
void M24C02_Write(unsigned char address, uint16_t info)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(address);
I2CWaitAck();
I2CSendByte(info);
I2CWaitAck();
I2CStop();
}
ADC
获取ADC数字(通用)
uint16_t Get_ADC(uint32_t ch) //eg:ADC_CHANNEL_17
{
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ch;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADC_Start(&hadc2);
HAL_ADC_PollForConversion(&hadc2,10); //很重要!!
return (uint16_t)HAL_ADC_GetValue(&hadc2);
}
简易
uint16_t getADC(void)
{
uint16_t adc = 0;
HAL_ADC_Start(&hadc2);
adc = HAL_ADC_GetValue(&hadc2);
return adc;
}
数字滤波
u16 Get_Adc_Average(u32 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_ADC(ch);
HAL_Delay(5);
}
return temp_val/times;
}
内部定时器
配置
Clock Source
选择Internal Clock
.Counter Period
设为 999
实现
//定时1ms
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint16_t TIM4_Cnt1 = 0;
static uint16_t SEG_Cnt1 = 0;
static uint16_t Print_Cnt1 = 0;
static uint16_t LED_Cnt1 = 0;
if(htim->Instance == TIM4){
TIM4_Cnt1++;
if(TIM4_Cnt1 >= 100){
TIM4_Cnt1 = 0;
Time++;
}
}
}
注意事项: 一定要初始化
HAL_TIM_Base_Start_IT(&htim4);
按键
中断触发
void EXTI0_IRQHandler(void)
{
if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET){
Key1_Flag = !Key1_Flag;
Key2_Flag = 0;
HAL_Delay(4);
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
}
}
纯软件实现
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,WKUP按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1; //按键松开标志
if(mode==1)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
{
delay_ms(10);
key_up=0;
if(KEY0==0) return KEY0_PRES;
else if(KEY1==0) return KEY1_PRES;
else if(KEY2==0) return KEY2_PRES;
else if(WK_UP==1) return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
return 0; //无按键按下
}
简易实现
uint8_t KeyScan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
if( HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == RESET){
while(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == RESET);
return 2;
}
else
return 1;
}
判断长按短按
简单那来说就是套两层软件按键检测,按键第一次time = 0,开始计数。
void KeyScan(){
static u8 key_up=1; //按键松开标志
static u8 key_ups=1;
if(key_ups && (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == RESET
|| HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == RESET )){
Time = 0;
key_ups = 0;
N_Flag = 1;
}else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == SET
&& HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == SET){
key_ups = 1;
}
if(Time >= 8) mode = 1;
else mode = 0;
if(Time > 5000) Time = 0;
if(mode == 1) key_up = 1;
if(key_up && (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == RESET
|| HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == RESET)){
key_up = 0;
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == RESET){
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == RESET){
}
}
else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == SET
&& HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == SET){
key_up = 1;
}
}
PWM捕获(求频率或占空比)
规范操作
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim -> Instance == TIM3)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
if(CaptureNumber == 0)
{
/* Get the Input Capture value */
IC3ReadValue1 = TIM3->CCR2;
CaptureNumber = 1;
__HAL_TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_FALLING);
}
else if(CaptureNumber == 1)
{
/* Get the Input Capture value */
IC3ReadValue2 = TIM3->CCR2;
CaptureNumber = 2;
__HAL_TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_RISING);
if (IC3ReadValue2 > IC3ReadValue1)
{
Capture_High = (IC3ReadValue2 - IC3ReadValue1);
}
else
{
Capture_High = ((0xFFFF - IC3ReadValue1) + IC3ReadValue2);
}
IC3ReadValue1 = IC3ReadValue2;
}
else if(CaptureNumber == 2)
{
/* Get the Input Capture value */
IC3ReadValue2 = TIM3->CCR2;
CaptureNumber = 0;
if (IC3ReadValue2 > IC3ReadValue1)
{
Capture_Low = (IC3ReadValue2 - IC3ReadValue1);
}
else
{
Capture_Low = ((0xFFFF - IC3ReadValue1) + IC3ReadValue2);
}
/* Frequency computation */
TIM3Freq = (uint32_t) 1000000 / (Capture_Low + Capture_High);
TIM3Duty = Capture_High * 1.0 / (Capture_Low + Capture_High);
}
}
}
}
记得使能
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);
简易(只能求频率)
uint32_t cc1_value_2 = 0; // TIMx_CCR1 的值
uint32_t RP2 = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
cc1_value_2 = __HAL_TIM_GET_COUNTER(&htim3);
__HAL_TIM_SetCounter(&htim3, 0);
RP2 = 1000000 / cc1_value_2;
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
}
外设部分
DS18b20
double GetTemp()
{
u16 read = (ds18b20_read() & 0x07FF);
return (read/16.);
}
数码管使用
原理图
数码管是共阴极的,所以当输入为1时,数码管亮。
74HC595是一个8位串行输入、并行输出的位移缓存器RCLK引脚:当RCLK到上升沿时,移位寄存器进入存储寄存器,也就是负责将数据传到数码管。
SCK 引脚到上升沿时:数据寄存器移位
SER 引脚是串行数据输入端
简单概括就是 SER负责写数据(0或1)然后写一位sck存储,然后sck移位,数据写完RCLK负责把数据传到数码管进行显示。
功能代码
显示函数
void SEG_DisplayValue(u8 Bit1, u8 Bit2, u8 Bit3)
{
u8 i = 0;
u8 code_tmp = 0;
RCLK_L;
code_tmp = Seg7[Bit3];
for(i = 0; i < 8; i++){
SCK_L;
if(code_tmp & 0x80){
SER_H;
}
else{
SER_L;
}
code_tmp = code_tmp << 1;
SCK_L;
SCK_H;
}
code_tmp = Seg7[Bit2];
for(i = 0; i < 8; i++){
SCK_L;
if(code_tmp & 0x80){
SER_H;
}
else{
SER_L;
}
code_tmp = code_tmp << 1;
SCK_L;
SCK_H;
}
code_tmp = Seg7[Bit1];
for(i = 0; i < 8; i++){
SCK_L;
if(code_tmp & 0x80){
SER_H;
}
else{
SER_L;
}
code_tmp = code_tmp << 1;
SCK_L;
SCK_H;
}
RCLK_L;
RCLK_H;
}
宏定义和参数部分
#define RCLK_PIN GPIO_PIN_2
#define SER_PIN GPIO_PIN_1
#define SCK_PIN GPIO_PIN_3
#define RCLK_H HAL_GPIO_WritePin(GPIOA, RCLK_PIN, GPIO_PIN_SET)
#define RCLK_L HAL_GPIO_WritePin(GPIOA, RCLK_PIN, GPIO_PIN_RESET)
#define SER_H HAL_GPIO_WritePin(GPIOA, SER_PIN, GPIO_PIN_SET)
#define SER_L HAL_GPIO_WritePin(GPIOA, SER_PIN, GPIO_PIN_RESET)
#define SCK_H HAL_GPIO_WritePin(GPIOA, SCK_PIN, GPIO_PIN_SET)
#define SCK_L HAL_GPIO_WritePin(GPIOA, SCK_PIN, GPIO_PIN_RESET)
uc8 Seg7[17] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f,
0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71, 0x00};
光敏
当作滑动变阻器即可
tmp = getADC();
snprintf((char *)str, sizeof(str), " R-P:%.2fK ", tmp / (4096. - tmp) * 10);
PWM输出
调整输出占空比
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1, 1000);
调整输出频率
__HAL_TIM_SET_AUTORELOAD(&htim2,999);
停止和开始
HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
三轴传感器
ptr = Lis302DL_Output();
x = ptr[0] * 18. / 1000;
y = ptr[1] * 18. / 1000;
z = ptr[2] * 18. / 1000;
以上是关于第十二届蓝桥杯嵌入式国赛(赛后总结)的主要内容,如果未能解决你的问题,请参考以下文章