In the previous section, we explained how to automatically create device nodes. In this section, based on the previous section, we realized lighting up LEDs.
Link to the previous article: http://blog.csdn.net/xiaoxiaopengbo/article/details/78779835
There are many ways to implement LED driver in the driver, including the character driver in this section (the dumbest method), the hybrid device driver, the use of the kernel GPIO function interface, the method of using the general platform device driver, etc. But don't stop learning because this section is the dumbest method, for beginners, learning step by step is a good habit, well, without further ado, let's get right to the point.
Q: How to write LED driver?
1. Build a character-driven framework (the previous section has been completed)
2. Improve the operation of the hardware
Q: What is the difference between operating the hardware registers in the driver and operating the hardware registers in the microcontroller?
Answer: The address of the register operated by the microcontroller is the physical address, and the operation in the driver must be a virtual address, because the driver is a part of the kernel, and the addresses in the kernel are all virtual addresses.
Q: How to convert physical addresses to virtual addresses?
A: Using the ioremap function, its function is to map physical addresses to virtual addresses. How to map it needs to see Linux memory management and so on.
Q: What if the application wants to pass data to the kernel?
Answer: Use the copy_from_user function. Similarly, if the kernel wants to transfer data to applications in the application space, use the copy_to_user function.
For details, please refer to the driver source code:
#include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <linux/module.h> #include <linux/device.h> //class_create static struct class *firstdrv_class; static struct device *firstdrv_device; volatile unsigned long *gpbcon = NULL; volatile unsigned long *gpbdat = NULL; int major; static int first_drv_open(struct inode * inode, struct file * filp) { printk("first_drv_open\n"); /* LED1, LED2, LED3, LED4 correspond to GPB5, GPB6, GPB7, GPB8 * Configure GPB5,6,7,8 as output */ *gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2))); *gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2))); return 0; } static int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos) { int val; printk("first_drv_write\n"); copy_from_user(&val, buffer, count); if (val == 1) { // lit *gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8)); } else { // Lights off *gpbdat |= (1<<5) | (1<<6) | (1<<7) | (1<<8); } return 0; } /* File operations struct for character device */ static const struct file_operations first_drv_fops = { .owner = THIS_MODULE, .open = first_drv_open, .write = first_drv_write, }; /* Driver entry function */ static int first_drv_init(void) { /* If the main device number is set to 0, the system will automatically assign the main device number */ major = register_chrdev(0, "first_drv", &first_drv_fops); /* Create firstdrv class */ firstdrv_class = class_create(THIS_MODULE, "firstdrv"); /* Create a xxx device under the firstdrv class for the application to open the device */ firstdrv_device = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xxx"); /* Map physical addresses to virtual addresses */ gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16); gpbdat = gpbcon + 1; return 0; } /* driver exit function */ static void first_drv_exit(void) { unregister_chrdev(major, "first_drv"); device_unregister(firstdrv_device); //Uninstall the device under the class class_destroy(firstdrv_class); //Uninstall the class iounmap(gpbcon); //Unmap } module_init(first_drv_init); //Used to decorate the entry function module_exit(first_drv_exit); //Used to decorate the exit function MODULE_AUTHOR ("LWJ"); MODULE_DESCRIPTION("Just for Demon"); MODULE_LICENSE("GPL"); //Follow GPL agreement
Application source code
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> /* first_test on * first_test off */ int main(int argc ,char *argv[]) { int fd; int val = 0; fd = open("/dev/xxx",O_RDWR); if (fd < 0) { printf("open error\n"); } if (argc != 2) { printf("Usage:\n"); printf("%s <on|off>\n",argv[0]); return 0; } if(strncmp(argv[1],"on",2) == 0) { val = 1; } else if (strncmp(argv[1],"off",3) == 0) { val = 0; } write(fd,&val,4); return 0; }
test steps
[WJ2440] # ls Qt driver_test lib root udisk TQLedtest etc linuxrc sbin usr app_test first_drv.ko mnt sddisk var bin first_test opt sys web dev home proc tmp [WJ2440]# ls -l /dev/xxx ls: /dev/xxx: No such file or directory [WJ2440]# insmod first_drv.ko [WJ2440]# lsmod first_drv 2300 0 - Live 0xbf003000 [WJ2440]# ls -l /dev/xxx crw-rw---- 1 root root 252, 0 Jan 2 00:23 /dev/xxx [WJ2440]# ./first_test first_drv_open Usage: ./first_test <on|off> [WJ2440]# ./first_test off first_drv_open first_drv_write [WJ2440]# ./first_test on first_drv_open first_drv_write [WJ2440]#It can be found that when the following statement is executed, the 4 LEDs on the development board are turned off at the same time:
[WJ2440]# ./first_test off
It can be found that when the following statement is executed, the 4 LEDs on the development board are lit at the same time:
[WJ2440]# ./first_test on
Reprinted from: http://blog.csdn.net/lwj103862095/article/details/17472455