読者です 読者をやめる 読者になる 読者になる

ConnectCore i.MX6UL スターターキット:マイクロSDカードからLinuxを起動する

ConnectCore i.MX6ULスターターキットの続き

その1 その2

パーティション作成

ツールでSDカードのメディアに次のようにパーティションを作成する。

ラベル サイズ フォーマット
boot 256M ext4
rootfs 残り全部 ext4

gpartedのインストール

Ubuntu16.04では次のようにする。

$ sudo apt-get install -y gparted

カーネルとデバイスツリーのコピー

$ sudo cp tmp/deploy/images/ccimx6ulstarter/zImage-ccimx6ulstarter.bin /media/${USER}/boot
$ sudo cp tmp/deploy/images/ccimx6ulstarter/zImage-imx6ul-ccimx6ulstarter-id129.dtb /media/${USER}/boot

ルートファイルシステムの展開

$ sudo tar xvf ./tmp/deploy/images/ccimx6ulstarter/core-image-base-ccimx6ulstarter.tar.bz2 -C /media/${USER}/rootfs

u-bootの設定

setenv bootargs console=${console},${baudrate} ${bootargs_linux}  root=/dev/mmcblk1p2 fstype=ext4
setenv load_kernel_mmc ext4load mmc 0 ${loadaddr} /zImage-ccimx6ulstarter.bin
setenv load_fdt_mmc ext4load mmc 0 ${fdt_addr} /zImage-imx6ul-ccimx6ulstarter-id129.dtb
setenv bootcmd 'mmc rescan; run load_kernel_mmc; run load_fdt_mmc; bootz ${loadaddr} - ${fdt_addr}'
saveenv

これでマイクロSDからブートされるようになる。

ConnectCore i.MX6UL スターターキット:ネットワーク越しにLinuxを起動する

前回の続き。

次のような構成でLinuxを起動する。

コンポーネント 読み込み方法
u-boot NANDフラッシュ(デフォルト)
linuxカーネル tftpサーバ
バイスツリー tftpサーバ
rootfs NFS

tftpサーバ

Ubuntu16.04では次のように設定する。

インストール

$ sudo apt-get install -y tftpd-hpa

起動

インストール直後は起動済みなので不要のはず。

$ sudo systemctl start tftpd-hpa

ファイルの格納

Ubuntuではデフォルトで/var/lib/tftpbootがtftpの公開ディレクトリに設定されているため、 ダウンロードしたいファイルはここにファイルを格納する。

ここではカーネルとデバイスツリーをコピーする。

$ sudo cp tmp/deploy/images/ccimx6ulstarter/zImage-ccimx6ulstarter.bin /var/lib/tftpboot
$ sudo cp tmp/deploy/images/ccimx6ulstarter/zImage-imx6ul-ccimx6ulstarter-id129.dtb /var/lib/tftpboot

nfsサーバ

インストール

$ sudo apt-get install -y nfs-kernel-server

公開ディレクトリの設定

tftpサーバとは異なり、デフォルトの公開ディレクトリは設定されていないため、この設定をおこなう。

/etc/exportsに下記の内容を追加する。

/exports 192.168.21.0/255.255.255.0(rw,no_root_squash,subtree_check)

/exportsを作成する。

$ sudo mkdir /exports

起動

$ sudo systemctl restart nfs-kernel-server

ファイルの展開

bitbakeで生成されたrootfsのイメージを/exportsに展開する。

$ sudo tar xvf ./tmp/deploy/images/ccimx6ulstarter/core-image-base-ccimx6ulstarter.tar.bz2 -C /exports/

u-bootの設定

以下のようにネットワークの設定を行う。

変数 説明 設定例
ipaddr ccimx6ulstarterのIPアドレス 192.168.21.100
serverip tftpサーバのIPアドレス 192.168.21.42
setenv ipaddr 192.168.21.100
setenv serverip 192.168.21.42

次にLinux起動のための設定を行う

setenv fdt_file zImage-imx6ul-ccimx6ulstarter-id129.dtb
setenv bootargs console=${console},${baudrate} ${bootargs_linux} ip=192.168.21.100 root=/dev/nfs nfsroot=192.168.21.42:/exports rw
setenv bootcmd 'tftp ${loadaddr} ${zimage};tftp ${fdt_addr} ${fdt_file}; bootz ${loadaddr} - ${fdt_addr}'
saveenv

saveenvで設定した内容がNANDフラッシュに設定される。

u-bootでは起動すると、最初にbootcmdの内容を実行しようとするため、次回起動時からはLinuxがネットワーク越しに起動されるようになる。

ConnectCore i.MX6UL スターターキットで遊ぶ

DIGIのConnectCore i.MX6UL スターターキットチップワンストップで購入した。

このボードはラズベリーパイシリーズの40ピンのGPIOヘッダと互換のピンヘッダを搭載している。 つまりハードウェアレベルではラズベリーパイのHATやLCDなどのデバイスが使用できるだろうということ。

カーネルやドライバなどをどうにかして、その手のデバイスを動かしてみるのも面白そうだと思い購入することにした。

ボード立ち上げの基本的な作業はここを参考にしている。

yoctoの入手

このボードは標準でYoctoProjectのBSPが提供されている。

$ mkdir -p ~/yocto/dey-2.0
$ cd ~/yocto/dey-2.0
$ repo init -u https://github.com/digi-embedded/dey-manifest.git -b refs/tags/2.0-r4
$ repo sync -j4 --no-repo-verify

プロジェクトの作成

$ source ./mkproject.sh -l
ccimx6sbc ccimx6ulsbc ccimx6ulstarter
$ mkdir build && cd build
$ source ../mkproject.sh -p ccimx6ulstarter

BitBake実行

$ bitbake core-image-base

u-boot

ccimx6ul starterは出荷時点でNANDフラッシュにu-bootが書き込まれており、 シリアルコンソールの設定を行って電源をいれると、それが起動するようになっている。

初期状態の環境変数

arch=arm
baudrate=115200
board=ccimx6ulstarter
board_id=129
board_name=ccimx6ulstarter
board_version=2
boot_fdt=yes
bootargs_linux=cma=96M 
bootargs_nand_linux=setenv bootargs console=${console},${baudrate} ${bootargs_linux} ${mtdparts} ubi.mtd=${mtdlinuxindex} ubi.mtd=${mtdrootfsindex} root=ubi1_0 rootfstype=ubifs rw ${bootargs_once} ${extra_bootargs}
bootargs_recovery=setenv bootargs console=${console},${baudrate} androidboot.hardware=ccimx6ulstarter androidboot.console=${console}${mtdparts} ${bootargs_once} ${extra_bootargs}
bootcmd=if run loadscript; then source ${loadaddr};fi;
bootdelay=1
bscantest=PASS
btaddr=00:40:9D:98:A0:51
console=ttymxc4
cpu=armv7
eth1addr=00:40:9D:98:A0:4F
ethact=FEC0
ethaddr=00:40:9D:98:A0:4E
ethprime=FEC
fdt_addr=0x83000000
fdt_file=zImage-imx6ul-ccimx6ulstarter.dtb
fdt_high=0xffffffff
initrd_addr=0x83800000
initrd_file=uramdisk.img
initrd_high=0xffffffff
install_linux_fw_sd=if load mmc 0 ${loadaddr} install_linux_fw_sd.scr;then source ${loadaddr};fi;
ipaddr=192.168.42.30
linux_file=core-image-base-ccimx6ulstarter.boot.ubifs
loadaddr=0x80800000
loadscript=if ubi part linux; then if ubifsmount ubi0:linux; then ubifsload ${loadaddr} ${script};fi;fi;
module_variant=0x02
mtddevname=bootloader
mtddevnum=0
mtdids=nand0=gpmi-nand
mtdlinuxindex=3
mtdparts=mtdparts=gpmi-nand:3m(bootloader),1m(environment),1m(safe),14m(linux),14m(recovery),128m(rootfs),-(update)
mtdrootfsindex=5
netmask=255.255.0.0
partition=nand0,0
recovery_file=recovery.img
recoverycmd=if ubi part recovery; thenif ubifsmount ubi0:recovery; thenubifsload ${loadaddr} ${zimage};ubifsload ${fdt_addr} ${fdt_file};ubifsload ${initrs_addr} ${initrd_file};run bootargs_recovery;bootz ${loadaddr} ${initrd_addr} ${fdt_addr};fi;fi;
rootfs_file=core-image-base-ccimx6ulstarter.ubifs
rootpath=/exports/nfsroot-ccimx6ulstarter
script=boot.scr
serverip=192.168.42.1
soc=mx6
stderr=serial
stdin=serial
stdout=serial
uboot_file=u-boot.imx
vendor=digi
verifyaddr=88400000
wlanaddr=00:04:f3:ff:ff:fb
zimage=zImage-ccimx6ulstarter.bin

カーネルやデバイスツリーをロードするために使用するDDR上のアドレスやファイル名などもあらかじめ、次のように変数として定義してある。

変数 説明
loadaddr 0x80800000 カーネルのロードアドレス
zimage zImage-ccimx6ulstarter.bin カーネルのファイル名
fdt_addr 0x83000000 バイスツリーのロードアドレス
fdt_file zImage-imx6ul-ccimx6ulstarter.dtb バイスツリーのファイル名

初期状態へ戻す

下記のコマンドで初期状態へ戻すことができる。

env default -a

カーネル、rootfsの書き込み

Digiのi.MX6UL Starter kitのu-bootは高機能なため、rootfsやカーネルなどの書き込み手順を一本化したupdateコマンドが用意されている。

updateコマンドはtftpサーバからイメージを取得するようになっているため、予め下記の設定をしておく必要がある。

setenv ipaddr x.y.z.w
setenv serverip a.b.c.d
saveenv
変数 説明
ipaddr ccimx6ulstarterのIPアドレス
serverip tftpサーバのIPアドレス

書き込みは次のように実行する。

update linux tftp core-image-base-ccimx6ulstarter.boot.ubifs
update rootfs tftp core-image-base-ccimx6ulstarter.ubifs
boot

最後のbootで書き込んだlinuxを起動している。

u-bootのbootcmdなどを見ると、NANDフラッシュにカーネルやルートファイルシステムをインストールし使用することを前提としているように見えるが、 内蔵のフラッシュロムには書き込み回数の上限、つまり寿命があるため、マイクロSDやNFSなどを使用し、なるべく内蔵のフラッシュロムを使用しない方法を検討する。

mortyでPandaboard ES Rev.B3

今更のPandaBoard ESをmortyで試してみる。 手元にあるのは悪名高きRev.B3。

Rev.B3はElpidaDDRを搭載しており、タイミングなどの設定が他のリビジョンと異なるため、 Pandaboard ESの他のリビジョンで動作するOSイメージをそのまま使用することができない。

ここをベースにYocotProjectで動くようにしてみた。

morty環境の作成

pokyを取得

~/pandaで作業する。

$ mkdir ~/panda
$ cd ~/panda
$ git clone git://git.yoctoproject.org/poky.git -b morty

meta-tiを取得

meta-tiには現時点でmortyブランチがないためmasterを取得する。

$ cd poky
$ git clone http://git.yoctoproject.org/git/meta-ti

meta-panda-es-rev-b3

Rev.B3のイメージを作成するためにmeta-panda-es-rev-b3を取得する。

$ git clone https://github.com/mickey-happygolucky/meta-panda-es-rev-b3.git

環境変数の設定

bitbakeの実行に必要な環境変数を読み込む。

$ cd ../
$ source poky/oe-init-build-env

レイヤの設定

ビルド対象にmeta-tiとmeta-panda-es-rev-b3を追加する。

$ bitbake-layers add-layer ../poky/meta-ti
$ bitbake-layers add-layer ../poky/meta-panda-es-rev-b3

local.confの設定

MACHINEをpandaboradに設定するため、local.confの頭の方に次を追加する。

MACHINE ?= "panda-es-rev-b3"

bitbakeの実行

core-image-minimalを生成する。

$ bitbake core-image-minimal

SDカードの作成

Pandaboardを起動するためにはSDカードの決まった場所にブートローダ(MLO,u-boot)を書き込む必要がある。 作業手順が複雑なため、スクリプトを作成した。

このスクリプトを使用してSDカードを作成する。

スクリプトのダウンロード

まずはスクリプトを取得し実行権限をつける。

$ wget https://gist.githubusercontent.com/mickey-happygolucky/40385889e3d013f00e1f97eb5ae04f95/raw/df99f41c760dcbed29340075cea49c749f9f6934/mksd_panda.sh
$ chmod +x ./mksd_panda.sh

スクリプトの実行

PCにSDカードをマウントした状態でスクリプトを実行する。

引数にはbitbakeで作成されたイメージの.tar.gzファイルを指定する。

実際には.tar.gzと同じディレクトリにあるカーネルブートローダもSDカードに書き込むため、 .tar.gz単体を他のディレクトリに移動して使用することはできないので注意。

$ ./mksd_panda.sh ./tmp/deploy/images/panda-es-rev-b3/core-image-minimal-panda-es-rev-b3.tar.gz

Pandaboard起動

作成したSDカードを挿入し、電源を投入する。

Poky (Yocto Project Reference Distro) 2.2 panda-es-rev-b3 /dev/ttyS2
                                                                                
panda-es-rev-b3 login: root
root@panda-es-rev-b3:~# uname -a
Linux panda-es-rev-b3 4.9.0+ #1 SMP Wed Jan 11 21:07:22 JST 2017 armv7l GNU/Linux
root@panda-es-rev-b3:~# 

無事、起動した。 今回はRS-232CでPCと接続しシリアルコンソールでログインしている。

raspberrypi3 meta-raspberrypiでuartコンソール

2017/01/18修正

ラズベリーパイ3にシリアルコンソールでログインしたい。

少し古い情報だと、ラズベリーパイ3はオンボードBluetoothの影響で、すこし複雑な設定を行う必要があるとなっているが、最近のラズベリーパイ3向けのカーネルでは、config.txtenable_uart=1とすることで使用できる。

ここではYoctoProjectのmeta-raspberrypiでシリアルコンソールを使用する方法について記述する。

USB-シリアル変換モジュール

最近のPCはシリアルポートを搭載しているものが少ない。、シリアルポートがないPCを使用する場合は、USBポートをシリアルとして使用するためにUSB-シリアル変換を行う必要がある。

今回は秋月電子で購入できる、これ を使用する。

変換モジュールとラズベリーパイ3の接続

秋月の変換モジュールはUSBポートからの給電で動作することができるため、ラズベリーパイとはTXDRXDを接続するだけで使用できる。

変換モジュールのジャンパを次のようにショートさせる。

ジャンパ 接続 説明
J1(3ピン) 1-2間をショート I/O電圧を3.3V
J2(2ピン) ショート USBバスから5Vを給電

これらは購入時そのままのはず。

ラズベリーパイ3との接続を次に示す。

f:id:mickey_happygolucky:20170118001627p:plain

ラズベリーパイ3側の使用ピンを示す。

ピン GPIO 機能
8 GPIO14 UART0_TXD
10 GPIO15 UART0_RXD
14 - GND

GNDはピン14の他にも複数あり、どれに接続してもよいが、接続し忘れると悲しいことになる。

Linuxの作成

今回はmortyブランチで作業する。

ベース環境の取得

$ git clone git://git.yoctoproject.org/poky.git -b morty
$ cd poky
$ git clone git://git.yoctoproject.org/meta-raspberrypi

meta-raspberrypiはまだmortyブランチがないのでmasterを使用する。

oe-init-build-envの読み込み

下記を実行する。

$ cd ~/rpi3
$ source poky/oe-init-build-env

これにより、クロスコンパイルに必要な環境変数が設定され、ビルドディレクトリに移動される。

meta-raspberrypiをビルド対象に追加

次のコマンドを実行する。

$ bitbake-layers add-layer ../poky/meta-raspberrypi

local.confの修正

次の行をlocal.confの先頭の方に追加する。

MACHINE ?= "raspberrypi3"
DL_DIR ?= "${HOME}/work/yocto/downloads"

ENABLE_UART = "1"

ENABLE_UART = "1"でシリアルコンソールが有効になる。

bitbakeの実行

$ bitbake rpi-basic-image

SDカードの作成

$ sudo dd if=tmp/deploy/images/raspberrypi3/rpi-basic-image-raspberrypi3.rpi-sdimg of=/dev/sdb bs=40M

/dev/sdbはSDカードのデバイス。環境により変更が必要な場合がある。

動作確認

作成したSDカードをラズベリーパイ3に挿入後、PCのUSBポートと変換モジュールを接続して、PC上でminicomなどを起動する。この時フロー制御は無効にしておくこと。

この状態でラズベリーパイ3に電源を入れるとログイン画面が表示される。

f:id:mickey_happygolucky:20161218111846p:plain

pandocでplantumlを使う

pandocでドキュメントを書いているときにUMLで図を差し込みたいことがある。

UMLを描くためにはいろいろとツールがあるが、テキストから図を生成できるPlantUMLが便利。

PlantUML自体は以前取り上げたことがあるので、導入方法はこちらで。

pandocではフィルタを使えば、plantumlの記法で記述したコードブロックを直接、図に変換することができる。

いろいろと実装が存在するが、今回はここのものを使用する。

ダウンロード

パスが通っている場所にフィルタのスクリプトをダウンロードする。

ここでは~/binとする。

$ cd ~/bin
$ wget https://raw.githubusercontent.com/matthiasbeyer/pandoc-paper-template/master/scripts/plantuml-filter.py
$ chmod +x ./plantuml-filter.py

スクリプトの作成

このフィルタはパスが通っているところにplantumlという名前でPlantUMLが実行できることを想定しているため、ラッパーとなるスクリプト~/bin/plantumlとして作成する。

#!/bin/bash

java -jar plantuml.jar $@

これに実行権限をつける。

$ chmod +x ~/bin/plantuml

図の埋め込み

pandocのマークダウンに次のように記述する。

```{.plantuml}
start

:test;

stop
```

これを試しにtest.mdとして保存する。

変換

これを次のように変換する。

pandoc -F plantuml-filter.py -otest.html test.md

次のような図が生成される。

f:id:mickey_happygolucky:20161209015130p:plain

日本語対応

PlantUMLのコードブロックに日本語が含まれる場合エラーが発生するので、次のように修正した。

--- plantuml-filter.py~  2016-12-09 01:14:35.235721804 +0900
+++ plantuml-filter.py    2016-12-09 01:39:31.432327258 +0900
@@ -12,7 +12,7 @@
 from pandocfilters import toJSONFilter, Str, Para, Image, attributes
 
 def sha1(x):
-  return hashlib.sha1(x).hexdigest()
+  return hashlib.sha1(x.encode('utf-8')).hexdigest()
 
 def out(s):
     sys.stderr.write('\t[PLTUML] ' + s + '\n');
@@ -21,7 +21,7 @@
 
 def pipe(cmd, data):
     p = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
-    p.stdin.write(data)
+    p.stdin.write(data.encode('utf-8'))
     p.stdin.close()
     data = p.stdout.read()
     p.stdout.close()

次のようなコードブロックを変換する。

```{.plantuml}
start

:日本語;

stop
```

すると、次のような図が生成される。

f:id:mickey_happygolucky:20161209015148p:plain

CMakeLists.txt 覚書

プロジェクト名の変数化

プロジェクト名を変数化しておくといろいろと便利。set(変数名 値)で変数を定義できる。

cmake_minimum_required(VERSION 2.8)
set(PROJ_NAME hello)

add_executable(${PROJ_NAME}
    ${PROJECT_SOURCE_DIR}/main.cpp
)

C++14有効化

include(CheckCXXCompilerFlag)コンパイラフラグチェックを有効化

include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
if(COMPILER_SUPPORTS_CXX14)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
else()
    message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++14 support. Please use a different C++ compiler.")
endif()

pkg-configによるモジュールの追加

find_package(PkgConfig)でpkg-configを有効化

# find opengl via pkg-config
find_package(PkgConfig)
pkg_check_modules(OPENCV REQUIRED opencv)
pkg_check_modules(LIBAV REQUIRED libavcodec libavfilter libavformat libavutil libavdevice libavformat libavresample)

この例ではOpenCV関連の変数のプリフィックスがOPENCV_となる。インクルードディレクトリはOPENCV_INCLUDE_DIRS、ライブラリはOPENCV_LIBRARIESとしてアクセスできるようになる。

LIBAVのように複数のパッケージをまとめて検索することもできる

インクルードディレクトリの設定

INCLUDE_DIRECTORIESのように変数にパスを設定しておき、include_directoriesで登録する。

# Include directories for this project
set(INCLUDE_DIR
  ${OPENCV_INCLUDE_DIRS}
  ${QT4_INCLUDE_DIRS}
  ${PROJECT_SOURCE_DIR}/include
)

# Add a include files
include_directories("${INCLUDE_DIR}")

ここによると、 次にようにINCLUDE_DIRECTORIES変数に設定することでもできるらしい。

set(INCLUDE_DIRECTORIES
  ${PROJECT_SOURCE_DIR}/../include
)

追加のライブラリ(dll)

LIBRARIESのように変数にライブラリを設定しておき、target_link_librariesで登録する。

set(LIBRARIES
    ${LIBAV_LIBRARIES}
    ${QT4_LIBRARIES}
    -lrt 
    -lpthread)

target_link_libraries(${PROJ_NAME} ${LIBRARIES})

pkg-configの変数や、-lrtのように個別に設定することもできる。

追加のライブラリ(static)

DLLの場合同様にSTATIC_LIBRARIESのように変数に登録しておき、target_link_librariesで登録する。

set(STATIC_LIBRARIES
    ${PROJECT_SOURCE_DIR}/prebuilt_lib/libsdi.a
    ${PROJECT_SOURCE_DIR}/prebuilt_lib/libgdi.a
    jpegdecdrv
)

target_link_libraries(${PROJ_NAME} ${STATIC_LIBRARIES})

ビルド済みのバイナリをリンクする場合は、ライブラリまでのフルパスを書く。

ソースからコンパイルする場合は、jpegdecdrvのようにライブラリ名のみ記述すると、パスの解決などをCMakeがやってくれる。

DLLとスタティックを同時にリンク

DLLとスタティックなライブラリを同時にリンクしたい場合は、次のように同時に設定できる。

target_link_libraries(${PROJ_NAME} ${LIBRARIES} ${STATIC_LIBRARIES})

実行ファイルの生成

CMakeで実行形式を生成したい場合は、add_executable(実行ファイル名 ソースファイル名)で設定する。

# Add a target executable
add_executable(${PROJ_NAME}
    ${PROJECT_SOURCE_DIR}/main.cpp
)

一つのCMakeLists.txtに複数add_executableを書くこともできる。

アセンブラの有効化

add_executableにhello.Sなどアセンブラのソースファイルを登録したい場合は、予め有効化しておく必要がある。

# Enable assembler
enable_language(ASM)

add_executable(${PROJ_NAME}
    ${PROJECT_SOURCE_DIR}/main.c
    ${PROJECT_SOURCE_DIR}/hello.S
)

サブディレクトリの登録

ライブラリなどのサブモジュールを一緒にビルドする場合、1つのCMakeLists.txtに全て定義することもできるが、 従来のMakefileのようにディレクトリごとにCMakeLists.txtを置くこともできる。

その場合はadd_subdirectory(ディレクトリ名)で登録する。

# Add sub-directories
add_subdirectory(libhello)
add_subdirectory(libbye)

スタティックライブラリの生成

スタティックライブラリをビルドする場合はadd_librarySTATICを設定する。

add_library(hello STATIC
    hello.cpp
    hello_func.cpp
)

この例ではlibhello.aが生成される。

CXX_FLAGSの設定

CXX_FLAGSなどの一般的に変数に関してはCMAKE_CXX_FLAGSのように予め定義してある。

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__ARMV8")