之前在注册SPIDRIVER时,probe函数中,使用了
gpio_pmodoled_setup_cdev()
gpio_pmodoled_init_gpio()
gpio_pmodoled_disp_init()
我们现在来编写这些函数。

先编写gpio_pmodoled_setup_cdev()。

static int gpio_pmodoled_setup_cdev(struct gpio_pmodoled_device *dev, dev_t *dev_id, struct spi_device *spi)
{int status = 0;struct device *device;cdev_init(&dev->cdev, &gpio_pmodoled_cdev_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &gpio_pmodoled_cdev_fops;dev->spi = spi;*dev_id = MKDEV(MAJOR(gpio_pmodoled_dev_id), cur_minor++);status = cdev_add(&dev->cdev, *dev_id, 1);if(status < 0) {return status;}/* Add Device node in system */device = device_create(gpio_pmodoled_class, NULL,*dev_id, NULL,"%s", dev->name);if(IS_ERR(device)) {status = PTR_ERR(device);dev_err(&spi->dev, "failed to create device node %s, err %d\n",dev->name, status);cdev_del(&dev->cdev);}return status;
}

caller调用函数时,会传递一些参数。
dev是一个struct gpio_pmodoled_device型的指针,它执行PMOD设备描述块。
dev_id是一个dev_t型的指针,它指向一个DEVID描述块。
spi是一个struct spi_device型的指针,它指向一个SPIDEVICE设备描述块。

首先初始化CDEV描述块。
PMOD描述块中,内含一个CDEV描述块。我们要使用的,就是这个描述块。
另外,要给CDEV关联一个fops。
我么定义了一个全局变量gpio_pmodoled_cdev_fops,它是一个struct file_operations型的数据变量,不是指针。
cdev_init()负责对CDEV描述块进行初始化,并注册到内核中。

填充CDEV描述块,
cdev.owner,cdev.ops。

填充PMOD描述块,
spi。

生成DEV_ID。
MAJOR()从dev_id中抽取MAJOR代码。
MKDEV()负责将major代码和minor代码组合成dev_id。

注册CDEV。
cdev_add()负责将CDEV描述块和dev_id关联起来,并注册到内核中。返回状态值。
判断返回的状态,
如果非法,则直接返回return.
如果合法,则继续执行。

在系统中生成device_node。
device_create()负责将DEV_ID所指向的设备,和一个CLASS描述块关联起来,注册到内核中。返回一个struct device型的指针,指向一个DEVICE描述块。
将返回的指针赋值给device,这是一个struct device型的指针,指向DEVICE描述块,后续用device作为句柄。

判断device的指针合法性。
如果非法,则设置status,后面返回时需要使用。然后打印错误信息,然后从系统中注销CDEV。
如果合法,则继续执行。

至此,全部完成。直接return status。

再来编写gpio_pmodoled_init_gpio()

static int gpio_pmodoled_init_gpio(struct gpio_pmodoled_device *dev)
{struct gpio gpio_pmodoled_ctrl[] = {{dev->iVBAT, GPIOF_OUT_INIT_HIGH, "OLED VBat"},{dev->iVDD, GPIOF_OUT_INIT_HIGH, "OLED VDD"},{dev->iRES, GPIOF_OUT_INIT_HIGH, "OLED_RESET"},{dev->iDC, GPIOF_OUT_INIT_HIGH, "OLED_D/C"},};int status;int i;for (i = 0; i < ARRAY_SIZE(gpio_pmodoled_ctrl); i++) {status = gpio_is_valid(gpio_pmodoled_ctrl[i].gpio);if(!status) {dev_err(&dev->spi->dev, "!! gpio_is_valid for GPIO %d, %s FAILED!, status: %d\n",gpio_pmodoled_ctrl[i].gpio, gpio_pmodoled_ctrl[i].label, status);goto gpio_invalid;}}status = gpio_request_array(gpio_pmodoled_ctrl, ARRAY_SIZE(gpio_pmodoled_ctrl));if(status) {dev_err(&dev->spi->dev, "!!  gpio_request_array FAILED!\n");dev_err(&dev->spi->dev, "          status is: %d\n", status);gpio_free_array(gpio_pmodoled_ctrl, 4);goto gpio_invalid;}gpio_invalid:return status;
}

caller调用该函数时,会传递一些参数。
dev是一个struct gpio_pmodoled_device型的指针,指向PMOD设备描述块。

首先定义一个临时数组gpio_pmodoled_ctrl,它是一个struct gpio型变量的数组。

判断数组中的GPIO描述块,所定义的GPIO的合法性。
采用循环结构,逐个进行检查。
gpio_is_valid()负责检查GPIO描述块中给出的GPIO编号,是否有效。返回状态值。
判断返回的状态是否合法。
如果非法,打印错误信息。跳出循环体,并进入退出处理栈gpio_invalid。
如果合法,则继续执行。

循环结束后,向内核注册GPIO。
gpio_request_array()负责向内核中注册GPIO,将GPIO_ARRAY中的GPIO描述块,逐个注册到系统中。返回状态值。
判断返回的状态值,
如果非法,打印错误信息,并进入退出处理栈gpio_invalid。
如果合法,则继续执行。

至此,全部完成。直接return status。

再来编写gpio_pmodoled_disp_init()

static void gpio_pmodoled_disp_init(struct gpio_pmodoled_device *dev)
{int status;uint8_t wr_buf[20];// We are going to be sending commands// so clear the data/cmd bitgpio_set_value(dev->iDC, OLED_CONTROLLER_CMD);// Start by turning VDD on and wait for the power to come upgpio_set_value(dev->iVDD, 0);msleep(1);// Display off Commandwr_buf[0] = OLED_DISPLAY_OFF;status = spi_write(dev->spi, wr_buf, 1);/* Bring Reset Low and then High */gpio_set_value(dev->iRES, 1);msleep(1);gpio_set_value(dev->iRES, 0);msleep(1);gpio_set_value(dev->iRES, 1);// Send the set charge pump and set precharge period commandswr_buf[0] = 0x8D;wr_buf[1] = 0x14;wr_buf[2] = OLED_SET_PRECHARGE_PERIOD;wr_buf[3] = 0xF1;status = spi_write(dev->spi, wr_buf, 4);/* Turn on VCC and wait 100ms */gpio_set_value(dev->iVBAT, 0);msleep(100);/* Set Display COntrast */wr_buf[0] = OLED_CONTRAST_CTRL;wr_buf[1] = 0x0F;/* Invert the display */wr_buf[2] = OLED_SET_SEGMENT_REMAP; // Remap Columnswr_buf[3] = OLED_SET_COM_DIR;   // Remap Rows// Select sequential COM configurationwr_buf[4] = OLED_SET_COM_PINS;wr_buf[5] = 0x00;wr_buf[6] = 0xC0;wr_buf[7] = 0x20;wr_buf[8] = 0x00;// Turn on Displaywr_buf[9] = OLED_DISPLAY_ON;status = spi_write(dev->spi, wr_buf, 10);
}

caller调用该函数时,会传递一些参数。
dev是一个struct gpio_pmodoled_device型的指针,它指向一个PMOD描述块。

首先设置GPIO,把数据刷到硬件上。
gpio_set_value()负责把数据设置到指定的GPIO编号的硬件引脚上。
msleep(),睡眠等待。

然后配置wr_buf,利用SPIDEVICE,生成SPI写时序,把数据刷到硬件上。
spi_write()负责将wr_buf中的数据,逐个生成SPI写时序,刷到硬件上。

设置GPIO,生成波形时序。
gpio_set_value()和msleep()配合,可以生成需要的波形。

然后配置wr_buf,利用SPIDEVICE,生成SPI写时序,把数据刷到硬件上。
spi_write()负责将wr_buf中的数据,逐个生成SPI写时序,刷到硬件上。

设置GPIO,生成波形时序。
gpio_set_value()和msleep()配合,可以生成需要的波形。

然后配置wr_buf,利用SPIDEVICE,生成SPI写时序,把数据刷到硬件上。
spi_write()负责将wr_buf中的数据,逐个生成SPI写时序,刷到硬件上。

添加内核驱动模块(4)(mydriver.c+ Konfig+Makefile )相关推荐

  1. linux内核驱动模块开发makefile实例解析

    昨天整理了一篇关于linux内核驱动模块的开发介绍入门,其中介绍了一些关于驱动模块的基本开发步骤,不过面广而不深,很多细节都没有涉及到,其中就包括如何编写驱动模块的makefile.那么,今天我们就来 ...

  2. 《Linux内核驱动模块编程指南》

    Foreword Table of Contents 作者声明 版本和注意 感谢 译者注 作者声明 <Linux内核驱动模块编程指南>最初是由Ori Pomerantz为2.2版本的内核编 ...

  3. 超详细!手把手演示编译OpenWrt内核驱动模块

    一.前言 构建自己的内核驱动模块,相关知识可以参考OpenWrt软件编译构建系统文章:https://dongshao.blog.csdn.net/article/details/102545618. ...

  4. xmake v2.6.2 发布,新增 Linux 内核驱动模块构建支持

    Xmake 是一个基于 Lua 的轻量级跨平台构建工具. 它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时. 它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLis ...

  5. linux内核驱动模块开发步骤及实例入门介绍

    最近在搞一个linux的项目,其中主要是在编写一些应用模块,对内核及其驱动模块涉及很少,遇到了一些驱动模块的问题时,临时查了些资料,大致了解了一下驱动模块开发的基本步骤和常规步骤,并从网上也收集到了一 ...

  6. 【学习笔记】编译Linux内核(下)---KConfig、Makefile详解以及ARM平台Linux内核的编译

    本文主要介绍Linxu2.6的内核配置系统. 如果你浏览一下源代码目录,就可以发现源码目录及其子目录中有很多的KConfig文件和Makefile文件.这些文件什么作用呢?正是这些文件组成了Linux ...

  7. 编译arm linux内核,编译Linux内核(下)---KConfig、Makefile详解以及ARM平台Linux内核的编译...

    转载自:http://blog.csdn.net/newthinker_wei/article/details/8022696 本文主要介绍Linxu2.6的内核配置系统. 如果你浏览一下源代码目录, ...

  8. 无法添加内核模式驱动的打印机

    症状描述 添加共享打印机时出现错误提示:"当前打印机驱动程序与计算机上启用的某个策略不兼容",导致无法添加成功. 原因分析 因为内核模式可以访问系统底层的内存,因此写得不好的内核模 ...

  9. Arm开发板内核驱动模块--Helloworld及Makefile

    1. 在/home/sxy/目录下新建hello_1文件夹,在此文件夹下编写hello.c和Makefile文件(假设已经编写好了),make生成模块文件: PS:hello.ko就是用于需要使用的驱 ...

  10. Linux内核驱动模块示例--Helloword及Makefile

    先看一个最简单的驱动程序: //hello.c #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE ...

最新文章

  1. kill()函数 详解
  2. Android onTouchEvent, onClick及onLongClick的调用机制
  3. MULE ESB学习笔记
  4. POJ3265 Problem Solving ——动态规划——Pku3265
  5. java格式化输出双精度小数,用Java格式化双精度类型
  6. 可以无限量服用的药材
  7. SAP CRM IBASE头部字段valid from和valid to的填充逻辑
  8. WCF三种通信模式(转)
  9. 使用鸢尾花数据集实现一元逻辑回归、多分类问题
  10. 【正式发布】火星人敏捷开发手册2012-12-25(基于Scrum的敏捷开发免费培训教材及公司内部宣传材料)...
  11. 用HTML,CSS和JavaScript创建iPhone/iPad应用程序
  12. 【Flink】Flink Kafka 消费卡死 消费组卡死 topic无写入 实际有数据 topic正常
  13. UOJ#450. 【集训队作业2018】复读机 排列组合 生成函数 单位根反演
  14. html 字符串 放到webbrowser,delphi 直接将html字符串读入WebBrowser中
  15. 设为主页代码及添加到收藏夹代码大全
  16. excel的poi和EasyExcel的基本读写
  17. Linux RT-PREEMPT的softirq机制
  18. 创业者妻子发声力挺老公:合伙创业七年未分股份被踢出局
  19. 精读《图解密码技术》--第一章 环游密码世界
  20. Traffic Shifting

热门文章

  1. un-app uni.navigateTo页面跳转做封装传参
  2. 大一高数下册笔记整理_高数下册学习笔记
  3. KBQA-Bert学习记录-CRF模型
  4. [培训-无线通信基础-6]:信道编码(分组码、卷积吗、Polar码、LDPC码、Turbo码)
  5. Win7连接共享打印机时,报 0x00000bcb 错误
  6. dism /online /get-packages与dism /image:e:\ /get-packages
  7. 云服务器带外管理-从IPMI到RedFish
  8. matlab 仿真短路故障设置,基于MATLAB的电力系统故障分析及仿真
  9. HTTP和HTTPS的工作原理及区别
  10. M6(面试)-01-牛客网Java面试题集锦