Article count:10804 Read by:13623255

Account Entry

Useful information | About matrix keyboards, using spreadsheets to assist programming

Latest update time:2018-03-07
    Reads:

Recently, key detection was needed in a project. The conventional idea was a matrix keyboard, so I did it this way.


Later, when programming, I found that implementing key detection is not an easy task, especially when multiple keys are pressed at the same time.


The logic is too easy to get messed up in this process, so I used spreadsheet tools (WPS or Excel) to try to save work.


Later I found that the effect is really good, so I share it here.


I used Altium Designer 10 to draw the PCB, but I didn’t have the software installed at home, so I used DesignSparkPCB to redraw the schematic diagram. You can just bear with it.



Our goal is to implement the detection of a 2*3 matrix keyboard. The idea is as follows:
1. Set Row1 low, set other pins to input state with pull-up, and record their state.
2. Set Row2 low, set other pins to input state with pull-up, record the input result and calculate the key state.

Maybe everyone thinks this process is easy. You can tell which key is pressed by looking at which Col is pulled low.


In fact, it is not the case. There may be multiple keys working together to cause a Col to be pulled low.

Why not use Col to scan? ——Because scanning Row only needs 2 scans, scanning Col needs 3 scans.

In order to solve this problem and detect as many different conditions of multiple keys pressed as possible, I used the famous spreadsheet (commonly known as Excel) to implement this detection.

As 6 keys, there are 2^6=64 different states, so we can achieve this work through the automatic filling function of the spreadsheet.


We define column A as 64 different states (0-63)

and then enter "=dec2bin(A2,6)" in cell B2 to convert the data in column A into binary numbers.
Column C has SW1 as the header and cell C2 has "=LEFT(B2,1)", which is the first number from the left end of the binary number.
Column D has SW2 as the header and cell D2 has "=RIGHT(LEFT(B2,2),1)", which is to take 2 numbers from the left end of the binary number and then take the one from the right end of these two numbers, which is the second number from the left.

Similarly:
Column E has SW3 as the header and cell E2 has "=RIGHT(LEFT(B2,3),1)"
Column F has SW4 as the header and cell F2 has "=RIGHT(LEFT(B2,4),1)"
Column G has SW5 as the header and cell G2 has "=RIGHT(LEFT(B2,5),1)"
Column H header SW6, cell H2 "=RIGHT(B2,1)"

After completing the above steps, we leave a column (column I) blank, and then calculate the logical relationship generated after the key is pressed, which is also the most troublesome problem.

In order to save column width (the screen size is limited and more content can be displayed), we enter R2 (Row_2) in cell J1, C1 (Col_1) in cell K1, L1=C2, M1=C3
Why is there no R1? ——Here we calculate the input status of other signal lines when R1 is set to low, and R1 must be 0.


The next step is how to judge the signal.


Since the pull-up is a weak pull-up, when the key is pressed, if the corresponding row is low, the column will be pulled low.


Here we agree that if SW1=1, it means SW1 is pressed, and the rest of the buttons are similar.

When R1=0, to make R2 equal to 0, SW1 and SW4 need to be pressed at the same time, or SW2 and SW5 need to be pressed at the same time, or SW3 and SW6 need to be pressed at the same time.


Therefore, the logic of J2 cell (Row2 column) is "=IF(C2*F2+D2*G2+E2*H2,0,1)" (don't forget the combinational logic).


Next, calculate the Col1 column, and the result of the Row2 column just calculated can be used.
We know that if Row1 is low and SW1 is pressed, then Col is low. If Row2 is low and SW4 is low, then Col is also low.


So cell K2: “=IF(C2+F2*(1-J2),0,1)”

L2: “=IF(D2+G2*(1-J2),0,1)”
M2: “=IF(E2+H2*(1-J2),0,1)”

Keep one column blank (column N). With previous experience, it is easy to fill in the following cells:
O2: “=IF(C2*F2+D2*G2+E2*H2,0,1)”
P2: “=IF(F2+C2*(1-O2),0,1)”
Q2: “=IF(G2+D2*(1-O2),0,1)”
R2: “=IF(H2+E2*(1-O2),0,1)”

Then select all the data in this row and fill it down to 65 rows (the header occupies one row and there are 64 rows of data).
The effect is shown in the figure below:



Someone may ask, isn't it just a logical calculation? Why make it so complicated? There are formulas and fills.


I have been talking for so long, but I haven't mentioned the electrical connection.
I originally used STM32 to implement this project, but this is an MSP430 board, so I will transplant it and use the most common MSP430G2553.
R1: P1.4
R2: P1.3
C1: P1.2
C2: P1.1
C3: P1.0


The initialization code will not be written, and two macros are defined:

  1. #define R1OUT() do{\

  2. P1DIR &= ~BIT3;\

  3. P1OUT |= BIT3;\

  4. P1REN |= BIT3;\

  5. P1REN &= ~BIT4;\

  6. P1OUT &= ~BIT4;\

  7. P1DIR |= BIT4;\

  8. }while(0)


  9. #define R2OUT() do{\

  10. P1DIR &= ~BIT4;\

  11. P1OUT |= BIT4;\

  12. P1REN |= BIT4;\

  13. P1REN &= ~BIT3;\

  14. P1OUT &= ~BIT3;\

  15. P1DIR |= BIT3;\

  16. }while(0)


Then we can divide the scan into two rounds and execute them regularly.
In the first round, scan P1IN when R2OUT() is running, and execute R1OUT()
after reading. In the second round, scan P1IN when R1OUT() is running, and execute R2OUT() after reading.

Then we can know that in the first round of scanning, R2 is output, and we need to read the key values ​​of P1.4 P1.2 P1.1 P1.0.


In the second round of scanning, R1 outputs, and we need to read the key values ​​of P1.3 P1.2 P1.1 P1.0.

Since continuous registers are used, we can easily use a register Key_Buffer to store the key code.


Therefore, we only need to clear the register in the first round of scanning, and then move the key value to the upper 4 bits of the register.
In the second round of scanning, we can directly add the lower 4 bits of P1IN to the register.

  1. void Key_Scan()

  2. {

  3. static unsigned char count = 1;

  4. static unsigned char Key_Buffer=0;

  5. static unsigned char Key_States=Key_Old_States=Key_Press=Key_Bottom_up=0;


  6. if(count&0x01)//odd round

  7. {

  8. unsigned char temp=0;

  9. temp=P1IN;

  10. Key_Buffer=((temp&0x07)<<4)+((temp&0x10)<<3);

  11. R1OUT();

  12. }

  13. else //even round

  14. {

  15. Key_Buffer+=(P1IN&0x0f);

  16. R1OUT();


  17. }

  18. count++;

  19. }


Those of you with sharp eyes may have noticed that I also defined four variables: Key_States, Key_Old_States, Key_Press and Key_Bottom_up. What are they used for?


Let's look at this.
The scan result corresponds to the values ​​of the JM and OR columns in the table, but what we need is the status of each key in columns C to H. It would be great if there was a way to convert them.


All right, let's go back to our spreadsheet and do some calculations.


As usual, leave a column (column S) blank as a separator. After two rounds of odd and even operations, our Key_Buffer is the value formed by concatenating the data in the JM and OR columns.


So we imitate this operation in cell T2 and fill in "=J2&K2&L2&M2&O2&P2&Q2&R2". In the spreadsheet, & is neither an AND operation nor a bitwise AND operation, but a string concatenation operation.


Continuing to look further, cell U2 “=bin2dec(T2)” fills this result down to get the final version of Sheet1.


(If the subsequent operations continue on the basis of this worksheet, the integrity of this table will be destroyed, so copy all the data in Sheet to Sheet2 to continue.)
Now the table looks like this:



As I just said, copy Sheet1 to Sheet2, and we will continue the calculation.


We noticed that there are a lot of identical data in the U2 column, which means the scan results are the same, but the key codes are different. So we need to find out the key values ​​that cannot be determined by the key codes.


Enter "=COUNTIF(U:U,U2)" in cell V2 and fill down.


So I saw many times more than once. Select the AV column, customize the sorting, select the row with the title, and sort in the order of V column descending, U column ascending, and A column ascending.

We intercepted a section of the scan results with the same key value:



It can be seen from this table that when SW1, SW2, and SW4 are pressed at the same time, it is impossible to determine whether SW5 is pressed.


Combined with the schematic diagram, we found that when SW1, SW2, and SW4 are pressed at the same time,
Row1, Row2, Col1, and Col2 are all short-circuited. Pressing or not pressing SW5 cannot change this result.


In other words, in this case, SW5 cannot be detected from the circuit.

For such a detection result, the best way to deal with it is to assume that it is the last key code.
Therefore, we first delete the rows with repeated scan results in the table (a total of 34 rows, more than half).

Back to the IDE, the Key_Buffer variable gets the result after the even-numbered round of scanning is completed, which is our scan key value.
Then the simplest way to deal with it is:

  1. Key_Old_States=Key_States;

  2. switch(Key_Buffer)

  3. {

  4. case 51:Key_States=36;break;

  5. case ……

  6. }


Although more than 30 lines have been deleted, there are still 30 lines left. It is painful to type them one by one. So let's continue:
We found that the key lies in "case 51:Key_States=36;break;". If such a statement can be automatically generated, it will be much simpler.


So type in cell W2:

  1. ="case "&U2&":Key_States="&A2&";break;"

Padding Down:



Copy it into your IDE happily, and the code is complete.
Don't forget to add "default:break;" at the end .
After all, we have deleted so many duplicate key values, so just keep the result of Key_State unchanged.

Then, what about Key_Press and Key_Bottom_up?
It's very simple. If Key_States==Key_Old_States is checked, it can be assumed that no key action has occurred.

  1. Key_Press=((Key_States^Key_Old_States)&Key_States);

  2. Key_Bottom_up=((Key_States^Key_Old_States)&Key_Old_States);


I won't mention how to implement specific functions according to the keys, everyone knows it. Click to read the original text to view the complete code.


Latest articles about

 
EEWorld WeChat Subscription

 
EEWorld WeChat Service Number

 
AutoDevelopers

About Us Customer Service Contact Information Datasheet Sitemap LatestNews

Room 1530, Zhongguancun MOOC Times Building,Block B, 18 Zhongguancun Street, Haidian District,Beijing, China Tel:(010)82350740 Postcode:100190

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号