如何使用AVR-GCC編譯軟件
安裝GNU C for AVR
一.執(zhí)行安裝程序
二.生成鏈接用的庫文件
$(AVR)表示安裝的根目錄。(在本人系統(tǒng)里為f:\avrgCC)
生成庫文件關(guān)鍵是要運行位于$(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
rem set PATH=.;f:\AVRGCC\bin;%path%
doskey
:install
rem 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?)
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,但要去掉rem if %1!==! GOTO end
的“rem”。去掉“rem”之后,后續(xù)的語句將被跳過。因此MAKE部分的“f:\AVRGCC\bin\”可加可不加。
編譯和鏈接應用程序
首先在 www.pICavr.com 上下載測試程序集gcctest.zip,然后安裝。
1. 將GCCTEST\INCLUDE下的MAKE1、MAKE2拷貝到$(AVR)\ INCLUDE
2. 將工作目錄的MAKEFILE
(每個工程都要有一個此文件,且可由自己進行修改以適合自己的應用。如果要利用原有文件,則注意只能有一個C文件)中的MCU、TRG、SRC、ASRC、INC、LIB等項填入合適的內(nèi)容
3.
在工作目錄運行位于$(AVR)\BIN下的MAKE.EXE(注意:由于系統(tǒng)可能存在其他應用程序的MAKE,因此可能還需要加路徑。也可以將其改名。)
4. 從MAKE1、MAKE2和MAKEFILE可以看出,用戶可以修改諸如輸出文件名等多種選項。
在C代碼中嵌入?yún)R編指令
一.GCC的ASM聲明
首先看一個從PORTD讀入數(shù)據(jù)的例子:
asm(“in %0, %1” : “=r”(value) : “I”(PORTD) : );
由上可以看出嵌入?yún)R編的4個部分:
1. 匯編指令本身,以字符串“in %0, %1”表示;
2. 由逗號分隔的輸出操作數(shù),本例為“=r”(value)
3. 由逗號分隔的輸入操作數(shù),本例為“I”(PORTD)
4. Clobber寄存器
嵌入?yún)R編的通用格式為:
asm(code : output operand list : input operand list : clobber list);
例子中%0表示第一個操作數(shù),%1表示第二個操作數(shù)。即:
%0 “=r”(value)
%1 “I”(PORTD)
如果在后續(xù)的C代碼中沒有使用到匯編代碼中使用的變量,則優(yōu)化編譯時會將這些語句刪除。為了防止這種情況的發(fā)生,需要加入volatile屬性:
asm volatile (“in %0, %1” : “=r”(value) : “I”(PORTD) : );
嵌入?yún)R編的的Clobber寄存器部分可以忽略,而其他部分不能忽略,但可以為空。如下例:
asm volatile(“cli” : :);
二.匯編代碼
用戶可以在C代碼里嵌入任意的匯編指令,就如同在匯編器里寫程序一樣。AVR-GCC提供了一些特殊的寄存器名稱:
符號寄存器
__SREG__狀態(tài)寄存器SREG(0x3F)
__SP_H__堆棧指針高字節(jié)(0x3E)
__SP_L__堆棧指針低字節(jié)(0x3D)
__tmp_reg__r0
__zero_reg__r1。對于C代碼而言其值永遠為0
三.輸入/輸出操作數(shù)
約束符號適用于范圍
a r16~r23
b指針Y,Z
d r16~r31
e指針X,Y,Z
G浮點常數(shù)0.0
I6比特正常數(shù)0~63
J6比特負常數(shù)-63~0
l r0~r15
M8比特正常數(shù)0~255
N整數(shù)常數(shù)-1
O整數(shù)常數(shù)8,16,24
P整數(shù)常數(shù)1
r r0~r31
t R0
W寄存器對r24,r26,r28,r30
X指針Xr27:r26
Y指針Yr29:r28
Z指針Zr31:r30
要注意的是,在使用這些約束符號時要防止選擇錯誤。例如,用戶選擇了”r”約束符號,而匯編語句則使用了”ori”。編譯器可以在r0~r31之間任意選擇寄存器。若選擇了r2~r15,則會由于不適用ori而出現(xiàn)編譯錯誤。此時正確的約束符應該是”d”。
約束符號還可以有前置修飾符,如下表所示。
修飾符指定
=只寫操作數(shù)
+讀-寫操作數(shù)(嵌入?yún)R編不支持)
&寄存器只能用做輸出
輸出操作數(shù)必須為只寫操作數(shù),C表達式結(jié)果必須為l(r0~r15)。編譯器不檢查匯編指令中的變量類型是否合適。
輸入操作數(shù)為只讀。如果輸入/輸出使用同一個寄存器怎么辦呢?此時可以在輸入操作數(shù)的約束字符里使用一個一位數(shù)字來達到這個目的。這個數(shù)字告訴編譯器使用與第n個(從0開始計數(shù))操作數(shù)相同的寄存器。例如:
asm volatile(“SWAP %0” : “=r”(value) : “0”(value));
這條語句的目的是交換變量value的高低4位。約束符號“0”告訴編譯器使用與第一個操作數(shù)相同的寄存器作為輸入寄存器。要注意的是,即使用戶沒有指定,編譯器也有可能使用相同的寄存器作為輸入/輸出。在某些情況下會引發(fā)嚴重的問題。如果用戶需要區(qū)分輸入/輸出寄存器,則必須為輸出操作數(shù)增加修飾符”&”。如下例所示。
asm volatile(“in %0, %1;
out %1, %2”
: “=&r”(input)
: “I”(port), “r”(output)
);
此例的目的是讀入端口數(shù)據(jù),然后給端口寫入另一個數(shù)據(jù)。若編譯器不幸使用了同一個寄存器作為參數(shù)input和output存儲位置,則第一條指令執(zhí)行后output的內(nèi)容就被破壞了。而用了修飾符”&”之后,這個問題得以解決。
下面為一個高16位與低16位交換的32位數(shù)據(jù)操作的例子:
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。如果用戶在匯編代碼里使用了沒有作為操作數(shù)聲明的寄存器,就需要在clobber里聲明以通知編譯器。下面為一個中斷無關(guān)的加一操作例子。
asm volatile(“cli;
ld r24, %a0;
inc r24;
st %a0, r24;
sei”
:
:”z”(ptr)
:”r24”
)
編譯結(jié)果為:
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)
);
}
現(xiàn)在看起來好象沒問題了。其實不盡然。由于優(yōu)化的原因,編譯器不會更新C代碼里其他使用這個數(shù)值的寄存器。出于同樣的優(yōu)化原因,上述代碼的輸入寄存器可能保持的不是當前最新的數(shù)值。用戶可以加入特殊的”memory”
clobber來強迫編譯器及時更新所有的變量。
更好的方法是將一個指針聲明為volatile,如下所示:
volatile uint8_t *ptr;
這樣,一旦指針指向的變量發(fā)生變化,編譯器就會重新加載最新的數(shù)值。