Linux character driver lights up LED

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


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325472705&siteId=291194637