IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 基于stm32f031c6t6的按键扫描,带超时重发机制与校验。 -> 正文阅读

[嵌入式]基于stm32f031c6t6的按键扫描,带超时重发机制与校验。

首先能你需要自己做一块最小系统,因为计时精度不需要太高,所以为了节省成本可以省去晶振,直接使用芯片内部的时钟。这是我的原理图采用的是stm32f031c6t6某宝5块钱,

硬件部分

?

?然后你可根据你的按键的模具画一块孔位匹配大小相等的pcb,并选用按键,我这边使用的是薄膜按键,当然你也可以使用机械按键或者更好的。最终得到pcb


软件部分

使用stm32cubemx进行辅助开发首先先勾选 “debug serial wire”(这非常重要不然生成的工程烧录后就会出现烧写一次后无法烧写)

?然后正常的设置gpio口的模式因为这边我使用的是5x4所以需要使用9个io口,且使用4个io口为输出,5个引脚为输入检测。io口配置为下图

?然后在配置串口1实现串口1的收发模式找到串口1 mode选择asynchronous。

?并配置相关的串口参数,一般就是改改波特率这是我的串口配置

?如何选择clock configuration 进行时钟配置如下图

?如何配置定时器进行超时判定,这里使用的是定时器2,这里呢涉及预分频和重装值,通过对时钟进行分频获得我们想要的频率,就是多少秒计一个数,以及什么时候触发计数,什么时候触发重装,触发定时器中断执行我们想要的程序。其中prescaler为预防频值这里是4799,把48mhz进行分频,48000000(48m)/(4799+1)=10000(10khz)根据频率公式:f=1/T ,t=1/f=0.1ms意思是每0.1ms电平变化一次,而我这边选择的模式为上升沿计数,便是下方的counter mode 选择为up。而重装值则为你需要的时间:T / t(计数的时间)-1,如我这边需要300ms 所以得到的period(重装值)就为2999.

?配置完后就可以进行代码的生成了选择 project manager,选择你想要的文件夹,和代码编译器

我这里选择的是keil 5 然后点击右上角的 GENERATE CODE 进行生成

?然后就可以开始写逻辑功能了

串口配置

首先可以先写个把C语言的printf给放到我们的串口中,不用也可以,然后在stm32f0xx.hal.c中加入下面的代码,串口发送定义到回串口1

#include <stdio.h>
extern UART_HandleTypeDef huart1;   //声明串口
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
 
/**
  * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;

且在主函数的文件中添加宏定义,好定义一个数组

#define RXBUFFERSIZE  256
char RxBuffer[RXBUFFERSIZE];

并在编译出来的main函数中,添加定时器中断函数。便可进行测试,定时器,与串口发送是否正常

主函数中的while(1)死循环中添加

HAL_TIM_Base_Start_IT(&htim2);//开启定时器2

在main.c文件的末尾添加以下定时器中断函数。

/*******************定时器中断*******/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	 if(htim->Instance==TIM2)
	{
        printf("test,test\n");
   __HAL_TIM_CLEAR_FLAG(&htim2,TIM_SR_UIF);//清除tim2的中断标志位

	}
	 
}

烧录到芯片后,便会定时发送 test ,我这便为300ms发送一次,倘若因为串口发送太快,亦可使用吧重装值preiod 调到29999,使其3s发送一次,便可大概知道代码是否正常。

代码段在以下此段中断period的值

?当成功后便可开始写

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?按键键测

按键扫描有许多种写法,如比较老实的使用 :if else if 或者使用 获取io寄存器的值进行取值与swich判断便可(当然你要明白他代表的意义)

要写逻辑,首先呢便要搞清楚你需要的逻辑,按键矩阵按键扫描方式我这里有两种,(当然会有其他我没想到的方式)我这 写的有 :

方法一,很简单通过单独对每一行进行输出,并进行io口的扫描,得到你输出的行数,与扫描列得的列数可知道几行几列。并赋值(简单)当然你也可以通过寄存器简化,代码就不用写这么多,这就是为什么说寄存器运行比(不管是标准库还是hal库,不接受反驳)库函数快的原因。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?实现代码为

char KeyPad_Scan()
{
	
		u16 readvalue = 0;
	 
	   
	    char data1=0;
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_RESET);//
		HAL_Delay(2);
		readvalue =GPIOA->IDR;	//读取gpioA所有的引脚
		readvalue &= 0x01f0;				//保留低8位的高5位的值
		if(readvalue != 0x01f0) 				//高5位引脚有一个被按下
		{
				HAL_Delay(5);
				readvalue =GPIOA->IDR;	//读取gpioA所有的引脚
				readvalue &= 0x01f0;				//保留低8位的值
				if(readvalue != 0x01f0) 				//高5位引脚有一个被按下
				{
                         
						HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_SET);//
						HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
						HAL_Delay(2);
						readvalue =GPIOA->IDR;	//读取gpioA所有的引脚
						readvalue &= 0x01f0;				//保留低8位的值
						if(readvalue != 0x01f0) 				//高5位引脚有一个被按下
						{
							HAL_Delay(2);
								if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0 )
								{
									 data1 = '1';
								}
								else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0)
								{
									 data1 = '4';
								}
								else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_6)==0)
								{
									data1 = '7';
								}
								else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_7)==0)
								{
									data1 = '0';
								}
								else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8)==0)
								{
									data1 = 'h';
								}
						}else{
								HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_SET);
								HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
								HAL_Delay(2);
								readvalue =GPIOA->IDR;	//读取gpioA所有的引脚
								readvalue &= 0x01f0;				//保留低8位的值
								if(readvalue != 0x01f0) 				//高5位引脚有一个被按下
								{
									HAL_Delay(2);
										if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0 )
										{
											data1 = '2';
										}
										else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0)
										{
											data1 = '5';
										}
										else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_6)==0)
										{
											data1 = '8';
										}
										else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_7)==0)
										{
											data1 = '.';
										}
										else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8)==0)
										{
											data1 = 'g';
										}
								}else{
								
										HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_SET);
										HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
										HAL_Delay(2);
										readvalue =GPIOA->IDR;	//读取gpioA所有的引脚
										readvalue &= 0x01f0;				//保留低8位的值
										if(readvalue != 0x01f0) 				//高5位引脚有一个被按下
										{
												HAL_Delay(2);
												if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0 )
												{
														data1 = '3';
												}
												else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0)
												{
														data1 = '6';
												}
												else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_6)==0)
												{
														data1 = '9';
												}
												else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_7)==0)
												{
														data1 = 'e';
												}
												else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8)==0)
												{
														data1 = 'f';
												}
										}else{
												
												HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_SET);
												HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
												HAL_Delay(2);
												readvalue =GPIOA->IDR;	//读取gpioA所有的引脚
												readvalue &= 0x01f0;				//保留低8位的值
												if(readvalue != 0x01f0) 				//高5位引脚有一个被按下
												{
														if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0 )
														{
															data1 = 'a';
														}
													   else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0)
														{
															data1 = 'b';
														}
													   else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_6)==0)
													   {
														   data1 = 'c';
													   }
													   else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_7)==0)
													   {
														   data1 = 'd';
													   }
												}
										}

								}

						}


				}

		}
		if(data1!=0)	//此处为把值存储在一个数组里,并把按键对应值保存在数组里
		{
            for(char j;j<5;j++)
			{
			   if (data[j]==0)
			   {		 data[j]=data1;
				   return data1;
			   }
			}

		}
       return data1;
		
;

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */
}

方式二,先把行(则是上面原理图中的x1234其代表的io)进行?置高输出,然后列(则是上面原理图的y12345其代表的io)进行io扫描,那一列变为高电平则为此列有效,把数据保存,在把列进行置高输出,然后行进行io扫描,哪一行为高电平则此行有效。(逻辑可能有点乱,但逻辑是这个逻辑) 提示(内含一下简单的寄存器操作,学过51虽然我没学过应该比较容易理解)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 实现代码为

 short KeyPad_Scan()
 {


	
	u16 readvalue = 0;
	char data;
	u16 re=0;
	u16 re1=0;
	u16 re2=0;
	KeyPad_Init1();				//PA0~3上拉PA4~8下拉
	readvalue =GPIOA->IDR;	    //读取gpioA所有的引脚
	readvalue &= 0x01ff;				//保留低8位的值
	if(readvalue != 0x000f) 				//高5位引脚有一个被按下
	{
		 HAL_Delay(10);//按键消抖10ms
		readvalue =GPIOA->IDR;	//读取gpioA所有的引脚
		if(readvalue != 0x000f)
		{
			re1 = GPIOA->IDR;	//读取gpioA所有的引脚
			re1 &= 0x01f0;  				//保留PA4~PA8


			 
			KeyPad_Init2();  				//PA4~8上拉,PA0~3下拉
			HAL_Delay(1);//经我测试,这里延迟50ms反应最快而很少出现不反应的状况
			re2 =GPIOA->IDR;	//读取gpioA所有的引脚
			re2 &= 0x000f;				//保留PA0-PA3的值
//		
			 re=re1|re2;					//相与,就知道哪一行那一列被按下
      		 switch(re)
		     {
			 case 0x0011: data='1';break;//以下为把按键对应的值赋予
			 case 0x0012: data='2';break;
			 case 0x0014: data='3';break;
             case 0x0018: data='a';break;
             case 0x0021: data='4';break;
			 case 0x0022: data='5';break;
			 case 0x0024: data='6';break;
             case 0x0028: data='b';break;
             case 0x0041: data='7' ;break;
			 case 0x0042: data='8' ;break;
			 case 0x0044: data='9' ;break;
             case 0x0048: data='c';break;
             case 0x0081: data='0' ;break;
			 case 0x0082: data='.' ;break;
			 case 0x0084: data='f' ;break;
             case 0x0088: data='d';break;
             case 0x0101: data = 'h';break;
			 case 0x0102: data='g';break;
			 case 0x0104: data='e';break;
			 default: return 1;			 
		     }		 
			return 0;
		}
	  
	 
	}
	return 1;
 }

?这其中有有两个函数的定义如下

void KeyPad_Init2()
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

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

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8, GPIO_PIN_SET);

  /*Configure GPIO pins : PA0 PA1 PA2 PA3 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN ;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PA4 PA5 PA6 PA7
                           PA8 */
  GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8;
  GPIO_InitStruct.Mode =  GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	
}

void KeyPad_Init1()
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

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

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_SET);

  /*Configure GPIO pins : PA0 PA1 PA2 PA3 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP ;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PA4 PA5 PA6 PA7
                           PA8 */
  GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8;
  GPIO_InitStruct.Mode =  GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	
}

以上便是按键扫描部分。(方法一和方法二选其一便可)

因为考虑接收的芯片的接收中断,我们不可让值一直发,使用我们要使得按键弹起时便不再发送,得写个下面的代码在主函数的死循环中

		 aaaa:
		 while((GPIOA->IDR & 0x01f0)!= 0x01f0)HAL_Delay(5); 	//读取gpioA所有的引脚
		 HAL_Delay(10);
		 if((GPIOA->IDR & 0x01f0 )!= 0x01f0)goto aaaa; 	//读取gpioA所有的引脚

以下为串口协议部分,因为很多时候都不单单的只发数值,要考虑数据错误,以及接收方接收到的数据是否完整,所以需要使用校验这就出现了很多校验方法(什 么 crc等等)这里就不讲了,我这里的校验单纯只是把我自身提前的定义?数据头 如我这里的字符‘k’,然后跟着的是数据,然后是数据发送次数,已经把前面全部相加得到的校验数值。 如图下(我这里是把串口发送也放在一起了)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

目录

硬件部分

软件部分

串口配置

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?按键键测

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

?串口协议

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 接收中断? ?


?串口协议

char data_send(char num,char data)
{
	char i;
	char check;
	senddata[4]=0;
	senddata[0]='k';
	senddata[1]=data;
	senddata[3]=(((num&0xf)>9)?((num&0xf)-9+'a'):((num&0xf)+'0'));
	senddata[2]=((((num>>4)&0xf)>9)?(((num>>4)&0xf)-9+'a'):(((num>>4)&0xf)+'0'));
//	check = 'k'+data+(((num&0xf)>9)?((num&0xf)-9+'a'):((num&0xf)+'0'))+((((num>>4)&0xf)>9)?(((num>>4)&0xf)-9+'a'):(((num>>4)&0xf)+'0'));
	check = senddata[0]+senddata[1]+senddata[2]+senddata[3];
	senddata[4]=check;
	for(i=0;i<5;i++)printf("%c",senddata[i]);
//	printf(senddata);
//	printf("\n");
	return check;
	
}

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 以下为我的

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 接收中断? ?

char c;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_UART_TxCpltCallback could be implemented in the user file
   */
 
	if(Uart1_Rx_Cnt >= 255)  //溢出判断
	{
		Uart1_Rx_Cnt = 0;
		memset(RxBuffer,0x00,sizeof(RxBuffer));
		HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF); 	
//        TIM2->CR1|=0x01;
	}
	else
	{
		
		RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
		if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
		{
			c=1;
			char i;	
			for(i=0;i<5;i++) 
			{
				if(RxBuffer[i]==senddata[i])
				{
				}
				else
				{
					c=0;
				}

		    }
			if(c)
			{
			   
				   for(i=0;i<4;i++)
				   {
					    data[i]= data[i+1];//数据前移 
					   
				   }
				    num++;
				    data[4]= 0;//清空数组		   
				if(data[0]==0){//关闭定时器
					HAL_TIM_Base_Stop_IT(&htim2);
				}
			   
			}
            while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
			Uart1_Rx_Cnt = 0;
			memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
		}
	}
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
}

数据处理完了,发送的数据也好了,最后的是超时重发了, 原理为:当我发送完数据后开启定时器,定时器开始计时,当上机接收到数据后把上机收到的数据回发,当我这边检测到数据,产生接收中断后便接收数据,然后对数据进行解校验和与原发送的数据进行对比,最后才进行关闭定时器,还是,不关等待定时器进行溢出然后发送数据。

最后功能便实现了?

实现了当按键按下后串口便发送,且每300ms发送一次,直到返回一样的数据后才暂停发送或者发送下一个按键数据。

这为板子

新人写作,一定有误,望多见谅。请多包涵。全部的代码随后我也发上,请随意取用。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-20 12:38:50  更:2021-10-20 12:40:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:29:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码