The key is one of the most commonly used input devices in the microcontroller system; almost as long as interactive input is required, a keyboard is necessary. This blog implements a general keyboard program. As long as a function for reading the key value (the underlying key value) is provided, the program will complete some column processing such as debouncing and storing in a queue. At the same time, this program provides the most commonly used 4*4 matrix keyboard program and 4 key program.
Hardware introduction:
This article mainly implements a general framework of a keyboard, which can be easily changed to different keyboard functions. Here are two types of keys, 4 separate keys and 4*4 row and column scanning keyboards. The
4-key one is like this: one end of the four keys is grounded, and the other end is connected to the pull-up resistor and then input to the P1.0-P1.3 port of the microcontroller; in this way, when the key is pressed, the microcontroller receives a low level, and when it is released, the microcontroller input signal is fixed to a high level by the pull-up resistor.
4*4 keys: The row input signal is equipped with a Sanla resistor, and the default level is high when no key is pressed; the column scan signal line is directly connected to the key column line; when reading a key, the column scan signal is given a low-level signal by the microcontroller (scanning column by column), and the row signal is read to determine which specific key it is; the circuit diagram is roughly as follows:
In the figure, IN is the column scan line of the keyboard, and OUT is the row signal line of the keyboard output. Scanning can also be scanned by row, in which case IN is the row scan line, and OUT is the column signal line of the key output. My program scans by column (the row and column scanning principles are the same, but the rows and columns are swapped).
Here, the transplantation of the scanf function of the 4*4 key is realized at the same time, and the transplantation of the printf function of the LCD previously realized is added, and a complete system that can interactively input and output is built; the printf of the LCD has added a function to realize backspace; you can backspace and re-enter when entering an incorrect number.
Program implementation:
Let's talk about the structure of the program first. The program implements a circular queue to store the key values that have been pressed. It can save the latest four keys and prevent the loss of keys. The program uses interrupts to press keys. It reads the keys every 16ms (using the interval interrupt of the watchdog) to determine whether the key value is valid. If it is valid, it is put into the queue and waits for reading.
Implementation of the circular queue: It is implemented with an array. In order to judge whether the queue is full, the last element of the array is not used to store the key code value:
/**********************Macro definition***************************/
#define KeySize 4 //Key code value queue
#define Length KeySize+1 //Number of queue array elements
/***************************************************/
/**********************Key value queue************************/
//KeySize (Length-1) key code circular queue occupies one element space
char Key[Length];
Enqueue function: When entering the queue, one is dequeued when the queue is full to save the latest four keys.
void AddKeyCode(char keyCode)
{
if((rear+1)%Length==front) //The queue is full
{
front=(front+1)%Length; //Dequeue one
}
Key[rear] = keyCode;
rear=(rear+1)%Length;
}
Dequeue function: The dequeue function is the function that reads the key, so that it can be called by other places where it is needed.
char ReadKey()
{
char temp;
//if(rear==front) return '\0'; //No keywhile(rear==
front);
temp = Key[front];
front=(front+1)%Length;
return temp;
}
KeyProcess: This function is the keyboard processing function, which needs to be called every 10ms-20ms. Here it is put into the 16ms interrupt of the watchdog timer; the function flow chart and function content are as follows:
void KeyProcess()
{
static char keyValue = 0xff; //Key identifier, key value
static char addedFlag = 0; //Add queue flag
char keyVal = GetKey();
if(keyVal==0xff) //No key
{
keyValue = 0xff;
addedFlag = 0;
return;
}
if(keyValue==0xff) //No key in the previous state
{
keyValue = keyVal;
return;
}
if(keyValue!=keyVal) //Different from the previous key
{
keyValue = keyVal; //Save the new key value
return;
}
if(addedFlag==1) //Added to the queue
{
return;
}
addedFlag = 1;
AddKeyCode(KeyCode[keyVal]);
}
This function completes the key judgment and compares it with the last time to determine whether it is a valid key, and then judges whether to save it according to whether it has been queued and saved.
This function needs to be interrupted every 10ms-20ms:
#pragma vector=WDT_VECTOR
__interrupt void WDT_ISR()
{
KeyProcess();
}
This is the interval timing interrupt of the 430 watchdog, which is set to interrupt every 16ms:
WDTCTL=WDT_ADLY_16; //Watchdog internal timer mode 16ms
IE1 |= WDTIE; //Allow watchdog interrupt
The GetKey function is called in KeyProcess. This function needs to be provided by the user to meet special key requirements. Here are two examples: 4 keys and 4*4 matrix keyboard.
The getkey function for 4 keys:
char GetKey()
{
if((P1IN&0X0F)==0x0E)
{
return 0;
}
if((P1IN&0X0F)==0x0D)
{
return 1;
}
if((P1IN&0X0F)==0x0B)
{
return 2;
}
if((P1IN&0X0F)==0x07)
{
return 3;
}
return 0xff;
}
Here, the original key value of each key is output, and 0xff is output if no key is pressed. When you provide the getkey function yourself, you also need to do this. When no key is pressed, 0xff is returned
to translate the corresponding original key value into the required key code, using the KeyCode array:
char KeyCode[] = "0123"; /*When there are 4 keys*/
Here it is converted into ASCII code for output, and you can change it if necessary.
4*4 matrix keyboard: getkey:
char GetKey()
{
P1DIR |= 0XF0; //High four-bit output
for(int i=0;i<4;i++)
{
P1OUT = 0XEF << i;
for(int j=0;j<4;j++)
{
if((P1IN&(0x01<<j))==0)
{
return (i+4*j);
}
}
}
return 0xff;
}
Here is column scanning, you can change to other scanning methods at will, just get the original key value, no key must return 0xff.
KeyCode, translated into ASCII code:
char KeyCode[] = "0123456789ABCDEF"
Here, the normal keyboard program ends, just add Key.c when calling, include Key.h and you can use it. After calling KeyInit first, you can read the keys normally. I won't go into details here.
Scanf transplantation: When transplanting scanf, an ASCII character device is required. To input data using ASCII code, an Enter key is also required. Only in this way can scanf be used to input data. In order to backspace and modify the input data when it is wrong, there is also a Backspace key.
Keyboard structure:
1
|
2
|
3
|
Backspace
|
4
|
5
|
6
|
reserve
|
7
|
8
|
9
|
reserve
|
reserve
|
0
|
reserve
|
Enter
|
The reserved keys use the character '\0', enter '\n' and backspace '\b'
, so: KeyCode:
char KeyCode[] = "123\b456\000789\0\0000\0\r"; /* 4*4, scanf transplantation*/
In the string, when \0 is followed by a number, '\000' must be used. Otherwise, the C language compiler thinks that \0 and the following number are combined into one character.
The porting of scanf requires the implementation of the getchar function, which is similar to the previous getchar function. It is placed in the Getchar.c file with the following content:
#include <stdio.h>
#include "Key.h"
#define LINE_LENGTH 20 //Line buffer size, determines the maximum number of characters input per line
/*Special ASCII code definition in standard terminal devices, do not modify*/
#define InBACKSP 0x08 //ASCII <-- (backspace key)
#define InDELETE 0x7F //ASCII <DEL> (DEL key)
#define InEOL '\r' //ASCII <CR> (Enter key)
#define InLF '\n' //ASCII <LF> (Enter)
#define InSKIP '\3' //ASCII control-C
#define InEOF '\x1A' //ASCII control-Z
#define OutDELETE "\x8 \x8" //VT100 backspace and clear
#define OutSKIP "^C\n" //^C and new line
#define OutEOF "^Z" //^Z and return EOF
int getchar()
{
static char inBuffer[LINE_LENGTH + 2]; //Where to put chars
static char ptr; //Pointer in buffer
char c;
while(1)
{
if(inBuffer[ptr]) //If there are characters in the buffer
return (inBuffer[ptr++]); //Then return characters one by one
ptr = 0; //Until the sending is completed, the buffer pointer returns to zero
while(1) //If there are no characters in the buffer, wait for character input
{
c = ReadKey(); //Wait to receive a character == key
if(c == InEOF && !ptr) //==EOF== Ctrl+Z
{ //Only valid when no other characters are entered
printf(OutEOF); //Terminal displays EOF character
return EOF; //Return EOF (-1)
}
if(c==InDELETE || c==InBACKSP) //==Backspace or delete key==
{
if(ptr) //Buffer has value
{
ptr--; //Remove a character from the buffer
printf(OutDELETE); //Display and delete a character at the same time
}
}
else if(c == InSKIP) //==Cancel key Ctrl+C ==
{
printf(OutSKIP); //Terminal display jumps to the next line
ptr = LINE_LENGTH + 1; //==0 end character==
break;
}
else if(c == InEOL||c == InLF) //== '\r' carriage return=='\n' carriage return
{
putchar(inBuffer[ptr++] = '\n');//Terminal line break
inBuffer[ptr] = 0; //Add terminator (NULL) at the end
ptr = 0; //Clear pointer
break;
}
else if(ptr < LINE_LENGTH) //== normal character==
{
if(c >= ' ') //Delete characters below 0x20
{
//Store in buffer
putchar(inBuffer[ptr++] = c);
}
}
else //Buffer is full
{
putchar('\7'); //== 0x07 buzzer, PC echoes once
}
}
}
}
Here is the detailed function to support backspace and other keys.
If you do not need to support backspace, you can simplify it to:
int getchar()
{
return ReadKey();
}
To implement scanf call, you also need to set it up. For detailed settings, refer to: MSP430 Program Library <IV>printf and scanf function transplantation ; you need to set the library to CLIB; in Option-general option-library configuration.
In this way, the scanf porting of the keyboard is completed. When you need to use it, just add the inclusion of the stdio.h file and then complete the initialization of the keyboard.
Usage example:
Here, the example implements a simple interaction between the keyboard and the LCD; the keyboard inputs data and the LCD displays normally; it is just like the keyboard and screen when debugging in C language; of course, it is not that rich. For
the LCD part, the original program is used. Here, in order to support backspace when input errors occur, backspace support is added to the original printf function. Specific reference: MSP430 program library <four> printf and scanf function porting (already updated).
In the project, the c program file of the LCD and the program file of printf (Lcd12864.c, Printf.c) are connected, and the file inclusion of Lcd12864.h is added; after initializing the LCD, printf can be used to output the content to be displayed to the LCD.
Keyboard: Add Key.c, include Key.h, add Getchar.c, initialize the keyboard in the program; then set the lib used to CLIB, for specific settings, see: MSP430 program library <four> printf and scanf function porting. After that, you can use the keyboard and LCD to complete simple interaction with the 430 microcontroller.
For details, refer to the sample project and main.c.
#include <msp430x16x.h>
#include <stdio.h>
#include "Lcd12864.h"
#include "Key.h"
long a;
void main( void )
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
ClkInit(
); LcdInit();
KeyInit();
_EINT();
while(1)
{
printf("Please enter a number: ");
scanf("%ld",&a);
printf("The number entered is: %ld",a);
_NOP();
}
}
In this way, you can use the keyboard to input data to the microcontroller, and use the LCD to easily know whether there is a problem with the data input.
This is the end of the keyboard library. If there are any deficiencies, please feel free to discuss.
|