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

淄博网站建设选哪家免费图片编辑工具

淄博网站建设选哪家,免费图片编辑工具,手机网页制作app,装饰行业网站模板并发与竞争 并发 ​ 多个“用户”同时访问同一个共享资源。 竞争 并发和竞争的处理方法 处理并发和竞争的机制#xff1a;原子操作、自旋锁、信号量和互斥体。 1、原子操作 ​ 原子操作就是指不能再进一步分割的操作#xff0c;一般原子操作用于变量或者位操作。 ​ …并发与竞争 并发 ​ 多个“用户”同时访问同一个共享资源。 竞争 并发和竞争的处理方法 处理并发和竞争的机制原子操作、自旋锁、信号量和互斥体。 1、原子操作 ​ 原子操作就是指不能再进一步分割的操作一般原子操作用于变量或者位操作。 ​ Linux 内核定义了叫做 atomic_t 的结构体来完成整形数据的原子操作在使用中用原子变量来代替整形变量 。 2、自旋锁 Linux 内核使用结构体 spinlock_t 表示自旋锁 。对于自旋锁而言如果自旋锁正在被线程 A 持有线程 B 想要获取自旋锁那么线程 B 就会处于忙循环-旋转-等待状态线程 B 不会进入休眠状态或者说去做其他的处理而是会一直傻傻的在那里“转圈圈”的等待锁可用。 注意中断里面可以使用自旋锁但是在中断里面使用自旋锁的时候在获取锁之前一定要先禁止本地中断(也就是本 CPU 中断对于多核 SOC来说会有多个 CPU 核)否则可能导致锁死现象的发生。 3、信号量 信号量常常用于控制对共享资源的访问。 相比于自旋锁信号量可以使线程进入休眠状态 。 使用信号量会提高处理器的使用效率。但是信号量的开销要比自旋锁大因为信号量使线程进入休眠状态以后会切换线程切换线程就会有开销。 总结一下信号量的特点 ①、因为信号量可以使等待资源线程进入休眠状态因此适用于那些占用资源比较久的场合。 ②、因此信号量不能用于中断中因为信号量会引起休眠中断不能休眠。 ③、如果共享资源的持有时间比较短那就不适合使用信号量了因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势。 计数型信号量不能用于互斥访问因为它允许多个线程同时访问共享资源。如果要互斥的访问共享资源那么信号量的值就不能大于 1此时的信号量就是一个二值信号量。 4、互斥体 在 FreeRTOS 和 UCOS 中也有互斥体将信号量的值设置为 1 就可以使用信号量进行互斥访问了虽然可以通过信号量实现互斥但是 Linux 提供了一个比信号量更专业的机制来进行互斥它就是互斥体—mutex。 互斥访问表示一次只有一个线程可以访问共享资源不能递归申 请互斥体。 mutex 的时候要注意如下几点 ①、 mutex 可以导致休眠因此不能在中断中使用 mutex中断中只能使用自旋锁。 ②、和信号量一样 mutex 保护的临界区可以调用引起阻塞的 API 函数。 ③、因为一次只有一个线程可以持有 mutex因此必须由 mutex 的持有者释放 mutex。并且 mutex 不能递归上锁和解锁。 中断 中断的处理方法 ①、使能中断初始化相应的寄存器。 ②、注册中断服务函数也就是向 irqTable 数组的指定标号处写入中断服务函数 ③、中断发生以后进入 IRQ 中断服务函数在 IRQ 中断服务函数在数组 irqTable 里面查找具体的中断处理函数找到以后执行相应的中断处理函数。 获取中断号 - 申请中断 - 中断使能 - 中断处理函数 - 释放中断 - 中断禁止 为实现中断处理函数的快进快出: **上半部**上半部就是中断处理函数那些处理过程比较快不会占用很长时间的处理就可以放在上半部完成。 下半部如果中断处理过程比较耗时那么就将这些比较耗时的代码提出来交给下半部去执行这样中断处理函数就会快进快出。 ①、如果要处理的内容不希望被其他中断打断那么可以放到上半部。 ②、如果要处理的任务对时间敏感可以放到上半部。 ③、如果要处理的任务与硬件有关可以放到上半部 ④、除了上述三点以外的其他任务优先考虑放到下半部。 上半部处理很简单直接编写中断处理函数就行了关键是下半部该怎么做呢 Linux 内核提供了多种下半部机制软中断 、tasklet 、工作队列 。 软中断、tasklet和工作队列都是用于处理中断事件的机制但它们的优先级和执行上下文有所不同。软中断的优先级最高可以在任何上下文中被调度执行tasklet的优先级次之它在软中断上下文中执行可以睡眠但不能被抢占工作队列的优先级最低它在进程上下文中执行可以睡眠也可以被抢占。根据具体的需求和性能要求可以选择合适的下半部机制来处理中断事件。阻塞IO和非阻塞IO 阻塞式 IO 会将应用程序对应的线程挂起放到等待队列当中直到设备资源可以获取为止。 非阻塞 IO应用程序对应的线程不会挂起它会通过poll函数不断的轮询查看驱动设备资源可以使用。 这两种方式都需要应用程序主动的去查询设备的使用情况。 poll、 epoll 和 select 可以用于处理轮询应用程序通过 select、 epoll 或 poll 函数来 查询设备是否可以操作如果可以操作的话就从设备读取或者向设备写入数据。当应用程序调 用 select、 epoll 或 poll 函数的时候设备驱动程序中的 poll 函数就会执行因此需要在设备驱动 程序中编写 poll 函数。 异步通知 当驱动程序可以访问时驱动可以主动向应用程序发送信号的方式来报告自己可以访问了应用程序获取到信号以后就可以从驱动设备中读取或者写入数据了。 整个过程就相当于应用程序收到了驱动发送过来了的一个中断然后应用程序去响应这个中断在整个处理过程中应用程序并没有去查询驱动设备是否可以访问一切都是由驱动设备自己告诉给应用程序的。 设备节点根节点“/”下 pinctrl结点 iomuxc 节点下 常规操作先设置某个 PIN 的复用功能、速度、上下拉等然后再设置 PIN 所对应的 GPIO。 其实对于大多数的 32 位 SOC 而言引脚的设置基本都是这两方面因此 Linux 内核针对 PIN 的配置推出了 pinctrl 子系统对于 GPIO 的配置推出了 gpio 子系统。 传统的配置 pin 的方式就是直接操作相应的寄存器但是这种配置 方式比较繁琐、而且容易出问题(比如 pin 功能冲突)。 pinctrl 子系统主要工作内容如下 ①、获取设备树中 pin 信息。 ②、根据获取到的 pin 信息来设置 pin 的复用功能 ③、根据获取到的 pin 信息来设置 pin 的电气特性比如上/下拉、速度、驱动能力等。 对于我们使用者来讲只需要在设备树里面设置好某个 pin 的相关属性即可其他的初始化工作均由 pinctrl 子系统来完成 pinctrl 子系统源码目录为 drivers/pinctrl。 // 设备树中添加pinctrl节点模板 // 1、创建对应的节点 // 同一个外设的PIN都放在一个节点里面在iomuxc节点中的“imx6ul-evk”子节点下添加“pinctrl_test”节点 pinctrl_test: testgrp{/*具体的PIN信息*/ }; // 2、添加“fsl,pins”属性来保存信息 // pinctrl 驱动程序是通过读取“fsl,pins”属性值来获取 PIN 的配置信息 pinctrl_test: testgrp{fsl,pins/*设备所使用的PIN配置信息*/ }; // 3、在“fsl,pins”属性中添加PIN配置信息 pinctrl_test: testgrp{fsl,pinsMX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config /*config是具体设置值*/ };如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话那么接下来就要用到 gpio 子系统了。 gpio 子系统就是用于初始化 GPIO 并且提供相应的 API 函数比如设置 GPIO为输入输出读取 GPIO 的值等。 gpio 子系统的主要目的就是方便驱动开发者使用 gpio驱动开发者在设备树中添加 gpio 相关信息然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程极大的方便了驱动开发者使用 GPIO。 //I.MX6ULL-ALPHA 开发板上的 UART1_RTS_B 做为 SD 卡的检测引脚 UART1_RTS_B 复用为 GPIO1_IO19通过读取这个 GPIO 的高低电平就可以知道 SD 卡有没有插入。 // 设置pinctrl 节点将 UART1_RTS_B 这个 PIN 复用为 GPIO1_IO19并且设置电气属性。 pinctrl_hog_1: hoggrp-1{fsl,pins MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */ } //设置gpio 节点在设备树中 SD 卡节点下添加一个属性来描述 SD 卡的 CD 引脚就行了 SD卡驱动直接读取这个属性值就知道 SD 卡的 CD 引脚使用的是哪个GPIO 了。 usdhc1 {pinctrl-names default, state_100mhz, state_200mhz;pinctrl-0 pinctrl_usdhc1;pinctrl-1 pinctrl_usdhc1_100mhz;pinctrl-2 pinctrl_usdhc1_200mhz;/* pinctrl-3 pinctrl_hog_1; *///用来调用名为 pinctrl_hog_1 的引脚组配置。cd-gpios gpio1 19 GPIO_ACTIVE_LOW;//用来指定一个GPIO控制引脚。描述了 SD 卡的 CD 引脚使用的哪个 IO。“GPIO_ACTIVE_LOW”表示低电平有效如果改为“GPIO_ACTIVE_HIGH”就表示高电平有效。keep-power-in-suspend;enable-sdio-wakeup;vmmc-supply reg_sd1_vmmc;status okay; }; // 根据上面这些信息 SD 卡驱动程序就可以使用 GPIO1_IO19 来检测 SD 卡的 CD 信号了设备树-无pinctrl子系统 传统的配置 pin 的方式就是直接操作相应的寄存器但是这种配置 方式比较繁琐、而且容易出问题(比如 pin 功能冲突)。 在根节点“/”下创建一个名为“alphaled”的子节点 alphaled {#address-cells 1;#size-cells 1;compatible atkalpha-led;status okay;reg 0X020C406C 0X04 /* CCM_CCGR1_BASE */0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */0X0209C000 0X04 /* GPIO1_DR_BASE */0X0209C004 0X04 ; /* GPIO1_GDIR_BASE */ };设备树-pinctrl子系统 pinctrl 子系统主要工作内容如下 ①、获取设备树中 pin 信息。 ②、根据获取到的 pin 信息来设置 pin 的复用功能 ③、根据获取到的 pin 信息来设置 pin 的电气特性比如上/下拉、速度、驱动能力等。 对于我们使用者来讲只需要在设备树里面设置好某个 pin 的相关属性即可其他的初始 化工作均由 pinctrl 子系统来完成 pinctrl 子系统源码目录为 drivers/pinctrl。 // SPI设备节点 ecspi1{fsl,spi-num-chipselects 1; // 表示只有一个设备cs-gpios gpio4 9 0; // 片选信号为GPIO4_IO09pinctrl-names defaults;// SPI设备所使用的IO名字pinctrl-0 pinctrl_ecspi1;// 使用的IO对应的pinctrl节点status okay; // 状态属性flash: m25p800{ // ECSP接口上接了一个m25p80设备“0”表示 m25p80 的接到了 ECSPI 的通道 0上#address-cells 1;#size-cells 1;//属性#address-cells 和#size-cells 都为 1表示 reg 属性中起始地址占用一个字长 (cell)地址长度也占用一个字长(cell)。compatible st, m25p32; // 用于匹配设备驱动spi-max-frequency 20000000;// 设置SPI控制器的最高频率reg 0; // 设置 m25p80 这个设备所使用的 ECSPI 通道和“m25p800”后面的“0”一样。}; };当我们向Linux内核注册成功spi_driver以后就可以使用SPI核心层提供的API函数来对设备进行读写操作了。 首先在 imx6ull-alientek-emmc.dts 文件中添加 ICM20608 所使用的 IO 信息在 iomuxc 节点中添加一个新的子节点来描述 ICM20608 所使用的 SPI 引脚子节点名字为 pinctrl_ecspi3节点内容如下所示 pinctrl_ecspi3: icm20608 { fsl,pins MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0 /* CS */ MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1 /* SCLK */ MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1 /* MISO */ MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1 /* MOSI */ ; };在 imx6ull-alientek-emmc.dts 文件中并没有任何向 ecspi3 节点追加内容的代码这是因为NXP 官方的 6ULL EVK 开发板上没有连接 SPI 设备。在 imx6ull-alientek-emmc.dts 文件最后面加入如下所示内容 ecspi3 { fsl,spi-num-chipselects 1;// 表示只有一个设备 cs-gpios gpio1 20 GPIO_ACTIVE_LOW;// 一定要使用 “cs-gpios”属性来描述片选引脚 SPI 主机驱动就会控制片选引脚。 pinctrl-names default;// SPI设备所使用的IO名字 pinctrl-0 pinctrl_ecspi3;// 设置 IO 要使用的 pinctrl 子节点 status okay;spidev: icm206080 { //icm20608 设备子节点因为 icm20608 连接在 ECSPI3 的第 0 个通道上因此后面为 0。compatible alientek,icm20608; //设置节点属性兼容值spi-max-frequency 8000000;// SPI 最大时钟频率reg 0;// icm20608 连接在通道 0 上因此 reg 为 0。}; };platform驱动程序编写 #include linux/types.h #include linux/kernel.h #include linux/delay.h #include linux/ide.h #include linux/init.h #include linux/module.h #include linux/errno.h #include linux/gpio.h #include linux/cdev.h #include linux/device.h #include linux/of_gpio.h #include linux/semaphore.h #include linux/timer.h #include linux/irq.h #include linux/wait.h #include linux/poll.h #include linux/fs.h #include linux/fcnt1.n #include linux/platform_device.h #include asm/mach/map.h #include asm/uaccess.h #include asm/io.h#define LEDDEV_CNT 1 /* 设备号长度 */ #define LEDDEV_NAME dtsplatled /* 设备名字 */ #define LEDOFF 0 #define LEDON 1/*leddev设备结构体*/ struct leddev_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device;/* 设备 */int major;/* 主设备号 */struct device_node *node;/* LED 设备节点 */int led0;/* LED 灯 GPIO 标号 */ };struct leddev_dev leddev; // 实例化对象/*LED打开/关闭*/ void led0_switch(u8 sta) {if(sta LEDON)gpio_set_value(leddev.led0, 0);else if(sta LEDOFF)gpio_set_value(leddev.led0, 1); }/*打开设备*/ static int led_open(struct inode *inode, struct file *filp) {// 将设备的私有数据指针指向leddev结构体的地址。filp-private_data是文件指针的一个成员用于存储与该文件相关的私有数据。在这里将其指向leddev结构体的地址以便在后续的操作中可以通过filp-private_data访问和操作leddev结构体中的数据。filp-private_data leddev; /*设置私有数据*/return 0; }/*向设备写数据*/ // 从用户空间拷贝数据到内核空间的缓冲区databuf然后根据拷贝的数据判断LED的状态并调用相应的函数控制LED的开关状态。返回0表示写操作成功。如果拷贝数据失败则返回错误码-EFAULT。 static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)//filp表示文件指针buf表示用户空间传递的数据缓冲区指针cnt表示写入数据的字节数offt表示文件偏移量。 {int retvalue; // 用于保存copy_from_user函数的返回值。unsigned char databuf[2]; // 用于存储从用户空间拷贝的数据。unsigned char ledstat; // 用于存储LED的状态。retvalue copy_from_user(databuf, buf, cnt); // 调用copy_from_user函数将用户空间的数据拷贝到内核空间的databuf数组中。copy_from_user函数返回成功拷贝的字节数如果返回值小于0则表示拷贝失败。if(retvalue 0) {printk(kernel write failed!\r\n);return -EFAULT;}ledstat databuf[0];if (ledstat LEDON) {led0_switch(LEDON);} else if (ledstat LEDOFF) {led0_switch(LEDOFF);}return 0; }/*设备操作函数*/ static struct file_operations led_fops {.owner THIS_MODULE,.open led_open,.write led_write, }/*probe函数当驱动与设备匹配以后此函数就会执行*/ tatic int led_probe(struct platform_device *dev) { printk(led driver and device was matched!\r\n);/* 1、设置设备号 */if (leddev.major) {leddev.devid MKDEV(leddev.major, 0);register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);} else {alloc_chrdev_region(leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);leddev.major MAJOR(leddev.devid);}/* 2、注册设备 */cdev_init(leddev.cdev, led_fops);cdev_add(leddev.cdev, leddev.devid, LEDDEV_CNT);/* 3、创建类 */leddev.class class_create(THIS_MODULE, LEDDEV_NAME);if (IS_ERR(leddev.class)) {return PTR_ERR(leddev.class);}/* 4、创建设备 */leddev.device device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);if (IS_ERR(leddev.device)) {return PTR_ERR(leddev.device);}/* 5、初始化IO */ leddev.node of_find_node_by_path(/gpioled);if (leddev.node NULL){printk(gpioled node nost find!\r\n);return -EINVAL;} leddev.led0 of_get_named_gpio(leddev.node, led-gpio, 0);if (leddev.led0 0) {printk(cant get led-gpio\r\n);return -EINVAL;}gpio_request(leddev.led0, led0);gpio_direction_output(leddev.led0, 1); /* led0 IO设置为输出默认高电平 */return 0; }/*remove函数移除platform驱动的时候此函数会执行*/ static int led_remove(struct platform_device *dev) {gpio_set_value(leddev.led0, 1); /*关闭LED0*/cdev_del(leddev.cdev);/*删除cdev*/unregister_chrdev_region(leddev.devid, LEDDEV_CNT);device_destory(leddev.class, leddev.devid);class_destroy(leddev.class);retrun 0;}/*匹配列表用于匹配设备树中的设备节点和驱动程序。*/ // 通过这样的定义当系统加载该驱动程序时会根据匹配列表中的匹配项和设备树中的设备节点进行匹配如果匹配成功则会调用probe函数进行初始化操作。当设备被移除时会调用remove函数进行清理操作。 static const struct of_device_id led_of_match[] {{ .compatible atkalpha-gpioled},{ /*Sentinel*/} // 是一个空的匹配项用于标记列表的结束。 }/*platform驱动结构体*/ static struct platform_driver led_driver {.driver {.name imx6ul-led,.of_match_table led_of_match,},.probe led_probe,.remove led_remove, };/*驱动模块加载函数表示设备被插入时要执行的操作。*/ static int __init leddriver_init(void) {return platform_driver_register(led_driver); }/*驱动模块卸载函数表示设备被移除时要执行的操作。*/ static int __exit leddriver_exit(void) {platform_driver_unregister(led_driver); }module_init(leddriver_init);Input子系统 input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层最终给用户空间提供可访问的设备节点 。 input 子系统就是管理输入的子系统和 pinctrl、 gpio 子系统一样都是 Linux 内核针对某一类设备而创建的框架。 Linux 内核会使用 input_event 结构体来表示输入事件所以我们要获取按键输入信息那么必须借助于 input_event 结构体。 在按键消抖定时器处理函数中上报输入事件也就是使用 input_report_key函数上报按键事件以及按键值最后使用 input_sync 函数上报一个同步事件这一步一定得做 使用 input_allocate_device 函数申请 input_dev然后设置相应的事件以及事件码(也就是 KEY 模拟成那个按键这里我们设置为 KEY_0)。最后使用 input_register_device函数向 Linux 内核注册 input_dev。 当我们向 Linux 内核成功注册 input_dev 设备以后会在/dev/input 目录下生成一个名为“eventX(X0….n)”的文件这个/dev/input/eventX 就是对应的 input 设备文件。 在input子系统中每个事件的发生都使用事件type-子事件code-值value 所有的输入设备的主设备号都是13input-core通过次设备来将输入设备进行分类。 我们读取这个文件就可以获取到输入事件信息比如按键值什么的。使用 read函数读取输入设备文件也就是/dev/input/eventX读取到的数据按照 input_event 结构体组织起来。 #include linux/types.h #include linux/kernel.h #include linux/delay.h #include linux/ide.h #include linux/init.h #include linux/module.h #include linux/errno.h #include linux/gpio.h #include linux/cdev.h #include linux/device.h #include linux/of_gpio.h #include linux/semaphore.h #include linux/timer.h #include linux/irq.h #include linux/wait.h #include linux/poll.h #include linux/fs.h #include linux/fcnt1.n #include linux/platform_device.h #include asm/mach/map.h #include asm/uaccess.h #include asm/io.h#define KEYINPUT_CNT 1 // 设备号个数 #define KEYINPUT_NAME keyinput // 名字 #define KEY0VALUE 0X01 // KEY0按键值 #define INVAKEY 0XFF // 无效的按键值 #define KEY_NUM 1 // 按键数量// 中断IO描述结构体 struct irq_keydesc{int gpio; // gpioint irqnum; // 中断号unsigned char value; // 按键对应的键值char name[10]; // 名字irqreturn_t (*handler)(int, void *); //中断服务函数 };//keyinput设备结构体 struct keyinput_dev{dev_t devid;/* 设备号 */struct cdev cdev;/* cdev */struct class *class;/* 类 */struct device *device;/* 设备 */struct device_node *nd;/* 设备节点 */struct timer_list timer;/* 定义一个定时器 */struct irq_keydesc irqkeydesc[KEY_NUM];/* 按键描述数组 */unsigned char curkeynum;/* 当前的按键号 */struct input_dev *inputdev;/* input 结构体 */ };struct keyinput_dev keyinputdev;/* key input 设备 */// 中断服务函数 static irqreturn_t key0_handler(int irq, void *dev_id) {struct keyinput_dev *dev (struct keyinput_dev *)dev_id;dev-curkeynum 0;dev-timer.data (volatile long)dev_id;mod_timer(dev-timer, jiffies msecs_to_jiffies(10));return IRQ_RETVAL(IRQ_HANDLED); }// 定时器服务函数 /*用于按键消抖定时器到了以后,再次读取按键值如果按键还是处于按下状态就表示按键有效。*/ void timer_function(unsigned long arg) {unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct keyinput_dev *dev (struct keyinput_dev *)arg;num dev-curkeynum;keydesc dev-irqkeydesc[num];value gpio_get_value(keydesc-gpio); /* 读取 IO 值 */if(value 0){ /* 按下按键 *//* 上报按键值 *///input_event(dev-inputdev, EV_KEY, keydesc-value, 1);input_report_key(dev-inputdev, keydesc-value, 1);/*1 按下*/input_sync(dev-inputdev);} else { /* 按键松开 *///input_event(dev-inputdev, EV_KEY, keydesc-value, 0);input_report_key(dev-inputdev, keydesc-value, 0);input_sync(dev-inputdev);}}// 按键IO初始化 static int keyio_init(void) {unsigned char i 0;char name[10];int ret 0;keyinputdev.nd of_find_node_by_path(/key);if (keyinputdev.nd NULL){printk(key node not find!\r\n);return -EINVAL;}/* 提取 GPIO */for (i 0; i KEY_NUM; i) {keyinputdev.irqkeydesc[i].gpio of_get_named_gpio(keyinputdev.nd,key-gpio, i);if (keyinputdev.irqkeydesc[i].gpio 0) {printk(cant get key%d\r\n, i);}}/* 初始化 key 所使用的 IO并且设置成中断模式 */for (i 0; i KEY_NUM; i) {memset(keyinputdev.irqkeydesc[i].name, 0, sizeof(name));sprintf(keyinputdev.irqkeydesc[i].name, KEY%d, i);gpio_request(keyinputdev.irqkeydesc[i].gpio,// 驱动入口函数 static int _init keyinput_init(void) {keyio_init();return 0; }// 驱动出口函数 static void _exit keyinput_init(void) {unsigned int i 0;/* 删除定时器 */del_timer_sync(keyinputdev.timer);/* 释放中断 */for(i 0; i KEY_NUM; i){free_irq(keyinputdev.irqkeydesc[i].irqnum, keyinputdev);}/* 释放IO */for(i 0; i KEY_NUM; i){gpio_free(keyinputdev.irqkeydesc[i].gpio);}/* 释放input_dev */input_unregister_device(keyinputdev.inputdev);input_free_device(keyinputdev.inputdev); }module_init(keyinput_init); module_exit(keyinput_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(zuozhongkai);Linux系统下板子上的外设分类如下 1. Graphics图形子系统负责管理显示器、显卡和图形处理器等图形设备。 2. Storage存储子系统负责管理硬盘、固态硬盘、光驱和闪存等存储设备。 3. Network网络子系统负责管理网络接口卡、无线网卡和网络协议栈等网络设备。 4. Sound声音子系统负责管理声卡、扬声器和麦克风等音频设备。 5. USB通用串行总线子系统负责管理USB接口和相关设备。 6. PCI外设互联总线子系统负责管理PCI总线和相关设备。 7. I2CInter-Integrated Circuit子系统负责管理I2C总线和相关设备。 8. SPISerial Peripheral Interface子系统负责管理SPI总线和相关设备。 9. UART通用异步收发传输器子系统负责管理串口和相关设备。 10. GPIO通用输入输出子系统负责管理通用输入输出引脚和相关设备。 11. Watchdog看门狗子系统负责管理看门狗定时器和相关设备用于系统的自动重启。 12. Power Management电源管理子系统负责管理电源和节能功能。 13. Thermal热管理子系统负责管理温度传感器和风扇等热管理设备。 14. RTC实时时钟子系统负责管理实时时钟和相关设备。 15. Camera摄像头子系统负责管理摄像头和相关设备。 16. Touch触摸子系统负责管理触摸屏和触摸板等触摸设备。 17. MISC杂项子系统:负责管理一些不属于其他特定子系统的杂项设备。如硬件监控设备、电源管理设备、硬件随机数生成器等 18. Input输入子系统:负责管理和处理输入设备如键盘、鼠标、触摸屏等。Linux RS232/485/GPS驱动实验 在 Linux 下通常通过串口和其他设备或传感器进行通信根据电平的不同串口分为 TTL 和 RS232。 对于正点原子的 I.MX6U-ALPHA 开发板而言 RS232、 RS485 以及 GPS 模块接口通通连接到了 I.MX6U 的 UART3 接口上因此这些外设最终都归结为 UART3 的串口驱动。 I.MX6U 的 UART 驱动 NXP 已经编写好了所以不需要我们编写。 我们要做的就是在设备树中添加 UART3 对应的设备节点即可。打开 imx6ull-alientek-emmc.dts文件在此文件中只有 UART1 对应的 uart1 节点并没有 UART3 对应的节点因此我们可以参考 uart1 节点创建 uart3 节点 。 Linux PWM驱动实验 PWM 驱动就不需要我们再编写了 NXP 已经写好了在实际使用的时候只需要修改设备树即可。 GPIO1_IO04 可以作为 PWM3 的输出引脚所以我们需要在设备树里面添加 GPIO1_IO04 的引脚信息以及 PWM3 控制器对应的节点信息。 Regmap 子系统 为了提高代码复用性和驱动一致性简化驱动开发过程以及减少底层 I/O 操作次数提高访问效率。使用Regmap 。原因regmap 在驱动和硬件之间添加了 cache降低了低速 I/O 的操作次数提高了访问效率缺点是实时性会降低。 作用针对 I2C 和 SPI 设备寄存器的操作都是通过相关的 API 函数进行操作的。这样 Linux 内核中就会充斥着大量的重复、冗余代码再者代码的复用性也会降低。由于这些操作本质上都是对寄存器进行操作的因此为了方便内核开发人员统一访问 I2C/SPI 设备为此引入了 Regmap 子系统。 如果在Linux内核中直接通过相关的API函数进行I2C和SPI设备寄存器的操作会导致代码中存在大量重复和冗余的代码。这是因为每个设备的寄存器操作都需要编写相应的API函数而这些函数可能会有很多相似的部分导致代码的冗余。此外直接通过API函数进行设备寄存器操作也会降低代码的复用性。如果每个设备都有自己的API函数那么在开发其他设备驱动程序时无法复用已有的代码需要重新编写相应的API函数。这样会增加开发的工作量并且可能导致代码的一致性和可维护性的问题。为了解决这些问题引入Regmap子系统可以统一访问I2C和SPI设备的寄存器减少重复和冗余的代码并提高代码的复用性。Regmap提供了一种通用的方式来读取和写入设备寄存器可以在不同的设备驱动程序中共享和复用相同的代码提高开发效率和代码的可维护性。为了优化针对I2C和SPI设备寄存器的操作可以使用Regmap子系统来统一访问这些设备。Regmap是Linux内核中的一个子系统用于管理和访问设备寄存器。它提供了一种通用的方式来读取和写入设备寄存器减少了重复的代码并提高了代码的复用性。使用Regmap子系统的步骤如下1. 在设备驱动程序中定义一个Regmap结构体用于管理设备寄存器。可以使用regmap_init_*函数来初始化Regmap结构体根据设备的通信接口选择相应的初始化函数如regmap_init_i2c()用于I2C设备regmap_init_spi()用于SPI设备。2. 在设备驱动程序中定义一个Regmap配置结构体用于配置Regmap的参数如设备地址、寄存器地址宽度等。可以使用regmap_config_init_*函数来初始化Regmap配置结构体根据设备的通信接口选择相应的初始化函数如regmap_config_init_i2c()用于I2C设备regmap_config_init_spi()用于SPI设备。3. 在设备驱动程序中使用regmap_register_*函数将Regmap结构体和Regmap配置结构体注册到内核以便后续使用。4. 在设备驱动程序中使用regmap_read()和regmap_write()函数来读取和写入设备寄存器。这些函数会根据Regmap结构体和Regmap配置结构体来进行相应的操作简化了对设备寄存器的访问。通过使用Regmap子系统可以减少重复的代码提高代码的复用性并且统一了对I2C和SPI设备寄存器的访问方式简化了设备驱动程序的开发和维护。
文章转载自:
http://www.morning.wqtzs.cn.gov.cn.wqtzs.cn
http://www.morning.tfznk.cn.gov.cn.tfznk.cn
http://www.morning.dqwkm.cn.gov.cn.dqwkm.cn
http://www.morning.qrdkk.cn.gov.cn.qrdkk.cn
http://www.morning.mwbqk.cn.gov.cn.mwbqk.cn
http://www.morning.skcmt.cn.gov.cn.skcmt.cn
http://www.morning.sqmlw.cn.gov.cn.sqmlw.cn
http://www.morning.kwfnt.cn.gov.cn.kwfnt.cn
http://www.morning.c7507.cn.gov.cn.c7507.cn
http://www.morning.zmnyj.cn.gov.cn.zmnyj.cn
http://www.morning.chzqy.cn.gov.cn.chzqy.cn
http://www.morning.pjtnk.cn.gov.cn.pjtnk.cn
http://www.morning.wrysm.cn.gov.cn.wrysm.cn
http://www.morning.tkzrh.cn.gov.cn.tkzrh.cn
http://www.morning.hxrfb.cn.gov.cn.hxrfb.cn
http://www.morning.ktrh.cn.gov.cn.ktrh.cn
http://www.morning.wfdlz.cn.gov.cn.wfdlz.cn
http://www.morning.snlxb.cn.gov.cn.snlxb.cn
http://www.morning.wqmyh.cn.gov.cn.wqmyh.cn
http://www.morning.tlyms.cn.gov.cn.tlyms.cn
http://www.morning.fthcn.cn.gov.cn.fthcn.cn
http://www.morning.ltkzb.cn.gov.cn.ltkzb.cn
http://www.morning.grynb.cn.gov.cn.grynb.cn
http://www.morning.ccffs.cn.gov.cn.ccffs.cn
http://www.morning.mytmn.cn.gov.cn.mytmn.cn
http://www.morning.pctsq.cn.gov.cn.pctsq.cn
http://www.morning.hous-e.com.gov.cn.hous-e.com
http://www.morning.rdnkx.cn.gov.cn.rdnkx.cn
http://www.morning.yqjjn.cn.gov.cn.yqjjn.cn
http://www.morning.pylpd.cn.gov.cn.pylpd.cn
http://www.morning.tldhq.cn.gov.cn.tldhq.cn
http://www.morning.fbpyd.cn.gov.cn.fbpyd.cn
http://www.morning.dndjx.cn.gov.cn.dndjx.cn
http://www.morning.llqch.cn.gov.cn.llqch.cn
http://www.morning.kwnnx.cn.gov.cn.kwnnx.cn
http://www.morning.nfbxgtj.com.gov.cn.nfbxgtj.com
http://www.morning.lblsx.cn.gov.cn.lblsx.cn
http://www.morning.phgz.cn.gov.cn.phgz.cn
http://www.morning.nkqnn.cn.gov.cn.nkqnn.cn
http://www.morning.tbcfj.cn.gov.cn.tbcfj.cn
http://www.morning.kwyq.cn.gov.cn.kwyq.cn
http://www.morning.mbqyl.cn.gov.cn.mbqyl.cn
http://www.morning.coffeedelsol.com.gov.cn.coffeedelsol.com
http://www.morning.jzbjx.cn.gov.cn.jzbjx.cn
http://www.morning.qkdcb.cn.gov.cn.qkdcb.cn
http://www.morning.qfnrx.cn.gov.cn.qfnrx.cn
http://www.morning.dtpqw.cn.gov.cn.dtpqw.cn
http://www.morning.tgtwy.cn.gov.cn.tgtwy.cn
http://www.morning.xjnjb.cn.gov.cn.xjnjb.cn
http://www.morning.qrwnj.cn.gov.cn.qrwnj.cn
http://www.morning.cszbj.cn.gov.cn.cszbj.cn
http://www.morning.mmjyk.cn.gov.cn.mmjyk.cn
http://www.morning.fprll.cn.gov.cn.fprll.cn
http://www.morning.hhrpy.cn.gov.cn.hhrpy.cn
http://www.morning.slysg.cn.gov.cn.slysg.cn
http://www.morning.wnbpm.cn.gov.cn.wnbpm.cn
http://www.morning.qlrwf.cn.gov.cn.qlrwf.cn
http://www.morning.fydsr.cn.gov.cn.fydsr.cn
http://www.morning.mrtdq.cn.gov.cn.mrtdq.cn
http://www.morning.bplqh.cn.gov.cn.bplqh.cn
http://www.morning.xptkl.cn.gov.cn.xptkl.cn
http://www.morning.qkkmd.cn.gov.cn.qkkmd.cn
http://www.morning.yhwmg.cn.gov.cn.yhwmg.cn
http://www.morning.phechi.com.gov.cn.phechi.com
http://www.morning.ljyqn.cn.gov.cn.ljyqn.cn
http://www.morning.srndk.cn.gov.cn.srndk.cn
http://www.morning.zhffz.cn.gov.cn.zhffz.cn
http://www.morning.kbynw.cn.gov.cn.kbynw.cn
http://www.morning.lhrcr.cn.gov.cn.lhrcr.cn
http://www.morning.jmlgk.cn.gov.cn.jmlgk.cn
http://www.morning.tqxtx.cn.gov.cn.tqxtx.cn
http://www.morning.gkgb.cn.gov.cn.gkgb.cn
http://www.morning.bhrbr.cn.gov.cn.bhrbr.cn
http://www.morning.pzrrq.cn.gov.cn.pzrrq.cn
http://www.morning.ydmml.cn.gov.cn.ydmml.cn
http://www.morning.hsxkq.cn.gov.cn.hsxkq.cn
http://www.morning.qbfkz.cn.gov.cn.qbfkz.cn
http://www.morning.tllws.cn.gov.cn.tllws.cn
http://www.morning.fkwgk.cn.gov.cn.fkwgk.cn
http://www.morning.xysxj.com.gov.cn.xysxj.com
http://www.tj-hxxt.cn/news/258420.html

相关文章:

  • 黄山景区的网站做的怎么样推荐常州模板网站建设
  • 济南网站建设咨询电话正规网站建设公司一般要多少钱
  • 静安做网站wordpress跳转到子页面
  • 长春站建筑风格数据中心idc机房建设
  • wordpress 2017 主题广州seo公司排行
  • 网站如何做京东联盟网站制作方案书
  • intitle 律师网站建设的重要性临淄信息港最新招聘
  • 网站后台管理界面html网站建设具体项目及价格
  • 小语种网站网站知识介绍
  • 摄影网站大全广东省住房及建设厅官方网站
  • 红河做网站的公司医疗网站建设基本流程
  • 闽清县城乡建设局网站移动网站开发书籍
  • 商城网站建设设计介绍万能短视频素材库
  • 网站目录扫描陕西个人证书查询网
  • 济南行业网站开发网页制作基础教程免费
  • 山西建设局网站汕头免费网站制作
  • 深圳傻瓜式网站建设公司好吗做网站前端用什么技术好
  • 站点推广名词解释html做的网站怎么弄
  • 建设旅游网站数据库设计怎么做网站教程html文本文档
  • 全屏网站大小seo网站外链专发
  • 网站建设公司地址在哪华为网站的建设建议书
  • 商城网站制作的教程用wordpress建的大部
  • 画图标网站wordpress 视频 播放器插件
  • 广州黄埔网站建设网站演示网站代码
  • 网站建设的通知网站维护分工网站服务器排名
  • 个人网站搭建步骤网站建设方案及
  • 广东官网网站建设企业设计建设网站公司哪家好
  • 怎么才能把网站优化做好网站建设的客户需求分析调研表
  • 北京做网站要多少钱iis 做网站
  • sem可以为网站建设做什么营销型建设网站实训总结