arm裸机编程

来源:互联网 发布:ubuntu 16.04安装jdk 编辑:程序博客网 时间:2024/05/01 23:07
裸机编程(汇编+C):
 
在windows下的工具:
ARM公司:
ARM  DS-5:armV5、armV6、armV7。
Keil  MDK-ARM:arm7、arm9、M、R4。
RVDS:RealView Development Studio,已被DS-5取代。
 
在Linux下工具:
编辑器:vi
编译器:arm-linux-gcc
工具:makefile
-------------------------------------------------------------------------------
CPU架构和资源:
 
1.CPU  Core:(cpu核心)
32KB  I/D  cache  1MGhz
L2  cache  512kb
NEON
 
2.System peripheral:
时钟
系统定时器
RTC:Real-Time Clock,实时时钟
Timer with PWM:带有脉冲宽度调制电路控制的定时器
Watachdog timer:看门狗定时器
PLL:倍频器(锁相环)
DMA:Direct Memory Access,直接内存存取
ADC:Analog-to-Digital Converter,模拟/数字转换器。
Keypad:键盘
Touch Screen:触屏
 
3.Connectivity:
 
Audio IF:(音频处理)
IIS:Internet Information Services,互联网信息服务
PCM:Pulse Code Modulation,脉冲编码调制
SPDIF:Sony/Philips Digital Interface Format,SONY、PHILIPS数字音频接口
AC97:Audio Codec97 ,AC97软声卡
 
Storage IF:(扩展存储)
MMC:MultiMedia Card,世界上最小的Flash Memory存贮卡
SD:Secure Digital Memory Card,安全数码卡
ATA:Advanced Technology Attachment,硬盘接口技术,全球硬盘标准
 
Connectivity:(接口)
USB Host/OTG
UART
IIC
SPI:serial peripheral interface
Modem IF:modem interface
GPIO
 
4.Multimedia:(多媒体)
Camera IF:camera interface
MIPI(DSIM\CSIS) : Mobile Industry Processor Interface, 移动产业处理器接口
DSIM:
CSIS:
CSI:Channel State Information ,信道状态信息
MFC: Multi Format Codec, 多格式编码器
2D VG/3D graphics engine:图形处理引擎
JPEG:
Image Rotator:
NTSC:National Television Standards Committee,国家电视标准委员会
PAL:电视广播制式
HDMI:High Definition Multimedia Interface,高清晰度多媒体接口
TV out:
Video DAC:
TFT  LCD:
 
5.Memory interface:(自带存储)
SRAM/ROM(cpu内部存储)
OneNAND(扩展存储)
NOR(扩展存储)(没有)
NAND(SLC/MLC) (扩展存储)
LPDDR1/OneDRAM/LPDDR2/DDR2(内存,DRAM、SDRAM)
 
6.Power Management:(电源管理)
 
7.Multi layer AHB/AXI Bus:(高速总线,cpu核心和外围的接口)
Internal SRAM 96Kb(内部的静态的随机存储器)(二级高速缓存)(IRAM、ISRAM)
Internal ROM 64Kb (内部的随机存储器)(固件)(IROM)
Crypto Engines(系统安全)
 
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中指定链接地址:
-T FILE   arm-linux-ld 的       -T选项 可以指定一个链接脚本
arm-linux-ld -Tsdram.lds -o sdram.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引脚为输出功能
    ldr r1, =0xE0200280
    ldr r0, =0x00001111
    str r0, [r1]
 
GPJ2DAT[7:0]表示八个管脚由[7:0]八位控制;每一位控制一个管脚。
DAT数据寄存器有8位:0xE020_0284
1:高电平
0:低电平
 
// 设置GPJ2DAT的bit[0:3],使GPJ2_0/1/2/3引脚输出低电平,LED亮
     ldr r1, =0xE0200284
     mov r0, #0
     str r0, [r1]
 
// 设置GPJ2DAT的bit[0:3],使GPJ2_0/1/2/3引脚输出高电平,LED灭
     ldr r1, =0xE0200284
     mov r0, #0xf
     str r0, [r1]
 
在c中定义和使用寄存器
 
#define      GPJ2CON      (*(volatile unsigned long *) 0xE0200280)
#define      GPJ2DAT       (*(volatile unsigned long *) 0xE0200284)
 
void led_blink()
// LED闪烁
{
     GPJ2CON = 0x00001111;// 配置引脚
     while(1)
     {
          GPJ2DAT = 0;// LED on
          delay(0x100000);
          GPJ2DAT = 0xf;// LED off
          delay(0x100000);
     }
}
 
******************
汇编点亮led源程序
汇编程序:
见led_s中的start.S
******************
 
Makefile文件:
led.bin: start.o
arm-linux-ld -Ttext 0x0 -o led.elf $^
arm-linux-objcopy -O binary led.elf led.bin
arm-linux-objdump -D led.elf > led_elf.dis
gcc mkv210_image.c -o mkmini210
./mkmini210 led.bin 210.bin
%.o : %.S
arm-linux-gcc -o $@ $< -c
%.o : %.c
arm-linux-gcc -o $@ $< -c clean: rm *.o *.elf *.bin *.dis –f
 
将led测试程序烧写到SD卡的write2sd脚本:
sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1
执行./write2sd后210.bin文件会被烧写到sd卡的扇区1中,sd卡的起始扇区为0,一个扇区的大小为512byte,sd启动时,IROM里的固化代码是从扇区1开始拷贝代码的。
 
------------------------
烧写裸机程序:
 
1.用dd烧写到sd卡
烧写到SD卡:
sudo dd iflag=dsync oflag=dsync if=XXX.bin of=/dev/sdb seek=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 -Ttext 0x20000000 -o led.elf $^
程序不能在nand中运行,只能在内存中运行。
 
-------------------------------------
手动关闭看门狗编程
 
看门狗用于监控cpu的运行,保证在故障干扰情况下能尽快回复正常,看门狗和定时器都能定时,而定时器不能发出复位信号,看门狗可以发出复位信号,在启动时IROM会自动关闭看门狗;这里学习手动关闭看门狗(关闭复位功能)。
 
看数据手册得到看门狗的寄存器信息:
 
看门狗定时器有四个寄存器:
WTCON:控制寄存器;0xE2700000
WTDAT:数据寄存器;0xE2700004
WTCNT:计数寄存器;0xE2700008
WTCLRINT:中断清除寄存器;0xE270000C
 
WTCON控制寄存器有32位:0xE2700000
[0]: Reset enable/disable
0:disable the reset function of the watchdog timer.
1:asserts reset signal of the S5PV210 at watchdog timer-out.
 
     // 关闭看门狗
     ldr     r0, =0xE2700000
     mov   r1, #0
     str     r1, [r0]
 
-------------------------------------
控制I cache(协处理器)
 
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可提高运行速度
#ifdef CONFIG_SYS_ICACHE_OFF
// clear bit 12 (I) I-cache
bic     r0, r0, #0x00001000
#else
// set bit 12 (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]
 
#define GPD0CON          (*(volatile unsigned long *)0xE02000A0)
void buzzer_init(void)
{
     GPD0CON |= 1<<0;
}
 
GPD0DTA:数据寄存器:
有4位,每一位控制一个管脚,GPD0_0由bit[0]控制,地址为0xE020_00A4。
0:低电平
1:高电平
 
#define GPD0DAT          (*(volatile unsigned long *)0xE02000A4)
void buzzer_on(void)
{
     GPD0DAT |= 1<<0;
}
 
void buzzer_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 = Reserved 0011 = KP_COL[0]
0011 ~ 1110 = Reserved
1111 = EXT_INT[16]
 
#define      GPH2CON      (*(volatile unsigned long *) 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          (*(volatile unsigned long *) 0xE0200C44)
 
// 读取KEY相关的引脚值,用于判断KEY是否被按下
          unsigned long dat;
          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管脚控制,由GPA0CON寄存器的bit[3:0]控制:
0000 = Input
0001 = Output
0010 = UART_0_RXD
0011 ~ 1110 = Reserved
1111 = GPA0_INT[0]
COM0的RST由GPA0_1管脚控制,由GPA0CON寄存器的bit[7:4]控制:
0000 = Input
0001 = Output
0010 = UART_0_TXD
0011 ~ 1110 = Reserved
1111 = GPA0_INT[1]
 
3.读数据手册,获取串口控制寄存器信息
串口寄存器很多,分别控制四个串口,以COM0为例,n都可以换成0-3。
没有列出都是保留位(reserved)。
 
ULCON0:串口线控寄存器,32位,地址:0xE290_0000=0x3(常规模式,不校验,一位停止位,8位数据位)
 
bit[1:0]: 数据位:
00:5位
01:6位
10:7位
11:8位(串口通信)
 
bit[2]: 停止位:
0:一位(串口通信)
1:两位
 
bit[5:3]: Parity(校验位):
0XX:不校验(串口通信)
100:奇校验
101:偶校验
 
bit[6]: Infrared(红外模式):
0:常规模式操作(串口通信)
1:红外模式
 
UCON0:串口控制寄存器,32位,地址:0xE290_0004=0x5(PLCK时钟,中断请求或查询方式收发数据)
 
bit[1:0]: Rx  模式:
00:禁用
01:中断请求或查询方式(串口通信)
10:DMA(直接内存访问)模式
 
bit[3:2]: Tx  模式:
00:禁止
01:中断请求或查询方式(串口通信)
10:DMA模式
 
bit[4]: break signal:
0:常规传送(默认0)
1:发送break signal
 
bit[5]: 回路模式:用来测试硬件是不是OK。
0:常规操作(默认0)
1:回路模式
 
bit[6]: Rx  错误状态中断使能位:
0:不产生Rx错误状态中断(默认0)
1:产生Rx错误状态中断
 
bit[7]: Rx   time out使能位:
0:禁止(默认0)
1:使能
 
bit[8]: Rx  中断类型:
0:pulse(默认0)
1:level
 
bit[9]: Tx  中断类型:
0:pulse(默认0)
1:level
 
bit[10]: 时钟选择位:
0:PCLK,通用外设时钟(默认0)
1:SCLK_UART,串口时钟
 
bit[16]:Rx  DMA触发大小:
0:1byte(默认0)
1:4byte
 
bit[20]:Tx  DMA触发大小
0:1byte(默认0)
1:4byte
 
UFCON0:uart fifo control register,串口先进先出控制寄存器,32位,地址:0xE290_0008=0x1(启用FIFO)
FIFO等价于串口的发送缓冲和接收缓冲。
 
bit[0]:FIFO使能位:
0:禁止
1:使能
 
bit[1]:Rx  FIFO复位:
0:常规
1:Rx FIFO 复位
 
bit[2]:Tx  FIFO 复位:
0:常规
1:Tx FIFO复位
 
bit[6:4]:Rx FIFO触发等级:有两个通道:
 
bit[10:8]:Tx FIFO 触发等级:有四个通道:
 
UTRSTAT0:uart   Tx/Rx   status   register:状态寄存器,32位,地址:0xE290_0010.
接收数据读取bit【0】,发送数据读取bit【1】或bit【2】。
 
bit[0]:接收缓冲区数据准备
0:缓冲区寄存器为空,说明没接收成功,循环等待
1:缓冲区寄存器有一个被接收的数据
 
bit[1]:发送缓冲区为空
0:发送缓冲区寄存器不为空,说明没发送成功,循环等待
1:发送缓冲区寄存器为空
 
bit[2]:发送器为空
0:不为空,发送不成功,循环等待
1:发送器为空
 
UTXH0:uart transmit buffer register:发送缓冲区寄存器,32位,地址:0xE290_0020.
发送的数据。
bit[7:0]:UTXH0,UART0的传输数据
 
URXH0:uart recive buffer register:接收缓冲区寄存器,32位,地址:0xE290_0024.
接收的数据。
bit[7:0]:URXH0,UART0的接收数据
 
UBRDIV0:uart channel baud rate division register:波特率配置寄存器,32位,地址:0xE290_0028
我们用35。
bit[15:0]:UBRDIVn:
 
#define UART_UBRDIV_VAL          35
UBRDIV0 = UART_UBRDIV_VAL;
 
UDIVSLOT0:uart channel dividing slot register: 波特率微调寄存器,32位,地址:0xE290_002C
我们用0x1.
bit[15:0]:UDIVSLOTn:一般不配置,频率低需要微调
 
#define UART_UDIVSLOT_VAL     0x1
UDIVSLOT0 = UART_UDIVSLOT_VAL;
 
波特率计算方法:
DIV_VAL = (PCLK / (bps x 16)) −1
PCLK是66.5*10^6,bps是115200,DIV_VAL取整数写入寄存器UBRDIVn。
 
波特率微调计算方法:
将DIV_VAL的小数部分乘以16得到的整数部分查表(见手册),得到写入UDIVSLOT0寄存器的值。
 
串口编程:
1.初始化GPIO寄存器设置为串口模式
2.初始化串口寄存器
3.设置串口波特率
4.进行数据收发
 
开发板接收的数据由串口终端发出
开发板发送的数据在串口终端显示
 
28 //通过调用该函数將接收缓冲区的数据返回给开发板
29 char uart_getchar()
30 {
31         while (! (UTRSTAT0 & (1 << 0)));
32         return URXH0;
33 }
34
35 //通过调用该函数,向发送缓充区放入数据,发送出去
36 void  uart_putchar(char thechar)
37 {
38         while (! (UTRSTAT0 & (1 << 2)));
39         UTXH0 = thechar;
40 }
 
通过串口工具进行调试
 
------------------------------------
移植printf函数和scanf函数
 
函数的移植:
1.移植linux内核中的printk(linux内核有自己的库)
2.移植u-boot中的printf和scanf(u-boot有自己的库)
3.自己实现
 
库函数(printf和scanf)
 
start.S
 
main.c
 
uart.c
 
Makefile
 
通过串口终端进行调试。
 
------------------------------------
增加命令功能:
类似于linux中的shell
 
实现简单的几个命令:
help:获取命令信息
md:获取内存信息
mw:写内存
loadb:向内存上传文件
go:运行内存中的程序
 
start.S
 
main.c
 
uart.c
 
shell.c
 
command.c
 
lib.c
 
stdio.c
 
Makefile
 
通过串口中断进行调试。
 
转义字符的使用:
c语言中的/n表示\n(换行)和\r(回车)两个动作。
 
下面为串口中的识别方法(终端设备的识别方法):
\r:表示回车,回到当前行的行首,也就是最左边。
\n:表示换行,换到当前列的下一行位置。
\b:表示退格,将当前位置向前移动一位。
\0:表示空字符。
 
分析:
在c语言程序中通过串口终端发送\n,实际是想发送转义字符使串口终端能实现回车键的功能(回车和换行),需要再发送一个\r,才能真正实现这个功能。
在c语言中程序通过串口终端接收\r,实际是想让开发板接收到回车,需要再返回一个\n。
 
------------------------------------
时钟
 
S5pV210有三类时钟:
主系统时钟domain(MSYS);
显示相关的时钟domain(DSYS);
外围设备的时钟domain(PSYS)。
 
MSYS:用来给处理器、DRAM控制器,3D、IRAM、IROM、中断控制器等提供时钟。
DSYS:用来给显示部件提供时钟,FIMC、FIMD、JPEG、IPs等
PSYS:用来给外围设备提供时钟,I2C、SPI、I2S、GPIO,uart等
 
210外接晶振(FIN)24Mhz,通过四个倍频器(锁相环,PLL)提高系统时钟:
APLL(MSYS);
MPLL(DSYS);
EPLL(PSYS);
VPLL(video相关时钟使用);
 
MSYS:
ARMCLK         :100MHZ,传给cpu的时钟
HCLK_MSYS      :200MHZ
PCLK_MSYS      :100MHZ
HCLK_IMEM      :100MHZ
SCLK_DMC0      :200MHZ
 
DSYS:
HCKL-DSYS      :166MHZ
PCLK_DSYS      :83MHZ
 
PSYS:
HCLK_PSYS           :133MHZ
PCLK_PSYS           :66MHZ
SCLK_ONENAND    :166MHZ
CLK_DPM              :
CLK_DVSEM           :
 
时钟的硬件组成有:
锁相环(倍频器);
多路选择器;
分频器;
 
以下均已配置ARMCLK为例,的到1MHZ输出个cpu。
 
锁相环(PLL)控制寄存器有四类(APLL、MPLL、EPLL、VPLL),以APLL为例:
 
APLL_CON0:APLL控制寄存器0,32位,地址0xE0100100
 
bit[2:0]:SDIV:pll  S分频值
 
bit[13:8]:PDIV:pll  P 分频值
 
bit[25:16]:MDIV:pll  M 分频值
 
bit[29]:Pll  锁指示位,只读位
0:解锁
1:锁
 
bit[31]:使能位
0:禁止
1:使能
 
S值 P值 M值用下列公式计算:
FOUT = MDIV X FIN / (PDIV × 2^(SDIV-1))
PDIV: 1 ≤ PDIV ≤ 63
MDIV: 64 ≤ MDIV ≤ 1023
SDIV: 1 ≤ SDIV ≤ 5
Fref (=FIN / PDIV): 1MHz ≤ Fref ≤ 12MHz
FVCO (=2 × MDIV × FIN / PDIV): 1000MHz ≤ FVCO ≤ 2060MHz
FOUT就是需要的输出频率,根据以上条件求出SPM三个值给寄存器。
 
APLL_CON1:APLL控制寄存器1(分频器(锁相环)),32位,地址0下E0100104
 
bit[4:0]:AFC,AFC值
 
bit[31]:AFC_ENB,AFC使能位
0:禁止
1:使能
 
时钟的多路选择器很多,所以时钟源控制寄存器很多,以0为例:
 
CLK_SRC0:时钟源控制寄存器0(多路选择器),32位,地址0xE0100200.
 
bit[0]:APLL_SEL:控制MUXAPLL(和ARMCLK有关)
0:FINAPLL(多路选择器MUX_APLL的0路输入)
1:FOUTAPLL(多路选择器MUX_APLL的1路输入)
FINAPLL:来自于外接晶振24MHZ
FOUTAPLL:锁相环APLL的输出
 
bit[4]:MPLL_SEL:控制MUXMPLL
0:FINPLL
1:FOUTMPLL
 
bit8]:EPLL_SEL:控制MUXEPLL
0:FINPLL
1:FOUTEPLL
 
bit[12]:VPLL_SEL:控制MUXVPLL
0:FINVPLL
1:FOUTVPLL
 
bit[16]:MUX_MSYS_SEL:控制MUX_MSYS(和ARMCLK有关)
0:SCLKAPLL(多路选择器MUX_MSYS的0路输入)
1:SCLKMPLL(多路选择器MUX_MSYS的1路输入)
 
bit[20]:MUX_DSYS_SEL:控制MUX_DSYS
0:SCLKMPLL
1:SCLKA2M
 
bit[24]:MUX_MSYS_SEL:控制MUX_PSYS
0:SCLKMPLL
1:SCLKA2M
 
bit[28]:ONENAND_SEL:控制MUXFlash
0:HCLK_PSYS
1:HCLK_DSYS
 
时钟的分频器很多,所以时钟分频控制寄存器很多,以0为例:
 
CLK_DIV0:时钟分频器控制寄存器,32位,地址0xE0100300。
 
bit[2:0]:APLL_RATIO:DIVAPLL时钟分频器的比率(和ARMCLK有关)
ARMCLK=MOUT_MSYS / (APLL_RATIO + 1)
MOUT_MSYS为多路选择器MUX_MSYS的输出;
ARMCLK为分频器DIV_APLL的输出,送给cpu。
 
bit[6:4]:A2M_RATIO:
 
bit10:8]:HCLK_MSYS_RATIO:
 
bit[14:12]:PCLK_MSYS_RATIO:
 
bit[19:16]:HCLK_DSYS_RATIO:
 
bit[22:20]:PCLK_DSYS_RATIO:
 
bit[27:24]:HCLK_PSYS_RATIO:
 
bit[30:28]:PCLK_PSYS_RATIO:
 
时钟编程参考数据手册实现
 
-------------------------------------
异常处理
 
程序正常执行流程中,程序计数器PC在其地址范围内连续增加,还可以跳转到附近程序标号,
或跳转并连接到子例程。当这个常规执行流程被转向到启用处理器内部或外部资源产生的事件时,就发生了异常。异常是由于软件错误引起的,是不正常现象。
 
210有7中异常(5种异常模式):
1.复位异常(管理模式)
2.软件中断异常(管理模式)
3.未定义指令异常(未定义模式)
4.预取异常(数据访问终止模式)
5.数据异常(数据访问终止模式)
6.IRQ(外部中断模式)
7.FIQ(快速中断模式)
 
1.复位异常:
发生在处理器上电、或上电后的复位,是一种硬件异常。
 
2.软件中断异常(SWI指令触发)
用户定义的同步中断指令,使得在user模式下运行的程序能够请求在管理模式下运行的特权操作。
 
3.未定义指令异常(指令触发)
当处理器或协处理器都没有定义该指令时产生。
 
4.预取指异常
在处理器试图执行一个未取指指令时发生,因为地址非法。
 
5.数据异常
在数据传送指令试图在非法地址载入或存储数据时发生。
 
6.IRQ(外部中断异常)
在处理器外部中断申请管脚有效(低电平),且cpsr中的I位为清除时发生。
 
7.FIQ(快速中断异常)
在处理器外部中断申请管脚有效(低电平),且cpsr中的F位为清除时发生。
 
异常发生时硬件自动处理四件事:
1.PC值变了,pc值跳转到异常向量表;
2.PC跳转之前,pc的值保存到异常模式中的lr中;
3.cpsr更改了;
4.cpsr更改之前,cpsr的值保存到异常模式中的spsr中。
 
异常向量表和异常优先级:
向量表占用32字节空间,向量表为每个异常房分配一个字空间,七种异常占用28字节,加上一个保留字就是32字节。向量表中保存的是跳转指令或载入pc指令来调整到相应的异常处理程序。当多个异常发生时,从优先级高的到优先级低的顺序处理。
 
异常向量表和优先级:
1:复位异常,在向量表中的地址0x0,异常模式为管理模式
6:未定义指令,地址0x4,未定义模式
6:软中断,地址0x8,管理模式
5:预取指令,地址0xc,数据访问终止模式
2:数据异常,地址0x10,数据访问终止模式
4:中断异常,地址0x18,IRQ模式
3:快速中断异常,地址0x1c,FIQ模式
 
1.中断向量表的实现
程序实现(以swi为例):
 
//往pc加载一个标号
ldr pc,_reset
ldr pc,_und
ldr pc,_swi
ldr pc,_prefetch
ldr pc,_data
b .
ldr pc,_irq
ldr pc,_fiq
 
//用函数来实现
_swi:
     .word swi
 
2.恢复通用寄存器和cpsr的异常
(所以在正常模式中要设置六种模式的六个栈(五个异常模式和正常模式))
stmfd sp!,{r0-3,r14}     //压栈
//异常处理
ldmfd sp!,{r0-r3,pc}^     //出栈,^用来恢复cpsr(将异常模式中的spsr给正常模式的cpsr)
 
3.恢复pc的异常
产生异常时,程序计数器指向的实际位置取决于异常的类型,也就是说pc跳转前保存到lr中的pc值和异常类型有关。
如果异常发生在arm状态,就将pc-4保存在lr中(正在执行的指令占一个字,已经读取的指令占用一个字,pc指向的指令占用一个字,所以pc-4就是返回到已经读取还没执行的指令继续执行);
如果是thumb状态,那个每种异常下的保存到pc中的值是不同的,所以需要软件程序中指定pc值,才能在异常处理完后正常返回到arm代码。
 
软中断和未定异常:
从已经取指的指令继续执行即可
 
异常返回:
mov pc,lr
 
异常处理:
stmfd sp!,{r0-3,r14}     
//异常处理
ldmfd sp!,{r0-r3,pc}^  
 
FIQ和IRQ异常:
pc被更新,存入lr的位pc-4,需要从正在执行的指令开始执行
 
从异常返回:
subs pc,lr,#4     
 
异常处理:
sub lr,lr,#4     
stmfd sp!,{r0-3,r14}     
//异常处理
ldmfd sp!,{r0-r3,pc}^ 
 
 
预取指令异常:
由于取指令失败,所以pc还没有更新,也就是说pc指向正在执行的指令的下一条取指失败的指令位置,而不是已经取指的指令的下一条指令位置,返回的程序需要回到正在执行的指令处,重新取指。
 
从异常返回:
subs pc,lr,#4     
异常处理
sub lr,lr,#4     
stmfd sp!,{r0-3,r14}     
//异常处理
ldmfd sp!,{r0-r3,pc}^ 
 
数据中断异常:
pc被更新,指向已经取指的下一条指令位置,返回时,需要返回到发生异常的指令。
从异常返回
subs pc,lr,#8
异常处理
sub lr,lr,#8
stmfd sp!,{r0-3,r14}     
//异常处理
ldmfd sp!,{r0-r3,pc}^ 
 
函数的实现(swi为例):
swi:
     stmfd sp!,{r0-r3,r14}
     //可以实现自己需要的一些操作,跳转过去即可
     ldmfd sp!,{r0-r3,pc}^
 
4.触发异常(以swi为例)
添加命令,在中断输入命令就可以触发软中断。
 
swi_test1:
     stmfd sp!,{lr}
     swi 1
     ldmfd sp!,{pc}
 
------------------------------------
中断(一种特殊的异常)
 
中断:cpu对系统发生的某个时间作出的反应,cpu暂停正在执行的程序,保留现场后自动的转去执行相应的处理程序,处理完该时间后再返回断点继续执行被打断的程序。
 
中断时cpu硬件具备的功能,是正常现象,系统停止当前正在运行的程序而转向其他服务,可能是因为优先级高的请求了服务,或者人为的安排。
 
210的中断控制器组成:
4个向量中断控制器VIC;
ARM PrimeCell PL192;
4个TrustZone Interrupt Controller (TZIC);
 
210支持93个中断源。
 
cpu处理中断过程:
cpu执行每条指令前都会检查有没有中断发生,若有硬件进行如下处理:
1.cpu进入irq模式;
2.将cpsr保存到spsr;
3.使用irq模式的r13和r14;
4.把下一条指令的地址存到r14;
5.跳转到irq程序入口地址。
 
软件进行的处理:
1.保存现场;
2.处理中断(判断中断,调用相应的处理函数,清中断);
3.恢复现场。
 
1.从数据手册得到中断控制寄存器的信息:
p565
 
2.程序编写
将按键的管脚设置为中断管脚(按下按键产生中断来点亮led)
设置中断向量表;
初始化中断控制器;
保护现场;
进入中断模式;
判断中断;
中断处理函数;
清中断;
恢复现场。
 
初始化中断控制器:
禁止所有中断;
选择中断类型为irq;
清VICxADDR;
 
------------------------------------
系统定时器
 
参考数据手册
-------------------------------------
pwm定时器
 
210有5个32位的PWM定时器,其中0、1、2、3有pwm功能,4没有输出引脚。
 
PWM使用PCLK_PSYS作为时钟源。
-------------------------------------
watchdog定时器
 
看门狗定时器相当于一个16位的普通定时器。看门狗定时器和pwm定时器的区别在于它可以产生reset信号。
------------------------------------
RTC实时时钟
 
RTC就是实时时钟芯片,用于在系统断电时,利用备用的电池记录时间。
-----------------------------------------------------------------------------------------------------------------------------------------------
外围编程
----------------------------------------------------
DMA(Direct Memory Access)
DMA关闭外设从内存获取数据需要经过cpu,有了cpu外设可以直接从内存获取数据。
DMA可以从内存直接读取数据,解放cpu,由DMA控制器来从内存获取数据。
对DMA控制器进行操作,设置DMA控制器的源地址、目的地址、长度,数据传送到DMA之后产生一个中断,表示数据传输完成。
 
1.从数据手册得到DMA寄存器信息:
p754
----------------------------------------------------
MMU(Memory Management Unit)
 
存储管理单元的作用:
1.权限管理
2.地址映射
 
cpu内核-MMU-存储管理器-DRAM
------------------------------------------------------
DRAM(内存)(以DDR2为例)
 
有四块ddr2,每块128Mb,共512Mb。
 
管脚分析:
CK/nCK:两个不同的时钟输入
CKE:时钟使能输入
nCS:片选输入
nWE/nRAS/nCAS:命令输入
ODT:死亡终点输入
DM:输入数据掩码
DQS:输入输出口,读数据输出,写数据输入
nDQS:输入输出口,
BA0-BA2:bank地址输入口
A0-A13:地址输入总线
DQ0-7:输入输出口,数据总线
 
1.从电路原理图得接口信息:
CK:       Xm1SCLK
nCK:       Xm1SCLKn
CKE:       Xm1CKE0
nCS:       Xm1CSn0
nWE:    Xm1WEn
nRAS:   Xm1RASn
nCAS:     Xm1CASn
DM:        Xm1DM0
DQS:       Xm1DQS0
nDQS:     Xm1DQSn0
BA0:     Xm1BA0
BA1:      Xm1BA1
BA2:      Xm1CSn1/BA2:Xm1CSn1
A0-A13:  Xm1ADDR[13:0]:Xm1ADDR0-13
DQ0-7:   Xm1DATA[7:0]:Xm1DATA0-7
 
2.从数据手册得到DRAM初始化信息
 
P596
3.从收据手册得DRAM控制寄存器信息
P614
参考uboot的实现
------------------------------------------------------
存储设备(以NAND为例)
 
内存(RAM):掉电易失,DRAM/SDRAM等,pc机叫DDR内存。
闪存(Flash/ROM):非掉电易失,NOR(很少用),NAND(SLC、MLC),U盘、MMC卡和SD卡(TF卡已经被淘汰)也算NAND,pc机叫硬盘。
 
RAM:分为SRAM和DRAM(SDRAM已经淘汰);SRAM开机就可以用,通常集成在CPU内部;SDRAM开机需要初始化才能用。
ROM:一般集成在CPU内部,作为固件使用。
 
对flash闪存的写入操作只能在空或已擦出的单元进行,NAND的擦除是以8-32kb的块进行的;NAND的接口使用复杂的I/O口来串行的存取数据,NAND的读和写采用512byte的块。
 
以三星的单片128MB的NAND SLC为例:
512MB*8bit,48pin
 
输入和输出是相对于NAND芯片的,输入(写)为从cpu到nand,输出(读)为从nand到cpu。
读:数据从nand到cpu;
写:数据从cpu到nand。
 
I/O 0-7:八位数据位,命令、地址、数据都通过这八位输入和输出,和cpu进行数据交换。空闲时为高阻态。
CLE:命令锁存使能,当CLE=1时,I/O口的输入为命令在~WE的上升沿被锁存。
ALE:地址锁存使能,当ALE=1时,I/O口的输入为地址在~WE的上升沿被锁存。
当CLE和ALE都是低电平时,I/O的输出为数据。
#CE:芯片使能,输入,设备选择控制引脚,低电平使能。
#RE:读使能,输入,,在~RE下降沿数据读入有效,数据从nand到cpu,同时自动使内部列地址计数器加1,下降沿锁存。
#WE:写使能,输入,控制I/O口的写操作,低电平有效,数据从cpu到nand,I/O口在~WE的上升沿被锁存。
#WP:写保护,输入,低电平时,内部高压产生器复位,nand只读。
R/#B:就绪/忙碌输出信号,输出,指明设备状态,低电平时,表示一个编程、擦出或随机读操作正在进行(也就是busy),操作完成变成高电平(也就是ready)。
 
NAND的单位:
5地址周期
页   :1page=2kb+64byte(校验)
块   :1block=64pages
设备:1device=8192blocks
 
NAND操作方法:
NAND内部固化了命令,可以直接使用,通过命令进行操作。
写入命令、地址、数据时,需要将~WE、~CE同时拉低,数据在~WE的上升沿被NAND锁存;
命令锁存信号CLE、地址锁存信号ALE用来分辨、锁存命令或地址;
 
存取NAND上的数据需要五个地址序列:
2个序列的列地址(页内偏移);
3个序列的行地址(页号)。
块擦除只需要3个行地址序列,因为页内偏移为0,就是从页首开始擦除。
 
NAND访问方法:
先传输命令,然后传输地址,最后读写数据;
 
1.从电路原理图得到管脚信息:
#CE:   Xm0CSn2/NFCSn0:            MP01_2
CLE:    Xm0FCLE/ONDAVD:          MP03_0
ALE:    Xm0FALE/ONDSMCLK:       MP03_1
#WE:   Xm0FWEn/ONDRPn:         MP03_2
#RE:    Xm0FREn:                      MP03_3
R/#B:     Xm0FRnB0/ONDXL_INT0: MP03_4
I/O 7-0:Xm0DATA[7:0]:                   MP06_7——0
 
2.从数据手册得到cpu控制nand的寄存器的信息:
 
MP01,MP03,MP04,MP05,MP06,MP07各有八个管脚,MP02只有4个管脚。
MP01,MP02,MP03位控制位,MP04,MP05位地址位,MP06,MP07为数据位。
在nand中只使用他们的控制寄存器,将所有管脚设置为nand模式相关即可。
 
MP0_1CON:MP0_1控制寄存器,32位,每四位控制一个管脚,地址:0xE02002E0
MP0_1CON = 0x22333322;
 
MP0_2CON:MP0_2控制寄存器,32位,每四位控制一个管脚,地址0xE0200300
MP0_2CON = 0x00002222;
MP0_3CON:MP0_3控制寄存器,32位,每四位控制一个管脚,地址0xE0200320
MP0_3CON = 0x22222222;
 
3.从数据手册得到nand控制寄存器的信息:
 
NFCONF:nand flash configuration register,nand flash配置寄存器,32位,地址:0xB0E00000
 
bitf[1]:地址周期
0: 1page=512bytes,为3地址周期;1page=2k或4k,为4地址周期;
1:1page=512bytes,为4地址周期;1page=2k或4k,为5地址周期(nand使用);
 
bit[2]:页大小
0:MLCFlash=0,2k;(nand使用)         MLCFlash=1,4k;
1:MLCFlash=0,512bytes;                  MLCFlash=1,2K
 
bit[3]:MLCFlash,判断那种flash,SLC和MLC
0:SLC(nand使用)
1:MLC
 
bit[7:4]:TWRPH1:从#WE开始结束使能(上升沿)开始,到CLE和ALE开始结束锁存之前(下降沿开始之前)这段时间
1111:给最长的时间
 
bit[11:8]:TWRPH0:从#WE开始使能(下降沿)之后,到#WE结束使能之前(上升沿开始之前)这段时间
1111:给最长的时间
 
bit[15:12]:TACLS:从CLE和ALE锁存(上升沿)开始,到#WE开始使能(下降沿)之前的这段时间
1111:给最长的时间
 
bit[24:23]:ECC type0:校验类型
00:不校验
 
bit[25]:MsgLength
0:512byte(不用,给0)
 
NFCONT:控制寄存器,32位,地址:0xB0E00004.
 
bit[0]:mode:模式位
0:禁用nand控制器(默认值)
1:启用nand控制器(设置值)
 
bit[1]:Reg_nCE0:nand flash memory nRCS[0] signal control
0:给低电平,片选使能(设置值)
1:给高电平,片选禁止(默认值)
 
bit[2]:Reg_nCE1:nand flash memory nRCS[1] signal control
0:给低电平,片选使能
1:给高电平,片选禁止
 
bit[3]:HW_nCE
 
bit[4]:initSECC:
 
bit[5]:initMECC
 
bit[6]:SECCLock
 
bit[7]:MECCLock
 
bit[8]:RnB_TransMode
 
bit[9]:EnblRnBINT
 
bit[10]:EnblllegalAccINT
 
bit[12]:EnbMLCDecINT
 
bit[13]:EnbMLCEncINT
 
bit[16]:LOCK
 
bit[17]:LockTight
 
bit[18]:MLCEccDirction
 
bit[22]:Reg_nCE2:nand flash memory nRCS[2] signal control
0:给低电平,片选使能
1:给高电平,片选禁止
 
bit[23]:Reg_nCE3:nand flash memory nRCS[3] signal control
0:给低电平,片选使能
1:给高电平,片选禁止
 
NFCMMD:nand 命令寄存器,32位,8位有效,地址:0xB0E00008
 
bit[7:0]:REG_CMMD,nand 命令值
 
NFADDR:nand地址寄存器,32位,8位有效,地址:0xB0E0000C
 
bit[7:0]:REG_ADDR,nand 地址值
 
NFDATA:nand 数据寄存器,32位,地址:0xB0E00010
 
bit[31:0]:nand 数据值
 
NFSTAT:NFCON status register:nand控制状态寄存器,32位,地址:0xB0E00028。
 
bit[0]:flash_RnB:RnB[0]输入管脚状态,只读位
0:nand busy
1:nand ready to operate
 
bit[2]:falsh_nCE[0]:nCE[0]输出管脚状态,只读位
 
bit[3]:flash_nCE[1]:nCE[1]输出管脚状态,只读位
 
bit[4]:RnB_TansDetect:检测RnB[0]管脚的状态
0:RnB 的变化不会被捕获,为busy,不能用
1:RnB的变化会被捕获,为ready,可用
 
bit[5]:IllegalAccess
 
bit[6]:MLCDecodeDone:
 
bit[7]:MCLEncodeDone:
 
bit[11:8]:flash_nCE[3:0]:nCE[3:0]输出管脚的状态,只读位
 
bit[27:24]:RnB_TransDetect_GRP:检测RnB[3:0]管脚的状态
0:RnB变化不会被捕获
1:RnB变化会被捕获
 
bit[31:28]:flash_RnB_GRP:RnB[3:0]输入管脚的状态,只读位
0:nand busy
1:nand ready to operate
 
程序实现:
参考数据手册的时序和nand命令来实现。
------------------------------------------------------
ADC(210cpu内置ADC功能)
 
210的ADC支持10bit和12bit,支持10路输入,然后将输入的模拟信号转换成10bit或12bit的二进制数字信号。
0 0
原创粉丝点击