蓝桥杯嵌入式STM32G4 HAL库应用综述:省赛实践总结

文章目录

  • 前言
  • 硬件资源
  • 下载方式
  • 模块总结
  • 时钟树
  • LED灯
  • 键盘
  • LCD
  • USART串口
  • EEPROM
  • MCP4017可编程电阻
  • ADC
  • DAC
  • 测量PWM频率和占空比
  • 定时器生成PWM波
  • RTC时钟
  • 写在最后

  • 前言

    由于去年大一的时候学了会单片机。参加了蓝桥杯单片机组,虽然只获得了一个省奖,但是带领我进入了单片机的世界,今年参加嵌入式组,由于之前一段时间去学CV去了,所以现在趁着这个机会复习一下stm32

    硬件资源

    嵌入式竞赛实训平台(CT117E-M4) 是北京国信长天科技有限公司设计、生产的一款“蓝桥杯全国软件与信息技术专业人才大赛-嵌入式设计与开发科目”专用竞赛平台,平台以STM32G431RBT6为主控芯片,预留扩展板接口,可为用户提供丰富的实验场景。CT117E-M4

    下载方式


    由于板载了DAP Link,所以直接接入CN2(USB-Type)接口,并保证NRST、SWCLK、SWDIO的跳线连接,即可下载程序
    CT117E-M4还板载了USB转串口功能默认与STM32G431RBT6的USART1连接。

    模块总结

    时钟树

    选择24M外部高速晶振
    选择24M外部高速晶振

  • CubeMx配置
  • LED灯

  • CubeMx配置
  • PC8-PC15:

  • GPIO output lebel:High
  • GPIO mde:Output Push Pull
  • GPIO Pull:No pull-up and no pull-down
  • PD2:

  • GPIO output lebel:Low
  • GPIO mde:Output Push Pull
  • GPIO Pull:No pull-up and no pull-down
  • void LED_Disp(unsigned char Led)
    {
    	//**将所有的灯熄灭
    	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
    												|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
    	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);		
    	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
    
    	//根据ucLed的数值点亮相应的灯
    	//LD8-LD1对应Led的8个位
    	HAL_GPIO_WritePin(GPIOC, Led<<8, GPIO_PIN_RESET);
    	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);		
    	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);	
    }
    

  • 我们可以在hal_gpio.h里找到所有hal库关于gpio的函数,其他的功能的函数都可以在对应的.h文件里找到
  • 键盘

  • CubeMx配置
  • GPIO mde:Input mode
  • GPIO Pull:No pull-up and no pull-down
  • unsigned char Key_Scan(void)
    {
    	unsigned char unKey_Val = 0;
    	
    	if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET)unKey_Val = 1;
    	if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == GPIO_PIN_RESET)unKey_Val = 2;
    	if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == GPIO_PIN_RESET)unKey_Val = 3;
    	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)unKey_Val = 4;	
    	
    	return unKey_Val;
    }
    void Key_Proc(void)
    {
    	//利用嘀嗒定时器消抖减速
    	if((uwTick -  uwTick_Key)<100)	return;
    	uwTick_Key = uwTick;	
    	
    	ucKey_Val = Key_Scan();
    	unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val); 
    	ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);	
    	ucKey_Old = ucKey_Val;
    	switch(unKey_Down)
    	{
    		case 1:
    		...
    	}
    }
    

    LCD

  • 比赛的时候官方会给我们提供驱动程序,所以我们只需移植就行了



  • 我们在需要在自己的BSP的文件夹里移植这3个文件就可以了
  • 别忘了在MDK里添加文件路径
  • void LCD_Init(void);
    void LCD_SetTextColor(vu16 Color);
    void LCD_SetBackColor(vu16 Color);
    void LCD_ClearLine(u8 Line);
    void LCD_Clear(u16 Color);
    void LCD_SetCursor(u8 Xpos, u16 Ypos);
    void LCD_DrawChar(u8 Xpos, u16 Ypos, uc16 *c);
    void LCD_DisplayChar(u8 Line, u16 Column, u8 Ascii);
    void LCD_DisplayStringLine(u8 Line, u8 *ptr);
    void LCD_SetDisplayWindow(u8 Xpos, u16 Ypos, u8 Height, u16 Width);
    void LCD_WindowModeDisable(void);
    void LCD_DrawLine(u8 Xpos, u16 Ypos, u16 Length, u8 Direction);
    void LCD_DrawRect(u8 Xpos, u16 Ypos, u8 Height, u16 Width);
    void LCD_DrawCircle(u8 Xpos, u16 Ypos, u16 Radius);
    void LCD_DrawMonoPict(uc32 *Pict);
    void LCD_WriteBMP(u32 BmpAddress);
    void LCD_DrawBMP(u32 BmpAddress);
    void LCD_DrawPicture(const u8* picture);
    
    /*----- Medium layer function -----*/
    void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue);
    u16 LCD_ReadReg(u8 LCD_Reg);
    void LCD_WriteRAM_Prepare(void);
    void LCD_WriteRAM(u16 RGB_Code);
    u16 LCD_ReadRAM(void);
    void LCD_PowerOn(void);
    void LCD_DisplayOn(void);
    void LCD_DisplayOff(void);
    
    /*----- Low layer function -----*/
    void LCD_CtrlLinesConfig(void);
    
    void LCD_BusIn(void);
    void LCD_BusOut(void);
    
  • 就可以直接调用LCD的API了
  • LCD_Init(); //常用的一些函数
    LCD_Clear(White);
    LCD_SetBackColor(White);
    LCD_SetTextColor(Blue);	
    sprintf((char *)Lcd_Disp_String, "   Hello world!!!");
    LCD_DisplayStringLine(Line1, Lcd_Disp_String);
    

    USART串口

  • 由于板载了USB-串口,所以我们接上USB线就能用USART1

  • CubeMx配置

  • GPIO记得选择PA9 PA10

  • 波特率可自行选择,和串口助手相同就行

  • 开启中断

  • //发送
    HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
    
  • UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
  • *pData 需要发送的数据
  • Size 发送的字节数
  • Timeout 最大发送时间,发送数据超过该时间退出发送
  • //接受
    HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
    
  • UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
  • *pData 接收到的数据存放地址
  • Size 接收的字节数
  • //常用格式
    //串口中断开启
    HAL_UART_Receive_IT(&huart1, &rx_buffer, 1);
    //串口中断回调函数
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    	LED(ON);
    	HAL_UART_Receive_IT(&huart1, &rx_buffer, 1);//这一句一定要加
    }
    
    //重定向c库函数printf
    int fputc(int ch, FILE *f)
    {
      HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
      return ch;
    }
    //重定向c库函数getchar,scanf
    int fgetc(FILE *f)
    {
      uint8_t ch = 0;
      HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
      return ch;
    }
    

    EEPROM


  • M24C02通过IIC通信接在单片机的PB6 PB7上,我们直接移植官方的代码
  • //EEPROM读写代码
    void eeprom_write(unsigned char *pucBuf, unsigned char ucAddr, unsigned char Size)
    {
    	I2CStart();
    	I2CSendByte(0xa0);
    	I2CWaitAck();
    	
    	I2CSendByte(ucAddr);	
    	I2CWaitAck();
    	
    	while(Size--)
    	{
    		I2CSendByte(*pucBuf++);
    		I2CWaitAck();	
    	}
    	I2CStop();
    	delay1(500);	
    }
    
    void eeprom_read(unsigned char *pucBuf, unsigned char ucAddr, unsigned char Size)
    {
    	I2CStart();
    	I2CSendByte(0xa0);
    	I2CWaitAck();
    	
    	I2CSendByte(ucAddr);	
    	I2CWaitAck();
    	
    	I2CStart();
    	I2CSendByte(0xa1);
    	I2CWaitAck();
    	
    	while(ucNum--)
    	{
    		*pucBuf++ = I2CReceiveByte();
    		if(Size)
    			I2CSendAck();	
    		else
    			I2CSendNotAck();
    	}
    	I2CStop();	
    }
    

    MCP4017可编程电阻



  • 这就是MC94017的内部结构,总的来说就是接通开关,电阻分压
  • 下面是计算公式
  • MCP4017地址
  • void resistor_write(uint8_t value)
    {
    	I2CStart();
    	I2CSendByte(0x5E);
    	I2CWaitAck();
    	
    	I2CSendByte(value);
    	I2CWaitAck();	
    	I2CStop();	
    }
    
    uint8_t resistor_read(void)
    {
    	uint8_t value;
    	I2CStart();
    	I2CSendByte(0x5F);
    	I2CWaitAck();
    	
    	value = I2CReceiveByte();
    	I2CSendNotAck();	
    	I2CStop();	
    	
    	return value;
    }
    

    ADC

  • 由于STM32的ADC的配置方式有很多种,这里我们只介绍常见的几种
  • CubeMx配置介绍
  • 单次转换模式:ADC只执行一次转换
  • 连续转换模式:转换结束之后马上开始新的转换
  • 扫描模式:ADC扫描被规则通道和注入通道选中的所有通道,在每个组的每个通道上执行单次转换。
  • 间断模式:触发一次,转换一个通道,在触发,在转换。在所选转换通道循环,由触发信号启动新一轮的转换,直到转换完成为止。
  • 单ADC采集:

  • 这里我们通过R38来调节PB12通道采集到的电压
  • CubeMx配置

  • 注意时钟选择,其他的可自行选择设置
  • uint16_t getADC1(void)
    {
    	uint16_t adc_value = 0;
    	
    	HAL_ADC_Start(&hadc1);
    	adc_value = HAL_ADC_GetValue(&hadc1);
    	
    	return adc_value;
    }
    

    多ADC采集:

  • 这里我们选择R37调节PB15
  • 和上面ADC1一样的配置即可
  • //只需把hadc1改为hadc2
    uint16_t getADC2(void)
    {
    	uint16_t adc_value = 0;
    	
    	HAL_ADC_Start(&hadc2);
    	adc_value = HAL_ADC_GetValue(&hadc2);
    	
    	return adc;
    }
    
  • 这只是最基础的ADC使用
  • 如果有需要还可以开启中断
  • 单ADC多通道可以开启DMA模式
  • DAC

  • CubeMx配置
  • DAC功能很多。但是比赛赛题里很少出现,这里不过多赘述
  • void DAC_OUT(float Val)
    {
    	uint32_t temp;
    	temp = (Val*4096/3.3f);
    	HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp);
    }
    
    

    测量PWM频率和占空比

  • 我们用板载的XL555信号发生器来生成PWM波,采集引脚为PA15
  • CubeMx配置

  • 复位Reset模式:使用内部时钟作为时钟源,TI1/2外部有效信号复位计数器。例如,CH1触发输入上升沿有效时,计数器复位到默认值0。
  • 原理:
    1.选择通道一测量pwm的频率和占空比,上升沿触发
    2.当上升沿来临时,②中寄存器会存下计数值,会触发中断,然后清零,这个值记做T
    3.当下降沿来临时,③中寄存器会存下计数值,会触发中断,不会清零,这个值记做D

  • 我们设置的80分频,所以只有1M的频率
  • 频率 = 1000000 / T
  • 占空比 = D / T
  • 我们在中断中读这两个寄存器的值,就可以计算出频率和占空比了
  • 这样在看CubeMx的配置就清晰很多了
  • /* 启动定时器 */
    HAL_TIM_Base_Start(&htim2);
    /* 启动定时器通道输入捕获并开启中断 */
    HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);		
    HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);	
    //输入捕获中断回调函数
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    	
    	  if(htim->Instance==TIM2)
     	 {
    		if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
    		{
    			T_Count =  HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1;
    			Freq = 1000000 / T_Count;
    			Duty = (float)D_Count/T_Count * 100;
    		}
    		else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
    		{
    			D_Count =  HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1;
    		}			
    			
    	}	
    }
    

    定时器生成PWM波

  • 我们利用PA2来实现PWM输出
  • CubeMx配置
  • PWM频率:
    Fpwm =Tclk / ((arr+1)*(psc+1))(单位:Hz)

  • arr 是计数器值
  • psc 是预分频值
  • 占空比:
    duty circle = TIM15->CCR1 / arr(单位:%)

  • TIM15->CCR1 用户设定值
  •   HAL_TIM_PWM_Start(&htim15,TIM_CHANNEL_1);
    

    RTC时钟

  • CubeMx配置
  • //RTC相关变量
    RTC_TimeTypeDef H_M_S_Time;
    RTC_DateTypeDef Y_M_D_Date;
    uint8_t Second;
    
    //函数调用
    HAL_RTC_GetTime(&hrtc, &H_M_S_Time, RTC_FORMAT_BIN);//读取日期和时间必须同时使用
    HAL_RTC_GetDate(&hrtc, &Y_M_D_Date, RTC_FORMAT_BIN);
    

    写在最后

    离比赛时间不远了,接下来就是做几套真题,希望大家和我都能取得一个好成绩,加油加油加油
    对了,总结几个我写程序平常容易出错的地方:

  • 记得配置中断优先级
  • 串口波特率双方一定要相同
  • 出BUG了一定不要着急,从配置开始检查,在检查代码

    大家冲冲冲
  • 物联沃分享整理
    物联沃-IOTWORD物联网 » 蓝桥杯嵌入式STM32G4 HAL库应用综述:省赛实践总结

    发表评论