> 文档中心 > STM32G431RB--基于HAL库(蓝桥杯嵌入式赛前梳理)

STM32G431RB--基于HAL库(蓝桥杯嵌入式赛前梳理)

文章目录

  • 前言
  • LED
  • KEY
  • I2C-EEPROM
  • LCD
  • RTC
  • ADC
  • USART(用mx配置时要修改引脚和GPIO时钟)!
  • TIM
  • PWM
  • 总结

前言

明天就进行蓝桥杯的比赛了,最后一天再重新梳理一下各个模块的使用和代码的编写。 如果各个模块的MX配置是根据我之前发的来的,那么这篇文章中的代码完全适用;如不是,原理部分也是相同的,代码部分适用,可以自行判断,作为一个参考。


LED

引脚:

PC8~PC15(LED1 ~ LED8)

1.控制LED灯亮灭时需要更改PD2引脚电平(先高后低)
2.如果不锁存无法保存数据
3.操作LCD时会影响LED
4.锁存:

HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);

5.与LCD发生冲突时使用数组解决

KEY

引脚:

PB0     ------> B0PB1     ------> B1PB2     ------> B2PA0     ------> B3

1.判断按键按下HAL_GPIO_ReadPin();
2.按键消抖5~10ms即可
3.whlie检测按键松开

I2C-EEPROM

1.写入字符串时通常是多个字节,需要多次调用字节写入函数,可以直接将函数写成多字节写入函数

void EEPROM_WriteBuff(uint8_t addr,uint8_t *sendBuff,uint32_t numByteToWrite)//EEPROM写数据{I2CStart();//开始通信 起始信号I2CSendByte(0xa0);//发送从设备地址 EEPROM地址为0XA0 最后一位数据为0方向为写入I2CWaitAck();//等待应答I2CSendByte(addr);//发送存储地址I2CWaitAck();//等待应答while(numByteToWrite--)//连续写入    { I2CSendByte(*sendBuff);//发送数据 I2CWaitAck();//等待应答 sendBuff++;//指针自增    }I2CStop();//结束通信 发送终止信号}void EEPROM_ReadBuff(uint8_t addr,uint8_t *readBuff,uint32_t numByteToRead)//EEPROM读数据{I2CStart();//开始通信 起始信号I2CSendByte(0xa0);//发送从设备地址 EEPROM地址为0XA0 最后一位数据为0方向为写入I2CWaitAck();//等待应答I2CSendByte(addr);//发送存储地址I2CWaitAck();//等待应答    I2CStart();//再次发送起始信号    I2CSendByte(0xa1);//发送从设备地址 EEPROM地址为0XA0 最后一位数据为1方向为读取    I2CWaitAck();//等待应答    while(numByteToRead--)//连续读取    { *readBuff=I2CReceiveByte();//读取一字节数据 if(numByteToRead==0)//如果读取结束 {     I2CSendNotAck();//主机发送非应答 } else//未结束 {     readBuff++;//指针自增     I2CSendAck();//主机发送应答信号 继续接收下一字节数据 }    }    I2CStop();//结束通信 发送终止信号}

​2.连续读写加延时等待,单片机工作频率远大于EEPROM工作频率,需等待数据完整保存或读出
3.EEPROM得存储的地址不能过大(从0开始用起就好了)
4.EEPROM设备地址为0XA0(写方向) 0XA1(读方向)
5.每写入一个字节数据都需要调用I2CWaitAck();//等待应答

LCD

​引脚:

PC8~PC15

1.使用前初始化LCD_Init();
2.每行有20单位长
3.有0~9共10行
4.每个字符高度为24,宽度为16
5.函数:

显示一行  LCD_DisplayStringLine(Line0,(unsigned char *)"      ");显示单个字符   LCD_DisplayChar(u8 Line, u16 Column, u8 Ascii);  (第一个参数为纵坐标,字符高度为24;第二个参数为横坐标,字符宽度为16;屏幕最右边横坐标为0,最坐边为19*16

RTC

1.配置:

A.使能时钟源B.使能日历C.开启闹钟D.开启中断E.配置初始化时间F.配置闹钟参数

2.结构体:

时间结构体RTC_TimeTypeDef日期结构体RTC_DateTypeDef闹钟结构体RTC_AlarmTypeDef

3.函数:

开启闹钟中断 HAL_RTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format);回调函数  HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc);读取时间  HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);读取日期  HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);设置时间  HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD);设置闹钟  HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm, RTC_FORMAT_BIN);

4.注意:
读完时间后要读日期时钟才能继续走

5.秒中断代码:

RTC_AlarmTypeDef sAlarm = {0};//将生成代码中的这一结构体定义为全局变量,并将生成的这一行代码删除void Get_Time()//获取当前时间{HAL_RTC_GetTime(&hrtc,&Now_Time,RTC_FORMAT_BIN);HAL_RTC_GetDate(&hrtc,&Now_Date,RTC_FORMAT_BIN);}void Show_Now_Time()//显示当前时间{char str[30];sprintf(str,"    T:%02d-%02d-%02d  ",Now_Time.Hours,Now_Time.Minutes,Now_Time.Seconds);LCD_DisplayStringLine(Line6,(unsigned char*)str);}void Set_Alarm() //设置下一秒的闹钟{sAlarm.AlarmTime.Seconds = Now_Time.Seconds+1;if(sAlarm.AlarmTime.Seconds==60)sAlarm.AlarmTime.Seconds=0;HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm, RTC_FORMAT_BIN);}void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)//RTC闹钟中断 时间显示(每秒) {Get_Time();//获取发生中断的时间Set_Alarm();//设定下一秒的闹钟Show_Now_Time();//显示当前时间}

ADC

引脚:

PB1     ------> ADC1_IN12PB12   ------> ADC1_IN11PB15   ------> ADC2_IN15

1.AD转换值上限为4096
2.不需要开启中断
3.配置

A.使用MX初始化时直接默认配置即可

4.函数:

开启ADC   HAL_ADC_Start(ADC_HandleTypeDef *hadc);读取ADC的转换值HAL_ADC_GetValue(ADC_HandleTypeDef *hadc)(每次getvalue之前都需要先Start);

​ 5.代码:

double ADC_GetValue(){uint32_t count;//保存计数值HAL_ADC_Start(&hadc2);//每次GetValue前都需要重新Startcount=HAL_ADC_GetValue(&hadc2);return count*3.3/4096;}

USART(用mx配置时要修改引脚和GPIO时钟)!

注意:需要更改HAL_USART_MspInit() 和 HAL_USART_MspDeInit()

引脚:

PA8     ------> USART1_CKPA9     ------> USART1_TXPA10   ------> USART1_RX 

1.参数配置:
Mode 异步通信
PIN GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;(MX内配置有误)
2.函数:

发送  HAL_USART_Transmit(&husart1,(unsigned char *)str,strlen(str),100); 注意长度printf重定向int fputc(int ch,FILE *f)   {HAL_USART_Transmit(&husart1,(uint8_t *)&ch,1,100); return ch;  }接收中断 HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)(每接收size字节数据中断一次)接收中断回调函数   HAL_UART_RxCpltCallback( UART_HandleTypeDef *  huart ) (每次中断后需要重新开启接收中断)

TIM

1.基本定时器:(TIM6 TIM7)
A.参数配置:

Prescaler(PSC)  预分频值,决定定时器工作频率Counter Mode  计数模式Counter Period重装载值Trigger Event Selection触发事件

B.函数:

回调函数 HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);以中断方式开启定时器 HAL_TIM_Base_Start_IT(&htim6);关闭定时器HAL_TIM_Base_Stop_IT(&htim6);

2.高级定时器:(输入频率测量)
A.参数配置:

模式选择输入捕获使能定时器中断

B.函数:

初始化函数启动输入捕获中断模式获取定时器计数值__HAL_TIM_GET_COUNTER(&htimx);//读TIMX->CNT也可以设置定时器计数值__HAL_TIM_SetCounter(&htimx,0);//清零 TIM->CNT=0也可以输入捕获回调函数HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//测量频率//在中断模式下启动TIM输入捕获测量HAL_TIM_IC_Start_IT(&htimx, TIM_CHANNEL_x);//使用此函数时必须在主函数先调用一次

C.例:(TIM测量输入频率)
定时器中断后在回调函数中获取当前计数值,用分频后的时钟频率/计数值即可得出输入频率,需要清零计数器并且开启一次中断为下次测量做准备
D.测输入频率占空比:需要将输入捕获中的捕获方式更改为双沿捕获,即上升沿和下降沿时都发生中断,再用高电平的计数值除以低电平的计数值即可得出占空比。

PWM

1.配置:

A.选择引脚功能为TIMx_CHxB.将定时器对应通道功能选择为PWM输出C.Prescaler为分频系数。将系统时钟进行分频D.Counter为溢出值E.Pulse是一个阈值,区分Pulse/Counter之前与之后的电平F.配置的参数都减一

2.函数:

回调函数  void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);开启PWMHAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);停止PWMHAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);配置占空比 __HAL_TIM_SET_COMPARE;

3.例:
输出一个频率为1KHz,占空比为80%的PWM
频率=时钟频率/Prescaler/Counter,假设时钟频率为80MHz,则将Prescaler配置为80,Counter配置为1000,可得频率为1KHz
占空比:高电平时间/一个周期时间,1KHz对应一个周期时间为1ms,可将Pulse设置为800,在0 ~ 800计数值时输出高电平,时间为8/10个周期即0.8ms,801~1000时输出低电平,可得占空比为80%

4.如果要用PWM输出引脚输出持续的高电平或低电平需要更改GPIO的模式,代码如下(13届模拟题)

void PWM_Out(double R37_V,uint32_t FRQ,uint8_t R) //PWM输出{uint32_t Period;//配置重装载值uint32_t Pulse;//配置占空比GPIO_InitTypeDef GPIO_InitStruct;//重新配置GPIO 默认为PWM输出 GPIO_InitStruct.Pin = GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);if(R37_V<0.01)//如果R37电压值为0V{HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);//PWM关闭GPIO_InitStruct.Mode =GPIO_MODE_OUTPUT_PP;//配置为通用推挽输出 如不配置 无法正常输出高低电平HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);//重新初始化HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET);//输出低电平}else if(R37_V>3.29)//如果R37电压值为3.3V{HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);//PWM关闭GPIO_InitStruct.Mode =GPIO_MODE_OUTPUT_PP;//配置为通用推挽输出HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);//重新初始化HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET);//输出低电平}else if(R37_V>0 && R37_V<3.3)//输入PWM{Period=TIM_Clock/(FRQ/R)-1;//输出PWM的重装载值=(定时器时钟频率/目标频率)-1Pulse=R37_V/3.3*Period-1;//输出PWM的Pulse=(R37电压值/3.3*重装载值)-1TIM3->ARR=Period;//直接对寄存器进行配置TIM3->CCR2=Pulse;HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);//开启PWM}}

总结

以上就是全部内容,如有错误请批评指正。
做好赛前梳理,争取在比赛中发挥正常水平,愿各位明天旗开得胜~

中国象棋