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

目录

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);//常用函数1HAL_Delay(100);//常用函数2HAL_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为是否是第一次上电(新板)的标志位1HAL_Delay(20);EEPROM_Write(0xab,1);//我们设定0xab为是否是第一次上电(新板)的标志位2HAL_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.mzph.cn/news/690653.shtml

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

相关文章

Gitlab CI/CD docker命令报错:/usr/bin/bash: line 136: docker:command not found

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

深入实战:ElasticSearch的Rest API与迭代器模式在高效查询中的应用

在我们公司&#xff0c;大多数Java开发工程师在项目中都有使用Elasticsearch的经验。通常&#xff0c;他们会通过引入第三方工具包或使用Elasticsearch Client等方式来进行数据查询。然而&#xff0c;当涉及到基于Elasticsearch Rest API的/_sql?formatjson接口时&#xff0c;…

2 物理层(三):数据传输的方式,同步传输和异步传输

目录 1 数据的传输方式1.1 并行传输1.2 串行传输 2 同步传输和异步传输2.1 同步传输2.2 异步传输2.3 同步和异步传输对比 1 数据的传输方式 在数据通信中&#xff0c;数据传输方式有并行传输和串行传输两种 1.1 并行传输 定义&#xff1a;并行传输是指数据以成组的方式在多个…

NC 输出模板自定义变量使用加减乘除余等公式计算时无法显示结果的问题处理办法

NC 输出模板自定义变量使用加减乘除余等公式计算时无法显示结果的问题处理办法 比如&#xff0c;求两个字段的差&#xff0c;如果这样写&#xff0c;模板打印输出的时候&#xff0c;是不会显示有值的&#xff1a; sub(vouchercreditamount, voucherdebitamount) 或者 voucherc…

picker选择器-年月日选择

从底部弹起的滚动选择器。支持五种选择器&#xff0c;通过mode来区分&#xff0c;分别是普通选择器&#xff0c;多列选择器&#xff0c;时间选择器&#xff0c;日期选择器&#xff0c;省市区选择器&#xff0c;默认是普通选择器。 学习一下日期选择器 平台差异说明 日期选择默…

K8s进阶之路-控制器无状态服务:

RC/RS/Deployment 控制器 deployment无状态&#xff08;最常用&#xff09;&#xff1a; nginx和Apache statefulset有状态&#xff1a; mysql和redis damonset初始化 job一次性任务 cronjob任务计划 1无状态&#xff1a;不会对本地环境产生依赖如&#xff1a;nginx和Apache …

Kubernetes基础(二十二)-k8s持久化存储详解

1 volume 1.1 介绍 在容器中的磁盘文件是短暂的&#xff0c;当容器崩溃时&#xff0c;Kubelet会重新启动容器&#xff0c;但容器运行时产生的数据文件都将会丢失&#xff0c;之后容器会以最干净的状态启动。另外&#xff0c;当一个Pod运行多个容器时&#xff0c;各个容器可能…

新版Java面试专题视频教程——框架篇

新版Java面试专题视频教程——框架篇 框架篇 01-框架篇介绍02-Spring-单例bean是线程安全的吗03-Spring-AOP相关面试题04-Spring-事务失效的场景05-Spring-bean的生命周期5.1 BeanDefinition 06-Spring-bean的循环依赖(循环引用)6.1 一般对象的循环依…

【C++】类与对象的项目实践 — 日期管理工具

类与对象的实践 项目背景项目需求项目实现1 日期结构设计2 构造函数2.1 全缺省构造函数2.2 拷贝构造函数2.3 析构函数 3 赋值运算符重载3.1 重载3.2 重载重载前置 和 后置 4 关系操作符重载5 工具方法5.1 计算日期差5.2 日期转换为字符串5.3 通过字符串构建对象 完整源代码Dat…

云数贸云生活中心:用云生活理念引领社会和谐发展

在数字经济的浪潮下&#xff0c;云数贸云生活中心不仅在科技进步与文明程度上作出了积极贡献&#xff0c;更在推动社会和谐、承担企业社会责任方面展现出了模范作用。通过与“草根互助爱心社区”的紧密合作&#xff0c;云数贸云生活中心正致力于构建一个更加和谐、互助的社会环…

socket通信 smallchat简介

文章目录 前言一、socket的基本操作(1) socket()函数(2) bind()函数(3) listen()、connect()函数(4) accept()函数(5) read()、write()等函数(6) close()函数 二、smallchat代码流程smallchat-server.csmallchat-client.cchatlib.c 参考资料 前言 本文介绍了socket通信的相关A…

六、图像的几何变换

文章目录 前言一、镜像变换二、缩放变换 前言 在计算机视觉中&#xff0c;图像几何变换是指对图像进行平移、旋转、缩放、仿射变换和镜像变换等操作&#xff0c;以改变图像的位置、尺寸、形状或视角&#xff0c;而不改变图像的内容。这些变换在图像处理、模式识别、机器人视觉…

更改WordPress作者存档链接author和用户名插件Change Author Link Structure

WordPress作者存档链接默认情况为/author/Administrator&#xff08;用户名&#xff09;&#xff0c;为了防止用户名泄露&#xff0c;我们可以将其改为/author/1&#xff08;用户ID&#xff09;&#xff0c;具体操作可参考『如何将WordPress作者存档链接中的用户名改为昵称或ID…

猪圈Pigsty-PG私有RDS集群搭建教程

博客 https://songxwn.com/Pigsty-PG-RDS/ 简介 Pigsty 是一个更好的本地自建且开源 RDS for PostgreSQL 替代&#xff0c;具有以下特点&#xff1a; 开箱即用的 PostgreSQL 发行版&#xff0c;深度整合地理、时序、分布式、图、向量、分词、AI等 150 余个扩展插件&#xff…

文件IO的lseek以及目录IO

文件IO之 lseek: 1. lseek off_t lseek(int fd, off_t offset, int whence); 功能: 重新设定文件描述符的偏移量 参数: fd:文件描述符 offset:偏移量 whence: SEEK_SET 文件开头 …

基于scrapy框架的单机爬虫与分布式爬虫

我们知道&#xff0c;对于scrapy框架来说&#xff0c;不仅可以单机构建复杂的爬虫项目&#xff0c;还可以通过简单的修改&#xff0c;将单机版爬虫改为分布式的&#xff0c;大大提高爬取效率。下面我就以一个简单的爬虫案例&#xff0c;介绍一下如何构建一个单机版的爬虫&#…

更快找到远程/自由工作的网站

不要使用Fiver或Upwork。 它们已经饱和了。 下面是10个更快找到远程/自由工作的网站&#xff1a; 1. Toptal 这个网站专门为熟练的自由职业者提供远程工作机会&#xff0c;如Shopify和Priceline等一流公司。 他们只接受软件开发、设计和金融等领域的顶级3%自由职业者。 htt…

2024-02-19(Flume)

1.flume中拦截器的作用&#xff1a;个人认为就是修改或者删除事件中的信息&#xff08;处理一下事件&#xff09;。 2.一些拦截器 Host Interceptor&#xff0c;Timestamp Interceptor&#xff0c;Static Interceptor&#xff0c;UUID Interceptor&#xff0c;Search and Rep…

C++集群聊天服务器 nginx+redis安装 笔记 (中)

一、nginx安装 nginx: download 下载nginx安装包 hehedalinux:~/package$ tar -zvxf nginx-1.24.0.tar.gz nginx-1.24.0/ nginx-1.24.0/auto/ nginx-1.24.0/conf/ nginx-1.24.0/contrib/ nginx-1.24.0/src/ nginx-1.24.0/configure nginx-1.24.0/LICENSE nginx-1.24.0/README…

PLC远程监控在制药行业的应用

PLC远程监控在制药行业的应用 制药行业是一个需要高度控制和精确性的行业&#xff0c;而PLC远程监控技术正是这种需求的完美解决方案。PLC远程监控技术是指将传感器、执行器和其他设备连接到PLC系统中&#xff0c;并使用网络和远程访问技术实现对设备的远程监控和控制。下面我…