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中,我们介绍的系统调用。只是为了做一个实验,在真正开发时,我们并不会直接在内核中添加一个新的系统调用,这样做会导致内核体积变大。 1、字符设备结构体 我们实现一个硬件字符设…...
服务保护sentinel
线程隔离 - 线程池隔离:给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果。 - 信号量隔离:不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求。…...

【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如下图: 光敏电阻接到了扩展模块的5号引脚,5号引脚接了2个电阻,R8和光敏电阻。我们通过ADC读取这…...
如何在微信小程序中实现分包加载和预下载
如何在微信小程序中实现分包加载和预下载 概述 微信小程序提供了分包加载和预下载功能,这有助于优化应用的加载时间,提升用户体验。本文将详细介绍如何在微信小程序中配置分包加载和预下载。 步骤一:配置分包加载 修改app.json文件&#x…...

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

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

高翔【自动驾驶与机器人中的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中爬取网页信息并存储的过程通常涉及几个关键步骤:发送HTTP请求、解析HTML内容、提取所需数据,以及将数据存储到适当的格式中(如文本文件、CSV文件、数据库等)。以下是一个更详细的指南,包括示例代码ÿ…...

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

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

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

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

需求6:如何写一个后端接口?
这两天一直在对之前做的工作做梳理总结,不过前两天我都是在总结一些bug的问题。尽管有些bug问题我还没写文章,但是,我今天不得不先停下对bug的总结了。因为在国庆之后,我需要自己开发一个IT资产管理的功能,这个功能需要…...
《Linux从小白到高手》理论篇(五):文件权限控制及文件操作相关的命令
本篇介绍Linux文件权限控制及文件操作相关的命令,看完本文,有关Linux文件权限控制及文件操作相关的常用命令你就掌握了99%了。 文件权限 在介绍文件权限之前先来复习下Linux的文件类型,始终记住那句话:Linux系统下,一…...

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

Leetcode: 0001-0010题速览
Leetcode: 0001-0010题速览 本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解 遵从开源协议为知识共享 版权归属-相同方式…...
计算机的错误计算(一百一十二)
摘要 计算机的错误计算(六十三)与(六十八)以及(六十九)分别探讨了大数与 附近数以及 附近数 的余切函数的计算精度问题。本节讨论余切序列(即迭代 )的计算精度问题。 余切序列是指…...

C++基础(7)——STL简介及string类
目录 1.STL简介 1.1什么是 1.2STL的历史版本 1.3STL的六大组件 编辑 1.4有用的网址 2.string类 2.1string的多种定义方式 2.2string的插入 2.2.1尾插(push_back) 2.2.2insert插入 2.3拼接(append) 2.4删除 2.4.1尾…...
配置Nginx以支持通过HTTPS回源到CDN
要配置Nginx以支持通过HTTPS回源到CDN,你需要确保Nginx已正确配置SSL,并且能够处理来自CDN的HTTPS请求。以下是一个简化的Nginx配置示例,它配置了SSL并设置了代理服务器参数以回源到CDN: server {listen 443 ssl;server_name you…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...