1. 程式人生 > >異構多核處理器開發嵌入式應用入門

異構多核處理器開發嵌入式應用入門

By Toradex Raul Rosetto Mu?oz

1). 簡介

每天都有新的異構多核處理器/片上系統 SoC 面市。在 SoC 上整合微控制器和外設控制核正變得越來越普遍,看看最新發布的 NXP? :i.MX 6SoloX、i.MX7 和即將面世的 i.MX 8。在我看來,這有點像曾經 ADC(模數轉換器)開始整合微處理器上的外設功能,在應用處理器上整合微控制器,可以解決 Linux 系統中一些實時可控相關的問題。

新技術的出現總是會引出許多問題,或許你會產生疑問,這是否需要很多工作量。本位旨在快速、明瞭地介紹一種使用異構多核方式開發應用的方法。這裡我們將會涉及搭建開發環境以及建立一個雙核通訊的 ping pong 應用的基本步驟,最後演示一個用微控制器通過 SPI 讀取 ADC 資料並把資料傳送至執行 Linux 的處理器的實際應用。

這是揭示利用異構多核處理構架 SoC 開發嵌入式系統的系列文章。通過實際操作和一些案例演示,你可以快速地開始開發。

2). 硬體

本文中將使用 Toradex 雙核 Colibri iMX7 計算機模組:該模組採用 NXP i.MX7 SoC,一個雙核 ARM Cortex-A7 和 一個 ARM Cortex-M4 核心,A7 主頻為 1GHz,M4 主頻為 200MHz,同時具備 512MB 儲存和 512MB 記憶體。模組如下圖所示:

載板採用 Aster。這是 Toradex 新發布的產品,使新專案開發更加容易。該載板具有標準的 Arduino 介面,使開發人員能夠利用市面上豐富的 Arduino 模組,縮減研發時間。除了 Arduino,還有一個相容 Raspberry Pi 的介面,允許在開發的硬體上使用模組,不僅能夠促進新產品的原型開發,也能夠幫助從概念驗證到可擴充套件、工業品質、保證生命週期硬體方案如 Toradex 的過渡。

3). 搭建開發環境

本文中演示的案例是在 Linux 電腦上開發的。所有 Cortex-M 上的程式碼都基於 Makefile 和 Cmake。你只需要安裝少量的軟體並正確配置編譯工具鏈,就可以編譯示例程式碼。

a). 我們建議使用 4.9 2015 Q3 版本 linaro toolchain。從這裡下載好壓縮包後,解壓如下:

-------------------------------------------

tar xjf ~/Downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2

-------------------------------------------

b). 因為編譯工具生成 32位應用,所以需要安裝 32位的 libc 和 libncurse。在 Ubuntu 上,命令如下:

-------------------------------------------

sudo dpkg --add-architecture i386 sudo apt-get update sudo apt-get install libc6:i386 libncurses5:i386

-------------------------------------------

c). 現在可以測試編譯工具:

-------------------------------------------

~/gcc-arm-none-eabi-4_9-2015q3/bin/arm-none-eabi-gcc –version

arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.9.3 20150529 (release) [ARM/embedded-4_9-branch revision 227977] Copyright (C) 2014 Free Software Foundation, Inc.

This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

-------------------------------------------

d). 最後,安裝 cmake 和 make:

-------------------------------------------

sudo apt-get install make cmake

-------------------------------------------

4). 下載示例

我們準備了一些示例,方便下載和測試,包括基本的 雙核通訊“Hello, World!”。下載原始碼:

-------------------------------------------

$ git clone -b colibri-imx7-m4-freertos-v8 git://git.toradex.com/freertos-toradex.git freertos-colibri-imx7/

$ cd freertos-colibri-imx7/

-------------------------------------------

所有我們將會使用的原始碼都在這個資料夾裡面。其中的檔案已經能夠支援 Colibri iMX7 和 FreeRTOS。在所有這些檔案中,我們主要使用包含示例的的資料夾:

-------------------------------------------

[[email protected] freertos-colibri-imx7]$ tree -L 2 examples/imx7_colibri_m4/

examples/imx7_colibri_m4/

├── board.c

├── board.h

├── clock_freq.c

├── clock_freq.h

├── demo_apps

│   ├── blinking_imx_demo

│   ├── hello_world

│   ├── hello_world_ddr

│   ├── hello_world_ocram

│   ├── low_power_imx7d

│   ├── rpmsg

│   └── sema4_demo

├── driver_examples

│   ├── adc_imx7d

│   ├── ecspi

│   ├── flexcan

│   ├── gpio_imx

│   ├── gpt

│   ├── i2c_imx

│   ├── uart_imx

│   └── wdog_imx

├── gpio_pins.c

├── gpio_pins.h

├── pin_mux.c

└── pin_mux.h

17 directories, 8 files

[[email protected] freertos-colibri-imx7]$

-------------------------------------------

5). 搭建硬體環境

本文中,我們將不涉及如何除錯 Cortex-M 的內容,我們使用 UART 列印韌體的輸出資訊。瞭解如何搭建產品開發環境是十分重要的。由於 Cortex-M 和  Cortex-A 共享外設介面,你需要知道 UART B 被 Cortex-M 上的韌體輸出列印資訊,UART A 則由 Cortex-A (U-boot and Linux) 使用。

所以我們將使用 UART A 和 UART B。對於 UART A,在 Aster 上已經有 FTDI 晶片,可以直接連線 USB X4。該介面不僅用於給載板供電,還可以訪問 UART-A, 所以當連線到電腦後,/dev/ttyUSBX 裝置將會被自動識別。

對於 UART B, Colibri iMX7  的 TX 和 RX 引腳在 X20 擴充套件口上。因為沒有 FTDI 或者 RS-232 轉換器,你需要使用 FTDI 串列埠線。連線 RX、TX 和 GND 到 X20 的 第8、10、9 引腳。

最後,圖下圖所示連線:

現在都已經正確連線,在 Linux 使用 picocom 開啟兩個終端,開啟串列埠:

終端 1:

-------------------------------------------

[[email protected] ~]$ picocom -b 115200 /dev/ttyUSB0

-------------------------------------------

終端 2:

-------------------------------------------

[[email protected] ~]$ picocom -b 115200 /dev/ttyUSB1

-------------------------------------------

6). 編譯第一個示例

a). 進入 SPI 示例目錄,編譯第一個應用:

-------------------------------------------

[[email protected] freertos-colibri-imx7]$ cd examples/imx7_colibri_m4/driver_examples/ecspi/ecspi_interrupt/master/

[[email protected] master]$ ls armgcc hardware_init.c main.c

-------------------------------------------

b). 所有的示例都有 main.c 、hardware_init.c 和 armgcc 資料夾。我們先不解釋原始碼,只是進入目錄,匯出下載的 toolchain 路徑然後編譯:

-------------------------------------------

[[email protected] armgcc]$ cd ..

[[email protected] master]$ cd armgcc/

[[email protected] armgcc]$ export ARMGCC_DIR=~/gcc-arm-none-eabi-4_9-2015q3/

[[email protected] armgcc]$ ./build_all.sh

-- TOOLCHAIN_DIR: /home/raul/gcc-arm-none-eabi-4_9-2015q3/

-- BUILD_TYPE: Debug

-- TOOLCHAIN_DIR: /home/raul/gcc-arm-none-eabi-4_9-2015q3/

-- BUILD_TYPE: Debug

-- Could not determine Eclipse version, assuming at least 3.6 (Helios). Adjust CMAKE_ECLIPSE_VERSION if this is wrong.

-- The ASM compiler identification is GNU

-- Found assembler: /home/raul/gcc-arm-none-eabi-4_9-2015q3//bin/arm-none-eabi-gcc

-- Configuring done

-- Generating done

-- Build files have been written to: /home/raul/freertos-colibri-imx7/examples/imx7_colibri_m4/driver_examples/ecspi/ecspi_interrupt/master/armgcc

Scanning dependencies of target ecspi_interrupt_master_example

[  5%] Building C object CMakeFiles/ecspi_interrupt_master_example.dir/home/raul/freertos-colibri-imx7/platform/utilities/src/debug_console_imx.c.obj

...

...

...

[ 94%] Building C object CMakeFiles/ecspi_interrupt_master_example.dir/home/raul/freertos-colibri-imx7/platform/drivers/src/uart_imx.c.obj

[100%] Linking C executable debug/ecspi_interrupt_master_example.elf

[100%] Built target ecspi_interrupt_master_example

-- TOOLCHAIN_DIR: /home/raul/gcc-arm-none-eabi-4_9-2015q3/

-- BUILD_TYPE: Release

-- Eclipse version is set to 3.6 (Helios). Adjust CMAKE_ECLIPSE_VERSION if this is wrong.

-- Configuring done

-- Generating done

CMake Warning:

  Manually-specified variables were not used by the project:

    CMAKE_TOOLCHAIN_FILE

-- Build files have been written to: /home/raul/freertos-colibri-imx7/examples/imx7_colibri_m4/driver_examples/ecspi/ecspi_interrupt/master/armgcc

[  5%] Building ASM object CMakeFiles/ecspi_interrupt_master_example.dir/home/raul/freertos-colibri-imx7/platform/devices/MCIMX7D/startup/gcc/startup_MCIMX7D_M4.S.obj

...

...

...

[ 94%] Building C object CMakeFiles/ecspi_interrupt_master_example.dir/home/raul/freertos-colibri-imx7/platform/drivers/src/uart_imx.c.obj

[100%] Linking C executable release/ecspi_interrupt_master_example.elf

[100%] Built target ecspi_interrupt_master_example

[[email protected] armgcc]$

         The binaries are located in the "release" directory.

[[email protected] armgcc]$ cd release/

[[email protected] release]$ ls

ecspi_interrupt_master_example.bin  ecspi_interrupt_master_example.hex

ecspi_interrupt_master_example.elf  ecspi_interrupt_master_example.map

[[email protected] release]$

-------------------------------------------

在這裡, bin 檔案是最重要的。我們使用 U-boot 將其載入到 Cortex-M4。

7). 執行韌體程式

為了執行韌體程式,U-boot 需要載入這個二進位制檔案,然後在 Cortex-M 上執行。也可以用另外的方法。我的建議是使用 SD 卡或者網路。我們將會演示如何使用這兩種方法。一方面,需要知道的是使用網路,開發將以動態的方式進行,因為不需要在載板上拔插 SD 卡。另一方面,為了使用乙太網載入檔案,你需要配置 tftp 伺服器,我這裡配置為 "/srv/tftp/"。參考 Flashing Linux Over Ethernet 瞭解 tftp 配置。

a). SD 卡:

複製檔案到 SD 卡,然後放到載板上:

-------------------------------------------

[[email protected] release]$ df

Filesystem              1K-blocks      Used Available Use% Mounted on

/dev/sdb1                 7780496    469540   7310956   7% /run/media/raul/DATA

[[email protected] release]$ cp ecspi_interrupt_master_example.bin /run/media/raul/DATA

[[email protected] release]$ umount /run/media/raul/DATA

-------------------------------------------

b). 乙太網:

複製檔案到 tftp 伺服器目錄,在載板上連線網線,配置好能夠連線到電腦的網路。這裡載板的 IP 是 192.168.0.170,電腦 IP 為 192.168.0.150。

-------------------------------------------

[[email protected] release]$ cp ecspi_interrupt_master_example.bin /srv/tftp/

-------------------------------------------

c). 開啟載板電源,上電的時候,在 UART-A (U-boot and Linux)  終端上按下任意按鍵。進入 U-boot,載入可執行檔案。

./ SD 卡:

-------------------------------------------

Colibri iMX7 # fatload mmc 0:1 0x7F8000 ecspi_interrupt_master_example.bin

reading ecspi_interrupt_master_example.bin

9956 bytes read in 20 ms (485.4 KiB/s)

-------------------------------------------

./ 乙太網:

Colibri iMX7 # tftp 0x7F8000 ecspi_interrupt_master_example.bin

Using FEC0 device

TFTP from server 192.168.0.150; our IP address is 192.168.0.170

Filename 'ecspi_interrupt_master_example.bin'. Load address: 0x7f8000

Loading: ################################################## 9.7 KiB

647.5 KiB/s

done

Bytes transferred = 9956 (26e4 hex)

-------------------------------------------

d). 載入完成後,無論是使用 SD 卡還是乙太網,執行下面的命令運作已經載入到 Cortex-M 上的程式。

-------------------------------------------

Colibri iMX7 # dcache flush

Colibri iMX7 # bootaux 0x7F8000

## Starting auxiliary core at 0x007F8000 ...

Colibri iMX7 #

-------------------------------------------

e). 接下來,你應該可以看到在 UART B 終端上打印出 Cortex-M 的除錯資訊。你的螢幕如下圖所示。

f). 在 UART B 終端裡按 “s”之前,試著將 SPI MISO 和 MOSI 連線起來。這樣就可以看到在迴環模式下的通訊,不僅是傳送資料,還可以接收 SPI 資料。

-------------------------------------------

-------------- ECSPI master driver example --------------

This example application demonstrates usage of SPI driver in master mode.

It transfers data to/from remote MCU in SPI slave mode.

Press "s" when spi slave is ready.

MASTER: Transmited data: 1 :

Received data: 1

MASTER: Transmited data: 2 :

Received data: 2 ... ... ...

MASTER: Transmited data: 19 :

Received data: 19

MASTER: Transmited data: 20 :

Received data: 20

-------------------------------------------

8). 示例 - SPI

a). 在之前的示例中,我們只編譯和執行了程式碼。現在我們將修改原始碼,實現同 Microchip MCP3008 的 SPI 通訊。這個一個10位 ADC,具有8個輸入。按下圖連線到 Aster 和麵包板:

b). 如果喜歡使用 Eclipse IDE,可以通過 CMake 生成 Eclipse 專案檔案。 Cmake 的 -G 引數可以配置 “build system generator”。確保 build_all.sh 指定 “Eclipse CDT4 – Unix Makefiles”。

./ 在 armgcc 示例目錄中:

-------------------------------------------

[[email protected] armgcc]$ vi build_all.sh

#!/bin/sh cmake -DCMAKE_TOOLCHAIN_FILE="../../../../../../../tools/cmake_toolchain_files/armgcc.cmake" -G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug .

make -j4

cmake -DCMAKE_TOOLCHAIN_FILE="../../../../../../../tools/cmake_toolchain_files/armgcc.cmake" -G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_BUILD_TYPE=Release .

make -j4

-------------------------------------------

./ 接下來執行 “build_all.sh”指令碼:

-------------------------------------------

[[email protected] armgcc]$ ./build_all.sh

[[email protected] armgcc]$ ls .cproject .project

.cproject .project

-------------------------------------------

./ 開啟 Eclipse 並匯入專案

File > Import…

./ 在 “Select root directory”,輸入 “armgcc”資料夾目錄

-------------------------------------------

/home/raul/freertos-colibri-imx7/examples/imx7_colibri_m4/driver_examples/ecspi/ecspi_interrupt/master/armgcc

-------------------------------------------

./ 開啟目錄中的 main.c”檔案

[TARGET] → [exec]ecspi_interrupt_master_example → Source Files

./ 標準的示例是十分簡單的。我們有必要介紹部分程式碼,從而在下面的示例中能夠清楚地瞭解需要檢視什麼地方。

-------------------------------------------

int main(void)

{

    uint8_t control_char;

    uint8_t i;

    ecspi_init_config_t ecspiMasterInitConfig = {

        .baudRate = 500000,

        .mode = ecspiMasterMode,

        .burstLength = ECSPI_MASTER_BURSTLENGTH,

        .channelSelect = BOARD_ECSPI_CHANNEL,

        .clockPhase = ecspiClockPhaseSecondEdge,

        .clockPolarity = ecspiClockPolarityActiveHigh,

        .ecspiAutoStart = ECSPI_MASTER_STARTMODE

    };

    /* Hardware initialize, include RDC, CLOCK, IOMUX, ENABLE MODULE */

    hardware_init();

    /* Update clock frequency of this module */

    ecspiMasterInitConfig.clockRate = get_ecspi_clock_freq(BOARD_ECSPI_BASEADDR);

    PRINTF("\n-------------- ECSPI master driver example --------------\n\n\r");

    PRINTF("This example application demonstrates usage of SPI driver in master mode.\n\r");

    PRINTF("It transfers data to/from remote MCU in SPI slave mode.\n\r");

    /* Ecspi module initialize, include configure parameters */

    ECSPI_MasterConfig(&ecspiMasterInitConfig);

    /* Wait slave ready, then press 's' to start communication. */

    while(true)

    {

        PRINTF("Press \"s\" when spi slave is ready.\n\r");

        control_char = GETCHAR();

        if((control_char == 's') || (control_char == 'S'))

            break;

    }

    /* Send 1~20 to slave and receive data from slave */

    for(i = 0; i < 20; i++)

    {

        txData[0]++;

        ECSPI_MasterTransfer((uint8_t*)txData, (uint8_t*)rxData, 1);

        while(ECSPI_MasterGetTransferStatus());

        PRINTF("MASTER: Transmited data: %d \n\r", txData[0]);

        PRINTF("      : Received data: %d \n\n\r", rxData[0]);

    }

    while(1);

}

-------------------------------------------

./ 第一個需要注意的配置引腳複用的地方。這裡我們將使用標準的 SPI。右擊“hardware_init();”函式,選擇“Open Declaration”

-------------------------------------------

void hardware_init(void)

{

    /* Board specific RDC settings */

    BOARD_RdcInit();

    /* Board specific clock settings */

    BOARD_ClockInit();

    /* initialize debug uart */

    dbg_uart_init();

    /* RDC ECSPI */

    RDC_SetPdapAccess(RDC, BOARD_ECSPI_RDC_PDAP, 3 << (BOARD_DOMAIN_ID * 2), false, false);

    /* Select board ecspi clock derived from OSC clock(24M) */

    CCM_UpdateRoot(CCM, BOARD_ECSPI_CCM_ROOT, ccmRootmuxEcspiOsc24m, 0, 0);

    /* Enable ecspi clock gate */

    CCM_EnableRoot(CCM, BOARD_ECSPI_CCM_ROOT);

    CCM_ControlGate(CCM, BOARD_ECSPI_CCM_CCGR, ccmClockNeededAll);

    /* Configure ecspi pin IOMUX */

    configure_ecspi_pins(BOARD_ECSPI_BASEADDR);

}

-------------------------------------------

./ 主要的硬體初始化和配置都在這個函式中完成。SPI 引腳的配置在最後一個函式“configure_ecspi_pins(BOARD_ECSPI_BASEADDR);”。

-------------------------------------------

void configure_ecspi_pins(ECSPI_Type* base)

{

         // ECSPI1 iomux configuration

         /* daisy chain selection */

         IOMUXC_ECSPI3_MISO_SELECT_INPUT = 0;  //(I2C1_SCL  SODIM 90)

         IOMUXC_ECSPI3_MOSI_SELECT_INPUT = 0;  //(I2C1_SCL  SODIM 90)

         /* iomux */

         IOMUXC_SW_MUX_CTL_PAD_I2C2_SCL = IOMUXC_SW_MUX_CTL_PAD_I2C2_SCL_MUX_MODE(3);    /* ECSPI SLK  */

         IOMUXC_SW_MUX_CTL_PAD_I2C1_SDA = IOMUXC_SW_MUX_CTL_PAD_I2C1_SDA_MUX_MODE(3);    /* ECSPI MOSI */

         IOMUXC_SW_MUX_CTL_PAD_I2C1_SCL = IOMUXC_SW_MUX_CTL_PAD_I2C1_SCL_MUX_MODE(3);    /* ECSPI MISO  */

         IOMUXC_SW_MUX_CTL_PAD_I2C2_SDA  = IOMUXC_SW_MUX_CTL_PAD_I2C2_SDA_MUX_MODE(3);     /* ECSPI SS0 */

         /* pad control */

         IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL =    IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL_PE_MASK  |

                            IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL_PS(0)    |      /* pull down */

                            IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL_DSE(0)   |

                            IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL_HYS_MASK;

         IOMUXC_SW_PAD_CTL_PAD_I2C1_SDA = IOMUXC_SW_PAD_CTL_PAD_I2C1_SDA_DSE(0)   |

                            IOMUXC_SW_PAD_CTL_PAD_I2C1_SDA_HYS_MASK;

         IOMUXC_SW_PAD_CTL_PAD_I2C1_SCL = IOMUXC_SW_PAD_CTL_PAD_I2C1_SCL_HYS_MASK;

         IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA  =  IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA_PE_MASK   |

                            IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA_PS(3)     |      /* pull up */

                            IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA_DSE(0)    |

                            IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA_HYS_MASK;

}

-------------------------------------------

./ 另外一個重要的檔案是“board.h”。在同一個函式中,搜尋 "configure_ecspi_pins (BOARD_ECSPI_BASEADDR);" 中的 "BOARD_ECSPI_BASEADDR",你將會發現部分“board.h”內容,這裡配置除了 SPI 外的其他內容,例如中斷向量表。

-------------------------------------------

/* Colibri SPI is ECSPI3 */

#define BOARD_ECSPI_RDC_PDAP                rdcPdapEcspi3

#define BOARD_ECSPI_CCM_ROOT                ccmRootEcspi3

#define BOARD_ECSPI_CCM_CCGR                ccmCcgrGateEcspi3

#define BOARD_ECSPI_BASEADDR                ECSPI3

#define BOARD_ECSPI_CHANNEL                 ecspiSelectChannel0

#define BOARD_ECSPI_IRQ_NUM                 eCSPI3_IRQn

#define BOARD_ECSPI_HANDLER                 eCSPI3_Handler

-------------------------------------------

./ 回到“main.c”我將改變主函式,獲取 MCP3008 的資料。具體地講,我們將讀取晶片 channel 0 的資料。

-------------------------------------------

/* Wait slave ready, then press 's' to start communication. */

    while(true)

    {

        PRINTF("Press \"s\" when spi slave is ready.\n\r");

        control_char = GETCHAR();

        if((control_char == 's') || (control_char == 'S'))

            break;

    }

-------------------------------------------

./ 刪除“break”,增加下面的程式碼。根據 MCP3008 白皮書,“00000001 10000000 00000000”序列分別表示起始位、通道選擇和10位資料的資訊。

-------------------------------------------

/* Wait slave ready, then press 's' to start communication. */

         while(true)

         {

                   PRINTF("Press \"s\" when spi slave is ready.\n\r");

                   control_char = GETCHAR();

                   if((control_char == 's') || (control_char == 'S'))

                   {

                            unsigned char datatx[3];

                            unsigned char datarx[3];

                            datatx[0] = 0b00000001;  //  first byte transmitted -> start bit

                            datatx[1] = 0b10000000; // second byte transmitted -> (SGL/DIF = 1, D2=D1=D0=0)

                            datatx[2] = 0b00000000; // third byte transmitted....don't care

                            /* SPI Read */

                            ECSPI_MasterTransfer((uint8_t*)&datatx[0], (uint8_t*)&datarx[0], 3);

                            while(ECSPI_MasterGetTransferStatus());

                            PRINTF("Transmited data: %d \n\r", datatx[0]);

                            PRINTF("Transmited data: %d \n\r", datatx[1]);

                            PRINTF("Transmited data: %d \n\r", datatx[2]);

                            PRINTF("Received data: %d \n\n\r", datarx[0]);

                            PRINTF("Received data: %d \n\n\r", datarx[1]);

                            PRINTF("Received data: %d \n\n\r", datarx[2]);

                            unsigned int a2dVal = 0;

                            a2dVal = (datarx[1]<< 8) & 0b1100000000; //merge data[1] & data[2] to get result

                            a2dVal |=  (datarx[2] & 0xff);

                            PRINTF("data = %d \n\n\r", a2dVal);

                   }

         }

-------------------------------------------

./ 修改完畢後,“int main (void)” 應該如下:

-------------------------------------------

int main(void)

{

         uint8_t control_char;

         uint8_t i;

         ecspi_init_config_t ecspiMasterInitConfig = {

                            .baudRate = 500000,

                            .mode = ecspiMasterMode,

                            .burstLength = ECSPI_MASTER_BURSTLENGTH,

                            .channelSelect = BOARD_ECSPI_CHANNEL,

                            .clockPhase = ecspiClockPhaseSecondEdge,

                            .clockPolarity = ecspiClockPolarityActiveHigh,

                            .ecspiAutoStart = ECSPI_MASTER_STARTMODE

         };

         /* Hardware initialize, include RDC, CLOCK, IOMUX, ENABLE MODULE */

         hardware_init();

         /* Update clock frequency of this module */

         ecspiMasterInitConfig.clockRate = get_ecspi_clock_freq(BOARD_ECSPI_BASEADDR);

         PRINTF("\n-------------- ECSPI master driver example --------------\n\n\r");

         PRINTF("This example application demonstrates usage of SPI driver in master mode.\n\r");

         PRINTF("It transfers data to/from remote MCU in SPI slave mode.\n\r");

         /* Ecspi module initialize, include configure parameters */

         ECSPI_MasterConfig(&ecspiMasterInitConfig);

         /* Wait slave ready, then press 's' to start communication. */

         while(true)

         {

                   PRINTF("Press \"s\" when spi slave is ready.\n\r");

                   control_char = GETCHAR();

                   if((control_char == 's') || (control_char == 'S'))

                   {

                            unsigned char datatx[3];

                            unsigned char datarx[3];

                            datatx[0] = 0b00000001;  //  first byte transmitted -> start bit

                            datatx[1] = 0b10000000; // second byte transmitted -> (SGL/DIF = 1, D2=D1=D0=0)

                            datatx[2] = 0b00000000; // third byte transmitted....don't care

                            /* SPI Read */

                            ECSPI_MasterTransfer((uint8_t*)&datatx[0], (uint8_t*)&datarx[0], 3);

                            while(ECSPI_MasterGetTransferStatus());

                            PRINTF("Transmited data: %d \n\r", datatx[0]);

                            PRINTF("Transmited data: %d \n\r", datatx[1]);

                            PRINTF("Transmited data: %d \n\r", datatx[2]);

                            PRINTF("Received data: %d \n\n\r", datarx[0]);

                            PRINTF("Received data: %d \n\n\r", datarx[1]);

                            PRINTF("Received data: %d \n\n\r", datarx[2]);

                            unsigned int a2dVal = 0;

                            a2dVal = (datarx[1]<< 8) & 0b1100000000; //merge data[1] & data[2] to get result

                            a2dVal |=  (datarx[2] & 0xff);

                            PRINTF("data = %d \n\n\r", a2dVal);

                   }

         }

}

-------------------------------------------

./ 重新編譯,根據前面的示例通過 SD 卡或者乙太網複製,執行二進位制程式。

SD 卡:

-------------------------------------------

[[email protected] release]$ df

Filesystem              1K-blocks      Used Available Use% Mounted on

/dev/sdb1                 7780496    469540   7310956   7% /run/media/raul/DATA

[[email protected] release]$ cp ecspi_interrupt_master_example.bin /run/media/raul/DATA

[[email protected] release]$ umount /run/media/raul/DATA

-------------------------------------------

乙太網:

-------------------------------------------

[[email protected] release]$ cp ecspi_interrupt_master_example.bin /srv/tftp/

-------------------------------------------

./ 將SD卡插入載板或者配置網路來執行編譯好的二進位制檔案

SD 卡:

-------------------------------------------

Colibri iMX7 # fatload mmc 0:1 0x7F8000 ecspi_interrupt_master_example.bin

reading ecspi_interrupt_master_example.bin

9956 bytes read in 20 ms (485.4 KiB/s)

-------------------------------------------

乙太網:

-------------------------------------------

Colibri iMX7 # tftp 0x7F8000 ecspi_interrupt_master_example.bin

Using FEC0 device

TFTP from server 192.168.0.150; our IP address is 192.168.0.170

Filename 'ecspi_interrupt_master_example.bin'.

Load address: 0x7f8000

Loading: ##################################################  9.7 KiB

          647.5 KiB/s

done

Bytes transferred = 9956 (26e4 hex)

-------------------------------------------

./ 一旦韌體載入完畢,使用哪種方法就不再重要,執行下面命令執行 Cortex-M 上載入的程式。

-------------------------------------------

Colibri iMX7 # dcache flush

Colibri iMX7 # bootaux 0x7F8000

## Starting auxiliary core at 0x007F8000 ...

Colibri iMX7 #

-------------------------------------------

./ 現在使用修改後的程式碼,在 UART B 終端中按“s”將顯示 channel 0 上模擬採集。

9). 同 Linux 之間的衝突

在使用這些 U-boot 命令之後,你或許想要在啟動 Linux 後執行“boot”命令。現在的問題是,我們的示例使用了 UART B 和 the SPI。想要正常啟動 Linux,就需要修改 device tree,讓 Linux 不去使用這些資源。

你可以使用下面的命令,暫時關閉  UART B 和 SPI,而無需修改 device tree:

-------------------------------------------

Colibri iMX7 # setenv fdt_fixup 'fdt addr ${fdt_addr_r} && fdt rm /soc/[email protected]/[email protected]/[email protected]  && fdt rm /soc/[email protected]/[email protected]/[email protected]'

Colibri iMX7 # saveenv

Saving Environment to NAND...

Erasing NAND...

Erasing at 0x380000 -- 100% complete.

Writing to NAND... OK

-------------------------------------------

更多關於修改 device tree 的內容,可以參考 Toradex 開發者中心網站上的這篇文章。

10). 自動部署

在我的演示示例中,我通過乙太網載入  Cortex-M 韌體程式。一個節約時間的方法是自動複製檔案到“/dev/tftp/”目錄中。在專案的根目錄中,開啟檔案:

-------------------------------------------

[email protected] master]$ vi armgcc/CMakeLists.txt

-------------------------------------------

在最後面新增下面幾行內容:

-------------------------------------------

[[email protected] master]$ vi armgcc/CMakeLists.txt ADD_CUSTOM_COMMAND(TARGET ${Project_Name}_Main POST_BUILD COMMAND cp ${EXECUTABLE_OUTPUT_PATH}/ecspi_interrupt_master_example.bin /srv/tftp/m4.bin)

-------------------------------------------

再次執行 “./build_all.sh”指令碼,如果使用 Eclipse 編譯,你可以在“console”中看到自動執行的命令:

-------------------------------------------

cp /home/raul/freertos-colibri-imx7/examples/imx7_colibri_m4/driver_examples/ecspi/ecspi_interrupt/master/armgcc/release/ecspi_interrupt_master_example.bin /srv/tftp/m4.bin

-------------------------------------------

另外一個對我有幫助的優化是,在 U-boot 中建立自動載入韌體程式的規則:

-------------------------------------------

Colibri iMX7 # setenv m4 'tftp 0x7F8000 m4.bin && dcache flush && bootaux 0x7F8000'

Colibri iMX7 # setenv bootcmd 'run m4; run ubiboot; setenv fdtfile ${soc}-colibri-${fdt_board}.dtb && run distro_bootcmd;'

-------------------------------------------

現在,每一次開啟模組,就會自動載入韌體程式然後執行 Linux。

11). 總結

在本文中,你可以掌握搭建異構多核處理器構架方案的基本步驟。通過兩個演示示例,我們看到了如何在 Colibri iMX7 計算機模組的 HMP SoC Cortex-M4 核上編譯和執行程式碼。我們也瞭解到 SoC 上的不同核心共享外設介面,所以你需要了解(以及規劃)每個核心分配的外設。

相關推薦

處理器開發嵌入式應用入門

By Toradex Raul Rosetto Mu?oz 1). 簡介 每天都有新的異構多核處理器/片上系統 SoC 面市。在 SoC 上整合微控制器和外設控制核正變得越來越普遍,看看最新發布的 NXP? :i.MX 6SoloX、i.MX7 和即將面世的 i.MX 8。

Davinci的間通訊基礎元件SysLink 2.0

======================================================================================================= 部分模組由於沒有用過,也沒看到SysLink包中提

創龍TI AM5728工業控制處理器

mco 教程 blog 工業機器人 image sgx 技術分享 ESS uart 由廣州創龍自主研發的SOM-TL5728核心板,基於TI-AM5728浮點雙DSP+雙ARM核,大小僅86.5mm*60.5mm,性能強、成本低、性價比高。采用沈金無鉛工藝的10層板設計,專

Zedboard(二)使用Vivado+SDK開發嵌入式應用程序——實例一

跳過 管理 lock 使用 階段 驅動 選中 boa his   本次介紹用Vivado構建Zedboard開發板的硬件平臺+SDK開發應用程序(Zedboard裸機開發) 過程如下: 一、運行Vivado,建立新工程   指定好工程路徑,下一步,選擇RTL Project

[py]你真的了解處理器嗎? 了解線程

註意 董事會 fusion 整體 用戶 繼續 高速 多核處理器 真的 你真的了解多核處理器嗎? 1.雙核≠雙性能 多核不一定會使你的手機或電腦速度更快,但它將提高你的PC的整體性能,這是一個有所不同的細微的技術特色。多核處理器的性能提升並不是簡單CPU核心的倍數,因為受到

[轉帖]AMD、英特爾為何爭相走向膠水處理器?真相在此

AMD、英特爾為何爭相走向膠水多核處理器?真相在此 膠水多核到底好不好?這個事不是簡單一句話能說明的,今天的超能課堂裡我們就來聊聊MCM膠水多核技術的過去及未來。 作者:孟憲瑞來源: 超能網|2018-11-21 15:25  收藏

DVFS及處理器功耗優化技術詳解

    概述         降低嵌入式系統功耗有多種方法,其中動態電源管理(DPM)和動態電壓頻率調整技術(DVFS)是降低系統功耗的兩種關鍵技術。其中動態電源管理主要依據裝置工作負載的變化切換器工作狀態以達到系統功耗最小化。本篇部落格則主要從DVFS技術做詳細的介紹。DV

Linux 的 Spinlock 在 MIPS 處理器中的設計與實現

引言 隨著科技的發展,尤其是在嵌入式領域,高效能、低功耗的處理器成為眾多廠商追逐的目標,但是由於技術和工藝的瓶頸,試圖在單核處理器上達到這樣的目標變得越發困難,於是人們提出了多核處理器的概念。多核處理器的核心思想是一個處理器中包含若干個核(或執行緒),所有核(或執行緒)之間共享 IO、Cache、記憶體等資

執行緒和下“鎖”的應用

  假設這樣一種情況:有多個執行緒(或多核)需要在共享資料A滿足某一條件時,對A進行操作. 以下舉例兩種實現 Fun_1() { lock()--------------1.1   Result=Check(A)-----1.2   Unlock()------------

spinlock在處理器上的效能比較

#define _GNU_SOURCE#include <sched.h>#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <stdlib.h>#includ

MySQL利用處理器

在工作中可能遇到這樣的情況,隨著業務的增長,使用者量也在逐漸增長,終究有一天,一到高峰期,資料庫伺服器CPU利用率直飈100%。 最簡單的做法就是直接提升硬體效能,簡單粗暴,直接有效。 假如我們最開始的伺服器CPU核數是4,然後我們覺得4個有點扛不住,那就直

作業系統與處理器

這篇文章解答了我心中的疑問,那就是作業系統會自動排程cpu資源來處理多程序,多執行緒的併發。      早在上世紀90年代末,就有眾多業界人士呼籲用CMP(單晶片多處理器)技術來替代複雜性較高的單執行緒CPU。IBM、惠普、Sun等高階伺服器廠商,更是相繼推出了多核伺服器CPU。不過,由於伺服器價格高、應

處理器&類UNIX系統 -> 之程序和執行緒效能比較

網路上有很多討論關於在*NIX系統在SMP環境下到底是應該用多執行緒還是多程序, 到底哪一個有更好的效能,  比如有很多人認為考慮到linux使用1-1執行緒模型(對核心來說, 執行緒就是一個程序)並且系統已經為程序處理做了很多優化,效率提升, 所以在liunx SMP環

處理器處理器的區別

最近遇到了一個讓人很是疑惑的問題,然後寫出來供大家解疑: 多個處理器&多核處理器? 多個處理器:多個單核處理器,就是說電腦和處理器有多個,但是這個電腦的處理器是單核的; 多核處理器:單個多核處理器,也就是說電腦有一個處理器,但是這個處理器是多核的;      

MySQL利用處理器 innodb_read_io_threads

在工作中可能遇到這樣的情況,隨著業務的增長,使用者量也在逐漸增長,終究有一天,一到高峰期,資料庫伺服器CPU利用率直飈100%。最簡單的做法就是直接提升硬體效能,簡單粗暴,直接有效。假如我們最開始的伺服器CPU核數是4,然後我們覺得4個有點扛不住,那就直接給他搞16個核。這下

ARM處理器啟動過程分析

說明: 該流程圖按照程式碼執行時間順序劃分為4部分: 1.     Bootloader在圖片上半部,最先啟動; 2.     Kernel在圖片下半部,由bootloader引導啟動; 3.CPU0執行流程在圖片左半部,bootloader程式碼會進行判斷,先行啟

執行緒與處理器 SMP 分析

首先分析執行緒的優勢: 1、提高程式的併發性(執行緒級並行,而非指令級並行); 2、一個程序內的所有執行緒共享所有資源; 3、切換執行緒的代價下; 4、互動式程式可以通過多執行緒方式改善響應時間。 多執行緒實現的方式: 1、程式採用多執行緒設計方案; 2、多核處理器為單一的

使用SpringBoot開發JavaWeb應用入門

使用SpringBoot開發JavaWeb應用入門 0、環境及開發工具準備 環境 開發工具 1、建立專案 統一編碼 建立SpringBoot專案 啟動看看效果 2、編寫控制器(Con

OpenCL的GPU和CPU計算--1

原作者:飛鴻驚雪 地址點選開啟連結 本文主要探究OpenCL的GPU和多核CPU的異構計算問題,主要簡要闡述了什麼是OpenCL異構計算,講述CPU和GPU各自的特點,並且把他們結合起來做異構計算的前景。然後具體講述在高效能實驗室Linux工作站上如何搭建多GPU和多核

OpenCL的GPU和CPU計算--2

        本文主要探究OpenCL的GPU和多核CPU的異構計算問題,主要簡要闡述了什麼是OpenCL異構計算,講述CPU和GPU各自的特點,並且把他們結合起來做異構計算的前景。然後具體講述在高