Application of linked list in embedded (2) - MultiButton based on GitHub open source project

1. Project description

A small and easy-to-use event-driven button driver module, which can expand buttons infinitely. The asynchronous processing method of callback of button events can simplify the program structure, remove redundant button processing hard codes, and make button business logic clearer.
The GitHub source code address is as follows:
Github source code address

2. Code porting

The development board used in this article is Punctual Atomic Explorer F407. First, use STM32CubMx to initialize peripheral information. Requirements: 1. Initialize any one of the key input pins PE2, PE3, and PE4, and 2. Serial port printing function.
If you are unfamiliar, you can refer to the previous blog; the
button
serial port
initialization configuration interface is as follows:
insert image description hereinsert image description here

Transplant the code project and add the MultiButton code to the new project file directory: as shown in the
figure:

insert image description here
Write button callback function:

uint8_t read_button1_GPIO()
{
    
    
	return HAL_GPIO_ReadPin(K1_GPIO_Port,K1_Pin);
}

void btn1_press_down_Handler(void *btn)
{
    
    
	printf("K1 press down...\r\n");
	HAL_GPIO_TogglePin(L1_GPIO_Port,L1_Pin);
}

void btn1_press_up_Handler(void *btn)
{
    
    
	printf("K1 press up...\r\n");
	HAL_GPIO_TogglePin(L0_GPIO_Port,L0_Pin);
}

Add button initialization and start button function before while(1):

	printf("MultiButton Test...\r\n");
	
	button_init(&button1,read_button1_GPIO,0);
	
	button_attach(&button1,PRESS_DOWN,btn1_press_down_Handler);
	
	button_attach(&button1,PRESS_UP,btn1_press_up_Handler);
	
	button_start(&button1);
	
	printf("MultiButton Test...\r\n");

Add the following code to while(1)

while (1)
{
    
    
	button_ticks();
	HAL_Delay(5);
}

Experimental phenomenon:
Press button K1, LED0 flips, lift K1 LED1 flips.
insert image description here

MultiButton source code analysis

First, the time type of the key is defined, including events such as key press, lift, single click, repeated key press, double-click, long-press single trigger, and long-press always trigger.

typedef enum {
    
    
	PRESS_DOWN = 0,//按键按下
	PRESS_UP,//按键抬起
	PRESS_REPEAT,//按下计数
	SINGLE_CLICK,//单次按下
	DOUBLE_CLICK,//双击
	LONG_PRESS_START,//长按
	LONG_PRESS_HOLD,//长按触发
	number_of_event,
	NONE_PRESS
}PressEvent;

Define the key list structure, where the bit field operation is used to solve the storage space of bytes.

typedef struct Button {
    
    
	uint16_t ticks;
	uint8_t  repeat : 4;//重复计数
	uint8_t  event : 4;//按键事件
	uint8_t  state : 3;//状态机状态位
	uint8_t  debounce_cnt : 3;//双击计数
	uint8_t  active_level : 1;//实际电平
	uint8_t  button_level : 1;//按键电平
	uint8_t  (*hal_button_Level)(void);
	BtnCallback  cb[number_of_event];
	struct Button* next;
}Button;

The initialization of the button object structure, the initialization members include the button handle, bind the GPIO level read function, and set the effective trigger level

void button_init(struct Button* handle, uint8_t(*pin_level)(), uint8_t active_level)
{
    
    
	memset(handle, 0, sizeof(struct Button));
	handle->event = (uint8_t)NONE_PRESS;
	handle->hal_button_Level = pin_level;
	handle->button_level = handle->hal_button_Level();
	handle->active_level = active_level;
}

After the initialization button is completed, the button binding operation will be performed, and the button structure members will be bound, the button trigger event, and the button callback function.

void button_attach(struct Button* handle, PressEvent event, BtnCallback cb)
{
    
    
	handle->cb[event] = cb;
}

Button start: that is, adding the button to the linked list and starting the button. The insertion method selected here is the head insertion method, which inserts the key node at the head of the linked list. High efficiency, time complexity is O(1).

int button_start(struct Button* handle)
{
    
    
	struct Button* target = head_handle;
	while(target) {
    
    
		if(target == handle) return -1;	//already exist.
		target = target->next;
	}
	handle->next = head_handle;
	head_handle = handle;
	return 0;
}

Press delete to delete the key from the current linked list. Deletes a key element using a secondary pointer. Same as the previous multi-timer deletion method.

void button_stop(struct Button* handle)
{
    
    
	struct Button** curr;
	for(curr = &head_handle; *curr; ) {
    
    
		struct Button* entry = *curr;
		if (entry == handle) {
    
    
			*curr = entry->next;
//			free(entry);
			return;//glacier add 2021-8-18
		} else
			curr = &entry->next;
	}
}

The button tick function triggers a button event every 5ms to drive the state machine to run.

void button_ticks()
{
    
    
	struct Button* target;
	//按键中断依次循环读取链表中所有的按键元素信息,并且驱动状态机工作
	for(target=head_handle; target; target=target->next) {
    
    
		button_handler(target);
	}
}

Soul of multi-button design:

State machine processing idea: read the state of the current pin and get which state the button currently belongs to.

PressEvent get_button_event(struct Button* handle)
{
    
    
	return (PressEvent)(handle->event);
}

The state machine, the key processing core function, drives the state machine.

/**
  * @brief  Button driver core function, driver state machine.
  * @param  handle: the button handle strcut.
  * @retval None
  */
  
void button_handler(struct Button* handle)
{
    
    
	/*读取当前按键引脚电平*/
	uint8_t read_gpio_level = handle->hal_button_Level();

	//ticks counter working..如果有状态机在运行,那么滴答定时器继续工作
	if((handle->state) > 0) handle->ticks++;

	/*------------button debounce handle---------------*/
	/*读取句柄引脚电平,连续读取3次引脚电平实现按键消抖功能*/
	if(read_gpio_level != handle->button_level) {
    
     //not equal to prev one
		//continue read 3 times same new level change
		if(++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {
    
    
			handle->button_level = read_gpio_level;
			handle->debounce_cnt = 0;
		}
	} else {
    
     //leved not change ,counter reset.
	/*假如引脚状态与先前的不同,那么会改变按键对象的引脚状态*/
		handle->debounce_cnt = 0;
	}

	/*-----------------State machine-------------------*/
	switch (handle->state) {
    
    
	case 0:
		if(handle->button_level == handle->active_level) {
    
    	//start press down
			handle->event = (uint8_t)PRESS_DOWN;
			EVENT_CB(PRESS_DOWN);
			handle->ticks = 0;
			handle->repeat = 1;
			handle->state = 1;
		} else {
    
    
			handle->event = (uint8_t)NONE_PRESS;
		}
		break;

	case 1:
		if(handle->button_level != handle->active_level) {
    
     //released press up
			handle->event = (uint8_t)PRESS_UP;
			EVENT_CB(PRESS_UP);
			handle->ticks = 0;
			handle->state = 2;

		} else if(handle->ticks > LONG_TICKS) {
    
    
			handle->event = (uint8_t)LONG_PRESS_START;
			EVENT_CB(LONG_PRESS_START);
			handle->state = 5;
		}
		break;

	case 2:
		if(handle->button_level == handle->active_level) {
    
     //press down again
			handle->event = (uint8_t)PRESS_DOWN;
			EVENT_CB(PRESS_DOWN);
			handle->repeat++;
			EVENT_CB(PRESS_REPEAT); // repeat hit
			handle->ticks = 0;
			handle->state = 3;
		} else if(handle->ticks > SHORT_TICKS) {
    
     //released timeout
			if(handle->repeat == 1) {
    
    
				handle->event = (uint8_t)SINGLE_CLICK;
				EVENT_CB(SINGLE_CLICK);
			} else if(handle->repeat == 2) {
    
    
				handle->event = (uint8_t)DOUBLE_CLICK;
				EVENT_CB(DOUBLE_CLICK); // repeat hit
			}
			handle->state = 0;
		}
		break;

	case 3:
		if(handle->button_level != handle->active_level) {
    
     //released press up
			handle->event = (uint8_t)PRESS_UP;
			EVENT_CB(PRESS_UP);
			if(handle->ticks < SHORT_TICKS) {
    
    
				handle->ticks = 0;
				handle->state = 2; //repeat press
			} else {
    
    
				handle->state = 0;
			}
		}else if(handle->ticks > SHORT_TICKS){
    
     // long press up
			handle->state = 0;
		}
		break;

	case 5:
		if(handle->button_level == handle->active_level) {
    
    
			//continue hold trigger
			handle->event = (uint8_t)LONG_PRESS_HOLD;
			EVENT_CB(LONG_PRESS_HOLD);

		} else {
    
     //releasd
			handle->event = (uint8_t)PRESS_UP;
			EVENT_CB(PRESS_UP);
			handle->state = 0; //reset
		}
		break;
	}
}

State Machine Flowchart
insert image description here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324148413&siteId=291194637
Recommended