当前位置:   article > 正文

2024年蓝桥杯嵌入式省赛笔记(模块搭建以及初始代码)_2024蓝桥杯单片机省赛

2024蓝桥杯单片机省赛

工程准备工作

选择芯片为STM32G431RBT6

 

 

 

1 LED灯

 1)因为控制LCD屏幕的引脚包括了PC8~PC15所以避免与LED冲突才设置了锁存器,PD2置高则·会将PC8~PC15的电平状态通到LED灯右边,反之则锁住。

2),由比赛官方的原理图可以知道 PC8~PC15引脚加锁存器控制引脚PD2控制,并且这八个LED另一端都接到了高电平,所以我们要让LED点亮要让LED灯另一端为低电平用到 HAL_GPIO_WritePin(); 函数控制 PC8~PC15 引脚的高低电平并且将PD2置为高电平使能锁存器,从而把PC8-PC15的电平输入到LED另一边,再通过锁存器保存LED右边的电平信号。

CubeMx配置部分

 选中PC8~PC15选择GPIO_Output(只是一个完整的工程,所以其他引脚设置,不必在意)

并且将输出电平默认为高 

 

因此代码部分

  1. void LED_DISPLAY(unisgned int LED_STA)
  2. {
  3. //先将灯全部关闭,再输入要更新(设置)的LED状态
  4. HAL_GPIO_WritePin(GPIOC ,GPIO_PIN_ALL ,GPIO_PIN_SET);
  5. HAL_GPIO_WritePin(GPIOC ,(LED_STA << 8) ,GPIO_PIN_RESET);
  6. //打开锁存器
  7. HAL_GPIO_WritePin(GPIOD ,GPIO_PIN_2 ,GPIO_PIN_SET);
  8. //关上锁存器
  9. HAL_GPIO_WritePin(GPIOD ,GPIO_PIN_2 ,GPIO_PIN_RESET);
  10. }

如果 LED_STA = 0x01 则是LED1灯亮其他灭。

 2 LCD

1) 由PC0~PC15 、PB8 、PB5、PB9、PA8控制

CubeMax配置将对应的引脚设置为GPIO-Output就行了然后比赛官方会有LCD的驱动程序

在HAL例程中的inc和src中找到 lcd.c 、lcd.h 、fonts.h三个文件就行了,然后复制到自己的工程

3 按键

        一共有四个按键 ,分别接到了 PB0 ,PB2 ,PB3 ,PA0 对应B1 ,B2 ,B3 ,B4

在CubeMX中选中对应引脚设置为GPIO_Input模式,其他不用设置了。

这里建议用定时器中断检测按键是否摁下。

        如果按键按下则是低电平。

当然别忘记开启定时器中断了

HAL_TIM_Base_Start_IT(&htimx); 如HAL_TIM_Base_Start_IT(&htim15);

        选择一个题目没有用到的定时器

我的预分频 Prescaler选择8000 - 1 ,自动重装载寄存器(Counter Period)选择100-1

用于按键的定时器的中断别忘记勾上了 

Keil中

创建一个 处理中断的文件 interrupt.c

注释中消抖处理原理是第一次 key_judge = 0 判断按键是否按下,若按下则为1(这是第一次中断做的)

第二次进入中断已经经过了10ms,然后再继续判断按键状态,若还是按下则认为是按键按下,并是key_short = 1(时钟频率是80MHz)

  1. #include "interrupt.h"
  2. struct key keys[4] = {0 ,0 ,0};
  3. //中断回调函数
  4. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  5. {
  6. if(htim->Instance == TIM15)
  7. {
  8. //接收保存按键状态
  9. keys[0].key_sta = HAL_GPIO_ReadPin(GPIOB ,GPIO_PIN_0);
  10. keys[1].key_sta = HAL_GPIO_ReadPin(GPIOB ,GPIO_PIN_1);
  11. keys[2].key_sta = HAL_GPIO_ReadPin(GPIOB ,GPIO_PIN_2);
  12. keys[3].key_sta = HAL_GPIO_ReadPin(GPIOA ,GPIO_PIN_0);
  13. for(int i = 0 ;i < 4 ;i++)
  14. {
  15. switch (keys[i].key_judge)
  16. {
  17. //按键是否按下
  18. case 0:
  19. if(keys[i].key_sta == 0)
  20. {
  21. keys[i].key_judge = 1;
  22. }
  23. break;
  24. //消抖处理,若还是低电平,这判断为按键按下
  25. case 1:
  26. if(keys[i].key_sta == 0)
  27. {
  28. keys[i].key_short = 1;
  29. keys[i].key_judge = 2;
  30. }
  31. else
  32. {
  33. keys[i].key_judge = 0;
  34. }
  35. break;
  36. //若还是低电平,则就key_judge不置0,继续等待松手,避免长按造成多次响应
  37. case 2:
  38. if(keys[i].key_sta == 1)
  39. {
  40. keys[i].key_judge = 0;
  41. }
  42. break;
  43. }
  44. }
  45. }
  46. }

对应头文件interrupt.h

  1. #ifndef __INTERRUPT_H
  2. #define __INTERRUPT_H
  3. #include "main.h"
  4. #include "stdbool.h"
  5. struct key
  6. {
  7. /*根据judge值做出相应的判断
  8. 0 :判断按键是否为按下
  9. 1 :继续判断按键是否按下,若按下则short = 1
  10. 2 :松手检测*/
  11. int key_judge;
  12. /*每次进入中断就读取按键的电平状态*/
  13. bool key_sta;
  14. /*按键短按标志位*/
  15. bool key_short;
  16. };
  17. #endif

 这里需要根据需求写代码,由于当时刷的题目并没有要求按键长按和按两下的操作,所有我只写了短按的操作

 4 ADC模块

原理图中有两个电位器 分别接到了 PB15(R37)和PB12(R38)

这个最简单选择电位器所接到的引脚选择ADCx_INx,然后在ADCx对应的通道上选择single-ended

 

 

keil代码部分

这样就返回了电压值0~3.3

  1. #include "MyADC.h"
  2. double GET_ADC(ADC_HandleTypeDef *pin)
  3. {
  4. unsigned int adc;
  5. //开启adc
  6. HAL_ADC_Start(pin);
  7. //调用函数获取adc值
  8. adc = HAL_ADC_GetValue(pin);
  9. return adc*3.3/4096;
  10. }

5 定时器

 5.1 定时器生产PWM波

本例用PA7输出PWM

选中PA7并挑选一个CHx,建议用TIM3_CH2 ,带N的是生成互补的PWM考试基本上用不到

根据所选择的定时器和通道配置模式

时钟源选择internal Clock ,在相应通道上选择PWM generation CH2 

pwm波的频率 = (时钟源频率)/(预分频数 * 重装载值) 

如果我们要设置 PWM波频率为 1000Hz 则 (预分频数 * 重装载值)  = 80M/1k =80000

建议重装载值选择 100 这样比较寄存器值就是占空比(这是在题目要求占空比精度为1及以上的时候,偶尔会有带小数的情况) 

这是默认初始比较值,可以根据题目需求设置初始比较寄存器值 

 

然后在主函数初始化时开启PWM

	HAL_TIM_PWM_Start(&htim3 ,TIM_CHANNEL_2);

5.2 定时器输入捕获

        先选定一个定时器通道作为输入捕获的通道,拿十四届蓝桥杯省赛为例,要求PA7引脚作为输入捕获的通道,用于捕获来自PA1的PWM波形.

         然后时钟源选择内部时钟,将对应通道选为 input capture direct mode .预分频选小一点,自动重装载值选择默认的最大值

         输入捕获通道就选默认的上升沿触发,输入捕获原理是当捕获通道检测到上升沿时,开始计数,当遇到第二个上升沿时触发捕获的中断,然后我们在中断函数里面获取定时器计数的值,然后将定时器时钟频率除以计数值就是所测量的PWM的频率。

        代码如下

只需将frq外部应用就行了 

  1. uint16_t tim_counter = 0 ,frq = 0;
  2. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  3. {
  4. //确认定时器
  5. if(htim -> Instance == TIM3)
  6. {
  7. //获取计数值
  8. tim_counter = HAL_TIM_ReadCapturedValue(htim ,TIM_CHANNEL_2);、
  9. //将计数值清零
  10. __HAL_TIM_SetCounter(htim ,0);
  11. //求出频率
  12. frq = (80000000/80)/tim_counter;
  13. //继续开启捕获中断
  14. HAL_TIM_IC_Start(htim ,TIM_CHANNEL_2);
  15. }
  16. }

        在main.c初始化中使用HAL_TIM_IC_Start_IT(&htim3 ,TIM_CHANNEL_2);开启捕获中断 

 6 USART串口

 在connectivity中找到对应的串口

这里用USART1

选择Asynchronous ,波特率根据需求选择(题目基本上是9600)

并且开启中断 

 

串口发送

 主要使用HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)

可以参考我写的方式

  1. char text[30];
  2. sprintf(text ,"your string");
  3. HAL_UART_Transmit(&huart1 ,(uint8_t *)text ,strlen(text) ,50);

 串口接收

和按键一样,写在interrupt.c

这里是值接收一个字符的写法

其中 HAL_UART_Receive_IT(&huart1 ,&rx_data ,1);可以写在主函数前作为开启串口中断的函数

  1. uint8_t rx_flag = 0;
  2. uint8_t rx_data;
  3. char rx_da;
  4. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  5. {
  6. if(rx_flag == 0)
  7. {
  8. HAL_UART_Receive_IT(&huart1 ,&rx_data ,1);
  9. rx_da = rx_data;
  10. rx_flag = 1;
  11. }
  12. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/706091
推荐阅读
相关标签
  

闽ICP备14008679号