Arduino 编程(2):抛弃 IDE

有了前面轻松愉快的 hello LED,顿时你就明白在这个平台上写程序是非常的简单的,那么我们现在要做的第一件事情是抛弃 IDE。为什么抛弃 IDE?抛弃是为了使用 IDE。首先我们得学习和了解一些理论知识。

  • Atmel 推出的这个 Atmega8 系列的芯片包括好几个其他的型号,Arduino Uno 上装载的是 Atmega 328P,32 表示 program memory 大小为 32Kb,8 应该是指这是个 8bit 的 CPU,更多信息在官方的 data sheet 上有介绍。2K SRAM,20MIPS,工作电压在 1.8-5.5V,低压情况下主频会降低,28 引脚
  • Atmel 这个系列使用的 AVR 构架,因此编译时使用的 gcc-avr 来做的编译,在 Atmel 自己的芯片系列中,atmega 明显是比较低端的片子。06 年 Atmel 也发布了 32bit 的 AVR 用来和 ARM 竞争,不过似乎手机上还是 ARM 一统天下的样子 =.=
  • 这类片子适用的问题一般只是 controller,使用的计算资源比较少。

安装在 debian 上的几个相关 package:

  • arduino:只是 IDE,使用 java 写的
  • arduino-core:相关的库,例子,文档(网上的版本在此),
  • arduino-mk:提供的 Makefile 和一些例子
  • avrdude:下载程序到片子上的工具
  • avr-libc:用户使用的 libc,也是每个 arch 一堆自己的文件
  • gcc-avr 提供了编译器和一些头文件,每个 arch 还有自己的一个 gcov 和 gcc 的 lib

所以脱离了 IDE 干的事情无非是写个 Makefile,看看 Makefile 如何调用编译器,最后使用 avrdude 写到片子上。那么怎么把我们之前的 hello LED 不借助 IDE 走完整个流程呢?参看这个 thread,一种想法就是看看 IDE 到底干了啥,然后咱 reverse engineering 一个。首先打开 arduino IDE,到 File->Preference 里面选择 verify 和 upload 使用 verbose 信息,这样点一下 verify 就能在 console 里面看到一系列的命令了,下面是一个 summary

  • 用户在 IDE 中书写的程序被命名为 .ino 文件,这个文件会被转换到 tmp 里面对应的一个 cpp 文件,就观察来看,那个文件只是在头上加入了 #include “Arduino.h” 所以,如果我们需要自己来写,可以手工写这个 C++ 文件,而不需要半截子,自然编译的时候设置了 CPU 为 atmega328p,从 arduino-core 里面选择了几个目录作为 cpp 读入的路径
  • 接着编译了 /usr/share/arduino/hardware/arduino/avr/cores/arduino/ 下面几个 .c 文件,很明显这些应该是 reference 里面提供的那些 API 对应的代码
  • 编译了 /usr/share/arduino/hardware/arduino/avr/cores/arduino/avr-libc/ 下面几个文件,看起来是 libc 里面常用的内存管理的 API
  • 比较有意思的是 /usr/share/arduino/hardware/arduino/avr/cores/arduino/ 里面提供了一个 main 的部分(main.cpp),这样我们可以看到执行的结构
  • 通过 ar 将上面那些 .o 打包到 .a 文件用来连接用户目标文件
  • 通过 ld 连接 .a 和用户 .o 到一个 elf 文件(Linux 原生可执行文件格式)
  • 通过 objcopy 将 elf 文件中部分复制到 eep 文件和 hex 文件,这部分大概是为了转到能直接写入 atmega328p 的 EEPROM 格式吧

下面是 main.cpp 的内容

#include <Arduino.h>

int main(void) {
  init();
#if defined(USBCON)
  USBDevice.attach();
#endif

  setup();
  for (;;) {
    loop();
    if (serialEventRun) serialEventRun();
  }
  return 0;
}

有了这玩意大家想怎么 customize 就可以怎么 customize 了,非常简单的 event dispatcher。okay 再看上传,其实最后有用的只有下面这个命令:

/usr/share/arduino/hardware/tools/avrdude -C/usr/share/arduino/hardware/tools/avrdude.conf \
  -v -v -v -v -patmega328p -carduino -P/dev/ttyACM0 -b115200 -D \
  -Uflash:w:/tmp/build1869494284204795286.tmp/hello_led.cpp.hex:i

不难发现这里有用的就是生成出来的 hex 文件,它被 avrdude 通过 Linux 通过 USB 模拟出来的串口设备 /dev/ttyACM0 以波特率 115200 写入到设备中,配置文件提供了 programming hardware 的描述和部件的定义。

很明显有了这些信息,我们应该可以反向写出来一个合理的 Makefile,但是我们何必 reinvent the wheel 呢?看看 arduino-mk 吧!参考例子,我们在 hello_led 目录下(含有 ino 文件)建立如下 Makefile,

BOARD_TAG    = uno
ARDUINO_LIBS =

include /usr/share/arduino/Arduino.mk

执行发现某些路径不对,好在调试信息很完整,直接命令行上 override 这些路径即可:

BOARDS_TXT=/usr/share/arduino/hardware/arduino/avr/boards.txt ARDUINO_CORE_PATH=/usr/share/arduino/hardware/arduino/avr/cores/arduino ARDUINO_VAR_PATH=/usr/share/arduino/hardware/arduino/avr/variants make

输出了一堆之后报告成功编译

/usr/share/arduino/hardware/tools/avr/bin/avr-gcc -mmcu=atmega328p -Wl,--gc-sections -Os    -o build-uno/hello_led.elf build-uno/hello_led.o build-uno/libcore.a  -lc -lm
/usr/share/arduino/hardware/tools/avr/bin/avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
	--change-section-lma .eeprom=0 -O ihex build-uno/hello_led.elf build-uno/hello_led.eep
/usr/share/arduino/hardware/tools/avr/bin/avr-objcopy: --change-section-lma .eeprom=0x0000000000000000 never used
/usr/share/arduino/hardware/tools/avr/bin/avr-objcopy -O ihex -R .eeprom build-uno/hello_led.elf build-uno/hello_led.hex

/usr/share/arduino/hardware/tools/avr/bin/avr-size --mcu=atmega328p -C --format=avr build-uno/hello_led.elf
AVR Memory Usage
----------------
Device: atmega328p

Program:    1030 bytes (3.1% Full)
(.text + .data + .bootloader)

Data:          9 bytes (0.4% Full)
(.data + .bss + .noinit)

剩下的就是上传我们的 .hex 文件了,这个文件编译后在目录下生成了一个 build-uno 的子目录,所有 build 产生的文件在此。实际上在 Makefile 的说明中也提到了,定义 MONITOR_PORT 指向我们的设备(如 /dev/tty*)就好了,之后 make upload。一切搞定!

——————
And Samlah died, and Saul of Rehoboth by the river reigned in his stead

Advertisements
Arduino 编程(2):抛弃 IDE

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s