=============================== 说明 ===============================
本文以A5为例,举8种我们公司常用接口的极度精简的驱动程序,只宜参考,使用时请自行补全纠错逻辑和驱动框架
内容如下:
1、gpio
2、外部中断
3、leds
4、uart
5、i2c
6、spi
7、pck
8、gadget

=============================== gpio ===============================
GPIO不需要在设备树中进行额外配置,A5启动时所有引脚的默认工作模式均是GPIO。GPIO的应用层接口是/sys/class/gpio。

1.gpio应用层接口的结构
/sys/class/gpio
┣ export # 申请引脚的接口
┣ unexport # 取消申请引脚的接口
┣ pioA15 # 单个引脚设备
┃ ┣ direction # 输出/输入控制
┃ ┣ value # 电平
┃ ┗ ......
┗ ......

2.使用方法
1)设置输入
设置B22引脚为输入并读取数值,例:
cd /sys/class/gpio
echo 54 > export # gpioA口为0号bank,54 = B*32 + 22
cd pioB22
echo in > direction # 字符串 "in"
cat value # 字符 '0' 或 '1'
如果是在应用程序中使用,那么cat可以替换为read函数,echo可以替换成write函数。
2)设置输出
设置C1引脚为输出并设置电平:
cd /sys/class/gpio
echo 65 > export
cd pioC1
echo out > direction
echo 1 > value
echo 0 > value

=========================== 外部中断 & key ===========================
外部中断有两种实现方式,纯驱动方式与key方式

1.纯驱动方式
假如要添加名为btn_test的按键驱动程序,当连接pioE0的按键按下时,打印"User button pressed!\n"
1)设备树修改,添加以下节点
/ {
ahb {
apb {
pinctrl@fffff200 {
board {
/* 声明需要使用的pin脚 */
pinctrl_btn_test_gpio: btn_test_gpio {
atmel,pins =
<AT91_PIOE 0 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
};
};
};
};
};
btn_test {
/* 驱动匹配符 */
compatible = "btn_test";
status = "okay";
/* 声明所需要使用的pin脚 */
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_btn_test_gpio>;
};
};

2)驱动程序的配置,此处仅给出probe函数的实现
#define GPIO_TO_PIN(port, pin) ((port) * 32 + pin)
#define BTN_PIN (GPIO_TO_PIN(4, 0))

// interrupt function ======================================================

static irqreturn_t BtnTest_Interrupt(int irq, void *dev_id)
{
/* 打印信息 */
printk(KERN_INFO "User button pressed!\n");
/* 返回值:中断正常退出 */
return IRQ_RETVAL(IRQ_HANDLED);
}

// probe & remove ==========================================================

static int BtnTest_Probe(struct platform_device *pdev)
{
struct pinctrl *pinctrl;
int ret = 0;
/* 初始化pin脚,此处需要依赖于设备树的 pinctrl-names 属性、pinctrl-0 属性 */
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
....
/* 申请中断号以及配置中断触发条件 */
ret = request_irq(gpio_to_irq(BTN_PIN), BtnTest_Interrupt,
IRQ_TYPE_EDGE_FALLING, "user key", NULL);

return ret;
}

=============================== leds ===============================
leds驱动是gpio驱动的变体

1.应用层接口
/sys/class/leds
┣ led_1 # 单个led设备
┃ ┣ brightness # 亮度控制,可以为'0' 或 '1'
┃ ┗ ......
┗ ......

1)点亮led灯
echo 1 > /sys/class/leds/led_1/brightness

2.驱动实现
leds的驱动非常简单,它是gpio的一种变体,只需要修改设备树就可以实现了,
添加以下节点:
/ {
leds {
compatible = "gpio-leds";

/* leds 设备对象 */
led_1 {
label = "led_1"; /* 应用层接口的名称 */
gpios = <&pioA 20 GPIO_ACTIVE_HIGH>; /* ACTIVE项为HIGH,则1表示高电平 */
};

......
};
};

=============================== uart ===============================
uart是linux中最常见的接口,驱动程序十分完善,一般在应用层操作即可

例:
// 打开串口,设置波特率115200,无校验位无停止位
int OpenUart(char *path)
{
int serial = 0;
struct termios options;
bzero(&options, sizeof(options));

//打开串口的例行操作
serial = open(path, O_RDWR | O_NONBLOCK);
tcflush(serial, TCIOFLUSH);
options.c_cflag &= ~CSIZE;
options.c_cflag |= (CLOCAL | CREAD);

// 配置选项 --- 可以随便修改
cfsetispeed(&options, B115200); // 接收速率
cfsetospeed(&options, B115200); // 发送速率
options.c_cflag |= CS8; // 8数据位
options.c_cflag &= ~PARENB; // 无校验
options.c_cflag &= ~CSTOPB; // 1停止位

// 消除一切特殊的软硬件流控制
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | CRTSCTS);
options.c_oflag &= ~(OPOST);

// 设置超时范围
options.c_cc[VTIME] = 150;
options.c_cc[VMIN] = 0;

// 写入配置
tcsetattr(serial, TCSANOW, &options);
tcflush(serial, TCIOFLUSH);

return serial;
}

读写则直接使用read和write

================================ i2c ================================
断网了。。。待完善

================================ spi ================================
linux源码中有一个非常优秀的spi接口驱动程序——spidev,这是我司规定的SPI标准接口

1、修改设备树
spi在sama5d3.dtsi中的声明如下:
spi1: spi@f8008000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "atmel,at91rm9200-spi";
reg = <0xf8008000 0x100>;
interrupts = <25 IRQ_TYPE_LEVEL_HIGH 3>;
dmas = <&dma1 2 AT91_DMA_CFG_PER_ID(15)>,
<&dma1 2 AT91_DMA_CFG_PER_ID(16)>;
dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi1>;
clocks = <&spi1_clk>;
clock-names = "spi_clk";
status = "disabled";
};

那么我们自己的dts文件在继承 sama5d3.dtsi 这个文件之后,不妨作一些小调整:
spi1: spi@f8008000 {
dmas = <0>, <0>; /* 禁用DMA */
pinctrl-0 = <&pinctrl_spi1 &pinctrl_spi1_cs3>; /* 增加cs脚的复用 */

spidev@3 {
compatible = "rohm,dh2228fv"; /* 重要的compatible属性,这是从spidev.c里面抠出来的 */
spi-max-frequency = <4000000>; /* 额定速度,因设备而异 */
reg = <3>; /* cs3 pin */
spi-cpha; /* cpha与cpol属性,可选,但这个选择很重要,一定要和设备吻合!否则会导致时序异常 */
spi-cpol; /* 关于cpha和cpol的配置,请度娘“SPI的四种模式”*/
};

status = "okay";
};

2、内核调整
menuconfig里面的 User mode SPI device driver support 一定要选上,这个选项直接和spidev.c关联
选了,设备树改好,烧写系统,在/dev 里面会出现一个spidev的设备

3、使用
我们是在应用层操作这个接口的,由于我们已经在设备树中存放了足够的信息,这个例子可以变得更简单,不过还是给出完整的过程
例子如下:

1)打开spi
int OpenSpi(char *path)
{
int spidev = 0;
unsigned char mode = SPI_MODE_3; // 设置工作模式

// 打开SPI并配置
spidev = open(path, O_RDWR);
ioctl(spidev, SPI_IOC_RD_MODE, &mode); //允许读
ioctl(spidev, SPI_IOC_WR_MODE, &mode); //允许写
ioctl(spidev, SPI_IOC_WR_MAX_SPEED_HZ, 4000000); //设置额定速率

return spidev;
}

2)读写数据
spi通常要面对的是这样一种情况:先发送一个地址,然后再读取;

例子:
int SPI_ReadReg(unsigned char reg_addr, char *buff, int reg_size)
{
// 读和写拆成两截buff
struct spi_ioc_transfer spi_buffer[2];
int ret = 0;

bzero(spi_buffer, sizeof(spi_buffer));

// 第一个buffer结构体长度为1,只写,存入地址
spi_buffer[0].bits_per_word = 8;
spi_buffer[0].len = 1;
spi_buffer[0].tx_buf = &reg_addr;
spi_buffer[0].cs_change = 0;

// 第二个buffer结构体长度为读出数据的长度,只读,把buff传进去存放返回值
spi_buffer[1].bits_per_word = 8;
spi_buffer[1].len = size;
spi_buffer[1].rx_buf = buff;
spi_buffer[1].cs_change = 0;

// 执行传输命令,结果就会存放到buff里面
ret = ioctl(spidev, SPI_IOC_MESSAGE(2), spi_buffer);
return ret;
}

实际上也是可以同时读写的,即是一个结构体就能搞定很多事情,看程序怎么写了

================================ pck ================================
sama5d3里面pck指时钟输出,可以使用在多种场合,为设备提供时钟信号

1)设备树
其实大体上和外部中断是一样的,只是引脚的复用功能配置有区别,注意 AT91_PERIPH_B:

pinctrl_pck_gpio: pck_gpio {
atmel,pins = <AT91_PIOD 30 AT91_PERIPH_B AT91_PINCTRL_PULL_UP>;
};

2)驱动
启用pck,如果想要最简洁的话,那么直接在probe里面把这个频率固死就可以了(大概也不会手贱到改频率?很危险的哦~)

在引脚输出时钟,例子:
int Probe(struct platform_device *dev)
{
int clk_src = 0;
int clk_rate = 0;
struct clk *mclk = NULL;

...... // 省略获得引脚的过程

// 首先取得一个叫main的内部时钟,这是在sama5d3片内集成的
clk_src = clk_get(NULL, "main");
clk_rate = clk_get_rate(clk_src);

// 配置pck0引脚相连的时钟控制器
clk_get(NULL, "pck0"); // 获得pck0时钟
clk_set_parent(mclk, clk_src); // 将main作为pck0的输入源,前提是硬件支持,否则会失败
clk_set_rate(mclk, MCLK_RATE); // 尝试调制pck0的速率到与MCLK_RATE最接近的数值

return clk_prepare_enable(mclk); // 开启时钟
}

============================== gadget ==============================

gadget是USB的一种工作模式,可以让设备变成U盘,或者串口

主要是一些配置工作,不需要写代码

1、内核menuconfig

1)进入 Device Drivers -> USB support,选择
USB Modem (CDC ACM) support
USB Mass Storage support
USB Serial Converter support
[*] USB Generic Serial Driver
USB Gadget Support
Mass Storage Gadget
Serial Gadget (with CDC ACM and CDC OBEX support)
CDC Composite Device (ACM and mass storage) (这个是重点,要选择m)
Multifunction Composite Gadget

2)把drivers/usb下的所有.ko搬到开发板,并modprobe

3)最后modprobe的g_acm_ms.ko有点不一样:
insmod g_acm_ms.ko file=/mnt/static.img luns=1 ro=0 stall=0 removable=1 iSerialNumber=3000111 iProduct=zhdgnss iManufacturer=zhd_survey

linux常见驱动修改的更多相关文章

  1. Linux常见疑难问答

    Linux常见疑难问答 (1)按a~z顺序排列启动服务进程. #exportLC_ALL=C           #英文环境变量设置,主要用于解决乱码问题 #chkconfig –list | gre ...

  2. linux设备驱动归纳总结(九):1.platform总线的设备和驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-111745.html linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxx ...

  3. linux设备驱动归纳总结(二):模块的相关基础概念【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59415.html linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10 ...

  4. 【转】深入浅出:Linux设备驱动之字符设备驱动

    深入浅出:Linux设备驱动之字符设备驱动 一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据 ...

  5. linux 设备驱动概述

    linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer):       主要利用C库函数和 ...

  6. Linux音频驱动-ALSA概述

    概述 ALSA(Advanced Linux Sound Architecture)是linux上主流的音频结构,在没有出现ALSA架构之前,一直使用的是OSS(Open Sound System)音 ...

  7. 【Linux开发】linux设备驱动归纳总结(九):1.platform总线的设备和驱动

    linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  8. 【Linux开发】linux设备驱动归纳总结(二):模块的相关基础概念

    linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10.04 开发平台:S3C2440开发板 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  9. 【转】linux查看及修改文件权限以及相关

    linux查看及修改文件权限以及相关 查看文件权限的语句: 在终端输入: ls -l xxx.xxx (xxx.xxx是文件名) 那么就会出现相类似的信息,主要都是这些: -rw-rw-r-- 一共有 ...

随机推荐

  1. URI

    1, URI (标识.定位任何资源的字符串) 在电脑术语中,统一资源标识符(Uniform Resource Identifier,或URI)是一个用于标识某一互联网资源名称的字符串. 该种标识允许用 ...

  2. Python应用01 原始Python服务器

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 之前我的Python教程中有人留言,表示只学Python没有用,必须学会一个框架( ...

  3. doc2vec 利用gensim 生成文档向量

    利用gensim 直接生成文档向量 def gen_d2v_corpus(self, lines): with open("./data/ques2_result.txt", &q ...

  4. [Flex] Accordion系列 - Header背景图的设置

    <?xml version="1.0" encoding="utf-8"?> <!--Flex中如何通过getHeaderAt()函数以及se ...

  5. java 的数据类型

    java 的数据类型有基本类型和引用类型 java的类的关系:有继承,有依赖,有关联,聚合,组成.

  6. AOP切入点注解报错

    开始学习AOP,出现如下的错误,最后发现是JDK与aspectj,aspectjweaver版本问题造成的.之后改成最新版本,代码运行正常. 利用这些特性可以进行代码的插入,比如权限,缓存结合mecm ...

  7. 项目积累——jQuery

    初始化时为文本框赋值,聚焦后清空内容 $(function(){ $("#buyDate").val("格式:2014-01-01"); $("#bu ...

  8. 关于codeblocks调试错误

    对于出血编程者,当代码有错误时,可能大家一般都是在程序的变量操作之后输出变量的值,但是这种方法较麻烦,工作量较大,也无法很快的找出错误,因此运用编程软件调试错误就显得尤为重要,刚才写啦一个代码,运用直 ...

  9. SVN+FTP服务器搭建(一)——SVN安装配置篇

    Subversion是一个自由,开源的版本控制系统.在Subversion管理下,文件和目录可以超越时空.Subversion将文件存放在中心版本库里.这个版本库很像一个普通的文件服务器,不同的是,它 ...

  10. 百度校招面试经历及总结(已发offer)

    听说发面经可以攒rp,希望早点给我确定的offer通知,也希望看到这个面经的小伙伴能顺利拿到心仪的offer~ 职位:机器学习-数据挖掘工程师 9.15 上午11点 一面 1.介绍项目 2.考研意向, ...