一、温故知新
1、开发环境
Ubuntu的Linux操作系统(18.04 20.04 22.04)
前面的版本号是双数,后面的版本号是04
lsb_release -a 用于查看系统版本号
uname -a 查看系统位数/内核版本号
2、体系架构
APP 各种控制界面\通信的应用程序
GUI Java\Qt\MiniGUI\LVGL
本地应用程序 在本地可执行的程序
Lib 动态库\静态库(区别、制作)
文件系统 根文件系统
内核 Linux\windows
bootloader Uboot
---------------------------------------------------------
S5P6818开发板 = 核心板 + 底板
核心板 = SOC(芯片) + DDR内存(1GB) + EMMC硬盘(8GB) + PMU(电源管理芯片) + PHY(网络芯片)
SOC = CPU(ARM核心【8核】1.4GHz) + 总线(CPU通过总线访问外设) + 内存 + 硬盘 + 外设
CPU = arm-cortex-a53 + cache(高速缓存) + mmu(内存管理单元) + 中断控制器 +。。。
底板 = key beep led SD0 SD1 USB UART LCD
arm---------->三星----------->九鼎科技------->easthome---------->我们
芯片设计 芯片厂商(ODM) 代理商(OEM) 技术整理 整理好的方案
3、阶段性学习
ARM体系结构与编程
系统移植
Linux下的驱动开发
项目
4、Linux的启动顺序
先启动Uboot(bootloader)----->启动uImage/zImage(Linux内核)----->启动rootfs(根文件系统)
未分区 已分区
EMMC[Uboot uImage | rootfs]
5、烧写Uboot
对于S5P6818开发板来说,板子的驱动设备有:
第一启动设备:SD0
第二启动设备:EMMC
第三启动设备:USB
一块新板的EMMC是没有内容的,所以我们需要使用SD卡启动
1)为啥是第513个字节
主要是因为S5P6818芯片中有一块ROM,里面存储的是三星公司固化好的程序,里面的代码固定了需要从SD卡的第513个字节读取bootloader,把bootloader读到RAM中,执行bootloader的第一个阶段。
2)为啥不放到第0个字节
主要是因为SD卡的前512个字节(第一个扇区),存放的是分区表
3)如何准确地写入到第SD卡的第513个字节
【1】使用dd命令
【2】使用文件系统的命令对其进行操作,open lseek read write close
未分区 已分区
SD卡 = [512byte + 总扇区的1/100 | ---fat32---]
4)SD卡分区操作
mount
umount
fdisk
mkfs.vfat
sync
5)把Uboot烧写到SD卡中
sudo dd if=文件名 of=文件名 seek=1
二、搭建开发环境
1、查询当前环境的编译器
我们先使用gcc -v查询ubuntu操作系统中gcc编译器的架构
CISC(复杂指令集) X86-Intel
RISC(精简指令集) ARM、MIPS、PPC
我们当前使用的Ubuntu操作系统的编译器是X86架构的,但我们程序需要在ARM架构的设备上运行,所以我们不能使用gcc来编译程序,我们需要使用交叉编译工具链。
2、搭建交叉编译工具链
代码咋上位机编译,可执行程序在下位机运行
下载官网:Downloads | GNU-A Downloads – Arm Developer
【1】、获取交叉编译工具链
1)找到工具包
arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz
注意:需要在Linux操作系统中发现工具包
a、共享目录
b、把资料包拷贝到Linux系统
2)创建目录
sudo mkdir /opt/toolchains -p
注意:/opt/ 存放的是附加的软件包
3)把tar包拷贝到新建目录
sudo cp arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz /opt/toolchains
4)进入新建目录
cd /opt/toolchains
5)解压tar包
sudo tar xvf arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz
【2】、交叉编译工具链的命名
arch-core-kernel-system
arch 用于哪个目标平台
core 使用的是哪个CPU核心
kernel 所运行的内核(Linux uclinux bare<无OS>)
system 交叉编译工具链所所选择的库和目标(gun ... eabi)
【3】配置环境变量
1)查询环境变量
env | grep PATH
2)修改环境变量
vim ~/.bashrc
export PATH=$PATH:/opt/toolchains/arm-cortex_a9-eabi-4.7-eglibc-2.18/bin
保存退出
source ~/.bashrc
3)验证交叉编译器是否搭建完成
输入:arm- + tab键,观察是否可以自动补全
3、保证下位机可以ping通上位机
1)修改虚拟操作系统的IP
在虚拟操作系统中,输入命令
sudo ifconfig ens33 192.168.1.27
也可以手动进行配置,
显示应用程序-->设置--->网络--->有线--->"+"--->身份--->名称--->"arm"--->ipv4--->手动--->
2)配置虚拟机
虚拟机-->设置--->网络适配器-->桥接模式--->
在windows系统中确定与开发板连接的网卡
在虚拟机中更改网络适配器
在VMnet0中选择与window相同的网卡,应用,确定
3)配置开发板的IP
使用串口进入到开发板的Uboot的命令行(在开发板启动过程中,按回车键)
printenv / print / pr 打印Uboot中的环境变量(Uboot支持最短命令关键词匹配)
【1】Uboot环境变量分析
bootargs | 启动内核时,回去修改这个环境变量 |
bootcmd | 延时之后要执行的命令 |
bootdelay | Uboot的延时计数 |
bootfile | 内核文件 |
ethaddr | 开发板的MAC地址 |
gatewayip | 网关 |
ipaddr | 开发板的IP地址 |
newmask | 子网掩码 |
serverip | 服务器的IP地址 |
stderr | 标准错误对应的是串口 |
stdin | 标准输入对应的是串口 |
stdout | 标准输出对应的是串口 |
注意:Uboot也需要与PC机通过网络进行通信,这些IP地址类的信息内容指的是在Uboot下的信息内容,将来启动了Linux系统之后,就不一定了。
【2】设置Uboot环境变量(虚拟机作为服务器,开发板作为客户端)
设置Uboot本地IP
setenv ipaddr 192.168.1.6
设置服务器的IP
setenv serverip 192.168.1.27
设置延时计数
setenv bootdelay 5
保存环境变量
saveenv
重启
[1]按重启键复位
[2]re
4、安装tftpd服务
我们可以使用它,将我们在虚拟机编译好的程序烧录到下位机。
例如,我们可以将ubootpak.bin(Uboot编译出来的二进制文件)和uImage(Linux内核)以及rootfs(根文件系统)下载并烧录到开发板上。
1)保证虚拟机连上外网
重启网络服务
sudo service network-mnanager restart
或
sudo /etc/init.d/network-manager restart
2)安装tftpd服务
安装tftpd客户端
sudo apt install tftpd
安装tftpd服务端
sudo apt install tftpd-hpa
3)创建tftp下载目录
创建tftp下载目录
sudo mkdir /tftpboot
修改tftp下载目录权限
sudo chmod 777 /tftpboot
4)配置tftp服务器
修改配置文件
sudo vim /etc/default/tftpd-hpa
5)重启tftp服务器
sudo service tftpd-hpa restart
5、验证tftpd服务
1)将准备好的ubootpak.bin下载到下位机
cp ubootpak.bin /tftpboot
2)保证下位机可以ping通上位机
ping 192.168.1.27
3)通过tftp服务下载文件
tftp 0x48000000 ubootpak.bin
tftp:命令
0x48000000:内存地址
ubootpak.bin:下载的文件名
注意:48000000这个地址是开发板的内存地址,我们使用tftp命令将文件下载到开发板0x48000000这个地址,重启后,ubootpak.bin会丢失,另外,在Uboot中,可以不写0x前缀,默认是十六进制。
4)将ubootpak.bin烧录到EMMC(硬盘)中
update_mmc 2 2ndboot 0x48000000 0x200 0x53ba8
录入之后,re重启
update_mmc:表示更新mmc
2:是第二个分区
2ndboot:表示第二个启动设备(EMMC)
0x48000000:表示从内存的0x48000000这个地址中读
0x200:偏移地址
0x53ba8:是ubootpak.bin文件大小
三、驱动S5P6818上的GPIO
一般来说,GPIO的PIN的功能是多功能复用型管脚
操作:
1、将GPIO的管脚选择功能复用为GPIO
2、将GPIO管脚配置为输出功能
3、控制管脚输出高/低电平
S5P6818芯片上有537个管脚
GPIO的管脚有160个,五组(A、B、C、D、E),每组有32个管脚
1、先看硬件
2、开发板原理图
LED0 GPIOB26
LED1 GPIOC11
LED2 GPIOC7
LED3 GPIOC12
4栈LED灯的阳极连接了VCC,阴极连接了GPIO管脚
如果GPIO管脚输出低电平,灯亮
如果GPIO管脚输出高电平,灯灭
3、查看数据手册
GPIOB26 选择Alternate Function 1(复用功能1)
GPIOC11 选择Alternate Function 1(复用功能1)
GPIOC7 选择Alternate Function 1(复用功能1)
GPIOC12 选择Alternate Function 1(复用功能1)
配置GPIO复用选择寄存器
GPIOXALTFN0可以控制GPIO0~GPIO15
GPIOXALTFN1可以控制GPIO16~GPIO31
*(volatile unsigned int *)(0xC0010000) &= ~(3 << 24); // 清零
*(volatile unsigned int *)(0xC0010000) |= (1 << 24); // 置一
配置GPIO输出使能寄存器
*(volatile unsigned int *)(0xC001C004) |= (1 << 12); // 置一
将对应的管脚置为高低电平
*(volatile unsigned int *)(0xC001C000) &= ~(1 << 12); // 灯亮
*(volatile unsigned int *)(0xC001C000) |= (1 << 12); // 灯灭
注意:ARM中所有的特殊功能寄存器都是32bit的,我们操作寄存器需要找到要操作寄存器的真实地址,然后使用位运算操作。
寄存器的真实地址 = 基地址 + 偏移地址
--------------------------------------------------
volatile 关键字的本意是易变的,作用是告诉编译器,由其修饰的变量,每次进行编译时,都要谨慎地从内存中取出,而不是从缓存中取其备份。
---------------------------------------------------
#define GPIOCOUT *(volatile unsigned int *)0xC001C000
#define GPIOCOUTENB *(volatile unsigned int *)0xC001C004
#define GPIOCALTFN0 *(volatile unsigned int *)0xC001C020
void delay(unsigned int x);
void led_test(void)
{
//把PC12管脚选择为复用功能1
GPIOCALTFN0 &= ~(3 << 24);
GPIOCALTFN0 |= (1 << 24);
//把PC12管脚的输出功能使能
GPIOCOUTENB |= (1 << 12);
while(1)
{
GPIOCOUT &= ~(1 << 12);//把PC12管脚拉低(灯亮)
delay(0x10000000);
GPIOCOUT |= (1 << 12);//把PC12管脚拉高(灯灭)
delay(0x10000000);
}
}
void delay(unsigned int x)
{
while(x--);
}
5、 编译代码
1)编译成目标文件
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c led_test.c -o led_test.o
2)编译成可执行文件
arm-cortex_a9-linux-gnueabi-ld -nostdlib -nostartfiles -Ttext=0x48000000 -e led_test led_test.o -o led
3)编译成二进制文件
arm-cortex_a9-linux-gnueabi-objcopy -O binary led led.bin
6、下载代码
1)拷贝到tftp下载目录
cp led.bin /tftpboot
2)下位机下载代码
tftp 0x48000000 led.bin
1)下位机执行代码
go 0x48000000
四、任务
1、流水灯
2、蜂鸣器
3、按键