linux驱动开发篇

1、编译出厂内核源码

1.1 复制linux内核源码到Ubuntu中

创建新目录

mkdir -p IMX6/linux-imx-4.1.15-2.1.0

解压linux内核源码

tar xf linux-imx-4.1.15-2.1.0-gb78e551-v1.4.tar.xz -C IMX6/linux-imx-4.1.15-2.1.0/

进入解压的目录中

cd IMX6/linux-imx-4.1.15-2.1.0

查看解压后的文件

执行编译

./build.sh

编译完成,及查看 tmp 目录下的编译目标文件,如下图,包含很多 dtb 文件(设备树),及 Linux 内核 zImage,还有 modules.tar.bz2(内核模块)。

2、NFS环境搭建

Windows 主机 IP:192.168.0.104
Ubuntu 虚拟机 IP:192.168.0.108
开发板 IP:192.168.0.106

执行以下指令设置开发板 IP,创建一个 get 目录,将虚拟机(192.168.0.108)NFS 共享目录挂载到到开发板的 get 目录中。

mkdir get
mount -t nfs -o nolock,nfsvers=3 192.168.0.105:/home/alientek/linux/nfs get

查看挂载的 NFS 目录:

df

在完成环境搭建之后,虚拟机把生成的可执行程序辅助到nfs挂载的目录下面。

运行QT程序

下次开启重启无法加载的时候,重启nfs。执行以下指令重启 NFS 服务器。

sudo /etc/init.d/nfs-kernel-server restart

执行以下指令查看 NFS 共享目录。

showmount -e


2022.5.17 P1029字符驱动开发。
驱动加载:insmod drv.ko
驱动卸载:rmmod drv.ko

2022.5.18 视频第一节

驱动就是获取外设,获取传感器数据,控制外设。数据交给应用程序。
通过系统调用/软中断陷入内核,操作内核。

字符设备

1、驱动设备表现就是/dev/下的文件。/dev/led。应用程序调用open函数打开设备,比如led。应用程序通过write函数向/dev/led写入数据,比如1表示打开,0表示关闭。如果关闭设备就是close函数。

2、编写驱动的时候,主要就是编写驱动对应open、close、write函数。字符设备驱动fileopation_struct结构体成员。

3、我的第一个Linux驱动实验。
参考此文件下的字符驱动编写。
编译linux驱动的时候,需要用到linux内核源码,因此要解压缩。编译完成之后得到zImage的.dtb设备树。需要编译后zImage和.dtb。
修改一下路径:
![在这里插入图片描述](https://img-blog.csdnimg.cn/a6e856548f3842c0bbfac31d8d2fc136.png
编译通过,终于好了。
加载,卸载驱动设备。

p1004 2022.5.18.22.41

2022.5.19 驱动视频第五节

需要向系统注册一个字符设备,需要使用函数register_chrdev,卸载驱动的时候,需要卸载到之前注册的设备。unregister_chrdev注销字符设备。

设备号
dev_t高12位为主设备号,低20位为次设备号。

举例:同是IIC设备,同中IIC设备占用不同的次设备号。
次设备号

设备号的操作函数或者宏:从dev_t\获取主设备号或者次设备号,MAJOR(dev_t),MINOR(dev_t)。也可以通过MKDEV(major,minor)获取设备号。

注册字符设备,当用了一个主设备号,剩下把次设备号全部都使用了。

查看设备号。


file_operation结构体:
### 第一个linux设备驱动

/***my first driver*
*/#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>#define   CHARDEVBASE_MAJOR  200 //主设备号#define   CHARDEVBASE_NAME "chrdevbase" //驱动名称static int chrdev_base_open(struct inode *inode, struct file *filp)
{printk("chrdev_base_open\n");return 0;}static int chrdev_base_relase(struct inode *inode, struct file *filp)
{printk("chrdev_base_relase\n");return 0;
}static ssize_t chrdev_base_read(struct file *filp, __user char *buf, size_t count,loff_t *ppos)
{printk("chrdev_base_read\n");return 0;
}static ssize_t chrdev_base_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{printk("chrdev_base_write\n");return 0;
}/*** 字符设备的操作集合
*/
static struct file_operations chrdev_base_fops = {.owner = THIS_MODULE,.open = chrdev_base_open,.release = chrdev_base_relase,.read = chrdev_base_read,.write = chrdev_base_write,
};static int __init chardevbase_init(void)
{int ret = 0;printk("权海洋,加油!\n");ret =  register_chrdev(CHARDEVBASE_MAJOR,CHARDEVBASE_NAME,&chrdev_base_fops);if(ret < 0){printk("chrdevbase init failed\n");}return 0;
}static void __exit chardevbase_exit(void)
{unregister_chrdev(CHARDEVBASE_MAJOR,CHARDEVBASE_NAME);printk("权海洋,再见!\n");
}//模块加载函数
module_init(chardevbase_init);//模块卸载
module_exit(chardevbase_exit);MODULE_LICENSE("QHY");

加载设备驱动之后,在进入/dev之后查看设备文件,并没有看到设备文件,原因是我们没有创建设备节点。

创建设备节点。
创建设备节点成功。

应用程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>int main(int argc, char **argv)
{int fd = 0;int ret = 0;char readbuf[10];char writebuf[100];fd = open(argv[1], O_RDWR);if (fd<0){perror("open error");exit(-1);}/*read*/ret =  read(fd, readbuf,10);if (ret < 0){perror("read error");}/*write*/ret = write(fd, writebuf,50);if (ret < 0){perror("write error");}else{}/*close*/close(fd);exit(0);
}

通过应用层程序操作驱动程序成功。

chardevbase虚拟设备的驱动。
要求:应用程序对驱动程序进行读写操作,读的话就是从驱动里面读取字符串,写的话就是向驱动写字符串。

应用程序不能访问内核数据!必须借助其他函数。
驱动程序:

/***my first driver*
*/
#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/init.h>#define   CHARDEVBASE_MAJOR  200 //主设备号#define   CHARDEVBASE_NAME "chrdevbase" //驱动名称static char readbuf[100]; /*读缓冲*/static char writebuf[100]; /*读缓冲*/static char kernelbuf[100] = {"kernel data"};static int chrdev_base_open(struct inode *inode, struct file *filp)
{//printk("chrdev_base_open\n");return 0;
}static int chrdev_base_relase(struct inode *inode, struct file *filp)
{//printk("chrdev_base_relase\n");return 0;
}static ssize_t chrdev_base_read(struct file *filp, __user char *buf, size_t count,loff_t *ppos)
{//printk("chrdev_base_read\n");int ret = 0;memcpy(readbuf,kernelbuf,sizeof(kernelbuf));ret = copy_to_user(buf,readbuf,count);if (ret < 0){}else{}return 0;
}static ssize_t chrdev_base_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{//printk("chrdev_base_write\n");int ret = 0;ret = copy_from_user(writebuf,buf,count);if (ret<0){printk("chrdev_base_write error");}else{printk("kernel receive:%s\r\n",writebuf);}return 0;
}/*** 字符设备的操作集合
*/
static struct file_operations chrdev_base_fops = {.owner = THIS_MODULE,.open = chrdev_base_open,.release = chrdev_base_relase,.read = chrdev_base_read,.write = chrdev_base_write,
};static int __init chardevbase_init(void)
{int ret = 0;ret =  register_chrdev(CHARDEVBASE_MAJOR,CHARDEVBASE_NAME,&chrdev_base_fops);if(ret < 0){printk("chrdevbase init failed\n");}return 0;
}static void __exit chardevbase_exit(void)
{unregister_chrdev(CHARDEVBASE_MAJOR,CHARDEVBASE_NAME);
}//模块加载函数
module_init(chardevbase_init);
//模块卸载
module_exit(chardevbase_exit);
MODULE_LICENSE("QHY");

测试应用程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>/*** 1 表示向驱动读取数据 * * 2 表示向驱动写入数据*
*/int main(int argc, char **argv)
{if (argc != 3){printf("Error Usage:\r\n");return -1;}int fd = 0;int ret = 0;char readbuf[10];char writebuf[100];static char usrdata[100] = {"qhy data!"};fd = open(argv[1], O_RDWR);if (fd<0){perror("open error");exit(-1);}if (atoi(argv[2]) == 1){/*read*/ret =  read(fd, readbuf,10);if (ret < 0){perror("read error");}else{printf("read data: %s\r\n", readbuf);}}else{}if (atoi(argv[2]) == 2){memcpy(writebuf,usrdata,sizeof(usrdata));/*write*/ret = write(fd, writebuf,50);if (ret < 0){perror("write error");}else{}}/*close*/close(fd);exit(0);
}

实验现象


2022 .5.19 完成P7

2022.5.20 P8学习之旅

裸机LED灯实验就是操作+6ULL的寄存器,Linux也可以操作寄存器。了解Linux下如何操作寄存器,Linux下不能直接对寄存器的物理地址进行读写。比如寄存器A的物理地址为0x01010101。裸机可以直接对这个物理地址进行操作,但是在linux下不行,因为linux会使能MMU

所以,在linux 下操作的都是虚拟地址,需要先得到物理地址转换成虚拟地址。
地址映射函数原型:

第一个参数物理地址的起始地址,第二个参数是要转换的字节数量。

va = ioremap(0x01010101,10);

卸载驱动的时候,需要释放掉地址映射。
卸载驱动的时候:iounmap(va);

P9手撕代码。

实践操作:
内存地址映射:

/*首先定义寄存器的物理地址*/
#define  CCM_CCGR1_BASE         (0X020C406C)
#define  SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define  SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define  GPIO1_DR_BASE          (0X0209C000)
#define  GPIO1_GDIR_BASE        (0X0209C004)/*地址映射后的虚拟地址映射*/
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

入口初始化:

 IMX6U_CCM_CCGR1     = ioremap(CCM_CCGR1_BASE,4);SW_MUX_GPIO1_IO03  = ioremap(SW_MUX_GPIO1_IO03_BASE,4);SW_PAD_GPIO1_IO03  = ioremap(SW_PAD_GPIO1_IO03_BASE,4);GPIO1_DR           = ioremap(GPIO1_DR_BASE,4);GPIO1_GDIR          = ioremap(GPIO1_GDIR_BASE,4);

通过寄存器操作对GPIO的初始化操作:

/*2、初始化*/val = readl(IMX6U_CCM_CCGR1);val &= ~(3<<26);/*清零bit26 27*/val |= (3<<26);   /*bit 26 27置1*/writel(val,IMX6U_CCM_CCGR1);writel(0x5,SW_MUX_GPIO1_IO03);//设置复用writel(0x10b0,SW_PAD_GPIO1_IO03);//设置电气属性val = readl(GPIO1_GDIR);val |= 1<<3;/*bit3 置为1 设置为输出*/writel(val,GPIO1_GDIR);val = readl(GPIO1_DR);val &= ~(1<<3);/*bit3 清0 打开led*/writel(val,GPIO1_DR);

如果使用的是心跳灯,必须关闭这个心跳灯,否则干扰实验现象。

echo none > /sys/class/leds/sys-led/trigger // 改变 LED 的触发模式

加载写完的驱动。
实验现象:

通过应用程序操作led

编译内核和应用程序。
加载驱动,发现字符设备已经存在了。
创建设备节点,并且查看设备节点。
应用程序操作led灯。

本节的C应用代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>/*** 1 表示向驱动读取数据 * * 2 表示向驱动写入数据*
*//*** ./ledApp <filename> < 0 1 > 0表示关灯 1表示开灯* ./ledApp /dev/led 1* ./ledApp /dev/led 0
*/#define LEDOFF 0#define LEDON  1int main(int argc, char **argv)
{int fd = 0;int ret = 0;unsigned char databuf[1];if (argc != 3){printf("Error Usage:\r\n");return -1;}fd = open(argv[1], O_RDWR);if (fd<0){perror("open error");exit(-1);}databuf[0] = atoi(argv[2]);ret = write(fd, databuf,sizeof(databuf));if (ret < 0){perror("write error");close(fd);exit(-1);}else{}/*close*/close(fd);exit(0);
}

本节的驱动代码:

/***my first driver*
*/
#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/init.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/fs.h>#define   LED_MAJOR  200 //主设备号#define   LED_NAME "LED" //驱动名称// static char readbuf[100]; /*读缓冲*/// static char writebuf[100]; /*读缓冲*/// static char kernelbuf[100] = {"kernel data"};/*首先定义寄存器的物理地址*/
#define  CCM_CCGR1_BASE         (0X020C406C)
#define  SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define  SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define  GPIO1_DR_BASE          (0X0209C000)
#define  GPIO1_GDIR_BASE        (0X0209C004)/*地址映射后的虚拟地址映射*/static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;#define LEDOFF 0 /*关闭*/#define LEDON  1 /*打开*/static void led_switch(u8 sta)
{u32 val = 0;if (sta == LEDON){val = readl(GPIO1_DR);val &= ~(1<<3);/*bit3 清0 打开led*/writel(val,GPIO1_DR);}else if(sta == LEDOFF){val = readl(GPIO1_DR);val |= 1<<3;/*bit3 清0 打开led*/writel(val,GPIO1_DR);}}static int led_open(struct inode *inode, struct file *filp)
{return 0;}static int led_release(struct inode *inode, struct file *filp)
{return 0;
}static ssize_t led_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{int ret = 0;unsigned char databuf[1];ret = copy_from_user(databuf,buf,count);if (ret<0){printk("kernel_write error");return -EFAULT;}led_switch(databuf[0]);return 0;
}/*** 字符设备的操作集合
*/
static const struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.release = led_release,.write = led_write,
};static int __init led_init(void)
{int ret = 0;u32 val = 0;IMX6U_CCM_CCGR1  = ioremap(CCM_CCGR1_BASE,4);SW_MUX_GPIO1_IO03  = ioremap(SW_MUX_GPIO1_IO03_BASE,4);SW_PAD_GPIO1_IO03  = ioremap(SW_PAD_GPIO1_IO03_BASE,4);GPIO1_DR           = ioremap(GPIO1_DR_BASE,4);GPIO1_GDIR          = ioremap(GPIO1_GDIR_BASE,4);/*2、初始化*/val = readl(IMX6U_CCM_CCGR1);val &= ~(3<<26);/*清零bit26 27*/val |= (3<<26);    /*bit 26 27置1*/writel(val,IMX6U_CCM_CCGR1);writel(0x5,SW_MUX_GPIO1_IO03);//设置复用writel(0x10b0,SW_PAD_GPIO1_IO03);//设置电气属性val = readl(GPIO1_GDIR);val |= 1<<3;/*bit3 置为1 设置为输出*/writel(val,GPIO1_GDIR);val = readl(GPIO1_DR);val &= ~(1<<3);/*bit3 清0 打开led*/writel(val,GPIO1_DR);//初始化led灯。/**/ret =  register_chrdev(LED_MAJOR,LED_NAME,&led_fops);if(ret < 0){printk("LED init failed\n");return -EIO;}return 0;
}static void __exit led_exit(void)
{iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);unregister_chrdev(LED_MAJOR,LED_NAME);
}//模块加载函数
module_init(led_init);//模块卸载
module_exit(led_exit);MODULE_LICENSE("QHY");

正点原子linux阿尔法开发板使用——驱动开发篇相关推荐

  1. 正点原子Linux阿尔法开发板4.3 寸多点电容触摸屏测试问题和gt9xx系列linux驱动移植

    正点原子Linux阿尔法开发板4.3 寸多点电容触摸屏测试问题和gt9xx系列linux驱动移植 正点原子官方CSDN 4.3 寸多点电容触摸屏测试问题 正点原子gt9xx系列linux驱动移植 1. ...

  2. linux utf8转gbk_「正点原子Linux连载」第四章开发环境搭建(2)

    1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南> 关注官方微信号公众号,获取更多资料:正点原子 4.5 Visual Studio Co ...

  3. 正点原子linux阿尔法开发板使用——platform平台总线模型

    Linux驱动分离与分层 目的:为了提高软件的重用,跨平台性能!!! 控制器驱动和设备驱动分离!!! 将驱动分离:主机控制器驱动和设备驱动,主机控制器驱动一般是半导体厂家写的.在linux驱动框架下编 ...

  4. 正点原子Linux阿尔法开发板4.3 寸多点电容触摸屏测试问题

    正点原子给的技术文档如上图,我按照文档上的步骤配置设备树更改驱动发现触摸没有反应,我以为是我的问题,后来发现我的显示屏驱动**IC不是GT9147.**** 附上链接 **https://downlo ...

  5. 正点原子Linux开发板 spi内核驱动 0.96寸ips屏教程

    正点原子Linux开发板 spi内核驱动 0.96寸ips屏教程 首先选择模块 设备树配置 spi驱动程序(用的spi_driver) app 最近做下底层SPI驱动来驱动IPS屏,本来想实现这种效果 ...

  6. 【正点原子Linux连载】第六十七章 Linux USB驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  7. 【正点原子Linux连载】第三十五章 Linux内核顶层Makefile详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  8. 【正点原子Linux连载】第七十一章 Linux 4G通信实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  9. 【正点原子Linux连载】第四十一章 嵌入式Linux LED驱动开发实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

最新文章

  1. Uipath控件找不到怎么办
  2. Prototype(原型)--对象创建模式
  3. 160 - 21 Cabeca
  4. s:iterator标签的使用
  5. 图片批量转换pdf文件
  6. php 单一职责原则,Laravel深入学习8 - 单一责任原则
  7. 先进技术android,React Native实战(JavaScript开发iOS和Android应用)/计算机科学先进技术译丛...
  8. list lt map gt java_利用Set 对Listlt;Maplt;String,Objectgt;gt; 中的map对象中某一个属性去重...
  9. MediaCodec 编码时间戳问题
  10. strust2 和 hibernate的整合------登录的实现
  11. Android 中 liblog 和 libcutils 的编译 trick
  12. python模块heapq之简单学习使用
  13. 什么是死亡之 Ping 攻击?
  14. 软件工程导论—需求分析
  15. unity测量模型尺寸
  16. Android踩坑记录:This view is not constrained vertically
  17. 图形界面 I: 在METATRADER 4终端中使用不同类型的程序来测试界面库 (第五章)
  18. 群晖开启root权限及修复Moments的AI人像服务
  19. 做一个商业网站要多少钱?
  20. 电脑重启后IDEA导包报错

热门文章

  1. Rockchip MPP(Media Process Platform)解码H264
  2. [合辑]最精致的python访客登记系统实例项目,微信机器人不再只当人工智障
  3. 安装KeyShot11
  4. 小白轻松学SpringBoot系列
  5. DJY系统的移植 01
  6. linux pclint配置_代码静态分析工具PC-LINT安装配置
  7. 转战C#---day1
  8. 转战前端 jQuery
  9. 法国通过法案允许保险商投资加密资产和代币
  10. Python源码剖析-SmallPathon