From cb56fdefb1497e7fa78efe2f3a7e5cd801cc0790 Mon Sep 17 00:00:00 2001 From: lion187 Date: Tue, 27 Nov 2018 14:17:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=BB=BA=E6=96=87=E4=BB=B6=20Hardware?= =?UTF-8?q?/SOC/=E5=BC=82=E6=9E=84=E5=A4=9A=E6=A0=B8=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=99=A8=E5=BC=80=E5=8F=91=E5=B5=8C=E5=85=A5=E5=BC=8F=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=85=A5=E9=97=A8.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SOC/异构多核处理器开发嵌入式应用入门.md | 617 ++++++++++++++++++ 1 file changed, 617 insertions(+) create mode 100644 Hardware/SOC/异构多核处理器开发嵌入式应用入门.md diff --git a/Hardware/SOC/异构多核处理器开发嵌入式应用入门.md b/Hardware/SOC/异构多核处理器开发嵌入式应用入门.md new file mode 100644 index 0000000..f251595 --- /dev/null +++ b/Hardware/SOC/异构多核处理器开发嵌入式应用入门.md @@ -0,0 +1,617 @@ +# [异构多核处理器开发嵌入式应用入门](https://blog.csdn.net/toradexsh/article/details/70278764) + +By Toradex Raul Rosetto Mu?oz + +## 简介 + +每天都有新的异构多核处理器/片上系统 SoC 面市。在 SoC 上集成微控制器和外设控制核正变得越来越普遍,看看最新发布的 NXP? :i.MX 6SoloX、i.MX7 和即将面世的 i.MX 8。在我看来,这有点像曾经 ADC(模数转换器)开始集成微处理器上的外设功能,在应用处理器上集成微控制器,可以解决 Linux 系统中一些实时可控相关的问题。 + +新技术的出现总是会引出许多问题,或许你会产生疑问,这是否需要很多工作量。本位旨在快速、明了地介绍一种使用异构多核方式开发应用的方法。这里我们将会涉及搭建开发环境以及创建一个双核通信的 ping pong 应用的基本步骤,最后演示一个用微控制器通过 SPI 读取 ADC 数据并把数据发送至运行 Linux 的处理器的实际应用。 + +这是揭示利用异构多核处理构架 SoC 开发嵌入式系统的系列文章。通过实际操作和一些案例演示,你可以快速地开始开发。 + +## 硬件 + +本文中将使用 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 的过渡。 + +## 搭建开发环境 + +本文中演示的案例是在 Linux 电脑上开发的。所有 Cortex-M 上的代码都基于 Makefile 和 Cmake。你只需要安装少量的软件并正确配置编译工具链,就可以编译示例代码。 + +我们建议使用 4.9 2015 Q3 版本 linaro toolchain。从这里下载好压缩包后,解压如下: + + tar xjf ~/Downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 + +因为编译工具生成 32位应用,所以需要安装 32位的 libc 和 libncurse。在 Ubuntu 上,命令如下: + + sudo dpkg --add-architecture i386 sudo apt-get update sudo apt-get install libc6:i386 libncurses5:i386 + +现在可以测试编译工具: + + ~/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. + +最后,安装 cmake 和 make: + + sudo apt-get install make cmake + +## 下载示例 + +我们准备了一些示例,方便下载和测试,包括基本的 双核通信“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。在所有这些文件中,我们主要使用包含示例的的文件夹: + + [raul@localhost 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 + [raul@localhost freertos-colibri-imx7]$ + +## 搭建硬件环境 + +本文中,我们将不涉及如何调试 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: + + [raul@localhost ~]$ picocom -b 115200 /dev/ttyUSB0 + +终端 2: + + [raul@localhost ~]$ picocom -b 115200 /dev/ttyUSB1 + +## 编译第一个示例 + +进入 SPI 示例目录,编译第一个应用: + + [raul@localhost freertos-colibri-imx7]$ cd examples/imx7_colibri_m4/driver_examples/ecspi/ecspi_interrupt/master/ + [raul@localhost master]$ ls armgcc hardware_init.c main.c + +所有的示例都有 main.c 、hardware_init.c 和 armgcc 文件夹。我们先不解释源代码,只是进入目录,导出下载的 toolchain 路径然后编译: + + [raul@localhost armgcc]$ cd .. + [raul@localhost master]$ cd armgcc/ + [raul@localhost armgcc]$ export ARMGCC_DIR=~/gcc-arm-none-eabi-4_9-2015q3/ + + [raul@localhost 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 + + [raul@localhost armgcc]$ + + The binaries are located in the "release" directory. + + [raul@localhost armgcc]$ cd release/ + [raul@localhost release]$ ls + ecspi_interrupt_master_example.bin ecspi_interrupt_master_example.hex + ecspi_interrupt_master_example.elf ecspi_interrupt_master_example.map + [raul@localhost release]$ + +在这里, bin 文件是最重要的。我们使用 U-boot 将其加载到 Cortex-M4。 + +## 运行固件程序 + +为了运行固件程序,U-boot 需要加载这个二进制文件,然后在 Cortex-M 上运行。也可以用另外的方法。我的建议是使用 SD 卡或者网络。我们将会演示如何使用这两种方法。一方面,需要知道的是使用网络,开发将以动态的方式进行,因为不需要在载板上拔插 SD 卡。另一方面,为了使用以太网加载文件,你需要配置 tftp 服务器,我这里配置为 "/srv/tftp/"。参考 Flashing Linux Over Ethernet 了解 tftp 配置。 + +SD 卡: + + [raul@localhost release]$ df + Filesystem 1K-blocks Used Available Use% Mounted on + /dev/sdb1 7780496 469540 7310956 7% /run/media/raul/DATA + [raul@localhost release]$ cp ecspi_interrupt_master_example.bin /run/media/raul/DATA + [raul@localhost release]$ umount /run/media/raul/DATA + +以太网: + + [raul@localhost release]$ cp ecspi_interrupt_master_example.bin /srv/tftp/ + +开启载板电源,上电的时候,在 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) + +加载完成后,无论是使用 SD 卡还是以太网,执行下面的命令运作已经加载到 Cortex-M 上的程序。 + + Colibri iMX7 # dcache flush + Colibri iMX7 # bootaux 0x7F8000 + ##Starting auxiliary core at 0x007F8000 ... + Colibri iMX7 # + +接下来,你应该可以看到在 UART B 终端上打印出 Cortex-M 的调试信息。你的屏幕如下图所示。 + +在 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 + ------------------------------------------- + + +## 示例 - SPI + +在之前的示例中,我们只编译和执行了代码。现在我们将修改源码,实现同 Microchip MCP3008 的 SPI 通信。这个一个10位 ADC,具有8个输入。按下图连接到 Aster 和面包板: + +如果喜欢使用 Eclipse IDE,可以通过 CMake 生成 Eclipse 项目文件。 Cmake 的 -G 参数可以配置 “build system generator”。确保 build_all.sh 指定 “Eclipse CDT4 – Unix Makefiles”。 + +在 armgcc 示例目录中: + + [raul@localhost 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”脚本: + + [raul@localhost armgcc]$ ./build_all.sh + [raul@localhost 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 + +标准的示例是十分简单的。我们有必要介绍部分代码,从而在下面的示例中能够清楚地了解需要查看什么地方。 + +```cpp +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” + +```cpp +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);”。 + +```cpp +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 外的其他内容,例如中断向量表。 + +```cpp +#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 的数据。 + +```cpp +/* 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位数据的信息。 + +```cpp +/* 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)” 应该如下: + +```cpp +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 卡: + + [raul@localhost release]$ df + Filesystem 1K-blocks Used Available Use% Mounted on + /dev/sdb1 7780496 469540 7310956 7% /run/media/raul/DATA + [raul@localhost release]$ cp ecspi_interrupt_master_example.bin /run/media/raul/DATA + [raul@localhost release]$ umount /run/media/raul/DATA + +以太网: + + [raul@localhost 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 上模拟采集。 + +## 同 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/aips-bus@30800000/spba-bus@30800000/serial@30890000 && fdt rm /soc/aips-bus@30800000/spba-bus@30800000/ecspi@30840000' + Colibri iMX7 # saveenv + Saving Environment to NAND... + Erasing NAND... + Erasing at 0x380000 -- 100% complete. + Writing to NAND... OK + +更多关于修改 device tree 的内容,可以参考 Toradex 开发者中心网站上的这篇文章。 + +## 自动部署 + +在我的演示示例中,我通过以太网加载 Cortex-M 固件程序。一个节约时间的方法是自动复制文件到“/dev/tftp/”目录中。在项目的根目录中,打开文件: + + raul@localhost master]$ vi armgcc/CMakeLists.txt + +在最后面添加下面几行内容: + + [raul@localhost 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。 + +## 总结 + +在本文中,你可以掌握搭建异构多核处理器构架方案的基本步骤。通过两个演示示例,我们看到了如何在 Colibri iMX7 计算机模块的 HMP SoC Cortex-M4 核上编译和运行代码。我们也了解到 SoC 上的不同内核共享外设接口,所以你需要了解(以及规划)每个内核分配的外设。 \ No newline at end of file