EasyARM2200开发板学习笔记:初级C程序运行环境的建立

以下启动代码是从EasyARM2200开发板的例子程序中提取出来的简单启动代码,当然这种启动代码在实际开发中是不太可能用到的,这里只是为了启动过程的理解方便。但就这么简单的几行代码却还是有一些东西让人不太理解,比如|Image$$XX$$Limit||Image$$XX$$Base|,有意思,那就接着分析吧。

ADS1.2的朋友都知道在编译连接一个ARM程序之前要根据开发板的实际存储配置设置连接器的选项,这里我就以EasyARM2200的开发板来配置,因为我使用的是LPC2210处理器,片内无FLASH,片内RAM地址范围初设置为0x40000000~0x40003FFF,大小只有16Kbytes,所以大一点的程序就不能在这里面调试了,当然此开发板片外也配有512Kbytes的RAM和2Mbytes的FLASH,它们的地址可根据跳线来配置,这里就先不讲解。我们这个程序只在片内RAM中调试就可以了,下面就是DebugDel生成目标的连接器配置:

配置完成后,点击Make后会生成一个编译连接报告,如下:

  • 结合启动代码、连接器配置、编译连接报告和ADS1.2帮助文档我们隐约可以看出刚才那几个怪怪的 symbol代表什么了:

    |Image$$RO$$Base|

    表示RO输出段运行时起始地址,也可以说是程序代码存放的起始地址,由-ro-base这个参数       指定,所以|Image$$RO$$Base|=0x40000000;

    |Image$$RO$$Limit|

    表示RO输出段运行时存储区域界限,其值可通过|Image$$RO$$Base|+Code sizes+RO Data sizes计算得出,所以|Image$$RO$$Limit|=0x40000090;

    |Image$$RW$$Base|

    表示RW输出段运行时起始地址,记得是运行时的地址,而不一定是加载时的存放地址,因为RW输出段 在加载时可能是在ROM中并紧跟着RO输出段存放的,当程序运行时才复制到RAM起始地址为|Image $$RW$$Base|的区域,由-rw-base这个参数指定,所以|Image$$RW$$Base|=0x40003000;如 果未指定-rw-base,默认紧跟RO输出段,那么|Image$$RW$$Base||Image$$RO$$Limit|

    |Image$$RW$$Limit|

    表示RW输出段运行时存储区域界限,其值可通过|Image$$RW$$Base|+RW Data sizes计算得 出,所以|Image$$RW$$Limit|=0x40003004;

    |Image$$ZI$$Base|

    表示ZI输出段运行时起始地址,它是运行时在RAM中生成的,紧跟着RW输出段存放,其值和|Image $$RW$$Limit|一样,所以|Image$$ZI$$Base|=0x400030004;

    |Image$$ZI$$Limit|

    表示ZI输出段运行时存储区域界限,其值可通过|Image$$ZI$$Base|+ZI Data sizes计算得 出,所以|Image$$ZI$$Limit|=0x40003008。

    看出什么没有,原来上面这些都是在运行时域的地址,那从加载时域到运行时域之间我们的处理器还对其烧进去的程序做了什么呢?这就又得回到启动代码中去看,认真分析可以看出LOOP0和LOOP1正是引导处理器对其上面这些输出段的操作。下图就是LOOP0和LOOP1所做的工作。

    可以看出这里的RW段完全可以不做复制,因为0x40003000也是在内部RAM中,那怎么让RW段不做复制呢?有两种方法:

    第一种是不定义RW Base为0x40003000,这样加载时域和运行时域的RW段在同一个地址且紧接着RO段,生成的ZI段也紧接着RW段,如下图:

    第二种是使用两个加载时域,定义RO段所在的加载时域的起始地址为0x40000000,RW段所在的加载时域的起始地址直接设置为0x40003000,如下图:这样RW段也就可以和RO段分开加载了,运行时就不用重新复制RW段,提高运行效率。

    不过上面这几种方法都只适用于调试,也就是程序加载时都是放在RAM中才行。如果是最终加载到ROM的程序那就要用到一个加载时域和多个运行时域的方法,这里先不深入,下次再分析,吃饭去了。

1 ; 启动文件,初始化C程序的运行环境,然后进入C程序代码。

3          IMPORT     |Image$$RO$$Limit|

4          IMPORT     |Image$$RW$$Base|

5          IMPORT     |Image$$ZI$$Base|

6          IMPORT     |Image$$ZI$$Limit|

7

8          IMPORT     Main      ; 声明C程序中的Main()函数

9

10          AREA      Start,CODE,READONLY   ; 声明代码段Start

11          ENTRY      ; 标识程序入口

12         CODE32     ; 声明32ARM指令    

13            

14 Reset   LDR        SP,=0x40003F00     ; 设置堆栈指针

15

16         ; 初始化C程序的运行环境

17         LDR        R0,=|Image$$RO$$Limit|

18          LDR        R1,=|Image$$RW$$Base|    

19          LDR        R3,=|Image$$ZI$$Base|    

20             

21          CMP        R0,R1

22          BEQ       LOOP1

23 LOOP0   CMP        R1,R3      

24          LDRCC     R2,[R0],#4     

25          STRCC     R2,[R1],#4

26          BCC        LOOP0

27         

28 LOOP1   LDR        R1,=|Image$$ZI$$Limit|

29          MOV        R2,#0

30 LOOP2   CMP        R3,R1

31          STRCC     R2,[R3],#4

32          BCC        LOOP2

33         

34          B         Main     ; 跳转到C程序代码Main()函数

35        

36          END

http://hi.baidu.com/spbeijilang/blog/item/63f135a1e63d118c46106441.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于EasyARM1768开发板和相关硬件资源实现万年历的示例代码,其中数码管使用了TM1638驱动芯片: ```c #include <LPC17xx.h> #include <stdio.h> #include <stdlib.h> #include "TM1638.h" #define LED_PIN (1 << 22) // P1.22 #define KEY1_PIN (1 << 23) // P1.23 #define KEY2_PIN (1 << 24) // P1.24 #define KEY3_PIN (1 << 25) // P1.25 void delay_ms(uint32_t ms) { uint32_t i, j; for (i = 0; i < ms; i++) { for (j = 0; j < 6000; j++); } } void LED_On(void) { LPC_GPIO1->FIOSET |= LED_PIN; } void LED_Off(void) { LPC_GPIO1->FIOCLR |= LED_PIN; } void RTC_Init(void) { LPC_RTC->CCR = 0; // Disable RTC LPC_RTC->ILR = 3; // Clear interrupt flags LPC_RTC->CIIR = 0; // Disable all interrupts LPC_RTC->AMR = 0; // Enable all alarm matches LPC_RTC->CALIBRATION = 0x00000000; LPC_RTC->CCR = 1; // Enable RTC } void RTC_SetTime(uint32_t hour, uint32_t minute, uint32_t second) { LPC_RTC->CCR = 0; // Disable RTC LPC_RTC->SEC = second; LPC_RTC->MIN = minute; LPC_RTC->HOUR = hour; LPC_RTC->CCR = 1; // Enable RTC } void RTC_GetTime(uint32_t *hour, uint32_t *minute, uint32_t *second) { *hour = LPC_RTC->HOUR; *minute = LPC_RTC->MIN; *second = LPC_RTC->SEC; } void RTC_SetDate(uint32_t year, uint32_t month, uint32_t day) { LPC_RTC->CCR = 0; // Disable RTC LPC_RTC->DOM = day; LPC_RTC->MONTH = month; LPC_RTC->YEAR = year; LPC_RTC->CCR = 1; // Enable RTC } void RTC_GetDate(uint32_t *year, uint32_t *month, uint32_t *day) { *year = LPC_RTC->YEAR; *month = LPC_RTC->MONTH; *day = LPC_RTC->DOM; } void RTC_SetAlarm(uint32_t hour, uint32_t minute, uint32_t second) { LPC_RTC->CCR = 0; // Disable RTC LPC_RTC->ALSEC = second; LPC_RTC->ALMIN = minute; LPC_RTC->ALHOUR = hour; LPC_RTC->CIIR |= 1 << 1; // Enable alarm match for hours, minutes, and seconds LPC_RTC->CCR = 1; // Enable RTC } void RTC_ClearAlarm(void) { LPC_RTC->ILR |= 1 << 1; // Clear alarm interrupt flag } void RTC_IRQHandler(void) { if (LPC_RTC->ILR & (1 << 1)) { LED_On(); // Turn on LED TM1638_DisplayString("ALARM"); // Display "ALARM" on TM1638 while (1) { if (LPC_GPIO1->FIOPIN & KEY1_PIN) { // Check if button 1 is pressed LED_Off(); // Turn off LED TM1638_DisplayString(" "); // Clear display RTC_ClearAlarm(); // Clear alarm interrupt flag break; } delay_ms(100); // Wait for button debounce } } } int main(void) { uint32_t year, month, day, hour, minute, second; uint32_t alarm_hour = 12, alarm_minute = 0, alarm_second = 0; LPC_GPIO1->FIODIR |= LED_PIN; // Set LED pin to output LPC_GPIO1->FIODIR &= ~(KEY1_PIN | KEY2_PIN | KEY3_PIN); // Set button pins to input TM1638_Init(); // Initialize TM1638 RTC_Init(); // Initialize RTC RTC_SetTime(0, 0, 0); // Set initial time to 00:00:00 RTC_SetDate(2022, 1, 1); // Set initial date to 2022-01-01 RTC_SetAlarm(alarm_hour, alarm_minute, alarm_second); // Set initial alarm NVIC_EnableIRQ(RTC_IRQn); // Enable RTC interrupt while (1) { RTC_GetTime(&hour, &minute, &second); // Get current time RTC_GetDate(&year, &month, &day); // Get current date char str[10]; sprintf(str, "%02d%02d%02d", hour, minute, second); TM1638_DisplayString(str); // Display time on TM1638 delay_ms(1000); // Wait for 1 second if (LPC_GPIO1->FIOPIN & KEY2_PIN) { // Check if button 2 is pressed alarm_hour = (alarm_hour + 1) % 24; // Increment alarm hour RTC_SetAlarm(alarm_hour, alarm_minute, alarm_second); // Update alarm while (LPC_GPIO1->FIOPIN & KEY2_PIN); // Wait for button release } if (LPC_GPIO1->FIOPIN & KEY3_PIN) { // Check if button 3 is pressed alarm_minute = (alarm_minute + 1) % 60; // Increment alarm minute RTC_SetAlarm(alarm_hour, alarm_minute, alarm_second); // Update alarm while (LPC_GPIO1->FIOPIN & KEY3_PIN); // Wait for button release } } } ``` 该代码实现了万年历的基本功能,包括显示时间和日期、修改时间、设置闹钟、触发报警中断、手动关闭提醒。需要注意的是,该代码仅作为示例,实际应用中可能需要进一步优化和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值