DAY1-驱动框架和uboot

469 阅读7分钟

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云烟驱动蜂鸣器的代码

image-20220820145350784