• 【蓝桥杯物联网赛项学习日志】Day2 中断矩阵按键


     写在前面,这个算是我第一个小项目,从头到尾的完成题目,花了一天写,花了一天调试改BUG,因为我是一个奋发向上的菜菜。

    题目

    引脚配置

    模块引脚配置

    首先读题,看关键字,中断,控制LED,继电器,电机。题目要求用中断按键控制,板子没有那么多按键,这个要用到扩展模块。长下面这个样子。

     原理图:

    模块引脚配置:

    ROW1ROW2COL1COL2COL3
    引脚PB6PB7PB1PB0PA8
    MODERISING_FALLINGRISING_FALLINGOUTPUT_PPOUTPUT_PPOUTPUT_PP
    PullPULLUPPULLUPNOPULLNOPULLNOPULL
    SpeedLOWLOWLOWLOWLOW

    板子外设限制,模块只有两行三列,设置行作为中断输入口,配置为默认上拉,速率为LOW;列为输出口,设置为推挽输出模式,无上拉无下拉。使能中断线,两个口共用中断线EXIT4_15,所以只需要使能该中断线该模块就配置好了。

    板载资源配置

    根据题目,我们需要配置USER按键,LD5,继电器,电机

    原理图:

     板载引脚配置:

    USERLD5P1P2K1K2
    引脚PC14PC15PA0PA1PA11PA12
    ModeFALLINGOUTPUT_PPOUTPUT_PPOUTPUT_PPOUTPUT_PPOUTPUT_PP
    PullPULLUPNOPULLNOPULLNOPULLNOPULLNOPULL
    SpeedLOWLOWLOWLOWLOWLOW

     这部分配置注意原理图中,USER引脚被上拉了,按键按下,低电平为1;LD5在实验中发现是高电平熄灭,低电平点亮;其他都是高电平驱动,低电平断开。看好原理图会对后面代码编写省很多事,这是我调代码调了老久的领悟。

    注意中断引脚为 ROW1:PB6 ROW2:PB7 USER:PC14

    USERROW1ROW2
    PC14PB6PB7

    设置好时钟为 32MHz 后就进行代码初始化;

    引脚初始化后CubeMax生成的代码

    1. void MX_GPIO_Init(void)
    2. {
    3. GPIO_InitTypeDef GPIO_InitStruct = {0};
    4. /* GPIO Ports Clock Enable */
    5. __HAL_RCC_GPIOC_CLK_ENABLE();
    6. __HAL_RCC_GPIOA_CLK_ENABLE();
    7. __HAL_RCC_GPIOB_CLK_ENABLE();
    8. /*Configure GPIO pin Output Level */
    9. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);
    10. /*Configure GPIO pin Output Level */
    11. HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|COL3_Pin|GPIO_PIN_11
    12. |GPIO_PIN_12, GPIO_PIN_RESET);
    13. /*Configure GPIO pin Output Level */
    14. HAL_GPIO_WritePin(GPIOB, COL1_Pin|COL2_Pin, GPIO_PIN_RESET);
    15. /*Configure GPIO pin : PC14 */
    16. GPIO_InitStruct.Pin = GPIO_PIN_14;
    17. GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;//?
    18. GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    19. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    20. /*Configure GPIO pin : PC15 */
    21. GPIO_InitStruct.Pin = GPIO_PIN_15;
    22. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    23. GPIO_InitStruct.Pull = GPIO_NOPULL;
    24. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    25. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    26. /*Configure GPIO pins : PA0 PA1 PAPin PA11
    27. PA12 */
    28. GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|COL3_Pin|GPIO_PIN_11
    29. |GPIO_PIN_12;
    30. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    31. GPIO_InitStruct.Pull = GPIO_NOPULL;
    32. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    33. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    34. /*Configure GPIO pins : PBPin PBPin */
    35. GPIO_InitStruct.Pin = COL1_Pin|COL2_Pin;
    36. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    37. GPIO_InitStruct.Pull = GPIO_NOPULL;
    38. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    39. HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    40. /*Configure GPIO pins : PBPin PBPin */
    41. GPIO_InitStruct.Pin = ROW1_Pin|ROW2_Pin;
    42. GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;//?
    43. GPIO_InitStruct.Pull = GPIO_PULLUP;
    44. HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    45. /* EXTI interrupt init*/
    46. HAL_NVIC_SetPriority(EXTI4_15_IRQn, 2, 0);
    47. HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
    48. }

    逻辑代码的编写

    宏定义

    为了方便代码的编写,对引脚进行了宏定义。代码如下:

    1. #define ROW1 GPIOB,GPIO_PIN_6
    2. #define ROW2 GPIOB,GPIO_PIN_7
    3. #define COL1 GPIOB,GPIO_PIN_1
    4. #define COL2 GPIOB,GPIO_PIN_0
    5. #define COL3 GPIOA,GPIO_PIN_8
    6. #define LD5 GPIOC,GPIO_PIN_15
    7. #define K1_LED GPIOA,GPIO_PIN_11
    8. #define K2_LED GPIOA,GPIO_PIN_12
    9. #define P1 GPIOA,GPIO_PIN_0
    10. #define P2 GPIOA,GPIO_PIN_1

    中断回调函数

    三个引脚配置成中断引脚,所以在回调函数中,要编写三种中断信号的处理

    USER

    题目要求USER按键开关所有外设,所以我直接在回调函数中翻转LD5,K1,K2,P1,P2的电平

    1. /* USER CODE BEGIN 1 */
    2. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    3. {
    4. delay_ms(10);
    5. switch(GPIO_Pin)
    6. {
    7. case GPIO_PIN_14:
    8. HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_15);
    9. HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_11|GPIO_PIN_12);
    10. break;
    11. case ROW1_Pin:
    12. Key_Val=Scan_KeyRow1();
    13. break;
    14. case ROW2_Pin:
    15. Key_Val=Scan_KeyRow2();
    16. break;
    17. }
    18. }
    19. /* USER CODE END 1 */

    ROW1 行1 中断信号的处理

    在行1中断中,改变中断引脚配置,关闭中断后,将列1,列2,列3的电平拉高,再依次拉低,读取ROW1的引脚电平,如果为低电平,说明该行,该列的按键按下,返回键值,再拉低每一列的电平,打开中断。代码如下:

    1. uint16_t Scan_KeyRow1(void)
    2. {
    3. uint16_t ucKey_val=0; //设立变量存键值
    4. HAL_GPIO_WritePin(COL1,GPIO_PIN_SET);//拉高3列的电平
    5. HAL_GPIO_WritePin(COL2,GPIO_PIN_SET);
    6. HAL_GPIO_WritePin(COL3,GPIO_PIN_SET);
    7. //遍历列
    8. for(uint8_t i=0;i<=2;i++)
    9. {
    10. HAL_NVIC_DisableIRQ(ROW1_EXTI_IRQn);//关闭ROW1的中断
    11. switch(i){
    12. case 0:
    13. HAL_GPIO_WritePin(COL1,GPIO_PIN_RESET);//拉低列1
    14. delay_ms(10);//消抖
    15. if(HAL_GPIO_ReadPin(ROW1) == 0)//读取键值
    16. ucKey_val = '1';
    17. HAL_GPIO_WritePin(COL1,GPIO_PIN_SET);//拉高列1
    18. break;
    19. case 1:
    20. HAL_GPIO_WritePin(COL2,GPIO_PIN_RESET);//拉低列2
    21. delay_ms(10);//消抖
    22. if(HAL_GPIO_ReadPin(ROW1) == 0)//读取键值
    23. ucKey_val = '2';
    24. HAL_GPIO_WritePin(COL2,GPIO_PIN_SET);//拉高列2
    25. break;
    26. case 2:
    27. HAL_GPIO_WritePin(COL3,GPIO_PIN_RESET);//拉低列3
    28. delay_ms(10);//消抖
    29. if(HAL_GPIO_ReadPin(ROW1) == 0)//读取键值
    30. ucKey_val = '3';
    31. HAL_GPIO_WritePin(COL3,GPIO_PIN_SET);//拉高列3
    32. break;
    33. }
    34. }
    35. //拉低三列的电平
    36. HAL_GPIO_WritePin(COL1,GPIO_PIN_RESET);
    37. HAL_GPIO_WritePin(COL2,GPIO_PIN_RESET);
    38. HAL_GPIO_WritePin(COL3,GPIO_PIN_RESET);
    39. //打开ROW1中断
    40. HAL_NVIC_EnableIRQ(ROW1_EXTI_IRQn);
    41. return ucKey_val;
    42. }

    ROW2 行2 中断信号的处理

    行2的逻辑和行1一样,拉高电平,关闭中断,依次拉低,返回键值,打开中断。代码如下:

    1. uint16_t Scan_KeyRow2(void)
    2. {
    3. uint16_t ucKey_val=0;
    4. HAL_GPIO_WritePin(COL1,GPIO_PIN_SET);
    5. HAL_GPIO_WritePin(COL2,GPIO_PIN_SET);
    6. HAL_GPIO_WritePin(COL3,GPIO_PIN_SET);
    7. for(uint8_t i=0;i<=2;i++)
    8. {
    9. HAL_NVIC_DisableIRQ(ROW2_EXTI_IRQn);
    10. switch(i){
    11. case 0:
    12. HAL_GPIO_WritePin(COL1,GPIO_PIN_RESET);
    13. delay_ms(10);
    14. if(HAL_GPIO_ReadPin(ROW2)==0)
    15. ucKey_val = '4';
    16. HAL_GPIO_WritePin(COL1,GPIO_PIN_SET);
    17. break;
    18. case 1:
    19. HAL_GPIO_WritePin(COL2,GPIO_PIN_RESET);
    20. delay_ms(10);
    21. if(HAL_GPIO_ReadPin(ROW2)==0)
    22. ucKey_val = '5';
    23. HAL_GPIO_WritePin(COL2,GPIO_PIN_SET);
    24. break;
    25. case 2:
    26. HAL_GPIO_WritePin(COL3,GPIO_PIN_RESET);
    27. delay_ms(10);
    28. if(HAL_GPIO_ReadPin(ROW2)==0)
    29. ucKey_val = '6';
    30. HAL_GPIO_WritePin(COL3,GPIO_PIN_SET);
    31. break;
    32. }
    33. }
    34. HAL_GPIO_WritePin(COL1,GPIO_PIN_RESET);
    35. HAL_GPIO_WritePin(COL2,GPIO_PIN_RESET);
    36. HAL_GPIO_WritePin(COL3,GPIO_PIN_RESET);
    37. HAL_NVIC_EnableIRQ(ROW2_EXTI_IRQn);
    38. return ucKey_val;
    39. }

    LED控制函数

    对返回的键值就可以实现对应的功能,实现题目要求。代码如下:

    1. void LED_Control(uint16_t ucState)
    2. {
    3. switch(ucState)
    4. {
    5. case '1':
    6. HAL_GPIO_TogglePin(LD5);//LD翻转
    7. HAL_GPIO_WritePin(K1_LED,GPIO_PIN_SET);//K1_LED ON
    8. HAL_GPIO_WritePin(K2_LED,GPIO_PIN_RESET);//K2_LED OFF
    9. HAL_GPIO_WritePin(P1,GPIO_PIN_SET);//P1 ON
    10. HAL_GPIO_WritePin(P2,GPIO_PIN_RESET);//P2 OFF
    11. break;
    12. case '2':
    13. HAL_GPIO_TogglePin(LD5);//LD翻转
    14. HAL_GPIO_WritePin(K1_LED,GPIO_PIN_RESET);//K1_LED OFF
    15. HAL_GPIO_WritePin(K2_LED,GPIO_PIN_SET);//K2_LED ON
    16. HAL_GPIO_WritePin(P1,GPIO_PIN_RESET);//P1 OFF
    17. HAL_GPIO_WritePin(P2,GPIO_PIN_SET);//P2 ON
    18. break;
    19. case '3':
    20. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);//LD5 OFF
    21. HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_RESET);//ALL OFF
    22. break;
    23. case '4':
    24. HAL_GPIO_WritePin(LD5,GPIO_PIN_SET);//LD5 OFF
    25. break;
    26. case '5':
    27. HAL_GPIO_WritePin(K1_LED,GPIO_PIN_RESET);//K1_LED OFF
    28. HAL_GPIO_WritePin(K2_LED,GPIO_PIN_RESET);//K2_LED OFF
    29. break;
    30. case '6':
    31. HAL_GPIO_WritePin(P1,GPIO_PIN_RESET);//P1 OFF
    32. HAL_GPIO_WritePin(P2,GPIO_PIN_RESET);//P2 OFF
    33. break;
    34. }
    35. }

    主函数

    最后,在头文件中声明函数,在主函数中显示扫描LED_Control()。代码如下:

    1. int main(void)
    2. {
    3. /* USER CODE BEGIN 1 */
    4. /* USER CODE END 1 */
    5. /* MCU Configuration--------------------------------------------------------*/
    6. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    7. HAL_Init();
    8. /* USER CODE BEGIN Init */
    9. /* USER CODE END Init */
    10. /* Configure the system clock */
    11. SystemClock_Config();
    12. /* USER CODE BEGIN SysInit */
    13. /* USER CODE END SysInit */
    14. /* Initialize all configured peripherals */
    15. MX_GPIO_Init();
    16. MX_SPI1_Init();
    17. MX_USART2_UART_Init();
    18. /* USER CODE BEGIN 2 */
    19. /* USER CODE END 2 */
    20. /* Infinite loop */
    21. /* USER CODE BEGIN WHILE */
    22. while (1)
    23. {
    24. delay_ms(50);
    25. LED_Control(Key_Val);
    26. /* USER CODE END WHILE */
    27. /* USER CODE BEGIN 3 */
    28. }
    29. /* USER CODE END 3 */
    30. }

     延时函数

    1. void delay_ms(uint16_t ms)
    2. {
    3. uint16_t i=0;
    4. while(ms--)
    5. {
    6. i=12000;
    7. while(i--);
    8. }
    9. }

    总结

    在代码编写的时候,把思路变成代码的过程,写的时候嘎嘎快,调的时候嘎嘎难受。在中断按键扫描的那点,卡住很长时间,一开始卡在中断,到后面一点一点,发现HAL_Delay会卡死中断,就自己编写delay函数,发现中断过后会再次读取引脚,两次引脚信号导致板子要按两次才能实现功能,调延时,最后调整ROW1中断,ROW2中断引脚为上升沿和下降沿都触发。解决问题,实现了题目要求。

  • 相关阅读:
    OpenStack创建云主机并连接CRT
    工业互联网数字化中台解决方案
    latex小节标题如何靠左显示
    [常用工具] Python视频解码库DeFFcode使用指北
    Postman和Jmeter的区别
    一次 MySQL 误操作导致的事故,「高可用」都顶不住了!
    你用过 Maven Shade 插件吗?
    day4:Node.js 核心库
    网页游戏的开发流程
    卡片介绍、EMV卡组织、金融认证---安全行业基础篇2
  • 原文地址:https://blog.csdn.net/peng_papa/article/details/127836027