裸机就是没有操作系统的程序,裸机有些情况下执行效率是比较高的,但裸机也有弊端,就是它不能做大型的功能。
要想写一段代码去驱动led灯,要具备两个条件:① 代码存放在什么位置 ② 代码运行在什么位置
stm32 芯片里有个cpu自带的存储 ROM和RAM
cpu的外部 挂了两个个DDR3 512M的内存 和一个4G大小的EMMC
当我的cpu一上电 他能不能直接执行emmc里的程序?不能
要想emmc或者ddr3能使用 首先必须要先驱动起来他们
要想能执行自己的代码 就要从cpu自身的启动流程来分析
cpu是先执行自己自带的存储里的东西
通过自带的存储里的代码 然后驱动其他的外设
stm32 自带的存储 512k ROM 64k RAM
高端的芯片 exynos4412内部也集成了类似的东西
IROM 64K IRAM 256k
芯片不需要有驱动程序就能访问IRAM 和IROM里的东西
我们的led灯的裸机代码 就可以放在这个位置
第一阶段:BL0
固化在cpu内部存储里的
这个代码我们无法操作 是由三星公司写好的
初始化一些环境信息
并且下载BL1的代码
校验BL1的代码 并且执行BL1
第二个阶段:BL1
假如启动介质是sd的话
他的位置 就是在sd卡的第一个分区的 第1-16扇区
这个代码由三星公司提供 我们需要将他烧录到sd的特定位置
下载BL2并且校验 运行BL2的代码
第三个阶段:BL2
假如启动介质是sd的话
他的位置就是sd卡的第一个分区的 第17-48扇区
这个代码三星公司并没有提供
三星公司只是给了一个建议
建议我们的BL2的代码去启动os
但是我们可以不遵循他的建议 来在这个位置写我们自己的代码
运行位置 0x02023400
要想点亮led灯,首先我们要知道以下信息:
led的引脚的位置(是第几个引脚)
led灯的有效电平(有效电平是低电平)
led相关的寄存器的信息
控制寄存器(主要是用来控制引脚的工作模式)
数据寄存器(给引脚数据)
#define GPM4_CON (*((unsigned int *)0x110002E0)) //同时打开四个灯
#define GPM4_DAT (*((unsigned int *)0x110002E4))
int main()
{
//1:设置控制寄存器的工作模式
GPM4_CON &= ~(0xFFFF);//清零
GPM4_CON |= 0x1111; //设置工作模式为输出
//2:点亮led灯
GPM4_DAT &= ~(0xF); //点亮4个LED灯
return 0;
}
#define GPM4_CON (*((unsigned int *)0x110002E0)) //流水灯及蜂鸣器
#define GPM4_DAT (*((unsigned int *)0x110002E4))
#define GPD0_CON (*((unsigned int *)0x114000A0))
#define GPD0_DAT (*((unsigned int *)0x114000A4))
void mydelay(int msc);
int main()
{
while(1)
{
//1:设置控制寄存器的工作模式
GPM4_CON &= ~(0xFFFF); //LED灯清零
GPD0_CON &= ~(0xF); //蜂鸣器清零
GPM4_CON |= 0x1111; //设置LED灯工作模式为输出
GPD0_CON |= 0x1;//设置蜂鸣器工作模式为输出
//2:点亮led灯、打开蜂鸣器
GPM4_DAT &= ~(0x1); //点亮第一个灯
GPD0_DAT &= ~(0x1); //打开蜂鸣器
mydelay(50000);
GPM4_DAT |=0xF; //关闭所有的灯
GPM4_DAT &= ~(0x1<<1); //点亮第二个灯
mydelay(50000);
GPM4_DAT |=0xF; //关闭所有的灯
GPM4_DAT &= ~(0x1<<2); //点亮第三个灯
mydelay(50000);
GPD0_DAT |=0xF; //关闭蜂鸣器
GPM4_DAT |=0xF; //关闭所有的灯
GPM4_DAT &= ~(0x1<<3); //点亮第四个灯
GPM4_DAT &= ~(0xF);
mydelay(50000);
GPM4_DAT |=0xF; //关闭所有的灯
}
return 0;
}
//延时函数
void mydelay(int msc)
{
while(msc--);
}
我们所写的代码要想能运行 必须放到BL2的位置
0号扇区 保留扇区
1-16:存放的BL1的代码
17-48:存放的是BL2的代码
烧录bl1
sudo dd iflag=dsync oflag=dsync if=./E4412_N.bl1.bin of=/dev/sdb seek=1
烧录BL2
sudo dd iflag=dsync oflag=dsync if=./main of=/dev/sdb seek=17
led灯并没有像我们想象的那样被点亮
为什么
问题出在架构问题
现在所编译的代码 是在下x86主机上编译的
并不能直接的在ARM架构上去执行
要想我们的程序能够在ARM架构上正常的运行
就需要我们安装交叉编译工具
补充:本次开发使用的编译器为 arm-linux-gcc,使用的为 4.5.1 版本,通过 arm-linux-gcc -v 查看版本号
-E:执行到编译预处理就停止,生成.i 的编译预处理文件。
-S:执行到汇编就停止,生成.s 汇编文件。
-c:只编译不链接。生成.o 文件。
-o:生成目标文件,后面必须接生成的文件。 arm-linux-ld将一个.o文件链接生成可执行程序。
-Ttext 指定代码段编译地址。 arm-linux-objcopy 不同文件之间的格式转换。
bin 文件:纯净的二进制文件,没有地址标记。
hex 文件:有地址标记。Intel HEX 文件是记录文本行的 ASCII 文本文件。16 进制文件。
(1)这里我先新创建了一个目录文件,准备将交叉编译工具的压缩包放在这里
(2)将交叉编译工具的压缩包拷贝到虚拟机中新创建的一个目录中
(3)在压缩包所在目录下解压压缩包:tar xvf arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz
(4)将解压后交叉编译工具所在的位置添加到环境变量里
首先通过以下步骤获取bin文件路径,找到后将它复制一下
然后,进入/etc/profile文件,在这个文件的最后追加上刚才复制的bin文件路径,以方便我们在任意的位置都能执行这个指令
ldw@LDW:~/linux_driver/opt/FriendlyARM/toolschain/4.5.1/bin$ sudo gedit /etc/profile
之后,会出现以下警告,不用管它,重新启动虚拟机
(5)安装兼容库
sudo apt-get install lib32ncurses5-dev
sudo apt-get install lib32z1
(6)验证交叉编译工具的安装情况
在任意的位置输入指令:arm-linux-gcc -v
当出现 gcc version 4.5.1 (ctng-1.8.1-FA) 就表示交叉编译工具安装完成了
(1)只编译不链接
arm-linux-gcc -c main.c -o main.o
(2)将代码的启动位置指定为 0x02023400
arm-linux-ld -Ttext 0x02023400 -o main.elf main.o
(3)编译成二进制的文件
arm-linux-objcopy -O binary main.elf main.bin
(4)添加偶校验的工具:三星要求BL2后边必须加上偶校验,偶校验的源码三星已经提供
gcc V310-EVT1-mkbl2.c -o mkbl2
(5)将代码加上偶校验
./mkbl2 main.bin bl2.bin 14336
(6)将BL1、BL2代码烧录到sd卡的17扇区
首先,把BL1的bin文件拷贝到虚拟机中
烧录BL1:sudo dd iflag=dsync oflag=dsync if=./E4412_N.bl1.bin of=/dev/sdb seek=1
烧录BL2:sudo dd iflag=dsync oflag=dsync if=./bl2.bin of=/dev/sdb seek=17