百问网七天物联网智能家居第5篇

1. 环形队列

       队列就是一种先入先出的数据结构。实际应用中,譬如按键检测,把按键信息存入环形队列,然后,在循环中再读出进行处理。环形队列的元素,不一定是简单的字节数据,也可以是抽象的结构体数据,譬如按键的事件信息结构体,里面包含按键的ID,键值等等。

       结合到动态分配,可以将队列的长度进行灵活的设置。环形队列的结构体如下:

typedef struct {
	uint8_t   * fifo;				// 环形fifot头地址
	uint16_t	size;				// 环形fifo大小
	uint16_t	pWrite;				// 写位置
	uint16_t	pRead;				// 读位置
}ringBuffer,* pRingBuffer;

抽象出这个结构体目的为了指定操作对象,本质是对里面的元素fifo指向的数组进行操作,pWrite指向待写入的数据位置,pRead指向待读出的数据位置。这里需要注意,pWrite指向的地方,还没有数据写入,实际操作过程中,如果队列有空间,则先写入数据,再把pWrite位置后移。pRead指向的位置,已经写入了数据,读出该有效数据之后,再把pRead位置后移。故读和写,都是先操作数据,然后再操作位置。

队列为空的条件为 读和写的位置相同。队列为满的条件,是写的位置处于读的位置后面,及(pWrite+1)%队列长度 == pRead , 这里需要说明,为了避免读写位置相等时候,是队列满还是空的歧义,才指定上述的队列满的条件。以此指导,队列的大小如果为N,则队列里面最大只能够储存N-1个数据。相当于牺牲了一个存储空间。环形队列的初始化函数如下:

int driver_ringFIFO_Init(pRingBuffer pBuffer, uint16_t size)
{
	if((pBuffer==NULL)||(size==0))	return -1;
	pBuffer->size = size;
	pBuffer->fifo = (uint8_t *)malloc(pBuffer->size);		// 包含于stdlib.h
	pBuffer->pWrite = 0;
	pBuffer->pRead  = 0;
	return 0;
}

写入1个字节的函数:

int driver_ringFIFO_writeByte(pRingBuffer pBuffer, uint8_t  dat)
{
	if((pBuffer==NULL)||(pBuffer->fifo==NULL)) 	return -1;	
	if((pBuffer->pWrite+1)%pBuffer->size == pBuffer->pRead)
	{	
		return -2;		// fifo满了,无法写入
	}
	else
	{
		pBuffer->fifo[pBuffer->pWrite] = dat;
		pBuffer->pWrite = (pBuffer->pWrite + 1)%pBuffer->size;	// 移动到写一个待写入位置
	}
	return 0;
}

读出1个字节的函数:

int driver_ringFIFO_readByte(pRingBuffer pBuffer, uint8_t * dat)
{
	if((pBuffer==NULL)||(pBuffer->fifo==NULL)||(dat==NULL)) 	return -1;	
	if(pBuffer->pWrite == pBuffer->pRead)
	{	
		return -2;		// fifo空了,无法读出数据
	}
	else
	{
		*dat = (pBuffer->fifo[pBuffer->pRead]);
		pBuffer->pRead = (pBuffer->pRead + 1)%pBuffer->size;  // 移动到写一个待读出位置
	}
	return 0;
}

读或者写多个字节的数据,就是调用上述读写1个字节的函数,不再赘述。

2. 按键检测

不同于以前的软件延时,这次视频中的方法配合了 外部中断和定时器中断的方法。当按键按下或者弹起的时候,会有多次的整栋,如果此时按键对应的IO开启了上升沿和下降沿触发的中断,会触发多次中断。开一个变量记录触发时间,每次中断的时候,就更新触发时间,触发时间往后加50ms(也可以是其它值)相当于按键检测判断的时刻,在定时器中断中,不断的检测时间,如果时间等于按键检测判断时间,则读按键的电平状态,如果是0,则是按下,记录按下时刻。如果是1,则是弹起,记录弹起时刻。这两个时间都不为0,则发生了一次按键按下弹起的事件。故可以抽象一个按键事件的结构体:

typedef struct _keyEvent
{
	uint16_t	ID;			// 按键ID
	uint16_t	time;		// 按键持续时间	
}KeyEvent,*pKeyEvent;

其实按键结构体可以根据实际,有更加复杂的元素。譬如可以加入按键处理的回调函数,如加入函数指针。当然也可以定义一个虚函数,作为有按键事件的处理函数。

结合上面的环形队列,可以定义一个按键事件的对象:

static	ringBuffer		keyEventFIFO={0};			// KeyEventFIFO对象

大小定义可以保存16个按键事件的结构体

	driver_ringFIFO_Init(&keyEventFIFO, sizeof(KeyEvent)<<4);	// 16个 KeyEvent结构体的大小

这里需要说明,实际中无法正好同时保存16个按键事件,原因是虽然开辟了16*4 = 64个字节的空间,但是只能够保存63个数据,队列就满了。当有按键事件时候,写入环形队列:

void driver_key_tickScan(void)
{
	KeyEvent			keyPressEvent = {0};
	static 	uint32_t 	keyDownTick = 0;		// 按键按下的tick
	static	uint32_t	keyUpTick = 0;			// 按键松开的tick
			uint32_t	temp = 0;
			temp = HAL_GetTick();
	if(keyTriggerTick==temp)
	{
		if(0==HAL_GPIO_ReadPin(KEY_GPIO_PORT,KEY_GPIO_PIN))		// 按键按下
		{
			keyDownTick = temp;
		}
		else													// 按键松开
		{
			keyUpTick = temp;				
		}
		if((keyUpTick!=0)&&(keyDownTick!=0))
		{
			keyPressEvent.ID = 'A';								// 按键ID
			keyPressEvent.time = keyUpTick - keyDownTick;
			keyUpTick = keyDownTick = 0;						// 清零,方便下次使用	
			driver_ringFIFO_writeNBytes(&keyEventFIFO, (uint8_t *)&keyPressEvent.ID, sizeof(keyPressEvent));			
		}
	}
}

该按键扫描函数,可以放入1ms的定时器中断中,如systick中断中。上面写入环形队列的函数中,传入的是结构体第一个元素的地址,也可以是结构体的地址,每次大小就是结构体的大小。

大循环中执行的函数如下:

/* 放到大循转之中,按键处理函数  */
void driver_key_exe(void)
{
	KeyEvent	keyPressEvent = {0};
	// 1. 按键队列中取出按键事件
	driver_ringFIFO_readNBytes(&keyEventFIFO, (uint8_t *)&keyPressEvent, sizeof(keyPressEvent));
	// 2. 执行按键处理函数 
	driver_key_Callback(&keyPressEvent);
}

这里看到读出函数是将环形队列中的数据,整体读出到结构体中,这样非常方便。最后调用按键处理函数,并将刚才读出的按键信息传入。该回调函数定义为虚函数,方便用户自己实现具体内容:

/* 按键处理函数 */
__weak void driver_key_Callback(KeyEvent * pKeyEvent)
{
  UNUSED(pKeyEvent);
}

这样子,调用该按键的方式就很简单了,首先定义一个 按键事件环形队列,然后将driver_key_exe()放入大循环中,最后自己重定义按键处理的回调函数,如本例

/* 按键回调函数 */
void driver_key_Callback(KeyEvent * pKeyEvent)
{
	uint16_t i = 0;
	if(pKeyEvent->ID=='A')
	{
		driver_led_writeStatus(LED_TOGGLE);
		printf("ID = %c , time = 0x%x\r\n",pKeyEvent->ID,pKeyEvent->time);
	}
}

如果是按键‘A’,则小灯电平反转,并输出按键的ID和time信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值