有
两个
选项可链接
ldc
发出的
编译目标文件
:用
avr-gcc
的链接器,或用
llvm
的内部链接器.
-gcc=avr-gcc
参数告诉在哪查找
avr-gcc
工具,或可用使用
llvm
的内置链接器的
--link-internally
.两者都会起作用.
使用
-gcc
相关的好处是可
arduino studio
,因为它有
objcopy,avrdude
等程序及其他有用的东西.让它正常工作,并更好地
集成
与其他库和工具等一起使用,如
gcc
提供的C运行时.
$ PATH=/opt/arduino/arduino-1.8.10/hardware/tools/avr/bin:$PATH ldc2 -betterC -Oz -mtriple=avr -mcpu=atmega328p -gcc=avr-gcc delay.d
$ file delay
delay: ELF 32-bit LSB executable, Atmel AVR 8-bit, version 1 (SYSV), statically linked, with debug_info, not stripped
$ ls -l delay
-rwxr-xr-x 1 me users 2440 Sep 18 08:28
使用
PATH
中的
arduino
工具,
-gcc=avr-gcc
参数就可工作.
使用
--link-internally
,你不需要这些,但是:
$ ldc2 -betterC -Oz -mtriple=avr -mcpu=atmega328p --link-internally delay.d
lld: warning: cannot find entry symbol _start; defaulting to 0x110B4
$ file delay
delay: ELF 32-bit LSB executable, Atmel AVR 8-bit, version 1 (SYSV), statically linked, not stripped
$ ls -l delay
-rwxr-xr-x 1 me users 708 Sep 18 08:31
注意它
链接
了,但它给出了关于丢失
_start
的警告.还要注意
不同
的文件大小.这是因为
gcc
提供的C运行时,提供了调用你的
extern(C)main
来执行一些
基本设置
的
开始
符号.没有它,你需要
自己
做更多的事情.我敢肯定,你也可在链接命令中列举
lib/object
文件来让
ldc
链接,但是使用
-avr-gcc
,可正常工作,因为它已知道
在哪
可找到所有
这些东西
.
avr链接,有点过时.
可用标准的
ldc2
安装,只需从
ldc
网站获取
二进制
.你不必自己编译它;正常下载中,
avr
支持已有一段时间了.
得到
ldc
后,先安装
arduino
工具.我直接安装
ArduinoStudio
.我把文件放在
/opt/arduino/arduino-1.8.10/
中.你可能要
调整
该路径.在那里,你会看到
arduino
命令,硬件目录等.
me@arsd:/opt/arduino/arduino-1.8.10$ ls
arduino arduino-linux-setup.sh hardware java libraries revisions.txt tools-builder
arduino-builder examples install.sh lib reference tools uninstall.sh
写个测试:
import ldc.llvmasm;
// delay_basic.h中的移植
void _delay_loop_1(ubyte __count) {
//该模板参数是必需的,以避免
//`!Call.getType()->isVoidTy()&&"Badinlineasm!"'`断定失败.
__asm!ubyte (
"1: dec $0\n\tbrne 1b",
"=r,0", (__count)
);
}
void _delay_loop_2(ushort __count) {
__asm!ushort (`
1: sbiw $0,1
brne 1b
`,
`=w,0`,
__count);
}
// arduino 中 delay.h 的端口
enum F_CPU = 1000000UL;
// 这是`_delay_ms`,但我做错了,把`double`改成`int`,我还是不喜欢
void _delay(int __ms) {
ushort __ticks;
ulong __tmp = (F_CPU * __ms) / 4000;
if(__tmp < 1)
__ticks = 1;
else if(__tmp > 65535) {
__ticks = cast(ushort) (__ms * 10.0);
while(__ticks) {
_delay_loop_2(cast(ushort) (((F_CPU) / 4e3) / 10));
__ticks--;
}
return;
} else
__ticks = cast(ushort) __tmp;
_delay_loop_2(__ticks);
}
// 这是来自`WebFreak`在`Dwiki`上的示例代码
enum AVR_ARCH = 5; // MCU 的 AVR 架构
static if (AVR_ARCH >= 100) {
enum SFR_OFFSET = 0x00;
} else {
enum SFR_OFFSET = 0x20;
}
enum ubyte* MMIO_BYTE(ubyte memAddr) = cast(ubyte*) memAddr;
enum ubyte* SFR_IO8(ubyte ioAddr) = MMIO_BYTE!(ioAddr + SFR_OFFSET);
enum ubyte* PINB = SFR_IO8!(0x03);
enum ubyte* DDRB = SFR_IO8!(0x04);
enum ubyte* PORTB = SFR_IO8!(0x05);
extern(C) void main() {
import core.volatile;
volatileStore(DDRB, 0xFF);
//端口所有B设置为输出
// 我添加了延迟
while (true) {
volatileStore(PORTB, 0xFF);
//端口所有B设置为高
foreach(i; 0 .. 10)
_delay(1000);
volatileStore(PORTB, 0x00);
//端口所有B设置为低
foreach(i; 0 .. 20)
_delay(1000);
}
}
先,
make
文件:
# Makefile
$ cat Makefile
all:
ldc2 delay.d -betterC -Oz -mtriple=avr -mcpu=atmega328p -Xcc=-mmcu=atmega328p -gcc=avr-gcc
avr-objcopy -O ihex -R .eeprom delay delay.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:delay.hex -C /opt/arduino/arduino-1.8.10/hardware/tools/avr/etc/avrdude.conf
请不要在
最后一行
的路径,再次安装
arduino
.
要运行它,请设置
PATH
为你的
arduino
安装,并运行
make
,如下所示:
PATH=/opt/arduino/arduino-1.8.10/hardware/tools/avr/bin:$PATH make
这编译
D代码
,然后把它
复制
到
arduino
硬件并
开始
运行它.灯(及连接到
引脚
上的其他东西)闪烁.
如果查看上面链接的
wiki
页面,其中
大部分
内容很熟悉.我随意
复制/粘贴
.
需要注意的一些
重要事项
:
1,不能用
extern(C)
从
arduinoC
的头文件中调用
delay_ms
,因为它是按内联函数
定义
的.库或目标文件中没有
内联函数
;你必须
移植
源.
源
WebFreak
在此移植了源码,我解释下.
gcc
和
ldc
中的内联
asm
略有不同.这是文件中的
_delay_loop_1
:
void _delay_loop_1(uint8_t __count)
{
__asm__ volatile (
"1: dec %0" "\n\t"
"brne 1b"
: "=r" (__count)
: "0" (__count)
);
}
而,如下是
D
文件中:
import ldc.llvmasm;
// delay_basic.h中的移植
void _delay_loop_1(ubyte __count) {
//该模板参数是必需的,以避免
//`!Call.getType()->isVoidTy()&&"Badinlineasm!"'`断定失败.
__asm!ubyte (
"1: dec $0\n\tbrne 1b",
"=r,0", (__count)
);
}