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。在所有这些文件中,我们主要使用包含示例的的文件夹:
-------------------------------------------
[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]$
-------------------------------------------
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:
-------------------------------------------
[raul@localhost ~]$ picocom -b 115200 /dev/ttyUSB0
-------------------------------------------
终端 2:
-------------------------------------------
[raul@localhost ~]$ picocom -b 115200 /dev/ttyUSB1
-------------------------------------------
6). 编译第一个示例
a). 进入 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
-------------------------------------------
b). 所有的示例都有 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。
7). 运行固件程序
为了运行固件程序,U-boot 需要加载这个二进制文件,然后在 Cortex-M 上运行。也可以用另外的方法。我的建议是使用 SD 卡或者网络。我们将会演示如何使用这两种方法。一方面,需要知道的是使用网络,开发将以动态的方式进行,因为不需要在载板上拔插 SD 卡。另一方面,为了使用以太网加载文件,你需要配置 tftp 服务器,我这里配置为 "/srv/tftp/"。参考 Flashing Linux Over Ethernet 了解 tftp 配置。
a). 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
-------------------------------------------
b). 以太网:
复制文件到 tftp 服务器目录,在载板上连接网线,配置好能够连接到电脑的网络。这里载板的 IP 是 192.168.0.170,电脑 IP 为 192.168.0.150。
-------------------------------------------
[raul@localhost 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 示例目录中:
-------------------------------------------
[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
./ 标准的示例是十分简单的。我们有必要介绍部分代码,从而在下面的示例中能够清楚地了解需要查看什么地方。
-------------------------------------------
int main(void)
{
.baudRate = 500000,
.mode = ecspiMasterMode,
.burstLength = ECSPI_MASTER_BURSTLENGTH,
.channelSelect = BOARD_ECSPI_CHANNEL,
.clockPhase = ecspiClockPhaseSecondEdge,
.clockPolarity = ecspiClockPolarityActiveHigh,
.ecspiAutoStart = ECSPI_MASTER_STARTMODE
};
hardware_init();
ecspiMasterInitConfig.clockRate = get_ecspi_clock_freq(BOARD_ECSPI_BASEADDR);
PRINTF("n-------------- ECSPI master driver example --------------nnr");
PRINTF("This example application demonstrates usage of SPI driver in master mode.nr");
PRINTF("It transfers data to/from remote MCU in SPI slave mode.nr");
ECSPI_MasterConfig(&ecspiMasterInitConfig);
while(true)
{
txData[0]++;
ECSPI_MasterTransfer((uint8_t*)txData, (uint8_t*)rxData, 1);
while(ECSPI_MasterGetTransferStatus());
PRINTF("MASTER: Transmited data: %d nr", txData[0]);
PRINTF(" : Received data: %d nnr", rxData[0]);
}
while(1);
}
-------------------------------------------
./ 第一个需要注意的配置引脚复用的地方。这里我们将使用标准的 SPI。右击“hardware_init();”函数,选择“Open Declaration”
-------------------------------------------
void hardware_init(void)
{
// ECSPI1 iomux configuration
IOMUXC_ECSPI3_MISO_SELECT_INPUT = 0; //(I2C1_SCL SODIM 90)
IOMUXC_ECSPI3_MOSI_SELECT_INPUT = 0; //(I2C1_SCL SODIM 90)
IOMUXC_SW_MUX_CTL_PAD_I2C2_SCL = IOMUXC_SW_MUX_CTL_PAD_I2C2_SCL_MUX_MODE(3);
IOMUXC_SW_MUX_CTL_PAD_I2C1_SDA = IOMUXC_SW_MUX_CTL_PAD_I2C1_SDA_MUX_MODE(3);
IOMUXC_SW_MUX_CTL_PAD_I2C1_SCL = IOMUXC_SW_MUX_CTL_PAD_I2C1_SCL_MUX_MODE(3);
IOMUXC_SW_MUX_CTL_PAD_I2C2_SDA = IOMUXC_SW_MUX_CTL_PAD_I2C2_SDA_MUX_MODE(3);
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) |
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) |
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 外的其他内容,例如中断向量表。
-------------------------------------------
#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 的数据。
-------------------------------------------
while(true)
{
PRINTF("Press "s" when spi slave is ready.nr");
control_char = GETCHAr();
if((control_char == 's') || (control_char == 'S'))
{
uint8_t control_char;
uint8_t i;
ecspi_init_config_t ecspiMasterInitConfig = {
PRINTF("Press "s" when spi slave is ready.nr");
control_char = GETCHAr();
if((control_char == 's') || (control_char == 'S'))
{
1.《异构多核处理器开发嵌入式应用入门》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《异构多核处理器开发嵌入式应用入门》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/jiaoyu/2372253.html