当前位置: 首页 > news >正文

Linux_kernel字符设备驱动12

一、字符设备的编程框架

        在Linux_kernel驱动开发11中,我们介绍的系统调用。只是为了做一个实验,在真正开发时,我们并不会直接在内核中添加一个新的系统调用,这样做会导致内核体积变大。

        1、字符设备结构体

                我们实现一个硬件字符设备的驱动程序,实际上是实例化一个struct cdev类型的对象。

        【1】struct cdev;        // 存储字符设备的相关信息

注意:

        在该结构体中,我们只需关注这两个成员,其他的成员由内核自己维护

        【2】dev_t dev;        // 指示当前的设备号

         【3】const struct file_operations *ops;        // 操作函数的集合

                1)设备号
        【1】区分主次设备号

设备号(32bit) = 主设备号(12bit [msb])+ 次设备号(20bit [lsb])

                1】示例

        ls -l /dev/tty0

主次设备号的范围理论上都是[0, 255]

主设备号:区分不同类型的设备

次设备号:区分同一类型设备的不同个体

        MINORBITS:次设备号的位数

        MINORMASK:次设备号掩码

        MAJOR(dev):得到主设备号

        MINOR(dev):得到次设备号

        MKDEV(ma,mi):将主设备号与次设备号合为一个32bit整型数(dev_t

        【2】静态注册设备号

                就是自己先挑一个没有被内核占用的设备号去注册

                0】查看被内核占用的设备号

        cat /proc/devices

                1】register_chrdev_region(注册设备号)

注释:

        from:要注册的起始设备号

        count:要连续注册的设备号个数

        name:给设备起的名称

                2】unregister_chrdev_region(注销设备号)

注释:

        from:要注销的起始设备号

        count:要连续注销的设备号个数

        【3】静态注册实验

                1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

                2】创建新的工程

        mkdir chrdev

                3】编写程序

        vim chrdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	10
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"leds"MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");int dev = 0;	// store the major dev number and the minor dev numberint __init chrdev_init(void)
{int major = CHRDEV_MAGOR;	// major dev numberint minor = CHRDEV_MINOR;		// minor dev number// dev = major << 20 | minor;// there is a define func to do this taskdev = MKDEV(major, minor);register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME);	//register the number of devicereturn 0;
}void __exit chrdev_exit(void)
{unregister_chrdev_region(dev, CHRDEV_NUM);return ;
}module_init(chrdev_init);
module_exit(chrdev_exit);

                4】编写Makefile

        vim Makefile

obj-m += chrdev.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.ko $(ROOTFS_PATH)clean:make -C $(KERNEL_PATH) M=$(PWD) clean

                5】编译工程

        make

                6】下位机验证

注意:我们现在只有设备号,而没有设备文件

        【4】动态注册设备号

                内核自己找一个没有注册的设备号,注册完归程序员使用

                1】alloc_chrdev_region(注册设备号)

注释:

        dev:回填设备号

        baseminor:次设备号的基值(起始值)

        count:要连续注册的设备号个数

        name:给设备起的名称

                2】unregister_chrdev_region(注销设备号)

注释:

        from:要注销的起始设备号

        count:要连续注销的设备号个数

         【5】动态注册实验

                1】编写程序

        vim chrdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	10
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"leds"MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");int dev = 0;	// store the major dev number and the minor dev number
#if 0
// fixed register
int __init chrdev_init(void)
{int major = CHRDEV_MAGOR;	// major dev numberint minor = CHRDEV_MINOR;		// minor dev number// dev = major << 20 | minor;// there is a define func to do this taskdev = MKDEV(major, minor);register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME);	//register the number of devicereturn 0;
}
#else
// variable register
int __init chrdev_init(void)
{int major = CHRDEV_MAGOR;	// major dev numberint minor = CHRDEV_MINOR;		// minor dev number// there is a define func to register the number of devices automaticallyalloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);major = MAJOR(dev);	// gain the major dev numberminor = MINOR(dev);	// gain the minor dev numberprintk(KERN_EMERG "dev number is :%d\n major number is :%d\n minor number is :%d\n", dev, major, minor);return 0;
}
#endifvoid __exit chrdev_exit(void)
{unregister_chrdev_region(dev, CHRDEV_NUM);return ;
}module_init(chrdev_init);
module_exit(chrdev_exit);

                2】编写Makefile

        vim Makefile

obj-m += chrdev.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.ko $(ROOTFS_PATH)clean:make -C $(KERNEL_PATH) M=$(PWD) clean

                3】编译工程

        make

                4】下位机验证

注意:我们现在只有设备号,而没有设备文件

        2)操作函数的集合
        【1】操作函数集合

       const struct file_operations *ops;        // 操作函数的集合

        实现一个字符设备驱动程序的主要编程工作都集中在操作函数集合,我们将来具体到某一个字符设备驱动程序的时候,只需要实现下列函数集合的子集就可以了。

        【2】内核中提供的操作cdev的API

                1】cdev_init(初始化cdev结构体)

                2】cdev_add(将cdev注册到内核)

注释:

        p:要注册的cdev地址

        dev:要注册的设备号

        count:要连续注册的cdev个数

                3】cdev_del(从内核中注销cdev)

注释:

        p:要注销的cdev地址

        【3】实验

                 1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

                2】创建新的工程

        mkdir chrdev_func

                3】编写程序

        vim chrdev_func.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	10
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"myleds"MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");int dev = 0;	// store the major dev number and the minor dev number// 1_step :define a struct cdev be named led_cdev
struct cdev led_cdev;// 3_step :implement the function of led_fops
int led_open(struct inode *inode, struct file *fp)
{printk(KERN_EMERG "enter:%s\n", __FUNCTION__);return 0;
}int led_close(struct inode *inode, struct file *fp)
{printk(KERN_EMERG "enter:%s\n", __FUNCTION__);return 0;
}// 2_step :define a struct file_operation be named led_fops
// what functions shall we to implement ?
// there is turn_on and turn_off of the leds
// So, we should to implement the function of open() and release(), eithor or we should keep up the same type as the definations of the struct file_operations
struct file_operations led_fops = {.owner = THIS_MODULE,// int (*open) (struct inode *, struct file *);.open = led_open,// int (*release) (struct inode *, struct file *);.release = led_close
};#if 0
// fixed register
int __init chrdev_init(void)
{int major = CHRDEV_MAGOR;	// major dev numberint minor = CHRDEV_MINOR;		// minor dev number// dev = major << 20 | minor;// there is a define func to do this taskdev = MKDEV(major, minor);register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME);	//register the number of devicereturn 0;
}
#else
// variable register
int __init chrdev_init(void)
{int major = CHRDEV_MAGOR;	// major dev numberint minor = CHRDEV_MINOR;		// minor dev number// there is a define func to register the number of devices automaticallyalloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);major = MAJOR(dev);	// gain the major dev numberminor = MINOR(dev);	// gain the minor dev numberprintk(KERN_EMERG "dev number is :%d\n major number is :%d\n minor number is :%d\n", dev, major, minor);// 4_step :initalize the struct cdev object led_cdevcdev_init(&led_cdev, &led_fops);// 5_step :register led_cdev into Kernelcdev_add(&led_cdev, dev, CHRDEV_NUM);return 0;
}
#endifvoid __exit chrdev_exit(void)
{// 6_step :destory cdevcdev_del(&led_cdev);unregister_chrdev_region(dev, CHRDEV_NUM);return ;
}module_init(chrdev_init);
module_exit(chrdev_exit);

                4】编写Makefile

        vim Makefile

obj-m += chrdev_func.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.ko $(ROOTFS_PATH)clean:make -C $(KERNEL_PATH) M=$(PWD) clean

                5】编译工程

        make

                6】下位机安装模块

                7】写一个应用层程序测试

        mkdir test

        cd test

        vim led_test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define CDEV_PATH	"/dev/myleds"int main(void)
{int fd = 0;if((fd = open(CDEV_PATH, O_RDWR)) < 0) {perror("open()");return -1;}printf("open success!\n");sleep(5);printf("closing...\n");close(fd);return 0;
}

        arm-cortex_a9-linux-gnueabi-gcc led_test.c -o led_test

        cp led_test /nfs_share/_install/

        8】下位机测试

        手动创建设备文件

        mknod /dev/myleds c 244 10

        ./led_test

        9】疑惑

        内核中的打印函数与应用程序中的打印函数,执行顺序孰先孰后?

        不确定:内核打印输出的是自己维护的缓冲区,应用程序打印输出的是标准输出缓冲区

二、GPIO库

        1、读懂开发板原理图

LED0        GPIOB26

LED1        GPIOC11

LED2        GPIOC7

LED3        GPIOC12

输出低电平,灯亮

输出高电平,灯灭

        2、CPU Data Sheet

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

        3、内核中提供的操作GPIO的API

        【0】gpio宏定义

        【1】gpio_request(申请GPIO管脚)

int gpio_request(unsigned gpio, const char *label)

        【2】使用GPI管脚

                1】gpio_direction_input(设置输入)

                2】gpio_direction_output(设置输出)

                3】gpio_set_value(设置value)

                4】gpio_get_value(获取value)

        【3】gpio_free(释放GPIO管脚)

void gpio_free(unsigned gpio)

        4、实验

        【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir led_drv

        【3】编写程序

        vim led_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <mach/platform.h>#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	26
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"myleds"
#define HIGH			1
#define LOW				0MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");int dev = 0;	// store the major dev number and the minor dev number// 1_step :define a struct cdev be named led_cdev
struct cdev led_cdev;// 3_step :implement the function of led_fops
int led_open(struct inode *inode, struct file *fp)
{printk(KERN_EMERG "enter:%s\n", __FUNCTION__);// c_step :set the value=0(turn on) of the gpiogpio_set_value(PAD_GPIOB26, LOW);return 0;
}int led_close(struct inode *inode, struct file *fp)
{printk(KERN_EMERG "enter:%s\n", __FUNCTION__);// c_step :set the value=1(turn off) of the gpiogpio_set_value(PAD_GPIOB26, HIGH);return 0;
}// 2_step :define a struct file_operation be named led_fops
// what functions shall we to implement ?
// there is turn_on and turn_off of the leds
// So, we should to implement the function of open() and release(), eithor or we should keep up the same type as the definations of the struct file_operations
struct file_operations led_fops = {.owner = THIS_MODULE,// int (*open) (struct inode *, struct file *);.open = led_open,// int (*release) (struct inode *, struct file *);.release = led_close
};#if 0
// fixed register
int __init chrdev_init(void)
{int major = CHRDEV_MAGOR;	// major dev numberint minor = CHRDEV_MINOR;		// minor dev number// dev = major << 20 | minor;// there is a define func to do this taskdev = MKDEV(major, minor);register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME);	//register the number of devicereturn 0;
}
#else
// variable register
int __init chrdev_init(void)
{int major = CHRDEV_MAGOR;	// major dev numberint minor = CHRDEV_MINOR;		// minor dev number// there is a define func to register the number of devices automaticallyalloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);major = MAJOR(dev);	// gain the major dev numberminor = MINOR(dev);	// gain the minor dev numberprintk(KERN_EMERG "dev number is :%d\n major number is :%d\n minor number is :%d\n", dev, major, minor);// 4_step :initalize the struct cdev object led_cdevcdev_init(&led_cdev, &led_fops);// 5_step :register led_cdev into Kernelcdev_add(&led_cdev, dev, CHRDEV_NUM);// a_step :apply gpiogpio_request(PAD_GPIOB26, "LED0");// b_step :set the default value=1(turn_off) of GPIOB26gpio_direction_output(PAD_GPIOB26, HIGH);return 0;
}
#endifvoid __exit chrdev_exit(void)
{// e_step :release gpiogpio_free(PAD_GPIOB26);// 6_step :destory cdevcdev_del(&led_cdev);unregister_chrdev_region(dev, CHRDEV_NUM);	// unregister the number of devreturn ;
}module_init(chrdev_init);
module_exit(chrdev_exit);
        【4】编写Makefile

        vim Makefile

obj-m += led_drv.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.ko $(ROOTFS_PATH)clean:make -C $(KERNEL_PATH) M=$(PWD) clean
        【5】编译工程

        make

        【6】下位机安装模块

        【7】编写应用层程序

        mkdir test

        cd test

        vim led_test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define CDEV_PATH	"/dev/myleds"int main(void)
{int fd = 0;if((fd = open(CDEV_PATH, ORDWR)) < 0) {perror("open()");return -1;}printf("open success!\n");sleep(5);printf("closing...\n");close(fd);return 0;
}

        arm-cortex_a9-linux-gnueabi-gcc led_test.c -o led_test

        cp led_test /nfs_share/_install/

        【8】下位机测试

        手动创建设备文件

        mknod /dev/myleds c 244 26

        ./led_test

三、用户态与内核态的数据交互

        用户空间不能直接访问内核空间

        内核空间不能直接访问用户空间

        1)内核中提供的数据交互的API

        【1】传递多数据
                1】copy_to_user(内核到用户)

int copy_to_user(void __user *to, const void *from, int n)

注释:

        to:内核空间缓冲区地址,

        from:用户空间地址

        n:数据字节数

        retval:不能被复制的字节数,返回0表示全部复制成功。

                2】copy_from_user(用户到内核)

int copy_from_user(void *to, const void __user *from, int n)

注释:

        to:内核空间缓冲区地址,

        from:用户空间地址

        n:数据字节数

        retval:不能被复制的字节数,返回0表示全部复制成功。

        【2】传递单数据

        可以从指定空间获取单个数据,单个数据并不是指一个字节数据,对ARM而言,一次性可获取一个char、short或者 int型的数据,即1、2或者4字节。

                1】put_user(x, ptr)(内核到用户)

注释:

        x :内核空间的数据,

        p :用户空间的指针。

        传递成功,返回 0,否则返回-EFAULT。

                2】get_user(x, ptr)(用户到内核)

注释:

        x :内核空间的数据,

        p :用户空间的指针。

        传递成功,返回 0,否则返回-EFAULT。

注意:

以上API与C标准库中memcpy(3)相似,但多了一个对访问的空间的权限检查

        2、实验

         【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir param_drv

        【3】编写程序

        vim param_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/uaccess.h>#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	26
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"myleds"
#define HIGH			1
#define LOW				0
#define LED0	(PAD_GPIO_B + 26)
#define LED1	(PAD_GPIO_C + 12)
#define LED2	(PAD_GPIO_C + 7)
#define LED3	(PAD_GPIO_C + 11)unsigned int leds[] = {LED0, LED1, LED2, LED3};
const char *leds_label[] = {"LED0", "LED1", "LED2", "LED3"};MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");int dev = 0;struct cdev led_cdev;int k_cmd = 0;	//kernel's buffer
int k_status = 0;	//the state of LEDsint led_open(struct inode *inode, struct file *fp)
{return 0;
}int led_close(struct inode *indoe, struct file *fp)
{return 0;
}ssize_t led_read(struct file *fp, char __user *buf, size_t len, loff_t *offset)
{int ret = 0;ret = copy_to_user(buf, &k_status, len);return len;
}ssize_t led_write(struct file *fp, const char __user *buf, size_t len, loff_t *offset)
{int ret = 0;int i = 0;ret = copy_from_user(&k_cmd, buf, len);for (i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) {gpio_set_value(leds[i], k_cmd);}k_status = k_cmd;return len;
}struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.release = led_close,.read = led_read,.write = led_write
};int __init chrdev_init(void)
{int major = CHRDEV_MAGOR;int minor = CHRDEV_MINOR;int i = 0;alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);major = MAJOR(dev);minor = MINOR(dev);printk(KERN_EMERG "major = %d\nminor = %d\n", major, minor);cdev_init(&led_cdev, &led_fops);cdev_add(&led_cdev, dev, CHRDEV_NUM);for (i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) {gpio_request(leds[i], leds_label[i]);gpio_direction_output(leds[i], HIGH);}return 0;
}void __exit chrdev_exit(void)
{int i = 0;for (i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) {gpio_free(LED1);}cdev_del(&led_cdev);unregister_chrdev_region(dev, CHRDEV_NUM);return ;
}module_init(chrdev_init);
module_exit(chrdev_exit);
        【4】编写Makefile

        vim Makefile

obj-m += param_drv.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.ko $(ROOTFS_PATH)clean:make -C $(KERNEL_PATH) M=$(PWD) clean
        【5】编译工程

        make

        【6】下位机安装模块

        【7】编写应用层程序

        mkdir test

        cd test

        vim led_test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>#define ON		0
#define OFF		1
#define CDEV_PATH	"/dev/myleds"int main(int argc, char *argv[])
{int fd = 0;int cmd = 0;int status = 0;if (argc < 2) {printf("Usage : %s <on/off>\n", argv[0]);return -1;}if (!strcmp(argv[1], "on")) {cmd = ON;} else if (!strcmp(argv[1], "off")){cmd = OFF;} else {printf("illegal param\n");return -2;}if((fd = open(CDEV_PATH, O_RDWR)) < 0) {perror("open()");return -3;}printf("open success!\n");write(fd, &cmd, sizeof(cmd));read(fd, &status, sizeof(status));if (status == ON) {printf("Led is On!\n");} else {printf("Led is Off!\n");}printf("closing...\n");close(fd);return 0;
}

        vim Makefile

SRC=led_test.c
OBJ=led_testARM_COMPILE=arm-cortex_a9-linux-gnueabi-
GCC=gccROOTFS_PATH=/nfs_share/_installall:$(ARM_COMPILE)$(GCC) $(SRC) -o $(OBJ)cp $(OBJ) $(ROOTFS_PATH)clean:rm -rf $(OBJ)
        【8】编译工程

        make

        【9】下位机测试

        手动创建设备文件

        mknod /dev/myleds c 244 26

        ./led_test

四、ioctl

        1)介绍

        2)实操

        【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir ioctl

        【3】编写程序

        vim ioctl.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/uaccess.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	26
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"myleds"
#define HIGH			1
#define LOW				0
#define LED0	(PAD_GPIO_B + 26)
#define LED1	(PAD_GPIO_C + 12)
#define LED2	(PAD_GPIO_C + 7)
#define LED3	(PAD_GPIO_C + 11)
#define TURN_ON			LOW
#define TURN_OFF		HIGHdev_t dev = 0;struct cdev led_cdev;typedef struct led_desc{unsigned int gpio;char *name;
}led_desc_t;led_desc_t leds[] = {{LED0, "LED0"},{LED1, "LED1"},{LED2, "LED2"},{LED3, "LED3"}
};long led_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{int k_index = 0;int ret = 0;ret = copy_from_user(&k_index, (const void *)arg, sizeof(int));if (k_index > 4 || k_index < 1)return -EINVAL;switch (cmd) {case TURN_ON:gpio_set_value(leds[k_index - 1].gpio, LOW);break;case TURN_OFF:gpio_set_value(leds[k_index - 1].gpio, HIGH);break;default:return -EINVAL;}return arg;
}struct file_operations led_fops = {.owner = THIS_MODULE,.unlocked_ioctl = led_ioctl
};int __init chrdev_init(void)
{int major = CHRDEV_MAGOR;int minor = CHRDEV_MINOR;int i = 0;alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);major = MAJOR(dev);minor = MINOR(dev);printk(KERN_EMERG "major = %d\nminor = %d\n", major, minor);cdev_init(&led_cdev, &led_fops);cdev_add(&led_cdev, dev, CHRDEV_NUM);for (i = 0; i < ARRAY_SIZE(leds); i++) {gpio_request(leds[i].gpio, leds[i].name);gpio_direction_output(leds[i].gpio, HIGH);}return 0;
}void __exit chrdev_exit(void)
{int i = 0;for (i = 0; i < ARRAY_SIZE(leds); i++) {gpio_free(leds[i].gpio);}cdev_del(&led_cdev);unregister_chrdev_region(dev, CHRDEV_NUM);return ;
}module_init(chrdev_init);
module_exit(chrdev_exit);
        【4】编写Makefile

        vim Makefile

obj-m += ioctl.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.ko $(ROOTFS_PATH)clean:make -C $(KERNEL_PATH) M=$(PWD) clean
        【5】编译工程

        make

        【6】下位机安装模块

        【7】编写应用层程序

        mkdir test

        cd test

        vim led_test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>#define ON		0
#define OFF		1
#define CDEV_PATH	"/dev/myleds"int main(int argc, char *argv[])
{int fd = 0;int cmd = 0;int index = 0;if (argc < 3) {printf("Usage : %s <on/off> <1/2/3/4>\n", argv[0]);return -1;}if (!strcmp(argv[1], "on")) {cmd = ON;} else if (!strcmp(argv[1], "off")){cmd = OFF;} else {printf("illegal param\n");return -2;}index = atoi(argv[2]);if (index < 1 || index > 4) {printf("illegal param\n");return -2;}if((fd = open(CDEV_PATH, O_RDWR)) < 0) {perror("open()");return -3;}printf("open success!\n");ioctl(fd, cmd, &index);printf("closing...\n");close(fd);return 0;
}

     vim Makefile

SRC=led_test.c
OBJ=led_testARM_COMPILE=arm-cortex_a9-linux-gnueabi-
GCC=gccROOTFS_PATH=/nfs_share/_installall:$(ARM_COMPILE)$(GCC) $(SRC) -o $(OBJ)cp $(OBJ) $(ROOTFS_PATH)clean:rm -rf $(OBJ)
        【8】编译工程

        make

        【9】下位机测试

        手动创建设备文件

        mknod /dev/myleds c 244 26

        ./led_test

相关文章:

Linux_kernel字符设备驱动12

一、字符设备的编程框架 在Linux_kernel驱动开发11中&#xff0c;我们介绍的系统调用。只是为了做一个实验&#xff0c;在真正开发时&#xff0c;我们并不会直接在内核中添加一个新的系统调用&#xff0c;这样做会导致内核体积变大。 1、字符设备结构体 我们实现一个硬件字符设…...

服务保护sentinel

线程隔离 - 线程池隔离&#xff1a;给每个服务调用业务分配一个线程池&#xff0c;利用线程池本身实现隔离效果。 - 信号量隔离&#xff1a;不创建线程池&#xff0c;而是计数器模式&#xff0c;记录业务使用的线程数量&#xff0c;达到信号量上限时&#xff0c;禁止新的请求。…...

【ubuntu】Ubuntu20.04安装中文百度输入法

1.download 百度Linux输入法-支持全拼、双拼、五笔 2.unzip unzip Ubuntu_Deepin-fcitx-baidupinyin-64.zip 3.setting 3.1 setting fcitx sudo apt install aptitude sudo aptitude install fcitx-bin fcitx-table fcitx-config-gtk fcitx-frontend-all sudo aptitude in…...

蓝桥杯【物联网】零基础到国奖之路:十八. 扩展模块之光敏和AS312

蓝桥杯【物联网】零基础到国奖之路:十八.扩展模块之光敏和AS312 第一节 硬件解读第二节 CubeMX配置第二节 代码 第一节 硬件解读 光敏和AS312如下图&#xff1a; 光敏电阻接到了扩展模块的5号引脚&#xff0c;5号引脚接了2个电阻&#xff0c;R8和光敏电阻。我们通过ADC读取这…...

如何在微信小程序中实现分包加载和预下载

如何在微信小程序中实现分包加载和预下载 概述 微信小程序提供了分包加载和预下载功能&#xff0c;这有助于优化应用的加载时间&#xff0c;提升用户体验。本文将详细介绍如何在微信小程序中配置分包加载和预下载。 步骤一&#xff1a;配置分包加载 修改app.json文件&#x…...

初识TCP/IP协议

回顾上文 来回顾一下TCP协议的特性&#xff0c;有一道比较经典的题&#xff1a;如何使用UDP实现可靠传输&#xff0c;通过应用程序的代码&#xff0c;完成可靠传输的过程&#xff1f; 原则&#xff0c;TCO有啥就吹啥&#xff0c;引入滑动窗口&#xff0c;引入流量控制&#x…...

使用 classification_report 评估 scikit-learn 中的分类模型

介绍 在机器学习领域&#xff0c;评估分类模型的性能至关重要。scikit-learn 是一个功能强大的 Python 机器学习工具&#xff0c;提供了多种模型评估工具。其中最有用的函数之一是 classification_report&#xff0c;它可以全面概述分类模型的关键指标。在这篇文章中&#xff…...

高翔【自动驾驶与机器人中的SLAM技术】学习笔记(十)高翔书中的细节:参考链接;卫星导航;ESKF

一、 参考链接 我认真查找了好多地方:结果在最后一页。 作者GITHUB链接如下: https://github.com/gaoxiang12/slam_in_autonomous_driving 全书所有参考链接 :如下 1 https://www.sae.org/standards/content/j3016_202104 2 http://www.evinchina.com/articleshow-217.htm…...

【在Python中爬取网页信息并存储】

在Python中爬取网页信息并存储的过程通常涉及几个关键步骤&#xff1a;发送HTTP请求、解析HTML内容、提取所需数据&#xff0c;以及将数据存储到适当的格式中&#xff08;如文本文件、CSV文件、数据库等&#xff09;。以下是一个更详细的指南&#xff0c;包括示例代码&#xff…...

ESP32 Bluedroid 篇(1)—— ibeacon 广播

前言 前面我们已经了解了 ESP32 的 BLE 整体架构&#xff0c;现在我们开始实际学习一下Bluedroid 从机篇的广播和扫描。本文将会以 ble_ibeacon demo 为例子进行讲解&#xff0c;需要注意的一点是。ibeacon 分为两个部分&#xff0c;一个是作为广播者&#xff0c;一个是作为观…...

【通配符】粗浅学习

1 背景说明 首先要注意&#xff0c;通配符中的符号和正则表达式中的特殊符号具备不同的匹配意义&#xff0c;例如&#xff1a;*在正则表达式中表示里面是指匹配前面的子表达式0次或者多次&#xff0c;而在通配符领域则是表示代表0个到无穷个任意字符。 此外&#xff0c;要注意…...

Spring MVC 常用注解

目录 基础概念 常用注解介绍 基础概念 1、MVC &#xff1a;代表一种软件架构设计思想&#xff0c;通俗的理解&#xff1a;客户端发送请求到后台服务器的Controller(C)&#xff0c;控制器调用Model(M)来处理业务逻辑&#xff0c;处理完成后&#xff0c;返回处理后的数据到Vie…...

水泵模块(5V STM32)

目录 一、介绍 二、传感器原理 1.尺寸介绍 2.继电器控制水泵电路原理图 三、程序设计 main.c文件 bump.h文件 bump.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 水泵模块(bump)通常是指用于液体输送系统的组件&#xff0c;它负责将水或其他流体从低处提…...

需求6:如何写一个后端接口?

这两天一直在对之前做的工作做梳理总结&#xff0c;不过前两天我都是在总结一些bug的问题。尽管有些bug问题我还没写文章&#xff0c;但是&#xff0c;我今天不得不先停下对bug的总结了。因为在国庆之后&#xff0c;我需要自己开发一个IT资产管理的功能&#xff0c;这个功能需要…...

《Linux从小白到高手》理论篇(五):文件权限控制及文件操作相关的命令

本篇介绍Linux文件权限控制及文件操作相关的命令&#xff0c;看完本文&#xff0c;有关Linux文件权限控制及文件操作相关的常用命令你就掌握了99%了。 文件权限 在介绍文件权限之前先来复习下Linux的文件类型&#xff0c;始终记住那句话&#xff1a;Linux系统下&#xff0c;一…...

异常场景分析

优质博文&#xff1a;IT-BLOG-CN 为了防止黑客从前台异常信息&#xff0c;对系统进行攻击。同时&#xff0c;为了提高用户体验&#xff0c;我们都会都抛出的异常进行拦截处理。 一、异常处理类 Java把异常当做是破坏正常流程的一个事件&#xff0c;当事件发生后&#xff0c;…...

Leetcode: 0001-0010题速览

Leetcode: 0001-0010题速览 本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer&#xff08;第 2 版&#xff09;》、《程序员面试金典&#xff08;第 6 版&#xff09;》题解 遵从开源协议为知识共享 版权归属-相同方式…...

计算机的错误计算(一百一十二)

摘要 计算机的错误计算&#xff08;六十三&#xff09;与&#xff08;六十八&#xff09;以及&#xff08;六十九&#xff09;分别探讨了大数与 附近数以及 附近数 的余切函数的计算精度问题。本节讨论余切序列&#xff08;即迭代 &#xff09;的计算精度问题。 余切序列是指…...

C++基础(7)——STL简介及string类

目录 1.STL简介 1.1什么是 1.2STL的历史版本 1.3STL的六大组件 ​编辑 1.4有用的网址 2.string类 2.1string的多种定义方式 2.2string的插入 2.2.1尾插&#xff08;push_back&#xff09; 2.2.2insert插入 2.3拼接&#xff08;append&#xff09; 2.4删除 2.4.1尾…...

配置Nginx以支持通过HTTPS回源到CDN

要配置Nginx以支持通过HTTPS回源到CDN&#xff0c;你需要确保Nginx已正确配置SSL&#xff0c;并且能够处理来自CDN的HTTPS请求。以下是一个简化的Nginx配置示例&#xff0c;它配置了SSL并设置了代理服务器参数以回源到CDN&#xff1a; server {listen 443 ssl;server_name you…...

yolov10+strongsort的目标跟踪实现

此次yolov10deepsort不论是准确率还是稳定性&#xff0c;再次超越了之前的yolodeepsort系列。 yolov10介绍——实时端到端物体检测 YOLOv10 是清华大学研究人员在 UltralyticsPython 清华大学的研究人员在 YOLOv10软件包的基础上&#xff0c;引入了一种新的实时目标检测…...

C# 字符与字符串

本课要点&#xff1a; 1、字符类Char的使用 2、字符串类String的使用 3、可变字符串****StringBuilder 4、常见错误 一 何时用到字符与字符串 问题&#xff1a; 输出C#**课考试最高分&#xff1a;**98.5 输出最高分学生姓名&#xff1a;张三 输出最高分学生性别&#x…...

在Ubuntu 16.04上使用LEMP安装WordPress的方法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 WordPress 是互联网上最流行的 CMS&#xff08;内容管理系统&#xff09;。它允许您在 MySQL 后端和 PHP 处理的基础上轻松设置灵…...

显示器放大后,大漠识图识色坐标偏移解决方法

原因分析&#xff1a; 显示器分辨率较高&#xff0c;DPI设置放大125% or 150% or 200%&#xff0c;游戏打开时也会默认会根据显示器的放大比例自行放大&#xff0c;但是大漠综合管理工具抓图不会放大&#xff1b; 解决方法&#xff1a; 1、大漠综合管理…...

C++容器之list基本使用

目录 前言 一、list的介绍&#xff1f; 二、使用 1.list的构造 2.list iterator的使用 3.list capacity &#x1f947; empty &#x1f947;size 4.list element access &#x1f947; front &#x1f947; back 5.list modifiers &#x1f947; push_front &#x1f947; po…...

Redis-哨兵

概念 Redis Sentinel 相关名词解释 注意: 哨兵机制不负责存储数据,只是对其它的redis-server进程起到监控的作用哨兵节点,也会搞一个集合,防止一个挂了 ⼈⼯恢复主节点故障 用户监控: 实际开发中,对于服务器后端开发,监控程序,是很重要的 服务器长期运行,总会有一些意外,…...

Pikachu-Sql-Inject - 基于时间的盲注

基于时间的盲注&#xff1a; 就是前端的基于time 的盲注&#xff0c;什么错误信息都看不到&#xff0c;但是还可以通过特定的输入&#xff0c;判断后台的执行时间&#xff0c;从而确定注入。 mysql 里函数sleep() 是延时的意思&#xff0c;sleep(10)就是数据库延时10 秒返回内…...

JAVA开源项目 旅游管理系统 计算机毕业设计

本文项目编号 T 063 &#xff0c;文末自助获取源码 \color{red}{T063&#xff0c;文末自助获取源码} T063&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…...

景联文科技入选《2024中国AI大模型产业图谱2.0版》数据集代表厂商

近日&#xff0c;大数据产业领域头部媒体数据猿携手上海大数据联盟联合发布了备受瞩目的《2024中国AI大模型产业图谱2.0版》。以大数据与AI为代表的智能技术为主要视角&#xff0c;聚焦全产业链&#xff0c;为业内提供更为专业直观的行业指导。 景联文科技凭借高质量数据集&…...

【C语言】内存函数的使用和模拟实现

文章目录 一、memcpy的使用和模拟实现二、memmove的使用和模拟实现三、memset的使用四、memcmp的使用 一、memcpy的使用和模拟实现 在之前我们学习了使用和模拟实现strncpy函数&#xff0c;它是一个字符串函数&#xff0c;用来按照给定的字节个数来拷贝字符串&#xff0c;那么问…...

集团微网站建设/电子商务网站建设教程

流动效率 是度量组织精益程度的指标。对于ITC公司来说&#xff1a;根据《Zsolt Fabok, Lean Agile Scotland, Sep 2012, Lean Kanban France, Oct 2012》报告&#xff0c;流动效率为2%流动效率在5%至15%认为正常流动效率>40%就很好了&#xff01;恰如《This is Lean》中所述…...

建设汽车行业网站/百度刷首页怎么刷

当前&#xff0c;https网站越来越多&#xff0c;有用户在访问某些https网站的时候&#xff0c;时常会遇到浏览器提示"此网站的安全证书有问题"以及"此网站出具的安全证书已过期或还未生效"&#xff0c;并且浏览器的地址栏的绿色安全锁也没有出现&#xff0…...

网站建设自学/怎么能在百度上做推广

日前&#xff0c;美团“无人配送防疫助力计划”而启用的无人配送车也宣布在北京市顺义、海淀等地投放&#xff0c;实现“无接触配送”。 图片来自于网络 无人驾驶技术的快速发展&#xff0c;使得无人配送车、无人消毒车等搭载着该技术的各类自主机器人冲上了抗疫前线&#xff…...

行业门户网站如何做/百度关键词搜索引擎

0. MobileNet介绍 MobileNet是M为移动和嵌入式设备提出的高效模型。MobileNet基于流线型(streamlined) 架构&#xff0c;使用深度可分离卷积(depthwise separable convolutions, 即Xception变体结构, 详细请参考干巴他爹–Depthwise卷积与Pointwise卷积)来构建轻量级深度神经网…...

西安网站推广/网络营销计划书怎么写

题目即将N进行拆分&#xff0c;求各部分的LCM有几种。 我们考虑对于一个可能的LCM的值&#xff0c;如果这个值可以由不互质的几部分得到&#xff0c;那么它一定可以由互质的几部分得到。 那么就可以先线性筛出素数。然后把每个素数的k(k1,2,3…)看成物品往背包里装&#xff0…...

wordpress弹窗/网络营销自学网站

Clean Code代码规范1、有意义的命名1.1、名副其实知道函数发生了什么&#xff0c;传入什么&#xff0c;返回什么1.2、避免误导避免留下隐藏代码本意的错误线索1.3、做有意义的区分废话是另外一种没有意义的区分1.4、使用读得出来的名称读起来像人话1.5、使用可搜索的名称2、类名…...