arm裸机编程.docx
《arm裸机编程.docx》由会员分享,可在线阅读,更多相关《arm裸机编程.docx(46页珍藏版)》请在冰豆网上搜索。
![arm裸机编程.docx](https://file1.bdocx.com/fileroot1/2023-1/23/4293bb78-9c00-4aee-9562-3cf8656cb8a0/4293bb78-9c00-4aee-9562-3cf8656cb8a01.gif)
arm裸机编程
在windows下的工具:
ARM公司:
ARM DS-5:
armV5、armV6、armV7。
Keil MDK-ARM:
arm7、arm9、M、R4。
RVDS:
RealViewDevelopmentStudio,已被DS-5取代。
在Linux下工具:
编辑器:
vi
编译器:
arm-linux-gcc
工具:
makefile
-------------------------------------------------------------------------------
CPU架构和资源:
1.CPU Core:
(cpu核心)
32KB I/D cache 1MGhz
L2 cache 512kb
NEON
2.Systemperipheral:
时钟
系统定时器
RTC:
Real-TimeClock,实时时钟
TimerwithPWM:
带有脉冲宽度调制电路控制的定时器
Watachdogtimer:
看门狗定时器
PLL:
倍频器(锁相环)
DMA:
DirectMemoryAccess,直接内存存取
ADC:
Analog-to-DigitalConverter,模拟/数字转换器。
Keypad:
键盘
TouchScreen:
触屏
3.Connectivity:
AudioIF:
(音频处理)
IIS:
InternetInformationServices,互联网信息服务
PCM:
PulseCodeModulation,脉冲编码调制
SPDIF:
Sony/PhilipsDigitalInterfaceFormat,SONY、PHILIPS数字音频接口
AC97:
AudioCodec97,AC97软声卡
StorageIF:
(扩展存储)
MMC:
MultiMediaCard,世界上最小的FlashMemory存贮卡
SD:
SecureDigitalMemoryCard,安全数码卡
ATA:
AdvancedTechnologyAttachment,硬盘接口技术,全球硬盘标准
Connectivity:
(接口)
USBHost/OTG
UART
IIC
SPI:
serialperipheralinterface
ModemIF:
modeminterface
GPIO
4.Multimedia:
(多媒体)
CameraIF:
camerainterface
MIPI(DSIM\CSIS):
MobileIndustryProcessorInterface,移动产业处理器接口
DSIM:
CSIS:
CSI:
ChannelStateInformation,信道状态信息
MFC:
MultiFormatCodec,多格式编码器
2DVG/3Dgraphicsengine:
图形处理引擎
JPEG:
ImageRotator:
NTSC:
NationalTelevisionStandardsCommittee,国家电视标准委员会
PAL:
电视广播制式
HDMI:
HighDefinitionMultimediaInterface,高清晰度多媒体接口
TVout:
VideoDAC:
TFT LCD:
5.Memoryinterface:
(自带存储)
SRAM/ROM(cpu内部存储)
OneNAND(扩展存储)
NOR(扩展存储)(没有)
NAND(SLC/MLC)(扩展存储)
LPDDR1/OneDRAM/LPDDR2/DDR2(内存,DRAM、SDRAM)
6.PowerManagement:
(电源管理)
7.MultilayerAHB/AXIBus:
(高速总线,cpu核心和外围的接口)
InternalSRAM96Kb(内部的静态的随机存储器)(二级高速缓存)(IRAM、ISRAM)
InternalROM64Kb(内部的随机存储器)(固件)(IROM)
CryptoEngines(系统安全)
237个多功能输入输出口,142个存储器口;34组通用管脚分组,2组存储器借口管脚分组。
GPIO:
控制146个GPIO中断;
控制32个外部中断;
237多功能输入输出口;
控制除了GPH0-3之外的所有管脚在睡眠模式的状态。
-------------------------------------------------------------------------------------
裸板编程
.s:
纯汇编
.S:
兼容C的汇编
编程步骤:
看电路原理图,知道硬件电路工作原理。
找到硬件对应的CPU管脚。
查看CPU手册,找到对应的管脚控制器。
编写启动程序start.S
编写头文件.h和源文件.c
编写makefile文件
烧写程序到开发板
编程用到的寄存器一般都是特殊功能寄存器SFR:
0xE000_0000——0xFFFF_FFFF(512MB)。
--------------------------
代码的重定位
程序有两个地址:
程序的当前地址:
程序在运行时所处的当前地址。
程序的链接地址:
程序运行时应该位于的地址,编译程序时可以指定链接地址。
对于裸机编程,只拷贝启动设备(sd或nand)中的前16KB代码到IRAM的BL1中,
如果代码超过16KB,需要用前16KB的代码将整个程序拷贝到DRAM中,
然后跳转到DRAM中运行程序,这就叫做重定位。
可以将不超过16KB的代码重定向到iSRAM中的其他地址,而不一定要从0xD0020010开始;
也可以将超过16KB的代码重定位到DRAM中的某个地址。
重定位到IRAM或重定位到DRAM
程序中需要进行3步操作完成重定位:
1.重定位
2.清bss
3.跳转
在makefile中指定链接地址:
-TFILE arm-linux-ld的 -T选项可以指定一个链接脚本
arm-linux-ld-Tsdram.lds-osdram.elf$^ #DRAM
链接脚本:
程序链接时的参考文件,扩展名一般为.lds,以DRAM为例:
********************
SECTIONS
{
.=0x0;
.text:
{
start.o
*(.text)
}
.data:
{
*(.data)
}
bss_start=.;
.bss:
{
*(.bss)
}
bss_end =.;
}
********************
链接脚本语法:
.=0x0;表示程序的链接地址;.表示当前地址。
后面表示各个段的内容,*表示所有文件;
start.o表示代码段需要和入口函数start.o合并。
start和end记录bss段的起始地址和结束地址,在汇编程序中调用。
------------------------------------------
第一个裸机程序——LED测试
看电路原理图,LED测试电路工作原理。
找到LED对应的CPU管脚。
查看CPU手册,找到对应的管脚控制器。
GPIO控制LED,在三星官方手册查找GPIO:
四个LED分别用的GPIO的GPJ2_0-3;
GPJ2有8个管脚GPJ2[n](n=0-7);
GPJ2有六个寄存器
GPJ2CON[0]表示GPJ2_0管脚,由[3:
0]四位控制该管脚的作用;
GPJ2CON[1]表示GPJ2_1,由[4:
7]四位控制,以此类推,共32位,控制八个管脚。
CON控制寄存器有32位:
0xE020_0280
0000=Input
0001=Output
0010=MSM_DATA[0]
0011=KP_COL[1]
0100=CF_DATA[0]
0101=MHL_D7
0110~1110=Reserved
1111=GPJ2_INT[0]
//设置GPJ2CON的bit[0:
15],配置GPJ2_0/1/2/3引脚为输出功能
ldrr1,=0xE0200280
ldrr0,=0x00001111
strr0,[r1]
GPJ2DAT[7:
0]表示八个管脚由[7:
0]八位控制;每一位控制一个管脚。
DAT数据寄存器有8位:
0xE020_0284
1:
高电平
0:
低电平
//设置GPJ2DAT的bit[0:
3],使GPJ2_0/1/2/3引脚输出低电平,LED亮
ldrr1,=0xE0200284
movr0,#0
strr0,[r1]
//设置GPJ2DAT的bit[0:
3],使GPJ2_0/1/2/3引脚输出高电平,LED灭
ldrr1,=0xE0200284
movr0,#0xf
strr0,[r1]
在c中定义和使用寄存器
#define GPJ2CON (*(volatileunsignedlong*)0xE0200280)
#define GPJ2DAT (*(volatileunsignedlong*)0xE0200284)
voidled_blink()
//LED闪烁
{
GPJ2CON=0x00001111;//配置引脚
while
(1)
{
GPJ2DAT=0;//LEDon
delay(0x100000);
GPJ2DAT=0xf;//LEDoff
delay(0x100000);
}
}
******************
汇编点亮led源程序
汇编程序:
见led_s中的start.S
******************
Makefile文件:
led.bin:
start.o
arm-linux-ld-Ttext0x0-oled.elf$^
arm-linux-objcopy-Obinaryled.elfled.bin
arm-linux-objdump-Dled.elf>led_elf.dis
gccmkv210_image.c-omkmini210
./mkmini210led.bin210.bin
%.o:
%.S
arm-linux-gcc-o$@$<-c
%.o:
%.c
arm-linux-gcc-o$@$<-cclean:
rm*.o*.elf*.bin*.dis–f
将led测试程序烧写到SD卡的write2sd脚本:
sudoddiflag=dsyncoflag=dsyncif=210.binof=/dev/sdbseek=1
执行./write2sd后210.bin文件会被烧写到sd卡的扇区1中,sd卡的起始扇区为0,一个扇区的大小为512byte,sd启动时,IROM里的固化代码是从扇区1开始拷贝代码的。
------------------------
烧写裸机程序:
1.用dd烧写到sd卡
烧写到SD卡:
sudoddiflag=dsyncoflag=dsyncif=XXX.binof=/dev/sdbseek=1
友善的板子需要用sdflasher将sd卡隔出一个无格式区,然后烧写到这个区域。
注意:
程序中要实现添加一个16byte的头。
2.用minitools直接烧写
minitools直接用usb就可以进行烧写,不需要使用串口控制。
minitools烧写程序有两种方法:
直接烧写到DRAM;
烧写到NAND;
直接烧写到DRAM中运行:
需要从已经烧写了superboot的sd卡启动,同时设置为usb传输模式;
烧写裸机程序到DRAM中的地址为0x20000000~0x3F5FFFFF的区域运行,共502MB空间;其他10mb空间用来运行superboot。
uboot来初始化内存和搬运代码。
烧写到NAND中:
需要从已经烧写了superboot的sd卡启动,同时设置为usb传输模式;
superboot会烧写到nand,用来加载裸机程序;
快速启动后,superboot会把裸机程序拷贝到DRAM的0x20000000地址处,然后跳转到给地址运行裸机程序。
当设置为从nand启动时(需要烧写裸机程序同时烧写uboot),nand中的superboot会引导裸机程序到nand中运行。
注意:
在makefile中要指定程序运行地址(在内存中运行):
arm-linux-ld-Ttext0x20000000-oled.elf$^
程序不能在nand中运行,只能在内存中运行。
-------------------------------------
手动关闭看门狗编程
看门狗用于监控cpu的运行,保证在故障干扰情况下能尽快回复正常,看门狗和定时器都能定时,而定时器不能发出复位信号,看门狗可以发出复位信号,在启动时IROM会自动关闭看门狗;这里学习手动关闭看门狗(关闭复位功能)。
看数据手册得到看门狗的寄存器信息:
看门狗定时器有四个寄存器:
WTCON:
控制寄存器;0xE2700000
WTDAT:
数据寄存器;0xE2700004
WTCNT:
计数寄存器;0xE2700008
WTCLRINT:
中断清除寄存器;0xE270000C
WTCON控制寄存器有32位:
0xE2700000
[0]:
Resetenable/disable
0:
disabletheresetfunctionofthewatchdogtimer.
1:
assertsresetsignaloftheS5PV210atwatchdogtimer-out.
//关闭看门狗
ldr r0,=0xE2700000
mov r1,#0
str r1,[r0]
-------------------------------------
控制Icache(协处理器)
cache:
在主存和cpu通用寄存器之间设置了一类高速的容量小的存储器,
把正在执行的指令地址附近的一部分指令或数据从主存调入到这类存储器,
供cpu在一段时间使用,提高程序运行速度。
这种介于主存和cpu之间的高速小容量存储器就是cache。
常见的cache包括icache和dcache。
系统刚上电时,cache中的内容是无效的,并且cache的功能是关闭的,
往cp15协处理器中的寄存器1的bit[12]写入1可以启动icache,
写入0可以停止icache。
往cp15的协处理器中的寄存器1的bit[2]写入1可以启动dcache,
写入0可以停止dcache。
dcache必须在mmu被启动后才能被启动,裸机编程一般不启动mmu。
//打开icache可提高运行速度
#ifdefCONFIG_SYS_ICACHE_OFF
//clearbit12(I)I-cache
bic r0,r0,#0x00001000
#else
//setbit12(I)I-cache
orr r0,r0,#0x00001000
#endif
mcr p15,0,r0,c1,c0,0
-------------------------------------
设置栈
代码段:
存放代码。
数据段:
存放已经初始化的全局变量和用static申明的变量。
BSS段:
存放没有初始化的变量。
堆(heap):
存放进程运行中被动态分配的内存段。
栈(stack):
用户存放程序临时创建的局部变量(不包括全局变量和static申明的变量);保护现场;函数调用时传递参数(参数个数超过四个);保存临时变量。
在汇编中设置栈,栈的地址由用户分配。
//设置栈,以便调用c函数
ldr sp,=0xD0037D80
ISRAM的内部空间的分配:
0xD003_7780——0xD003_7D80 SVC(管理模式)栈(1.5KB)
0xD003_7D80——0xD003_7F80 IRQ(中断模式)栈(512B)
-------------------------------------
蜂鸣器
1.从电路原理图得知蜂鸣器由GPD0_0管脚控制,高电平动作。
2.从cpu数据手册得知:
GPD0有四个管脚,六个寄存器:
GPD0CON:
控制寄存器:
有16位,每四位控制一个管脚,GPD0_0由bit[3:
0]控制,地址为0xE020_00A0。
0000=Input
0001=Output
0010=TOUT_X
0011~1110=Reserved
1111=GPD0_INT[X]
#defineGPD0CON (*(volatileunsignedlong*)0xE02000A0)
voidbuzzer_init(void)
{
GPD0CON|=1<<0;
}
GPD0DTA:
数据寄存器:
有4位,每一位控制一个管脚,GPD0_0由bit[0]控制,地址为0xE020_00A4。
0:
低电平
1:
高电平
#defineGPD0DAT (*(volatileunsignedlong*)0xE02000A4)
voidbuzzer_on(void)
{
GPD0DAT|=1<<0;
}
voidbuzzer_off(void)
{
GPD0DAT&=~(1<<0);
}
-------------------------------------
按键
1. 由电路原理图得知cpu提供的keypad可以通过GPH2(column)和GPH3(row)扩展出8行和8列的矩阵键盘,开发板中使用GPH2_0——GPH2_3来控制4个按键,GPH3_0——GPH3_3来控制4个按键,共八个按键。
按键为输入模式,如果按键按下,那么读入低电平,弹起时读入高电平。
2. 从数据手册得知:
GPH2和GPH3各有8个管脚,各有6个寄存器,以一个为例。
GPH2CON:
控制寄存器,地址:
0xE020_0C40
有32位,每四位控制一个管脚,bit[0:
3]控制GPH2_0管脚,其他类推。
0000=Input
0001=Output
0010=Reserved0011=KP_COL[0]
0011~1110=Reserved
1111=EXT_INT[16]
#define GPH2CON (*(volatileunsignedlong*)0xE0200C40)
#define GPH2_0_INTPUT ~(0xf<<(0*4))
#define GPH2_1_INTPUT ~(0xf<<(1*4))
#define GPH2_2_INTPUT ~(0xf<<(2*4))
#define GPH2_3_INTPUT ~(0xf<<(3*4))
GPH2CON=GPH2_0_INTPUT&GPH2_1_INTPUT&GPH2_2_INTPUT&GPH2_3_INTPUT;
GPH2DAT:
数据寄存器,地址:
0xE020_0C44
有8位,每一位控制一个管脚,bit[0]控制GPH2_0管脚。
0:
低电平
1:
高电平
#define GPH2DAT (*(volatileunsignedlong*)0xE0200C44)
//读取KEY相关的引脚值,用于判断KEY是否被按下
unsignedlongdat;
dat=GPH2DAT;
//判断KEY1:
GPH2_0
if(dat&(1<<0)) //KEY1被按下,则LED1亮,否则LED1灭
GPJ2DAT|=1<<0; //OFF
else
GPJ2DAT&=~(1<<0); //ON
-------------------------------------
串口(UART)
串行异步通信:
异步协议以字符为数据传输单位,在每个字符开始传输时对字符内的比特位进行同步,通信中,两个字符之间的间隔是不固定的,但是每个字符内的时间间隔是固定的。
字符的组成:
起始位(用0表示)+5-8个数据位+校验位+1-2个停止位(以1表示)
不进行传输时是空闲位或停止位(以1表示)。
传输过程:
由一个低电平的起始位开始,然后按收、发双方约定的频率对约定的字符比特数进行逐位接收,数据位按照低位在前,高位在后的顺序进行传输,最后以约定的算法进行错误检验,最后传送的是高电平的停止位,传输结束。
RS232采用EIA电平,开发板和计算机接口采用TTL电平,所以,串口使用了电平转换电路;RS232主要有DB25和DB9两种类型的连接器。
210提供了四个串口,9针的串口实际只用到了第2和第3两个管脚,2是RSR用来接收数据,3是RST用来发送数据。
每个串口由GPA0和GPA1复用,每个串口有自己的内部串口寄存器来控制。
1.从底板电路原理图得到串口和核心板的接口,从核心板得到串口的控制管脚:
COM0:
GPA0_0:
XuRXD0:
RSRXD0
GPA0_1:
XuTXD0:
RSTXD0
2.从数据手册得到cpu控制串口的寄存器的信息:
GPA0和GPA1都是有8个管脚。
GPA0CON:
控制寄存器,32位,每四位控制一个管脚,地址:
0xE020_0000=0x(222222)22
COM0的RSR由GPA0_0管脚控制