32单片机矩阵键盘-同列组合键不能识别故障-已解决

这篇具有很好参考价值的文章主要介绍了32单片机矩阵键盘-同列组合键不能识别故障-已解决。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、电路原理

1.1. 矩阵键盘电路

32单片机矩阵键盘-同列组合键不能识别故障-已解决,单片机,stm32,单片机,嵌入式硬件

 1.2. gd32f103单片机端是iic,中间经过一个pca9535芯片。

32单片机矩阵键盘-同列组合键不能识别故障-已解决,单片机,stm32,单片机,嵌入式硬件

1.3 pca9535 的功能请参考相关文档

这里主要用到的是设置输入输出模式,读取输入值,输出高或者输出低等功能。

二、基本要求

2.1 单个按键识别

2.2 组合键识别(一般是两个按键同时按下)

2.3 按键消抖

2.4 防止识别重复按键,不需要识别长短按键。

三、前期已经完成的识别程序

3.1 程序基本原理描述(称为行扫描模式)

3.1.1 L信号(P1端口)作输入模式,H信号(P0端口)作输出模式,任意时刻只有一个H信号为低电平,其他为高电平。

使用i计数,i=0时,H1为低,其他(H2-H6)输出高,读取L信号,如果都为1(取低6位,0x3f),则表示没有按键被按下,如果不等于0x3f,则表示有按键被按下,有按键被按下,则需要循环6次,判断其中的哪些位(考虑多个按键被按下的情况)为0,则为0的位表示有按键被按下,这样就找到了被按下的按键。

其他行依次类推,i=1时,H2输出低,其他(H1,H3-H6)输出高。

L信号是输入引脚,外部电路有10K上拉电阻,如果不跟外部连接的话,读信号肯定是个高电平,所以没有按键按下时,判断为高电平。

如果按键按下,同时按键所在的H信号正好也是低电平的话,按键闭合导致电路形成回路,上拉电阻的一端(这一端正好接着L信号)电压被拉低为0,L读到的正好也是低电平,这时可以判断按键被按下。特别说明,H信号为高的时候,即使按键被按下,L信号仍然是高,没法得出按键被按下的情况。所以这里一定要某一个H信号(比如H1)为低,其他H信号(则是H2-H6)为高,就能够分辨是哪一行有按键了。

3.1.2 按键也要消抖,这里是连续采集到两次,则认为识别到了一个有效按键。

g_btns_info.pressCnt[index]++;

3.1.3 按键也要防止重复识别,按下一次就识别一次,不能因为久按而识别多次。

if(g_btns_info.pressCnt[index] < 2)
 {    
           g_btns_info.pressCnt[index]++;
            if(g_btns_info.pressCnt[index] == 2)//检测到不止1次

3.1.4 我程序中有串口上报键值的部分,可以注释掉。printf是串口打印,用于调试

send_btn_change_to_cpu(index+1,1); //发送按键按下/松开

3.2 源程序

结构体

typedef struct btn_info{
    uint8_t  value[KEY_MAX];         //0表示松开,1表示按下
    uint8_t  reportEn[KEY_MAX];   //1,需要上报,0不需要上报
    uint16_t  pressCnt[KEY_MAX];     //消抖,长按区分
}BTN_INFO;

		
//只有6行,这些数字用于分别扫描每一行
const static uint8_t key_scan_line[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};

// 6*6 的键盘矩阵,总共有33个按键,按键个数在h文件中定义
static BTN_INFO g_btns_info;

TaskHandle_t  TaskHandle_key_Matrix;   //存放按键任务指针



//端口的配置
void matrix_keys_init(void)
{
	uint8_t outcfg_dat[2]={0,0xff};   //IIC芯片GPIO输出模式,对应的位要设置为0
	//1. iic的初始化
	nca9555_init(NCA9555_IIC_CONTROLER);
		
	//矩阵按键,P0端口配置为输出,P1端口配置为输入,因为P1端口上用了上拉电阻
	nca9555_write_2config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,outcfg_dat);

	nca9555_write_outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0); //P0端口输出0

#ifdef 	BTNS_USE_INT   //宏在btns_leds.h中定义
	//中断引脚初始化
	//2. 中断引脚的初始化 PB12,外部中断12
	//2.1 时钟使能
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_AF);		
	
	gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, GPIO_PIN_12);	
	
	//2.2 复用为外部中断引脚,
	gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOB, GPIO_PIN_SOURCE_12);
	
	//设置触发方式,低电平触发
	exti_init(EXTI_12, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
	exti_interrupt_enable(EXTI_12);
	exti_interrupt_flag_clear(EXTI_12);
	//2.3 nvic允许中断
	//中断控制器使能,使用的是外部中断12
	nvic_irq_enable(EXTI10_15_IRQn,  7, 0);   //允许中断,并设置优先级

	//初始化之后读取一次
	matrix_keys_row_scan();	

#endif			
	memset(&g_btns_info,0,sizeof(g_btns_info));   //数据清零	
}








static uint8_t matrix_keys_row_scan(void)
{
	uint8_t key_row_dat;
	//读取P1(L信号)端口的值
	if(nca9555_read_inport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,1,&key_row_dat) == 0)  //表示读取成功
	{
		if((key_row_dat&0x3f) != 0x3f)   //只判断低6位,不相等表示有按键按下
		{
			return key_row_dat&0x3f;
		}
		else
		{
			return 0x3f;
		//	printf("ERROR: KEY_ROW_SCAN key_row_dat == 0x3f\r\n");
		}
	}
	else //iic读取失败
	{
		printf("ERROR: KEY_ROW_SCAN nca9555_read_inport\r\n");
		return 0xff;
	}
}

				
							

/***
 *函数名:KEY_SCAN
 *功  能:6*6按键扫描
 *返回值:1~36,对应36个按键,0表示没有检测到
 */
char matrix_keys_scan(void)
{    
    uint8_t key_row_num=0;        //行扫描结果记录
    uint8_t i,j;
	uint8_t index;   //
	static uint8_t release_report = 0;  //松开上报。
	
	key_row_num = matrix_keys_row_scan();
	if(key_row_num < 0x3f)   //读取到了一个有效的按键触发
	{	
		for(i=0;i<COL_NUM;i++)  //每一列扫描一次
		{
			//分六次,对行信号进行分别输出,任意时刻,只有一个行信号是0,其他都是高电平
			if(nca9555_write_outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, key_scan_line[i])) //P0端口输出0
			{
				printf("ERROR: KEY_ROW_SCAN nca9555_write_outport i=%d\r\n",i);
				continue;  //写入失败,直接往下试试
				//	return -1;
			}
			//再次读取输入
			key_row_num = matrix_keys_row_scan();

			for(j=0;j<ROW_NUM;j++)  //每一行扫描一次
			{
				index = 6*i+j;
				if(!((key_row_num>>j)&(1))) //按下
				{
					if(g_btns_info.pressCnt[index] < 2)
					{	
						g_btns_info.pressCnt[index]++;
						if(g_btns_info.pressCnt[index] == 2)//检测到不止1次
						{   //条件限制上报一次
							g_btns_info.value[index] = 1;
						//	g_btns_info.reportEn[index] = 1;  //按下上报
							send_btn_change_to_cpu(index+1,1); //发送按键按下/松开
							printf("----btn:%d press\r\n",index+1);
							release_report = 1;   //记录需要释放标志
						}
					}
				}
				else //松开
				{
					if(g_btns_info.value[index]) //之前的状态是按下
					{
						g_btns_info.value[index] = 0;
					//	g_btns_info.reportEn[index] = 2;   //松开上报
						send_btn_change_to_cpu(index+1,0); //发送按键按下/松开
						printf("++++btn:%d release\r\n",index+1);
						g_btns_info.pressCnt[index] = 0;
					}		
				}
			}
		}
	}
	else
	{
		if(release_report)  //需要上报释放信息。
		{		
			for(index=0;index<COL_NUM*ROW_NUM;index++)
			{
				if(g_btns_info.value[index]) //之前的状态是按下
				{
					g_btns_info.value[index] = 0;
				//	g_btns_info.reportEn[index] = 2;   //松开上报
					send_btn_change_to_cpu(index+1,0); //发送按键按下/松开
					printf("#####btn:%d release\r\n",index+1);
					g_btns_info.pressCnt[index] = 0;
				}
				
			}
		//	btn_start_scan = 0;   //按键不再扫描
			release_report = 0;
		}
	}
	
	//设置输出0,这样按下时,L端就会变为0,触发中断
	nca9555_write_outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0);	
	
	return 0;
}




#ifdef 	BTNS_USE_INT
//外部中断12的处理函数,按键按下和松开都会触发中断!!!!
void exint12_handle(void)
{

	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	
	xTaskNotifyFromISR(TaskHandle_key_Matrix, 0, eIncrement, &xHigherPriorityTaskWoken);  //唤醒休眠的任务
	//并且禁止中断
	exti_interrupt_disable(EXTI_12);   //扫描完毕之后再使能
}

#endif


//freeRTos任务
void task_matrix_keys_scan(void* arg)
{	
	//1. 矩阵按键扫描初始化
	matrix_keys_init();

	while(1)
	{	
		//等待任务被唤醒
		ulTaskNotifyTake(pdFALSE, portMAX_DELAY);   //减1,然后无限等待
		{	
			matrix_keys_scan();

			vTaskDelay(30);    //延时30ms
#ifdef 	BTNS_USE_INT	
			exti_interrupt_enable(EXTI_12);   //扫描完毕之后再使能		
#endif

		}		
	}
}

单个按键的识别是正常的,不在同列的组合按键也是没有问题的。

 

四、遇到的问题

4.1 部分组合键不能识别

同一列(同一根L线上)比如A2+A8这种类似的组合都不能正常识别。

按下A2时,(程序打印)提示A2被按下,A2不松开的情况下,同时按下A8,程序提示A2松开。

反之,先按下A8,提示A8按下,同时把A2也按下,提示A8松开。

32单片机矩阵键盘-同列组合键不能识别故障-已解决,单片机,stm32,单片机,嵌入式硬件

4.2 不正常的组合键的示波器波形(测试L引脚上的信号)

32单片机矩阵键盘-同列组合键不能识别故障-已解决,单片机,stm32,单片机,嵌入式硬件

 4.3 正常的按键的示波器波形(测试L引脚上的信号)

32单片机矩阵键盘-同列组合键不能识别故障-已解决,单片机,stm32,单片机,嵌入式硬件

 4.4 大致原因

从示波器来看的话,很明显的低电平没有拉下去,而单片机将那样的一个电平识别为高。

软件得到高电平,认为第一次的按键被释放。(实际是两个按键同时按下了)。

这确实是个芯片问题,没想到9535这个芯片输出低的时候,阻抗这么大,还是说两个按键同时按下之后,实际是芯片内部电路被短路了,因为有一个引脚是高,而另一个引脚是低,这个时候电流大,导致芯片内部一些保护措施?

32单片机矩阵键盘-同列组合键不能识别故障-已解决,单片机,stm32,单片机,嵌入式硬件

 H中只有一个为低,两个按键按下后,所以必然会有这样的问题是存在的。(这里假设A2和A8同时按下 的情况)

五、问题的解决

5.1  考虑列扫描的方式

行扫描有问题,那能不能换成列扫描呢?

两个按键在同一个列上,那换成列扫描,两个按键就肯定不会在同一行,这个确实是可以的。

然而,我尝试了一下,发现可以解决这个问题,但是同一行的两个按键又不可识别了。

这里因为行没有接上拉电阻,我只能用默认这个H的信号为低电平,有按键按下时是高电平

	//只有6行,这些数字用于分别扫描每一行
const uint8_t key_scan_line[] = {0x1,0x2,0x4,0x8,0x10,0x20};


 
//端口的配置,全部配置为输入接口
void matrix_keys_init(void)
{
	uint8_t outcfg_dat[2]={0xff,0xff};   //IIC芯片GPIO输出模式,对应的位要设置为0
	//1. iic的初始化
	nca9555_init(NCA9555_IIC_CONTROLER);
		
	//矩阵按键,P0端口配置为输出,P1端口配置为输入,因为P1端口上用了上拉电阻
	nca9555_write_2config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,outcfg_dat);

//	nca9555_write_outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0); //P0端口输出0
	
	//默认是输入,把输入寄存器读出来一次,读出来的值不关心
	nca9555_read_inport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0,outcfg_dat);
	nca9555_read_inport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,1,outcfg_dat);
	
	//P1 端口的输出寄存器全部配置为0,表示输出的时候会输出低电平
	outcfg_dat[0] = 0;
	outcfg_dat[1] = 0;
	nca9555_write_2outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,outcfg_dat);
	//key_board cs引脚PE8,不需要??
//	rcu_periph_clock_enable(RCU_GPIOE);			
//	gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_8);	
//	gpio_bit_reset(GPIOE, GPIO_PIN_8);

#ifdef 	BTNS_USE_INT   //宏在btns_leds.h中定义
	//中断引脚初始化
	//2. 中断引脚的初始化 PB12,外部中断12
	//2.1 时钟使能
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_AF);		
	
	gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, GPIO_PIN_12);	
	
	//2.2 复用为外部中断引脚,
	//GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9);
	gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOB, GPIO_PIN_SOURCE_12);
	
	//设置触发方式,低电平触发
	exti_init(EXTI_12, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
	exti_interrupt_enable(EXTI_12);
	exti_interrupt_flag_clear(EXTI_12);
	//2.3 nvic允许中断
	//中断控制器使能,使用的是外部中断12
	nvic_irq_enable(EXTI10_15_IRQn,  1, 2);   //允许中断,并设置优先级

	//初始化之后读取一次
	//matrix_keys_row_scan();	

#endif		
	
	memset(&g_btns_info,0,sizeof(g_btns_info));   //数据清零
	
	
}




uint8_t matrix_keys_row_scan(void)
{
	uint8_t key_row_dat;
	
	nca9555_write_config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0x0);  //设置输出,释放一下电平
	nca9555_write_config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0xff);  //设置输入
	//读P0端口,没有按键时,应该是0,有按键时不是0
	if(nca9555_read_inport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0,&key_row_dat) == 0)  //表示读取成功
	{
		if((key_row_dat & 0x3f) != 0)   //只判断低6位,不相等表示有按键按下
		{
			return key_row_dat&0x3f;
		}
		else
		{
			return 0x7f;
		//	printf("ERROR: KEY_ROW_SCAN key_row_dat == 0x3f\r\n");
		}
	}
	else //iic读取失败
	{
		printf("ERROR: KEY_ROW_SCAN nca9555_read_inport\r\n");
		return 0xff;
	}
}




 
char matrix_keys_scan(void)
{    
//    uint8_t Key_Num=0xff;            //1-16对应的按键数
    uint8_t key_row_num=0;        //行扫描结果记录
    uint8_t i,j;
	uint8_t index;   //
	static uint8_t release_report = 0;  //松开上报。
	
	key_row_num = matrix_keys_row_scan();
	if(key_row_num < 0x3f)   //读取到了一个有效的按键触发
	{
		for(i=0;i<COL_NUM;i++)  //每一列扫描一次
		{
			
			//P1端口只有一个引脚为输入模式,其他为输出,并且输出低电平
			if(nca9555_write_config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,1, key_scan_line[i])) 
			{
				printf("ERROR: KEY_ROW_SCAN nca9555_write_outport i=%d\r\n",i);
				continue;  //写入失败,直接往下试试
				//	return -1;
			}
			//再次读取输入
			key_row_num = matrix_keys_row_scan();
			
		//	printf("i = %d num = %#x\r\n",i,key_row_num);
			
			if(key_row_num >= 0x3f)
				continue;
			
			for(j=0;j<ROW_NUM;j++)  //每一行扫描一次
			{
				index = 6*i+j;
				if(((key_row_num>>j)&(1))) //按下
				{
					if(g_btns_info.pressCnt[index] < 2)
					{	
						g_btns_info.pressCnt[index]++;
						if(g_btns_info.pressCnt[index] == 2)//检测到不止1次
						{   //条件限制上报一次
							g_btns_info.value[index] = 1;
						//	g_btns_info.reportEn[index] = 1;  //按下上报
							send_btn_change_to_cpu(index+1,1); //发送按键按下/松开
							printf("----btn:%d press\r\n",index+1);
							release_report = 1;   //记录需要释放标志
						}
					}
				}
				else //松开
				{
					if(g_btns_info.value[index]) //之前的状态是按下
					{
						g_btns_info.value[index] = 0;
					//	g_btns_info.reportEn[index] = 2;   //松开上报
						send_btn_change_to_cpu(index+1,0); //发送按键按下/松开
						printf("++++btn:%d release\r\n",index+1);
						g_btns_info.pressCnt[index] = 0;
					}
		
				}
			}		
		}
	}
	else
	{
		if(release_report)  //需要上报释放信息。
		{		
			for(index=0;index<COL_NUM*ROW_NUM;index++)
			{
				if(g_btns_info.value[index]) //之前的状态是按下
				{
					g_btns_info.value[index] = 0;
				//	g_btns_info.reportEn[index] = 2;   //松开上报
					send_btn_change_to_cpu(index+1,0); //发送按键按下/松开
					printf("####btn:%d release\r\n",index+1);
					g_btns_info.pressCnt[index] = 0;
				}
				
			}
			btn_start_scan = 0;   //按键不再扫描
			release_report = 0;
		}
	}
	//P1端口全部改为输入模式
	//nca9555_write_config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0x0);  //设置输出,释放一下电平
	nca9555_write_config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,1, 0xff);
//	nca9555_write_config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0xff);
	//nca9555_write_outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0);	
	
	return 0;
}

5.2  在原有行扫描的基础上,增加列扫描方式。

后来发现如果按下两个键的时候,软件检测到了按键松开,那我能否在检测到松开的时候,再进行一次列扫描呢?

答案是可以的,经过一番调试,已经能够正常识别了。

(这里因为行没有接上拉电阻,我只能用默认这个H的信号为低电平,有按键按下时是高电平)

注意增加的两个函数

//检测到按键释放后,扫描一次行。
//P1(L信号)输出高,P0(H信号) 改为输入,读到某些位高电平表示有按键被按下,如果是0则表示按键都松开
//col : 0-5 分别表示L1 - L6
uint8_t matrix_keys_col_scan(uint8_t col)

//识别是哪个按键
//col : 0-5 分别表示L1 - L6
void matrix_keys_scan_col(uint8_t col,uint8_t dat)

#if 1   //2023-02-09  增加



//检测到按键释放后,扫描一次行。
//P1(L信号)输出高,P0(H信号) 改为输入,读到某些位高电平表示有按键被按下,如果是0则表示按键都松开
//col : 0-5 分别表示L1 - L6
uint8_t matrix_keys_col_scan(uint8_t col)
{
	uint8_t outcfg_dat[2]={0xff,0};  //P0改为输入,P1改为输出,并且输出高
	uint8_t key_row_dat;
	uint8_t outdat = 0;
	uint8_t ret;

	//P0改输入之前,先输出0,释放一下电平
	nca9555_write_outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0);
	//配置
	nca9555_write_2config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,outcfg_dat);
//	nca9555_write_config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0x0);  //设置输出,释放一下电平
//	nca9555_write_config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0xff);  //设置输出,释放一下电平
	
	//P1 输出1
	outdat = 1<< col;
	nca9555_write_outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,1, outdat); //P1(L信号)端口输出0
	
	//读P0(H信号)端口,没有按键时,应该是0,有按键时不是0
	if(nca9555_read_inport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0,&key_row_dat) == 0)  //表示读取成功
	{
		if((key_row_dat & 0x3f) != 0)   //只判断低6位,不相等表示有按键按下
		{
			ret = key_row_dat&0x3f;
		}
		else
		{
			ret = 0x7f;
		//	printf("ERROR: KEY_ROW_SCAN key_row_dat == 0x3f\r\n");
		}
	}
	else //iic读取失败
	{
		printf("ERROR: KEY_ROW_SCAN nca9555_read_inport\r\n");
		ret = 0xff;
	}
	
	//配置,默认是P0(H信号)输出,P1(L信号) 改为输入
	outcfg_dat[0]=0;   
	outcfg_dat[1]=0xff;
	nca9555_write_2config(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,outcfg_dat);
	
	return ret;
}


//识别是哪个按键
//col : 0-5 分别表示L1 - L6
void matrix_keys_scan_col(uint8_t col,uint8_t dat)
{	
	uint8_t index,j;
	if(!dat || (dat >= 0x3f))
		return;
	
	for(j=0;j<ROW_NUM;j++)  //每一行扫描一次
	{
		index = 6*j+col;
		if(((dat>>j)&(1))) //高电平表示按下,低表示松开
		{
			if(!g_btns_info.value[index])
			{	
			//	g_btns_info.pressCnt[index]++;
			//	if(g_btns_info.pressCnt[index] == 2)//检测到不止1次
				{   //条件限制上报一次
					g_btns_info.value[index] = 1;
				//	g_btns_info.reportEn[index] = 1;  //按下上报
					send_btn_change_to_cpu(index+1,1); //发送按键按下/松开
					printf("@#@#btn:%d press\r\n",index+1);
				//	release_report = 1;   //记录需要释放标志
				}
			}
		}
		else if(g_btns_info.value[index]) //之前的状态是按下
		{								
			g_btns_info.value[index] = 0;
		//	g_btns_info.reportEn[index] = 2;   //松开上报
			send_btn_change_to_cpu(index+1,0); //发送按键按下/松开
			printf("@@@btn:%d release\r\n",index+1);
			g_btns_info.pressCnt[index] = 0;			
		}
	}	
}



#endif




							

/***
 *函数名:KEY_SCAN
 *功  能:6*6按键扫描
 *返回值:1~36,对应36个按键,0表示没有检测到
 */
char matrix_keys_scan(void)
{    
    uint8_t key_row_num=0;        //行扫描结果记录
    uint8_t i,j;
	uint8_t index,col_dat;   //
	static uint8_t release_report = 0;  //松开上报。
	
	key_row_num = matrix_keys_row_scan();
	if(key_row_num < 0x3f)   //读取到了一个有效的按键触发
	{
		for(i=0;i<COL_NUM;i++)  //每一列扫描一次
		{
			if(nca9555_write_outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, key_scan_line[i])) //P0端口输出0
			{
				printf("ERROR: KEY_ROW_SCAN nca9555_write_outport i=%d\r\n",i);
				continue;  //写入失败,直接往下试试
				//	return -1;
			}
			//再次读取输入
			key_row_num = matrix_keys_row_scan();

			for(j=0;j<ROW_NUM;j++)  //每一行扫描一次
			{
				index = 6*i+j;
				if(!((key_row_num>>j)&(1))) //按下
				{
					if(g_btns_info.pressCnt[index] < 2)
					{	
						g_btns_info.pressCnt[index]++;
						if(g_btns_info.pressCnt[index] == 2)//检测到不止1次
						{   //条件限制上报一次
							g_btns_info.value[index] = 1;
						//	g_btns_info.reportEn[index] = 1;  //按下上报
							send_btn_change_to_cpu(index+1,1); //发送按键按下/松开
							printf("----btn:%d press\r\n",index+1);
							release_report = 1;   //记录需要释放标志
						}
					}
				}
				else //松开
				{
					if(g_btns_info.value[index]) //之前的状态是按下
					{
						#if 1
						col_dat = matrix_keys_col_scan(j);
				//		printf("j = %d dat = %#x,index = %d\r\n",j,col_dat,index);
						if(col_dat >= 0x3f)
						{						
							g_btns_info.value[index] = 0;
						//	g_btns_info.reportEn[index] = 2;   //松开上报
							send_btn_change_to_cpu(index+1,0); //发送按键按下/松开
							printf("++++btn:%d release\r\n",index+1);
							g_btns_info.pressCnt[index] = 0;
						}
						else{
							matrix_keys_scan_col(j,col_dat);
						}
						#else
						g_btns_info.value[index] = 0;
					//	g_btns_info.reportEn[index] = 2;   //松开上报
						send_btn_change_to_cpu(index+1,0); //发送按键按下/松开
						printf("++++btn:%d release\r\n",index+1);
						g_btns_info.pressCnt[index] = 0;
						#endif
					}		
				}
			}
		}
	}
	else
	{
		if(release_report)  //需要上报释放信息。
		{		
			for(index=0;index<COL_NUM*ROW_NUM;index++)
			{
				if(g_btns_info.value[index]) //之前的状态是按下
				{
					#if 1
					j = index%6;
					col_dat = matrix_keys_col_scan(j);
				//	printf("22- j = %d dat = %#x,index = %d\r\n",j,col_dat,index);
					if(col_dat >= 0x3f)
					{						
						g_btns_info.value[index] = 0;
					//	g_btns_info.reportEn[index] = 2;   //松开上报
						send_btn_change_to_cpu(index+1,0); //发送按键按下/松开
						printf("####btn:%d release\r\n",index+1);
						g_btns_info.pressCnt[index] = 0;
					}
					#else
				
					g_btns_info.value[index] = 0;
				//	g_btns_info.reportEn[index] = 2;   //松开上报
					send_btn_change_to_cpu(index+1,0); //发送按键按下/松开
					printf("#####btn:%d release\r\n",index+1);
					g_btns_info.pressCnt[index] = 0;
					#endif
				}
				
			}
		//	btn_start_scan = 0;   //按键不再扫描
			release_report = 0;
		}
	}

	nca9555_write_outport(NCA9555_IIC_CONTROLER,KEYS_IIC_ADDR,0, 0);	
	
	return 0;
}

以为这是一个死结,但是只要能想到办法,还是可以解决的。

两个键组合已经没有问题了,但是这个软件仍然有bug,三个键组合的时候,还是有些不能识别的情况,如同在一个列上两个按下,第三个与其中一个同行,这样无法识别第三个按键的按下和松开,好在项目没有这个需求,先这样吧。

感谢大家多来批评指正。谢谢文章来源地址https://www.toymoban.com/news/detail-611029.html

到了这里,关于32单片机矩阵键盘-同列组合键不能识别故障-已解决的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 51单片机(六)矩阵键盘和矩阵键盘密码锁

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月04日
    浏览(38)
  • 单片机实验四 矩阵键盘实验

    1.熟悉实验软件和硬件,进行正确的接线; 2.按下按键,1602显示其键值; 1.Keil uVision4 2.PZISP自动下载软件 3.HC6800S开发板 按键: 矩阵键盘 4X4 的矩阵键盘,一共是16 个按键。我们照习惯称横为“行”,“竖”为列。那么5、6、7、8 我们称之为“行线”,则1、2、3、4 称为“列线

    2024年02月02日
    浏览(27)
  • 6.51单片机之矩阵键盘

    👻 1.矩阵键盘的介绍 在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式。 采用逐行或逐列的“扫描”,就可以读出任何位置按键的状态。 结构:在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式。在矩阵式键盘中,每条

    2024年02月07日
    浏览(30)
  • 三、单片机关于矩阵键盘的设计

    目录 前言 一、矩阵键盘 1、矩阵键盘原理: 2、矩阵键盘原理图 二、单片机原理图绘制 1、所需元件名称 2、绘制原理图 三、编写程序 总结 今天我们来学习如何通过AT89C52上的矩阵按键来控制数码管显示对应字母或者数字,期间我们用到的软件有proteus和keil两个软件,欢迎大

    2024年02月04日
    浏览(24)
  • 51单片机学习笔记-4矩阵键盘

    [toc] 注:笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。 注:工程及代码文件放在了本人的Github仓库。 在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式。采用逐行或逐列的“扫描”,就可以读

    2024年02月06日
    浏览(38)
  • 51单片机矩阵键盘——数码管显示

    当我们熟悉了数码管的位选与段选,并了解的矩阵键盘的扫描之后就可以编写程序了。 按下矩阵键盘S1并松开,数码管第一位(LED8)显示0;按下矩阵键盘S2并松开,数码管第一位显示1;...按下矩阵键盘S16并松开,数码管第一位显示F; 矩阵键盘扫描(输入扫描)     原理:

    2024年02月11日
    浏览(41)
  • 51单片机实现矩阵键盘密码锁

    使用51单片机的矩阵键盘模块以及led1602显示屏,实现模拟密码锁。 当程序烧录到单片机中后,led1602屏幕会显示文字。 第一行会显示单词“PASSWORD”,第二行显示4个0,表示我们要写入的四位密码,每位默认为0。 矩阵键盘前两行与第三行的前两个分别代表输入1-9与0,第三行第

    2024年02月03日
    浏览(37)
  • 51单片机矩阵键盘——LCD1602显示

    本次的实验需要用到LCD1602液晶屏,需要用到LCD1602.c 与LCD1602.h文件链接: https://download.csdn.net/download/YLG_lin/86404949 使用矩阵键盘随意按下按键,LCD1602第二行前两位就会显示对应按键按下的数字。 在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式 采用

    2023年04月09日
    浏览(29)
  • 51单片机入门——矩阵键盘(附51代码)

    硬件如图非常简单,将一个4*4的矩阵键盘的8个管脚引到端子上,在连接到8个I/O口上,ARRAY_H代表着行,ARRAY_L代表着列,当行与列的电平都置低的时候,就选中的相应的矩阵按键,比如当s1按下时,ARRAY_H1会置低,其他ARRAY_H给高电平,那么选中的就是第一行,然后到列,ARRAY_

    2024年02月11日
    浏览(27)
  • 51单片机学习--矩阵键盘、电子密码锁

    矩阵键盘的模块原理图: 功能:按下矩阵按键,在LCD上显示对应的数值。 采用模块化编程,在此只给出MatrixKey.c 和 main.c 两部分代码 这里要注意必须先判断KeyNum是否为0,否则while不断循环会不断显示0,导致看不到按下按钮显示的数字 要利用写好的矩阵键盘来制作一个密码锁

    2024年02月16日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包