跳到主要內容

[Arduino]Inside Arduino: 分析Andes Andino M1的系統核心

這篇文章以Andes Andino M1為例,探索Arduino的系統組成,揭開Arduino的神秘面紗。
歡迎各位多多指教~

順帶一提

使用SSD的朋友們,為了減少占用C槽的空間,可以將你的Arduino IDE在安裝時選擇裝到D:下
然而,preference設定檔位置似乎無法修改路徑,在安裝新的板子時,toolchain等檔案會佔用不少C槽空間,因此可以在Windows建立以下連結,將檔案存放到D:再link回來歐~
mklink /d "C:\Users\使用者\AppData\Local\Arduino15" "D:\Program Files\Arduino\Arduino15"
另外,以下貼程式碼部分使用的是Google Code Prettify,有興趣的人自己Google啦~

直攻核心

以下用${P}代表preference設定檔位置的省略(preference設定檔位置在前一篇介紹過)
在Andino M1的board support package,其架構分為:
  1. ${P}\packages\Andino\hardware有nds32\1.6.5資料夾存放與板子相關的Arduino系統核心程式碼
  2. ${P}\packages\Andino\tools有m2c_burner\0.0.1與nds32le-elf-mculib-v3m\4.0.3分別為燒錄程式與toolchain
如同上一篇提到的,main.cpp出現在${P}\packages\Andino\hardware\nds32\1.6.5\cores\arduino\main.cpp
因此我們由這個Arduino系統核心程式碼main.cpp開始看起
#include "Arduino.h"

HardwareSerial Serial(UART0);
HardwareSerial Serial1(UART1);

int main( void )
{
    System_init();
    TIMER1_init();
    setup();
    for (;;)
    {
        loop();
    }
    return 0;
}
在以上程式碼中,Line 3,4看到class HardwareSerial兩個在global的instance: UART0、UART1
class HardwareSerial與main.cpp一樣定義在${P}\packages\Andino\hardware\nds32\1.6.5\cores\arduino路徑下
此class的繼承關係為:
  class HardwareSerial : public Stream
  class Stream : public Print
  class Print
再來看到main()中,其中Line 10 setup()與Line 14 loop()就是我們撰寫Ardunio Sketch的那兩大function
這也就是為什麼每次寫一個新的Sketch需要連著系統核心程式碼一起編譯連結
回到開頭的Line 1,Arduino.h將串起了整個Ardunio系統,其內容包含:
  1. include與MCU相關的Header,如: m2c8001.h, m2c8001_sys.h, m2c8001_int.h, m2c8001_uart.h,定義各模組的init、enable/disable等函數(這些Header定義在${P}\packages\Andino\hardware\nds32\1.6.5\cores\arduino\sys\inc)
  2. include標準C函式庫,如:stdint.h, stdlib.h, string.h, math.h, stdbool.h...等(這些Header定義在${P}\packages\Andino\tools\nds32le-elf-mculib-v3m\4.0.3\lib\gcc\nds32le-elf\4.9.2\include)
  3. include與CPU架構相關的Header,如: nds32_intrinsic.h(定義在${P}\packages\Andino\tools\nds32le-elf-mculib-v3m\4.0.3\lib\gcc\nds32le-elf\4.9.2\include)
  4. 定義來自Ardunio sketch的兩大函數extern void setup( void )與extern void loop( void )
  5. 以define macro方式轉換Ardunio sketch中的常數與函式,如:lowByte/highByte, bitRead/bitWrite, INPUT/OUTPUT/INPUT_PULLUP
  6. include提供Ardunio sketch的一些補強類別與函式,如:WString.h字串class、WCharacter.h字元函式、HardwareSerial.h、WMath.h
  7. 定義Ardunio sketch中使用的Digital I/O(pinMode, digitalWrite, digitalRead)、Analog I/O(analogReference, analogRead, analogWrite...)、Advanced I/O(pulseIn, shiftIn...)、External interrupt(attachInterrupt, detachInterrupt)、Time function(delay, delayMicroseconds...)、Ardunio觀點的GPIO pin numbers(D0~D13, A0~A5),以上除了GPIO pin numbers是以enum定義之外,其他都在${P}\packages\Andino\hardware\nds32\1.6.5\cores\arduino目錄下實作function,經由轉換後再呼叫MCU相關header的function操作
    如:以GPIO為例,
    digitalWrite()定義在${P}\packages\Andino\hardware\nds32\1.6.5\cores\arduino\Arduino.h,
    實作在${P}\packages\Andino\hardware\nds32\1.6.5\cores\arduino\wiring_digital.c並呼叫GPIO_SetOutput(),
    uint32_t pin_dmux[14]={20,19,23,24,3,18,7,10,1,2,28,25,26,27};
    
    void digitalWrite( int pin_m, uint8_t value )
    {
        uint32_t pin = pin_dmux[pin_m];
        GPIO_SetOutput(pin, value);
    }
    
    而GPIO_SetOutput()定義在${P}\packages\Andino\hardware\nds32\1.6.5\cores\arduino\sys\inc\m2c8001_gpio.h,
    實作在${P}\packages\Andino\hardware\nds32\1.6.5\cores\arduino\sys\driver\m2c8001_gpio.c
    void GPIO_SetOutput(u8 u8Idx, u8 u8Val)
    {
        if (u8Val == GPIO_OUT_HIGH)
            GPIO->DOUT |=(1 <<u8idx);
        else
            GPIO->DOUT &amp=(1 <<u8idx);
    }
    在以上這段code中,最神奇的是底層的code看起來竟然如此向物件導向,追根究柢發現原來GPIO是define的macro,是用struct來操作memory map I/O所對應到的那塊memory,以下節錄部分code(定義在${P}\packages\Andino\hardware\nds32\1.6.5\cores\arduino\sys\inc\m2c8001.h)出來:
    #define GPIO_BASE           (0x00F07000)
    #define GPIO    ((GPIO_TypeDef*)GPIO_BASE)
    typedef struct
    {
        __RW u32 DOUT;       /* Offset 0x00  GPIO data output register*/
        //以下省略
    }GPIO_TypeDef;
    
    所以說根本該做的事、該寫的code可是一行都不少呢!!
    只是Arduino幫你先寫好了,而且包的漂漂亮亮der~
    讓我們回顧一下,傳統使用Keil C操作8051系列時的寫法:
    #include &ltreg52.h&gt
    sbit LED = P2^0;            // Defining LED pin, port 2 bit 0
    
    void main (void)
    {
        while(1)                // infinite loop
        {
            LED = 0;            // LED ON
            Delay();
            LED = 1;            // LED OFF
            Delay();
        }
    }
    
    然後在reg52.h中定義的port 2(P2)為
    //...
    sfr P2    = 0xA0;
    //...
    
    是不是瞬間變難懂許多了呢~真的是code越包越多層,提供越高的abstraction人越容易懂,但是效能是不是也讓步了一點呢?
在看完Arduino.h的介紹之後,其實整個Arduino核心也差不多看完了,最後附上一張目錄的樹狀圖,有興趣的人自己trace看看啦~

What's Next

有興趣探討以上所提到的詳細實作細節,除了自己trace code以外,也可以看看"Arduino 底層原始碼解析心得(http://www.slideshare.net/roboard/arduino-385580)"~

留言

這個網誌中的熱門文章

Run Android VTS with Android R emulator through WSL

Background Android VTS The Android Vendor Test Suite (VTS) provides extensive new functionality for Android testing and promotes a test-driven development process. Android R emulator Run the latest Android R image from Google CI build artifacts WSL WSL is a collection of components that enables native Linux ELF64 binaries to run on Windows . It contains both user mode and kernel mode components. It is primarily comprised of: User mode session manager service that handles the Linux instance life cycle Pico provider drivers (lxss.sys, lxcore.sys) that emulate a Linux kernel by translating Linux syscalls Pico processes that host the unmodified user mode Linux (e.g. /bin/bash) It is the space between the user mode Linux binaries and the Windows kernel components where the magic happens. By placing unmodified Linux binaries in Pico processes we enable Linux system calls to be directed into the Windows kernel. The lxss.sys and lxcore.sys drivers translate the Linux s...

[Qt][Embedded][PXA270]
Porting Qt 4.8.6 for Creator XScale PXA270

這篇文章介紹這學期我在嵌入式作業系統課程,使用Creator XScale PXA270板子的一些學習筆記與製作final project的一些過程,歡迎各位多多指教~ 背景介紹 Creator XScale PXA270開發版是一塊由 新華電腦(microtime) 公司出品的 模組化XScale/ARM/SoC/FPGA/DSP嵌入式行動通訊發展平台 ,在此課程與final project主要使用的是"Create XScale‐PXA270 子板"與"MTLCD‐0708048 LCD Module",在Create XScale‐PXA270子板上的為Intel(現在已出售給Marvell)的 Xscale系列 PXA270 處理器,基於ARMv5TE架構指令集,在旁附有SDRAM Memory 64MB、Flash Memory 32MB、SD Card Connectors、RJ45 10/100 Base‐T Ethernet Interface、USB 1.1 Device/Host Port各一、AC97 Audio Codec(Line_in、Mic_in、Headphone)、ADC Interface*4、PWM Interface、CMOS Camera Interface、TFT-LCD Interface、GPIO Interface...等;在軟體功能部分,由新華科技提供了GNU GCC cross compiler toolchain(arm-unknown-linux-gnu-gcc 4.0.2)、Uboot 1.1.5、Linux Kernel 2.6.15.3與patch(提供支援子板上的Ethernet、USB Host、TFT‐LCD(Frame buffer、Touch Screen)、AC97‐Codec...等與母板LED、7‐Seg LED、Key Pad...等的device driver)、rootfs,相關的實驗環境設定可以參考 User Guide 。 看到以上這麼多的軟硬體介面,別以為這板子有多強,其實這是 2006 年 ASUS P535 手機使用的規格了,不禁讓人感嘆科技進步之快阿~ 在此附上開發版本尊 Qt 是自由且開放原始碼的跨平台C...

[Android][AOSP][ddmlib][Intelij IDEA][Gradle]搭建Android ddmlib編譯環境

Android ddmlib在AOSP的platform/tools/base的repo內 可以使用以下指令將整個repo的master branch抓回: git clone https://android.googlesource.com/platform/tools/base 取其中的annotations、common、ddmlib 建立Intelij IDEA專案 使用Intelij IDEA開一新的Java Project,GroupId、ArtifictedId、Version隨便,並使用graddle wrapper方式建立專案: GroupId 'com.android.tools.ddms' ArtifictedId = 'ddmlib' Version '1.0-SNAPSHOT' 刪除IDE建立的src資料夾,建立base資料夾,將annotations、common、ddmlib複製進來 參考Android Studio建立的專案,修改build.gradle為: // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { mavenCentral() } } allprojects { repositories { mavenCentral() } } task clean(type: Delete) { delete rootProject.buildDir } 修改settings.gradle,加入annotations、common、ddmlib為include rootProject.name = 'ddmlib' include ':base:annotations' include ':base:common' include ':base:ddmlib' 修改annotations、common、ddmlib內的b...