搜索
bottom↓
回复: 22

iccavr使用(抄录)

[复制链接]

出0入0汤圆

发表于 2005-10-5 15:45:35 | 显示全部楼层 |阅读模式
ICCAVR中文使用说明(1)



ImageCraft ICCAVR 的中文使用说明

ICCAVR 介绍

1 ImageCraft 的ICCAVR 介绍

ImageCraft 的ICCAVR 是一种使用符合ANSI 标准的C 语言来开发微控制器MCU

程序的一个工具它有以下几个主要特点

ICCAVR 是一个综合了编辑器和工程管理器的集成工作环境IDE 其可在

WINDOWS9X/NT 下工作

源文件全部被组织到工程之中文件的编辑和工程的构筑也在这个环境中完成编译

错误显示在状态窗口中并且当你用鼠标单击编译错误时光标会自动跳转到编辑窗口中

引起错误的那一行这个工程管理器还能直接产生您希望得到的可以直接使用的INTEL

HEX 格式文件INTEL HEX 格式文件可被大多数的编程器所支持用于下载程序到芯片

中去

ICCAVR 是一个32 位的程序支持长文件名

出于篇幅考虑本说明书并不介绍通用的C 语言语法知识仅介绍使用ICC AVR 所

必须具备的知识因此要求读者在阅读本说明书之前应对C 语言有了一定程度的理解

2 ICCAVR 中的文件类型及其扩展名

文件类型是由它们的扩展名决定的IDE 和编译器可以使用以下几种类型的文件

输入文件

.c 扩展名----表示是C 语言源文件

.s 扩展名----表示是汇编语言源文件

.h 扩展名----表示是C 语言的头文件

.prj 扩展名----表示是工程文件这个文件保存由IDE 所创建和修改的一个工程的有

关信息

.a 扩展名----库文件它可以由几个库封装在一起libcavr.a 是一个包含了标准C 的

库和AVR 特殊程序调用的基本库如果库被引用链接器会将其链接到您的模块或文件中

您也可以创建或修改一个符合你需要的库

输出文件

.s 对应每个C 语言源文件由编译器在编译时产生的汇编输出文件

.o 由汇编文件汇编产生的目标文件多个目标文件可以链接成一个可执行文件

.hex INTEL HEX 格式文件其中包含了程序的机器代码

.eep INTEL HEX 格式文件包含了EEPROM 的初始化数据

.cof COFF 格式输出文件用于在ATMEL 的AvrStudio 环境下进行程序调试

.lst 列表文件在这个文件中列举出了目标代码对应的最终地址

.mp 内存映象文件它包含了您程序中有关符号及其所占内存大小的信息

.cmd NoICE 2.xx 调试命令文件

.noi NoICE 3.xx 调试命令文件

.dbg ImageCraft 调试命令文件

3 附注和扩充

#pragma 编译附注

这个编译器接受以下附注

#pragma interrupt_handler <func1>:<vector number> <func2>:<vector> ...

这个附注必须在函数之前定义它说明函数func1 func2 是中断操作函数所以编译

器在中断操作函数中生成中断返回指令reti 来代替普通返回指令ret 并且保存和恢复函数

所使用的全部寄存器同样编译器根据中断向量号vector number 生成中断向量地址

#pragma ctask <func1> <func2>...

这个附注指定了函数不生成挥发寄存器来保存和恢复代码它的典型应用是在RTOS

实时操作系统中让RTOS 核直接管理寄存器

#pragma text:<name>

改变代码段名称使其与命令行选项相适应

#pragma data:<data>

改变数据段名称使其与命令行选项相适应这个附注在分配全局变量至EEPROM

中时必须被使用读者可参考访问EEPROM 的例子

#pragma abs_address:<address>

函数与全局数据不使用浮动定位重定位而是从<address>开始分配绝对地址这

在访问中断向量和其它硬件项目时特别有用

#pragma end_abs_address

结束绝对定位使目标程序使用正常浮动定位

C++ 注释

如果你选择了编译扩充(Project->Options->Compiler) 你可以在你的源代码中使用C ++

的 // 类型的注释

二进制常数

如果你选择了编译扩充(Project->Options->Compiler) 你可以使用0b<1|0>* 来指定二

进制常数例如0b10101 等于十进制数21

在线汇编

你可以使用asm("string")函数来指定在线汇编代码读者可参考在线汇编

4 代码转换

IAR 或其它ANSI C 编译系统的代码转换

IAR C 编译器作为应用于AVR 的第一个C 编译器它有十分丰富的源代码当你从IAR

编译系统转换到ImageCraft 编译系统时绝大多数符合ANSI C标准的程序代码不需要转

换IAR C 中IO 寄存器的定义与ICCAVR 也是相同的

中断操作描述ICCAVR 使用pragma 附注描述中断操作函数而IAR 引入了语法扩

充interrupt 关键字下面是一个对照

在ICCAVR 中

#pragma interrupt_handler func:4 // 4 是这个中断的向量号func 为中断处理函数名称

ICCAVR 可以使多个中断向量共用一个中断处理函数

在IAR 中

interrupt [vector_name] func() // vector_name 是某一个中断向量的名称IAR C 的中

断向量地址使用中断名称来代替以增加程序的可读性

扩充关键字

IAR 引入flash 关键字将项目分配进入程序存贮空间FLASH 存贮器ICCAVR 使用

const 关键字来达到相同的目的

过程调用转换

在两个编译系统之间函数参数传递使用的寄存器是不同的这仅影响手工写的汇编函



在线汇编宏等IAR 不支持在线汇编符号而ICCAVR 支持在线汇编



ICCAVR中文使用说明(2)



1 起步

自你启动IDE 后首先从Project 菜单系统选择Open 命令进入\icc\examples.avr 目

录并且选择并打开led 工程工程管理器显示在这个工程中只有一个文件 led.c 然

后从Project 菜单中选择Options 命令打开工程编译选项在"Target"标号下选择目标处理

器然后从Project 菜单中选择Make Project 命令IDE 将调用编译器编译这个工程文件

并且在状态窗口中显示所有的信息

如果没有错误在与源文件同一个目录在这个例子中是\icc\examples.avr 中输出

一个文件 led.hex 这个文件是INTEL HEX 格式大多数能支持AVR MCU 的编程器

和模拟器都支持这种格式并且能下载这个程序进入你的目标系统这样就完成了一个

程序的构筑

如果你希望用支持COFF 调试信息的工具来测试你的程序比如AVR Studio 那么

你需要从Project 菜单中选择Options 命令在编译标签下选择COFF 输出文件格式对

一些常用的功能你也可使用工具条或鼠标右键弹出菜单例如你可以在工程窗口单

击鼠标右键选择编译选项

在工程窗口中双击文件名IDE 将使用编辑器打开这个文件按这个方法打开led.c

作为试验可设置一些错误例如从一行中删除分号现在从Project 菜单中选择Make

Project 命令IDE 首先自动保存已经改变的文件并且开始编译这个文件这时在状态

窗口中会显示错误信息单击状态窗口中错误信息行或单击其左边的错误符号光标

将移到编辑器中错误行的下面一行上基本上所有C 编译器都是这样

开始一个新的工程

从Project 菜单中选择New 命令并且浏览至你希望输出工程文件的目录输出文

件的名称取决于你的工程文件名称例如如果你创建一个名称为foo.prj 的工程那么

输出文件名称为foo.hex 或foo.cof 等

自从创建你自己的工程后你可以开始写你的源代码(C 或汇编格式) 并且将这个

文件加入到工程文件排列中单击工具栏中Build 图标可以很容易地构筑这个工程

IDE 输出与ATMEL 的AVR Studio 完全兼容的COFF 文件你可以使用ATMEL 的AVR

Studio 来调试你的代码

为更容易地使用这个开发工具你可以使用应用程序向导来生成一些使用有关硬件

的初始化代码

2 C 程序的剖析

一个C 程序必须定义一个main 调用函数编译器会将你的程序与启动代码和库函

数链接成一个可执行文件因此你也可以在你的目标系统中执行它启动代码的用

途在启动文件中很详细地被描述了一个C 程序需要设定目标环境启动代码初始化这

个目标使其满足所有的要求

通常你的main 例程完成一些初始化后然后是无限循环地运行作为例子让我

们看 \icc\examples 目录中的文件led.c

#include <io8515.h>

/* 为使能够看清LED 的变化图案延时程序需要有足够的延时时间*/

void Delay()

{

unsigned char a, b;

for (a = 1; a; a++)

for (b = 1; b; b++)

;

}

void LED_On(int i)

{

PORTB = ~BIT(i); /* 低电平输出使LED 点亮 */

Delay();

}

void main()4

{

int i;

DDRB = 0xFF; /*定义 B口输出*/

PORTB = 0xFF; /* B 口全部为高电平对应LED 熄灭*/

while (1)

{

/*LED 向前步进 */

for (i = 0; i < 8; i++)

LED_On(i);

/* LED 向后步进 */

for (i = 8; i > 0; i--)

LED_On(i);

/* LED 跳跃*/

for (i = 0; i < 8; i += 2)

LED_On(i);

for (i = 7; i > 0; i -= 2)

LED_On(i);

}

}

这个main 例程是很简单的在初始化一些IO 寄存器后之后它运行在一个无限循

环中并且在这个循环中改变LED 的步进图案LED 是在LED_On 例程中被改变的

在LED_On 例程中直接写正确的数值到IO 端口因为CPU 运行很快为能够看见图案

变化LED_On 例程调用了延时例程因为延时的实际延时值不能被确定这一对嵌套循

环只能给出延时的近似延时时间如果这个实际定时时间是重要的那么这个例程应该

使用硬件定时器来完成延时

其它的例子8515intr.c 程序很简单但同样清楚地显示了如何用C 写一个中断处理

过程这两个例子可以作为你的程序的起点

四ICCAVR 的IDE 环境

1 编译一个单独的文件

正常建立一个输出文件的次序是你首先应该建立一个工程文件并且定义属于这个

工程的所有文件然而我们有时也需要将一个文件单独地编译为目标文件或最终的输

出文件这时可以这样操作从IDE 菜单File 中选择Compile File... 命令来执

行to Object 和to Output 中的任意一个当你调用这个命令时文件应该是打开的

并且在编辑窗口中可以编辑的

编译一个文件为目标文件to Object 对检查语法错误和编译一个新的启动文件是

很有用的编译一个文件为输出文件to Output 对较小的并且是一个文件的程序较为

有用注意这里使用默认的编译选项

2 创建一个新的工程

为创建一个新的工程从菜单Project 中选择New 命令IDE 会弹出一个对话

框在对话框中你可以指定工程的名称这也是你的输出文件的名称如果你使用一些

已经建立的源文件你可在菜单Project 中选择AddFile(s) 命令

另外你可以在菜单File 中选择New 命令来建立一个新的源文件来输入你的

代码你可以在菜单File 中选择Save 或Save As 命令来保存文件然后你可

以象上面所述调用AddFile(s) 命令将文件加入到工程中也可在当前编辑窗口中单击

鼠标右键选择Add to Project 将文件加入已打开的工程列表中通常你输出源文件在

工程同一个目录中但也可不作这样要求

工程的编译选项使用菜单中Project 中的Options 命令

3 工程管理

工程管理允许你将多个文件组织进同一个工程而且定义它们的编译选项这个特

性允许你将工程分解成许多小的模块当你处理工程构筑时只有一个文件被修改和重

新编译如果一个头文件作了修改当你编译包含这个头文件的源文件时IDE 会自动

重新编译已经改变的头文件

一个源文件可以写成C 或汇编格式的任意一种C 文件必须使用.c 扩展名汇

编文件必须使用.s 扩展名你可以将任意文件放在工程列表中例如你可以将一个

工程文档文件放在工程管理窗口中工程管理器在构筑工程时对源文件以外的文件不予

理睬

对目标器件不同的工程可以在编译选项中设置有关参数当你新建一个工程时

使用默认的编译选项你可以将现有编译选项设置成默认选项也可将默认编译选项装

入现有工程中默认编译选项保存在default.prj 文件中

为避免你的工程目录混乱你可以指定输出文件和中间文件到一个指定的目录通

常这个目录是你的工程目录的一个子目录

4 编辑窗口

编辑窗口是你与IDE 交流信息的主要区域在这个窗口中你可以修改相应的文件

当编译存在错误时用鼠标单击有关错误信息时编辑器会自动将光标定位在错误行的

位置注意对C 源文件中缺少分号的错误编辑器定位于其下面一行

5 应用构筑向导

应用构筑向导是用于创建外围设备初始化代码的一个图形界面你可以单击工具条

中的Wizard 按钮或菜单Tools 中的ApplicationBuilder 命令来调用它

应用构筑向导使用编译选项中指定的目标MCU来产生相应的选项和代码

应用构筑向导显示目标MCU 的每一个外围设备子系统它的使用是很显而易见的

在这里你可以设置MCU 的所具有的中断内存定时器IO 端口UART SPI 和模拟

量比较器等外围设备并产生相应的代码如果你需要的话还可产生main( )函数

6 状态窗口

状态窗口显示IDE 的状态信息

7 终端仿真

IDE 有一个内置的终端仿真器注意它不包含任意一个ISP 在系统编程功能但

它可以作为一个简单的终端或许可以显示你的目标装置的调试信息也可下载一个ASC

码文件

从6.20 版本开始IDE 加入了对ISP 的支持

五 菜单解释

1 弹出菜单

在ICCAVR 环境中单击右键那么ICCAVR 会根据实际情况弹出相应的工具菜单

2 File Menu 文件菜单

New 新建一个文件你可在编辑窗口是输入文字或代码

Reopen 重新打开历史文件有关历史文件显示的右边的子菜单中

Open 打开一个已以经存在的文件用于编辑文件用浏览窗口选择

Reload … form Disk 放弃全部的修改从磁盘中重新装载当前文件

Reload ….from Back UP 从最后一次的备份文件中装载当前文件

Save 保存当前文件如果环境设置中设置了保存备份文件则将原文件以

<file>.~<ext>形式保存

Save as 将当前文件用另外一个名称来保存

Close 关闭当前文件如果文件有过修改系统会进行提示

Compile File … to Object 编译当前文件成目标文件注意目标文件不可以直接

用于对芯片编程或用于调试其主要用于语法检查为创建新的启动文件或库产生

目标文件

Compile File ... to Output 编译当前文件成输出文件其产生的输出文件可用于编

程器和调试器

Save All 保存所有打开的文件

Closs All 关闭当前打开的所有文件同样它会提示你保存已经修改的文件

Print 打印当前文件

Exit 退出ICCAVR 的IDE 环境

3 Edit Menu 编辑菜单

Undo 撤消最后一次的修改

Redo 撤消最后一次的Undo

Cut 剪切选择的内容到剪帖板

Copy 拷贝选择的内容到剪帖板

Paste 将剪帖板内容粘帖在当前光标的位置

Delete 删除选择的内容

Select All 选择全部内容

Block Indent 对选择的整块内容右移

Block Outdent 对选择的整块内容左移

4 Search menu 寻找菜单

Find … 在编辑窗口中寻找一个文本

它有以下选项

Match Case – 区分大小写

Whole Word – 全字匹配

Up/Down –往上或往下

Find in Files... –在当前打开的文件中或在当前工程的所有文件中或当前目录中

的文件中寻找一段文本它有以下选项

Case Sensitive –大小写敏感

Whole Word - 全字匹配

Regular Expression – 寻找规则的表达式

Replace... – 在编辑器中替换文本

Find Again – 寻找下一个

Goto Line Number – 转到指定行号

Add Bookmark – 添加书签

Delete Bookmark – 删除书签

Next Bookmark – 跳转到下一个签

Goto Bookmark – 跳转到指定的书签

5 View Menu 视图菜单

Status Window – 如果选中显示状态窗口

Project Makefile – 以只读方式打开makefile 文件

Output Listing File – 以只读方式打开列表文件

6 Project Menu 工程菜单

New... – 创建一个新的工程文件

Open –打开一个已经存在的工程文件

Open All Files... – 打开工程的全部源文件

Close All Files – 关闭全部打开的文件

Reopen... – 重新打开一个最近打开过的工程文件

Make Project –解释和编译已经修改的文件为输出文件

Rebuild All – 重新构筑全部文件注意在版本升级后对原有工程最好全部重新构筑

Add File(s) – 添加一个文件到工程中这个文件可以是非源文件

Remove Selected Files – 从工程中删除选择的文件

Option... – 打开工程编译选项对话框

Close – 关闭工程

Save As... – 将工程换一个名称存盘

7 Tools Menu 工具菜单

Environment Options – 打开环境和终端仿真器选项对话框

Editor and Print Options –打开编辑和打印选项对话框

AVR Calc – 打开AVR 计算器可以计算UART 的波特率定时器的定时常数

Application Builder –打开应用向导程序生成硬件的初始化代码

Configure Tools – 允许你添加自己的内容到工具菜单

Run –以命令行方式运行一个程序

8 Compiler Options 编译选项

编译选项总共有三个页面Paths Compiler 和Target

在Paths 页面中有

Include Path(s) –你可以指定包含文件的路径

Assembler Include Path(s) – 指定汇编包含文件的路径

Library Path – 链接器所使用的库文件的路径

Output Directory –输出文件的目录

Compiler 页面有

Strict ANSI C Checking – 严格的ANSI C 语法检查

Accept Extensions – 接受C++类型语法扩充

Macro Define(s) – 定义宏宏之间用空格或分号分开宏定义形式如下

name[:value] 或 name[=value]

例如

DEBUG:1;PRINT=printf

等价于

#define DEBUG 1

#define PRINT printf

Macro Undefine(s) –同上但意义相反

Output File Format – 输出文件格式COFF/HEX Intel HEX 或COFF

Optimizations – 代码优化

Default – 基本优化象寄存器分配共用相同的子例程等

Maximize Code Size Reduction – 只有专业版才可使用它调用了代码压缩优化去

除了无用的碎片代码

Target 页面有

Device Configuration – 选择目标MCU

Memory Sizes – 要选择"Custom" 时指定内存大小包括ROM SRAM 和EEPROM

Text Address – 通常代码地址开始于中断向量区域后面

Data Address – 指定数据起始地址通常为0x60

Use Long JMP/CALL – 指定MCU 是否支持长跳转和长调用

Enhanced Core – 指定硬件支持增强核指令

IO Registers Offset Internal SRAM – 指定内部SRAM 的偏移量 例如, 8515 的SRAM

起始于0x60, 在IO 寄存器空间后面延伸了512 字节而 Mega603 , IO 寄存器覆盖在SRAM

空间中因此SRAM 也是从0 开始的

Internal 对External SRAM – 指定你的目标系统的数据SRAM 类型

PRINTF Version – 选择PRINTF 的版本

Small 或 Basic: 只有 %c, %d, %x, %X, %u, and %s 格式支持

Long: 支持 %ld, %lu, %lx, %lX

Floating point: %f 支持注意这个选项需要很大的内存

AVR Studio Simulator IO – 如果选中 AVR Studio 的终端模拟仿真被支持

Additional Libraries –使用标准库以外的附加库

Strings in FLASH – 字符串只保存在FLASH存贮器中

Return Stack Size – 指定编译器使用的硬件堆栈的大小编译器使用的软件堆栈的大小

不需地指定

Non Default Startup –允许你指定一个启动文件的位置系统默认的启动文件在Paths 页

中指定这样IDE 可以使用多个启动文件

Unused ROM Fill Pattern – 用一串十六进制数填充空余的ROM 空间



ICCAVR中文使用说明(3)



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 的每

个中断入口地址使用两个字word 而非Mega 芯片每一个中断入口地址使用一个字

word .

你也可以有多个启动文件你可以在工程选项对话框中很方便地直接指定一个启动文

件加入您的工程中注意您必须指定启动文件的绝对路径或启动文件必须位于工程选项

库路径所指定的目录中

2 常用库介绍

1 库源代码

这个库源代码缺省路径为c:\icc\libsrc.avr\libsrc.zip 是一个密码保护的ZIP 压缩文件

你可以从互连网上任意下载一个UNZIP 程序进行解压缩当本软件被开锁后密码显示在

"About"对话框中例如

unzip -s libsrc.zip

; unzip 提示输入密码

2 AVR 特殊函数----- ICCAVR 有许多访问UART EEPROM 和SPI 的函数堆栈检

查函数对检测堆栈是否溢出很有用另外我们的互连网上有一个页专门存放用户写的源代



3 io*.h (io2313.h, io8515.h, iom603.h, ... 等.)

这些文件中是从ATMEL 官方公开的定义IO 寄存器的源文件经过修改得到的应该用

这些文件来代替老的avr.h 文件

PORTB = 1;

uc = PORTA;

4 macros.h

这个文件包含了许多有用的宏和定义

5 其它头文件

下列标准的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 II 字符集字符分类使用这些函数之前应当用"#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=y 2 n y

的范围为0.5 y 1 y 值被函数返回而n 值存放到pexp 指向的变量中

float fround(float x)

返回最接近x 的整型数

float ldexp(float x, int exp)

返回x 2 e x p

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)

返回x y 值

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)和输出(putchar)程序

如果你针对不同的装置使用高层的IO 函数例如用 printf 输出LCD 你需要全部重新定

义最底层的函数

为在ATMEL 的AVR Studio 模拟器终端IO 窗口使用标准IO 函数应当在编译选

项中选中相应的单选钮

注意作为缺省单字符输出函数putchar 是输出到UART 装置没有修改无论如何

为使输出能如期望的那样出现在程序终端窗口中'
' 字符必须被映射为成对的回车和换

行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 支持三个版本取决于你的特别需要和代码的大小越高的要求代码越大

基本形: 只有 %c, %d, %x, %u, 和 %s 格式说明符是承认的

长整形: 针对长整形数的修改 %ld, %lu, %lx 被支持, 以适用于精度要求较高的领域

浮点形: 全部格式包括%f 被支持

你使用编译选项对话框来选择版本代码大小的增加是值得关注的

int putchar(int c)

输出单个字符这个库程序使用了UART 以查寻方式输出单个字符注意输出’
’字

符至程序终端窗口

int puts(char *s)

输出以NL 结尾的字符串

int sprintf(char *buf, char *fmt)

按照格式说明符输出格式化文本frm 字符串到一个缓冲区格式说明符同printf( )

"const char *" 支持功能

cprintf 和csprintf 是将FLASH中的格式字符串分别以prinf 和sprinf 形式输出

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;

_NewHeap(&_bss_end+1, &_bss_end + 201); // 初始化200 字节大小的堆

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 和下列字符串及字符阵列函数

void *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 和三个宏

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()来实现

#include <stdarg.h>

int printf(char *fmt, ...)

{

va_list ap;

va_start(ap, fmt);

vfprintf(fmt, ap);

va_end(ap);

}

9 堆栈检查函数

有几个库函数是用于检查堆栈是否溢出内存图如下如果硬件堆栈增长到软件堆栈

中那么软件堆栈的内容将会被改变也就是说局部变量和别的堆栈项目被改变硬件堆

栈是用作函数的返回地址如果你的函数调用层次太深偶然会发生这种情况

同样地软件堆栈溢出进数据区域将会改变全局变量或其它静态分配的项目如果你

使用动态分配内存还会改变堆项目这种情况在你定义了太多的局部变量或一个局部集

合变量太大也会偶然发生

高端地址

硬件堆栈区

警戒线

软件堆栈区

警戒线

数据区 低端地址

警戒线

启动代码写了一个正确的关于数据区的地址字节和一个类似的正确的关于软件堆栈的

地址字节作为警戒线[注意如果你使用了你自己的启动文件而其又是以6.20 版本之

前的启动文件为基础的你将需要额外改造为新的启动文件]

注意如果你使用动态分配内存你必须跳过警戒线字节_bss_end 来分配你的堆参考内

存分配函数

堆栈检查

你调用_StackCheck(void)函数来检查堆栈溢出如果警戒线字节仍然保持正确的值那

么函数检查通过如果堆栈溢出那么警戒线字节将可能被破坏

注意当你的程序堆栈溢出的时候你的程序将可能运行不正常或偶然崩溃当

_StackCheck 检查错误条件时它调用了带一个参数的函数_StackOverflowed(char c) 如果

参数是1 那么硬件堆栈有过溢出如果参数是0 那么软件堆栈曾经溢出在那个例子中

制造了两个功能调用它是两个堆栈都可能溢出的无论如何在_StackOverflowed 执行

起作用时第二个调用不可以出现作为例子如果函数复位了CPU 那么将不能返回

_StackCheck 函数

缺省的 _StackOverflowed 函数

当它被调用时库会用一个缺省的_StackOverflowed 函数来跳转到0 的位置因此复

位CPU 和程序 你可能希望用一个函数来代替它以指示更多的错误条件一个作为例子

它可能切断所有的中断并且点亮LED 注意自堆栈溢出指示故障程序以来

_StackOverflowed 函数或许不能执行任何太复杂的事或实现程序的正常工作

这两个函数的原型在头文件macros.h.中

ICCAVR中文使用说明(4)



AVR 硬件访问的编程

1 访问AVR 的低层硬件

AVR 系列使用高级语言编程时有很高的C 语言密度它允许你对访问目标MCU 的底

层硬件进行访问由于AVR 性能除了要最大程序地优化代码外很少使用汇编偶然情况

下目标MCU 的硬件特点在C 语言中不能很好地使用很显然使用在线汇编和预处理宏能

访问这些特点

头文件io*.h 如io8515.h iom603.h 等定义了指定AVR MCU 的IO 寄存器细节

这些文件是从ATMEL 官方发布的文件经过修改以匹配这个编译器的语法要求文件

macros.h 定义了许多有用的宏例如宏UART_TRANSMIT_ON( )能使UART 开始工作

这个编译器的效率很高当访问由IO 寄存器映射的内存时能产生单周期指令象in out

sbis sbi 等参考IO 寄存器

注意老的头文件avr.h 定义IO 寄存器的bit 有一些模糊尽管io*.h 定义了它们的bit

的位置因此使用io*.h 和IO 寄存器的bit 很多时候你将需要使用定义在macros.h 文件

中的BIT()宏例如

avr.h:

#define SRE 0x80// 外部 RAM 使能

... (你的C 程序)

MCUCR |= SRE;

io8515.h

#define SRE 7

... (你的C 程序)

#include <macros.h>

MCUCR |= BIT(SRE);

2 位操作

一个共同的任务是编程微控制器MCU打开或关闭IO 寄存器的一些位bit 很幸运

标准C 有较好的和适用的位操作功能而没有借助于汇编指令或其它非标准C 结构C 定

义了一些按位进行的运算是很有用的

a | b – 按位或这个表达式指示中a 被表达式中的b 按位进行或运算这惯用于打开某些

位尤其常用|=的形式例如

PORTA |= 0x80; // 打开位7 (最高位)

a & b – 按位与这个运算在检查某些位是否置1 时有用例如

If ((PORTA & 0x81) == 0) // 检查位7 和位0

注意圆括号需要括在&运算符的周围因为它和= = 相比运算优先级较低这是C 程

序中很多错误的原因之一

a ^ b – 按位异或这个运算对一个位取反有用例如在下面的例子中位7 是被翻转的

PORTA ^= 0x80; // 翻转位7

~a – 按位取反. 在表达式中这个运算执行一个取反当用按位与运算关闭某些位时与这

个运算组合使用尤其有用如

PORTA &= ~0x80; // 关闭位7

这个编译器对这些运算能产生最理想的机器指令例如sbic 指令可以用在根据位的

状态进行条件分枝的按位与运算中

3 程序存贮器和常量数据

AVR 是哈佛结构的MCU 它的程序存贮器和数据存贮器是分开的这样的设计是有

一些优点的例如分开的地址空间允许AVR 装置比传统结构访问更多的存贮器例如

Atmega 系列允许有超过64K 字WORD 的程序存贮器和64K 字节的数据存贮器将来

的MCU 装置可能用到更多的程序存贮器而程序计数器仍保留在16 位上

不幸的是C 不是在这种机器上发明的特别地C 指针是任意一个数据指针或函数

指针C 规则已经指定你不可以假设数据和函数指针能被向前和向后修改可是同是哈佛

结构的AVR 要求数据指针能指向任一个数据内存和程序内存

非标准C 解决了这个问题ImageCraft AVR 编译器使用"const"限定词表示项目是在程

序存贮器中注意对指针描述这个const 限定词可以应用于不同的场合不管是限定指

针变量自己还是指向项目的指针例如

const int table[] = { 1, 2, 3 };

const char *ptr1;

char * const ptr2;

const char * const ptr3;

"table"是表格式样分配进程序存贮器"ptr1"是一个项目在数据存贮器而指向数据的指

针在程序存贮器"ptr2"是一个项目在程序存贮器而指向数据的指针在数据存贮器最后

"ptr3"是项目在程序存贮器而指向数据的指针也在程序存贮器在大多数的例子中"table" 和

和"ptr1"是很典型的C 编译器生成LPM 指令来访问程序存贮器

注意C 标准不要求"const"数据是放入只读存贮器中而且在传统结构中除了正确访

问就没有要紧的了因而在承认参数的C 标准中使用const 限定是非传统的无论如何

这样做与标准C 函数定义是有一定冲突的

例如标准"strcpy"的原型是strcpy(char *dst, const char *src) 带有const 限定的第二个

参数表示函数不能修改参数然而在ICCAVR 下const 限定词表示第二个参数指向程序存

贮器是不合适的因此这些函数定义设有const 限制

最后注意只有常数变量以文件存贮类型放入FLASH 中例如定义在函数体外的变

量或有静态存贮类型限制的变量如果你使用有const 限制的局部变量将不被放入FLASH

中而可能导致不明确有结果

4 字符串

在哈佛结构的AVR 中程序内存和数据内存分开给程序内存和数据内存的说明带来了一

定的复杂性这页说明字符串

字符串

这个编译器将带有const 说明的表和项目放入程序存贮器中最困难的是字符串的分

配和处理问题在于C 中将字符串转换为char 指针如果字符串是分配进程序存贮器中

那么所有字符串库函数中的任意一个必须被复制成不同于指针的操作或者字符串也必须

被分配在数据存贮器中

ImageCraft 编译器提出了解决这个问题的两个方法

缺省的字符串分配

这个缺省的方法是同时分配字符串在数据和程序存贮器中所有涉及的字符串是拷贝

进数据存贮器的为了确保它们的值是正确的在程序启动时字符串是由程序存贮器拷贝

进数据存贮器中的因此只有单一的字符串拷贝函数是必须的编译器执行全局变量初始

化也是这样处理的

如果你希望节省空间你能使用常量字符型数组来将字符串只分配进程序存贮器中

例如

const char hello[] = "Hello World";

在这个例子中hello 可以在上下文中作为字符串使用但不能用作标准C 库中字符串

函数的参数

Printf 已被扩展成带%S 格式字符来输出只存贮于FLASH 中字符串另外新的字符

串函数已加入了对只存贮于FLASH中字符串的支持

只分配全部字符串到FLASH存贮器中

当对应"Project->Options->Target->Strings In FLASH Only"检查框被选中时你可以指

挥编译器将字符串只放在FLASH 中这时称必须很小心地调用库函数当这个选项是选

中的字符串类型"const char *"是有效的并且你必须保证函数获得了合适的参数类型

除了新的"const char *"与字符串有关系外创建了cprintf 和csprintf 函数承认字符串格式

的类型参考标准输入输出函数

注意当选项2 只分配全部字符串到FLASH 存贮器中时应使用cprintf() 对const char*

及const char ptr[ ]类型字符串并且加%S 参数

当选项1 时应使用printf() 对const char*及const char ptr[ ]类型字符串并且加%S

参数

5 堆栈

生成代码使用两个堆栈一个是用于子程序调用和中断操作的硬件堆栈一个是用于

以堆栈结构传递的参数临时变量和局部变量的软件堆栈

硬件堆栈起初是用于存贮函数返回的地址它代表了许多小的软件堆栈通常如果

你的程序没有子程序调用也不调用象带有%f 格式的printf()等库函数那么默认的16 字

节应该在大多数的例子中能良好工作在绝大多数程序中除了很繁重的递归调用程序再

入式函数最多40 个字节的硬件堆栈应该是足够的

硬件堆栈是从数据内存的顶部开始分配的而软件堆栈是在它下面一定数量字节处分

配硬件堆栈和数据内存的大小是受在编译器选项中的目标装置项设定限制的数据区从

0x60 开始分配在IO 空间后面是正确的允许数据区和软件堆栈彼此相向生长

如果你选择的目标装置带有32K 或64K 的外部SRAM 那么堆栈是放在内部SRAM

的顶部而且向低内存地址方向生长参考程序和数据内存的使用

堆栈检查

任意一个程序失败的重要原因是堆栈溢出到其它数据内存的范围两个堆栈中的任意

一个都可能溢出并且当一个堆栈溢出时会偶然产生坏的事情你可以使用堆栈检查函数

检测溢出情况

6 在线汇编

除了在汇编文件中写汇编函数外在线汇编允许你写汇编代码进你的C 文件中当

然在你的工程使用汇编源文件作为一个部件是良好的在线汇编的语法是

asm("<string>");

多个汇编声明可以被符号
分隔成新的一行String 可以被用来指定多个声明除

了额外增加的ASM 关键词为了在汇编声明中访问一个C 的变量可使用%<变量名>格

式如

register unsigned char uc;

asm("mov %uc,R0
"

"sleep
");

任意一个C 变量都可以被引用如果你在汇编指令中需使用一个CPU 寄存器你必

须使用寄存器存贮类register 来强制分配一个局部变量到CPU 寄存器中

通常使用在线汇编引用局部寄存器的能力是有限的如果你在函数中描述了太多的

寄存器变量就很可能没有寄存器可用在这种情况你将从汇编程序得到一个错误那

时也不能控制寄存器变量的分配所以你的在线汇编指令很可能失败作为例子使用LDI

指令需要使用R16~R31 中的一个寄存器但这里没有请求使用在线汇编同样也没有引用

上半部分的整数寄存器

在线汇编可以被用在C 函数的内部或外部编译器将在线汇编的每行都分解成可读的

不象AVR 汇编器ImageCraft 汇编器允许标签放置在任意地方所以你可以在你的在线汇

编代码中创建标签当汇编声明在函数外部时你可能得到一个警告你不要理睬这个警



7 IO 寄存器

IO 寄存器包括状态寄存器SREG 可以被两条路线访问IO 地址在0x00 和0x3f 之

间可以使用IN 和OUT 指令读写IO 寄存器或者使用在0x20 和0x5F 之间的数据内存

地址可以使用普通数据访问指令和地址模式两种方法在C 中都可使用

数据内存地址一个直接地址可以通过加指针类型符号直接访问例如SREG 的数

据内存在地址是0x5F:

unsigned char c = *(volatile unsigned char *)0x5F; // 读SREG

*(volatile unsigned char *)0x5F |= 0x80; // 打开全局断位

注意数据内存地址0 到31 涉及到CPU 寄存器注意不要不注意地改变CPU 寄存器

当访问在IO 寄存器范围中的数据内存时编译器自动生成低级指令象in out sbrs

sbrc 等是首选的方法

IO 地址你可以使用在线汇编和预处理宏来访问IO 地址

register unsigned char uc;

asm("in %uc,$3F");// 读 SREG

asm("out $3F,%uc"); // 打开全局中断位

注意老的头文件avr.h 定义IO 寄存器的bit 有一些模糊尽管io*.h 定义了它们的bit

的位置因此使用io*.h 和IO 寄存器的bit 很多时候你将需要使用定义在macros.h 文件

中的BIT()宏例如

avr.h:

#define SRE 0x80// 外部 RAM 使能

... (填充你的C 程序)

MCUCR |= SRE;

io8515.h

#define SRE 7

... (填充你的C 程序)

#include <macros.h>

MCUCR |= BIT(SRE);

8 绝对内存地址

你的程序可能需要使用绝对内存地址例如外部IO 设备通常被映射成特殊的内存

这些可能包括LCD 界面和双口SRAM 通常你可以使用在线汇编或单独的汇编文件来描

述那些定位在特殊内存地址的数据在稍后版本的编译器中已在C 语言中提供这些能力

在下面有例子中假设有一个两字节的LCD 控制寄存器定位在0x1000 地址一个两

字节的LCD 数据寄存器定位在0x1002 地址并且有一个100 字节的双口SRAM 定位在

0x2000 的地址

使用汇编模式在一个汇编文件中输入以下内容

.area memory(abs)

.org 0x1000

_LCD_control_register :: .blkw 1

_LCD_data_register:: .blkw 1

. org 0x2000

_dual_port_SRAM:: .blkb 100

在你的C 文件中必须这样描述

extern unsigned int LCD_control_register, LCD_data_register;

extern char dual_port_SRAM[100];

注意

界面规定在汇编文件中外部变量名称是带'_'前坠的并且使用两个冒号定义为全局变量

使用在线汇编

在线汇编遵守同样的汇编语法规则除了它被附加了一个asm()伪函数在C 文件中

关于上面的汇编代码被变为如下代码

asm(".area memory(abs)"

".org 0x1000"

"_LCD_control_register:: .blkw 1"

"_LCD_data_register:: .blkw 1");

asm(".org 0x2000"

"_dual_port_SRAM:: .blkb 100");

在C 中你仍然要使用"extern"描述变量正象上面使用单独的汇编文件那样否则C

编译器不会真正知道在asm 中的声明

9 C 任务Tasks

作为汇编界面的描述和调用规则编译器通常在生成代码来保存和恢复保护的寄存器

在一些情况下这些行为可能是不合适的例如如果你使用RTOS 实时操作系统RTOS

管理着寄存器的保存和恢复并作为任务切换处理的一部分编译器如果再插入这些代码就

变得多余了

为了禁止这种行为你可以使用"#pragma ctask" 例如

#pragma ctask drive_motor emit_siren

....

void drive_motor() { ... }

void emit_siren() {...}

这个附注(pragma)必须被用在函数定义之前注意作为默认的情况从不返回的程序

"main"是有这个属性的它也没有必要为返回保存和恢复任意一个寄存器

10 中断操作

C 中断操作

中断操作中C 中可以使用无论函数定义在文件的什么地方你必须用一个附注

pragma 在函数定义之前通知编译器这个函数是一个中断操作

#pragma interrupt_handler <name>:<vector number> *

"vector number" 中断的向量号注意向量号是从1 开始的那是复位向量这个附注

有两个作用:

对中断操作函数编译器生成RETI 指令代替RET 指令而且保存和恢复在函数中用

过的全部寄存器

编译器生成以向量号和目标MCU为基础的中断向量

例如

#pragma interrupt_handler timer_handler:4

void timer_handler()

{

...

}

编译器生成的指令为

rjmp _timer_handler ; 对普通AVR MCU

或者

jmp _timer_handler ;对Mega MCU

上述指令定位在0x06 字节地址针对普通装置和0x0c 字节地址针对Mega 装

置Mega 使用2 个字作为中断向量非Mega 使用1 字作为中断向量

如果你希望对多个中断入口使用同一个中断操作 你可以在一个interrupt_handler 附

注中放置多个用空格分开的名称分别带有多个不同的向量号例如

#pragma interrupt_handler timer_ovf:7 timer_ovf:8

汇编中断操作

你可以用汇编语言写中断操作如果在你的汇编操作内部调用C 函数无论如何要小

心汇编程序要保存和恢复挥发寄存器参考汇编界面C 函数不做这些工作

如果你使用汇编中断操作那么你必须自己定义向量你使用"abs"属性描述绝对区域

用".org"来声明rjmp 或jmp 指令的正确地址注意这个".org" 声明使用的是字节地址

; 对全部除ATMega 以外的MCU

.area vectors(abs) ;中断向量

.org 0x6

rjmp _timer

; 对 ATMega MCU

.area vectors(abs) ; 中断向量

.org 0xC

jmp _timer

11 访问UART

默认的库函数getchar 和putchar 使用查寻模式从UART 中进行读写在\icc\examples.avr

目录有一个以中断方式工作的IO 程序可以代替默认的程序

12 访问EEPROM

EEPROM 在运行时可以使用库函数访问在调用这些函数之前加入#include

<eeprom.h>

EEPROM_READ(int location, object)

这个宏调用了EEPROMReadBytes 函数从EEPROM 指定位置读取数据送给数据对象

"object"可以是任意程序变量包括结构和数组例如

int i;

EEPROM_Read(0x1, i); // 读2 个字节给i

EEPROM_WRITE(int location, object)

这个宏调用了EEPROMWriteBytes 函数将数据对象写入到EEPROM 的指定位置

"object"可以是任意程序变量包括结构和数组例如

int i;

EEPROM_WRITE(0x1, i); //写两个字节至0x1

这些宏和函数可以用于任意AVR 装置可是对EEPROM 单元少于256 字节的MCU

即使不需要高地址字节它们也是欠佳的因为它仍然是要写的如果它关系重大你可以

为EEPROM 较少的目标装置重新编译库源代码

初始化EEPROM

EEPROM 可以在你的程序源文件中初始化在C 源文件中它作为一个全局变量被分配

到特殊调用区域"eeprom."中的这是可以用附注实现的结果是产生扩展名为.eep 的输出

文件例如

#pragma data:eeprom

int foo = 0x1234;

char table[] = { 0, 1, 2, 3, 4, 5 };

#pragma data:data

...

int i;

EEPROM_READ((int)&foo, i); // i 等于 0x1234

第二个附注是必须的为返回默认的"data."区域需要重设数据区名称

注意因为AVR 的硬件原因初始化EEPROM 数据至0 地址是不可以使用的

注意当使用外部描述比如访问在另一个文件中的foo 你不需要加入这个附注例



extern int foo;

int i;

EEPROM_READ((int)&foo, i);

内部函数

如果需要下列函数可以直接使用但是上面关于宏的描述对大多数装置应该是有能力的

unsigned char EEPROMread(int location)

从EEPROM 指定位置读取一个字节

int EEPROMwrite(int location, unsigned char byte)

写一个字节到EEPROM 指定位置如果成功返回0

void EEPROMReadBytes(int location, void *ptr, int size)

从EEPROM 指定位置处开始读取"size"个字节至由"ptr."指向的缓冲区

void EEPROMWriteBytes(int location, void *ptr, int size)

从EEPROM 指定位置处开始写"size"个字节写的内容由"ptr."指向的缓冲区提供

13 访问SPI

一个以查寻模式访问SPI 的函数是提供的更多的信息参考spi.h

14 相对转移/调用的地址范围

一个带8K 程序存贮器的装置全部范围内的跳转可以使用相对转移和调用指令(rjmp

和 rcall) 为实现这个目的相对转移和调用的范围是以8K 为分界的例如一个较远的

跳转跳转到0x2100 字节处0x2000 为8K 实际上会跳转到地址0x100 处

这个选项是由工程管理器自动检测的只要目标装置的程序存贮器是8K 的



ICCAVR中文使用说明(5)



C 的运行结构

1 数据类型

类型 长度 (字节) 范围

unsigned char 1 0..256

signed char 1 -128..127

char (*) 1 0..256

unsigned short 2 0..65535

(signed) short 2 -32768..32767

unsigned int 2 0..65535

(signed) int 2 -32768..32767

unsigned long 4 0..4294967295

(signed) long 4 -2147483648..2147483647

float 4 +/-1.175e-38..3.40e+38

double 4 +/-1.175e-38..3.40e+38

(*) "char" 等同于 "unsigned char"

floats 和 doubles 是 IEEE 标准 32 位格式7 位表示指数23 位表示尾数1 位表示

符号

位域类型必须被赋予unsigned 或signed 关键字而且将被包含在一个较小的空间中

如可定义成结构

struct {

unsigned a : 1, b : 1;

};

这个结构体的长度只有一个1 byte 位域是从右往左放置的

2 汇编界面和调用规则

a) 名称

C 语言中的名称在汇编文件中是以下划线为前坠的如函数main( )在汇编模块中是以

_main( )引用的名称的有效长度为32 个字符在名称后面加两个冒号: 可以定义成

一个全局变量例如

_foo::

.word 1

(在C 文件中)

extern int foo;

b) 传递参数和返回值所使用的寄存器

第一个参数若是整型则通过R16/R17 传递第二个参数则通过R18/R19 传递如

果参数是长整型或浮点数则通过R16/R17/R18/R19 传递其余参数通过软件堆栈传递

比整型参数小的如char 参数扩展成整型int 长度传递即使函数原型是可用的

如果R16/R17 已传递了第一个参数而第二个参数是长整型或浮点数则第二个参数

的低半部分通过R18/R19 传递而高半部分通过软件堆栈传递

整型返回值是通过R16/R17 返回而长整型或浮点数返回则通过R16/R17/R18/R19

返回

c) 保护的寄存器

在汇编函数中必须保护和恢复下列寄存器

R28/R29 或Y 这是结构指针

R10/R11/R12/R13/R14/R15/R20/R21/R22/R23 这些寄存器是调用保护寄存器这

些寄存器的内容在被汇编语言函数调用后必须保持不变

d) 挥发寄存器

别的寄存器如

R0/R1/R2/R3/R4/R5/R6/R7/R8/R9/R24/R25/R26/R27/R30/R31

SREG

可以在汇编语言函数中使用而不被保护和恢复这些寄存器是调用挥发寄存器

这些寄存器的内容在被函数调用后可以改变

e 中断处理

这不同于普通的函数调用在中断操作中必须保护和恢复它所使用的全部寄存器

如果你是使用C 函数来描述中断处理那么编译器有能力自动完成的如果使用汇编

写中断处理而它又调用了普通的C 函数那么汇编操作必须保护和恢复挥发性寄存

器普通C 函数调用不保护它们中断处理操作同普通程序操作是异步的中断处理

或它的函数调用不能改变任意一个MCU寄存器

3 函数返回非整型值

在调用函数前必须描述其返回的一个长整型浮点数或结构值作为例子在调用

任意浮点函数之前应当用#include 语句包含头文件<math.h> 否则在这些程序返回它

们的值后你的程序将不工作这和那些返回整型值的函数是有不同之处的

返回长整型数或浮点数

长整型数或浮点数返回值是设定在一些寄存器R16-R19 中

传递结构值

如果传递结构值结构允许通过堆栈传递而不是通过寄存器传递结构索引(也就是

传递结构的地址) 和传递任意数据项目的地址是相同的, 都是通过一个2 字节的指针

返回结构值

当一个返回结构的函数被调用时这个调用函数分配一个临时贮藏库而且传递一个

隐藏指针给调用函数当这个函数返回时它拷贝返回值进这个临时贮藏库

程序和数据区的使用

程序存贮器

程序存贮器是被用于保存你的程序代码常数表和确定数据的初始值比如字符串全

局变量编译器可以生成一个对应程序存贮器映像的输出文件HEX 文件这个文件可

以被编程器用来对芯片编程

通常编译器不能使用任意64K 字节以上的程序存贮器为了访问64K 字节边界以上

的存贮器如在Mega103 装置中你需要在设定RAMPZ寄存器后直接调用ELPM 指令

数据存贮器仅指内部SRAM

这个数据存贮器是被用于保存变量堆栈结构和动态内存分配的堆通常它们不产

生输出文件但在程序运行时使用一个程序使用数据内存如下图

硬件堆栈

软件堆栈



全局变量和字符串

CPU 和IO 寄存器

内存图的底部是地址0 开始的96 0x60 字节是CPU 寄存器和IO 寄存器编译器

从0x60 往上放置全局变量和字符串在变量区域的顶部你可以分配动态内存在高端地址

硬件堆栈开始于SRAM 的最后位置在它的下面是向下生长的软件堆栈它要求你作为程

序师要确保硬件堆栈不生长进软件堆栈而软件堆栈不生长进堆否则将导致意外的结



数据存贮器外部SRAM

如果你选择带有32K 或64K 外部SRAM 的目标装置那么堆栈是放置在内部SRAM

的顶部并且是朝低端内存地址向下生长数据内存是开始于硬件堆栈的顶部并且向上生长

这样分配的原因是在多数场合访问内部SRAM 比访问外部SRAM 的速度要快分配堆栈

到较快的内存是有很多好处的

5 编程区域

编译器生成代码和数据到不同的区域"areas." 区域按照内存地址增高的顺序被编译器

使用

只读存贮器

interrupt vectors -----这个区域包括中断向量

func_lit – 函数表区这个区的每个字包括了函数入口的地址为了与代码压缩完全兼

容所有间接的函数索引必须通过间接的额外对准如果你在C 中使用函数指针调用函数

这是自动完成的在汇编中举例如下

; 假设 _foo 是函数的名称

.area func_lit

PL_foo:: .word _foo ; 创建函数表入口

. area text

ldi R30,<PL_foo

ldi R31,>PL_foo

rcall xicall

你可以间接地在函数表入口地址送入R30/R31 寄存器对后使用库函数xicall 调用这

个函数

lit – 这个区域包括了整型数和浮点数常量

idata -全局变量和字符串的初始值保存在这个区域

text – 这个区域包括程序代码

数据内存

data -这个区域包括全局变量静态变量和字符串全局变量和字符串的初始值是保存

在"idata" 区域并且是在启动时被拷贝进数据区的

bss -这个区域包括未初始化的全局变量按ANSI C 定义这些变量在启动时将初始化

为0

EEPROM 存贮器

eeprom - 这个区域包括EEPROM 数据EEPROM 数据是写进扩展名为.eep 的输出文件

其格式INTEL HEX 文件格式

九 调试

ICCAVR 可以输出COFF 格式调试文件使用户可在ATMEL 的AVRStudio 中进行源

程序级的调试如果用户想使用AVRStudio 中的模拟IO 及终端仿真器那么在ICCAVR

的编译选项中必须将AVR Studio Simulator IO 一项打钩

十 ICCAVR 汇编参考

1 汇编语法

汇编有以下语法

1 名称

所有汇编名称必须由下列字符组成

( ‘_’ | [a-z] ) [ [a-z] | [0-9] | ‘_’ ] *

在ICC 中汇编名称必须由下划线或字母开始随后跟字母数字或下划线组成在这

个文档中名称和符号是同名词名称可以是表示一个常数值的符号名称或代表某一个PC

地址的标号名称中的任意一个一个名称的长度最多为30 个字符长而且区分大小写汇

编指令和汇编伪指令除外

符号可以只用在程序模块中也可显式地被其它模块使用在以前的例子中符号表

示局部符号而在以后的例子中表示全局符号

如果在一个文件一个符号没有被定义而直接使用了那么它是被假设为在其它文件已

经定义而且它的值由链接器决定

2 数

如果数带有一个0x 或$前坠那么这个数是一个十六进制数

例如: 10

0x10

$10

0xBAD

0xBEEF

0xC0DE

-20

3 汇编文件格式

汇编文件必须是一个ASCII 文件纯文本文件而且要遵守一定的规则文件的每一

行应该是如下的格式

[label: [:]] [command] [operands] [;comments]

label 表示标号一个冒号表示局部符号两个符号表示全局符号

command 表示操作码指令码

operands 表示操作数

[ ] 表示为可选项

comments 表示注释注意在C 文件中注释用 / /或 /* ….*/ 引导而在汇编文件中用

分号 或 // 引导

上式的每项之间必须用一个或多个空格分开系统汇编时对注释部分不进行处理

标号

一个后面跟着一个或两个冒号的名称表示标号标号的值是程序中某一点的PC 计数

器的值一个标号带两个符号表示全局符号它在其它模块中也是显式的

操作码应该是AVR 指令伪指令或宏操作数是指指令所需要的参数在这个文档

中不描述AVR 指令因为它们与标准的Atmel 文档中的指令描述相同例外的是

xcall

应用于Mega 芯片而且只支持长调用或跳转指令它可解释为rcall 或call 中的任

意一个

xjmp

应用于Mega 芯片而且只支持长调用或跳转指令它可解释为rjmp 或jmp 中的任意

一个

4 表达式

在指令后的操作数可以是表达式例如直接地址是一个最简单的表达式

lds R10,asymbol

asymbol 是一个简单表达式有例子它是一个符号或标号名称通常一个表达式描述



expr: term |

{ expr }

unop expr

expr binop expr

term: . 当前程序计数器PC 值

name |

#name

圆括号用于分组其运算优先级高于运算符表达式不能随意使用有一个限制是使

用链接器的重定位信息表达式的基本规则是它只能出现一个重定位符号例如

lds R10,foo+bar

如果foo 和bar 两个全都是外部符号那么表达式无效

5 运算符

下面列出了各种运算符和它们的优先级运算符的优先级是很有用的只有加号可以

用于重定位符号计算比如外部符号其它所有运算符必须用于常数或本文件中定义的符

号的运算

运算符 功能 类型 优先级

* 乘法 二进制 10

/ 除法 二进制 10

% 取模 二进制 10

<< 左移 二进制 5

>> 右移 二进制 5

^ 按位异或XOR 二进制 4

& 按位与AND 二进制 4

| 按位或 二进制 4

- 负号 一元运算符 11

~ 取补运算 一元运算符 11

< 取低字节 一元运算符 11

> 取高字节 一元运算符 11

6 圆点. 或程序计数器

如果圆点出现在表达式中那么当前程序计数器的值被放置在圆点的位置

2 汇编伪指令

1 .area <name> [(attributes)]

定义代码或数据装入的内存区域链接器将所有使用同一名称的区域集合至一起并

且根据它们的属性进行连接或覆盖

属性有两类一类为

abs, 或绝对定位区域

rel 重定位区域

另一类为

con, 或 连接定位

ovr 覆盖定位

绝对定位区域的起始点地址是在汇编文件中由它自己指定的而重定位区域的起始地

址是由送命令选项给链接器来指定的对带连接属性的区域链接器连接这个区域到另一

个同名区域后面对带有覆盖属性的区域对每一个文件链接器都是从同一地址开始安排

区域下面举例说明它们的区别

file1.o:

.area text 10 bytes, 调用 text_1

.area data 10 bytes

.area text 20 bytes, 调用 text_2

file2.o:

.area data 20 bytes

.area text 40 bytes, 调用 text_3

text_1 和text_2 在这个例子中是正确的名称实际上它们是不会获得一个单独的名

称的让我们假设text 区域的起始地址设置为0 如果这个text 区域有 “con”属性那么text_1

将从0 开始text_2 从地址10 开始而text_3 从30 地址开始如果这个text 区域有 “ovr”

属性那么text_1 和text_2 将分别地从0 和10 开始但对text_3 由于它在另外一个文件

中定义所以它将同样从0 地址开始所有同名的区域必须有相同有属性即使它们用在

不同的模块中下面是具有合适属性的全部例子

.area foo(abs)

.area foo(abs,con)

.area foo(abs,ovr)

.area foo(rel)

.area foo(rel,con)

.area foo(rel,ovr)

.ascii strings?

2 .ASC 字符串

.ASCIZ 字符串

这个伪指令用于定义字符串无论哪一个都要附上一对分界符在两个分界符之内

任意可打印字符都是有效的下面是C 语言中的类型转义字符转义字符都是从反斜线 \ 开

始的

\e ESC

\b 退格

\f 换页


换行

\r 回车

\t TAB

\<最多三个八进制数> 字符是等于这个八进制数的值

ASCIZ 定义在字符串的结尾增加了NUL 字符 (\0) 它使\0 正确地嵌入字符串的内部

例如: asciz “Hello World


asciz “23\0456”

3 .byte <expr> [,<expr>]*

.word <expr> [,<expr>]*

.long<expr> [,<expr>]*

这些伪指令是定义常数它们分别表示字节常数(byte) 字常数(2byte) 和双字长数

(4byte) 字和双字常数是以高低字节倒置的格式输出这个格式用于AVR 微控制器(MCU)

注意双字常数只能用作操作数而另外两个可以用于重定位表达式

例如: .byte1, 2, 3

.word label,foo

4 .blkb <value>

.blkw <value>

.blkl <value>

这些伪指令是保留空间而没有给它们赋值指令后面的数分别是指保留的字节字或

双字的数目

5 .define <symbol> <value>

定义一个文本替代符无论何时"symbol"可用在表达式中如寄存器符号或标号它

是用"value."定义的例如

.define quot R15

mov quot,R16

6 .if <symbol name>

.else

.endif

上述三个伪指令定义了一个条件汇编语句如果条件<symbol name>为真非0 则执

行.if 和.else 之间的指令如果条件<symbol name>为假等于0 则执行.else 和.endif 之间

的指令.else 可以省略条件语句最多可以嵌套10 层如

.if cond

lds R10,a

.else

lds R10,b

.endif

如果cond 不等于0 则将a 装入R10 如果cond 等于0 则将b 装入R10

7 .macro <macroname>

定义一个宏由一直到 .endmacro 之间的所有声明组成宏除了另外一个宏声明外

任意汇编声明可以是宏的组成部分在宏内部表达式@digit(digit 由0~9 的数字代替)是宏被

调用时相应的宏参数定义的宏名称不能与汇编指令及汇编伪指令名称相冲突例如

定义宏名 “foo”

.macro foo

lds @0,a

mov @1,@0

.endmacro

调用宏foo 需要两参数如

foo R10,R11

等同于

lds R10,a

mov R11,R10

8 .endmacro 结束一个宏定义

9 <macro> [<arg0> ][,<args>]*]

调用宏是在操作码的位置放置宏名后面跟上相应的参数汇编器使用组成宏的声明

来替换宏同时用相应的宏参数来扩展@digit 你可以指定多个参数

例如:foo bar,x

调用宏foo 而且带两个参数bar 和x.

<symbol> = <value>

定义一个符号等于常数值如:

foo = 5

10 .include “<filename>”

处理指定的由文件名称指定的文件如果当前目录该文件不存在汇编器将试图按文

件名称指定的路径打开指定的文件

如: .include “registers.h”

11 .org <value>

设定程序计数器PC 的值为"value." 这个伪指令中在带有"abs"属性的区域内有效注

意"value"是字节地址

例如:.area interrupt_vectors(abs)

org 0xFFD0

dc.wreset

12 .globl <symbol> [, <symbol>]*

将一个符号定义为全局符号使其在当前或其它模块中都是显式的这也和跟着两个

冒号的标号相同否则符号只能在当前模块中使用

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2005-10-6 10:03:34 | 显示全部楼层
全部收下,谢谢。

出0入0汤圆

发表于 2005-10-19 15:40:57 | 显示全部楼层
谢谢

真好

收到了

出0入0汤圆

发表于 2005-10-19 16:32:44 | 显示全部楼层
谢谢,挺完整的使用说明

出0入0汤圆

发表于 2009-6-24 13:59:47 | 显示全部楼层
谢谢了。

出0入0汤圆

发表于 2009-6-24 14:05:24 | 显示全部楼层
直接上传PDF好了,
点击此处下载 ourdev_455754.pdf(文件大小:139K) (原文件名:doc_iccavr_manual_cn.pdf)

出0入0汤圆

发表于 2010-8-15 17:37:32 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-9-30 20:06:00 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-1 22:40:25 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-4-24 23:30:35 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-5-13 13:47:22 | 显示全部楼层
谢谢了

出0入0汤圆

发表于 2011-5-31 14:14:26 | 显示全部楼层
谢谢楼主,我在使用ICCAVR时使用了#pragma ctask Hal_Usart0_RXISR,但从AVR Studio中发现在Hal_Usart0_RXISR的入口处还会加放R寄存器保护的代码, 请问这是什么原因,有什么办法可以使ICCAVR不保护寄存器或是保存R0-R31全陪寄存器?谢谢各位

出0入0汤圆

发表于 2011-5-31 15:19:28 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-7-7 06:28:13 | 显示全部楼层
不错  嘿嘿

出0入0汤圆

发表于 2011-11-18 15:44:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-11-18 16:33:00 | 显示全部楼层
这个要顶

出0入0汤圆

发表于 2011-11-20 16:07:13 | 显示全部楼层
填鸭式的指导,呵呵

出0入0汤圆

发表于 2011-11-20 21:13:55 | 显示全部楼层
回复【16楼】taojie
-----------------------------------------------------------------------

哇塞 极品,正找这个呢! 谢谢!

出0入0汤圆

发表于 2011-11-25 12:42:09 | 显示全部楼层
楼主辛苦啊

出0入0汤圆

发表于 2012-5-10 10:58:57 | 显示全部楼层
不错,。

出0入0汤圆

发表于 2012-5-10 13:20:00 | 显示全部楼层
学习!!!

出0入0汤圆

发表于 2012-6-20 16:59:00 | 显示全部楼层
概括的很好~~

出0入0汤圆

发表于 2013-6-2 13:40:42 | 显示全部楼层
谢谢楼主,对初学者很好的帮助
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-16 03:51

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表