i.MX6驱动开发学习日志


Day 1 【了解常识】

基本概念了解
环境熟悉

1.1 MCIMX6U8DVM10AB 芯片概述

  • 32位MPU
  • 双核ARM - Cortex-A9
  • 1GHz主频
  • 芯片命名方式:

1.2 关于Linux内核的一些基础概念

  • eMMC: embedded Multi Media Card,嵌入式存储器标准规格

  • gzip压缩: Linux常见压缩工具,压缩为*.gz格式文件

  • 常见内核文件:


    • vmlinux: 编译出来的原始内核文件
    • zImage: 经过gzip压缩的vmlinux
    • bzImage: big zImage,内核较大需要用bzImage,占用内存1M以上
    • uImage: U-boot专用映像文件
    • vmlinuz: zImage/bzImage的拷贝或链接
    • initrd: initial ramdisk,引导硬件可被实际内核vmlinuz接管
  • 内核源码结构:


    • arch: 硬件架构
    • block: 块设备驱动程序I/O调度
    • crypto: 常用加密、散列、压缩、校验算法
      • crypto-【英文前缀】保密的
      • crypto-community,地下党
      • cryptography,密码学
    • Documentation: 内核各部分的注释
    • drivers: 设备驱动程序
    • fs: file system,文件系统
    • include: 头文件
    • init: 内核初始化代码
    • ipc: 进程间通信代码
    • kernel: 内核核心部分,包括进程调度、定时器
    • lib: 库文件
    • mm: memory manage,内存管理
    • net: 网络代码
    • scripts: 用于配置内核的脚本
    • security: SELinux模块
    • sound: 音频设备驱动代码
    • usr: 用于打包和压缩的cpio
      • cpio: copy input output,用来建立、还原备份档的工具程序

  • 内核组成部分:


    • 进程调度 - SCHED
    • 内存管理 - MM
    • 虚拟文件系统 - VFS
    • 网络接口 - NET
    • 进程间通信 - IPC

1.3 Linux嵌入式开发基本概念

  • 单片机简单的操作系统:
    • UCOS,FreeRTOS
    • 只具备Kernel
    • 网络、文件系统、GUI等需要移植
  • 可移植操作系统的嵌入式开发:
    • 底层驱动开发:
      • 裸机开发:
        • 不具备IDE - 安装交叉编译环境
      • Linux操作系统开发:
        • 系统移植:
          • 移植Uboot(一种bootloader代码,用于启动Linux内核)
          • 移植Linux内核
          • 移植根文件系统(rootfs),包含常用命令和文件

        • 三大驱动:
          • 字符设备驱动
          • 块设备驱动
          • 网络设备驱动
    • 应用开发
      (待)

1.4 关于交叉编译器

  • Ubuntu 自带的gcc编译器是针对x86架构的
  • 在x86架构的PC上安装的、可以编译ARM架构代码的、GCC编译器 - 交叉编译器

1.5 MfgTool系统烧入工具

  • 运行在Windows下

  • 可烧写uboot.imx,zImage,dtb,rootfs

  • 问题1: litialize operation failed, please refer to “MfgTool.log” for detailed information, error code: 4.


    • 我出现这个问题的原因是我将mfgtools解压在移动硬盘里,可能系统需要获取管理员权限才能执行脚本,将mfgtools文件夹改到其他硬盘里问题就解决了
  • 问题2: Unassigned
    尚未解决

1.6 Linux 文件系统

  • 文件系统:
    • 将二进制数据翻译为人类语言的文件格式
    • 不同的物理结构需要不同的文件系统管理


Day 2 【熟悉环境】

Xshell通信
代码编译与运行
点灯


2.1 查看原理图

  • 点灯用的是开发板P8的4个引脚

2.2 设备树文件

  • 设备树文件 imx6qdl-sabresd.dtsi
    • 路径 /home/uptech/fsl-6dl-source/kernel-3.14.28/arch/arm/boot/dts/imx6qdl-sabresd.dtsi

2.3 运行代码

  • i.MX6 开发板
    • 内部有之前编译好的执行文件驱动,可以直接使用

    • 若没有,可以通过NFS共享Ubuntu虚拟机/imx6文件夹挂载到开发板/mnt目录下


      • 在Xshell里对开发板运行指令

      • 用网线接好开发板与电脑,确保主机、虚拟机、开发板处于同一网段下

      • 通过Xshell使用命令ifconfig eth0 192.168.1.130可改变开发板的地址

      • 通过Xshell使用命令mount -t nfs 192.168.1.120:/imx6 /mnt/可将虚拟机下的 /imx6 文件夹 挂载到开发板的 /mnt/ 文件夹

    • insmod ./driver/LEDBuzzer.ko 加载驱动

    • ./LEDBuzzer 运行执行文件,效果:


Day 3 【裸机开发了解】

Cortex-A7 MPCore架构(Multi-Processor)
关于IO和GPIO
启动方式

3.1 Cortex-A处理器运行模式(9种)

模式 描述
USR 用户模式,大部分程序运行模式,【非特权模式】
FIQ Fast Interrupt rQuest, 快速中断模式
IRQ Interrupt ReQuest,一般中断模式
SVC SupVisor, 超级管理员模式,操作系统使用
MON MONitor, 监视模式,用于安全拓展模式
ABT ABorT,数据访问终止模式,用于虚拟存储、存储保护
HYP HPYe-supervisor, 超级监视,用于虚拟化拓展r
UNDef 未定义指令终止模式
SYStem 系统模式

3.2 关于 I/O

  • IOMUX Controller,IO复用控制

  • 32位软件复用控制寄存器,32-bit software mux control registers

  • IO命名: IOMUXC_SW_MUC_CTL_PAD_XX_XX, XX_XX为IO口功能,例如UART1_TX_DATA

  • IO复用功能查询: IMX6SDLRM.pdf手册Charpter4 - External Signals and Pin Multiplexing

  • IO和GPIO的差别:


    • IO是宏观的
    • GPIO是IO的一种工作方式,比如说点亮LED、按键输入等操作
  • IO参数配置:


    • 两个寄存器:


      • IOMUXC_SW_MUX_CTL_PAD_XX_XX,配置复用功能
      • IOMUXC_SW_PAD_CTL_PAD_XX_XX,配置IO参数:

        • HYS: Hysteresis comparator,施密特触发器使能,施密特触发器的迟滞现象
        • PUS: Pull Up/down Set,上下拉电阻
        • PUE: Pull Up/down Enable,上下拉/状态保持器
        • PKE: 使能/失能上下拉/状态保持器
        • ODE: 使能/使能IO开路输出
        • SPEED: IO速度
        • DSE: IO驱动能力
        • SRE: Slew Rate,压摆率
    • IO口功能配置:

  • GPIO时钟使能


    • 时钟系统Clock Control Module(CCM)
    • 门控寄存器 - CCM Clock Gating Register
      • CCM_CCGRx [0, 6]
  • GPIO使能方式:


    • 使能时钟
    • 设置复用功能
    • 设置IO参数
    • 配置GPIO参数

3.3 启动方式

BOOT_MODE 描述
00 FUSE启动
01 串行下载
10 内部BOOT模式
11 保留


Day 4 字符设备

ARM Linux驱动开发概述
字符设备驱动简介
字符设备驱动开发


4.1 ARM Linux驱动开发概述

  • 三大类驱动:`
    • 字符设备驱动:
      • GPIO
      • I2C
      • SPI 等
    • 块设备驱动:
      • 存储器设备的驱动
      • EMMC
      • NAND
      • SD
      • U盘等
    • 网络设备驱动:
      • USB WIFI

4.2 字符设备驱动简介

  • 字符设备:


    • 通过字节流进行读写操作的设备
  • Linux的应用程序如何调用驱动程序:
    在这里插入图片描述

  • Linux一切皆文件,驱动操作文件位于/dev/device_name

  • 应用程序运行在用户空间,驱动属于内核空间,用户必须系统调用才能调用内核空间实现对底层驱动的操作
    在这里插入图片描述


    • 应用程序使用的函数,在具体驱动中都有与之对应的函数
      • 应用程序调用open,驱动程序也要有open
      • 内核驱动操作函数集合include/linux/fs.h: struct file_operations
  • 常用内核驱动操作函数

操作函数 函数功能
owner 该结构体的指针
llseek 修改文件当前读写位置
read 读取设备文件
write 向设备文件写入数据
poll 查询设备是否可以进行非阻塞读写
unlocked_ioctl 提供对设备的控制功能,与应用程序ioctl对应
mmap 将设备的内存映射到进程空间(用户空间),帧缓冲设备会使用此函数,例如LCD显存
open 打开设备文件
release 释放(关闭)设备文件,与应用程序close对应
fasvnc 刷新待处理的数据,将缓冲区数据刷新到磁盘中
aio_fsync 异步刷新待处理的数据

4.3 字符设备驱动开发

  • 加载和卸载:
    • Linux驱动两种运行方式:
      • 编译进内核,开机自启
      • 编译成模块(.ko文件),insmod xxx.ko加载模块,调试阶段使用
        • 模块加载 module_init(xxxx_init)
          • insmod时调用xxxx_init
        • 模块卸载module_exit(xxxx_exit)
          • rmmod时调用xxxx_exit
// 字符设备驱动模块加载和卸载函数模板

/* 驱动入口函数 */
static int __init xxx_init(void){ 
	return 0;  
} 

/* 驱动出口函数 */
static void __exit xxx_exit(void){
	
}

/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(xxx_init);
module_exit(xxx_exit);
  • insmod 与 modprobe 指令比较


    • insmod
      • 简单
      • 但不能决定模块的依赖关系
      • dvc_1.ko 依赖于 dvc_2.ko
        • insmod dvc_2.ko
        • insmod dvc_1.ko
      • 卸载驱动insmod dvc_1.ko
    • modprobe
      • 智能化加载驱动
      • 卸载驱动modprove -r dvc_1.ko
      • 会卸载掉驱动的依赖
      • 需要防止依赖项有多个驱动在使用
  • 字符设备注册与注销


    • 注册字符设备:
      • cat/proc/devices查看已注册的设备号,不可重复
        在这里插入图片描述
      • register_chrdev(_*_),参数:
        • 主设备号
        • 设备名称
        • 操作函数合集结构体的拷贝

// 字符设备注册和注销

// 操作函数集合结构体变量 test_fops
// 尚未定义open release等成员函数
static struct file_operations test_fops; 

/* 驱动入口函数 */
static int __init xxx_init(void){ 

	int retvalue = 0; 
	
	// 注册字符设备驱动
	// 主设备号200
	// 设备名称 chrtest
	// 结构体拷贝 &test_fops
	retvalue = register_chrdev(200, "chrtest", &test_fops);
	
	if(retvalue < 0){
		// 字符设备注册失败,自行处理 
	}
	
	return 0;
}


/* 驱动出口函数 */
static void __exit xxx_exit(void){

	// 注销字符设备驱动 
	unregister_chrdev(200, "chrtest");
}

/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(xxx_init);
module_exit(xxx_exit);

  • 设备具体操作函数

  • 设备打开chrtest_open()


/* 打开设备 */
static int chrtest_open(struct inode *inode, struct file *filp){ 
	return 0; 
}
    • 从设备读取chrtest_read()
    /* 从设备读取 */
    static ssize_t chrtest_read(struct file *filp, char __user *buf, 
    size_t cnt, loff_t *offt){
    	return 0;
    }
    

    • 向设备写数据chrtest_write()
    
    
    /* 向设备写数据 */
    static ssize_t chrtest_write(struct file *filp,
    const char __user *buf,
    size_t cnt, loff_t *offt){
    	return 0;
    }
    
    • 释放设备chrtest_release()
    
    
    /* 关闭 /释放设备 */
    static int chrtest_release(struct inode *inode, struct file *filp){
    	return 0;
    }
    
    • 用以上定义好的函数来初始化结构体 struct file_operations test_fops

    static struct file_operations test_fops = {
    	.owner = THIS_MODULE, 
    	.open = chrtest_open,
    	.read = chrtest_read,
    	.write = chrtest_write,
    	.release = chrtest_release,
    };
    
    • 驱动入口 xxx_init()
    /* 驱动入口函数 */
    static int __init xxx_init(void){
    
    	int retvalue = 0;
    
    	/* 注册字符设备驱动 */
    	retvalue = register_chrdev(200, "chrtest", &test_fops);
    	
    	if(retvalue < 0){
    		/* 字符设备注册失败,自行处理 */
    	}
    	return 0;
    }
    
    
    
    • 驱动出口xxx_exit()

    /* 驱动出口函数 */
    	static void __exit xxx_exit(void){
    	/* 注销字符设备驱动 */
    	unregister_chrdev(200, "chrtest");
    }
    
    • 入口/出口指定

    	module_init(xxx_init);
    	module_exit(xxx_exit);
    
    • 添加LICENSE(必须加)
    // LICENSE 采用GPL协议
    MODULE_LICENSE('GPL');
    // 作者 不必须加
    MODULE_AUTHOR('***');
    
      • Linux设备号
        • 主设备号
          • 具体的驱动
          • 12 位 0 - 4095
        • 次设备号
          • 驱动的各个设备
          • 20位
        • 定义位置
          • include/linux/types.h
        • 分配方式
          • 静态分配
            • cat/proc/devices 查看已分配的设备号
          • 动态分配
            • int alloc_chrdev_region(dev_t _dev, unsigned baseminor, unsigned count, const char _name)
            • dev 保存32位设备号
            • baseminor alloc_chrdev_region可申请一段连续的,主设备号相同,次设备号不同的设备,此变量设置次设备号起始地址,一般为0
            • count要申请的设备号数量
            • name设备名称
            • from 要释放的设备号
            • count 从from开始,释放的设备号数量


      Day 5 设备树

        • .dts: 描述板级信息,开发板有哪些设备
        • .dtsi:描述SOC级信息,有几个CPU,主频、控制器信息
        • DTB:编译后的DTS
        • DTC:DTS编译器