Linux驱动课程安排:
1.驱动框架和ARM原理
Linux驱动框架 ARM体系结构和指令集 ARM裸机开发
2.Linux内核驱动
系统移植介绍
内核模块的开发和使用
字符设备驱动的实现
混杂设备驱动框架
内核中的IO内存映射
内核中中断的使用
内核中的时间流
平台设备总线驱动模型(platform)
输入子系统(input)/I2C驱动框架
块设备和网络设备
项目
一.驱动框架
1.层次结构
操作系统是资源的管理者和服务的提供者。系统中的驱动实现必须符合系统的要求,Linux也一样。
在Linux系统中访问驱动是通过设备文件(/dev目录下),访问接口也是文件系统IO的接口,文件系统IO的接口最终会调用内核中对应驱动的file_operations接口中对应的函数。在这些函数内部实现具体硬件的操作,从而实现在用户层对硬件的访问。
2.Linux的启动流程
注:系统移植就是将引导程序,内核,根文件系统,应用程序修改安装到目标硬件平台。
3.uboot
(1)引导程序(bootloader) --------- boot+loader
1.完成基础硬件(CPU,时钟,内存,flash,串口...)的初始化,将系统的软硬件环境设置到
一个合适的状态,为启动内核做准备。
2.加载操作胸痛内核到内存,跳转到系统内核代码运行。
uboot是一种引导程序,开源免费(德国DENX)。支持多种架构的CPU,支持引导多种操作系统。
GEC6818进入uboot命令行的方法:
uboot启动后会倒数,按空格键打断倒数就会进入uboot命令行,否则加载启动Linux内核
(2)uboot命令
help命令可以查看当前uboot支持的所有命令
1)print/printenv --------------- 打印当前uboot的环境变量
print 环境变量名
2)set/setenv -------------- 新建/修改环境变量
set 环境变量名 环境变量值
set 环境便利名 ----------- 删除环境变量
//设置环境变量的值修改了内存中的数据,重启后恢复
//如果希望修改后永久生效需要将环境变量表保存到emmc
3)save/saveenv ------------ 保存当前环境变量表到emmc
save
常见的环境变量
bootdelay --------------------- 启动后加载内核的延时时间
bootcmd ------------------ 加载启动内核的命令
bootcmd=ext4load mmc 2:1 0x48000000 uImage;bootm 0x48000000
bootargs ----------------- 加载内核时传递给内核的参数
bootargs=lcd=at070tn92 tp=gslx680-linux root=/dev/mmcblk0p2 rw rootfstype=ext4
ipaddr ------------- 开发板ip
serverip ----------- tftp服务器ip
netmask ------------ 子网掩码
gatewayip --------- 网关
4)fastboot ---------- USB刷机命令
5)go ----------------- 跳转到某个内存地址执行
6)ping -------------- ping网络测试命令
tftp --------------- tftp下载命令
7)boot ------------- 执行bootcmd加载内核
8)loadx/loady --------- 串口下载命令
练习:
将uboot中开发板ip改为分配给你开发板ip,tftp服务器ip改为电脑ip
(3)开发板和电脑网络连接
如果uboot启动时出现了如下的打印信息,说明该uboot不支持网络,需要重新烧写uboot。
1)进入uboot命令行
2)连接安卓线到电脑
3)在uboot命令行执行fastboot
此时开发板屏幕上会出现刷机图像
电脑会识别开发板连接到设备,一般需要安装驱动(Android 1.0设备)
安装驱动
驱动安装成功后
4)双击刷机包中的auto.bat文件等待刷机完成
5)关闭电源,拔掉USB线,重启开发板,成功驱动网卡
6)电脑和开发板介入同一个局域网(直连,同一个路由)
关闭电脑的杀毒软件和防火墙
7)配置开发板和电脑处于同一网段,server ip=电脑ip
8)在uboot命令行测试网络是否连通
(4)uboot命令通过网络传输文件到开发板
在PC上运行tftp服务器,将要传输的文件放入下载目录,在uboot命令行执行命令
二.C语言实现驱动GEC6818裸机环境下的硬件(以LED为例)
1.看原理图
在PCB上找到要操作硬件的丝印,得到对应硬件的名称信息(D7 D8 D9 D10),去原理图上搜索这些名称。
看原理图要达到两个目的
1.要操作的硬件如何工作(集合硬件说明手册) 低电平亮 高电平灭 2.要操作的硬件连接到了CPU的哪些引脚 GPIOE13 GPIOC17 GPIOC8 GPIOC7
2.查看CPU的芯片手册
输出模式
1.复用功能选择寄存器(GPIOxALTFN)必须设置为GPIO功能
2.输出使能寄存器(GPIOxOUTENB)设置为输出功能(对应位给1)
3.输出寄存器(GPIOxOUT)控制输出高/低电平
相关寄存器:
(1)寄存器地址
基地址:0xc0010000
偏移:......
(2)寄存器位描述
1)输出寄存器
对应位给1输出高电平,给0输出低电平
2)输出使能寄存器
对应位给1输出模式,给0输入模式
3)复用功能选择寄存器
对应位给00 ------ 复用功能0
01 ------ 复用功能1
10 ------ 复用功能2
11 ------ 复用功能3
复用功能映射可以从原理图/芯片手册2.3节
GPIOE13选择复用功能0
GPIOC17 C8 C7选择复用功能1
3.编写驱动代码
(1)初始化
设置复用功能位GPIO
设置为输出模式
(2)控制亮灭
设置输出寄存器
程序源码:
void delay(unsigned int ms);
void led()
{
//定义寄存器 GPIOE13
volatile unsigned int* gpioe_alt0 = (unsigned int*)0xc001e020;
volatile unsigned int* gpioe_outenb = (unsigned int*)0xc001e004;
volatile unsigned int* gpioe_out = (unsigned int*)0xc001e000;
//1.初始化
//将gpioe_alt0的26~27位清0
*gpioe_alt0 &= ~(0x3 << 26);
//将gpioe_outenb的13位置1
*gpioe_outenb |= (0x1 << 13);
//2.闪烁
while (1) {
//亮 gpioe_out的13位清0
*gpioe_out &= ~(0x1 << 13);
//延时
delay(500);
//灭 gpioe_out的13位置1
*gpioe_out |= (0x1 << 13);
//延时
delay(500);
}
}
//循环延时
void delay(unsigned int ms)
{
int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 25000; j++);
}
4.编译逻辑程序
(1)编译成ELF文件
arm-linux-gcc -nostartfiles -nostdlib -Ttext 48000000 -e led led.c -o led
//-nostartfiles -------- 不加入起始文件
//-nostdlib ------------ 不加入标准库
//-Ttext --------------- 执行代码运行地址(代码段)
//-e led --------------- 指定程序入口
(2)将ELF文件转换成二进制文件
arm-linux-objcopy -O binary led led.bin
5.将编译生成的二进制文件传送到开发板运行
tftp 48000000 led.bin
go 48000000
练习:
修改程序,将其他三盏灯驱动起来
void delay(unsigned int ms);
void led()
{
//定义寄存器 GPIOE13
volatile unsigned int *gpioe_alt0 = (unsigned int *)0xc001e020;
volatile unsigned int *gpioe_outenb = (unsigned int *)0xc001e004;
volatile unsigned int *gpioe_out = (unsigned int *)0xc001e000;
//C17 C8 C7
volatile unsigned int *gpioc_alt0 = (unsigned int *)0xc001c020;
volatile unsigned int *gpioc_alt1 = (unsigned int *)0xc001c024;
volatile unsigned int *gpioc_outenb = (unsigned int *)0xc001c004;
volatile unsigned int *gpioc_out = (unsigned int *)0xc001c000;
//1.初始化
//将gpioe_alt0的26~27位清0
*gpioe_alt0 &= ~(0x3<<26);
//将gpioc_alt0的14~17位0101
*gpioc_alt0 &= ~(0xf<<14);
*gpioc_alt0 |= (0x5<<14);
//将gpioc_alt1的2~3位01
*gpioc_alt1 &= ~(0x3<<2);
*gpioc_alt1 |= (0x1<<2);
//将gpioe_outenb的13位置1
*gpioe_outenb |= (0x1<<13);
//将gpioc_outenb的17 7 8位置1
*gpioc_outenb |= (0x1<<17)|(0x3<<7);
//2.闪烁
while(1){
//亮 gpioe_out的13位清0
*gpioe_out &= ~(0x1<<13);
//gpioc_out的17 8 7位清0
*gpioc_out &= ~((0x1<<17)|(0x3<<7));
//延时
delay(500);
//灭 gpioe_out的13位置1
*gpioe_out |= (0x1<<13);
//gpioc_out的17 8 7位置1
*gpioc_out |= (0x1<<17)|(0x3<<7);
//延时
delay(500);
}
}
//循环延时
void delay(unsigned int ms)
{
int i,j;
for(i=0;i<ms;i++)
for(j=0;j<25000;j++);
}
作业:
实现C云烟驱动蜂鸣器的代码