当前位置:   article > 正文

Arduino IDE的编译执行过程解读_void initarduino()

void initarduino()

avr-gcc

1997年ATMEL公司的A先生和V先生推出了全新配置的8位精简指令集微处理器(RISC-Reduced Instrction Sot CPU),起名为AVR。AVR是一种指令内核的统称,内部又分为ATtiny、AT90S、ATmega三大系列,分别对应AVR的低、中、高档产品。对于开发者而言,关注更多的是AVR单片机的开发方式,而AVR单片机最初在设计的时候的目的就是为了迎合采用高级编程语言来开发这一需求。AVR单片机高级语言开发工具有很多,其中WINAVR是个免费的AVR开发程序集,它以著名的自由软件GCC 为C/C++编译器。GCC可以编译多种语言,比如说C、C++、Objective-C、Fortran、Java和Ada等。AVR也得到了GCC的支持,它也是GCC 支持的唯一一种8 位处理器。

Ref

回到问题上来,Arduino作为一款很火的开源硬件,其编程环境Arduino IDE是processing IDE开发的,简单易用,关键是,这个IDE也是开源的。Arduino语言基于wiring语言开发的,也是对 AVR-GCC库的二次封装,所以Arduino的编程实现非常简单,即使没有单片机基础也可以去做Arduino开发。但是,在这些简单的编程语言背后的执行过程又是什么样的呢?

可以到github上去找Arduino的源码,再结合Arduino的官网发布的信息,自己去分析,下面给出几个有用的链接:

Arduino Builder

https://github.com/arduino/arduino-builder

Arduino IDE源码

https://github.com/arduino/Arduino/

Arduino Build Process

https://www.arduino.cc/en/Hacking/BuildProcess

其实本身也可以直接利用avr-gcc为Arduino编写程序,因为Arduino本身就是对avr-gcc的二次封装,只需要一个终端、文本编辑器和avr-gcc的工具链就可以了。

编译执行原理解读

当你在写自己的Arduino库文件的时候,会发现Arduino确实很奇葩,自己写好的.h和.cpp文件用#include<…>的形式直接引用的时候,Arduino IDE会认为这个文件不存在而报错。而且在一个项目中可以建立多个Sketch文件,不用#include<…>就可以直接合并到一块。

不扯了,那么在Arduino IDE中,究竟是如何编译执行的呢?其实,官网上有一篇很好的说明文档,Arduino Build Process

https://www.arduino.cc/en/Hacking/BuildProcess

从刚开始在Arduino IDE上书写的类C语言代码,再到最终可以在Arduino开发板子上运行的程序,其实,大概经过了下面几个步骤:

首先,Arduino IDE对代码进行转换,确保生成正确的C/C++代码(两种常用的编程语言);

再通过avr-gcc编译器将上一步生成的代码编译成机器能识别的指令,或者可以称之为目标文件;

然后,通过链接器将上一步生成的目标文件与标准的Arduino库文件(比如说,digitalWrite()等)共同链接,生成一个.hex文件,这个hex文件中的指定内容将被写入到Arduino开发板上的单片机的闪存中。

最后,再将hex文件上传到Arduino的板子上,比如说用USB或者串口,通过板子上已经有的bootloader传输到Arduino板,当然也可以通过其他的工具直接烧写。

文件预处理

对于在Arduino IDE中写好的源码,即sketch文件,以.ino为后缀,在把这个文件传给avr-gcc编译器之前,Arduino IDE会对这个sketch文件进行一系列预处理。在一开头加入#include "WProgram.h"(0023版本)或者 #include"Arduino.h"(1.0版本),Arduino.h的源码见附录,主要包含Arduino标准核心库所需的所有声明;Arduino IDE再遍历所有的.ino 文件中定义的函数,为它们创建函数原型,这些声明将被插在最前面的的注释、预处理语句(#include或#define)之后,其它语句(包括类型定义)之前。若在函数中使用了自定义类型,则需要将该类型的定义单独放入一个头文件中。然后将所有 .ino 文件拼接起来,最终当前目标板的main.cxx文件中的所有内容,追加在主sketch文件之后。

此外,Arduino IDE支持多种目标板与多种芯片、CPU频率、bootloader等,这些都是在板配置文件中进行定义,配置文件中包含name、mcu、单片机的时钟频率、链接时的核心库等信息。

编译及链接

Arduino IDE使用avr-gcc来编译程序文件。

首先,程序文件所在目录、目标板目录(/hardware/cores/<CORE>)和avr的include目录(/hardware/tools/avr/avr/include/),以及主程序文件引用的头文件所在的函数库目录(/hardware/libraries)会被加入到一个引用目录列表。

从Arduino IDE的源码中就可以知道:Arduino/app/src/processing/app/目录下的Sketch.java文件中,有下面这么一段代码:

  1. public void importLibrary(UserLibrary lib) throws IOException {
  2. importLibrary(lib.getSrcFolder());
  3. }
  4. /**
  5. * Add import statements to the current tab for all of packages inside
  6. * the specified jar file.
  7. */
  8. private void importLibrary(File jarPath) throws IOException {
  9. // make sure the user didn't hide the sketch folder
  10. ensureExistence();
  11. String list[] = Base.headerListFromIncludePath(jarPath);
  12. if (list == null || list.length == 0) {
  13. return;
  14. }
  15. // import statements into the main sketch file (code[0])
  16. // if the current code is a .java file, insert into current
  17. //if (current.flavor == PDE) {
  18. if (hasDefaultExtension(current.getCode())) {
  19. setCurrentCode(0);
  20. }
  21. // could also scan the text in the file to see if each import
  22. // statement is already in there, but if the user has the import
  23. // commented out, then this will be a problem.
  24. StringBuilder buffer = new StringBuilder();
  25. for (String aList : list) {
  26. buffer.append("#include <");
  27. buffer.append(aList);
  28. buffer.append(">\n");
  29. }
  30. buffer.append('\n');
  31. buffer.append(editor.getText());
  32. editor.setText(buffer.toString());
  33. editor.setSelection(0, 0); // scroll to start
  34. setModified(true);
  35. }

可以很清晰地看到Arduino IDE将库的路径添加到Compiler.headerListFromIncludePath中,然后在Editor中写入"#include"中。那些标准的库文件、 avr 头文件、 C 标准库文件都会被加入到引用目录列表中。

Arduino IDE 会扫描 main.cxx (即所有的 .ino 文件)中的#include < ...> ,如果无法在当前引用目录里找到的话,就会去 library 目录下查找所有子目录(只找一级),如果找到,就将其加入引用目录列表之中。

当编译一个sketch程序文件时,将在系统临时目录(如Mac里的/tmp)中进行构建。当上传一个sketch程序文件时,将在程序文件所在目录(可通过Sketch > Show Sketch Folder菜单进行访问)的applet/子目录中构建。

目标板核心的.c与.cpp文件将在同级目录下被编译生成.o文件,主程序文件、程序其它.c和.cpp文件及#include包含的函数库中的.c或.cpp文件同样处理。

这些.o文件将最终生成一个静态库,主程序文件与之链接。只有主程序中使用到的库代码才会被写入到最终的.hex文件中,所以这样做就减少了绝大多数程序的大小。

也就是说,对于普通的 C/C++ 文件,则会单独编译成一个静态库,最后和 main.cxx 以及各个 library 的编译结果链接起来。.hex文件是编译的最终文件,然后被上传到Arduino板。

附录

Arduino.h的源码:

  1. /*
  2. Arduino.h - Main include file for the Arduino SDK
  3. Copyright (c) 2005-2013 Arduino Team. All right reserved.
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with this library; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  15. */
  16. #ifndef Arduino_h
  17. #define Arduino_h
  18. #include <stdlib.h>
  19. #include <stdbool.h>
  20. #include <string.h>
  21. #include <math.h>
  22. #include <avr/pgmspace.h>
  23. #include <avr/io.h>
  24. #include <avr/interrupt.h>
  25. #include "binary.h"
  26. #ifdef __cplusplus
  27. extern "C"{
  28. #endif
  29. void yield(void);
  30. #define HIGH 0x1
  31. #define LOW 0x0
  32. #define INPUT 0x0
  33. #define OUTPUT 0x1
  34. #define INPUT_PULLUP 0x2
  35. #define PI 3.1415926535897932384626433832795
  36. #define HALF_PI 1.5707963267948966192313216916398
  37. #define TWO_PI 6.283185307179586476925286766559
  38. #define DEG_TO_RAD 0.017453292519943295769236907684886
  39. #define RAD_TO_DEG 57.295779513082320876798154814105
  40. #define EULER 2.718281828459045235360287471352
  41. #define SERIAL 0x0
  42. #define DISPLAY 0x1
  43. #define LSBFIRST 0
  44. #define MSBFIRST 1
  45. #define CHANGE 1
  46. #define FALLING 2
  47. #define RISING 3
  48. #if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  49. #define DEFAULT 0
  50. #define EXTERNAL 1
  51. #define INTERNAL 2
  52. #else
  53. #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__)
  54. #define INTERNAL1V1 2
  55. #define INTERNAL2V56 3
  56. #else
  57. #define INTERNAL 3
  58. #endif
  59. #define DEFAULT 1
  60. #define EXTERNAL 0
  61. #endif
  62. // undefine stdlib's abs if encountered
  63. #ifdef abs
  64. #undef abs
  65. #endif
  66. #define min(a,b) ((a)<(b)?(a):(b))
  67. #define max(a,b) ((a)>(b)?(a):(b))
  68. #define abs(x) ((x)>0?(x):-(x))
  69. #define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
  70. #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
  71. #define radians(deg) ((deg)*DEG_TO_RAD)
  72. #define degrees(rad) ((rad)*RAD_TO_DEG)
  73. #define sq(x) ((x)*(x))
  74. #define interrupts() sei()
  75. #define noInterrupts() cli()
  76. #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
  77. #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
  78. #define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )
  79. #define lowByte(w) ((uint8_t) ((w) & 0xff))
  80. #define highByte(w) ((uint8_t) ((w) >> 8))
  81. #define bitRead(value, bit) (((value) >> (bit)) & 0x01)
  82. #define bitSet(value, bit) ((value) |= (1UL << (bit)))
  83. #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
  84. #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
  85. // avr-libc defines _NOP() since 1.6.2
  86. #ifndef _NOP
  87. #define _NOP() do { __asm__ volatile ("nop"); } while (0)
  88. #endif
  89. typedef unsigned int word;
  90. #define bit(b) (1UL << (b))
  91. typedef bool boolean;
  92. typedef uint8_t byte;
  93. void init(void);
  94. void initVariant(void);
  95. int atexit(void (*func)()) __attribute__((weak));
  96. void pinMode(uint8_t, uint8_t);
  97. void digitalWrite(uint8_t, uint8_t);
  98. int digitalRead(uint8_t);
  99. int analogRead(uint8_t);
  100. void analogReference(uint8_t mode);
  101. void analogWrite(uint8_t, int);
  102. unsigned long millis(void);
  103. unsigned long micros(void);
  104. void delay(unsigned long);
  105. void delayMicroseconds(unsigned int us);
  106. unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout);
  107. unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout);
  108. void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
  109. uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder);
  110. void attachInterrupt(uint8_t, void (*)(void), int mode);
  111. void detachInterrupt(uint8_t);
  112. void setup(void);
  113. void loop(void);
  114. // Get the bit location within the hardware port of the given virtual pin.
  115. // This comes from the pins_*.c file for the active board configuration.
  116. #define analogInPinToBit(P) (P)
  117. // On the ATmega1280, the addresses of some of the port registers are
  118. // greater than 255, so we can't store them in uint8_t's.
  119. extern const uint16_t PROGMEM port_to_mode_PGM[];
  120. extern const uint16_t PROGMEM port_to_input_PGM[];
  121. extern const uint16_t PROGMEM port_to_output_PGM[];
  122. extern const uint8_t PROGMEM digital_pin_to_port_PGM[];
  123. // extern const uint8_t PROGMEM digital_pin_to_bit_PGM[];
  124. extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[];
  125. extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];
  126. // Get the bit location within the hardware port of the given virtual pin.
  127. // This comes from the pins_*.c file for the active board configuration.
  128. //
  129. // These perform slightly better as macros compared to inline functions
  130. //
  131. #define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
  132. #define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )
  133. #define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) )
  134. #define analogInPinToBit(P) (P)
  135. #define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )
  136. #define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )
  137. #define portModeRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_mode_PGM + (P))) )
  138. #define NOT_A_PIN 0
  139. #define NOT_A_PORT 0
  140. #define NOT_AN_INTERRUPT -1
  141. #ifdef ARDUINO_MAIN
  142. #define PA 1
  143. #define PB 2
  144. #define PC 3
  145. #define PD 4
  146. #define PE 5
  147. #define PF 6
  148. #define PG 7
  149. #define PH 8
  150. #define PJ 10
  151. #define PK 11
  152. #define PL 12
  153. #endif
  154. #define NOT_ON_TIMER 0
  155. #define TIMER0A 1
  156. #define TIMER0B 2
  157. #define TIMER1A 3
  158. #define TIMER1B 4
  159. #define TIMER1C 5
  160. #define TIMER2 6
  161. #define TIMER2A 7
  162. #define TIMER2B 8
  163. #define TIMER3A 9
  164. #define TIMER3B 10
  165. #define TIMER3C 11
  166. #define TIMER4A 12
  167. #define TIMER4B 13
  168. #define TIMER4C 14
  169. #define TIMER4D 15
  170. #define TIMER5A 16
  171. #define TIMER5B 17
  172. #define TIMER5C 18
  173. #ifdef __cplusplus
  174. } // extern "C"
  175. #endif
  176. #ifdef __cplusplus
  177. #include "WCharacter.h"
  178. #include "WString.h"
  179. #include "HardwareSerial.h"
  180. #include "USBAPI.h"
  181. #if defined(HAVE_HWSERIAL0) && defined(HAVE_CDCSERIAL)
  182. #error "Targets with both UART0 and CDC serial not supported"
  183. #endif
  184. uint16_t makeWord(uint16_t w);
  185. uint16_t makeWord(byte h, byte l);
  186. #define word(...) makeWord(__VA_ARGS__)
  187. unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
  188. unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
  189. void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0);
  190. void noTone(uint8_t _pin);
  191. // WMath prototypes
  192. long random(long);
  193. long random(long, long);
  194. void randomSeed(unsigned long);
  195. long map(long, long, long, long, long);
  196. #endif
  197. #include "pins_arduino.h"
  198. #endif

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/67612
推荐阅读
相关标签
  

闽ICP备14008679号