如何使用AVR-GCC
2012-11-16
标签: AVRGCC

安装GNU C for AVR

一.执行安装程序

二.生成链接用的库文件

$(AVR)表示安装的根目录。(在本人系统里为f:\avrgcc)

生成库文件关键是要运行位于$(AVR)下的RUN.BAT。原程序如下:

@echo off
if NOT %AVR%!==! goto install
rem set environment variables
set AVR=f:\AVRGCC
set CC=avr-gcc
set PATH=.;f:\AVRGCC\bin;%path%
doskey
:install
if %1!==! GOTO end
rem install libc
cd f:\AVRGCC\lib\avr-libc-20010701\src
rem first win32_make_dirs will make some errors(I don't know why?)
make -f makefile-win32 win32_make_dirs
make -f makefile-win32 
make -f makefile-win32 install
make -f makefile-win32 clean
:end
f:
cd f:\AVRGCC
mode con: lines=43
要修改为:
@echo off
if NOT %AVR%!==! goto install
rem set environment variables
set AVR=f:\AVRGCC
set CC=avr-gcc
remset PATH=.;f:\AVRGCC\bin;%path%
doskey
:install
remif %1!==! GOTO end
rem install libc
cd f:\AVRGCC\lib\avr-libc-20010701\src
rem first win32_make_dirs will make some errors(I don't know why?)
f:\AVRGCC\bin\make -f makefile-win32 win32_make_dirs
f:\AVRGCC\bin\make -f makefile-win32 
f:\AVRGCC\bin\make -f makefile-win32 install
f:\AVRGCC\bin\make -f makefile-win32 clean
:end
f:
cd f:\AVRGCC
mode con: lines=43

在以后的应用中,运行的是修改之前的RUN.BAT,但要去掉remif %1!==! GOTO end 的“rem”。去掉“rem”之后,后续的语句将被跳过。因此MAKE部分的“f:\AVRGCC\bin\”可加可不加。

编译和链接应用程序

首先在www.avrfreaks.net上下载测试程序集gcctest.zip,然后安装。

1. 将GCCTEST\INCLUDE下的MAKE1、MAKE2拷贝到$(AVR)\ INCLUDE

2. 将工作目录的MAKEFILE (每个工程都要有一个此文件,且可由自己进行修改以适合自己的应用。如果要利用原有文件,则注意只能有一个C文件)中的MCU、TRG、SRC、ASRC、INC、LIB等项填入合适的内容

3. 在工作目录运行位于$(AVR)\BIN下的MAKE.EXE(注意:由于系统可能存在其他应用程序的MAKE,因此可能还需要加路径。也可以将其改名。)

4. 从MAKE1、MAKE2和MAKEFILE可以看出,用户可以修改诸如输出文件名等多种选项。

在C代码中嵌入汇编指令

一.GCC的ASM声明

首先看一个从PORTD读入数据的例子:

asm(“in %0, %1” : “=r”(value) : “I”(PORTD) : );

由上可以看出嵌入汇编的4个部分:

1. 汇编指令本身,以字符串“in %0, %1”表示;

2. 由逗号分隔的输出操作数,本例为“=r”(value)

3. 由逗号分隔的输入操作数,本例为“I”(PORTD)

4. Clobber寄存器

嵌入汇编的通用格式为:

asm(code : output operand list : input operand list : clobber list);

例子中%0表示第一个操作数,%1表示第二个操作数。即:

%0  “=r”(value)

%1  “I”(PORTD)

如果在后续的C代码中没有使用到汇编代码中使用的变量,则优化编译时会将这些语句删除。为了防止这种情况的发生,需要加入volatile属性:

asm volatile (“in %0, %1” : “=r”(value) : “I”(PORTD) : );

嵌入汇编的的Clobber寄存器部分可以忽略,而其他部分不能忽略,但可以为空。如下例:

asm volatile(“cli” : :);

二.汇编代码

用户可以在C代码里嵌入任意的汇编指令,就如同在汇编器里写程序一样。AVR-GCC提供了一些特殊的寄存器名称:

符号寄存器
__SREG__状态寄存器SREG(0x3F)
__SP_H__堆栈指针高字节(0x3E)
__SP_L__堆栈指针低字节(0x3D)
__tmp_reg__r0
__zero_reg__r1。对于C代码而言其值永远为0

三.输入/输出操作数

约束符号适用于范围
ar16~r23
b指针Y,Z
dr16~r31
e指针X,Y,Z
G浮点常数0.0
I6比特正常数0~63
J6比特负常数-63~0
lr0~r15
M8比特正常数0~255
N整数常数-1
O整数常数8,16,24
P整数常数1
rr0~r31
tR0
W寄存器对r24,r26,r28,r30
X指针Xr27:r26
Y指针Yr29:r28
Z指针Zr31:r30

要注意的是,在使用这些约束符号时要防止选择错误。例如,用户选择了”r”约束符号,而汇编语句则使用了”ori”。编译器可以在r0~r31之间任意选择寄存器。若选择了r2~r15,则会由于不适用ori而出现编译错误。此时正确的约束符应该是”d”。

约束符号还可以有前置修饰符,如下表所示。

修饰符指定
=只写操作数
+读-写操作数(嵌入汇编不支持)
&寄存器只能用做输出

输出操作数必须为只写操作数,C表达式结果必须为l(r0~r15)。编译器不检查汇编指令中的变量类型是否合适。

输入操作数为只读。如果输入/输出使用同一个寄存器怎么办呢?此时可以在输入操作数的约束字符里使用一个一位数字来达到这个目的。这个数字告诉编译器使用与第n个(从0开始计数)操作数相同的寄存器。例如:

asm volatile(“SWAP %0” : “=r”(value) : “0”(value));

这条语句的目的是交换变量value的高低4位。约束符号“0”告诉编译器使用与第一个操作数相同的寄存器作为输入寄存器。要注意的是,即使用户没有指定,编译器也有可能使用相同的寄存器作为输入/输出。在某些情况下会引发严重的问题。如果用户需要区分输入/输出寄存器,则必须为输出操作数增加修饰符”&”。如下例所示。

asm volatile(“in %0, %1;
out %1, %2”
: “=&r”(input)
: “I”(port), “r”(output)
);

此例的目的是读入端口数据,然后给端口写入另一个数据。若编译器不幸使用了同一个寄存器作为参数input和output存储位置,则第一条指令执行后output的内容就被破坏了。而用了修饰符”&”之后,这个问题得以解决。

下面为一个高16位与低16位交换的32位数据操作的例子:

asm volatile(“mov__tmp_reg__, %A0;
mov%A0, %D0;
mov%D0, __tmp_reg__;
mov__tmp_reg__, %B0;
mov%B0, %C0;
mov%C0, __tmp_reg__”
: “=r”(value)
: “0”(value)
)
31………………2423………………1615………………87…………………0
DCBA

四.Clobber

如前所示,asm语句的最后一部分为clobber。如果用户在汇编代码里使用了没有作为操作数声明的寄存器,就需要在clobber里声明以通知编译器。下面为一个中断无关的加一操作例子。

asm volatile(“cli;
ld r24, %a0;
inc r24;
st %a0, r24;
sei”
: 
:”z”(ptr)
:”r24”
)

编译结果为:

CLI
LD R24, Z
INC R24
ST Z, R24
SEI

当然,用户也可以用__tmp_reg__来取代r24。此时就没有clobber寄存器了。

下面为考虑更详细的例子:

c_func
{
uint_t s;
asm volatile(“in %0, __SREG__;
cli;
ld, __tmp_reg__, %a1;
inc __tmp_reg__;
st %a1, __tmp_reg__;
out __SREG__, %0”
: “=r”(t)
:”z”(ptr)
);
}

现在看起来好象没问题了。其实不尽然。由于优化的原因,编译器不会更新C代码里其他使用这个数值的寄存器。出于同样的优化原因,上述代码的输入寄存器可能保持的不是当前最新的数值。用户可以加入特殊的”memory” clobber来强迫编译器及时更新所有的变量。

更好的方法是将一个指针声明为volatile,如下所示:

volatile uint8_t *ptr;

这样,一旦指针指向的变量发生变化,编译器就会重新加载最新的数值。

可能会用到的工具/仪表
相关文章
推荐文章
热门文章
章节目录
本站简介 | 意见建议 | 免责声明 | 版权声明 | 联系我们
CopyRight@2024-2039 嵌入式资源网
蜀ICP备2021025729号