3.7 C库函数与启动文件
1.启动文件
这个链接器会自动将启动文件连接到程序之前,并将标准库libcavr.a与程序相连接,启动文件根据目标MCU的不同在crtavr.o和crtatmega.o中任意选择一个。启动文件定义了一个全局符号_start,它也是程序的起点。启动文件的功能有:
1)初始化硬件和软件堆栈指针。
2)从idata区复制初始化数据到直接寻址数据区data区。
3)将bss区全部初始化为零。
4)调用用户主例程main函数。
5)定义一个退出点,如果主函数main()一旦退出,它将进入这个退出点进行无限循环。
启动文件也定义了复位向量,不需要修改启动文件来使用别的中断,具体可参考中断操作部分。
为修改和使用新的启动文件:
cd \icc\libsrc.avr;进入安装的编译器路径
<edit crtavr.s>;编辑修改crtavr.s文件
<open crtavr.s using the IDE>;用IDE打开crtavr.s文件
<Choose"Compile File To->Object">;选择编译到目标文件,创建一个新的crtavr.o copy crtavr.o..\lib;复制到库目录
如果使用的目标MCU是Mega,应该用“crtatmega”代替“crtavr”,注意Mega的每个中断入口地址使用两个字,而非Mega芯片每一个中断入口地址使用一个字。
也可以有多个启动文件,可以在工程选项对话框中很方便地直接指定一个启动文件加入工程中。注意,必须指定启动文件的绝对路径或启动文件必须位于工程选项库路径所指定的目录中。
2.常用库介绍
库源代码,这个库源代码(默认路径为c:\icc\libsrc.avr\libsrc.zip)是一个密码保护的ZIP压缩文件,可以从互联网上任意下载一个UNZIP程序进行解压缩。本软件被开锁后,密码显示在“About”对话框中,例如:unzip-s libsrc.zip;unzip提示输入密码。
VR特殊函数,ICCAVR有许多访问UART、EEPROM和SPI的函数,堆栈检查函数对检测堆栈是否溢出很有用。另外互联网上有一个页专门存放用户写的源代码。
io*.h(io2313.h,io8515.h,iom603.h等),这些文件是从ATMEL官方公开的定义IO寄存器的源文件经过修改得到的,应该用这些文件来代替旧的avr.h文件。
PORTB=1;
uc=PORTA;
macros.h这个文件包含了许多有用的宏和定义。
还有其他头文件,下列标准的C头文件是被支持的,如果程序使用了头文件所列出的函数,那么包含头文件是一个好习惯,在使用浮点数和长整型数的程序中必须用#include预编译指令包含这些函数原形的头文件。读者可参考返回非整型值的函数。
assert.h:assert(),声明宏。
ctype.h:字符类型函数。
float.h:浮点数原形。
limits.h:数据类型的大小和范围。
math.h:浮点运算函数。
stdarg.h:变量参数表。
stddef.h:标准定义。
stdio.h:标准输入输出(IO)函数。
stdlib.h:包含内存分配函数的标准库。
string.h:字符串处理函数。
3.字符类型库
下列函数按照输入的ACSⅡ字符集字符分类,使用这些函数之前应当用“#include<ctype.h>”包含。
int isalnum(int c)
如果c是数字或字母返回非零数值,否则返回零。
int isalpha(int c)
如果c是字母返回非零数值,否则返回零。
int iscntrl(int c)
如果c是控制字符(如FF、BELL、LF等)返回非零数值,否则返回零。
int isdigit(int c)
如果c是数字返回非零数值,否则返回零。
int isgraph(int c)
如果c是一个可打印字符而非空格返回非零数值,否则返回零。
int islower(int c)
如果c是小写字母返回非零数值,否则返回零。
int isprint(int c)
如果c是一个可打印字符返回非零数值,否则返回零。
int ispunct(int c)
如果c是一个可打印字符而不是空格、数字或字母返回非零数值,否则返回零。
int isspace(int c)
如果c是一个空格字符返回非零数值,包括空格CR、FF、HT、NL和VT,否则返回零。
int isupper(int c)
如果c是大写字母返回非零数值,否则返回零。
int isxdigit(int c)
如果c是十六进制数字返回非零数值,否则返回零。
int tolower(int c)
如果c是大写字母则返回c对应的小写字母,其他类型仍然返回c。
int toupper(int c)
如果c是小写字母则返回c对应的大写字母,其他类型仍然返回c。
4.浮点运算库
下列函数支持浮点数运算,使用这些函数之前必须用#include<math.h>包含。
float asin(float x) 以弧度形式返回x的反正弦值
float acos(float x) 以弧度形式返回x的反余弦值
float atan(float x) 以弧度形式返回x的反正切值
float atan2(float x,float y) 返回y/x的反正切,其范围在-π~+π之间
float ceil(float x) 返回对应x的一个整型数,小数部分四舍五入
float cos(float x) 返回以弧度形式表示的x的余弦值
float cosh(float x) 返回x的双曲余弦函数值
float exp(float x) 返回以e为底的x的幂,即ex
float exp10(float x) 返回以10为底的幂,即10x
float fabs(float x) 返回x的绝对值
float floor(float x) 返回不大于x的最大整数
float fmod(float x,float y) 返回x/y的余数
float frexp(float x,int*pexp)把浮点数x分解成数字部分y(尾数)和以2为底的指数n两个部分,即x=y2n,y的范围为0.5≤y≤1,y值被函数返回,而n值存放到pexp指向的变量中
float fround(float x) 返回最接近x的整型数
float ldexp(float x,int exp) 返回x·2exp
float log(float x) 返回x的自然对数
float log10(float x) 返回以10为底的x的对数
float modf(float x,float*pint)把浮点数分解成整数部分和小数部分,整数部分存放到pint指向的变量,小数部分应当大于或等于0而小于1,并且作为函数返回值返回
float pow(float x,float y) 返回xy值
float sqrt(float x) 返回x的平方根
float sin(float x) 返回以弧度形式表示的x的正弦值
float sinh(float x) 返回x的双曲正弦函数值
float tan(float x) 返回以弧度形式表示的x的正切值
float tanh(float x) 返回x的双曲正切函数值
5.标准输入输出库
标准的文件输入输出是不能真正植入微控制器(MCU)的,标准stdio.h的许多内容不可以使用,不过有一些IO函数是被支持的,同样使用之前应用“#include<stdio.h>”预处理,并且需要初始化输出端口,最底层的IO程序是单字符的输入(getchar)和输出(put-char)程序,如果针对不同的装置使用高层的IO函数,例如用printf输出LCD,需要全部重新定义最底层的函数。
为了在ATMEL的AVR Studio模拟器(终端IO窗口)使用标准IO函数,应当在编译选项中选中相应的单选按钮。
注意:作为默认,单字符输出函数putchar是输出到UART装置没有修改,无论如何为使输出能如期望的那样出现在程序终端窗口中,‘\n’字符必须被映射为成对的回车和换行(CR/LF)。
int getchar()使用查寻方式从UART返回一个字符
int printf(char*fmt,..)按照格式说明符输出格式化文本frm字符串,格式说明符是标准格式的一个子集
%d:输出有符号十进制整数;
%o:输出无符号八进制整数;
%x:输出无符号十六进制整数;
%X:除了大写字母使用‘A’~‘F’外,同%x;
%u:输出无符号十进制整数;
%s:输出一个以C中空字符NULL结束的字符串;
%c:以ASCII字符形式输出,只输出一个字符;
%f:以小数形式输出浮点数;
%S:输出在FLASH存储器中的字符串常量。
printf支持3个版本,取决于特别需要和代码的大小(越高的要求,代码越大):
基本型:只有%c、%d、%x、%u和%s格式说明符是承认的;
长整型:针对长整型数的修改%ld、%lu、%lx被支持,以适用于精度要求较高的领域;
浮点型:全部格式包括%f被支持。
使用编译选项对话框来选择版本,代码大小的增加是值得关注的。
int putchar(int c)输出单个字符,这个库程序使用了UART以查寻方式输出单个字符,注意输出‘\n’字符至程序终端窗口。
int puts(char*s)输出以NL结尾的字符串。
int sprintf(char*buf,char*fmt)按照格式说明符输出格式化文本frm字符串到一个缓冲区,格式说明符同printf()。
“const char*”支持功能,cprintf和csprintf是将FLASH中的格式字符串分别以prinf和sprintf形式输出。
6.标准库和内存分配函数
标准库头文件<stdlib.h>定义了宏NULL和RAND_MAX以及新定义的类型size_t,并且描述了下列函数。注意在调用任意内存分配程序(比如calloc、malloc和realloc)之前,必须调用_NewHeap来初始化堆heap。
int abs(int i) 返回i的绝对值。
int atoi(char*s) 转换字符串s为整型数并返回它,字符串s起始必须是整型数形式字符,否则返回0。
double atof(const char*s) 转换字符串s为双精度浮点数并返回它,字符串s起始必须是浮点数形式字符串。
long atol(char*s) 转换字符串s为长整型数并返回它,字符串s起始必须是长整型数形式字符,否则返回0。
void*calloc(size_t nelem,size_t size) 分配“nelem”个数据项的内存连续空间,每个数据项的大小为size字节并且初始化为0。如果分配成功返回分配内存单元的首地址,否则返回0。
void exit(status) 终止程序运行,典型的是无限循环,它是作为用户main函数的返回点。
void free(void*ptr) 释放ptr所指向的内存区。
void*malloc(size_t size) 分配size字节的存储区,如果分配成功则返回内存区地址。如内存不够分配则返回0。
void_NewHeap(void*start,void*end) 初始化内存分配程序的堆,一个典型的调用是将符号_bss_end+1的地址用作“start”值,符号_bss_end定义为编译器用来存放全局变量和字符串的数据内存的结束,加1的目的是堆栈检查函数,使用_bss_end字节存储为标志字节,这个结束值不能被放入堆栈中。
extern char_bss_end;
_New Heap(&_bss_end+1,&_bss_end+201);
初始化200B大小的堆
int rand(void) 返回一个在0和RAND_MAX之间的随机数。
void*realloc(void*ptr,size_t size) 重新分配ptr所指向内存区的大小为size字节,size可比原来大或小,返回指向该内存区的地址指针。
void srand(unsigned seed) 初始化随后调用的随机数发生器的种子数。
long strtol(char*s,char**endptr,int base)
按照“base.”的格式转换“s”中起始字符为长整型数,如果“endptr”不为空,*endptr将设定“s”中转
换结束的位置。
unsigned long strtoul(char*s,char**endptr,int base)
除了返回类型为无符号长整型数外,其余同“strtol”。
7.字符串函数
用“#include<string.h>”预处理后,编译器支持下列函数。<string.h>定义了NULL类型size_t和下列字符串及字符阵列函数。
*memchr(void*s,int c,size_t n)
在字符串s中搜索n个字节长度寻找与c相同的字符,如果成功返回匹配字符的地址指针,否则返回NULL。
int memcmp(void*s1,void*s2,size_t n)
对字符串s1和s2的前n个字符进行比较,如果相同则返回0,如果s1中字符大于s2中字符则返回1,如果s1中字符小于s2中字符,则返回-1。
void*memcpy(void*s1,void*s2,size_t n)
复制s2中n个字符至s1,但复制区不可以重叠。
void*memmove(void*s1,void*s2,size_t n)
复制s2中n个字符至s1,返回s1,其与memcpy基本相同,但复制区可以重叠。
void*memset(void*s,int c,size_t n)
在s中填充n个字节的c,返回s。
char*strcat(char*s1,char*s2)
复制s2到s1的结尾,返回s1。
char*strchr(char*s,int c)
在s1中搜索第一个出现的c,包括结束NULL字符。如果成功,返回指向匹配字符的指针,如果没有匹配字符找到,返回空指针。
int strcmp(char*s1,char*s2)
比较两个字符串,如果相同返回0,如果s1>s2则返回1,如果s1<s2则返回-1。
char*strcpy(char*s1,char*s2)
复制字符串s2至字符串s1,返回s1。
size_t strcspn(char*s1,char*s2)
在字符串s1搜索与字符串s2匹配的第一个字符,包括结束NULL字符,其返回s1中找到的匹配字符的索引。
size_t strlen(char*s)
返回字符串s的长度,不包括结束NULL字符。
char*strncat(char*s1,char*s2,size_t n)
复制字符串s2(不含结束NULL字符)中n个字符到s1,如果s2长度比n小,则只复制s2,返回s1。
int strncmp(char*s1,char*s2,size_t n)
基本和strcmp函数相同,但其只比较前n个字符。
char*strncpy(char*s1,char*s2,size_t n)
基本和strcpy函数相同,但其只复制前n个字符。
char*strpbrk(char*s1,char*s2)
基本和strcspn函数相同但它返回的是在s1匹配字符的地址指针,否则返回NULL指针。
char*strrchr(char*s,int c)
在字符串s中搜索最后出现的c,并返回它的指针。否则返回NULL。
size_t strspn(char*s1,char*s2)
在字符串s1搜索与字符串s2不匹配的第一个字符,包括结束NULL字符,其返回s1中找到的第一个不匹配字符的索引。
char*strstr(char*s1,char*s2)
在字符串s1中找到与s2匹配的子字符串,如果成功,它返回s1中匹配子字符串的地址指针,否则返回NULL。
“const char*”支持函数
这些函数除了它的操作对象是在FLASH中常数字符串外,其余同c中的函数。
size_t cstrlen(const char*s);
char*cstrcpy(char*dst,const char*src);
int cstrcmp(const char*s1,char*s2);
8.变量参数函数
<stdarg.h>提供再入式函数的变量参数处理,它定义了不确定的类型va_list和3个宏。
va_start(va_list foo,<last_arg>) 初始化变量foo。
va_arg(va_list foo,<promoted type>) 访问下一个参数,分派指定的类型,注意那个类型必须是高级类型,如int、long或double。小的整型类型如“char”不能被支持。
va_end(va_list foo) 结束变量参数处理。
例如,printf()可以使用vfprintf()来实现。
9.堆栈检查函数
有几个库函数是用于检查堆栈是否溢出。如果硬件堆栈增长到软件堆栈中,那么软件堆栈的内容将会被改变。也就是说局部变量和别的堆栈项目被改变,硬件堆栈是用作函数的返回地址。如果函数调用层次太深,偶尔会发生这种情况。
同样地,软件堆栈溢出进数据区域将会改变全局变量或其他静态分配的项目(如果使用动态分配内存,还会改变堆项目)。这种情况在定义了太多的局部变量或一个局部集合变量太大时也会偶尔发生这种情况,如图3-14所示。
图3-14 堆栈情况图