S3C2440开发工具realview MDK4.22之库的使用

发布者:数字冒险最新更新时间:2016-06-12 来源: eefocus关键字:S3C2440  开发工具  realview  MDK4.22 手机看文章 扫描二维码
随时随地手机看文章
一。与c库会强制链接

如果你写了一个c程序,必然会和c库链接,尽管你没有直接使用c库函数。这是因为编译器为了改进程序,可能隐含的产生了对c库函数调用。

即便你的程序没有main()函数,也只是说c库没变初始化而已,一些c库函数仍然可以使用并且编译器可以隐含地调用这些函数。

 

二。ARM的c的运行时库

c标准库由以下组成:

ISO99标准库定义的所有函数。

依赖于目标的函数,用来在semihosted环境中执行c库函数。你可以在你的应用程序中重定义这些函数。

被编译器隐含调用的函数。

由ARM扩展的,但是不是由ISO C定义,且包含在这个库里的。

 

c微型库由以下组成(可以代替c标准库,它是非常适合只有小容量内存的深度嵌入式应用):

为了达到最小的代码体积,已经高度优化的函数。

不服从ISO C规范的函数。

不服从1985 IEEE 754 标准

 

三。c库的特性

c库用标准的ARM semihosted 环境提供一些功能,不如说输入、输出。该环境由ARM RVI调试单元和Real-Time Simulator Model/(RTSM)所支持的。

你可以重新定义任何与设备相关的c库函数,写在自己的应用程序中即可。这允许你裁剪c库以应用在自己的执行环境中。

也可以裁剪与设备无关的函数,以适应自己的特殊应用要求。例如malloc簇,ctype簇,所有的locale-specific函数。

许多c库函数与其他函数是独立的并且是目标无关的,你可以简单的从库里利用这些函数。

c库函数负责以下事情:

1.创建一个c环境的执行环境(创建栈,如果需要创建堆,初始化程序里用到的库的部分)

2.开始执行main()

3.支持ISO C定义的函数

4.捕获允许错误、信号,根据情况终止程序或者退出程序

 

四。ARM C库的堆使用需求

任何暗含的使用堆或者显示调用使用堆都需要事先准备好堆。

在c标准库里,暗含的堆使用发生在:

1.调用库函数fopen()并且一个I/O操作第一次使用这个fopen()所产生的stream

2.传递命令行参数给main()函数

分配80字节堆作为FILE结构体的存储空间。当第一次I/O操作发生,且直到这个操作产生,一个附加的512字节堆空间分配给与该操作相关的buffer区。可以利用setvbuf()重新

定义堆大小。

当fclose()调用的时候,80字节未被释放,保存在一个自由表中以备再次使用。512字节在fclose()时候被释放了。

声明带参数的main()需要256字节的堆空间。这个内存一直未释放,因为要在main()期间有效。在microlib里,不允许声明带参的main(),因此这个用法仅使用于标准库。在

标准库的环境里,如果存在堆,它就可以生效。

 

五。c库基于不同的build options会选择不同的目标集

比如说,目标架构,指令集(ARM,Thumb,Thumb-2);字节须(大小端);浮点支持(softVFP,VFP);位置无关

 

六。Thumb C 库

当连接器探测到以下事情发生的时候,会自动连接Thumb C库:

1.Thumb or Thumb-2 or --thumb选项 or #pragma thumb

2.在ARMv4T下,使用--apcs / interwork

3.ARMv6-M   Cortex-M1/MO

4.ARMv7-M   Cortex-M3

 

七。ARM C库和多线程

当你使用RTOS,ARM C库支持多线程

 

八。__user_libspace

__user_libspace为C库保持了静态数据。这是一个96字节,0初始化的数据块,该块由C库创建。在C库初始化期间可以用来当做临时栈。

默认的ARM C库用__user_libspace区域保持以下内容:

1.errno,由可以设置errno的函数使用。默认,__rt_errno_addr()返回指向errno的指针。

2.浮点状态字,用于软件浮点。在硬件浮点里,不使用。默认,__re_fp_status_addr()返回指向FP状态字的指针。

3.一个指向堆基址的指针,也就是__Heap_Descriptor,被所有malloc-相关的函数使用。

4.当前的locale设置,被像setlocale()函数使用,同时也被所有依赖于它们的其它函数使用。

 

九。在一个程序中使用库

可以用以下方法在应用程序使用C库:

1.建立一个semihosting应用,它可以在一个semihosted环境中调试,比如说利用RV。

2.建立一个非host的应用,它可以嵌入到ROM中。

3.建立一个不包含main()的应用,且不初始化库。该应用具有非常有限的库功能,除非重定义一些函数。

 

十。在一个semihosting环境利用C库

如果你开发一个应用,为了调试,运行在semihosted环境,你必须有一个执行环境,可以支持ARM 或者 Thumb semihosting,且有足够的存储空间。

1.用标准的半主机功能,默认情况下,在RVI中会提供。

2.为半主机调用执行自己的处理过程。

如果使用默认的C库的半主机功能,不必重写任何函数和头文件。ARM调试代理支持半主机,但是C库所假定的内存格局需要裁减以匹配正在调试的硬件。

 

十一。使用$sub$$使用半主机和非半主机I/O功能

例如,fputc()的实现是直接往UART写,还有1个fputc()的实现是半主机的。可以提供这两个版本,具体要看传递到函数的FILE指针的性质。

 

int $Super$fputc(int c, FILE *fp);
int $Sub$fputc(int c, FILE *fp)
{
    if (fp == (FILE *)MAGIC_NUM) // where MAGIC_NUM is a special value that
    {                            // is different to all normal FILE * pointer
                                 // values.
        write_to_UART(c);
        return c;
    }
    else
    {
        return $Super$fputc(c, fp);
    }
}
可以看到根据FILE指针判断是哪种类型,$$super$代表正常的,$$sub$代表特殊的,一般和自己的目标硬件相关。

 

 

十二。以非半主机模式使用库

一些C库函数使用半主机。如果不想使用的话,下列方式之一可以实现:

1.移除所有的半主机函数调用

2.重定义低层函数,比如fputc()。不必重定义所有的半主机函数。只是重定义在程序里需要使用到的函数。

有些函数虽然不直接与目标依赖,但是它依赖于某些底层函数,而底层函数依赖于目标。例如,printf(),你必须重定义fputc(),如果不用得话,那就不必重定义了。

3.以自己特别的方式实现所有半主机调用的一种方式。一个作为这种处理的例子是,拦截调用,将它们重指向自己的非主机实现,也就是,具体目标的函数。

IMPORT __use_no_semihosting from ASM

#pragma import(__use_no_semihosting) from C

 

十三。直接的半主机依赖

S3C2440开发工具realview MDK4.22之库的使用
 

 

 

十四。间接的半主机依赖

S3C2440开发工具realview MDK4.22之库的使用
 

 

十五。建立一个不使用C库的应用程序

如果程序中不包含main(),那么库不会被初始化,且如下功能不可用:

1.带有_sys_前缀的底层stdio函数

2.信号处理函数

3.其它函数,如atexit()

有些函数即便库未初始化也能使用,一些其它不可用的函数也能被使用,除非它们所依赖的库函数被重定义。

 

十六。裸机代码

如果你写一个程序,里面没有使用库,且没有任何环境初始化,你必须:

1.如果用到heap的话,重定义__rt_raise()

2.不定义main()

3.写一个汇编语言代码建立c语言运行需要的环境,必须要跳转到你c函数的入口

4.提供自己的RW/ZI初始化代码

5.确保你的汇编代码在你的重启处理段

6.用--fpu=none

当满足这些要求,连接器用合适的C库变体寻找任何需要编译的函数,它们是隐形调用的。

尽管没有main(),__user_lbspace()被自动创建,占用96字节的ZI段。

 

十七。定制C库启动代码且访问C库函数

应用程序里不需要C库,如果你程序里有main(),连接器会自动包含初始化代码。可能存在一些情况,这是不必的。例如,一个运行RTOS系统可能通过RTOS启动代码

有它自己的执行环境配置。

你可以创建一个应用包含自己的启动代码且仍然可以使用库函数的大部分功能。你可以做下列之一的选择:

1.避免使用需要初始化的函数

2.提供初始化和底层函数函数实现

 

十八。利用C库使用底层函数

如果你的应用中没有main(),而使用库的话,必须重定义一些在库里的函数

注意:如果用heap的话,__rt_raise()是必须的。

 

十九。利用库中的高层函数

如果低层的函数被重定义了,那么高层I/O函数能被使用。高层函数是像fprintf() printf() scanf() puts()等,低层函数是那些如fputc() fgetc() 以及__backspace()函数。

大多数格式化输出函数也需要调用setlocale()。

 

二十。利用库中的malloc()函数

如果在裸机C代码中需要堆得支持,那么__init_alloc()必须要首先调用支持堆的边界初始化,__rt_heap_extend()必须要提供,尽管返回失败。如果没有__rt_heap_extend()函数,特定的库功能会被包含进去,这会导致裸机C代码出现问题。

 

二十一。执行环境的初始化和程序的执行

一个程序的入口是位于C库里的__main,它会做如下事情:

1.拷贝非根区代码从它们的加载地址到运行地址。如果任何数据段被压缩了得话,它们也会被解压缩。

2.用0初始化ZI区。

3.跳转到__rt_entry。

 

如果你不想C库做这些事情,你可以定义自己的__main,__main里可以跳转到__rt_entry。例如:

 

 IMPORT __rt_entry
  EXPORT __main
  ENTRY
__main
  B  __rt_entry
  END

 

 

库函数__rt_entry像下面这样运行程序:

1.建立堆和栈通过以下方式之一:调用__user_setup_srackheap(),调用__rt_stackheap_int()或加载scatter文件里的绝对地址。

2.调用__rt_lib_init()初始化引用的库函数,初始化locale,如果必要的话,为main()建立argc和argv。

3.调用main(),用户级的根函数。

4.从main()函数起,你的程序可能调用库函数。

5.利用main()返回的数值作为参数调用exit()函数。

 

二十二。在main()里调用库函数

.main()是用户级的根函数。它需要已经初始化了的执行环境和可以使用的输入输出函数。在main()里程序可能执行下列的动作之一,下列动作调用了用户定制的C库函数:

1.扩展堆,栈。

2.调用库函数。该库函数需要调用用户定义的函数。例如__rt_fp_status_addr() 或者clock()。

3.调用使用locale或CTYPE的库函数。

4.执行需要浮点单元或者浮点库支持的浮点运算。

5.通过低层函数的输入输出函数,如putc()或间接通过高层函数,如fprintf()等。

6.发起一个错误或其他信号,例如ferror。

 

二十三。修改用于错误信号,错误处理和程序退出的C库修改

所有由C库发起的陷阱或错误信号都是通过__raise()函数。可以重定义这个函数或者它所使用到的低层函数。

 

二十四。避免使用堆和使用堆的库函数

 

IMPORT __use_no_heap from assembly language

#pragma import(__use_no_heap) from C.

二十五。在裸机C代码里使用heap

 

1.调用__init_alloc(base, top)

2.定义函数unsigned  _rt_heap_extend(unsigned size, void block)处理扩展堆得需要。

 

二十六。栈的初始化和堆界限

可以指定栈指针,指定哪一块区域为堆,用以下任何一种方式:

1.定义__initial_sp,如果需要堆, 定义__heap_base and __heap_limit

2.用scatter文件,下列方式之一

   2.1定义ARM_LIB_STACK and ARM_LIB_STACK区

   2.2不用堆,只需ARM_LIB_STACK

   2.3定义一个ARM_LIB_STACKHEAP,此时堆栈是一体的,相向生长。

微库仅支持上述2种方式。

3.实现__user_setup_stackheap()建立栈指针和返回初始堆区域。

4.用遗留的__user_initial_stackheap()也可以实现

初始化堆指针必须指向的时8字节倍数对齐的区域。

默认情况下,潜在的堆栈冲突会被自动探测到且请求的堆分配失败。如果不希望自动的冲突检测,可以通过用#pragma import __use_two_region_memory预留一小段空间。

5.作为遗留的原因,也可以使用__rt_stackheap_init()和__rt_heap_extend()。

 

二十七。C库中对低层函数的依赖

表中显示了高层函数对低层函数的依赖,如果定义了自己的低层函数版本,可以直接使用高层函数库中的版本。

fgetc()用__FILE,但是fputc()用__FILE和ferror()。

必须提供__stdin和__stdout的定义,如果使用和它们相关的高层函数。虽然重定义了其它函数,如fgetc()和fputc(),它们没有引用任何在__stdin __stdout里的数据。

 

Table key:

  1. __FILE, the file structure.

  2. __stdin, the standard input object of type __FILE.

  3. __stdout, the standard output object of type __FILE.

  4. fputc(), outputs a character to a file.

  5. ferror(), returns the error status accumulated during file I/O.

  6. fgetc(), gets a character from a file.

  7. fgetwc()

  8. fputwc()

  9. __backspace(), moves the file pointer to the previous character.

  10. __backspacewc().

如果选择重定义fgetc() fputc() __backspace() 要认识到fopen和相关的函数使用ARM默认的FILE结构。如果重定义了__FILE,那么也必须重定义fopen()和相关的函数。

C库输出函数族仅依赖fputc() ferror();C库输入函数族仅依赖于fgetc() __FILE __backspace()。

S3C2440开发工具realview MDK4.22之库的使用
 

S3C2440开发工具realview MDK4.22之库的使用
 

 

二十八。重定义低层函数,以便直接使用高层函数库函数

如果你重定义了__FILE  fputc() ferror() __stdout可以使用所有的printf()函数族。

 

Example 9. Retargeting printf()

#include 

struct __FILE
{
  int handle;

  /* Whatever you require here. If the only file you are using is */
  /* standard output using printf() for debugging, no file handling */
  /* is required. */
};

/* FILE is typedef’d in stdio.h. */

FILE __stdout;

int fputc(int ch, FILE *f) 
{
  /* Your implementation of fputc(). */
  return ch;
}
int ferror(FILE *f)
{
  /* Your implementation of ferror(). */
  return 0;
}
void test(void)
{
  printf("Hello world\n");
}


 

二十九。

__backspace()被scanf()函数族使用。

__backspace()必须仅在从流里读取一个字符后调用。

 
关键字:S3C2440  开发工具  realview  MDK4.22 引用地址:S3C2440开发工具realview MDK4.22之库的使用

上一篇:S3C2440开发工具realview MDK4.22使用入门
下一篇:S3C2440在MDK4.22下使用printf向串口打印调试

推荐阅读最新更新时间:2024-03-16 14:56

s3c2440学习之路-001 汇编点亮led
1. 原理分析 2. 主要流程 3. 源码 4. dis文件分析 硬件平台:jz2440 软件平台:Ubuntu16.04 arm-linux-gcc-3.4.5 1.原理分析 点亮LED最简单的方法就是给二极管正负极接上电,中间串一个电阻 图1 点亮LED 由于2440芯片Pin脚的驱动能力不够,所以无法直接用Pin脚来点亮LED,只能把Pin脚连接到LED的负极,充当开关的作用。 当Pin脚为高电平时,LED两端无电压差,LED灭 当Pin脚为低电平时,LED两端有电压差,LED亮 图2 2440连接LED 2.主要流程 2.1原理图介绍 这里只介绍LED1, LED1负极与2440的GPF4相连,中
[单片机]
<font color='red'>s3c2440</font>学习之路-001 汇编点亮led
基于ARM的液压系统智能数据采集终端硬件设计
  针对液压系统的特点,设计了基于ARM 的智能数据采集终端系统。该系统通过传感器对油压、流量和温度3 类信号进行采集,调理后的数据经过ARM 处理器S3C2440 进行处理和压缩,压缩后的数据利用GTM900C 无线传输模块远程传输。整个系统硬件电路分为主控电路部分、数据采集部分和无线传输部分。   液压系统具有功率大、响应快及精度高等特点,已经广泛应用于冶金和制造领域。但其故障又具有隐蔽性、多样性、不确定性及因果关系复杂等特点,故障出现后不易查找原因,而且故障发生会带来巨大的经济损失。通常,液压系统只能靠定期检查和维护来排除故障,这种方法有一定的滞后性。因此需要实时监测液压系统的状态数据并及时分析以减少故障率,确保工程机械正
[单片机]
基于ARM的液压系统智能数据采集终端硬件设计
s3c2440之外部中断
对s3c2440的硬件操作无非就是配置寄存器,中断也不例外: 需要设置的寄存器: GPGCON :引脚配置寄存器,设置为第二功能,中断引脚; EINTPEND:中断挂起寄存器,当有中断发生且没有被屏蔽,相应位会自动置1,在进入中断服务程序后必须用软件将其相应位清0以免发生错误中断; SRCPND:源挂起寄存器由32位组成,其每一位都涉及一个中断源。如果中断源产生了中断则相应的位被置1并且等待中断服务。此寄存器指示出是哪个中断源正在等待请求服务。 注意:此寄存器不顾INTMAST的屏蔽位,由硬件自动将相应中断位置1,在进入中断服务程序后必须通过写1清除相应位,以防发生错误中断。 EINTMASK:外部中断屏蔽
[单片机]
<font color='red'>s3c2440</font>之外部中断
三ARM9(S3C2440)的串口UART——程序实例讲解
串口通信程序编写步骤 UART通信程序可以采用查询、中断和DMA模式。我们通过使用较多的中断方式来介UART通信程序的编写。简单做法是,UART通信程序的编写参照例子程序。 选通道,通过函数Uart_Select();选UART0~UART2; 选波特率和波特率发生器时钟,选波特率通过函数Uart_Pclk_En(int ch, int baud)或Uart_Pclk_En(int ch, int baud)来进行。时钟选UCLK ,rUCON0|=0x400;时钟选PCLK ,rUCON0&=0x3ff。 通信协议(rULCON0)设定,如果正常通信,一位停止位,8位数据位,无奇偶效验: rULCON0=(0 6
[单片机]
s3c2440头文件之2440slib.h
//=================================================================== // File Name : 2440lib.h // Function : S3C2440 // Date : February 26, 2002 // Version : 0.0 // History // 0.0 :Feb.20.2002:SOP : Programming start // 0.01:Mar.29.2002:purnnamu: For Sleep_wake_up, the START... label is added //==================
[单片机]
S3C2440-启动分析
本文是我对bootloader中2440init.s文件的一些理解,详细注释了一下,希望对大家有所帮助,下一步我准备移植一下uboot。 ;========================================= ; NAME: 2440INIT.S ; DESC: C start up codes ; Configure memory, ISR ,stacks ; Initialize C-variables ; HISTORY: ; 2002.02.25:kwtark: ver 0.0 ; 2002.03.20:purnnamu: Add some functions for testing STOP
[单片机]
LINUX中s3c2440总线频率、时钟的设置
很多硬件的正常运行需要有总线时钟的支持,比如LCD、I2C等设备。本文分析一下s3c2440的总线时钟,以及在linux中对s3c2440总线时钟频率的相关操作。首先分析硬件s3c2440的总线时钟。 1. s3c2440的FCLK HCLK PCLK: 时钟源首先来自外部晶振12MHz。对于必须运行在200MHz以上的ARM920t内核来说,这个频率实在太低了,不能直接使用,所以首先要通过s3c2440片上的pll硬件电路将12MHz的晶振时钟信号升频,而具体升到多少MHz是通过MPLL控制寄存器来控制的。比如MPLLCON赋值为 0x5c 12 | 2 4 | 1就可以将PLL电路的输出时钟设置为400MHz,也就是将
[单片机]
基于S3C2440的测试系统数字稳压电源设计
0 引言 直流稳压电源是一种比较常见的电子设备,一直被广泛地应用在电子电路、实验教学、科学研究等诸多领域。近年来,嵌入式技术发展极为迅速,出现了以单片机、嵌入式ARM为核心的高集成度处理器,并在自动化、通信等领域得到了广泛应用。电源行业也开始采用内部集成资源丰富的嵌入式控制器来实现数字稳压电源的控制系统。数字稳压电源是用脉宽调制波(PWM)来控制MOS管等开关器件的开通和关闭,从而实现电压电流的稳定输出。数字稳压电源还具备自诊断功能,能实现过压过流保护、故障警告等。 相比之前的模拟电源,数字稳压电源大大减少了在模拟电源中常见的误差、老化、温度漂移、非线性不易补偿等诸多问题,提高了电源的灵活性和适应性。将SAMSUNG公司的嵌
[单片机]
基于<font color='red'>S3C2440</font>的测试系统数字稳压电源设计
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved