STM32综合-基于HAL库(第十二届蓝桥杯嵌入式省赛)

2023-05-16

文章目录

  • 前言
  • 一、CubeMX配置(第十二届省赛完整版)
  • 二、代码相关定义、声明
    • 1.函数声明
    • 2.宏定义
    • 3.变量定义
  • 三、主要函数
    • 1.按键扫描
    • 2.串口接收中断、定时器中断(接收)
    • 3.数据解析
    • 4.判定数据正误
    • 5.数据更新
    • 6.结算
    • 7.Main函数
  • 四、实验结果
    • 1.数据长度有误
    • 2.数据不合法
    • 3.数据正常
  • 五、源码(转载请注明出处)
  • 总结


前言

相关说明:

开发板:CT117E-M4(STM32G431RB 蓝桥杯嵌入式比赛板)
开发环境: CubeMX+Keil5
涉及题目:第十二届蓝桥杯嵌入式省赛
题目难点:停车管理系统逻辑编写;数据接收,解析,判定,更新。
代码思路:(使用usart1时需要修改引脚为PA8 PA9 PA10)串口接收到数据后,先判定数据接收长度是否正确,即每接收到一个字节都重新开启定时器,最后一字节数据接收完且进入定时器中断后判断接收数据长度,准确无误则进行数据解析;解析时将数据分段保存:车类型,车牌号,时间;保存好后再对数据的合法性进行判定,车类型是否为规定类型之一,类型、车牌号数据长度是否为四位。时间是否合法(月份对应天数,时分秒对应进制);最后是存储数据的更新,车牌号是否已经存在?不存在的话判断是否还有空余车位?有则将类型、车牌号、时间等数据存储在数组中;存在的话考虑现在接收时间是否大于到达时间?时间合法则对存储在数组组中的数据进行计算和输出。


CubeMX配置、主要函数代码及说明:

一、CubeMX配置(第十二届省赛完整版)

1.使能外部高速时钟:在这里插入图片描述

2.配置时钟树:在这里插入图片描述

3.GPIO:

在这里插入图片描述

4.TIM3(PWM):在这里插入图片描述在这里插入图片描述

5.TIM6(串口在接收到最后一字节数据5us后进入定时器中断函数):在这里插入图片描述

6.USART1:在这里插入图片描述

7.NVIC(中断配置):在这里插入图片描述

二、代码相关定义、声明

1.函数声明

main.c
void Car_Change(char *type,char *carNum,time_t *time,char *str);		//Car数组改变
uint8_t Dat_Check(char *type,char *carNum);								//判断接收数据正误(格式 时间)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);			//定时器6中断 数据接收超时判定(避免一个合法数据分多次发送)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);				//串口接收中断函数(每接收一字节中断一次)
void Settle_Accounts(struct car outCar);								//结账
void Switch_RecBuff(char *type,char *carNum,time_t *time,char *timStr);	//CNBR:A392:200202120000 数据解析(类型 车牌 时间)
void LCD_Init_Show();				//LCD初始化显示
void LCD_Refresh(uint8_t page);		//LCD更新显示
void LED_Change();					//LED状态改变

gpio.h
void KEY_Scan(void);					//按键扫描
void LED_AllClose(uint8_t *LCD_Close);	//LED显示更新

2.宏定义

#define LED_GPIO_PORT	GPIOC
#define LED1_GPIO_PIN	GPIO_PIN_8
#define LED2_GPIO_PIN	GPIO_PIN_9
#define LED3_GPIO_PIN	GPIO_PIN_10
#define LED4_GPIO_PIN	GPIO_PIN_11
#define LED5_GPIO_PIN	GPIO_PIN_12
#define LED6_GPIO_PIN	GPIO_PIN_13
#define LED7_GPIO_PIN	GPIO_PIN_14
#define LED8_GPIO_PIN	GPIO_PIN_15

#define ON 	GPIO_PIN_RESET
#define OFF	GPIO_PIN_SET

#define LED1(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED1_GPIO_PIN,a)
#define LED2(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED2_GPIO_PIN,a)
#define LED3(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED3_GPIO_PIN,a)
#define LED4(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED4_GPIO_PIN,a)
#define LED5(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED5_GPIO_PIN,a)
#define LED6(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED6_GPIO_PIN,a)
#define LED7(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED7_GPIO_PIN,a)
#define LED8(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED8_GPIO_PIN,a)

#define KEY1_GPIO_PORT 	GPIOB
#define KEY1_GPIO_PIN	GPIO_PIN_0
#define KEY2_GPIO_PORT 	GPIOB
#define KEY2_GPIO_PIN	GPIO_PIN_1
#define KEY3_GPIO_PORT 	GPIOB
#define KEY3_GPIO_PIN	GPIO_PIN_2
#define KEY4_GPIO_PORT 	GPIOA
#define KEY4_GPIO_PIN	GPIO_PIN_0

3.变量定义

main.c
uint8_t CNBR=0;							//CNBR类型车辆数
uint8_t VNBR=0;							//VNBR类型车辆数
uint8_t IDLE=8;							//空闲位置
double CNBR_Price=3.5;					//CNBR类型停车费用
double VNBR_Price=2.0;					//VNBR类型停车费用
char str[30];							//用于组合字符串
uint8_t LED_Close[3]={1,0,1};			//LED关闭数组

uint8_t recDatBuff[3][20]={0,0,0,0};	//数据接收数组(recDatBuff[0]存储停车类型,recDatBuff[1]存储车牌号,recDatBuff[2]存储时间,冒号存储在数组0,1行的最后一个位置)
uint8_t recDex=0; 						//接收数组下标
uint8_t recNum=0;						//接收数组行号
uint8_t recDat;							//本次接收数据(一次一字节)
uint32_t recLong=0;						//数接收长度
uint8_t firstByte=1;					//接收到本次传输数据的第一个字节(用于判定传输超时)
uint8_t switch_flag=0;					//接收数据转换标志,完整接收一次数据后置1,随后进行转换、判断、存储

int Error;		//接收数据错误标志
int recYear;	//接收年
int recMon;		//接收月
int recDay;		//接收日
int recHour;	//接收时
int recMin;		//接收分
int recSec;		//接收秒

struct car		//车辆结构体
{
	char num[10];		//车牌号
	char type[10];		//车辆类型
	int dftime;			//时间差
	double EndPrice;	//最终价格
	double type_price;	//停车单价(元/小时)
	char reach_time[60];//到达时间字符串
	char leave_time[60];//离开时间字符串
	int reach;			//到达时间时间戳
	int leave;			//离开时间时间戳
};
struct car car[9];		//车辆存储结构体数组
uint8_t car_dex=0;		//存储数组下标

三、主要函数

首先是按键按下对数据以及输出PWM的更改;更改PWM输出时,按键按下后,先判断LED_Close[2]存储的状态,为灭则使LED2亮,开启PWM,输出1KHz信号。为亮则使LED2灭,暂停PWM,输出持续的低电平。(PWM在MX的配置为1KHz的输出。)

1.按键扫描

gpio.c
void Data_Change(uint8_t mode)//数据改变
{
	switch(mode)
	{
		case ADD:
			CNBR_Price+=Price_step;
			VNBR_Price+=Price_step;
			break;
		
		case SUB:
			CNBR_Price-=Price_step;
			VNBR_Price-=Price_step;
			break;
	}
}

void Setting_Mode()//设置模式
{
	uint8_t delay=0;
	while(1)
	{
		if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)//change mode
		{
			HAL_Delay(10);
			if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
			{
				while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);
				LCD_Refresh(1);
				break;
			}
		}
		
		else if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)//++
		{
			HAL_Delay(10);
			if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)
			{
				while(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET);
				Data_Change(ADD);
				LCD_Refresh(2);
			}
		}
		
		else if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)//--
		{
			HAL_Delay(10);
			if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)
			{
				while(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET);
				Data_Change(SUB);
				LCD_Refresh(2);
			}
		}
	}
}

void KEY_Scan()//按键扫描
{
	if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)//change mdoe
	{
		HAL_Delay(10);
		if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
		{
			while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);
			LCD_Refresh(2);
			Setting_Mode();
		}
	}
	
	else if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)//PWM
	{
		HAL_Delay(10);
		if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)
		{
			while(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET);
			if(LED_Close[2]==1)//如果灭 即输出低电平
			{
				LED_Close[2]=0;
				HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
			}
			else
			{
				LED_Close[2]=1;
				HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET);
			}
		}
	}
}

接下来是数据的处理,分别为接收,解析,判定和更新。

2.串口接收中断、定时器中断(接收)

串口在接收到一个字节数据时进入串口中断函数,每次进入串口中断函数都需要重新开启判定,在最后一字节数据接收完5us后进入定时器中断函数,在定时器中断函数中判定接收数据长度是否符合要求,不符合则返回Error,符合要求则按照设定好的规则进行保存。我使用的是二维数组,遇到冒号就换行,最后根据数据长度来设定接收结束的标志(接收结束后将开始解析)即可。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器6中断 数据接收长度判定
{
	HAL_TIM_Base_Stop_IT(&htim6);//关闭判定
	HAL_UART_Receive_IT(&huart1,&recDat,sizeof(recDat));//重新开启串口接收中断
	if(recLong!=22)
	{
		Error=1;//错误标志位置1
	}
	else
	{
		switch_flag=1;//字符串转换标志位置1
	}
	recLong=0;//接收长度重置
	recNum=0;//recNum重置
	recDex=0;//recDex重置
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口接收中断函数(每接收一字节中断一次) 在最后一字节接收完5us后判断数据长度
{
	HAL_TIM_Base_Stop_IT(&htim6);//关闭判定
	
	recLong++;
	
	recDatBuff[recNum][recDex++]=recDat;//将接收到的数据存储进数组
	
	if(recDat==':')//如果本次接收数据为冒号则数组换行,下标置为0
	{
		recNum++;
		recDex=0;
	}
 
	TIM6->CNT=0;
	HAL_TIM_Base_Start_IT(&htim6);//重新开启判定
	
	HAL_UART_Receive_IT(&huart1,&recDat,sizeof(recDat));//重新开启串口接收中断
}

3.数据解析

将保存在二维数组中的数据进行字符串组合,数组中第0行数据为车的类型+冒号,第1行数据为车牌号+冒号,第2行数据为时间
这里用到time.h中的函数对时间进行转换,先将时间进行类型转换(字符型转整型),转换后根据mktime函数规则进行调整(年份减1900,月份减1),再将调整后的结果赋值给时间结构体,再调用mktime函数进行时间转换,转换后的时间为自1970年1月1日以来持续时间的秒数 (为什么用一个错误的时间测试,mktime不会返回-1,欢迎懂的大佬留言)

void Switch_RecBuff(char *type,char *carNum,time_t *time,char *timStr)//CNBR:A392:200202120000 数据解析 类型 车牌 时间
{
	struct tm timeTem;//定义时间结构体
	sprintf(type,"%c%c%c%c",recDatBuff[0][0],recDatBuff[0][1],recDatBuff[0][2],recDatBuff[0][3]);//解析类型
	
	sprintf(carNum,"%c%c%c%c",recDatBuff[1][0],recDatBuff[1][1],recDatBuff[1][2],recDatBuff[1][3]);//解析车牌号
	
	sprintf(timStr,"%c%c%c%c%c%c%c%c%c%c%c%c",recDatBuff[2][0],recDatBuff[2][1],recDatBuff[2][2],recDatBuff[2][3],recDatBuff[2][4],recDatBuff[2][5],recDatBuff[2][6],recDatBuff[2][7],recDatBuff[2][8],recDatBuff[2][9],recDatBuff[2][10],recDatBuff[2][11]);//解析时间
	
	recYear=2000+(recDatBuff[2][0]-48)*10+(recDatBuff[2][1]-48)-1900;//将时间转换为int类型数据 如'0'要转换成0,字符0对应的ASCII码为48,则0为'0'-48 1~9以此类推
	recMon=(recDatBuff[2][2]-48)*10+(recDatBuff[2][3]-48)-1;//时间结构体存储规则,月份减1,年份减1900
	recDay=(recDatBuff[2][4]-48)*10+(recDatBuff[2][5]-48);
	recHour=(recDatBuff[2][6]-48)*10+(recDatBuff[2][7]-48);
	recMin=(recDatBuff[2][8]-48)*10+(recDatBuff[2][9]-48);
	recSec=(recDatBuff[2][10]-48)*10+(recDatBuff[2][11]-48);
	
	timeTem.tm_year=recYear;//一一对应赋值
	timeTem.tm_mon=recMon;
	timeTem.tm_mday=recDay;//刚开始这里成员选成了tm_yday,需要注意,排查了好久,yday是代表一年中的第几天,mday代表一月中的第几天
	timeTem.tm_hour=recHour;
	timeTem.tm_min=recMin;
	timeTem.tm_sec=recSec;
	*time=mktime(&timeTem);//将时间结构体用mktime函数转化为自1970年1月1日以来持续时间的秒数 (为什么用一个错误的时间测试,mktime不会返回-1,欢迎懂的大佬留言)
}

4.判定数据正误

车类型是否为规定类型之一,类型、车牌号数据长度是否为四位(这里用冒号的位置进行判断)。时间是否合法(月份对应天数是否准确,,2月份还需考虑闰年;时分秒对应进制有误);

uint8_t Dat_Check(char *type,char *carNum)//判定接收数据正误 格式 时间
{
	recMon+=1;//月份在上面为了转换减了1 这里需要加回来 recYear同理
	recYear-=100;
	if(strcmp(type,"CNBR")!=0 && strcmp(type,"VNBR")!=0)//如果类型不是其中之一(判断类型)
	{
		return 1;
	}
	
	if(recDatBuff[0][4]!=':' || recDatBuff[1][4]!=':')//数组最后一个是否为:(判断格式)
	{
		return 1;
	}
	
	if(recMon>12 || recMon<0)//判断时间合法性 下同 很好理解
	{
		return 1;
	}
	else if(recMon==2)//2月
	{
		if(recYear%4==0)//闰年
		{
			if(recDay>28 ||recDay<0)
			{
				return 1;
			}
		}
		else//非闰年
		{
			if(recDay>29 ||recDay<0)
			{
				return 1;
			}
		}
	}
	else if(recMon==1 || recMon==3 || recMon==5 || recMon==7 || recMon==8 || recMon==10 || recMon==12)//大月
	{
		if(recDay>31 ||recDay<0)
		{
			return 1;
		}
	}
	else if(recMon==4 || recMon==6 || recMon==9 || recMon==11)//小月
	{
		if(recDay>30 ||recDay<0)
		{
			return 1;
		}
	}
	
	if(recHour>23 || recHour<0)//时
	{
		return 1;
	}
	
	if(recMin>59 || recMin<0)//分
	{
		return 1;
	}
	
	if(recSec>59 || recSec<0)//秒
	{
		return 1;
	}
	
	return 0;//无误返回0
}

5.数据更新

接收数据判定无误后进入数据更新步骤。首先判断车牌号是否已经存在,如不存在则为进入,进入时需要判断是否有空余车位,有则将接收数据保存在数组中,并更新车位信息;如果车牌号存在则为离开,离开需判断时间是否大于车辆到达时间,若合法则将离开车辆信息传递给结算函数进行结算并更新车位信息,不合法返回Error。

void Car_Change(char *type,char *carNum,time_t *time,char *str)//Car数组改变
{
	uint8_t i;
	uint8_t dir=1;//方向标志位 1为进 0为出
	uint8_t outcar_dex;//离开车辆下标
	for(i=0;i<9;i++)//判断停车场是否存在该车
	{
		if(strcmp(carNum,car[i].num)==0)//如存在
		{
			dir=0;//方向为出
			outcar_dex=i;//记录下标
			if(*time<car[outcar_dex].reach)//与到达时间对比 判断时间是否合法
			{
				printf("Error\n");
				return;
			}
			break;
		}
	}
	
	if(dir==1)//in
	{
		if(IDLE==0)//无空闲车位
		{
			return;
		}
		if(strcmp(type,"CNBR")==0)//如果车辆类型为CNBR
		{
			CNBR++;
			IDLE--;
			car[car_dex].type_price=CNBR_Price;//存储价格
		}
		else if(strcmp(type,"VNBR")==0)//如果车辆类型为VNBR
		{
			VNBR++;
			IDLE--;
			car[car_dex].type_price=VNBR_Price;//存储价格
		}
		strcpy(car[car_dex].type,type);//存储车类型
		strcpy(car[car_dex].num,carNum);//存储车牌号
		strcpy(car[car_dex].reach_time,str);//存储车到达时间字符串
		car[car_dex].reach=*time;//存储车到达时间
		car_dex++;//下标++ 为储存下一辆车做准备
	}
	else//out
	{
		if(strcmp(type,"CNBR")==0)
		{
			CNBR--;
			IDLE++;
		}
		else if(strcmp(type,"VNBR")==0)
		{
			VNBR--;
			IDLE++;
		}
		strcpy(car[outcar_dex].leave_time,str);//存储车离开时间字符串
		car[outcar_dex].leave=*time;//存储车离开时间
		Settle_Accounts(car[outcar_dex]);//结算
		for(i=outcar_dex;i<car_dex;i++)//存储数组前移
		{
			car[i]=car[i+1];
		}
		car_dex--;//下标-- 为储存下一辆车做准备
	}
}

6.结算

打印到达和离开信息,并用difftime函数计算时间差,单位为秒,再将时间单位化为小时,最后计算费用并将时间和费用信息进行打印。

void Settle_Accounts(struct car outCar)//结账
{
	printf("%s:%s:%s\n",outCar.type,outCar.num,outCar.reach_time);//串口打印到达信息
	printf("%s:%s:%s\n",outCar.type,outCar.num,outCar.leave_time);//串口打印离开信息
	outCar.dftime=difftime(outCar.leave,outCar.reach)/60/60;//difftime函数返回两时间的差值 单位为秒
	if(outCar.dftime==0)outCar.dftime=1;//不足一小时记为一小时 
	outCar.EndPrice=outCar.dftime*outCar.type_price;  //计算费用
	printf("%s:%s:%d:%.2f\n\n",outCar.type,outCar.num,outCar.dftime,outCar.EndPrice);//串口打印时长及费用信息
}

7.Main函数

在主循环之前需要做好初始化工作。
1.要先重置定时器更新标志位(TIMX->SR=0),否则程序运行后将立刻进入定时器中断函数。
2.开启串口接收中断
3.LCD初始化显示
4.PWM初始化
主循环逻辑大体就是实时更新LCD和LED,并检测是否需要对数据进行转换,转换完后判断数据是否合法,合法的话是车辆进入还是车辆离开。

int main(void)
{
  /* USER CODE BEGIN 1 */
	time_t time;//保存传输的时间
	char type[10];//保存传输的类型
	char carNum[20];//车牌号
	char timStr[60];//时间字符串
//	memset(&time, 0, sizeof(time));
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */
	LCD_Init();//LCD初始化
	LCD_Init_Show();//LCD初始化显示
	HAL_UART_Receive_IT(&huart1,&recDat,sizeof(recDat));//开启串口接收中断
	TIM6->SR=0;//中断标志位清零
	
	LED_Close[2]=1;//LED2默认为灭
	HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);//PWM关闭
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET);//输出持续低电平
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		KEY_Scan();//按键扫描
		LCD_Refresh(1);//LCD更新显示
		if(switch_flag==1 && !Error)//接收数组转换标志为1并且无错误就继续执行
		{
			switch_flag=0;//转换标志位重置
			Switch_RecBuff(type,carNum,&time,timStr);//进行数据转换
			Error=Dat_Check(type,carNum);//判定接收数据是否合法
			if(!Error)//如果无错误
			{
				Car_Change(type,carNum,&time,timStr);//对存储车辆信息进行更新
			}
			else//接收数据不合法
			{
				Error=0;//重置错误标志位
				printf("Error\n");//串口输出提示信息
			}
			LED_Change();
		}
		else if(Error)//接收数据长度不符
		{
			Error=0;//重置错误标志位
			printf("Error\n");//串口输出提示信息
		}
		LED_AllClose(LED_Close);//LED显示更新
  }
  /* USER CODE END 3 */
}

四、实验结果

1.数据长度有误

a.数据过长
在这里插入图片描述
b.数据过短
在这里插入图片描述
c.返回
在这里插入图片描述

2.数据不合法

a.类型错误
在这里插入图片描述
b.时间不合法
在这里插入图片描述
c.离开时间小于到达时间
在这里插入图片描述

在这里插入图片描述
d.返回
在这里插入图片描述

3.数据正常

a.输入车辆到达信息
在这里插入图片描述
b.输入车辆离开信息
在这里插入图片描述
c.返回
在这里插入图片描述

五、源码(转载请注明出处)

在这里插入图片描述


总结

以上就是全部内容,如有错误请批评指正。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32综合-基于HAL库(第十二届蓝桥杯嵌入式省赛) 的相关文章

  • ApacheDS搭建

    前提 安装jdk sudo apt get install openjdk 8 jdk 安装 先安装apacheds2 配置客户端远程登录 xff0c 这里使用Apache Directory Studio xff0c 配置界面如下 xff
  • Windows 10 内置linux执行带GUI的应用程序

    1 安装MobaXterm xff0c 并运行 2 打开内置的Linux xff0c 命令执行带GUI的运行程序即可
  • Repo介绍

    目录 1 概要2 工作原理 2 1 项目清单库 repo manifests 2 2 repo脚本库 repo repo 2 3 仓库目录和工作目录 3 使用介绍 3 1 init3 2 sync3 3 upload3 4 download
  • Android 8.0.0-r4源码目录结构详解

    android的移植按如下流程 1 android linux 内核的普通驱动移植 让内核可以在目标平台上运行起来 2 正确挂载文件系统 确保内核启动参数和 android 源代码 system core rootdir 目录下的 init
  • Android8.0.0-r4的编译系统

    一 概述 1 1 编译系统变化 从Android 7 0开始 xff0c android的编译系统发生了变化 xff0c 之前依赖Makefile组织编译系统 xff0c 从7 0开始逐步引入了kati soong optional未正式使
  • [Android Studio]Android Studio 三种添加插件的方式

    何给Android Studio添加插件 添加插件的路径有三种 xff0c 我把他们分类如下 xff1a 点击设置小按钮 点击 xff3b Plugins xff3d 这里展示的是你已经安装的插件 xff0c 我们可以点击插件名称 xff0
  • Gerrit 服务器插件安装-示例插件delete project

    gerrit2 X 中没法直接删除一个项目 xff0c 之前需要手工删除 xff0c 后来社区提供了一个插件delete project来搞定这个事 xff0c 安装方法如下 xff1a 到 gerritforge xff0c 找到对应的
  • Windows平台下载Android源码(整理)

    Google官方下载源码使用的系统Ubuntu系统 xff0c 不过现在我们需要在Windows系统中下载Android源码文件 网站的地址是 xff1a https android googlesource com 里面包括Android
  • Ubuntu 16.04 文件服务器--samba的安装和配置

    Samba是在Linux系统上实现的SMB xff08 Server Messages Block xff0c 信息服务块 xff09 协议的一款免费软件 它实现在局域网内共享文件和打印机 xff0c 是一个客户机 服务器型协议 客户机通过
  • 深入剖析Android音频之AudioTrack

    播放声音能够用MediaPlayer和AudioTrack xff0c 两者都提供了java API供应用开发人员使用 尽管都能够播放声音 但两者还是有非常大的差别的 当中最大的差别是MediaPlayer能够播放多种格式的声音文件 比如M
  • 树莓派4 运行 Tensorflow Lite

    树莓派4 运行 Tensorflow Lite 1 更新树莓派 span class token function sudo span apt update 2 下载安装脚本 span class token function git sp
  • 操作系统进程进行系统调用详细过程

    翻阅很多资料 xff0c 综合了各处所述进程在进行系统调用之后的状态会如何的解答 xff0c 以下是我个人理解 xff0c 欢迎各位读者纠错 PS 特别感谢以下这个帖子 xff0c 看完他们的讨论我才茅塞顿开 xff0c 非常感谢 xff0
  • 解决Ubuntu 找不到ARM64 的源的问题(转)

    Ubuntu 安装了NVIDIA的驱动还有DriveWokrs之后 xff0c 好像把系统添加了arm64的架构 xff0c 因此 xff0c 在源更新的时候 xff0c 也会更新arm64相关的源 xff0c 但是问题在于 xff0c 用
  • asp.net 实现打开文件所在的文件夹, 本地可以打开,发布后点击按钮没有反应的解决办法

    此类情况大概是安全范畴的问题 确定上传文件夹的共享 xff0c iis 以及电脑帐户 xff0c 以及aspnet 等是否有对应的相关权限 1 确认ASPNET 账户属于管理员级别 2 在 服务 里面找到 IIS Admin xff0c 双
  • numpy 和 tensor 的区别

    关系 xff1a 两者共享内存 xff0c 转换方便 xff0c 没有额外的开销 区别 xff1a 1 数据类型上面的区别 xff1a numpy 默认类型是 float64 int32 tensor 默认类型是float32 int64
  • 关于docker无法apt-get update的问题

    在看这篇文章https www jianshu com p 21d66ca6115e 有一个部分是 但是发现自己的 Node 没有ping命令 想着去apt get update 但是出现如下错误 只要在命令签名加上 sudo 就行
  • ubuntu下安装zip unzip

    安装命令 apt get install zip unzip 执行命令常见错误 xff1a 1 unable to locate package 解决办法 xff1a 执行sudo apt get update命令后再执行安装命令就可以了
  • 平衡小车卡尔曼滤波算法

    最近研究STM32的自平衡小车 xff0c 发现有两座必过的大山 xff0c 一为卡尔曼滤波 xff0c 二为PID算法 网上看了很多关于卡尔曼滤波的代码 xff0c 感觉写得真不咋地 一怒之下 xff0c 自己重写 xff0c 不废话 x
  • FreeRTOS学习-前言与FreeRTOS发行版

    1 前言 因为工作的需要 xff0c 学习FreeRTOS已经有一段时间了 接下来一段时间会定期更新本人学习FreeRTOS的系列笔记 系列笔记主要参考了官方的说明手册和FreeRTOS的源代码 其主要思想是先了解FreeRTOS的对外接口
  • FreeRTOS学习-内存管理

    1 动态内存分配与FreeRTOS 从v9 0 0后 xff0c FreeRTOS开始支持内核对象的静态分配方式 xff0c 因此 xff0c 内存管理库可以被裁剪 但在大多数嵌入式应用中 xff0c 堆的使用还是非常常见的 因此 xff0

随机推荐

  • FreeRTOS学习-任务管理(Task管理)(1)

    1 简介 任务管理 xff08 或称进程管理 xff09 是所有操作系统内核的最基本组成模块之一 xff0c FreeRTOS也不例外 想要了解一个操作系统 xff0c 不得不理解其任务管理的设计和实现 任务管理的介绍由两篇文章组成 xff
  • Java基础之Java枚举

    絮叨 昨天刚好有遇到一个枚举的小问题 xff0c 然后发现自己并不是那么熟悉它 xff0c 然后在开发中 xff0c 枚举用的特别多 xff0c 所以有了今天的文章 什么是枚举 Java中的枚举是一种类型 xff0c 顾名思义 xff1a
  • C++ STL 移动一个vector的元素到另一个vector

    1 背景 有的时候 xff0c 我们需要提取某个现有的vector中的元素到另一个vector中 xff0c 或者对多维的vector进行纬度的转换 在这种场景下 xff0c 往往原始的vector中的数据可能并不需要了 xff0c 为了节
  • Qt/C++ 临时屏蔽控件信号(signal)的实用方法

    1 背景 在使用Qt的控件时 xff0c 我们大概率会使用Qt的信号与槽 xff08 signal slot xff09 的机制来实现自己的UI交互逻辑 由于Qt内置控件的信号种类是有限的 xff0c 我们常常会遇到如下窘境 xff1a 以
  • FreeRTOS学习-队列管理

    1 简介 在FreeRTOS中 xff0c 提供了多种任务间通讯的机制 xff0c 包括消息队列 信号量和互斥锁 事件组 任务通知 xff0c 他们的总体特征如下图所示 xff1a 从图中可以看出 xff0c 消息队列 信号量和互斥锁 事件
  • Qt/C++ 如何删除QListWidget的指定项

    1 简介 QListWidget是Qt中 xff0c 用于展示列表类型数据的常用控件 它提供了一个类似于QListView的列表视图 xff0c 但是具有用于添加和删除项的接口 QListWidget使用一个内部模型来管理列表中的每个QLi
  • C++ std::result_of/std::invoke_result的简明指南

    1 简介 在C 43 43 中 xff0c 有时我们需要获取函数或可调用对象的返回值类型 xff0c 以便进行后续的操作 xff0c 在泛型编程中很常用 xff0c 特别是当不同的参数集的结果类型不同时 在早期的C 43 43 版本中 xf
  • FreeRTOS学习-任务通知(Task Notification)

    1 简介 任务通知本质上就是一种进程间通信机制 之前的文章介绍的消息队列 事件组 信号量等都是一种间接的通信方式 xff0c 而任务通知则是更加直接的方式 xff0c 允许两个任务 xff08 或中断和任务 xff09 之间直接通信 2 任
  • 异常行为分析模型设计

    本文针对异常访问现状及问题进行简要描述 xff0c 在此基础上提出基于一元线性回归的最小二乘法异常访问分析模型 xff0c 通过该模型解决了异常访问中时间与访问间相关性问题 异常访问是指网络行为偏离正常范围的访问情况 异常访问包含多种场景
  • 无人机PX4学习(1)

    内容源自 xff1a PX4飞控用户手册 链接 xff1a https docs px4 io master en Basic concepts Drone an unmanned 34 robotic 34 vehicle that ca
  • 无人机PX4学习(2)

    内容源自 xff1a PX4飞控用户手册 链接 xff1a https docs px4 io master en getting started flight controller selection html Flight Contro
  • 无人机PX4学习(3)

    内容源自 xff1a PX4飞控用户手册 链接 xff1a PX4 Flight Modes Overview PX4 User Guide Flight Mode Section RC或GCS上可以切换飞行模式 xff0c 但有些模式有限
  • 两篇论文入坑AIOps异常检测

    AIOps简介 以下部分内容来源于清华大学裴丹教授发表在 中国计算机学会通讯 第13卷第12期的专栏 基于机器学习的智能运维 我们都知道 xff0c 当代社会生活中的大型软硬件系统为了确保能够安全 可靠地运行 xff0c 需要有专业的运维人
  • python代码,两个4*4旋转矩阵之间的位姿变化,相对旋转矩阵

    python代码 xff0c 两个4 4旋转矩阵之间的位姿变化 xff0c 也就是求两个旋转矩阵之间的相对旋转矩阵 import numpy as np def get transform matrix rot mat1 rot mat2
  • crazyS中给firefly飞机添加两个相机

    firefly飞机中原本只有一个相机 xff0c 由于项目需要一个飞机去识别前方两架飞机 xff0c 因此需要增加一个相机 在rotors descriptioin gt urdf gt mav with vi sensor中可以看到此处
  • gitee(码云)和gitHub的区别

    1 gitee与gitHub概念 xff1f Gitee xff08 码云 xff09 是开源中国社区推出的代码托管协作开发平台 xff0c 支持Git和SVN xff0c 提供免费的私有仓库托管 Gitee专为开发者提供稳定 高效 安全的
  • 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    郭孟琦 43 原创作品转载请注明出处 43 Linux内核分析 MOOC课程http mooc study 163 com course USTC 1000029000 首先我选择的系统调用是122号系统调用 uname 简单的介绍一下un
  • 局域网共享文件夹加密(100%成功版本)

    文章目录 一 创建新用户1 右键此电脑选择管理2 点击本地用户和组3 点击用户A 右键空白区域选择创建新用户B 设置用户名和密码C 取消勾选用户下次登录时须更改密码D 勾选用户不能更改密码和密码永不过期 二 创建共享文件夹1 创建一个新文件
  • STM32PWM--基于HAL库(第十三届蓝桥杯嵌入式模拟题)

    文章目录 前言一 CubeMX配置 第十三届模拟题完整版 二 代码相关定义 声明1 函数声明2 宏定义3 变量定义 三 主要函数1 按键扫描2 配置模式3 LCD显示4 频率检测 TIM2输入捕获中断函数 5 PWM输出 TIM3 6 Ma
  • STM32综合-基于HAL库(第十二届蓝桥杯嵌入式省赛)

    文章目录 前言一 CubeMX配置 第十二届省赛完整版 二 代码相关定义 声明1 函数声明2 宏定义3 变量定义 三 主要函数1 按键扫描2 串口接收中断 定时器中断 接收 3 数据解析4 判定数据正误5 数据更新6 结算7 Main函数