The test program and Makefile are the same as the previous experiment. Here we only need to record the source code of the driver. The changes are not big. We just move the operations of waking up the process and sending asynchronous signals to the timeout function of the timer. This is done The purpose is to eliminate the mechanical jitter of the keys.
Driver source code:
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GRH_MODULE_NAME "key_interrupt" static int major; static struct class *key_interrupt_class; static struct class_device *key_interrupt_device; static int key_value; //Two variables required by the wait_event_interruptible function static DECLARE_WAIT_QUEUE_HEAD(grh_wait_interrupt); //Sleeping process queue head static volatile int sleep_for_interrupt; //When this variable is 0, the read function will sleep, set it to 1 in the interrupt, and set it to 0 at the end of the read function //Asynchronous signal queue definition static struct fasync_struct *grh_async_queue; //Define a timer for key anti-shake static struct timer_list grh_timer_shake; static int first_timer_timeout; //pin_desc is a description of each key interrupt. It can not only be an integer, but also a more complex field. Here, a simple key value will suffice. int pin_desc[6] = { 1, 2, 3, 4, 5, 6 }; //interrupt handler function static irqreturn_t grh_handle_key_eint(int irq, void *dev_id){ int *p; p = dev_id; //printk(KERN_EMERG"key pressed! key=%dn", *p); key_value = *p; //Reset the timeout time of the timer, where HZ is the system count value corresponding to one second. The following timer is 100ms. mod_timer(&grh_timer_shake, jiffies+HZ/10); //The work of waking up the dormant process and sending asynchronous signals is left to the timer timeout processing function. return IRQ_HANDLED; } static void grh_timer_shake_handler(unsigned long n){ if(first_timer_timeout){ first_timer_timeout = 0; return; } //Wake up the dormant process sleep_for_interrupt = 1; wake_up_interruptible(&grh_wait_interrupt); //Send asynchronous signal to user space process kill_fasync(&grh_async_queue, SIGIO, POLL_IN); } static void init_key(void){ //Register the irq interrupt processing function, bind the key value and interrupt number, and hand over all operations of clearing interrupts and initializing interrupt-related registers //The kernel completes it automatically. There is no need to explicitly read and write registers like a bare-metal program. After an interrupt occurs, it will automatically jump to grh_handle_key_eint. request_irq(IRQ_EINT8, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key1", pin_desc); request_irq(IRQ_EINT11, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key2", pin_desc+1); request_irq(IRQ_EINT13, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key3", pin_desc+2); request_irq(IRQ_EINT14, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key4", pin_desc+3); request_irq(IRQ_EINT15, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key5", pin_desc+4); request_irq(IRQ_EINT19, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key6", pin_desc+5); } static int key_interrupt_open(struct inode *inode, struct file *file){ printk(KERN_EMERG"DRIVER: OPENn"); sleep_for_interrupt = 0; init_key(); return 0; } static ssize_t key_interrupt_write(struct inode *inode, const char __user *buf, size_t count, loff_t *ppos){ printk(KERN_EMERG"DRIVER: WRITEn"); return 0; } static ssize_t key_interrupt_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ printk(KERN_EMERG"DRIVER: READn"); //Determine whether to add the driver process to the sleep queue grh_wait_interrupt based on the value of sleep_for_interrupt, and sleep the process immediately wait_event_interruptible(grh_wait_interrupt, sleep_for_interrupt); copy_to_user(buf, &key_value, 4); //The next time you enter read, continue to sleep and wait for an interrupt to occur. sleep_for_interrupt = 0; return 0; } int key_interrupt_release(struct inode *inode, struct file *file){ //Logout interrupt free_irq(IRQ_EINT8, pin_desc); free_irq(IRQ_EINT11, pin_desc+1); free_irq(IRQ_EINT13, pin_desc+2); free_irq(IRQ_EINT14, pin_desc+3); free_irq(IRQ_EINT15, pin_desc+4); free_irq(IRQ_EINT19, pin_desc+5); printk(KERN_EMERG"DRIVER: RELEASEn"); return 0; } //sys_poll will repeatedly call key_interrupt_poll in an infinite loop static unsigned int key_interrupt_poll(struct file *file, struct poll_table_struct *wait){ unsigned int mask; printk(KERN_EMERG"DRIVER POLLn"); poll_wait(file, &grh_wait_interrupt, wait); //Put the current process into the sleep queue, but do not sleep immediately mask = 0; if(sleep_for_interrupt){ //Interrupt occurred mask |= POLLIN | POLLRDNORM; //Set the flag bit with readable data to 1, and the user layer can get this mask } return mask; /* If the returned mask is 0, then the process directly enters scheduled sleep. If an interrupt occurs during scheduled sleep, the value in sys_poll After the scheduled sleep ends, sys_poll will call key_interrupt_poll in a loop again, but at this time the mask must return a non-zero value. The hibernation in sys_poll ends and the process continues running. If the interrupt never occurs during scheduled sleep, then after the scheduled sleep times out, sys_poll calls key_interrupt_poll again, and then determines whether it times out. If it times out, it directly ends the sleep of the process. This is The general principle of the poll mechanism of the Linux kernel. In this way, when the poll function is called once at the user level, the maximum sleep time of the process is the wait parameter passed in. As soon as the interrupt occurs, the scheduled sleep will end immediately, otherwise the process will sleep until the end of a scheduled sleep. */ } //When the user space program calls fcntl(fd, F_SETFL, flag | FASYNC), the following asynchronous notification setting function will be called static int key_interrupt_fasync(int fd, struct file *file, int on){ //fasync_helper function will pass the pid of the user space process into grh_async_queue //In this way, the signal sent in the interrupt handling function can be received by the user space application printk(KERN_EMERG"DRIVER : FASYNCn"); //The work of initializing grh_async_queue is left to fasync_helper, and the driver will not implement it. return fasync_helper(fd, file, on, &grh_async_queue); } static struct file_operations key_interrupt_fops = { .owner = THIS_MODULE, .open = key_interrupt_open, .write = key_interrupt_write, .read = key_interrupt_read, .release = key_interrupt_release, .poll = key_interrupt_poll, .fasync = key_interrupt_fasync, }; int key_interrupt_module_init(void){ printk(KERN_EMERG"INIT MODULE!n"); //Initialize the anti-shake timer, the default timeout is 0 init_timer(&grh_timer_shake); grh_timer_shake.function = grh_timer_shake_handler; add_timer(&grh_timer_shake); first_timer_timeout = 1; //register the driver with the device major = register_chrdev(0, GRH_MODULE_NAME, &key_interrupt_fops); //create my own device class key_interrupt_class = class_create(THIS_MODULE, "key_interrupt_class"); //create my device of my own class key_interrupt_device = device_create(key_interrupt_class, NULL, MKDEV(major,0), NULL, "key_interrupt_device"); return 0; } void key_interrupt_module_exit(void){ unregister_chrdev(major, GRH_MODULE_NAME); device_unregister(key_interrupt_device); class_destroy(key_interrupt_class); printk(KERN_EMERG"EXIT MODULE!n"); } module_init(key_interrupt_module_init); module_exit(key_interrupt_module_exit); MODULE_AUTHOR("GRH"); MODULE_VERSION("1.0"); MODULE_DESCRIPTION("KEY POLL DRIVER"); MODULE_LICENSE("GPL");
Previous article:mini2440 button-driven POLL mechanism experiment
Next article:mini2440 button driver asynchronous signal notification mode experiment
Recommended ReadingLatest update time:2024-11-16 04:12
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- 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
- CodeBug Hands-on Learning 1 - Unboxing
- Please help me solve the problem of converting gerber files to PCB files
- HP laptop battery is swollen, where can I get a new one?
- pyRTOS(1): Introduction
- Robot arm forward and inverse solution simulation demonstration UI based on LVGL
- CircuitPython 5.0.0 Beta 5 released
- Problem with conditional execution with $value$plusargs
- An Engineer's Guide to EMI - Specification and Measurement
- C5000 compiles SUBC instruction to implement division
- Improvements and updates to the forum's "Invite Netizens" function