资讯详情

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

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.bz(内核模块)。

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 1

int 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>
#inc

标签: 传感器1004

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台