蓝桥杯嵌入式STM32G431RBT6知识点(主观题部分)

news2024/12/28 19:55:59

目录

1  前置准备

1.1 Keil

1.1.1  编译器版本及微库

1.1.2  添加官方提供的LCD及I2C文件

1.2  CubeMX

1.2.1   时钟树

1.2.2   其他

1.2.3  明确CubeMX路径,放置芯片包

2  GPIO

2.1  实验1:LED1-LED8循环亮灭

​编辑

2.2  实验2:按键控制LED亮灭(检测电平法)

2.3  实验3:按键控制LED亮灭(外部中断法)

 2.4  实验4:蜂鸣器

2.5  实验5:按键消抖

2.6  实验6:长按短按

2.7  实验7:双击

2.8  实验8: 长按双击综合

3  ADC/DAC

3.1  实验1:获取电位器引脚的电压

 3.2  实验2:设定双引脚电压并读取

4  I2C EEPROM

4.1  实验1:EEPROM的读写+浮点数的处理

4.2  实验2:大位数读取

4.3  实验3:EEPROM掉电不丢失

4.4  实验4:第一次上电问题

 4.5  实验5:MCP4017可编程电阻

5  UART/USART

5.1  实验1:轮询收发

 5.2  实验2:中断收发

5.3  实验3:中断回调函数

5.4  字符串问题注意

 5.5  实验4:发送指定格式的字符串并从字符串中提取指定信息

5.6  实验5:DMA及几种收发方式的分析

5.7  实验6:串口的不定长收发(DMA+空闲中断)

6  TIM

6.1  实验1:延时

6.2  实验2:PWM输出(控制蜂鸣器)

6.3  实验3:检测555信号发生器信号频率和占空比

 6.4  实验4:检测自己输出的PWM频率和占空比(上升沿中断)

6.5  实验5:检测自己输出的PWM频率和占空比(PWM中断)

7  RTC

7.1  实验1:显示年月日时分秒

7.2  实验2:秒中断

7.3  实验3:闹钟中断


1  前置准备

1.1 Keil

1.1.1  编译器版本及微库

编译器版本调整至version 5,勾选Micro LIB

1.1.2  添加官方提供的LCD及I2C文件

这五个文件是需要添加进自己的工程中的

这个是官方比赛提供的数据包,有关I2C的文件从2中提取,有关LCD的文件从5中提取(.c文件在Src中,.h文件在Inc中)

1.2  CubeMX

1.2.1   时钟树

1.2.2   其他

 

1.2.3  明确CubeMX路径,放置芯片包

2  GPIO

2.1  实验1:LED1-LED8循环亮灭

 在最小系统原理图中找到LED1-LED8对应的引脚是PC8-PC15,那么我们在CubeMX中将这几个引脚配置成GPIO_Output即可

while内的代码:

	    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);//常用函数1
		HAL_Delay(100);//常用函数2
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_SET);
		HAL_Delay(100);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_SET);
		HAL_Delay(100);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_SET);
		HAL_Delay(100);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_12,GPIO_PIN_SET);
		HAL_Delay(100);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_12,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
		HAL_Delay(100);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);
		HAL_Delay(100);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);
		HAL_Delay(100);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);

2.2  实验2:按键控制LED亮灭(检测电平法)

 参考按键的引脚,同时别忘了把按键对应的引脚调整为上拉输入(起始高电平)

while内代码:

        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
		{
			HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
			HAL_Delay(500);
			HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
		}

2.3  实验3:按键控制LED亮灭(外部中断法)

找到按键对应引脚:

 打开外部中断:

/* USER CODE BEGIN PFP */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN)
{
	if(GPIO_PIN==GPIO_PIN_0)
	{
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);
	}
}
/* USER CODE END PFP */

 2.4  实验4:蜂鸣器

当PB3为高电平时,二极管断开,所以蜂鸣器路; 当PB3为低电平时,二极管导通,所以蜂鸣器路短路

只要配置好PB3的GPIO,就能轻松使用,这里不再用代码解释

2.5  实验5:按键消抖

按键按下和放下的过程中会出现抖动,进而出现高低电平的交替,我们通过扫描两次按键的情况来具体判断情况

1.如果第一次扫描为高电平,那么按键没有被按下

2.如果第一次扫描为低电平,第二次扫描为高电平,那么认为是抖动,不计入成功按键

3.如果两次扫描均为低电平,成功按键

两次扫描的间隔用定时器中断来做

芯片信号频率为80MHz,分频系数设置为8000-1,计数器溢出值设置为100-1,那么定时器溢出时间为10ms

/* USER CODE BEGIN PTD */
char buf[20];
struct keys{
	int step;
	int state;
}key[4];
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM1)
	{
		key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		for(int i=0;i<4;i++)
		{
			switch(key[i].step)
			{
				case 0:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						key[i].step=1;
					}
				}
				break;
				case 1:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						key[i].step=2;
						sprintf(buf,"%d",i);
						LCD_DisplayStringLine(Line4,(uint8_t*)buf);
					}
					else
					{
						key[i].step=0;
					}
				}
				break;
				case 2:
				{
					if(key[i].state==GPIO_PIN_SET)
					{
						key[i].step=0;
					}
				}
				break;
			}
			
		}
	}
}
/* USER CODE END PFP */
 /* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	I2CInit();
	HAL_TIM_Base_Start_IT(&htim1);
	
  /* USER CODE END 2 */

2.6  实验6:长按短按

1.如果第一次扫描为高电平,则没有按键

2.如果第一次扫描为低电平,第二次扫描为高电平,那么认为是抖动,不计入成功按键

3.如果两次扫描均为低电平,成功按键,如果按键时间大于700ms,则视为长按,若小于700ms,则视为短按

/* USER CODE BEGIN PTD */
char buf[20];
struct keys{
	int step;
	int state;
}key[4];
int a=-1;
int cnt;
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM1)
	{
		key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		for(int i=0;i<4;i++)
		{
			switch(key[i].step)
			{
				case 0:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						key[i].step=1;
						cnt=0;
					}
				}
				break;
				case 1:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						key[i].step=2;
                        sprintf(buf,"SINGLE");
						LCD_ClearLine(Line4);
						LCD_DisplayStringLine(Line4,(uint8_t*)buf);
					}
					else
					{
						key[i].step=0;
					}
				}
				break;
				case 2:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						if(i==a&&cnt>70)
						{
								sprintf(buf,"%d %d",i,cnt);
                                LCD_ClearLine(Line4);
								LCD_DisplayStringLine(Line4,(uint8_t*)buf);
						}
						else
						{
							a=i;
						}
						cnt++;
					}
					else
					{
						key[i].step=0;
					}
				}
				break;
			}
			
		}
	}
}
/* USER CODE END PFP */

2.7  实验7:双击

1.如果第一次能够被视为成功按键,那么计时开始

2.如果两次成功按键的间隔小于700ms,视为双击成功

/* USER CODE BEGIN PTD */
char buf[20];
struct keys{
	int step;
	int state;
	int cnt;
}key[4];
int a=-1;
int cnt;
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM1)
	{
		key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		for(int i=0;i<4;i++)
		{
			switch(key[i].step)
			{
				case 0:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						key[i].step=1;
					}
				}
				break;
				case 1:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
                        sprintf(buf,"SINGLE");
						LCD_ClearLine(Line4);
						LCD_DisplayStringLine(Line4,(uint8_t*)buf);						
                        if(i==a)
						{
								if(key[i].cnt<70)
								{
									sprintf(buf,"%d %d",i,key[i].cnt);
									LCD_ClearLine(Line4);
									LCD_DisplayStringLine(Line4,(uint8_t*)buf);
									key[i].step=0;
									a=-1;
								}
								key[0].cnt=0;
								key[1].cnt=0;
								key[2].cnt=0;
								key[3].cnt=0;
						}
						else
						{
							a=i;
						}
						key[i].step=2;
					}
					else
					{
						key[i].step=0;
					}
				}
				break;
				case 2:
				{
					if(key[i].state==GPIO_PIN_SET)
					{
						key[i].step=0;
					}
				}
				break;
			}
			
		}
		key[a].cnt++;
	}
}
/* USER CODE END PFP */

2.8  实验8: 长按双击综合

其实不用写在一个定时器内,那样逻辑会比较复杂。我们可以开两个定时器,一个定时器检测长按,一个定时器检测双击

/* USER CODE BEGIN PTD */
char buf[20];
struct keys{
	int state;
	int step1;
	int step2;
	int double_time;
}key[4];
int a=-1,b=-1;
int cnt;
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM3)
	{
		key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		for(int i=0;i<4;i++)
		{
			switch(key[i].step2)
			{
				case 0:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						key[i].step2=1;
						cnt=0;
					}
				}
				break;
				case 1:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						key[i].step2=2;
						/*sprintf(buf,"SINGLE");
						LCD_ClearLine(Line4);
						LCD_DisplayStringLine(Line4,(uint8_t*)buf);*/
					}
					else
					{
						key[i].step2=0;
					}
				}
				break;
				case 2:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						if(i==b&&cnt>70)
						{
								sprintf(buf,"LONG:%d %d",i,cnt);
                LCD_ClearLine(Line4);
								LCD_DisplayStringLine(Line4,(uint8_t*)buf);
						}
						else
						{
							b=i;
						}
						cnt++;
					}
					else
					{
						key[i].step2=0;
					}
				}
				break;
			}
		}
	}
	if(htim->Instance==TIM1)
	{
		key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		for(int i=0;i<4;i++)
		{
			switch(key[i].step1)
			{
				case 0:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						key[i].step1=1;
					}
				}
				break;
				case 1:
				{
					if(key[i].state==GPIO_PIN_RESET)
					{
						sprintf(buf,"SINGLE");
						LCD_ClearLine(Line4);
						LCD_DisplayStringLine(Line4,(uint8_t*)buf);
						if(i==a)
						{
								if(key[i].double_time<70)
								{
									sprintf(buf,"DOUBLE:%d %d",i,key[i].double_time);
									LCD_ClearLine(Line4);
									LCD_DisplayStringLine(Line4,(uint8_t*)buf);
									key[i].step1=0;
									a=-1;
								}
								key[0].double_time=0;
								key[1].double_time=0;
								key[2].double_time=0;
								key[3].double_time=0;
						}
						else
						{
							a=i;
						}
						key[i].step1=2;
					}
					else
					{
						key[i].step1=0;
					}
				}
				break;
				case 2:
				{
					if(key[i].state==GPIO_PIN_SET)
					{
						key[i].step1=0;
					}
				}
				break;
			}
		}
		key[a].double_time++;
	}
}
/* USER CODE END PFP */
/* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Blue);
	HAL_TIM_Base_Start_IT(&htim1);
	HAL_TIM_Base_Start_IT(&htim3);
  /* USER CODE END 2 */

3  ADC/DAC

3.1  实验1:获取电位器引脚的电压

我们想获取两个电位器的电压:

这里以PB12为例: 

/* USER CODE BEGIN PTD */
char buf[20];
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
double getADC()
{
	HAL_ADC_Start(&hadc1);
	return HAL_ADC_GetValue(&hadc1)*3.3/4096;
}
/* USER CODE END PFP */
/* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	
  /* USER CODE END 2 */
/* USER CODE BEGIN 3 */
	sprintf(buf,"%.3lf",getADC());
	LCD_ClearLine(Line4);
	LCD_DisplayStringLine(Line4,(uint8_t*)buf);
	HAL_Delay(1000);
  }
  /* USER CODE END 3 */

转动电位器R38可观察到电压发生明显变化 

 3.2  实验2:设定双引脚电压并读取

 

这里我们发现测量ADC的两个引脚均在ADC1上,所以获取电压会有先后之分

 采样时间调长,采样准确些,这里我们看到优先采集PA4,再采集PA3

/* USER CODE BEGIN PFP */
void setDAC()
{
	HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
	HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);
	HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,0,1.1*4096/3.3);
	HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,0,2.2*4096/3.3);
}
double getADC()
{
	HAL_ADC_Start(&hadc2);
	return HAL_ADC_GetValue(&hadc2)*3.3/4096;
}
/* USER CODE END PFP */
 /* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	setDAC();
  /* USER CODE END 2 */
 /* USER CODE BEGIN 3 */
	sprintf(buf,"%.3lf",getADC());
	LCD_ClearLine(Line4);
	LCD_DisplayStringLine(Line4,(uint8_t*)buf);
	HAL_Delay(1);//需要延时
	sprintf(buf,"%.3lf",getADC());
	LCD_ClearLine(Line5);
	LCD_DisplayStringLine(Line5,(uint8_t*)buf);
	HAL_Delay(1000);
  }
  /* USER CODE END 3 */

4  I2C EEPROM

4.1  实验1:EEPROM的读写+浮点数的处理

对于EEPROM读写的函数我们有固定模板:

uint8_t EEPROM_Read(uint8_t addr)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	uint8_t temp=I2CReceiveByte();
	I2CWaitAck();
	I2CStop();
	return temp;
}
void EEPROM_Write(uint8_t addr,uint8_t info)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CSendByte(info);
	I2CWaitAck();
	I2CStop();
}

借用3.2的实验数据进行读写,建议EEPROM不要写在while内,EEPROM的读写是有寿命的,每次读写都要延时一下:

/* USER CODE BEGIN PTD */
char buf[20];
double V1[5],V2[5];
uint8_t addr,res1,res2,res3,res4;
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
void setDAC()
{
	HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
	HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);
	HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,0,1.1*4096/3.3);
	HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,0,2.2*4096/3.3);
}
double getADC()
{
	HAL_ADC_Start(&hadc2);
	return HAL_ADC_GetValue(&hadc2)*3.3/4096;
}
uint8_t EEPROM_Read(uint8_t addr)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	uint8_t temp=I2CReceiveByte();
	I2CWaitAck();
	I2CStop();
	return temp;
}
void EEPROM_Write(uint8_t addr,uint8_t info)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CSendByte(info);
	I2CWaitAck();
	I2CStop();
}
/* USER CODE END PFP *//* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	I2CInit();
	setDAC();
	HAL_Delay(1);
	for(int i=0;i<5;i++)
	{
		V1[i]=getADC();
		HAL_Delay(1);
		V2[i]=getADC();
		HAL_Delay(1);
	}
	for(int i=0;i<5;i++)
	{
		EEPROM_Write(addr++,(uint8_t)V1[i]);
		HAL_Delay(50);
		EEPROM_Write(addr++,(V1[i]-(uint8_t)V1[i])*100);
		HAL_Delay(50);
		EEPROM_Write(addr++,(uint8_t)V2[i]);
		HAL_Delay(50);
		EEPROM_Write(addr++,(V2[i]-(uint8_t)V2[i])*100);
		HAL_Delay(50);
	}
	addr=0;
	for(int i=0;i<5;i++)
	{
		res1=EEPROM_Read(addr++);
		HAL_Delay(50);
		res2=EEPROM_Read(addr++);
		HAL_Delay(50);
		res3=EEPROM_Read(addr++);
		HAL_Delay(50);
		res4=EEPROM_Read(addr++);
		HAL_Delay(50);
		sprintf(buf,"%.2lf  %.2lf",res1+(double)res2/100,res3+(double)res4/100);
		LCD_DisplayStringLine(Line4,(uint8_t*)buf);
	}
  /* USER CODE END 2 */

4.2  实验2:大位数读取

8位范围:0-255

16位范围:0-65535

24位范围:0-16777215

32位范围:0-4294967296

这里以到24位为例

/* USER CODE BEGIN PTD */
char buf[20];
int num[10]={105798,367842,56674,4,256,8917,56565,34343,1025,788};
uint8_t temp1,temp2,temp3,res1,res2,res3;
uint8_t addr;
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
uint8_t EEPROM_Read(uint8_t addr)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	uint8_t temp=I2CReceiveByte();
	I2CWaitAck();
	I2CStop();
	return temp;
}
void EEPROM_Write(uint8_t addr,uint8_t info)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CSendByte(info);
	I2CWaitAck();
	I2CStop();
}
/* USER CODE END PFP */
/* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	I2CInit();
	for(int i=0;i<10;i++)
	{
		temp1=num[i]&0xFF;
		temp2=((num[i]-temp1)>>8)&0xFF;
		temp3=((num[i]-temp1-(temp2<<8))>>16)&0xFF;
		EEPROM_Write(addr++,temp1);
		HAL_Delay(50);
		EEPROM_Write(addr++,temp2);
		HAL_Delay(50);
		EEPROM_Write(addr++,temp3);
		HAL_Delay(50);
	}
	addr=0;
	for(int i=0;i<10;i++)
	{
		res1=EEPROM_Read(addr++);
		HAL_Delay(50);
		res2=EEPROM_Read(addr++);
		HAL_Delay(50);
		res3=EEPROM_Read(addr++);
		HAL_Delay(50);
		sprintf(buf,"%d",res1+(res2<<8)+(res3<<16));
		LCD_ClearLine(Line4);
		LCD_DisplayStringLine(Line4,(uint8_t*)buf);
		HAL_Delay(1000);
	}
  /* USER CODE END 2 */

4.3  实验3:EEPROM掉电不丢失

去掉上面程序的写的部分,重新烧录即可验证

4.4  实验4:第一次上电问题

我们以这道题为例:如果我们将程序烧录到新板时,EEPROM的值是不确定的,而题目要求初次上电就要能读取相关值。所以我们需要判断板子是否是第一次上电,然后做出相关步骤

if(EEPROM_Read(0xaa)!=1&&EEPROM_Read(0xab)!=1)
{
	EEPROM_Write(0xaa,1);//我们设定0xaa为是否是第一次上电(新板)的标志位1
	HAL_Delay(20);
	EEPROM_Write(0xab,1);//我们设定0xab为是否是第一次上电(新板)的标志位2
	HAL_Delay(20);
	EEPROM_Write(0xa0,30);
	HAL_Delay(20);
	EEPROM_Write(0xa1,50);
	HAL_Delay(20);
	EEPROM_Write(0xa2,70);
	HAL_Delay(20);
}

 4.5  实验5:MCP4017可编程电阻

原理:

Rs为单个电阻阻值;RWS为总阻值,与R17串联,两者对VDD电压进行分压,可以通过测量PB14的电压判断可编程电阻的阻值。

MCP4017的默认总阻值为100kΩ ,对应0-127个档位,当寄存器为0时,阻值为0;当寄存器为0x7F时阻值为100kΩ

R = 787.4 * readresistor


V=3.3*\tfrac{R}{R+10}

模板:

void RWrite(uint8_t value)
{
	I2CStart();
	I2CSendByte(0x5e);
	I2CWaitAck();
	I2CSendByte(value);
	I2CWaitAck();
	I2CStop();
}
uint8_t RRead(void)
{
	uint8_t value;
	I2CStart();
	I2CSendByte(0x5F);
	I2CWaitAck();
	
	value = I2CReceiveByte();
	I2CSendNotAck();
	I2CStop();
	return value;
}

检测从0-127,PB14的电压

/* USER CODE BEGIN PFP */
void RWrite(uint8_t value)
{
	I2CStart();
	I2CSendByte(0x5e);
	I2CWaitAck();
	I2CSendByte(value);
	I2CWaitAck();
	I2CStop();
}
uint8_t RRead(void)
{
	uint8_t value;
	I2CStart();
	I2CSendByte(0x5F);
	I2CWaitAck();
	
	value = I2CReceiveByte();
	I2CSendNotAck();
	I2CStop();
	return value;
}
double getADC()
{
	HAL_ADC_Start(&hadc1);
	return HAL_ADC_GetValue(&hadc1)*3.3/4096;
}
/* USER CODE END PFP */
/* USER CODE BEGIN WHILE */
	for(int i=0;i<128;i++)
	{
		RWrite(i);
		HAL_Delay(20);
		sprintf(buf,"%d %lf",RRead(),getADC());
		LCD_ClearLine(Line4);
		LCD_DisplayStringLine(Line4,(uint8_t*)buf);
		HAL_Delay(1000);
	}
	
  while (1)
  {
    /* USER CODE END WHILE */

5  UART/USART

CubeMX默认的引脚是不对的,所以别忘了更改引脚

5.1  实验1:轮询收发

HAL_UART_Receive:接收不完指定数量的字符不会进行下一步,设置的等待时间一般为无限大

 /* USER CODE BEGIN WHILE */
	HAL_UART_Receive(&huart1,(uint8_t*)buf,5,0xFFFF);//接收不完指定数量的字符不会进行下一步
	HAL_UART_Transmit(&huart1,(uint8_t*)buf,5,0xFFFF);
	
  while (1)
  {
    /* USER CODE END WHILE */

 5.2  实验2:中断收发

CubeMX别忘打开串口中断

HAL_UART_Receive_IT:不会等待,最多收指定个数,收几个无所谓,但是只有收满了才会进入中断回调函数

下面这个程序如果不写在while里是不会收到除了空白以外的任何字符的,因为只执行一次的话Receive函数已经过了,不会再执行

while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,5);//不会等待,最多收5个,收几个无所谓
		HAL_UART_Transmit_IT(&huart1,(uint8_t*)buf,5);
		
  }
  /* USER CODE END 3 */

5.3  实验3:中断回调函数

/* USER CODE BEGIN PFP */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==USART1)
	{
		HAL_UART_Transmit_IT(&huart1,(uint8_t*)buf,5);
		HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,5);
	}
}
/* USER CODE END PFP */
 /* USER CODE BEGIN WHILE */
	HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,5);//不会等待,最多收5个,收几个无所谓
	
  while (1)
  {
    /* USER CODE END WHILE */

5.4  字符串问题注意

每个字符串结尾都有\r\n占两个位置,而且如果我们在串口助手里勾选发送新行后,每次发送的字符串后都带\r\n

 5.5  实验4:发送指定格式的字符串并从字符串中提取指定信息

比如我们想发送时间,指定格式为时:分:秒,我们想从串口收到的字符串中提取到时,分,秒三个信息

这里用到sscanf函数来提取信息

当然也可以根据实际情况单个单个提取,不过会略显复杂

/* USER CODE BEGIN PTD */
char buf[20];
int hour,min,second;
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==USART1)
	{
		sscanf(buf,"%d:%d:%d",&hour,&min,&second);
		sprintf(buf,"hour:%d",hour);
		LCD_ClearLine(Line2);
		LCD_ClearLine(Line4);
		LCD_ClearLine(Line6);
		LCD_DisplayStringLine(Line2,(uint8_t*)buf);
		sprintf(buf,"min:%d",min);
		LCD_DisplayStringLine(Line4,(uint8_t*)buf);
		sprintf(buf,"second:%d",second);
		LCD_DisplayStringLine(Line6,(uint8_t*)buf);
		memset(buf,0,sizeof(buf));
		HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,8);
	}
}
/* USER CODE END PFP */
/* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	I2CInit();
	
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,8);
  while (1)
  {
    /* USER CODE END WHILE */

5.6  实验5:DMA及几种收发方式的分析

打开DMA: 

 打开中断:

DMA是默认打开中断的

 因为DMA是不占用CPU的,我们可以尽量使用DMA来提高效率

这里我们发现收到的数据不完整,然后我们延时一下,这次数据完整

/* USER CODE BEGIN PTD */
char buf[20];
int hour,min,second;
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==USART1)
	{
		sscanf(buf,"%d:%d:%d",&hour,&min,&second);
		sprintf(buf,"hour:%d",hour);
		LCD_ClearLine(Line2);
		LCD_ClearLine(Line4);
		LCD_ClearLine(Line6);
		LCD_DisplayStringLine(Line2,(uint8_t*)buf);
		sprintf(buf,"min:%d",min);
		LCD_DisplayStringLine(Line4,(uint8_t*)buf);
		sprintf(buf,"second:%d",second);
		LCD_DisplayStringLine(Line6,(uint8_t*)buf);
		sprintf(buf,"%02d:%02d:%02d",hour,min,second);
		HAL_UART_Transmit_DMA(&huart1,(uint8_t*)buf,8);
		//int t=100000; while(t--);
		LCD_DisplayStringLine(Line8,(uint8_t*)buf);
		memset(buf,0,sizeof(buf));
		HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,8);
	}
}
/* USER CODE END PFP */
/* USER CODE BEGIN WHILE */
	HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,8);
  while (1)
  {
    /* USER CODE END WHILE */

然后我们试图将Transmit一句改成IT,然后将USART1的中断优先级调整比DMA高,然后发现仍然可以接收到

但是如果我们全部改成IT,就不可取了,初步分析是中断回调函数的事件过多

所以我们尽量采取DMA的方式收发数据

5.7  实验6:串口的不定长收发(DMA+空闲中断)

其中中断服务函数需要到

 中找

 /* USER CODE BEGIN WHILE */
	HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,20);
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
  while (1)
  {
    /* USER CODE END WHILE */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)==SET)
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);
		HAL_UART_DMAStop(&huart1);
		len=20-__HAL_DMA_GET_COUNTER(huart1.hdmarx);
		HAL_UART_Transmit_DMA(&huart1,(uint8_t*)buf,len);
		HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,20);
	}
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

6  TIM

6.1  实验1:延时

while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		if(__HAL_TIM_GetCounter(&htim1)==10000)
		{
			HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);
			__HAL_TIM_SetCounter(&htim1,0);
		}
		
  }
  /* USER CODE END 3 */

6.2  实验2:PWM输出(控制蜂鸣器)

PWM原理如图所示: 

/* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	I2CInit();
	LED_Close();
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
	__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,5000);
  /* USER CODE END 2 */

6.3  实验3:检测555信号发生器信号频率和占空比

 分析:每次捕捉到上升沿我们就进入一次中断,这时我们就得到了一个信号周期的大小

现在我们已知定时器计一个数的时间,只要我们读取定时器计了多少数,就能通过公式:

信号周期=定时器计一个数的时间*定时器计数值

算出信号周期,进而算出信号频率

占空比的计算可以另外设置一个通道,根据占空比的定义:

占空比=一个周期内高电平的时间/一个周期

测量PA15引脚对应的555信号发生器:

注意:分频系数设置为80比较好,这样记一次数的时间比较短,测量比较精确,而且计数器不容易溢出

注意TIM2CHANNEL1对应的引脚设置成PA15

这里注意打印%的方法

/* USER CODE BEGIN PFP */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM2&&htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
	{
		cnt=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);
		cnt_down=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2);
		__HAL_TIM_SetCounter(&htim2,0);
		f=10000000/cnt;
		duty=1-(double)cnt_down/(double)cnt;
	}
}
/* USER CODE END PFP */
/* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	I2CInit();
	HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
	HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
  /* USER CODE END 2 */
 while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		sprintf(buf,"%dHz %.2lf%%",f,duty*100);
		LCD_DisplayStringLine(Line4,(uint8_t*)buf);
		HAL_Delay(1000);
		LCD_ClearLine(Line4);
  }
  /* USER CODE END 3 */

 6.4  实验4:检测自己输出的PWM频率和占空比(上升沿中断)

找到板子上能插杜邦线的两个引脚,一个引脚输出PWM,另一个引脚测量输入的PWM有关性质

我选择了PB15和PB11两个引脚,PB15用来产生PWM波,PB11用来测量PWM的有关性质

 PB15:

设置分频系数为8,计数器最大值为1000-1,所以PWM的频率为:

80MHz/8/1000=10000Hz

PB11:

分频系数为80-1,所以计数频率(1/计一个数的时间)为:

80MHz/80=1MHz

 set可以设置为在0-1000内的值,假如我们设置为300,那么占空比为30%

/* USER CODE BEGIN PTD */
int cnt,cnt_down,f;
double duty;
char buf[20];
int set;
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM2&&htim->Channel==HAL_TIM_ACTIVE_CHANNEL_4)
	{
		cnt=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_4);
		cnt_down=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_3);
		__HAL_TIM_SetCounter(&htim2,0);
		f=1000000/cnt;
		duty=1-(double)cnt_down/(double)cnt;
	}
}
/* USER CODE END PFP */
 /* USER CODE BEGIN 2 */
	HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_4);
	HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_3);
	HAL_TIM_PWM_Start(&htim15,TIM_CHANNEL_2);
	__HAL_TIM_SetCompare(&htim15,TIM_CHANNEL_2,set);
	LCD_Init();
	LCD_Clear(Blue);
  /* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		sprintf(buf,"%dHz %.4lf",f,duty);
		LCD_DisplayStringLine(Line2,(uint8_t*)buf);
		HAL_Delay(200);
  }
  /* USER CODE END 3 */

 最后测量得到频率为10204Hz,占空比为0.5102,和预估结果大致相同

6.5  实验5:检测自己输出的PWM频率和占空比(PWM中断)

 捕捉到上升沿中断其实跟PWM中断一样,都是上升沿开始时触发中断

我们打开PWM中断,使用PWM中断回调函数

/* USER CODE BEGIN PTD */
int cnt,cnt_down,f;
double duty;
char buf[20];
int set;
/* USER CODE END PTD */
/* USER CODE BEGIN PFP */
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM15&&htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2)
	{
		cnt=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_4);
		cnt_down=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_3);
		__HAL_TIM_SetCounter(&htim2,0);
		f=1000000/cnt;
		duty=1-(double)cnt_down/(double)cnt;
	}
}
/* USER CODE END PFP */
 /* USER CODE BEGIN 2 */
	HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_4);
	HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_3);
	HAL_TIM_PWM_Start_IT(&htim15,TIM_CHANNEL_2);
	__HAL_TIM_SetCompare(&htim15,TIM_CHANNEL_2,set);
	LCD_Init();
	LCD_Clear(Blue);
  /* USER CODE END 2 */
 /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		sprintf(buf,"%dHz %.4lf",f,duty);
		LCD_DisplayStringLine(Line2,(uint8_t*)buf);
		HAL_Delay(200);
  }
  /* USER CODE END 3 */

7  RTC

7.1  实验1:显示年月日时分秒

 

/* USER CODE BEGIN PTD */
char buf[20];
/* USER CODE END PTD */
/* USER CODE BEGIN PD */
RTC_TimeTypeDef TIME;
RTC_DateTypeDef DATE;
/* USER CODE END PD */
/* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		HAL_RTC_GetTime(&hrtc,&TIME,RTC_FORMAT_BIN);
		HAL_RTC_GetDate(&hrtc,&DATE,RTC_FORMAT_BIN);
		LCD_ClearLine(Line2);
		LCD_ClearLine(Line4);
		sprintf(buf,"%d-%d-%d",DATE.Year,DATE.Month,DATE.Date);
		LCD_DisplayStringLine(Line2,(uint8_t*)buf);
		sprintf(buf,"%d:%d:%d",TIME.Hours,TIME.Minutes,TIME.Seconds);
		LCD_DisplayStringLine(Line4,(uint8_t*)buf);
  }
  /* USER CODE END 3 */

7.2  实验2:秒中断

上面我们已经设置好RTC的频率为750MHz,按照如上系数配置,750K/125/6000=1Hz

1秒发生一次中断,而跟我们设定的闹钟时间无关

/* USER CODE BEGIN PTD */
char buf[20];
/* USER CODE END PTD */
/* USER CODE BEGIN PD */
RTC_TimeTypeDef TIME;
RTC_DateTypeDef DATE;
/* USER CODE END PD */
/* USER CODE BEGIN PFP */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
	HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_3);
}
/* USER CODE END PFP */
/* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		HAL_RTC_GetTime(&hrtc,&TIME,RTC_FORMAT_BIN);
		HAL_RTC_GetDate(&hrtc,&DATE,RTC_FORMAT_BIN);
		LCD_ClearLine(Line2);
		LCD_ClearLine(Line4);
		sprintf(buf,"%d-%d-%d",DATE.Year,DATE.Month,DATE.Date);
		LCD_DisplayStringLine(Line2,(uint8_t*)buf);
		sprintf(buf,"%d:%d:%d",TIME.Hours,TIME.Minutes,TIME.Seconds);
		LCD_DisplayStringLine(Line4,(uint8_t*)buf);
  }
  /* USER CODE END 3 */

7.3  实验3:闹钟中断

日期时分都不看,只看秒,如果闹钟设定的秒跟当前时间一样,则进入中断,自己需要编写的其它代码跟实验2一致

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1457500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#使用MiniExcel导入导出数据到Excel/CSV文件

MiniExcel简介 简单、高效避免OOM的.NET处理Excel查、写、填充数据工具。 目前主流框架大多需要将数据全载入到内存方便操作&#xff0c;但这会导致内存消耗问题&#xff0c;MiniExcel 尝试以 Stream 角度写底层算法逻辑&#xff0c;能让原本1000多MB占用降低到几MB&#xff…

提取游戏音频文件.bnk

提取游戏音频文件.bnk 什么是.bnk准备Wwise-Unpacker工具使用Wwise-Unpacker工具总结 什么是.bnk .bnk其实是一种对音频的加密方式&#xff0c;一个.bnk文件中通常包含了多个语音文件&#xff0c;一般可以使用Wwise-Unpacker来解码.bnk格式文件 准备Wwise-Unpacker工具 Wwis…

视频基础学习一——色立体、三原色以及像素

文章目录 前言一、什么是颜色1.色立体特征2.色立体模型 二、三原色和色立体1.三原色(RGB)2.RGB颜色叠加 三、像素和三原色总结 前言 本文的目的是为了梳理音视频基础相关的知识&#xff0c;有很多做流媒体、音视频相关的研发对于音视频的根本原理是不清楚的。博主也是查阅了相…

Linux 内存top命令详解

通过top命令可以监控当前机器的内存实时使用情况&#xff0c;该命令的参数解释如下&#xff1a; 第一行 15:30:14 —— 当前系统时间 up 1167 days, 5:02 —— 系统已经运行的时长&#xff0c;格式为时:分 1 users ——当前有1个用户登录系统 load average: 0.00, 0.01, 0.05…

Code Composer Studio (CCS) - 全局搜索功能

Code Composer Studio [CCS] - 全局搜索功能 1. Ctrl H&#xff0c;全局搜索功能References 1. Ctrl H&#xff0c;全局搜索功能 References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

小程序列表下拉刷新和加载更多

配置 在小程序的app.json中&#xff0c;检查window项目中是否已经加入了"enablePullDownRefresh": true&#xff0c;这个用来开启下拉刷新 "window": {"backgroundTextStyle": "light","navigationBarBackgroundColor": &q…

winform实现最小化至系统托盘

NotifyIcon类介绍 NotifyIcon 是 .NET中的一个类&#xff0c;它用于在系统托盘中显示图标。这个类在 System.Windows.Forms 命名空间下。 使用 NotifyIcon 类&#xff0c;你可以在系统托盘中创建一个图标&#xff0c;当用户点击或右键点击这个图标时&#xff0c;可以触发一些事…

GA-kmedoid 遗传算法优化K-medoids聚类

遗传算法优化K-medoids聚类是一种结合了遗传算法和K-medoids聚类算法的优化方法。遗传算法是一种基于自然选择和遗传机制的随机优化算法&#xff0c;它通过模拟生物进化过程中的遗传、交叉、变异等操作来寻找问题的最优解。而K-medoids聚类算法是一种基于划分的聚类方法&#x…

微服务-微服务Nacos配置中心

1.1 配置中心架构 1.2 Config Client源码分析 配置中心核心接口ConfigService public class ConfigServerDemo {public static void main(String[] args) throws NacosException, InterruptedException {String serverAddr "localhost";String dataId "naco…

2024 年 7 项值得学习的高收入技能

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要7项高收入技能6 个职业目标示例1. 晋升领导职务2.成为思想领袖3.致力于个人发展4.转向新的职业道路5.体验职业稳定性6.制定职业目标 简述概要 2023年已过&#xff0c;2024年已来&#xff0c;陆陆续续开始了复工&am…

面试经典150题 -- 链表 (总结)

总的地址 : 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 c链表总结 : 链表总结 -- 《数据结构》-- c/c-CSDN博客 141 . 环形链表 详细题解参考 : 141 . 环形链表-CSDN博客 这里给出慢双指针的代码 : /*** Defini…

【JAVA WEB】JS的应用

目录 猜数字 预期效果 涉及接口预览 代码实现 表白墙 预期效果 代码实现 代办事项 预期效果 代码实现 猜数字 预期效果 涉及接口预览 //当我们要获得文本框上输入的内容&#xff0c;可以通过.value获取 let guess_text document.querySelector(.guess_num) guess_…

社交商业策略:揭秘Facebook Shops的成功之道

随着数字化时代的不断发展&#xff0c;社交媒体已经成为了商业活动的重要平台之一。在这个趋势下&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;不仅仅是人们交流互动的场所&#xff0c;更成为了商家开展电子商务的重要渠道。其中&#xff0c;Facebook Shops…

MySQL中SQL语句的执行流程(高频考点)

文章目录 前言SQL语句的执行流程查询语句的执行流程更新语句的执行流程 总结 前言 昨天跟大家讲了MySQL的基础架构&#xff08;链接&#xff1a;MySQL的基础架构&#xff09;&#xff0c;今天讲一讲我们的高频面试题MySQL中SQL语句的执行流程。 建议看完 MySQL的基础架构 再来…

flutter 文件上传组件和大文件分片上传

文件分片上传 资料 https://www.cnblogs.com/caijinglong/p/11558389.html 使用分段上传来上传和复制对象 - Amazon Simple Storage Service 因为公司使用的是亚马逊的s3桶 下面是查阅资料获得的 亚马逊s3桶的文件上传分片 分段上分为三个步骤&#xff1a;开始上传、上传对…

【漏洞复现-通达OA】通达OA WHERE_STR 存在前台SQL注入漏洞

一、漏洞简介 通达OA(Office Anywhere网络智能办公系统)是由北京通达信科科技有限公司自主研发的协同办公自动化软件,是与中国企业管理实践相结合形成的综合管理办公平台。通达OA WHERE_STR存在前台SQL注入漏洞,攻击者可通过该漏洞获取数据库敏感信息。 二、影响版本 ●…

全网最详细的从0到1的turbo pnpm monorepo的前端工程化项目[vitePress篇]

全网最详细的从0到1的turbo pnpm monorepo的前端工程化项目[vitePress篇] 前言选型为什么选择VitePress安装VitePress运行优化默认UI使用自定义UI编辑自定义布局编写home页面组件编写page页面组件 结语 前言 一个好的工程化项目&#xff0c;必然有一个好的文档管理&#xff0c;…

【Go-Zero】goctl生成model层后报错Unresolved reference ‘ErrNotFound‘解决方案

【Go-Zero】goctl生成model层后报错Unresolved reference ErrNotFound’解决方案 大家好 我是寸铁&#x1f44a; 总结了一篇goctl生成model层后报错Unresolved reference ErrNotFound’报错解决方案的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 问题背景 大家好&#xff…

Sora 的工作原理

原文&#xff1a;How Sora Works (And What It Means) 作者&#xff1a; DAN SHIPPER OpenAI 的新型文本到视频模型为电影制作开启了新篇章 DALL-E 提供的插图。 让我们先明确一点&#xff0c;我们不会急急忙忙慌乱。我们不会预测乌托邦或预言灾难。我们要保持冷静并... 你…

java面试多线程篇

文章说明 在文章中对所有的面试题都进行了难易程度和出现频率的等级说明 星数越多代表权重越大&#xff0c;最多五颗星&#xff08;☆☆☆☆☆&#xff09; 最少一颗星&#xff08;☆&#xff09; 1.线程的基础知识 1.1 线程和进程的区别&#xff1f; 难易程度&#xff1a;☆☆…