On matrix keyboard, keyboard & display logic of UI interface in a project

stay Last article In, an upscale method is reproduced, using state machine, function pointer, structure, structure pointer, etc. the exception is upscale, but there are some bug s.


First, record the debugging process.

Matrix debugging and other ideas

debugging

First, in the main function of the source code

u8 Key_Value;

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
 while (1)
  {
  
	if(App_LEAVE != currState->loop())  //(check whether you can leave the current status. If not, the current status will be pushed out)
	{                                 //Is to call the loop function of currstate to get the return value
		return 0;
	}
	// Button released
	if(currState == &colScanningPressed)
	{
		printf("KEY:%d \r\n",Key_Value); 
	}
	// Next state (after executing the current state, the state should be transferred to the next state)
	//(it can be seen that currState, as a pointer structure, is the function of a pointer to store the current state for judgment)
	currState = currState == &rowScanning ? &colScanning : currState == &colScanning ? &colScanningPressed : &rowScanning;
	//If you switch to the next state, its enter() function is called
	currState->enter();	
	}
	

At this time, in

uint8_t colScanningLoop()
{
	if(GPIO_PIN_RESET == checkPressedLow(COL0_GPIO_Port,COL0_Pin))
	{
//		Key_Value |= 0;printf("KEY:%d \r\n",Key_Value); 
    return App_LEAVE;
	}
	if(GPIO_PIN_RESET == checkPressedLow(COL1_GPIO_Port,COL1_Pin))
	{
		Key_Value |= 1; //The lower two bits store column values
//		printf("KEY:%d \r\n",Key_Value); 
    return App_LEAVE;
	}
	if(GPIO_PIN_RESET == checkPressedLow(COL2_GPIO_Port,COL2_Pin))
	{
		Key_Value |= 2; //The lower two bits store column values
//		printf("KEY:%d \r\n",Key_Value); 
    return App_LEAVE;
	}
	if(GPIO_PIN_RESET == checkPressedLow(COL3_GPIO_Port,COL3_Pin))
	{
		Key_Value |= 3; //The lower two bits store column values
//		printf("KEY:%d \r\n",Key_Value); 
    return App_LEAVE;
	}
	 return App_STAY;
}

Printf here can be output normally, but there is no printf in main. In other words, it takes many times to trigger once.
So debug a wave, emmm, if (app_leave! = currstate - > loop()) / / (check whether you can leave the current state, and if not, the current state will be pushed) {/ / is to call the loop function of currstate to get the return value return 0;}
The return directly written in the main function is to exit the function directly, so as long as no key is detected here, it will always exit the main function and re-enter, so change it
Change the if to this while loop.

while(App_LEAVE != currState->loop());

However, there is still a problem, that is, pressing once may output several times. There are also keys to eliminate shaking, but it is still unstable.
Then, a solution is in if (currstate = = & colscanningpressed) {printf ("key:% d \ R \ n", key_value);} A while judgment is added after sending. As long as the key value does not change, I will not exit. However, there is a problem. If I press the same key twice, there will be a bug

So, split the sentence currstate = currstate = = & rowscanning& colScanning : currState == &colScanning ? & colScanningPressed : &rowScanning;
As follows:

	currState=&rowScanning;
		currState->enter();	
	while(App_LEAVE != currState->loop());
//	if(0) / / (check whether you can leave the current state. If not, the current state will be pushed out)
//	{/ / is to call the loop function of currstate to get the return value
//		return 0;
//	}
	// The released action is detected
		
	currState= &colScanning;
		currState->enter();	
	currState->loop();
		
	currState= &colScanningPressed;
		currState->enter();	

	while(App_LEAVE != currState->loop());
	printf("KEY:%d \r\n",Key_Value); 

The conditional statement was changed into a three-step implementation, but it still didn't solve the problem.

Finally, I think that the key anti shake may not be too strict, so I have to go back there to have a look.

//Do a good job in key shaking elimination
GPIO_PinState checkPressedLow(GPIO_TypeDef *port, uint16_t pin)
{
		if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(port, pin))
		{
			 HAL_Delay(DEBOUNCE_DELAY);
			if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(port, pin))
			return HAL_GPIO_ReadPin(port, pin);
		}
		if(GPIO_PIN_SET == HAL_GPIO_ReadPin(port, pin))
		{
			 HAL_Delay(DEBOUNCE_DELAY);
			if(GPIO_PIN_SET == HAL_GPIO_ReadPin(port, pin))
			return HAL_GPIO_ReadPin(port, pin);
		}
		 return GPIO_PIN_SET;
}
	

After this improvement, we can finally.

Add a little to improve code simplicity

For this function

uint8_t colScanningPressedLoop()
{
	int col = 3 & Key_Value; //Read column value 
	if(0 == col)
	{
		if(GPIO_PIN_SET == HAL_GPIO_ReadPin(COL0_GPIO_Port, COL0_Pin)) 
		{
			return App_LEAVE;
		}
	}
	else if(1 == col)
	{
		if(GPIO_PIN_SET == HAL_GPIO_ReadPin(COL1_GPIO_Port, COL1_Pin)) 
		{
			return App_LEAVE;
		}
	}
	else if(2 == col)
	{
		if(GPIO_PIN_SET == HAL_GPIO_ReadPin(COL2_GPIO_Port, COL2_Pin)) 
		{
			return App_LEAVE;
		}
	}
	else 
	{
		if(GPIO_PIN_SET == HAL_GPIO_ReadPin(COL3_GPIO_Port, COL3_Pin)) 
		{
			return App_LEAVE;
		}
	}

	return App_STAY;
}
In fact, the writing is complicated. It can be changed to switch case
 It would be more concise

Other matrix keyboard scanning ideas

A classic scanning idea doesn't need to be said. It is to use 8 lines and a byte value to look up the table. This can be written later. The main idea here is to use violence.

Violence scanning

It is to poll the value of the IO port corresponding to each key, such as
while wait for the end before return ing

The last two versions of the code are here

In addition, record the ideas of the UI interface

I've always wanted to figure out how to do the human-computer interface and how to realize the control logic when doing a project. Let's sort out our ideas

main function while scan 1 (multiple interfaces with two keys)

while(1) in the main function does nothing else but key scanning and screen display.
Take a project as an example. First of all, we only have two buttons and there are three modes. Each mode (mode 123) is represented by a variable mode, and each mode has several sub modes (sub mode 123...). Represented by a variable zmode, I will display Mab in the upper right corner of the display screen (where ab is the number of mode and sub mode respectively). For example, M13 is the third sub mode of the first mode.

Well, there are only two buttons. How do you control it,
In the form of pseudo code

mode pattern
zmode Sub mode
while(1){
	if(key1 ==0)
	{
		mode++;
		mode %=3;This control is within the 3 mode
	}
	if(key2 == 0)
	{
		zmode ++;
		zmode %= 3; 
	}
	switch(mode)
	{
		case 1: {
				switch(zmode)
					{
						case 1:Screen display function and other operations
						case 2:
						case 3:
	One thing to note here is that these three case No break,
	Then the effect that can be achieved is,
	Mode 1 can display three, mode 2 only displays two, and mode 3 only displays one thing, which is this effect.
					}
				}
		case 2: { switch(zmode) }
		...
	}
	
}

Is such a nested switch To achieve this, only the key values are scanned in the front,
Let's render later, and then keep cycling

Question 1 - improvement

Now, I am in mode M13, and then change to mode 2. It will automatically be sub mode 3, that is, M23. Because they are two separate variables.

If zmode starts from 1 every time the value of mode is changed, can it be solved,
NO, there is another problem now.
It used to be M13, but now it is adjusted to mode M21. When I return to mode 1, it becomes mode M11 again, which is not humanized. Therefore, we further improve and use two-dimensional array

i,j
modetabel[i][j]={initialize value} Mij

while(1){
	if(key1 == 0)
	{
		i++;
		if(i>maxi)
		i = 0;
		
	}
	if(key2 == 0)
	{
		j++;
		if(j>maxj)
		j = 0;
	}
	switch(mode[])
	{
		case 1: {
				switch(zmode)
					{
						case 1:Screen display function and other operations
						case 2:
						case 3:
	One thing to note here is that these three case No break,
	Then the effect that can be achieved is,
	Mode 1 can display three, mode 2 only displays two, and mode 3 only displays one thing, which is this effect.
					}
				}
		case 2: { switch(zmode) }
		...
	}
	
}

Is such a nested switch To achieve this, only the key values are scanned in the front,
Let's render later, and then keep cycling

Supplement 2

Now, with two keys, you can also add the logic of long press, add the third logic, and add a variable Pmode to display the interface of long press

if(key1 == 0)
	{
	while(key == 0)
		{time++;
		delay
		if delay >= maxtime
		Pmode = 1;
		}
		At this time, if my mode before entering the long press interface is M12
		However, you do not need to adjust the mode at this time. If you click key 1 at this time,
		Maybe when you go back M2 perhaps M3,The main mode changes,
		So it needs to be changed logically at this time
		if(Pmode == 0)
		{
		only Pmode = 0 The operation of adding one will be performed
		i++;
		if(i>maxi)
		i = 0;
		}
	}
Then, you can just switch case Another one outside,
yes Pmode conduct switch case 
At the same time, you can continue to use it at this time key2 ,Continue switch Nesting of 

main function while scanning 2 (multiple interfaces with two keys)

This is, you need at least four buttons,
key1 enters a state and keeps waiting. The card does not go out here. key2 sets it, key3 determines it, and key4 exits.
Example

int main(void)
{
	//u16 kkk=445*5;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //Arrowhead
	delay_init();
	LED_Init();
	Beep_Init();	
	uart_init();
	KEY_Init();
	EXTIX_Init();
	GPIO_Stepper_Init();
	
	TIM2_Int_Init(MOTOR1_FRE,0);
  TIM3_Int_Init(MOTOR1_FRE,0);
  TIM4_Int_Init(MOTOR1_FRE,0);
	TIM5_Int_Init(MOTOR1_FRE,0);
	MOTOR1_PUL_H;
	OLED_GPIO_Init();
  OLED_Init();
	
	Item.i=1;Item.j1=1;Item.j2=1;Item.k=1;Item.k2=3;Item.L=0,Item.D=50;
	
	OLED_CLS();
	OLED_P8x16Str(0,0,"Reseting...");
	delay_ms(200);
	
	
	Stepper_Reset();
	
	
	delay_ms(200);
	OLED_CLS();
	OLED_P8x16Str(0,0,"WELCOME...");
	
	while(1)
	{		
		if(!KEY2){
			show_setting();
			delay_ms(300);
			while(1){
				if(!KEY2){
					Item.i++;
					if(Item.i>2)Item.i=1;
					OLED_CLS();
					show_setting();
					delay_ms(300);
				}
				if(!KEY3){
					if(Item.i==1)Item.j1++;
					if(Item.i==2)Item.j2++;
					if(Item.j1>2)Item.j1=1;
					if(Item.j2>4)Item.j2=1;
					show_setting();
					delay_ms(300);
				}
				if(!KEY4){
					if(Item.i==1){Item.k++;delay_ms(300);}
					if(Item.i==2){Item.k2++;delay_ms(300);}
					if(Item.k>2)Item.k=1;
					if(Item.k2>15)Item.k2=1;
					show_setting();
				}
				if(!KEY5){
					if(Item.i==1){Item.L++;}
					if(Item.j2==3){Item.D+=10;}
					if(Item.L>15)Item.L=0;
					if(Item.D>90)Item.D=0;
					show_setting();
					delay_ms(300);
				}
				if(!KEY1){
					OLED_P8x16Str(0,0,"   WAITING   ");
					break;
				}
			}
		}
		

Broadcast mode

Use the global variable, constantly press the key to scan, and then change the value of the global variable. Perform switchcase in the key action function to execute various functions

In addition, you can also use external interrupts, which do nothing, assign a value to only one variable, and then make continuous judgment in the main function, such as making key1= 1 in the interrupt
In main
if(key1 = 1)
{
key1 = 0
...
}

Keywords: Programming Single-Chip Microcomputer stm32

Added by QuizToon on Sat, 15 Jan 2022 07:29:05 +0200