打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
TQ210裸机编程(5)

之前都是把程序直接下载到DDR内存,然后直接跳转到内存去运行,之所以可以运行是因为开发板自带的u-boot已经初始化好了DDR内存、时钟等。由于u-boot已经初始化好了时钟,因此这次实验就不能像之前那样操作了,而需要把程序直接烧写到SD卡,然后从SD卡启动。


S5PV210启动流程:

查看S5PV210芯片手册和《S5PV210_iROM_ApplicationNote_Preliminary_20091126》

可以通过配置OM引脚选择如下任意一个设备启动
· General NAND Flash memory
·OneNAND memory
· SD/ MMC memory (such as MoviNAND and iNAND)
·eMMC memory
·eSSD memory
· UART and USB devices


在系统复位时,CPU从固化在片内ROM里的代码开始执行,然而系统复位可能不是在启动时,也可能在被唤醒时,因此IROM Code必须根据复位状态做出适当的处理。


· iROM代码放在片内64KB ROM中。它初始化基本的系统功能,比如时钟,栈,堆。

· iROM代码从从指定的启动设备(NAND/SD/NOR等)加载第1阶段boot loader(BL1)到片内96KB的SRAM。启动设备通过OM引脚选择。

· 第1阶段的boot loader(BL1)加载第2阶段的boot loader(BL2)到片内SRAM

· 第2阶段boot loader(BL2)初始化系统时钟,UART和DRAM控制器。初始化DRAM后,它从启动设备加载操作系统镜像到DRAM。

· 当启动完成后,第2阶段boot loader(BL2)跳转到操作系统去执行。


程序开始于iROM,然后到SRAM,最终程序在DRAM中执行。

iROM(BL0)启动序列如下:

1.  关闭看门狗
2.  初始化指令icache.
3.  初始化栈和堆
4.  初始化块设备拷贝函数
5.  设置时钟分频, 锁定时间, 锁相环(PLL)和时钟源.
6.  检测OM引脚选择从哪个设备启动,然后从启动设备加载BL1(最大16KB)到iRAM

7.  对BL1的校验和进行验证,如果验证失败,iROM将尝试从第2个设备启动
8.  如果是安全模式启动,则对BL1进行完整性验证

9. 跳转到BL1的起始地址(0xD0020010)

iRAM(BL1)启动序列如下:

1. 从启动设备加载BL2(最大80KB)到iRAM

2. 初始化系统时钟,UART,DRAM

3. 从启动设备加载OS到DRAM

4. 跳转到DRAM中的OS执行(0x2000000 或 0x40000000)


iROM在加载BL1时会校验BL1的头信息,规定如下

0x0:BL1的大小(最大16KB - 16B)

0x4 : 0 规定

0x8 : BL1的校验和

0x16 : 0 规定

所以我们在生成led.bin后,还需要添加16B的头信息。

校验和计算方法见《S5PV210_iROM_ApplicationNote_Preliminary_20091126》

  1. for(count=0;count< dataLength;count+=1)   
  2. {   
  3.     buffer = (*(volatile u8*)(uBlAddr+count));   
  4.     checkSum = checkSum + buffer;   
  5. }   

- count             unsigned int 类型的变量.
- dataLength    unsigned int类型的变量。它包含BL1的大小.
- buffe              unsigned short 类型的变量。 它用来从BL1中读取一个字节.
- checksum      unsigned int 类型的变量。它包含BL1的和.


注意:《S5PV210_iROM_ApplicationNote_Preliminary_20091126》中有这样一段描述

SD/MMC拷贝函数,我的判断是:在iROM从SD卡加载BL1时,使用的就是这个函数,注意里面的关键参数

param u16 blockSize : Number of blocks to copy.
拷贝多少块,也就是说在iROM从SD卡加载BL1时是按块的整数倍拷贝的,而不是按字节拷贝。再接着看

从这可以看出一个块的大小是512字节,而且第0块保留不用,我们需要将led.bin 从第1块开始烧写。

所以BL1的头信息的BL1的大小(包括头信息16B的大小)应该是512的整数倍。我刚开始没有注意到这点,在制作头信息时按led.bin的实际大小(多少字节)来填充第0字节,导致iROM校验失败,没法启动。


时钟配置:

S5PV210由3个时钟域构成,分别是主系统(MSYS),显示系统(DSYS),外围系统(PSYS)。

1.MSYS域包括Cortex A8核、DRAM内存控制器、3D、iROM、iRAM、INTC等。

2.DSYS域包括显示相关模块,包括FIMC、FIMD、JPEG等。

3.PSYS域用于安全、I/O外围、低功耗的声音播放等。

如下图所示

S5PV210包含4个锁相环(APLL、MPLL、EPLL、VPLL)

手册上建议使用24MHz的晶振为4个PLL提供输入时钟。

在S5PV210中的典型应用:

· Cortex A8 and MSYS clock domain uses APLL (that is, ARMCLK, HCLK_MSYS, and PCLK_MSYS).
· DSYS and PSYS clock domain (that is, HCLK_DSYS, HCLK_PSYS, PCLK_DSYS, and PCLK_PSYS) and
other peripheral clocks (that is, audio IPs, SPI, and so on) use MPLL and EPLL.
· Video clocks uses VPLL.

手册推荐的时钟

·freq(ARMCLK) = 1000 MHz
·freq(HCLK_MSYS) = 200 MHz
·freq(HCLK_IMEM) = 100 MHz
·freq(PCLK_MSYS) = 100 MHz
·freq(HCLK_DSYS) = 166 MHz
·freq(PCLK_DSYS) = 83 MHz
·freq(HCLK_PSYS) = 133 MHz
·freq(PCLK_PSYS) = 66 MHz
· freq(SCLK_ONENAND)   = 133 MHz, 166 MHz

PLL:

APLL 用来驱动 MSYS 域 和 DSYS 域. 它能产生高达 1 GHz的频率
MPLL用来驱动 MSYS 域和 DSYS 域.它能产生高达 2 GHz的频率
EPLL 主要用来产生 声音相关的时钟.
VPLL主要用来产生视频系统操作的时钟, 54 MHz.  
典型的, APLL 驱动MSYS域,MPLL 驱动DSYS 域.

时钟配置步骤如下:

Turn on a PLL
(A,M,E,V)PLL_CON[31] = 1; // Power on a PLL (Refer to (A, M, E, V) PLL_CON SFR)

wait_lock_time;   // Wait until the PLL is locked

(A, M, E, V)PLL_SEL = 1; // Select the PLL output clock instead of input reference clock, after PLL  
  output clock is stabilized. (Refer to 0, 4, 8, 12th bit of CLK_SRC0 SFR)

Once you turned on any PLL, do not turn off that.  

Change PLL’s PMS values
Set PMS values;   // Set PDIV, MDIV, and SDIV values
 (Refer to (A, M, E, V) PLL_CON SFR)

Change the system clock divider values
CLK_DIV0 [31:0] = target value0;

Change the divider values for special clocks
CLK_DIV1 [31:0] = target value1;
CLK_DIV2 [31:0] = target value2;

代码如下:

start.S

  1. .global _start              /* 声明一个全局的标号 */  
  2. _start:  
  3.     bl clock_init           /* 时钟初始化 */  
  4.     bl main                 /* 跳转到C函数去执行 */  
  5. halt:  
  6.     b halt  

问:为什么start.S中没有像S3C2440那样首先关闭看门狗、为调用C函数设置栈这些操作?

答:因为在iROM里的代码已经帮我们做好了这些,包括基本的时钟初始化。

iROM初始化的时钟配置如下:


clock.c

  1. #define APLLCON0        *((volatile unsigned int *)0xE0100100)  
  2. #define MPLLCON         *((volatile unsigned int *)0xE0100108)  
  3. #define EPLLCON0        *((volatile unsigned int *)0xE0100110)  
  4. #define VPLLCON         *((volatile unsigned int *)0xE0100120)  
  5. #define CLK_SRC0        *((volatile unsigned int *)0xE0100200)  
  6. #define CLK_DIV0        *((volatile unsigned int *)0xE0100300)  
  7. #define CLK_DIV1        *((volatile unsigned int *)0xE0100304)  
  8. #define CLK_DIV2        *((volatile unsigned int *)0xE0100308)  
  9. #define CLK_DIV3        *((volatile unsigned int *)0xE010030C)  
  10.   
  11. void clock_init()  
  12. {  
  13.     /* 1、设置PLL_LOCK寄存器(这里使用默认值) */  
  14.     /* 2、设置PLL_CON寄存器(使用芯片手册推荐的值) */  
  15.     APLLCON0    = (1 << 0) | (3 << 8) | (125 << 16) | (1 << 31);    /* FOUTAPLL = 1000MHz */  
  16.     MPLLCON     = (1 << 0) | (12 << 8) | (667 << 16) | (1 << 31);   /* FOUTMPLL = 667MHz */  
  17.     EPLLCON0    = (1 << 0) | (12 << 8) | (667 << 16) | (1 << 31);   /* FOUTEPLL = 96MHz */  
  18.     VPLLCON     = (3 << 0) | (6 << 8) | (108 << 16) | (1 << 31);    /* FOUTVPLL = 54MHz */  
  19.       
  20.     /* 3、选择PLL为时钟输出 */  
  21.     /* MOUT_MSYS = SCLKAPLL = 1000MHz 
  22.     ** MOUT_DSYS = SCLKMPLL = 667MHz 
  23.     ** MOUT_PSYS = SCLKMPLL = 667MHz 
  24.     */  
  25.     CLK_SRC0 = (1 << 0) | (1 << 4) | (1 << 8) | (1 << 12);  
  26.       
  27.     /* 4、设置系统时钟分频值 */  
  28.     /* freq(ARMCLK) = MOUT_MSYS / (APLL_RATIO + 1) = 1000MHz / (0 + 1) = 1000MHz 
  29.     ** freq(HCLK_MSYS) = ARMCLK / (HCLK_MSYS_RATIO + 1) = 1000MHz / (4 + 1) = 200MHz 
  30.     ** freq(PCLK_MSYS) = HCLK_MSYS / (PCLK_MSYS_RATIO + 1) = 200MHz / (1 + 1) = 100MHz 
  31.     ** freq(HCLK_DSYS) = MOUT_DSYS / (HCLK_DSYS_RATIO + 1) = 667 / (3 + 1) = 166MHz 
  32.     ** freq(PCLK_DSYS) = HCLK_DSYS / (PCLK_DSYS_RATIO + 1) = 166 / (1 + 1) = 83MHz 
  33.     ** freq(HCLK_PSYS) = MOUT_PSYS / (HCLK_PSYS_RATIO + 1) = 667 / (4 + 1) = 133MHz 
  34.     ** freq(PCLK_PSYS) = HCLK_PSYS / (PCLK_PSYS_RATIO + 1) = 133 / (1 + 1) = 66MHz 
  35.     */  
  36.     CLK_DIV0 = (0 << 0) | (4 << 8) | (1 << 12) | (3 << 16) | (1 << 20) | (4 << 24) | (1 << 28);  
  37. }  

led.c

  1. #define GPC0CON     *((volatile unsigned int *)0xE0200060)  
  2. #define GPC0DAT     *((volatile unsigned int *)0xE0200064)  
  3.   
  4. void delay(volatile unsigned int t)  
  5. {  
  6.     volatile unsigned int t2 = 0xFFFF;  
  7.     while (t--)  
  8.         for (; t2; t2--);  
  9. }  
  10.   
  11. int main()  
  12. {  
  13.     int toggle = 0;  
  14.     GPC0CON &= ~(0xFF << 12);  
  15.     GPC0CON |= 0x11 << 12;    // 配置GPC0_3和GPC0_4为输出  
  16.       
  17.     while (1)  
  18.     {  
  19.         GPC0DAT &= ~(0x3 << 3);       // 熄灭LED1和LED2  
  20.           
  21.         if (toggle)  
  22.             GPC0DAT |= 1 << 3;        // 点亮LED1  
  23.         else  
  24.             GPC0DAT |= 1 << 4;        // 点亮LED2  
  25.           
  26.         toggle = !toggle;  
  27.         delay(0x50000);  
  28.     }  
  29.       
  30.     return 0;  
  31. }  


Makefile

  1. led.bin: start.o clock.o led.o  
  2.     arm-linux-ld -Ttext 0xD0020010 -o led.elf $^  
  3.     arm-linux-objcopy -O binary led.elf $@  
  4.     arm-linux-objdump -D led.elf > led.dis  
  5.       
  6. %.o : %.c  
  7.     arm-linux-gcc -c $< -o $@  
  8. %.o : %.S  
  9.     arm-linux-gcc -c $< -o $@  
  10.       
  11. clean:  
  12.     rm *.o *.elf *.bin *.dis  


执行make后生成led.bin

addheader.c用于构造带有头信息的bin文件

  1. /* 
  2. ** 在BL0阶段,iROM内固化的代码读取nandflash或SD卡前面最大16K的内容(即BL1)到iRAM, 
  3. ** 并比对前16字节中的校验和是否正确,正确则继续,错误则尝试启动下一个设备。 
  4. ** BL1的头信息规定如下 
  5. ** 0x0:BL1的大小(最大16K,包括BL1头信息的大小) 
  6. ** 0x4: 0(规定) 
  7. ** 0x8:校验和 
  8. ** 0xC:0(规定) 
  9. */  
  10. #include <stdio.h>  
  11. #include <string.h>  
  12. #include <stdlib.h>  
  13.   
  14. #define IMG_SIZE                (16*1024)  
  15. #define HEADER_SIZE             16  
  16. #define BLKSIZE                 512  
  17.   
  18. int main (int argc, char *argv[])  
  19. {  
  20.     FILE            *fp;  
  21.     unsigned        char *Buf;  
  22.     int             BufLen;  
  23.     int             nbytes, fileLen;  
  24.     unsigned int    checksum, count;  
  25.     int             i;  
  26.   
  27.     if (argc != 3)  
  28.     {  
  29.         printf("Usage: %s <source file> <destination file>\n", argv[0]);  
  30.         return -1;  
  31.     }  
  32.   
  33.     /* 分配16K的buffer */  
  34.     BufLen = IMG_SIZE;  
  35.     Buf = malloc(BufLen);  
  36.     if (!Buf)  
  37.     {  
  38.         perror("Alloc buffer failed!");  
  39.         return -1;  
  40.     }  
  41.     memset(Buf, 0x00, BufLen);  
  42.   
  43.     /* 读源bin到buffer */  
  44.     fp = fopen(argv[1], "rb");  
  45.     if( fp == NULL)  
  46.     {  
  47.         perror("source file open error");  
  48.         free(Buf);  
  49.         return -1;  
  50.     }  
  51.     /* 获取源bin长度 */  
  52.     fseek(fp, 0L, SEEK_END);  
  53.     fileLen = ftell(fp);  
  54.     fseek(fp, 0L, SEEK_SET);  
  55.   
  56.     /* 源bin长度不得超过16K-16byte */  
  57.     fileLen = (fileLen < (IMG_SIZE - HEADER_SIZE)) ? fileLen : (IMG_SIZE - HEADER_SIZE);  
  58.   
  59.     /* 读源bin到buffer[16] */  
  60.     nbytes = fread(Buf + HEADER_SIZE, 1, fileLen, fp);  
  61.     if (nbytes != fileLen)  
  62.     {  
  63.         perror("source file read error\n");  
  64.         free(Buf);  
  65.         fclose(fp);  
  66.         return -1;  
  67.     }  
  68.     fclose(fp);  
  69.       
  70.     /* 计算校验和 */  
  71.     for(i = 0, checksum = 0; i < fileLen; i++)  
  72.         checksum += Buf[HEADER_SIZE + i];  
  73.   
  74.     /* 计算BL1的大小: 
  75.     ** BL1的大小包括BL1的头信息 
  76.     ** 另外iROM从SD卡拷贝是按块拷贝的,因此这里需要调整大小为512字节的整数倍 
  77.     */  
  78.     fileLen += HEADER_SIZE;  
  79.     count = fileLen / BLKSIZE * BLKSIZE;  
  80.     if (count < fileLen)  
  81.         count += BLKSIZE;  
  82.     memcpy(Buf, &count, 4);     // 保存BL1的大小到Buf[0-3]  
  83.   
  84.     // 将校验和保存在buffer[8~15]  
  85.     memcpy(Buf + 8, &checksum, 4);  
  86.   
  87.     fp = fopen(argv[2], "wb");  
  88.     if (fp == NULL)  
  89.     {  
  90.         perror("destination file open error");  
  91.         free(Buf);  
  92.         return -1;  
  93.     }  
  94.     // 将count + HEADER_SIZE字节的buffer拷贝到目的bin中  
  95.     nbytes  = fwrite(Buf, 1, count, fp);  
  96.     if (nbytes != count)  
  97.     {  
  98.         perror("destination file write error");  
  99.         free(Buf);  
  100.         fclose(fp);  
  101.         return -1;  
  102.     }  
  103.   
  104.     free(Buf);  
  105.     fclose(fp);  
  106.   
  107.     return 0;  
  108. }  

执行如下命令生成addheader

# gcc addheader.c -o addheader

然后再用addheader制作带有头信息的bin文件

# ./addheader led.bin 210.bin

然后将SD卡插入SD读卡器,将鼠标移到虚拟机中,然后将SD卡读卡器插入电脑,这是ubuntu中将自动挂载SD卡

在ubuntu中执行df查看分区信息

root@zjh:/mnt/hgfs/E/cloud/embedded/my_code/tq210# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda1             39544232  10202800  27332652  28% /
none                    506996       260    506736   1% /dev
none                    512616       164    512452   1% /dev/shm
none                    512616       372    512244   1% /var/run
none                    512616         0    512616   0% /var/lock
.host:/              331099132 158170568 172928564  48% /mnt/hgfs
/dev/sdd                 46220     46220         0 100% /media/XIAOMI
/dev/sdb1              3864000         4   3863996   1% /media/720C-93BE

sdb1就是我们的SD卡的第1个分区

执行如下命令烧写210.bin到SD的第1个块

# dd bs=512 iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1

1+1 records in
1+1 records out
528 bytes (528 B) copied, 0.0110204 s, 47.9 kB/s

bs指定一次烧写多少字节

dsync表示为数据使用同步I/O

if指定输入文件

of指定输出设备

seek指定从第几块开始烧写

然后拔下SD卡,将其插入的TQ210开发板,然后拨动启动选择开关,选择从SD启动

然后上电可以看到LED交替闪烁,可以去掉start.S中的时钟初始化,再次编译程序,烧写,可以看出LED闪烁变慢了。

  1. .global _start              /* 声明一个全局的标号 */  
  2. _start:  
  3.     //bl clock_init         /* 时钟初始化 */  
  4.     bl main                 /* 跳转到C函数去执行 */  
  5. halt:  
  6.     b halt  


转载请注明来源:http://blog.csdn.net/zjhsucceed_329/

QQ:809205580

技术交流群:153530783 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
S5PV210--1---210启动方式和代码前16字节
mkv210_image.c
使uboot支持S3C6410的SD启动
Exynos 4412的启动过程分析
今日头条
linux:上电启动
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服