【第13届蓝桥杯嵌入式】省二经验分享和基于STM32G431的CubeMX具体配置方法

目录

第13届蓝桥杯嵌入式赛题:密码锁

1、K.e.i.l.5 2032版破解软件

2、背景颜色修改问题

        知网 CAJViewer 的背景色

3、GPIO的输入输出

4、NVIC中断的理解和流程

5、IIC相关操作

6、ADC采集电压显示

6.1 基本参数记录

6.2问题及注意事项

7、定时器TIM相关     

7.1 基本定时器TIM6

7.2 通用定时器TIM2(需要中断)

7.3 通用定时器TIM15

7.4 通用定时器TIM3/17(不需要中断)

8、RTC时钟

9、常用函数的寻找

9.1 systick滴答定时器优先级设置

9.2 新建工程要添加的配置(蓝桥杯嵌入式比赛)

10、keil5 MDK的使用技巧

10.1 keil5左上角的三个按钮Translate、build、rebuild

11、UART的配置


第13届蓝桥杯嵌入式第一场赛题:密码锁

1、结果:成绩省二

2、题目:密码锁

        1、led        2、key        3、lcd        4、方波和PWM波切换输出PA1        5、uart

3、心得体会:        

        突击了一个月,有时候感觉自己能拿省一,有时候又感觉省三。最后拿了省二,也算是居中了哈哈。值得吐槽的是这次组委会出成绩太慢了,以往都是10天左右,这次花了3周,4.9号考完,五一放假前才出成绩。明年继续参加,目标要拿省一。

        选择题很变态,以前都是单选和多选分开,现在是混在一起考。像我基础差的,选择题全靠蒙。但是大题就是中规中矩了,编程题考的都是基础模块,但是在考场把基础做好也不容易呀。考场突发情况太多了,比如一开始CubeMX不能生成keil5的模板,问题是Projcet Manager -> Advanced Settings -> Mcu and Firmware Package加载错了,和考场给的不匹配,要注意修改默认的Package,用考场给的资源包。

        剩下的就没啥了,不紧张就好了,没必要一直看着5个小时的倒计时,容易心态乱奥。下面是我做的一套省赛模板,里面有按键Key、led、lcd、uart、i2c、adc、定时器tim(基本定时器、输入捕获、输出比较、PWM输出)、rtc时钟等等。大家想要什么资料可以评论奥。

//头文件
#include "main.h"
#include "RCC\bsp_rcc.h"
#include "KEY_LED\bsp_key_led.h"
#include "LCD\bsp_lcd.h"
#include "UART\bsp_uart.h"
#include "I2C\bsp_i2c.h"
#include "ADC\bsp_adc.h"
#include "TIM\bsp_tim.h"
#include "RTC\bsp_rtc.h"


//变量创建区
__IO uint32_t uwTick_Key_Set_Point = 0;	//记录时钟滴答值(1ms变化一次),从而控制Key_Proc的执行速度。
__IO uint32_t uwTick_Led_Set_Point = 0;	
__IO uint32_t uwTick_Lcd_Set_Point = 0;	
__IO uint32_t uwTick_Uart_Set_Point = 0;	


//*Key扫描专用变量
unsigned char ucKey_Val,unKey_Down,unKey_Up,ucKey_Old;

//*LED专用变量
unsigned char ucLed;

//*LCD显示专用变量
unsigned char Lcd_Disp_String[22];	//创建字符串数组

//*uart发送存储变量
uint8_t rx;
uint16_t counter;
uint8_t str[40];

//*I2C变量
uint8_t EEPROM_String_1[5] ={0x11,0x22,0x33,0x44,0x55};
uint8_t EEPROM_String_2[5] ={0};

//*RES4017可编程电阻
uint8_t RES_4017;

//*TIM6 测试
uint8_t	i;

//*TIM2输入捕获 求频率和占空比
float PWM1_Duty; 		//占空比
uint16_t PWM1_D_Count;	//高电平时间
uint16_t PWM1_T_Count;	//全周期

//*RTC结构体定义
RTC_TimeTypeDef hour_min_sec_Time;
RTC_DateTypeDef year_mon_day_Date;


//*子函数声明区
void Key_Proc(void);
void Led_Proc(void);
void Lcd_Proc(void);
void Usart_Proc(void);

int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
	
  /* Configure the system clock */
  SystemClock_Config();
	
	/*bsp资源的初始化*/
	KEY_LED_Init();
	
	LCD_Init();
	//不可以把这三行放到void Lcd_Proc()函数里面去,会造成一直刷新闪烁。
	LCD_Clear(Blue);		
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);	
	
	//**串口初始化
	MX_USART1_UART_Init();
	HAL_UART_Receive_IT(&huart1,&rx,1);		//串口接收打开中断
	
	//**I2C初始化
	I2CInit();
	//**EEPROM测试读写
	iic_24c02_write(EEPROM_String_1,0,5);	//把EEPROM_String_1里的数据写入到EEPROM里,从地址0开始写
	HAL_Delay(1);													//读写之间加延时
  iic_24c02_read(EEPROM_String_2,0,5);	//读出数据,放到EEPROM_String_2里
	
	//**可变电阻4017的测试读写
	write_register(0x10);			// 由公式20kΩ = (100kΩ * N)/127 + 10kΩ; 可以求出N=12.7,取整即为13 = 0x0D
	HAL_Delay(1);
	RES_4017 = read_register();			
	
	//**ADC初始化
	MX_ADC1_Init();
	MX_ADC2_Init();
	
	//**TIM初始化	
	MX_TIM6_Init();		//基本定时器TIM6  一般设置1秒加1。
	HAL_TIM_Base_Start_IT(&htim6);

	//**输入捕获,接收555定时器和TIM3/17的PWM输入,可以显示频率和占空比。
	MX_TIM2_Init();		
	HAL_TIM_Base_Start_IT(&htim2);
	HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
	HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
	
	//**输出比较,可以输出555方波,可以触发事件。而PWM输出不能触发,常用于控制电机等
	MX_TIM15_Init();	
	HAL_TIM_OC_Start_IT(&htim15, TIM_CHANNEL_1);
	HAL_TIM_OC_Start_IT(&htim15, TIM_CHANNEL_2);	
	
	//**输出PWM,	可以控制输出的频率和占空比。	
	MX_TIM3_Init();		
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);	
	//**输出PWM	
	MX_TIM17_Init();		
	HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_2);		
	
	//**RTC初始化
	MX_RTC_Init();
	
  while (1)
  {
		Key_Proc();
		Led_Proc();
		Lcd_Proc();
		Usart_Proc();
  }
}	

//***Key扫描子函数
void Key_Proc(void)
{
	if((uwTick - uwTick_Key_Set_Point) < 50) return;				//减速函数,延时50ms
		uwTick_Key_Set_Point = uwTick;									
																											
	//标准3行代码
	ucKey_Val = Key_Scan();		//按键扫描Key_Scan只有1234四个值
	unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val);	//unKey_Down是检测某按键下降沿
	unKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);	//ucKey_Val是检测按钮是否在按下状态
	ucKey_Old = ucKey_Val;
	
	if(unKey_Down == 4)		//按键4按下,LD8和LD4亮起
	{
		ucLed = 0x88;
	}
	if(unKey_Down == 3)		//按键3按下,全灭
	{
		ucLed = 0x00;	
	}
	
}	

//***Led扫描子函数
void Led_Proc(void)
{
	if((uwTick - uwTick_Led_Set_Point) < 200)		return;	//减速函数,延时200ms
		uwTick_Led_Set_Point = uwTick;

		LED_Disp(ucLed);	
}

//**Lcd扫描子函数
void Lcd_Proc(void)
{
	if((uwTick - uwTick_Lcd_Set_Point) < 200)		return;		//减速函数,延时200ms
		uwTick_Lcd_Set_Point = uwTick;	
	
	//串口发送显示
	sprintf((char *)Lcd_Disp_String,"Uart1_Transmit:%02d",counter);
	LCD_DisplayStringLine(Line0, Lcd_Disp_String);
	
	//*I2C显示
	sprintf((char *)Lcd_Disp_String,"I2C_EEP:%x%x%x%x%x",EEPROM_String_2[0],EEPROM_String_2[1],EEPROM_String_2[2],EEPROM_String_2[3],EEPROM_String_2[4]);
	LCD_DisplayStringLine(Line1, Lcd_Disp_String);
	//*RES4017可编程电阻显示
	sprintf((char *)Lcd_Disp_String,(const char *)"RES_K:%5.2fk", 0.7874*RES_4017);
	LCD_DisplayStringLine(Line2, Lcd_Disp_String);
	//*通过RES4017可编程电阻来求电压	
	sprintf((char *)Lcd_Disp_String,(const char *)"VOLTAGE:%5.2fV", 3.3*((0.7874*RES_4017)/((0.7874*RES_4017)+10)));	//串联电阻求电压
	LCD_DisplayStringLine(Line3, (uint8_t *)Lcd_Disp_String);			
	
	//**ADC1显示
	sprintf((char *)Lcd_Disp_String,(const char*)"ADC1:%5.2fV",getADC1()/4096.0*3.3);
	HAL_Delay(100);				//注意要有延时
	LCD_DisplayStringLine(Line4,(uint8_t *)Lcd_Disp_String);
	
	//**ADC2显示
	sprintf((char *)Lcd_Disp_String,(const char*)"ADC2:%5.2fV",getADC2()/4096.0*3.3);
	HAL_Delay(100);				//注意要有延时
	LCD_DisplayStringLine(Line5,(uint8_t *)Lcd_Disp_String);
	
	//**TIM6显示
	sprintf((char *)Lcd_Disp_String,(const char*)"TIM6:%02dS",i);
	LCD_DisplayStringLine(Line6,(uint8_t *)Lcd_Disp_String);	
	
	//**TIM2显示
	sprintf((char *)Lcd_Disp_String,(const char*)"TIM2:%04dhz,%04.1f%%",(unsigned int)(1000000/PWM1_T_Count),PWM1_Duty*100);
	LCD_DisplayStringLine(Line7,(uint8_t *)Lcd_Disp_String);		
	
	//RTC(时间和日期必须同时获取,不要在中间分开)
	HAL_RTC_GetTime(&hrtc, &hour_min_sec_Time, RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc, &year_mon_day_Date, RTC_FORMAT_BIN);	
	sprintf((char *)Lcd_Disp_String,(const char*)"RTC_Time:%02d:%02d:%02d",(unsigned int)hour_min_sec_Time.Hours,(unsigned int)hour_min_sec_Time.Minutes,(unsigned int)hour_min_sec_Time.Seconds);
	LCD_DisplayStringLine(Line8,(uint8_t *)Lcd_Disp_String);			
	sprintf((char *)Lcd_Disp_String,(const char*)"RTC_Date:%02d-%02d-%02d",(unsigned int)year_mon_day_Date.Year,(unsigned int)year_mon_day_Date.Month,(unsigned int)year_mon_day_Date.WeekDay);
	LCD_DisplayStringLine(Line9,(uint8_t *)Lcd_Disp_String);		

}	

void Usart_Proc(void)
{
	if((uwTick - uwTick_Uart_Set_Point) < 1000)		return;		//减速函数,延时1s
		uwTick_Uart_Set_Point = uwTick;	
		
	//串口显示
	sprintf((char *)str,"Uart1_Transmit:%02d\r\n",counter);	
	HAL_UART_Transmit(&huart1,(uint8_t *)str,strlen(str),50);		//50ms是发送超时时间。如果波特率为9600,发送一个位需要的时间为1/9600s=0.0001042s=0.1042ms,计算...
	
	if(++counter == 60)
		counter = 0;
	
}

//**串口接收回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	LED_Disp(0xc0);
	HAL_Delay(300);	//在串口中断里嵌套了滴答systick定时器中断,因此systick中断优先级应该更高。
	LED_Disp(0xff);	
	
	HAL_UART_Receive_IT(&huart1,&rx,1);		//此行必须保留
}

//**TIM6 基本定时器 中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM6)
	{	
		i++;
		HAL_TIM_Base_Start_IT(&htim6);
	}
}
	
//**TIM2 输入捕获 中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM2)
	{
		if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)		//高电平通道
		{
			PWM1_T_Count = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1)+1;		//整个周期 T
			PWM1_Duty = (float)PWM1_D_Count/PWM1_T_Count;
		}
		else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
		{
			PWM1_D_Count = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2)+1;		//高电平持续时间D		
		}
	}
}

**TIM15 输出比较 中断回调函数
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM15)
	{
		if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
		{
			__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, __HAL_TIM_GetCounter(htim)+100);	//5khz
		}	
		else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
		{
			__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, __HAL_TIM_GetCounter(htim)+500);	//1khz
		}			
	}


}

1、K.e.i.l.5 2032版破解软件

        为了大家方便,分享一下2032的软件。

        注意:要以管理员身份打开 ,否则报错奥! 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YaF5pyJ5bCP54yq5Y2W,size_20,color_FFFFFF,t_70,g_se,x_16

链接:https://pan.baidu.com/s/1QrtW42YD5xp-y-PY6p50hw 
提取码:Lu26 
--来自百.度..网.盘.超.级.会.员.V3的分享

2、背景颜色修改问题

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5YaF5pyJ5bCP54yq5Y2W,size_20,color_FFFFFF,t_70,g_se,x_16

最终效果:watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5YaF5pyJ5bCP54yq5Y2W,size_20,color_FFFFFF,t_70,g_se,x_16

具体步骤:点击"Edit"-> "Configuration" 到如下界面。

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5YaF5pyJ5bCP54yq5Y2W,size_20,color_FFFFFF,t_70,g_se,x_16

        一定要把Text,Number,....直到Incomplete String等等10多个类型的Background都给改了。否则就会出现文末的常见问题。

 watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5YaF5pyJ5bCP54yq5Y2W,size_19,color_FFFFFF,t_70,g_se,x_16

         为了方便,可以填一个自定义颜色。不用一直输入红绿蓝的颜色数值了。

知网 CAJViewer 的背景色

具体步骤:点击工具"-> "参数设置" 到如下界面。

3、GPIO的输入输出

详见蚂蚁工程的PPT->第05章 LED指示灯程序设计

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YaF5pyJ5bCP54yq5Y2W,size_20,color_FFFFFF,t_70,g_se,x_16

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YaF5pyJ5bCP54yq5Y2W,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YaF5pyJ5bCP54yq5Y2W,size_20,color_FFFFFF,t_70,g_se,x_16

4、NVIC中断的理解和流程

听了蚂蚁的课,对于STM32中断的理解。

1、前期准备工作

①初始化引脚为中断模式。 //在CubeMX中配置

②HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2)

        //设置中断优先级分组,在文件stm32xxx_hal_msp.c中。

③HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 1);   

        //配置中断优先级,一般在对应的.c文件中

④HAL_NVIC_EnableIRQ(EXTI0_IRQn);         //使能外中断,同上。

2、断信号产生(按键KEY4按下),进入IRQHandler函数。

3、在中断函数IRQHandler中再次判断是否产生中断标志if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u),如果确实产生了中断标志,则执行中断服务函数。即进入Callback回调函数。

4、执行回调函数

5、IIC相关操作

1、要把bsp_iic.c文件中的代码行SDA_Output_Mode(); 换一下顺序。从1、换成2、

1、                                2、
SDA_Output_Mode();	               SCL_Output(0);	 
SCL_Output(0);                     delay1(DELAY_TIME);
delay1(DELAY_TIME);                SDA_Output_Mode();	
  

2、手写AT24C02的写程序

6、ADC采集电压显示

6.1 基本参数记录

1、引脚和通道

        ADC1        引脚PB12        通道IN11        电阻R38

        ADC2        引脚PB15        通道IN15        电阻R37

2、时钟分频选择异步2分频,不需要配置中断。

3、注意时钟的变化:

6.2问题及注意事项

1:LCD会显示电压恒为 0V ?   

sprintf(buf, "R38_VAL:%6.2fV", getADC()/4096*3.3);

原因:

        getADC( ) / 4096,因为getADC()比较小对4096取余恒为0,乘以3.3后还是0。

解决方法:

        核心思想是把整型运算变成浮点数运算。

1、(float)getADC()/4096*3.3)        //强制转换结果为浮点数

2、getADC()/4096.0 * 3.3)        // 给4096变成4096.0,变成浮点数运算

3、getADC()*3.3/4096        //先乘以3.3,变成浮点数,再除4096

 注:

①        %6.2f的格式含义是,数字整体长度包括小数点为6位,保留两位小数,位数大于6则输出实际位数。

②     %5d即宽度至少为5位,右对齐,位数大于5则输出实际位数。

        %0nd 用得比较多,表示输出的整型宽度至少为n位,不足n位用0填充。

        printf("%05d",1)输出:00001

        printf("%5d",1)输出:****1(*为空格)

③常用转义字符

常用转义字符

\b

退格符(Backspace)

\t

水平制表符(相当于tab,缩进)

\n

换行符

\f

换页符

\r

回车符

\r\n

Enter = 回车+换行( \r\n )

\\

转义输出\

%%

转义输出%

7、定时器TIM相关     

     定时器序号

功能

启动函数区别

回调函数

注意事项

TIM6

单纯计时功能,无对应的引脚。

HAL_TIM_Base_Start_IT();

HAL_TIM_PeriodElapsedCallback ();

配置值:分频为1Hz。7999、9999

需要配置中断

TIM2

输入捕获来显示频率和占空比

HAL_TIM_IC_Start_IT ();

HAL_TIM_IC_CaptureCallback ();

配置值:分频为15Hz左右。79、65535

需要配置中断

TIM15

输出比较模式(方波等)

HAL_TIM_OC_Start_IT ();

HAL_TIM_OC_DelayElapsedCallback();

配置值:同上

需要配置中断

TIM3/17

PWM输出模式,不同频率和占空比

HAL_TIM_PWM_Start ();

配置值:分频为1000Hz。79、999

Base代表基本定时器Tim6ICInput Capture)代表输入捕获Tim2

OCOutput Compare)代表输出比较Tim15

7.1 基本定时器TIM6

        功能:单纯计时功能。无对应的引脚。

        TIM6配置参数:(需要中断)

7.2 通用定时器TIM2(需要中断)

        1、功能:输入捕获来显示频率和占空比,例如555方波和TIM3/17输出的PWM信号。

        2、PWM输入捕获占空比的原理,如下图。

        3、TIM2配置参数:

        注:TIM2的通道CH1,在GPIO相关配置中更改引脚为PA15

        当Prescaler为79时,把时钟80Mhz分频为1Mhz;Counter Period选择最大65535。

7.3 通用定时器TIM15

1、功能:比较输出模式,可以触发事件。而PWM输出不能触发,常用于控制电机等。

2、引脚TIM15 -> PA2。

3、TIM12配置参数

7.4 通用定时器TIM3/17(不需要中断

1、功能:PWM输出,可以输出不同频率和占空比的PWM信号。

2、引脚:TIM3 -> PA6 ;TIM17 -> PA7 ;

3、TIM3配置参数:

4、TIM17配置参数:

 4、TIM3(PA6)和TIM17(PA7)引脚连线图:

8、RTC时钟

配置参数如下:

        注意: 别忘了时钟切换奥!

9、常用函数的寻找

9.1 systick滴答定时器优先级设置

举例:在uart回调函数里 调用HAL_Delay(),就要使滴答定时器uwTick的优先级高于串口。

修改答定时器uwTick优先级的步骤:

第一步:

 第二步:

 第三步:

9.2 新建工程要添加的配置(蓝桥杯嵌入式比赛)

 还要注意adc  要添加3个底层文件,其中特殊的是stm32xx_II_adc.c文件

10、keil5 MDK的使用技巧

10.1 keil5左上角的三个按钮Translate、build、rebuild

第1个按钮(Translate):

        作用:检查某个文件是否有语法错误。

        速度:快

第2个按钮(build):

        作用:编译器会记录上次编译过的源程序,在下次编译的过程中只对修改过的编译。

        速度:较快

        优点:大大节省编译时间,建议常用。

第3个按钮(rebuild):

        作用:检查某个文件是否有语法错误。

        速度:很慢

        注:如果修改了优先级等全局变量,最好用这个按钮全编译一下。

11、UART的配置

 注意引脚改为PA10 PA9。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

内有小猪卖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值