蓝桥杯嵌入式备赛相关知识点

发布于:2024-04-03 ⋅ 阅读:(58) ⋅ 点赞:(0)

一、介绍

​ 蓝桥杯嵌入式使用的单片机是STM32G431RBT6,内核ARM Cortex - M4,MCU+FPU,170MHz/213DMIPS,高达128KB Flash,32KB SRAM,其余的外设就不多介绍了,参照数据芯片数据手册

​ CT117E-M4开发板资源:微控制器STM32G431RBT6、一路USB转串口、2.4寸TFT-LCD、4个功能按键、1个复位按键、8个LED、一个E2PROM(AT24C02)、一个可编程电阻(100K)、2路信号发生器、2个分压电位器、2个扩展接口、一个CMSIS DAP Link调试器

二、初始化Cubemx

1.新建工程

2.下载程序引脚配置

3.启用外部晶振,时钟配置

4.生成工程

如果工程路径不是纯英文则会缺少启动文件

三、下载程序配置

1.LED

1.1 原理图

1.2 引脚配置

其中PD2高电平使能锁存器,PC8-15默认给高电平,放置上电初始化LED亮

1.3 添加库文件夹路径,并新建LED库文件

1.4 相关代码

LED.c

#include "led.h"

void LED_Disp(uchar dsLED)
{
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET); // 所有PC引脚置高
    HAL_GPIO_WritePin(GPIOC,dsLED<<8,GPIO_PIN_RESET); // 将传入的dsLED位数置低,为了使对应的LED亮
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET); // 锁存器置高使能,记住PC的引脚电平
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET); // 锁存器置地,锁住PC的引脚电平
}

LED.h

#ifndef _LED_H_
#define _LED_H_

#include "main.h"
void LED_Disp(uchar dsLED);

#endif
1.5 头文件添加

1.6 工程调试

2.LCD

2.1 原理图

2.2 将比赛提供的液晶显示工程复制到自己工程文件中

2.3 引脚配置

2.4 工程配置

2.5 工程调试

3.按键

3.1 原理图

3.2 引脚配置
3.3 定时器配置

这里按键选择按下触发定时器进入中断 所以需要配置定时器

使用定时器4(通用定时器,使用外部晶振),分频系数为80(从0开始则为80-1),则每1s
1M次,定时评率为为10000,对应1s 1M/10000次,频率为10ms每次,一定记得开启NVIC使能中断

3.4 相关代码

interrupt.c

#include "interrupt.h"
#include "stdio.h"

struct keys key[4] = {0,0,0,0,0};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//中断回调函数
{
	if(htim->Instance == TIM4)//判断是否来自于定时器4
	{
		key[0].key_status = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key[1].key_status = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].key_status = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].key_status = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		for(int i=0;i<4;i++)
		{
			switch(key[i].judge_status)
			{
				case 0:
					if(key[i].key_status == 0)
					{
						key[i].judge_status = 1;
						key[i].key_time = 0;
					}
					break;
				case 1:
					if(key[i].key_status == 0)
					{
						key[i].judge_status = 2;
					}
					else
					{
						key[i].judge_status = 0;
					}
					break;
				case 2:
					if(key[i].key_status == 1)
					{
						key[i].judge_status = 0;
						if(key[i].key_time < 70)
						{
							key[i].key_flag = 1;
							key[i].key_longflag = 0;
						}
						else
						{
							key[i].key_time++;
							if(key[i].key_time > 70)
							{
								key[i].key_longflag = 1;
								key[i].key_flag = 0;
							}
						}
					}
					break;
			}
		}
	}
}

interrupt.h

#ifndef __INTERRUPT_H_
#define __INTERRUPT_H_

#include "stdbool.h"
#include "main.h"

struct keys
{
	uint8_t judge_status;
	bool key_flag;
	bool key_status;
	bool key_longflag;
	uint8_t key_time;
};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

#endif
3.5 工程调试

在interrupt.h定义的结构体须在main.c中用extern再次声明

4.pwm

4.1 pwm输出引脚配置

配置PA6、PA7为输出引脚

频率fre=80 000 000 / (PSC+1)/ (ARR+1)

占空比duty=CRR / ARR

4.2 输入捕获引脚配置

原理图

根据题目需求设置捕获引脚即可 第一个一般是模拟输出频率 第二个则是模拟输出电压 R37~R40分别对应硬件板上的四个旋钮

相关配置

选用PB4, PA15为定时器通道CH1
配置预分频,重装载值(设为最大,防止溢出),启用中断

4.3 相关代码

interrupt.c

#include "interrupt.h"

unsigned int Cnta1 = 0,Cnta2 = 0;
unsigned int fre1 = 0,fre2 = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM2)//获取频率
	{
			Cnta1 = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);//得到计时值
			__HAL_TIM_SetCounter(htim,0);//计时值清0
			fre1 = (80000000/80)/Cnta1;//获得频率

			HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);//打开定时器
	}
	if(htim->Instance == TIM3)
	{
			Cnta2 = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
			__HAL_TIM_SetCounter(htim,0);
			fre2 = (80000000/80)/Cnta2;
		
			HAL_TIM_IC_Start(htim,TIM_CHANNEL_2);
	}
}

interrupt.h

#ifndef __INTERRUPT_H_
#define __INTERRUPT_H_

#include "main.h"

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);

#endif
4.4 工程调试

通过选择R39、R40即可调动频率值

5.ADC

5.1 原理图

5.2 引脚配置

5.3 相关代码

adc1.c

#include "adc1.h"

double getADC(ADC_HandleTypeDef *pin)
{
	unsigned int adc;
	HAL_ADC_Start(pin);
	adc = HAL_ADC_GetValue(pin);
	return adc*3.3/4096;
}

adc1.h

#ifndef __ADC1_H_
#define __ADC1_H_

#include "main.h"

double getADC(ADC_HandleTypeDef *pin);

#endif
5.4 工程调试

通过旋转R37~R38即可调动模拟输出电压

6.I2C

6.1 原理图及芯片资料

芯片资料中前面的nK就是芯片大小 我们只需要看第一行即可 第一行中前面四位固定是1010 后面的A0、A1、A2分别对应原理图中的E1、E2、E3 原理图中E1、E2、E3分别接地 所以在设置地址时设置0即可 最后的R/W位即用来配置是读或者写 0则为写入 1为读取 这对后续配置I2C相关工程有很大关系

6.2 引脚i配置

简单的使能PB6、PB7的引脚为输出模式即可

6.3 将比赛提供库函数移植到自己工程文件中

6.4 相关代码

在复制过来的工程I2C-hal.c底部编写以下函数

unsigned char I2C_Read(unsigned char addr)
{
	unsigned char data;
	I2CStart();
	I2CSendByte(0xa0);//0是写 1是读
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CStop();
	
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	data = I2CReceiveByte();
	I2CSendAck();
	I2CStop();
	return data;
}

void I2C_Write(unsigned char addr,unsigned char data)
{
	I2CStart();
	I2CSendByte(0xa0);//0是写 1是读
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CSendByte(data);
	I2CWaitAck();
	I2CStop();
}

头文件中定义

unsigned char I2C_Read(unsigned char addr);
void I2C_Write(unsigned char addr,unsigned char data);
6.5 工程调试

将上面的频率值分别的高八位和低八位分别写入I2C的指定地址 再将其读取

7.UART

7.1 引脚配置

波特率根据题目设定 一般为9600 同时需要开启中断

7.2 相关代码
//发送字符测试
char temp[20];串口调试
sprintf(temp,"fre1 = %d\r\n",fre1);
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);

//接收字符函数
char rx_Alldata[30];
uint8_t rx_data;
unsigned char rx_pointer;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	rx_Alldata[rx_pointer++] = rx_data;
	HAL_UART_Receive_IT(&huart1,&rx_data,1);
}
7.3 工程调试
void uart_rx_process()
{
	if(rx_pointer>0)
	{
		if(rx_pointer == 22)
		{
			sscanf(rx_Alldata,"%4s:%4s:%12s",car_type,car_data,car_time);
		}
		else
		{
			char temp[20];
			sprintf(temp,"Error");
			HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
		}
		rx_pointer = 0;
		memset(rx_Alldata,0,30);
	}
}

须在头文件中包含string,h 

8.RTC

8.1 引脚配置

8.2 相关代码
RTC_TimeTypeDef rtc_time;
RTC_DateTypeDef rtc_date;

void RTC_Rorcess()
{
	HAL_RTC_GetTime(&hrtc,&rtc_time,RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc,&rtc_date,RTC_FORMAT_BIN);
}
8.3 工程调试
char text[20];
sprintf(text, "%02d-%02d-%02d",rtc_time.Hours,rtc_time.Minutes,rtc_time.Seconds);
LCD_DisplayStringLine(Line7, (uint8_t *)text);
sprintf(text, "%04d-%02d-%02d-%d",rtc_date.Year ,rtc_date.Month,rtc_date.Date,rtc_date.WeekDay );
LCD_DisplayStringLine(Line8, (uint8_t *)text