【蓝桥杯物联网赛项学习日志】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生成的代码

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|COL3_Pin|GPIO_PIN_11
                          |GPIO_PIN_12, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, COL1_Pin|COL2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC14 */
  GPIO_InitStruct.Pin = GPIO_PIN_14;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;//?
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : PC15 */
  GPIO_InitStruct.Pin = GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : PA0 PA1 PAPin PA11
                           PA12 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|COL3_Pin|GPIO_PIN_11
                          |GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PBPin PBPin */
  GPIO_InitStruct.Pin = COL1_Pin|COL2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : PBPin PBPin */
  GPIO_InitStruct.Pin = ROW1_Pin|ROW2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;//?
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI4_15_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);

}

逻辑代码的编写

宏定义

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

#define ROW1 GPIOB,GPIO_PIN_6
#define ROW2 GPIOB,GPIO_PIN_7
#define COL1 GPIOB,GPIO_PIN_1
#define COL2 GPIOB,GPIO_PIN_0
#define COL3 GPIOA,GPIO_PIN_8
#define LD5 GPIOC,GPIO_PIN_15
#define K1_LED GPIOA,GPIO_PIN_11
#define K2_LED GPIOA,GPIO_PIN_12
#define P1 GPIOA,GPIO_PIN_0
#define P2 GPIOA,GPIO_PIN_1

中断回调函数

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

USER

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

/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	delay_ms(10);
	switch(GPIO_Pin)
	{
		case GPIO_PIN_14:
			HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_15);
			HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_11|GPIO_PIN_12);
		break;
		case ROW1_Pin:
			Key_Val=Scan_KeyRow1();
		break;
		case ROW2_Pin:
			Key_Val=Scan_KeyRow2();
		break;
	}
}
/* USER CODE END 1 */

ROW1 行1 中断信号的处理

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

uint16_t Scan_KeyRow1(void)
{
	uint16_t ucKey_val=0;   //设立变量存键值
	HAL_GPIO_WritePin(COL1,GPIO_PIN_SET);//拉高3列的电平
	HAL_GPIO_WritePin(COL2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(COL3,GPIO_PIN_SET);
	//遍历列
	for(uint8_t i=0;i<=2;i++)
	{
		HAL_NVIC_DisableIRQ(ROW1_EXTI_IRQn);//关闭ROW1的中断
		switch(i){
			case 0:
				HAL_GPIO_WritePin(COL1,GPIO_PIN_RESET);//拉低列1
				delay_ms(10);//消抖
				if(HAL_GPIO_ReadPin(ROW1) == 0)//读取键值
					ucKey_val = '1';
				HAL_GPIO_WritePin(COL1,GPIO_PIN_SET);//拉高列1
				break;
			case 1:
				HAL_GPIO_WritePin(COL2,GPIO_PIN_RESET);//拉低列2
				delay_ms(10);//消抖
				if(HAL_GPIO_ReadPin(ROW1) == 0)//读取键值
					ucKey_val = '2';
				HAL_GPIO_WritePin(COL2,GPIO_PIN_SET);//拉高列2
				break;
			case 2:
				HAL_GPIO_WritePin(COL3,GPIO_PIN_RESET);//拉低列3
				delay_ms(10);//消抖
				if(HAL_GPIO_ReadPin(ROW1) == 0)//读取键值
					ucKey_val = '3';
				HAL_GPIO_WritePin(COL3,GPIO_PIN_SET);//拉高列3
				break;
			}
	}
        //拉低三列的电平
		HAL_GPIO_WritePin(COL1,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(COL2,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(COL3,GPIO_PIN_RESET);
        //打开ROW1中断
		HAL_NVIC_EnableIRQ(ROW1_EXTI_IRQn);

	return ucKey_val;
}

ROW2 行2 中断信号的处理

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

uint16_t Scan_KeyRow2(void)
{
	uint16_t ucKey_val=0;
	HAL_GPIO_WritePin(COL1,GPIO_PIN_SET);
	HAL_GPIO_WritePin(COL2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(COL3,GPIO_PIN_SET);
	for(uint8_t i=0;i<=2;i++)
	{
		HAL_NVIC_DisableIRQ(ROW2_EXTI_IRQn);
		switch(i){
			case 0:
				HAL_GPIO_WritePin(COL1,GPIO_PIN_RESET);
				delay_ms(10);
				if(HAL_GPIO_ReadPin(ROW2)==0)
					ucKey_val = '4';
				HAL_GPIO_WritePin(COL1,GPIO_PIN_SET);
				break;
			case 1:
				HAL_GPIO_WritePin(COL2,GPIO_PIN_RESET);
			  delay_ms(10);
				if(HAL_GPIO_ReadPin(ROW2)==0)
					ucKey_val = '5';
				HAL_GPIO_WritePin(COL2,GPIO_PIN_SET);
				break;
			case 2:
				HAL_GPIO_WritePin(COL3,GPIO_PIN_RESET);
				delay_ms(10);
				if(HAL_GPIO_ReadPin(ROW2)==0)
				 ucKey_val = '6';
				HAL_GPIO_WritePin(COL3,GPIO_PIN_SET);
				break;
		}
	}
	HAL_GPIO_WritePin(COL1,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(COL2,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(COL3,GPIO_PIN_RESET);
	HAL_NVIC_EnableIRQ(ROW2_EXTI_IRQn);

	return ucKey_val;
}

LED控制函数

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

void LED_Control(uint16_t ucState)
{
	
	switch(ucState)
		{
			case '1':
				HAL_GPIO_TogglePin(LD5);//LD翻转
				HAL_GPIO_WritePin(K1_LED,GPIO_PIN_SET);//K1_LED  ON
				HAL_GPIO_WritePin(K2_LED,GPIO_PIN_RESET);//K2_LED  OFF
				HAL_GPIO_WritePin(P1,GPIO_PIN_SET);//P1 ON
				HAL_GPIO_WritePin(P2,GPIO_PIN_RESET);//P2 OFF
			break;
			case '2':
				HAL_GPIO_TogglePin(LD5);//LD翻转
				HAL_GPIO_WritePin(K1_LED,GPIO_PIN_RESET);//K1_LED  OFF 
				HAL_GPIO_WritePin(K2_LED,GPIO_PIN_SET);//K2_LED  ON
				HAL_GPIO_WritePin(P1,GPIO_PIN_RESET);//P1 OFF
				HAL_GPIO_WritePin(P2,GPIO_PIN_SET);//P2 ON
			break;
			case '3':
				HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);//LD5 OFF
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_RESET);//ALL OFF
			break;
			case '4':
				HAL_GPIO_WritePin(LD5,GPIO_PIN_SET);//LD5 OFF
			break;
			case '5':
				HAL_GPIO_WritePin(K1_LED,GPIO_PIN_RESET);//K1_LED OFF
				HAL_GPIO_WritePin(K2_LED,GPIO_PIN_RESET);//K2_LED OFF
			break;
			case '6':
				HAL_GPIO_WritePin(P1,GPIO_PIN_RESET);//P1 OFF
				HAL_GPIO_WritePin(P2,GPIO_PIN_RESET);//P2 OFF
			break;
		}
}

主函数

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

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
		
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		delay_ms(50);
		LED_Control(Key_Val);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 延时函数

void delay_ms(uint16_t ms)
{
	uint16_t i=0;
	while(ms--)
	{
		i=12000;
		while(i--);
	}
}

总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值