NanoPi NEO Air使用一:介绍
NanoPi NEO Air使用二:固件烧录
NanoPi NEO Air使用三:OverlayFS、CPU温度和频率、wifi、蓝牙、npi-config
NanoPi NEO Air使用四:操作GPIO
NanoPi NEO Air使用五:安装Xfce和xrdp,实现远程访问
NanoPi NEO Air使用六:使用摄像头
NanoPi NEO Air使用七:获取并编译U-boot和Linux的源码
NanoPi NEO Air使用八:编写个简单的驱动和应用程序
NanoPi NEO Air使用九:使用Linux内核自带的LED驱动
NanoPi NEO Air使用十:自己编写驱动来控制LED
NanoPi NEO Air使用十一:编写SPI驱动点亮TFT屏幕,ST7789V

修改设备树

打开/home/ql/linux/H3/linux/arch/arm/boot/dts/sunxi-h3-h5.dtsi文件,发现H3的pin功能都是由pinctrl子系统控制的:

     pio: pinctrl@01c20800 {/* compatible is in per SoC .dtsi file */reg = <0x01c20800 0x400>;interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;clock-names = "apb", "hosc", "losc";gpio-controller;#gpio-cells = <3>;interrupt-controller;#interrupt-cells = <3>;csi_pins: csi {pins = "PE0", "PE1", "PE2", "PE3", "PE4","PE5", "PE6", "PE7", "PE8", "PE9","PE10", "PE11";function = "csi";};emac_rgmii_pins: emac0 {pins = "PD0", "PD1", "PD2", "PD3", "PD4","PD5", "PD7", "PD8", "PD9", "PD10","PD12", "PD13", "PD15", "PD16", "PD17";function = "emac";drive-strength = <40>;};i2s0_pins: i2s0 {pins = "PA18", "PA19", "PA20", "PA21";function = "i2s0";};i2c0_pins: i2c0 {pins = "PA11", "PA12";function = "i2c0";};i2c1_pins: i2c1 {pins = "PA18", "PA19";function = "i2c1";};i2c2_pins: i2c2 {pins = "PE12", "PE13";function = "i2c2";};mmc0_pins_a: mmc0@0 {pins = "PF0", "PF1", "PF2", "PF3","PF4", "PF5";function = "mmc0";drive-strength = <30>;bias-pull-up;};mmc0_cd_pin: mmc0_cd_pin@0 {pins = "PF6";function = "gpio_in";bias-pull-up;};mmc1_pins_a: mmc1@0 {pins = "PG0", "PG1", "PG2", "PG3","PG4", "PG5";function = "mmc1";drive-strength = <30>;bias-pull-up;};mmc2_8bit_pins: mmc2_8bit {pins = "PC5", "PC6", "PC8","PC9", "PC10", "PC11","PC12", "PC13", "PC14","PC15", "PC16";function = "mmc2";drive-strength = <30>;bias-pull-up;};spdif_tx_pins_a: spdif@0 {pins = "PA17";function = "spdif";};spi0_pins: spi0 {pins = "PC0", "PC1", "PC2", "PC3";function = "spi0";};spi1_pins: spi1 {pins = "PA15", "PA16", "PA14", "PA13";function = "spi1";};uart0_pins_a: uart0@0 {pins = "PA4", "PA5";function = "uart0";};uart1_pins: uart1 {pins = "PG6", "PG7";function = "uart1";};uart1_rts_cts_pins: uart1_rts_cts {pins = "PG8", "PG9";function = "uart1";};uart2_pins: uart2 {pins = "PA0", "PA1";function = "uart2";};uart2_rts_cts_pins: uart2_rts_cts {pins = "PA2", "PA3";function = "uart2";};uart3_pins: uart3 {pins = "PA13", "PA14";function = "uart3";};uart3_rts_cts_pins: uart3_rts_cts {pins = "PA15", "PA16";function = "uart3";};pwm0_pins: pwm0 {pins = "PA5";function = "pwm0";};};

我们要把开发板上的状态灯作为普通输出IO来使用,该pin的功能也应该在这里定义。打开开发板原理图,发现状态灯的IO为PA10。

因此回到/home/ql/linux/H3/linux/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi,添加如下内容:

&pio {leds_npi: led_pins {pins = "PA10";function = "gpio_out";};
};

但是我们发现该文件下,开发板自己已经有了:

&pio {leds_npi: led_pins {pins = "PA10";function = "gpio_out";};spi0_cs_pins: spi0_cs_pins {pins = "PC3", "PA6";function = "gpio_out";};
};

于是我们就不用再添加了。
继续打开该文件,在根节点下面把原来的leds 节点注释掉,新增testleds节点,修改后如下:

 /*leds {compatible = "gpio-leds";pinctrl-names = "default";pinctrl-0 = <&leds_npi>, <&leds_r_npi>;status {label = "status_led";gpios = <&pio 0 10 GPIO_ACTIVE_HIGH>;linux,default-trigger = "heartbeat";};pwr {label = "LED2";gpios = <&r_pio 0 10 GPIO_ACTIVE_HIGH>;default-state = "on";};};*/testleds {compatible = "test-gpio-leds";pinctrl-names = "default";pinctrl-0 = <&leds_npi>;gpios = <&pio 0 10 GPIO_ACTIVE_HIGH>;status = "okay";};

testleds节点的compatible 属性用来匹配驱动。pinctrl-0属性来指示使用的引脚和功能。

编写platform 驱动

新建02_led文件夹,在该文件夹下添加leddriver.c文件,内容为:

#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/fcntl.h>
#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     "testled"     /* 设备名字     */
#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设备 *//** @description        : LED打开/关闭* @param - sta   : LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return           : 无*/
void led0_switch(u8 sta)
{if (sta == LEDON )gpio_set_value(leddev.led0, 0);else if (sta == LEDOFF)gpio_set_value(leddev.led0, 1);
}/** @description      : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{filp->private_data = &leddev; /* 设置私有数据  */return 0;
}/** @description      : 向设备写数据 * @param - filp   : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[2];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);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,
};/** @description     : flatform驱动的probe函数,当驱动与*                     设备匹配以后此函数就会执行* @param - dev  : platform设备* @return          : 0,成功;其他负值,失败*/
static 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、获取设备节点:/testleds  */    leddev.node = of_find_node_by_path("/testleds");if (leddev.node == NULL){printk("testleds node nost find!\r\n");return -EINVAL;} /* 6、获取设备树中testleds节点的 gpios 属性,得到 LED 所使用的 GPIO 编号  */leddev.led0 = of_get_named_gpio(leddev.node, "gpios", 0);if (leddev.led0 < 0) {printk("can't get gpios\r\n");return -EINVAL;}gpio_request(leddev.led0, "led0");     /* 申请GPIO,在使用一个 GPIO 之前一定要使用 gpio_request进行申请   */gpio_direction_output(leddev.led0, 1); /* led0 IO设置为输出,默认高电平   */return 0;
}/** @description      : platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev   : platform设备* @return          : 0,成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{gpio_set_value(leddev.led0, 1);    /* 卸载驱动的时候关闭LED */cdev_del(&leddev.cdev);               /*  删除cdev */unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */device_destroy(leddev.class, leddev.devid);class_destroy(leddev.class);return 0;
}/* 匹配列表 */
static const struct of_device_id led_of_match[] = {{ .compatible = "test-gpio-leds" },{ /* Sentinel */ }
};/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver        = {.name   = "test-led",            /* 驱动名字,用于和设备匹配 */.of_match_table    = led_of_match, /* 设备树匹配表       */},.probe     = led_probe,.remove        = led_remove,
};/** @description : 驱动模块加载函数* @param         : 无* @return       : 无*/
static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);
}/** @description  : 驱动模块卸载函数* @param         : 无* @return       : 无*/
static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qlexcel");

添加应用程序

在该文件夹下添加ledApp.c文件,内容为:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define LEDOFF     0
#define LEDON   1/** @description      : main主程序* @param - argc   : argv数组元素个数* @param - argv    : 具体参数* @return            : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[2];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开led驱动 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);  /* 要执行的操作:打开或关闭 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}

添加Makefile

在该文件夹下添加Makefile文件,内容为:

KERNELDIR := /home/ql/linux/H3/linux
CURRENT_PATH := $(shell pwd)obj-m := leddriver.obuild: kernel_moduleskernel_modules:make ARCH=arm CROSS_COMPILE=arm-linux- -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:make ARCH=arm CROSS_COMPILE=arm-linux- -C $(KERNELDIR) M=$(CURRENT_PATH) clean

编译

上面3个文件添加完成后,目录结构如下:

在该目录下执行make编译驱动。

执行arm-linux-gcc ledApp.c -o ledApp编译应用程序。

执行如下命令编译设备树:

cd /home/ql/linux/H3/linux
make dtbs ARCH=arm CROSS_COMPILE=arm-linux-

测试

把设备树、驱动、应用程序传到开发板上,命令如下:

scp /home/ql/linux/H3/linux/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dtb root@192.168.0.103:/boot
scp /home/ql/linux/H3/MyDriver/02_led/leddriver.ko root@192.168.0.103:/lib/modules/4.14.111/
scp /home/ql/linux/H3/MyDriver/02_led/ledApp root@192.168.0.103:/lib/modules/4.14.111/


传好后,重启开发板。
使用命令ls /sys/bus/platform/drivers/查看系统中驱动是否正常:

使用命令ls /sys/bus/platform/devices/查看系统中设备是否正常:

使用如下命令更改控制台消息等级:

echo 5 >/proc/sys/kernel/printk
cat /proc/sys/kernel/printk

进入/lib/modules/4.14.111目录,执行insmod leddriver.ko加载驱动

当驱动和设备匹配成功以后就会执行驱动中的led_probe函数,就会输出“led driver and device was matched!”,说明驱动加载成功。

驱动和设备匹配成功以后就可以测试 LED 灯驱动了,输入如下命令打开 LED 灯:

./ledApp /dev/testled 0 //打开 LED 灯

在输入如下命令关闭 LED 灯:

./ledApp /dev/testled 1 //关闭 LED 灯

观察一下 LED 灯能否打开和关闭,如果可以的话就说明驱动工作正常,如果要卸载驱动的话输入如下命令即可:

rmmod leddriver.ko

NanoPi NEO Air使用十:自己编写驱动来控制LED相关推荐

  1. NanoPi NEO Air使用十五:移植RTL8723BU驱动

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

  2. NanoPi NEO Air使用十一:编写SPI驱动点亮TFT屏幕,ST7789V

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

  3. NanoPi NEO Air使用八:编写个简单的驱动和应用程序

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

  4. NanoPi NEO Air使用十五:使用V4L2驱动USB摄像头

    USB摄像头初识   Linux UVC driver(uvc) 该驱动适用于符合USB视频类(USB Video Class)规范的摄像头设备,它包括V4L2内核设备驱动和用户空间工具补丁.大多数大 ...

  5. NanoPi NEO Air使用十二:使用自带的fbtft驱动点亮SPI接口TFT屏幕,ST7789V

    上节自己编写spi驱动来点亮spi接口的小屏幕,其实Linux内核里已经提供spi接口小屏的设备驱动,即内核中已经自带了此类驱动,名字为fbtft.本节就来使用它. 引脚 240x240分辨率,1.3 ...

  6. NanoPi NEO Air使用十四:FrameBuffer的理解和使用

    FrameBuffer的介绍 应用程序直接通过操作显存来操作 LCD,实现在 LCD 上显示字符.图片等信息.在 Linux 中应用程序最终也是通过操作 RGB LCD 的显存来实现在 LCD 上显示 ...

  7. NanoPi NEO Air使用十六:使用python做开发

    准备工作 更改python指令默认版本 因为ubuntu是默认安装 python 2.7 和 python 3.5 双版本的,输入指令 python 进入的是 python2.7,输入python3才 ...

  8. NanoPi NEO Air使用九:使用Linux内核自带的LED驱动

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

  9. NanoPi NEO Air使用七:获取并编译U-boot和Linux的源码

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

最新文章

  1. 设计模式之中介者模式(Mediator)摘录
  2. Xcode - Other Linker Flags
  3. 创建 Monitor 并测试 - 每天5分钟玩转 OpenStack(124)
  4. 转:Google的系统工程师(SA)如何工作
  5. mysql 5.5 外键_MySQL 5.5添加外键失败,错误[HY000] [150]和[HY000] [1005]
  6. 手把手教你强化学习 (八) 强化学习中的值函数近似算法
  7. 16.进程间的通信:管道
  8. QT_布局管理器 网格布局管理器 QGridLayout 超简单
  9. 如何设置路由器wifi的用户黑名单?(使别人不能连接你家WIFI)
  10. ARCore 使用 SceneForm 框架 —— 三维空间中,通过三个点绘制平面(Plane)
  11. CIE 国际照明委员会
  12. 关于c4d默认渲染器的玻璃材质调试
  13. 关于互联网+分布式光伏运维平台的应用介绍-李亚俊
  14. opencv读取大恒相机
  15. 大华linux密码,Linux用户和组的实战练习
  16. es java api 进行聚合+桶聚合查询
  17. [Android答答答]Handler是什么?
  18. 20个令人疯狂的投资事实
  19. 开源软件之许可证(三)
  20. 算法 图中求最小环路径 最小环个数 最大平均环 求简单无向图中环的个数

热门文章

  1. oracle+rac+算法,Oracle RAC中的投票算法
  2. string转date类型_10:Wrapper;String;Date;Math;File;Enumeration;Syst
  3. android 启动模式_安卓学习笔记之Android中Activity的4种启动模式
  4. ubuntu 如何右上角显示键盘
  5. 电子科技大学计算机学院保研夏令营,电子科技大学计算机科学与工程学院网络空间安全保研夏令营...
  6. python requests cookies请求_python的requests库怎么发送带cookies的请求
  7. 大数据产品开发流程规范_大数据技术思想入门(三):分布式文件存储的流程
  8. Nature子刊 | 研究人员提出神经脆性可作为癫痫发作区(SOZ)的脑电图(EEG)标志物
  9. 类脑芯片怎么搞?三星哈佛:直接复制粘贴神经元 | Nature子刊
  10. 简单点,让论文写作简单点,老牌名校助理教授给出8个建议