第十二届蓝桥杯嵌入式国赛(赛后总结)

前言

笔者今年是第一次参加蓝桥杯,赛道为嵌入式设计,目前取得了国二的成绩,虽说不是最好,但从中学到了许多,收获了许多。今年我所使用的是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;

版权声明:本文为CSDN博主「ORI2333」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45628620/article/details/117753766

生成海报
点赞 0

ORI2333

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

蓝桥杯嵌入式——题目总结及文章汇总

蓝桥杯嵌入式——题目总结及文章汇总 一、前言 笔者也是最近准备参加第十二届蓝桥杯嵌入式的选手,希望能够和大家一起学习。我也将我自己学习过程中,学习stm32遇到的一些问题,以及刷题的代码等都进行了整

蓝桥杯嵌入式——题目总结及文章汇总

蓝桥杯嵌入式——题目总结及文章汇总 一、前言 笔者也是最近准备参加第十二届蓝桥杯嵌入式的选手,希望能够和大家一起学习。我也将我自己学习过程中,学习stm32遇到的一些问题,以及刷题的代码等都进行了整

基于STM32的指纹密码锁

设计简介: 本设计是基于单片机的指纹密码锁,主要实现以下功能: 矩阵按键输入密码,并通过按键显示*号可通过按键或手机开门密码可通过按键进行开门可通过蓝牙模块连接手机进行开门可通过指纹进