SSブログ

トラ技ARM基板をCMSIS-DAPにしてOpenOCDを使ってお中華STMボードをどうこうするテスト [電子工作]

tragi.jpg

約$2で送料無料、日本のAmazonでも290円というおおよそあたまがおかしいんじゃないかという価格で売られているSTM32F103C8T6搭載ボードを買ってみました。

ついでに、昔買ったきり放置していたトラ技ARM基板をCMSIS-DAPにして
OpenOCDからどうこうする環境を作ってテストしてみました。

※ぶっちゃけ、デバッガに関しては秋月からSTLink/V2を買ったほうが安いんですがw

OpenOCDをCMSIS-DAPを有効にしてビルド

※Nucleoボードも使うので、STLinkも有効にしてます

Macの場合

$ sudo port install libtool autoconf texinfo libusb
$ sudo port install hidapi-devel

$ git clone http://repo.or.cz/r/openocd.git openocd

$ cd openocd
$ ./bootstrap
$ ./configure --enable-stlink --enable-cmsis-dap
$ make -j4


Ubuntuの場合

hidapiライブラリの名前がlibhidapi-devになります。

$ sudo apt-get install libtool autoconf texinfo libusb
$ sudo apt-get install libhidapi-dev

$ git clone http://repo.or.cz/r/openocd.git openocd

$ cd openocd
$ ./bootstrap
$ ./configure --enable-stlink --enable-cmsis-dap
$ make


Ubuntuの場合は以下のudevルールの追加が必要です

$ cat /etc/udev/rules.d/99-CMSIS.rules 
# CMSIS-DAP compatible adapters
ATTRS{product}=="*CMSIS-DAP*", MODE="664", GROUP="plugdev"


※参考:STLink用のudevルール

$ cat /etc/udev/rules.d/99-stlink.rules 
# STLink v1
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3744", MODE="664", GROUP="plugdev"

# STLink v2
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="664", GROUP="plugdev"

# STLink v2-1
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE="664", GROUP="plugdev"



※以下はhidapiのソースを取ってきてビルドする手順
$ sudo apt-get install libudev-dev
$ git clone git://github.com/signal11/hidapi.git
$ cd hidapi
$ ./bootstrap
$ ./configure
$ make
$ sudo make install
$ sudo ln -s /usr/local/lib/libhidapi-hidraw.so.0 /usr/lib/libhidapi-hidraw.so.0


トラ技ARM基板 -> 中華STM32基板

※トラ技ARM基板には、雑誌付属のCD-ROM、またはトラ技HomePageからCMSIS-DAPファームウェアを書き込んでおく。

中華STM32基板とトラ技ARM基板との3.3V, GND, SWDIO, SWCLKを接続し、
トラ技ARM基板にUSBで接続

$ cd tcl
$ ../src/openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg


openocd_cmsis copy.png

を、いい感じですね。

適当なバイナリを書き込んでみましょう

openocd_cmsis_write.png

速度もまあまあでしょうか?


ところで、HALドライバのデバイスの定義で、F103C8が無くってF103C6とCBしかないのが
紛らわしくて困りました。
C8とCBの違いはFLASHサイズだけのようなので、CBの定義を使っておけばいいんでしょうけど…

STM32 ComparatorのイベントでTimerのInput Captureにトリガするには? [電子工作]

CubeMXで構成できないか調べているところですが、

COMPx

Configurationで[Output Parameters] -> [Output Internal Selection]で
"Timer x Input Capture x"を指定

TIMx

上記でしていしたTimerのChannelについて、"Input Capture direct mode"を指定

ここで問題。

CubeMXでは"Input Capture direct mode"を指定すると、必ずTIMx_CHxの入力ピンを構成してしまう模様。

なのでTIMx_CHxの入力ピンが空いていないとCubeMXでは構成できない?

とりあえず、TimerのChannel設定無しでコードを生成してから
下記のコードをMX_TIMx_Init()の関数に差し込めばTIMx_CHxのピンを使わずに
COMP -> TIMのInputCaptureへの接続ができそう。
(下記の例は TIM3 Channel3にCOMPからのイベントを接続)

  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }

STM32 + HAL だいぶわかってきた [つぶやき]

Nucleo32F303K8をmbedとして使わずにSTM32開発ボードとしていじり中。

mbedとして使う気は無いのでSB16,SB18のジャンパを剥がしました。邪魔なので。

とりあえずHALライブラリを使って、

・UARTでPCと通信(ポーリング、割り込み、DMA送信)
・SPIでMMC互換カード(uSD)からファイルread(ポーリング、DMA)
・DACで音声出力(タイマ割り込み、DMA)

の使い方がだいたいわかった。はず。

HALライブラリだけど、、、
ライブラリのコードを読まないと使い方がわからん気がするんだけどそんなもんなんかな。
使い方さえわかればあっさり動くので便利といえば便利なのだけれど。

そんでコードを読んでいると冗長コードがすげぃ多くて気になって仕方がなかったり。

実際、UART、SPI、DAC、DMA、FatFsを組み込んでwavファイルを再生するだけで
Flashサイズ30kBとなかなかに素敵なバイナリサイズに。
F303K8で64kBのFlashがあるのでまあいいじゃんといえばいいのかもだけど(消費電力を気にしなければ)。

関数ごとのサイズを調べてみると、くそでかいのは

・HAL_RCC_OscConfig
・HAL_RCC_ClockConfig
・UART_SetConfig
・HAL_DMA_IRQHandler
・HAL_SPI_TransmitReceive
・HAL_SPI_TransmitReceive_DMA

あたりなので、このへんだけ書き直せばだいぶサイズを減らせるのかなあとか。

いっそHALライブラリを参考にしてレジスタを直で叩くかという気もするけれど、
ポータビリティが無くなるし全部をやるのは苦労の割にそんなに効果がなさそうなので
ポイントを絞って削るほうが良さげ。

VirtualBoxのUbuntuにgnu arm embedded toolchain + OpenOCDでSTM32の開発環境を作る [電子工作]

開発環境をUbuntuに変更しました。

今回は、STM32CubeMXから出力したプロジェクトフォルダを「そのまま」でビルドを目指します。

環境
・MacBook Pro (15-inch, Early 2011) (16GB RAM + 1TB SSD)
・macOS Sierra バージョン 10.12.1
・VirtualBox 5.0.28
・Ubuntu 16.04 LTS 日本語Remix (4GB RAM + 2Core割り当て)

目標
・テキストエディタ+makeで開発
・launchpadのGNU ARM Embedded Toolchainでビルドする
・OpenOCDで書き込み & デバッグする

ターゲット
・Nucleo-F303K8 ボード
・STM32CubeMXのプロジェクトをそのままビルドするMakefileを書く!

それでは手順開始です。

0.ダウンロードフォルダのシンボリックリンクを作る
Firefoxでダウンロードするときのフォルダ名が「ダウンロード」とコマンドから使いづらいので、
シンボリックリンクを作っておきます。
    cd ~
    ln -s ダウンロード downloads

1.arm-none-eabi-gccの入手
お手軽にlaunchpadからバイナリを入手します。
GNU ARMダウンロード.png

右のDownloadsのリストから、gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2をダウンロードし、HOME直下に展開します。
makefileから参照するために、フォルダ名からバージョン番号を外しておきます。
    $ cd ~
    $ tar jxf ~/downloads/gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2
    $ mv gcc-arm-none-eabi-5_4-2016q3 gcc-arm

とりあえず、pathは通さないでおきます。

2.OpenOCDの入手とインストール
今回はgitリポジトリから最新版のソースを持ってきて自前でビルドしてみます。

ビルドに必要なパッケージをインストール
    $ sudo apt-get install git
    $ sudo apt-get install libtool autoconf
    $ sudo apt-get install texinfo
    $ sudo apt-get install libusb-1.0-0-dev
 


gitからソースツリーを入手
    $ cd ~
    $ git clone http://repo.or.cz/r/openocd.git openocd


あとはビルドするだけ
    $ cd openocd
    $ ./bootstrap 
    $ ./configure --enable-stlink
    $ make -j2


USB接続のST-LinkをOSで認識するようにudevルールを設定します。
contrib/99-openocd.rulesから、ST-Link V2.1の設定を抜き出して設定しました。
(このファイルをそのまま設定しちゃってもいいような気もしますが)
    $ grep 374b contrib/99-openocd.rules > 99-stlink.rules
    $ cat 99-stlink.rules 
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE="664", GROUP="plugdev"
    $ sudo cp 99-stlink.rules /etc/udev/rules.d/
    $ sudo service udev restart


Nucleoボードを接続して動作を確認してみましょう。
VirtualBoxの方で、ST-Linkのデバイスを仮想マシンに回すように設定します。

MacBookにNucleoボードを接続し、VirtualBoxの仮想マシンの設定で、ポート - USBを選択

ST-LINK仮想マシン設定 copy.png

USBデバイスフィルター.png

追加アイコンをクリックして、STM32 STLinkを追加

ST-Linkを追加.png

ST-Linkを追加2.png

MacBookから一度Nucleoボードを取り外して再度接続すると、仮想マシン側で認識します。

mbedのフォルダが自動で表示される.png

接続を確認してみます。
    $ cd ~/openocd/tcl
    $ ../src/openocd -f board/st_nucleo_f3.cfg
Open On-Chip Debugger 0.10.0-dev-00419-gbcaf775 (2016-11-19-09:01)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v28 API v2 SWIM v16 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 3.252528
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints


このように出力され、NucleoボードのLEDが赤と緑に点滅すれば成功です。

確認したら、Ctrl-CでOpenOCDを終了させてボードを外しておきましょう。

3.STM32CubeMXのインストール
まずJava JREをインストールします。
OpenJDK版でもいいような気もしますが、Oracle版を入れてみます。

Oracleのサイトから、Linux x64のアーカイブをダウンロード

Oracle JREのダウンロード.png

以下の手順でインストールします。

    $ sudo mkdir -p /usr/lib/jvm
    $ sudo tar zxf ~/downloads/jre-8u112-linux-x64.tar.gz -C /usr/lib/jvm
    $ sudo update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jvm/jre1.8.0_112/bin/java" 1
    $ sudo update-alternatives --set java /usr/lib/jvm/jre1.8.0_112/bin/java
    $ java -version


JREをインストール後、JAVA_HOMEをユーザーのプロファイルに設定しておきます。

.bashrcの末尾に以下を追記
export JAVA_HOME=/usr/lib/jvm/jre1.8.0_112


STMicroのサイトから、STM32CubeMXをダウンロードして展開

STM32CubeMXをダウンロード.png

    $ cd ~/downloads
    $ unzip en.stm32cubemx.zip


ここで、インストーラを実行するために、32ビット用のライブラリが必要になります。
最初はこれがわからずにえらいハマった…

    $ sudo dpkg --add-architecture i386
    $ sudo apt-get update
    $ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386


インストーラを実行

STM32CubeMXインストーラ実行.png

デフォルト設定でインストールします。

 STM32CubeMXインストーラ.png

インストールできたら、STM32CubeMXを実行

STM32CubeMX実行.png

Help - Install New Librariesで、STM32F3のFirmware Packageをダウンロードします。

STM32CubeMXパッケージインストール.png

STM32CubeF3ダウンロード.png

4.STM32CubeMXでプロジェクト作成
とりあえず、Lチカさせてみましょう。

STM32CubeMXを起動してNew Projectをクリック

CubeMX_New Project.png

Board Selectorタブで、NUCLEO-F303K8を選択

CubeMX_Board Select.png

このボードではPB3がLED3に接続されているので、PinoutのタブでPB3のピン設定をGPIO_Outputに設定

CubeMX_Config LED3 Pin.png

ConfigurationのタブでGPIOを選択し

CubeMX_Config LED3 Pin Label.png

User Labelに「LED3」の名前をつけておきます。

CubeMX_Config LED3 Pin Label-2.png

こうすることで、コードからはGPIO_ほにゃらら_LED3でピン設定できるようになります。

Project - Settingで出力先と、使用するToolchainを選択します。
Toolchainには、必ずSW4STM32を選択するようにしてください。

CubeMX_Project Setting.png

CubeMX_Project Setting detail.png

Project - Generate Codeでコードを生成
CubeMX_Code Generate.png

生成されたフォルダで、makefileを作成します。
CubeMX_edit makefile.png

PROJECT = nucleof303k8_led
 
DIR=~/gcc-arm/bin
CC=$(DIR)/arm-none-eabi-gcc
NM=$(DIR)/arm-none-eabi-nm
CP=$(DIR)/arm-none-eabi-objcopy
OD=$(DIR)/arm-none-eabi-objdump
SZ=$(DIR)/arm-none-eabi-size

OPENOCD=~/openocd
BOARDCNF=st_nucleo_f3.cfg

DEFS = -DUSE_HAL_DRIVER -DSTM32F303x8

MCU = cortex-m4
MCFLAGS  = -MD -mcpu=$(MCU) -march=armv7e-m -mtune=$(MCU)
MCFLAGS += -mthumb -mlittle-endian -mno-unaligned-access -mno-sched-prolog
MCFLAGS += -mfloat-abi=hard  -mfpu=fpv4-sp-d16
MCFLAGS += -fno-strict-aliasing -fsigned-char
MCFLAGS += -ffunction-sections -fdata-sections
MCFLAGS += -fno-schedule-insns2
MCFLAGS += -fno-common -fno-hosted
MCFLAGS += -mthumb-interwork
MCFLAGS += -fmessage-length=0
MCFLAGS += -ffreestanding
MCFLAGS += -fno-move-loop-invariants
MCFLAGS += -Wl,--gc-sections

STM32_INCLUDES = \
	-IInc \
	-IDrivers/CMSIS/Device/ST/STM32F3xx/Include \
	-IDrivers/CMSIS/Include \
	-IDrivers/STM32F3xx_HAL_Driver/Inc/Legacy \
	-IDrivers/STM32F3xx_HAL_Driver/Inc

#OPTIMIZE = -Os
OPTIMIZE = -gdwarf-2 -O0 -flto-partition=none -fipa-sra

CFLAGS  = $(MCFLAGS) $(OPTIMIZE) $(DEFS) $(STM32_INCLUDES)
CFLAGS += -Wl,-T,STM32F303K8Tx_FLASH.ld
AFLAGS  = $(MCFLAGS)

CSRC = \
	Src/main.c \
	Src/stm32f3xx_it.c \
	Drivers/CMSIS/Device/ST/STM32F3xx/Source/Templates/system_stm32f3xx.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_cortex.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_rcc.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c \
	Src/_exit.c

ASRC = Drivers/CMSIS/Device/ST/STM32F3xx/Source/Templates/gcc/startup_stm32f303x8.s

COBJ = $(CSRC:.c=.o)
AOBJ = $(ASRC:.s=.o)

all: build size

build: elf hex lst sym

elf: $(PROJECT).elf
hex: $(PROJECT).hex
lst: $(PROJECT).lst
sym: $(PROJECT).sym

%.hex: %.elf
	@echo
	$(CP) -O ihex $< $@

%.lst: %.elf
	@echo
	$(OD) -h -S -C $< > $@

%.sym: %.elf
	@echo
	$(NM) -n $< > $@

size:
	@echo
	$(SZ) $(PROJECT).hex
	$(SZ) $(PROJECT).elf
	$(SZ) -A -x $(PROJECT).elf

%.elf: $(AOBJ) $(COBJ)
	@echo
	$(CC) $(CFLAGS) $^ -o $@

$(COBJ) : %.o : %.c
	@echo
	$(CC) -c $(CFLAGS) $< -o $@

$(AOBJ) : %.o : %.s
	@echo
	$(CC) -c $(CFLAGS) $< -o $@

clean:
	@echo
	find . -name '*.o' -print -or -name '*.d' -print | xargs rm -f
	rm -f $(PROJECT).elf $(PROJECT).hex $(PROJECT).lst $(PROJECT).sym

flash: $(PROJECT).elf
	$(OPENOCD)/src/openocd -s $(OPENOCD)/tcl -f board/$(BOARDCNF) -c "program $^ verify reset exit"


Srcフォルダに_exit.cも足しましょう
CubeMX_edit _exit.c.png

どっかに、いい_exit.cないですかねえ?

Src/main.cを編集して、Lチカコードを書きます。
User Labelをつけたので、実際のピン番号(PB3)を気にせず書けます。
生成されたコードのあちこにに埋め込まれている、「USER CODE BEGIN」〜「USER CODE END」の
間にコードを書くと、書いたコードを残したままで、後でCubeMXでコードを生成しなおせます。
CubeMX_LED blink test.png

ビルド
    $ make


ビルドできましたか?

CubeMX_build result.png

make flashで、OpenOCDで焼き込みできるようにしてあります。
    $ make flash


LEDが点滅すれば成功です!
タグ:STM32

[電子工作] MacでSTM32の開発環境を GNU arm + OpenOCDで作ってみる [電子工作]

ようやくビルド+書き込みが出来るようになったレベルですが、まとめておきます。

-> CubeMXで生成したプロジェクトのビルドはこちら
環境
・MacBook Pro (15-inch, Early 2011) (いい加減古い…)
・macOS Sierra バージョン 10.12.1
・make … たぶんXCodeのコマンドラインツール。

目標
・テキストエディタ+makeで開発
・launchpadのGNU ARM Embedded Toolchainでビルドする
・OpenOCDで書き込み & デバッグする

ターゲット
・Nucleo-F303K8 ボード
 単品で同じ石を秋月で買えるから、という理由で選びましたが、ちょっと使いづらいかも
・STM32CubeF3 Firmware PackageのExampleのGPIO_IOToggleをビルドし実行する
 いわゆるLチカ

それでは手順開始です。

1.arm-none-eabi-gccの入手
お手軽にlaunchpadからバイナリを入手します。
右のDownloadsのリストから、gcc-arm-none-eabi-5_4-2016q3-20160926-mac.tar.bz2をダウンロードし、~/STM32Toolchain下に展開します。
(展開先は適当)
      cd ~
      mkdir STM32Toolchain
      cd STM32Toolchain
      tar jxf ~/Downloads/gcc-arm-none-eabi-5_4-2016q3-20160926-mac.tar.bz2

とりあえず、pathは通さないでおきます。

2.OpenOCDの入手とインストール
こちらもお手軽に、GNU ARM Eclipse OpenOCDのインストールパッケージで入れてしまいます。
githubからインストールパッケージを入手します。
Downloadsのリストから、gnuarmeclipse-openocd-osx-0.10.0-201610281609-dev.pkgをダウンロードし、実行します。

インストール先は、/Applications/GNU ARM Eclipse/OpenOCD になっています。

3.STM32 HAL Driverの入手
STmicroのサイトから、ターゲットのCPUに合わせたドライバをダウンロードします。
今回はSTM32F30xシリーズなので、STM32CubeF3をダウンロードします。
メールアドレスなどの入力が必要ですが、いくつかの手順を経るとen.stm32cubef3.zipというファイルとしてダウンロードできます。
これも、~/STM32Toolchain下に展開します。

    cd ~/STM32Toolchain
    unzip ~/Downloads/en.stm32cubef3.zip


4.プロジェクトのひな形を作成
プロジェクトフォルダとして、~/STM32ProjectフォルダにNucleo_F303K8_sample_gpioフォルダを作成します。

    cd ~
    mkdir -p STM32Project/Nucleo_F303K8_sample_gpio


作成したフォルダに、STM32CubeF3を展開したフォルダから、Driversフォルダをまるごとごっそりコピーします。

    cd ~/STM32Project/Nucleo_F303K8_sample_gpio
    cp -a ~/STM32Toolchain/STM32Cube_FW_F3_V1.6.0/Drivers .


5.サンプルのコードをコピー
STM32CubeF3を展開したフォルダから、Projects/STM32F303K8-Nucleo/Examples/GPIO/GPIO_IOToggleフォルダの、SrcIncSW4STM32フォルダをコピーします。

    cd ~/STM32Project/Nucleo_F303K8_sample_gpio
    cp -a ~/STM32Toolchain/STM32Cube_FW_F3_V1.6.0/Projects/STM32F303K8-Nucleo/Examples/GPIO/GPIO_IOToggle/Src .
    cp -a ~/STM32Toolchain/STM32Cube_FW_F3_V1.6.0/Projects/STM32F303K8-Nucleo/Examples/GPIO/GPIO_IOToggle/Inc .
    cp -a ~/STM32Toolchain/STM32Cube_FW_F3_V1.6.0/Projects/STM32F303K8-Nucleo/Examples/GPIO/GPIO_IOToggle/SW4STM32 .


6.makefileを準備
プロジェクトフォルダ直下に、下記の内容でmakefileを作ります。

※コンパイルフラグはよくわからずに書いているものもあるのでもう少し煮詰めたほうが良いような気はしています。
※HAL Driverのソースは、ビルドしながら必要なものを集めました。
PROJECT = sample

DIR=~/STM32Toolchain/gcc-arm-none-eabi-5_4-2016q3/bin
CC=$(DIR)/arm-none-eabi-gcc
NM=$(DIR)/arm-none-eabi-nm
CP=$(DIR)/arm-none-eabi-objcopy
OD=$(DIR)/arm-none-eabi-objdump
SZ=$(DIR)/arm-none-eabi-size

DEFS = -DUSE_HAL_DRIVER -DSTM32F303x8

MCU = cortex-m4
MCFLAGS  = -MD -mcpu=$(MCU) -march=armv7e-m -mtune=$(MCU)
MCFLAGS += -mthumb -mlittle-endian -mno-unaligned-access -mno-sched-prolog
MCFLAGS += -mfloat-abi=hard  -mfpu=fpv4-sp-d16
MCFLAGS += -fno-strict-aliasing -fsigned-char
MCFLAGS += -ffunction-sections -fdata-sections
MCFLAGS += -fno-schedule-insns2
MCFLAGS += -fno-common -fno-hosted
MCFLAGS += -mthumb-interwork
MCFLAGS += -fmessage-length=0
MCFLAGS += -ffreestanding
MCFLAGS += -fno-move-loop-invariants

STM32_INCLUDES = -IInc \
	-IDrivers/BSP/STM32F3xx_Nucleo_32 \
	-IDrivers/CMSIS/Device/ST/STM32F3xx/Include \
	-IDrivers/CMSIS/Include \
	-IDrivers/STM32F3xx_HAL_Driver/Inc/Legacy \
	-IDrivers/STM32F3xx_HAL_Driver/Inc

#OPTIMIZE       = -Os
OPTIMIZE       = -gdwarf-2 -O0 -flto-partition=none -fipa-sra

CFLAGS	= $(MCFLAGS)  $(OPTIMIZE)  $(DEFS)  $(STM32_INCLUDES)
CFLAGS += -Wl,-T,SW4STM32/STM32F303K8_NUCLEO/STM32F303K8Tx_FLASH.ld
AFLAGS	= $(MCFLAGS)

SRC = Src/main.c \
	Src/stm32f3xx_it.c \
	Src/system_stm32f3xx.c \
	Drivers/BSP/STM32F3xx_Nucleo_32/stm32f3xx_nucleo_32.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_cortex.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_rcc.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c

ASRC = SW4STM32/startup_stm32f303x8.s

COBJ      = $(SRC:.c=.o)
AOBJ      = $(ASRC:.s=.o)

all: build size

build: elf hex lst sym

elf: $(PROJECT).elf
hex: $(PROJECT).hex
lst: $(PROJECT).lst
sym: $(PROJECT).sym

%.hex: %.elf
	@echo
	$(CP) -O ihex $< $@

%.lst: %.elf
	@echo
	$(OD) -h -S -C $< > $@

%.sym: %.elf
	@echo
	$(NM) -n $< > $@

size:
	@echo
	$(SZ) $(PROJECT).hex
	$(SZ) $(PROJECT).elf
	$(SZ) -A -x $(PROJECT).elf

%.elf:  $(AOBJ) $(COBJ)
	@echo
	$(CC) $(CFLAGS) $^ -o $@

$(COBJ) : %.o : %.c
	@echo
	$(CC) -c $(CFLAGS) $< -o $@

$(AOBJ) : %.o : %.s
	@echo
	$(CC) -c $(CFLAGS) $< -o $@

clean:
	@echo
	find . -name '*.o' -print | xargs rm
	rm $(PROJECT).elf $(PROJECT).hex $(PROJECT).lst $(PROJECT).sym


makeしてみましょう。

/Users/user/STM32Toolchain/gcc-arm-none-eabi-5_4-2016q3/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7e-m/fpu/libc.a(lib_a-exit.o): In function `exit':
exit.c:(.text.exit+0x16): undefined reference to `_exit'
collect2: error: ld returned 1 exit status
make: *** [sample.elf] Error 1


_exitが無いと怒られてしまいました。
たぶん空関数で良いと思うので、適当に作ってSrcフォルダに突っ込みます。

_exit.c
void
__attribute__((weak))
_exit(int code __attribute__((unused)))
{
  // TODO: write on trace
  while (1)
    ;
}


makefileのビルド対象にも加えて、再ビルド。

SRC = Src/main.c \
	Src/stm32f3xx_it.c \
	Src/system_stm32f3xx.c \
	Drivers/BSP/STM32F3xx_Nucleo_32/stm32f3xx_nucleo_32.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_cortex.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_rcc.c \
	Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c \
	Src/_exit.c


sample.elfファイルが生成されれば成功です。
タグ:STM32 STM32F303K8

STM32を初めてみようと思いつつ [つぶやき]

何周遅れなんだというのは置いといて。

やりたいことの大抵はMega328で足りるし、
それ以上でもXMEGAが使えればそんなに困ってないし、
と言うのはあるんですが。

とは言え、XMEGAは入手性がよろしくない…

STM32のCoretex-M4のチップ単品が410円というイイカンジの値段で秋月で入手可能だし
だいぶんハヤってるみたいだしなので環境を作ってなんか作ってみようかなあ、などと考えてます。

やっつけ中のブツがまだあるのでなかなか手を付けられれませんが。
#NucleoボードとDiscoveryだけは買った。


んで。

このブログ、なんでかC++関連のメモのほうが閲覧数が多いようで…
そういうのはいくらでも情報があるから需要ないんじゃと思ってたんですがどうなんでしょう?
なんか書いて欲しいネタとかあるか訊いてみたい気もします。

[電子工作] ATxmega (E series)でFatFsをDMA対応する - rcvr_spi_milti関数 - [電子工作]

いよいよ本丸です。

いきなりコードから。

/* Receive a data block fast */
static
void rcvr_spi_multi (
	BYTE *p,	/* Data read buffer */
	UINT cnt	/* Size of data block */
)
{
	volatile uint8_t dmy = 0xff;

	/* DMA Chennel0 -> Transfer from USARTD0.Data to buffer */
	EDMA.CH0.ADDRCTRL     = EDMA_CH_RELOAD_NONE_gc | EDMA_CH_DIR_FIXED_gc;
	EDMA.CH0.DESTADDRCTRL = EDMA_CH_RELOAD_NONE_gc | EDMA_CH_DIR_INC_gc;
	EDMA.CH0.TRIGSRC  = EDMA_CH_TRIGSRC_USARTD0_RXC_gc;

	EDMA.CH0.ADDRL =  (uint16_t)(&USARTD0.DATA)       & 0xff;
	EDMA.CH0.ADDRH = ((uint16_t)(&USARTD0.DATA) >> 8) & 0xff;

	EDMA.CH0.DESTADDRL =  (uint16_t)p       & 0xff;
	EDMA.CH0.DESTADDRH = ((uint16_t)p >> 8) & 0xff;

	EDMA.CH0.TRFCNTL =  cnt       & 0xff;
	EDMA.CH0.TRFCNTH = (cnt >> 8) & 0xff;

	/* DMA Chennel2 -> Transfer dummy 0xff to USARTD0.Data */
	EDMA.CH2.ADDRCTRL     = EDMA_CH_RELOAD_NONE_gc | EDMA_CH_DIR_FIXED_gc;
	EDMA.CH2.DESTADDRCTRL = EDMA_CH_RELOAD_NONE_gc | EDMA_CH_DIR_FIXED_gc;
	EDMA.CH2.TRIGSRC  = EDMA_CH_TRIGSRC_USARTD0_DRE_gc;

	EDMA.CH2.ADDRL =  (uint16_t)(&dmy)       & 0xff;
	EDMA.CH2.ADDRH = ((uint16_t)(&dmy) >> 8) & 0xff;

	EDMA.CH2.DESTADDRL =  (uint16_t)(&USARTD0.DATA)       & 0xff;
	EDMA.CH2.DESTADDRH = ((uint16_t)(&USARTD0.DATA) >> 8) & 0xff;

	EDMA.CH2.TRFCNTL =  cnt       & 0xff;
	EDMA.CH2.TRFCNTH = (cnt >> 8) & 0xff;

	/* To make stable transmission, Rx must be enaled first ! */
	EDMA.CH0.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_SINGLE_bm;
	EDMA.CH2.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_SINGLE_bm;

	while ( !(EDMA.INTFLAGS & EDMA_CH0TRNFIF_bm) ) {}

	EDMA.INTFLAGS |= EDMA_CH0TRNFIF_bm | EDMA_CH2TRNFIF_bm;
}


(E)DMAの使い方は、
 1. 転送元アドレスを設定
 2. 転送先アドレスを設定
 3. 転送条件(開始トリガ)を設定
 4. 完了を待つ
が基本のようです。

3.の転送条件について様々なペリフェラルの状態を設定できます。
今回はUSARTのSPIマスタモードの転送なので、それに合わせて設定することになります。

SPI転送には「送信処理」と「受信処理」の両方が必要になります。
DMAは片方向の転送処理なので、「送信DMA動作」と「受信DMA動作」の2つのchannelを使うことになります。
今回は Channel0 -> 受信動作, Channel2 -> 送信動作 に割り当てています。

USART-SPI on DMA.jpg

1バイトの受信(転送)処理とDMA転送との対応は下記のようになります。

/* Exchange a byte */
static
BYTE xchg_spi (		/* Returns received data */
	BYTE dat		/* Data to be sent */
)
{
	/* 送信処理 => データ受信時は、ダミーで0xffを送る! => DMA Channel2に割り当て */

	/* 送信可能を待つ => DMAでは「EDMA_CH_TRIGSRC_USARTD0_DRE_gc」条件 */
	while ( !(USARTD0.STATUS & USART_DREIF_bm));
	USARTD0.DATA = dat;								/* データを送信 */

	/* 受信処理 => DMA Channel0に割り当て*/

	/* 受信完了を待つ => DMAでは「EDMA_CH_TRIGSRC_USARTD0_RXC_gc」条件 */
	while ( !(USARTD0.STATUS & USART_RXCIF_bm));
	return USARTD0.DATA;							/* 受信データを読む */
}


ここで一点、注意が。

	/* To make stable transmission, Rx must be enaled first ! */
	EDMA.CH0.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_SINGLE_bm;
	EDMA.CH2.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_SINGLE_bm;


ねむいさんのブログにも書かれていましたが、DMAを起動する際に受信側を先に起動する必要があるようです。
(合わせてDMAの優先度も受信側が優先度高にしたほうが良い?)
理由はよくわからないのですが、逆にするとDMA転送しなくなることがありました。
これ、本家Application Noteに書いといてほしいですね…

[電子工作] ATxmega (E series)にFatFsを組み込む(USART-SPI) [電子工作]

FatFs R0.12bのATxmega Eシリーズへの組み込みです。

サンプルプロジェクトの、avr/srcから以下のコードを持ってきてベースとします

diskio.h -> 使用しない機能の設定
ff.c -> そのまま使う
ff.h -> そのまま使う
ffconf.h -> 下記の構成設定
integer.h -> そのまま使う
mmc_avr_usart.c -> XMEGA EシリーズのDMA化する

今回はとりあえず読めればいいので、以下の最小構成としました。

ffconf.h
_FS_READONLY = 1 (read only)
_FS_MINIMIZE = 1 (opendir, readdirを使いたいので)
_USE_STRFNUC = 0
_USE_FIND = 0
_USE_MKFS = 0
_USE_FASTSEEK = 0
_USE_EXPAND = 0
_USE_CHMOD = 0
_USR_LABEL = 0
_USE_FORWARD = 0
_USE_LFN = 0
_FS_RPATH = 0

_VOLUMES = 1
_STR_VOLUME_ID = 0
_MULTI_PARTITION = 0
_USR_TRIM = 0
_FS_NOFSINFO = 0

_FS_TINY = 1
_FS_EXFAT = 0
_FS_NORTC = 1
_FS_LOCK = 0
_FS_REENTRANT = 0

diskio.h
_USE_WRITE = 0
_USE_IOCTL = 0
_USE_ISDIO = 0

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

対応のメインはmmc_avr_usart.cです。
Chan氏のサンプルではAVR MEGA系(1284P)のUSART-SPIの実装になっています。

編集する箇所のサマリは下記になります。

power_on()
 USART-SPIとEDMAの有効化を設定(後述)

power_off()
 USART_SPIとEDMAの無効化を設定(後述)

xchg_spi()
 USART-SPIへのデータ送信&受信(1byte)(後述)
 受信のときはダミーデータ(0xff)を送る

rcvr_spi_multi()(後述)
 DMA化のキモ
 指定された回数だけ、ダミー値(0xFF)を送信してからデータを受信します。

xmit_spi_multi()
 今回はread onlyなので関数ごと削除
 なお、対応自体はrcvr_spi_multiとほぼ同じ。

disk_status(), disk_initialize(), disk_read()
 サンプルでは、CompacfFlashCardとSD-Card(MMC)のそれぞれをdiskio.cで
 呼び分けるようになっています。CFは不要なので、mmc_avr_usart.cにある
 関数"mmc_disk_xxx()"の名前を"disk_xxx()"に変更して直接呼ぶようにします。
 それぞれの関数の第一引数に"BYTE pdrv"を追加して、
 pdrv != 0だったらエラーリターンするよう一応ガードしておきます。

disk_timerproc()
 適当なタイマで100Hzの定期割込みを作って呼ぶようにする。
 WriteProtectとCardSenseを接続しない場合は状態チェックのコードを削除します。

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

ではいよいよ対応の詳細に行きます。

XMega EのUSART-SPI対応

XMega EにはUSARTC0, USARTD0の2つのUSARTがあります。
今回はUSARTD0側を使いました。
また、ピンアサインの都合で、PORTD[5:7]にリマップしています。
SSはPORTD4を割り当てます。

 PORTD4 : SPI-SS -> Output, High
 PORTD5 : SPI-SCK -> Output, Low
 PORTD6 : SPI-MISO -> In, 外部抵抗でプルアップ
 PORTD7 : SPI-MOSI -> Output, High

USART-SPIの有効化/無効化の設定は以下のようになります。

power_on()関数にて
{
	PORTD.DIRSET   = PIN4_bm;
	PORTD.OUTSET   = PIN4_bm;

	PORTD.DIRSET = PIN5_bm | PIN7_bm;
	PORTD.OUTCLR = PIN5_bm;
	PORTD.OUTSET = PIN7_bm;

	PORTD.DIRCLR = PIN6_bm;

	PORTD.REMAP = PORT_USART0_bm;

	/* Setup USART D0 as MSPI mode */

	/* SPI Mode 0, MSB first */
	USARTD0.CTRLC = USART_CMODE_MSPI_gc;

	USARTD0.CTRLB = USART_TXEN_bm | USART_RXEN_bm;

	USARTD0.BAUDCTRLA = 63; /* 100kHz */


power_off()関数にて
	USARTD0.CTRLB     = 0;

	PORTD.OUTSET = PIN4_bm;


1バイト送信&受信は以下の流れになります。

「送信可能(DREIF = 1)か確認する」->「DATAに書き込む」
 ->「受信完了(RXCIF = 1)を待つ」->「DATAから読む」

static
BYTE xchg_spi (		/* Returns received data */
	BYTE dat		/* Data to be sent */
)
{
	while ( !(USARTD0.STATUS & USART_DREIF_bm));
	USARTD0.DATA = dat;
	while ( !(USARTD0.STATUS & USART_RXCIF_bm));
	return USARTD0.DATA;
}


ここで、とりあえずrcvr_spi_multi()関数を上記のxchg_spi()の処理を繰り返すように実装すれば、USART-SPIを用いたSD-Cardアクセスが動きます。

rcvr_spi_multi()関数のDMA化については長くなってきたので別記事に分けます。

[電子工作] ATxmega (E series)でSD Card WAVE Playerの実験(予告) - SPI Master with DMA - [電子工作]

実験成功したので記事化予告です。

すっかり影の薄いATxmegaの更に影の薄いEシリーズにChan氏のFatFsを組み込んでDMA化しました。Eシリーズは(E)DMAとDACを積んでいて、同等のAシリーズ(32A4)よりもちょいと安くてちょいと小さいのが魅力です(^_^;)
ペリフェラルについて微妙にAシリーズとは互換が無いものがあるので注意が必要です(^_^;)
(Timer4/5とかEDMAとか)

レシピ:
 マイコン : ATxmega32E5
  TQFP-32pinと小型でDACが載っているAVR XMEGAシリーズのマイコンです。
  RSコンポーネントやDigiKeyで450〜500円くらい。
  秋月にリクエストして取り扱ってもらいましょう!(切実)

 クロック : 12.8MHz
  特に理由は無し…
  たぶん8MHzでも間に合うんじゃないかな。

 SDカードからのRead
  Chan氏のFatFs(R0.12b)を最小構成,Read Onlyで組み込んでDMA化します。
  素のSPIはDMAでMaster動作できないので、USARTのMaster SPI modeを使います。
  EDMAを2 Standard Channel構成(CH0:Rx, CH2:Tx)で使用します。
  256バイト超のreadを256+残りに分ければPeripheral channelでもいいかも。

 DAC
  タイマでサンプリングレート毎に割込みを生成し、割込み処理でDACにデータを書き出します。
  アンプ(HT82V739)を通してスピーカーに出力しました。

結果:
 12.8MHzクロック (SPIクロック6.4MHz)で、読み出し約570kbyte/sec。
 44.1kHz/16bit/StereoのWAVファイルをリアルタイムで再生成功しました。
 写真はこの時のread の観測波形です。
  =>H期間がDMAのセットアップを含む1024バイトのread。
    パルスの周期は44.1kHz x 256サンプル = 約6ms。
sdread.jpg

[電子工作] ATXMEGA EシリーズのDFLL32Mの補正精度を確かめてみる [電子工作]

どのくらい使い物になるんでしょうねということでやってみました。

ちなみに前回試したDシリーズではなく、新たに入手したEシリーズです。
(Eシリーズも秋月で扱ってくれないかなあ)

クロックリファレンスにはちゃんとしたクロックジェネレータを使いたいところですが
持っていないのであくまで参考値です。

□リファレンス
 Mega328P + 12.288MHzの水晶(±30ppm)で f=50Hz Duty=50%の矩形波を出力。

□確認方法
 リファレンスの波形から5ms遅らせて10msのタイマーをスタート。
 10ms毎にリファレンスの出力を確認して、正しい出力が取れなくなったら
 5msずれたと判断してタイマーを停止する。

□結果
 RC32MHzのみ -> 数秒でアウト -> ±0.3%くらい?
 RC32MHz + 32.768kHz水晶(±20ppm)でDFLL -> 数十秒くらいでアウト -> ±200ppmくらい?

いちおう精度は上がっているようですがもうちょっと頑張って欲しいような気も。
ブレッドボードでさらに変換基板まで介しているせいか?

参考にXMEGA側にも12.288MHzの水晶 + 10pFのセラコンをつけたところ
あっという間にずれてしまいビックリしました。
負荷容量を22pFにしたら安定してずれなくなりましたが、データシートだと10pFだったような?


ロジックアナライザか多チャンネルのオシロスコープが欲しいなあ (^^;


余談:
XMEGA EシリーズはTQFP32でサイズが手頃だったり、3〜400円くらいで値段も手頃だったり、
8bit AVRでは珍しくDACがついていたりで遊べそうなんですが、
入手性が悪かったりところどころに罠があったりでなかなか楽には行かないですねー。

タイマ4のOverflow Flagが割込みで自動クリアされなくて二時間位ハマってしまいました。
データシートのOverflow Flagのところには自動クリアの記載はないので間違ってはないけど
tinyやmegaでは自動クリアされてたしxmegaでもコンペアマッチのフラグは自動クリアされるんで
いやいやそれはないだろうとか(汗
 

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。