Based on s3c2440 and linux, a 3*4 matrix keyboard driver was implemented.
Function: Delay debounce, repeat key press, multi-key press (??)
More detailed description document: "Matrix keyboard design based on S3C24440 and embedded Linux", Electronic Technology, 2008, 45(5): 21-23
/****************************************************** *********
* s3c2440-keyboard.c
*
* keyboard driver for S3C2440 based PDA
*
*
* History:2007/04/30
*
*
*************** ************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "s3c2440-kb" //Keyboard device name
static int kbMajor = 0; //Default major device number
#define MAX_KB_BUF 10 //Circular queue size
typedef struct {
unsigned int keyStatus;
int irq;
// int timeCount;
u_short buf[MAX_KB_BUF]; /* Circular queue*/
unsigned int head, tail; /* Read and write pointers of circular queue*/
spinlock_t lock; /* Lock*/
} KB_DEV ;
static KB_DEV kbdev;
#define KEY_UP 0 //Key up
#define KEY_DOWN 1 //Key pressed
#define NO_KEY_DOWN 2 //No key pressed
#define EINT1_DOWN 0
#define EINT3_DOWN 1
#define EINT8_DOWN 2
#define NO_EINT_DOWN 3
/*Circular queue operation*/
#define BUF_HEAD (kbdev.buf[kbdev.head])
#define BUF_TAIL (kbdev.buf[kbdev.tail])
#define INCBUF(x) if((++ x)==MAX_KB_BUF) x=0
/*Timer settings*/
#define KB_TIMER_DELAY (HZ/50) /*HZ represents the number of clock ticks generated per second, and the timer timeout is 20ms*/
#define REPEAT_START_DELAY (HZ) / * Automatic repeat start delay: 1 second*/
#define REPEAT_DELAY (HZ/2) /*Automatic repeat delay: 0.5 seconds*/
static struct timer_list kb_timer;
static struct timer_list repeat_timer;
spinlock_t repeat_lock;
static int timeCount =1;
static int TIME_OUT =5;
/* keyboard matrix*/
static u_short keyboard_code_map[4][3]={
{1 , 2 , 3 },
{4 , 5 , 6 },
{7 , 8 , 9 } ,
{10, 11, 12}
}; //Keyboard code corresponding to each key
static u_short pre_keyboard_code = 0; //Key value obtained from the last scan
static u_short curr_keyboard_code = 0; //Key value obtained from the current scan
static u_short snap_keyboard_code[4][3]={
{0 , 0 , 0 },
{0 , 0 , 0 },
{0 , 0 , 0 },
{0, 0,
0}
GPJCON |= 0xFC03FFFF; GPJCON |= 0x01540000 ; }; //temporary key value
#define DETECTION_THROLD 3
static int requestIrq();
static int s3c2440_kb_release(struct inode *inode, struct file *filp);
/*----------------------------------------------------
* func: Initialize GPJCON register and configure GPJ9,GPJ10,GPJ11
,GPJ12
* as output legs
*
------------------------------------------------------*/
static void init_gpjcon()
{
//GPJ9,GPJ10,GPJ11,GPJ12------>output
GPJCON &= 0xFC03FFFF;
GPJCON |= 0x01540000;
}
/*----------------------------------------------------
* func: Output value to GPJ9,GPJ10,GPJ11,GPJ12
* param:
* value: output value
*
------------------------------------------------------*/
//
static inline void output_giop(int value ) //Output to all lines
{
value &= 0x0000000F;
value <<= 9;
GPJDAT &= 0xE1FF;
GPJDAT |= value;
udelay(2);
}
/*----------------------------------------------------
* func: Determine whether eint is currently at a low level
* param:
* irq: the irq number of the eint that currently triggers the interrupt
* return:
* EINT1_DOWN: eint1 is at a low level
* EINT3_DOWN: eint3 is at a low level
* EINT8_DOWN: eint8 is at a low level
* NO_EINT_DOWN: eint is not at a low level
------------------------------------------------------*/
int get_eint_value(int irq)
{
u_int IOValue;
IOValue = GPFDAT ;
if( (irq == 1) && (( IOValue & 0x00000002)==0) )
{
return 0x00000008
)==0) )
{
return EINT3_DOWN;
}
IOValue = GPGDAT ;
if((irq ==36) && (( IOValue & 0x0000001)==0) )
{
return EINT8_DOWN;
}
return NO_EINT_DOWN;
}
/*----------------------------------------------------
* func: Scan the keyboard
to
determine which key is pressed
* param:
* x: get the row number of the key
* y: get the column number of the key
* return:
* KEY_DOWN: A key is pressed on the keyboard
* NO_KEY_DOWN: No key is pressed on the keyboard
------------------------------------------------------*/
static inline int scan_keyboard(int* x,int* y)
{
int matrix_row,matrix_col,matrix_col_status;
output_giop(0xF); //Output 1 to all rows
//Determine which row the key is in
matrix_row=matrix_col=-1;
output_giop(0xE);//output 1 on line 1 and 0 on the rest of the lines
matrix_col_status = get_eint_value(kbdev.irq);
if(matrix_col_status != NO_EINT_DOWN)
{
matrix_row = 0;
matrix_col = matrix_col_status;
goto scanend;
}
output_giop(0xD);//output 1 on line 2 and 0 on the rest of the lines
matrix_col_status = get_eint_value(kbdev.irq);
if(matrix_col_status != NO_EINT_DOWN)
{
matrix_row=1;
matrix_col = matrix_col_status;
goto scanend;
}
output_giop(0xB);//output 1 on line 3 and 0 on the rest of the lines
matrix_col_status = get_eint_value(kbdev.irq);
if(matrix_col_status != NO_EINT_DOWN)
{
matrix_row=2;
matrix_col = matrix_col_status;
goto scanend;
}
output_giop(0x7);//Output 1 on line 4, and output 0 on the remaining lines
matrix_col_status =get_eint_value(kbdev.irq);
if(matrix_col_status != NO_EINT_DOWN)
{
matrix_row=3;
matrix_col = matrix_col_status;
goto scanend;
}
scanend:
output_giop(0);
if(matrix_row >=0 )
{
snap_keyboard_code[matrix_row][matrix_col_status]= snap_keyboard_code[matrix_row][matrix_col_status] + 1;
if(snap_keyboard_code[matrix_row][matrix_col]>=DETECTION_THROLD)
{
*x=matrix_row;
*y=matrix_col;
curr_keyboard_code = keyboard_code_map[matrix_row][matrix_col];
return KEY_DOWN;
}
}
return NO_KEY_DOWN;
}
/*----------------------------------------------------
* func: Determine whether the current key is the same as the previous key
* param:
*
* return:
* 0: Same
* 1: Different
------------------------------------------------------*/
static inline int key_changed()
{
return (pre_keyboard_code == curr_keyboard_code)? 0 : 1;
}
/*----------------------------------------------------
* func: Save the keyboard code corresponding to the key into the circular queue
* param:
* keyValue: The corresponding keyboard code of the key
* return:
*
------------------------------------------------------*/
static inline void save_key_to_queue(u_short keyValue)
{
if (kbdev.keyStatus == KEY_DOWN)
{
BUF_HEAD = keyValue;
INCBUF(kbdev.head);
//wake_up_interruptible(&(kbdev.wq));
}
}
/*----------------------------------------------------
* func: Repeat key timer handler. If a key is pressed all the time,
the keyboard code of the key will be stored in the circular queue at a fixed time.
* param:
* data: no parameters
* return:
*
------------------------------------------------------*/
static inline void repeat_timer_handler(unsigned long data)
{
spin_lock_irq(&(repeat_lock));
if(kbdev.keyStatus ==KEY_DOWN)
{
repeat_timer.expires = jiffies + REPEAT_DELAY;//Set automatic repeat delay
add_timer(&repeat_timer);//Add timer to queue
if(pre_keyboard_code != 0)
{
//printk("repeat save keyvaluen %d",pre_keyboard_code);
save_key_to_queue(pre_keyboard_code);//Store the key value in the circular queue
}
}
else//If the key pops up
{
//del_timer(&repeat_timer);
// printk("del repeat timern");
}
spin_unlock_irq(&(repeat_lock));
}
/*----------------------------------------------------
* func: Enable interrupt
* param:
* return:
*
------------------------------------------------------*/
//Enable interrupt
static inline void enableIrq()
{
//Clear the corresponding bits of eint1 eint2 eint8 in the SRCPND register
SRCPND = 0x0000002A;
//Enable interrupt
enable_irq(IRQ_EINT1);
enable_irq(IRQ_EINT3);
enable_irq(IRQ_EINT8);
}
/*----------------------------------------------------
* func: Keyboard timing scanning program, if a stable key code is obtained, the key code will be stored
in the circular queue; if not, the scan will continue after a delay of 20ms
* param:
* data: no parameters
* return:
*
------------------------------------------------------*/
static inline void kb_timer_handler(unsigned long data)
{
int x,y;
spin_lock_irq(&(kbdev.lock));
x = y = 0;
if(scan_keyboard(&x,&y) == KEY_DOWN)
{
// printk("snap_keyboard_code=%d, %d, %d, %dn", snap_keyboard_code[0][1],snap_keyboard_code[1][1],snap_keyboard_code[2][1],snap_keyboard_code[3][1]);
kbdev.keyStatus =KEY_DOWN;
if(key_changed())
{
pre_keyboard_code = curr_keyboard_code;
save_key_to_queue(pre_keyboard_code);
//printk("KEY_DOWN:%d x=%d y =%dn",timeCount,x,y);
//Set the automatic repeat start delay timer
/*repeat_timer.expires = jiffies + REPEAT_START_DELAY;
add_timer(&repeat_timer);*/
}
timeCount=1;
memset(snap_keyboard_code,0,12*sizeof(u_short));
//curr_keyboard_code =0;
kb_timer.expires = jiffies + KB_TIMER_DELAY;
add_timer(&kb_timer);
}
else
{
//printk("snap_keyboard_code=%d, %d, %d, %dn", snap_keyboard_code[3][0],snap_keyboard_code[3][1],snap_keyboard_code[3][2],snap_keyboard_code[3][3]);
kb_timer.expires = jiffies + KB_TIMER_DELAY;
add_timer(&kb_timer);
//printk("timeCount:%dn",timeCount);
if (timeCount==TIME_OUT) //No stable key value is obtained after scanning 5 times
{
//Reset counter
timeCount=1;
kbdev.keyStatus =KEY_UP;
//Enable interrupt
enableIrq();
//Turn off timer
del_timer(&kb_timer);
del_timer(&repeat_timer);
//printk("enable irq nnn");
curr_keyboard_code = 0;
pre_keyboard_code= 0 ;
memset(snap_keyboard_code,0,12*sizeof(u_short));
}
else
timeCount++;
}
spin_unlock_irq(&(kbdev.lock));
}
/*----------------------------------------------------
* func: Read the key code of the key from the circular queue
* param:
* return: the key code of the key
*
------------------------------------------------------*/
static inline int kbRead()
{
u_short keyvalue;
spin_lock_irq(&(kbdev.lock));
keyvalue = BUF_TAIL;
INCBUF(kbdev.tail );
spin_unlock_irq(&(tsdev.lock));
return keyvalue;
}
/*----------------------------------------------------
* func: The function corresponding to file reading, if there is a key code in the circular queue,
the key code will be copied to the buffer in the user space.
* param:
*
* return:
* Returns the number of bytes of the key code read from the circular queue
*
*
------------------------------------------------------*/
static ssize_t
S3C2440_kb_read(struct file *filp, char *buffer, size_t count, loff_t * ppos)
{
u_short keyvalue;
if(kbdev.head == kbdev.tail)
{
return 0;
}
else
{
keyvalue = kbRead();
count = sizeof(keyvalue);
/*Copy data to user space*/
copy_to_user(buffer,&(keyvalue),count);
return count;
}
}
/*----------------------------------------------------
* func: The open function corresponding to opening a file, initializes global variables and timers
and requests
interrupts param:
*
*
* return:
*
------------------------------------------------------*/
static int S3C2440_kb_open(struct inode *inode, struct file *filp)
{
kbdev.keyStatus = KEY_UP;
kbdev.head=kbdev.tail = 0;
kbdev.lock = SPIN_LOCK_UNLOCKED;
repeat_lock = SPIN_LOCK_UNLOCKED;
output_giop(0);
//Initialize timer
init_timer(&kb_timer);
kb_timer.function = kb_timer_handler;
//Initialize repeat key timer
init_timer(&repeat_timer);
repeat_timer.function = repeat_timer_handler;
/*if(requestIrq() != 0)
return -1;*/
enableIrq();
MOD_INC_USE_COUNT;
return 0;
}
static struct file_operations kb_fops = {
owner: THIS_MODULE,
open: S3C2440_kb_open,
read: S3C2440_kb_read,
release: s3c2440_kb_release,
};
/*----------------------------------------------------
* func: interrupt handler, turn off interrupt and turn on keyboard scan timer
* param:
*
*
* return:
*
------------------------------------------------------*/
static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
spin_lock_irq(&kbdev.lock);
//Disable all interrupts
disable_irq(IRQ_EINT1);
disable_irq(IRQ_EINT3);
disable_irq(IRQ_EINT8);
kbdev.irq = irq;
//printk("irq=%dn",kbdev.irq);
//Start timer
kb_timer.expires = jiffies + KB_TIMER_DELAY;
add_timer(&kb_timer);
repeat_timer.expires = jiffies + REPEAT_START_DELAY;
add_timer(&repeat_timer);
spin_unlock_irq(&kbdev.lock);
}
/*----------------------------------------------------
* func: Initialize eint interrupt related registers and install interrupt handler
* param:
*
*
* return:
*
------------------------------------------------------*/
static int requestIrq()
{
int ret;
/* Enable interrupt */
//=====================================================
// irq: Linux interrupt number, different from hardware interrupt number
// handle: interrupt handler
// flag: SA_INTERRUPT indicates that this is a fast interrupt handler
// dev_id: interrupt signal line used for sharing, usually set to NULL
//====================================================
ret = set_external_irq(IRQ_EINT1,EXT_FALLING_EDGE,GPIO_PULLUP_DIS);
if(ret)
goto eint_failed;
ret = request_irq(IRQ_EINT1, keyboard_interrupt, SA_INTERRUPT,
DEVICE_NAME, NULL);
if(ret)
goto eint1_failed;
ret = set_external_irq(IRQ_EINT8,EXT_FALLING_EDGE,GPIO_PULLUP_DIS); //EXT_LOWLEVEL
if(ret)
goto eint_failed;
ret = request_irq(IRQ _EINT8, keyboard_interrupt , SA_INTERRUPT,
DEVICE_NAME, NULL);
if(ret) goto
eint8_failed
;
ret = set_external_irq(IRQ_EINT3,EXT_FALLING_EDGE,GPIO_PULLUP_DIS);
if(ret
)
goto eint_failed;
ret = request_irq(IRQ_EINT3, keyboard_interrupt, SA_INTERRUPT,
DEVICE_NAME, NULL );
if(ret)
goto eint3_failed;
return 0;
eint3_failed:
free_irq(IRQ_EINT3, keyboard_interrupt);
eint8_failed:
free_irq(IRQ_EINT8, keyboard_interrupt);
eint1_failed:
free_irq(IRQ_EINT1, keyboard_interrupt);
eint_failed:
printk(DEVICE_NAME ": IRQ Requeset Error n");
return ret;
}
static int s3c2440_kb_release(struct inode *inode, struct file *filp)
{
/*Unregister device*/
// unregister_chrdev(kbMajor, DEVICE_NAME);
/*Release interrupt*/
/*
free_irq(IRQ_EINT1,NULL);
free_irq (IRQ_EINT8,NULL);
free_irq(IRQ_EINT3,NULL);
MOD_DEC_USE_COUNT;
*/
return 0;
}
/*---------------------------------------- ------------
* func: Initialize keyboard driver, register character device
* param:
*
*
* return:
>=0: Initialize keyboard driver successfully
<0: Failed
*
------- -----------------------------------------------*/
static int __init s3c2440_kb_init(void)
{
int ret;
/*Initialize the configuration of the tube leg*/
init_gpjcon();
output_giop(0);
/*Register the device*/
ret = register_chrdev(99, DEVICE_NAME, &kb_fops);
if(ret < 0) {
printk(DEVICE_NAME " can't get major numbern");
return ret;
}
kbMajor = ret;
printk("%s: major number=99n",DEVICE_NAME);
requestIrq();
// Temporarily disable all interrupts and wait until open Then turn on
disable_irq(IRQ_EINT1);
disable_irq(IRQ_EINT3);
disable_irq(IRQ_EINT8);
return 0;
}
/*---------------------------------- ------------------
* func: Unregister character device, release interrupt
* param:
*
*
* return:
*
------------------------------------------------------*/
static void __exit s3c2440_kb_exit(void)
{
/*Deregister device*/
unregister_chrdev(kbMajor, DEVICE_NAME);
printk("exitn");
/*Release interrupt*/
free_irq(IRQ_EINT1,NULL);
free_irq(IRQ_EINT8,NULL);
free_irq(IRQ_EINT3,NULL);
}
module_init(s3c2440_kb_init);
module_exit(s3c2440_kb_exit);
//EXPORT_SYMBOL(s3c2440_kb_init)
;
//EXPORT_SYMBOL(s3c2440_kb_exit);
If you combine this driver with the qtopia program, you need to modify the qkeyboard_qws.cpp file of the qt source code to add support for the matrix keyboard
class QWSHPCButtonsHandler : public QWSKeyboardHandler
{
Q_OBJECT
public:
QWSHPCButtonsHandler();
virtual ~QWSHPCButtonsHandler();
bool isOpen() { return buttonFD > 0; }
private slots:
void readKeyboardData();
private:
QString terminalName;
int buttonFD;
struct termios newT, oldT;
QSocketNotifier *notifier;
};
QWSHPCButtonsHandler::QWSHPCButtons Handler() : QWSKeyboardHandler()
{
#ifdef QT_QWS_HPC
terminalName = "/dev/keyboard";
buttonFD = -1;
notifier = 0;
if ((buttonFD = open(terminalName, O_RDONLY | O_NDELAY, 0)) < 0) {
qWarning("Cannot open %sn", terminalName.latin1());
return;
}
notifier = new QSocketNotifier( buttonFD , QSocketNotifier::Read, this );
connect( notifier, SIGNAL(activated(int)),this,
SLOT(readKeyboardData()) );
#endif
}
QWSHPCButtonsHandler::~QWSHPCButtonsHandler()
{
#ifdef QT_QWS_HPC
if ( buttonFD > 0 ) {
::close( buttonFD );
buttonFD = -1;
}
#endif
}
void QWSHPCButtonsHan dler::readKeyboardData()
{
#ifdef QT_QWS_HPC
//-----------port form ButtonDetect-begin-----------
int tempint,i;
unsigned short buffer[1];
tempint=0;
int current_press=-1;
do{
tempint=read(buttonFD,buffer,1);
if(tempint > 0 )
{
// printf("nCurrent Press Buttom %d n",buffer[0]);
current_ press = (int)buffer[1];
goto set_hpckey;
}
}while(tempint >0);
//-----------port form ButtonDetect-end-------------
set_hpckey:
int k=(-1);
switch(current_press) {
case 3: k=Qt::Key_Up; break; //
case 11: k=Qt::Key_Down; break; //
case 8: k=Qt::Key_Left; break; //
case 6: k=Qt::Key_Right; break; //
case 7: k=Qt::Key_Enter; break; // Enter
case 4: k=Qt::Key_Escape; break; // Enter
default: k=(-1); break;
}
if ( k >= 0 )
{
qwsServer->processKeyEvent( 0, k, 0, 1, false );
}
#endif
}
Previous article:S3C2440 timer configuration
Next article:Compilation and configuration of u-boot-2011.06 based on s3c2440 development board
Recommended ReadingLatest update time:2024-11-16 09:46
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- There are a lot of questions recently, please don't mind.
- [SC8905 EVM Review] Upgrading and troubleshooting the use of I2Ctool on Nanxin power board
- Electromagnetism and quantum mechanics
- ST NUCLEO-L452RE Development Board Introduction
- Linux Command Detailed Dictionary
- ARM register analysis and exception handling methods
- Qorvo has recently launched Wi-Fi 6 and IoT solutions. Will you choose them?
- TI DSP Simulation
- Radar System Engineering: Antenna System (summarizes many sub-projects) You can take a look at it to absorb scattered memories
- 【GD32307E-START】Development environment construction + development practice -> light up the onboard LED