がれすたさんのDIY日記

電子回路、Python、組み込みシステム開発、自作エフェクターを語るblog

PySide2でQtQuick(qml)使うメモ2

前回に引き続き今回もPySide2+Qtquick(qml)のメモ

GUIだと結構定番の電卓っぽい奴の実装

f:id:gsmcustomeffects:20190818201337g:plain


Button押下でイベント発生させてTextInputからデータもらってTextInputに返すサンプルだと思ってくれればいい

Pythonコード

import sys
import os
from PySide2 import QtCore, QtWidgets, QtQml

class Connect(QtCore.QObject):
    def __init__(self, parent=None):
        super(Connect, self).__init__(parent)

    @QtCore.Slot(int,int,result = float)
    def sum(self,arg1,arg2):
        return arg1 + arg2

    @QtCore.Slot(int, int, result=float)
    def sub(self, arg1, arg2):
        return arg1 - arg2

    @QtCore.Slot(int, int, result=float)
    def mul(self, arg1, arg2):
        return arg1 * arg2

    @QtCore.Slot(int, int, result=float)
    def div(self, arg1, arg2):
        return arg1 / arg2

if __name__ == "__main__":
    os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
    app = QtWidgets.QApplication(sys.argv)
    myconnect = Connect()
    engine = QtQml.QQmlApplicationEngine()
    ctx = engine.rootContext()
    ctx.setContextProperty("Connect", myconnect)
    engine.load('mypyside2.qml')
    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())

qmlコード

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.0

Window {
    objectName: "a"
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    TextInput {
        id: textInput1
        x: 104
        y: 108
        width: 80
        height: 37
        text: qsTr("12")
        font.pixelSize: 20
    }

    TextInput {

        id: textInput2
        x: 179
        y: 108
        width: 80
        height: 37
        text: qsTr("2")
        font.pixelSize: 20
    }

    TextInput {

        id: textInput3
        x: 270
        y: 108
        width: 80
        height: 37
        text: qsTr("1")
        font.pixelSize: 20
    }

    Row {
        id: row
        x: 104
        y: 186
        width: 427
        height: 119
        spacing: 9

        Button {
            objectName:"button"
            id: button1
            width: 100
            height: 50
            text: qsTr("add")
            font.pointSize: 14
            onClicked:function(){
                textInput3.text = Connect.sum(textInput1.text,textInput2.text)
            }
        }

        Button {
            id: button2
            width: 100
            height: 50
            text: qsTr("sub")
            font.pointSize: 13
            objectName: "button"
            onClicked:function(){
                textInput3.text = Connect.sub(textInput1.text,textInput2.text)
            }
        }

        Button {
            id: button3
            width: 100
            height: 50
            text: qsTr("mul")
            font.pointSize: 12
            objectName: "button"
            onClicked:function(){
                textInput3.text = Connect.mul(textInput1.text,textInput2.text)
            }
        }

        Button {
            id: button4
            width: 100
            height: 50
            text: qsTr("div")
            font.pointSize: 12
            objectName: "button"
            onClicked:function(){
                textInput3.text = Connect.div(textInput1.text,textInput2.text)
            }
        }
    }
}

解説的なやつ

python

@QtCore.Slot(int,int,result = float)
    def sum(self,arg1,arg2):
        return arg1 + arg2

addの例で解説する。
@QtCore.Slot(int,int,result = float)で引数の型と返り値の型をqmlに対して公開する。
あとはdefで関数を実装するだけ前回との違いはSlotでreturnしているとこ。
ここで注意が必要なのが書かなくてもエラーは出ないがresult = floatは必須ということ
書かないと何も値が返ってこない

qml側

TextInput {

        id: textInput3
        x: 270
        y: 108
        width: 80
        height: 37
        text: qsTr("1")
        font.pixelSize: 20
    }

Button {
            objectName:"button"
            id: button1
            width: 100
            height: 50
            text: qsTr("add")
            font.pointSize: 14
            onClicked:function(){
                textInput3.text = Connect.sum(textInput1.text,textInput2.text)
            }
        }

こちらもaddの例で説明する。

まずqmlオブジェクトに対してはtextInput3.textのようにid.propertyでアクセスできる。(qmlではオブジェクト以下のパラメータ類のことをPropertyと呼ぶ

次にonclicked部分で押下時の動作を定義している。
textInput3.textにpython側のsumの返り値を代入するということをしているだけだ。

あくまでqmlオブジェクトへの代入はqml側でやるというとこがQtの方針
PythonC++側からも子オブジェクトにアクセスできるがQt公式は推奨していない(参照関係がごちゃごちゃになるため

この関係を守るとUI設計者と内部実装側が完全分業可能なので合理的ではある。

PySide2でQtQuick(qml)使うメモ1

自分用のメモです。

自分の動作環境は

  • PyCharm 無料版
  • Python 3.7.4 or 3.6(仮想で両方で試した)
  • Pycharm内臓のVenvでパッケージ管理

導入

PythonでQtを扱うにはPyQtとPysideの2つがある。

verごとに書くとこんな感じ

  • Qt4 : PyQt4,PyiSde
  • Qt5 : PyQt5,PySide2

という感じになる。
現行で使うならQt5系を利用するほうがいいだろうということでPyQt5,PySide2を選ぶことになる。
選定にあたりいろいろググった結果PySide2ってのがQt公式がサポートするQt5バインディング(Qt for Pythonと呼ばれてるのがこれにあたる)らしいのでこれを使うことにする。

ui -> pyをする使い方

QtDesignerというソフトを使ってグラフィカルにUIをデザインしてそのファイル(.ui)をpyside2-uic.exeを使って.py拡張子ファイルに変換してインポートする手法のこと。
この方法が一番情報も多くて使いやすいと思う。

いろんな人が情報を公開しているのでその辺を読むといいだろう
note.mu

qiita.com

UIファイルの更新のたびにPyCharm側でpyside2-uic.exeを自動で呼び出すこともできる。
qiita.com

QtQuickについて

これについては公式の説明が分かりやすい
Qt Quick 入門 第1回: Qt Quick とは - Qt Japanese Blog

ようはuiファイルではなくqmlというjson風の表記を使ってUIをデザインしていく手法であり比較的楽にかっこいいUIを作成できる。

f:id:gsmcustomeffects:20190818004444p:plain

画像は私が作成したUIであるが5分ぐらいでこのぐらいのGUIを作成できる。

PySide2導入とテスト

早速本題に入る。
まずはPyCharmで新規プロジェクトをつくる。
f:id:gsmcustomeffects:20190818010410p:plain

上部メニューのFile/settingでこの画面が開くのでproject interpreterの画面まで持ってくる。
f:id:gsmcustomeffects:20190818010741p:plain

+ボタンをクリックして必要なモジュールをインストールしてくる。
ここではPySide2を検索して持ってくる。
f:id:gsmcustomeffects:20190818011621p:plain

インストールができたら.pyファイルを作成して以下のリンクのコードを実行してみる。
https://doc.qt.io/qtforpython/tutorials/basictutorial/dialog.html

このような画面が出ていればPySide2の導入はOK
f:id:gsmcustomeffects:20190818012746p:plain

PySide2でQtQuick(qml)ファイルを扱う

次にqmlファイルを作るためにQt Creatorをダウンロードしてくる。
Download Qt: Choose commercial or open source

アカウントとか聞かれますけどスキップで何とかなります。
適当に自分の欲しいものをインストールしてください

CreatorとQt5.x系があればOKです。

Qt Creatorをインストール出来たら開いて新規プロジェクトでUI Prototypeを選ぶ
f:id:gsmcustomeffects:20190818021459p:plain

キットに関しては適当に選ぶといいです。
するとこのような画面になるのでデザインをクリックします。
f:id:gsmcustomeffects:20190818021603p:plain

UIデザイン画面が開くので次にインポートタブを開きます。
f:id:gsmcustomeffects:20190818022028p:plain

QtQuick.ControlsとQtQuick.Controls.Materialsを追加
f:id:gsmcustomeffects:20190818022236p:plain

そうするとエレメントタブにボタン類が入ってくる
f:id:gsmcustomeffects:20190818022427p:plain

ボタンをドラッグしてUIに追加する。
f:id:gsmcustomeffects:20190818022542p:plain

次にできたqmlをPycharmプロジェクトに入れてあげる
f:id:gsmcustomeffects:20190818022837p:plain

そしたらこのコードを実行

import sys
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QUrl

if __name__ == '__main__':
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    engine.load(QUrl("mypyside2.qml"))

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())

先ほど作ったUIが表示できていればOK
f:id:gsmcustomeffects:20190818023149p:plain

ボタンへのイベント追加

次に配置したウィジェットPythonコードをつないでいく。

python code

import sys
import os
from PySide2 import QtCore, QtWidgets, QtQml

class Connect(QtCore.QObject):
    def __init__(self, parent=None):
        super(Connect, self).__init__(parent)

    @QtCore.Slot()
    def button_clicked(self):
        print("button clicked")



if __name__ == "__main__":
    os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
    app = QtWidgets.QApplication(sys.argv)
    myconnect = Connect()
    engine = QtQml.QQmlApplicationEngine()
    ctx = engine.rootContext()
    ctx.setContextProperty("Connect", myconnect)
    engine.load('mypyside2.qml')
    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())

qml code

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Button {
        id: button
        x: 270
        y: 220
        text: qsTr("Button")
        onClicked:Connect.button_clicked()//python側のクラスとつなぐ
    }
}


実行してボタンをクリックするとprint文が実行されるはず。
f:id:gsmcustomeffects:20190818025353p:plain

説明(Python側)

まずQtCore.QObjectを継承したクラス(Connect)を作成する。
@QtCore.Slotでqml側にメソッドの存在を通知する。これによりqml側でdef以下を呼べるようになる。
@表記はPythont的に言うとデコレータってやつらしい。

os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"

Materialテーマを有効にする記述で特に必要はない
有効にしていると以下のリンクのようなことができる。
https://doc.qt.io/qt-5/qtquickcontrols2-material.html

ctx = engine.rootContext()
ctx.setContextProperty("Connect", myconnect)

engine内部の子ウィジェットを検索可能にする記述(あってるかわかんないけど
QMLにアクセスしたりQMLからアクセスしたりする場合はこの二行が必須っぽい

説明(qml側)

説明するといってもonClickedぐらい

クリックされた時の動作を記述するメンバー
ウィジェットごとにイベントが用意されてるSliderならonValueChangedみたいな感じで書ける。
Ctrl+Spaceで補完できるので何があるかは分かると思う。

メンバーにカーソルを合わせてF1を押すとウィジェットの説明を表示できる
f:id:gsmcustomeffects:20190818031331p:plain


参考文献

rootcontextの記述はC++も同様なので参考になった。
その他qmlとの相互イベント通知の参考になる。
QMLとC++のバインディング - Qiita

今回の記事はこの問題の劣化版みたいなものなのでこっちのスレッド読むともっといいのがつくれると思う
Connect python signal to QML ui slot with PySide2 - Stack Overflow

公式のサンプルとチュートリアル
QMLの例が2個しかないけど書き方の参考にはなる。
https://doc.qt.io/qtforpython/tutorials/index.html

MIMXRT10xxのUARTを使ってみる(ポーリングAPI)

今回はUARTについてやっていきたいと思います。
prinfに関しては評価ボードを使う限りは初期プロジェクト作成でリンクしてくれるので説明はしません。あくまでbyte転送APIで何か送るという感じですね。

環境としては以下を例に説明しますがRT1020でもRT1015でも同じシリーズならできると思います。

  • MCUXpressoIDE v11.0
  • Forlix MIMXRT1052CVL5B or MIMXRT1050-EVK: i.MX RT1050

プロジェクトの作成

新規プロジェクト(new project)作成をクリック

f:id:gsmcustomeffects:20190526045710p:plain
プロジェクトの作成

次にメモリ関連の設定です。
ここに関しては自分の環境にあったメモリ構成を作成して下さい

f:id:gsmcustomeffects:20190526045811p:plain
メモリ構成について

ForlinxのRT1052boardを使う場合のメモリ構成や起動に関してはここを読むといいと思います。

プロジェクト構成の確認とピン設定

前項まででプロジェクトの作成が終わっていると思うのでプロジェクト構成の確認をしていきます。
普通通りにプロジェクトを作成すると評価ボード用の設定がいろいろインサートされてると思います。(公式評価ボードのSDKおよびBSPをフォークして使っているため)

f:id:gsmcustomeffects:20190526054128p:plain
ピン設定ツール

CANとかCSIとかその辺いらないと思うので整理します。

f:id:gsmcustomeffects:20190526060145p:plain
ルーチンの整理
もとから入ってるBOARD_InitDEBUG_UARTPinsってのがあるのでそれはそのまま使うとして
LPUART1を使うのでそのピンが設定されていることを一応確認する。
(もし別のLPUART(2~8)を使いたい場合はそれのピン設定もしておくとよい)
f:id:gsmcustomeffects:20190526065817p:plain
BOARD_InitDEBUG_UARTPins

整理したら上のほうにあるupdate projectをクリックしてコードを吐き出します。

クロックの設定

次にクロックの確認をしていく
自分は工業グレードのMIMXRT1052CVL5Bを使っているのでメインクロックを528MHzにします。(コンシューマグレードの方は600MHzでいいと思う

f:id:gsmcustomeffects:20190527064643p:plain
メインクロックの設定

そしてLPUARTのクロックの設定を確認する。

f:id:gsmcustomeffects:20190528232809p:plain
LPUARTのクロック

ボーレート計算が以下の図のように計算されるのでボーレート誤差が大きいようなら元クロックを調整する必要があると思うがこの辺はブログ末尾のメモ読んでくれればいいと思う。

f:id:gsmcustomeffects:20190529073610p:plain
ボーレートの計算

※OSRはオーバーサンプリングレシオのことでBAUDレジスタで設定することが可能(SDKだと自動?
こちらも同様に終わったらupdate projectをクリックしてコードを出力しておく

ペリフェラルツールで設定する

最後にペリフェラルツールでLPUARTの設定をする。
右上のUSBマークアイコンをクリックして設定画面を開く。

f:id:gsmcustomeffects:20190530004528p:plain
ペリフェラルツール

LPUART画面のコンフィグタブでプリセットを選ぶとほぼ自動設定してくれる。
基本的な使い方をするならこれで十分なのでこういったアシストがあるのはうれしい

f:id:gsmcustomeffects:20190530005832p:plain
LPUART設定

こちらも同様に終わったらupdate projectをクリックしてコードを出力

コードを書いていく

最後にコードについてだが、初期化に関しては別ファイルで行われているので実質記述するのは数行でいい

/**
 * @file    uarttest2.c
 * @brief   Application entry point.
 */
#include <stdio.h>
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "MIMXRT1052.h"
#include "fsl_debug_console.h"
/* TODO: insert other include files here. */

/* TODO: insert other definitions and declarations here. */

/*
 * @brief   Application entry point.
 */
uint8_t txbuff[] = "uart sample\r\n";
int main(void) {

  	/* Init board hardware. */
    BOARD_InitBootPins();
    BOARD_InitBootClocks();
    BOARD_InitBootPeripherals();
    BOARD_InitDEBUG_UARTPins();
  	/* Init FSL debug console. */
    //BOARD_InitDebugConsole();

    //PRINTF("Hello World\n");
    LPUART_WriteBlocking(LPUART_1_PERIPHERAL, txbuff, sizeof(txbuff) - 1);
    /* Force the counter to be placed into memory. */
    volatile static int i = 0 ;
    /* Enter an infinite loop, just incrementing a counter. */
    while(1) {
        i++ ;
    }
    return 0 ;
}

説明的なのをすると

uint8_t txbuff[] = "uart sample\r\n";

で送信するバッファを定義している。

BOARD_InitDEBUG_UARTPins();
/* Init FSL debug console. */
//BOARD_InitDebugConsole();

BOARD_InitDEBUG_UARTPins()で先ほど作ったピン設定をする。
BOARD_InitDebugConsole()はprintfとUARTをつないでくれるやつで今回は使わないのでコメントアウトしている。

最後にポーリングAPIです。

LPUART_WriteBlocking(LPUART_1_PERIPHERAL, txbuff, sizeof(txbuff) - 1);

受信はLPUART_ReadBlockingというAPIでできます。

uint8_t rxbuff[10] = {0};
LPUART_ReadBlocking(LPUART_1_PERIPHERAL, rxbuff, sizeof(rxbuff));

こんな感じでいいと思います。

Memo

ボーレート設定について

ボーレート値の計算はペリフェラルツール上で行ってるわけではなくLPUART_Initの冒頭でOSRとSBRの最適化を行う設計になっているため実行するまで実ボーレートがわからない仕様になっている。

f:id:gsmcustomeffects:20190529081358p:plain
LPUART_Init冒頭のボーレートにかかわるレジスタ値の最適値探索

そして探索後誤差を評価して返す

f:id:gsmcustomeffects:20190529081642p:plain
誤差評価

これかならず動く設定が決まってればいいけど可変ボーレートにした場合落ちる可能性もあるよね・・・・・
というよりmainでこのAPI呼んだ際にエラーコード見てない感じなのでサンプルとしてはそんなに優秀ではないかなという印象

エラーコードはこのように定義されているのでもとからあるアサートを利用するなりifで区切って無限ループで止めたりバグになりにくい設計にするといいかもしれない

f:id:gsmcustomeffects:20190530014303p:plain
LPUARTのエラーコード

MIMXRT10xxのADCを使ってみる

今回はMIMXRT10xxシリーズのADCを使ってみようと思う。
ペリフェラルの特徴としてまとめると

  • 1MS/sec sample rate
  • 1モジュールに8つのシングルエンド入力
  • single conversion と continuous conversionに対応
  • 12bit,10bit,8bitに対応
  • 下限値、上限値、値の一致、範囲内、範囲外の割り込みに対応
  • Hardware average functionを内蔵

な感じである。
個人的にはValue trigger , Hardware average functionが使えそうだなと感じた。

次に内部をのぞいてみる。

f:id:gsmcustomeffects:20190308162044p:plain
概略

Kinetisだと16bitだったんですが12bitになったんですね。

割り当てピンとしては以下の表を参考にしてほしい。
一つのチャンネルに対して16本の入力ピンがある感じなので好きなとこ使えばいいと思う
ただし、データレジスタの数が8つまでなのでADC1で使える本数は8個までそれ以上使う場合はADC2を使う。

signal descriptor pad
ADC1_IN0 ch1 input 0 GPIO_AD_B1_11
ADC1_IN1 ch1 input 1 GPIO_AD_B0_12
ADC1_IN2 ch1 input 2 GPIO_AD_B0_13
ADC1_IN3 ch1 input 3 GPIO_AD_B0_14
ADC1_IN4 ch1 input 4 GPIO_AD_B0_15
ADC1_IN5 ch1 input 5 GPIO_AD_B1_00
ADC1_IN6 ch1 input 6 GPIO_AD_B1_01
ADC1_IN7 ch1 input 7 GPIO_AD_B1_02
ADC1_IN8 ch1 input 8 GPIO_AD_B1_03
ADC1_IN9 ch1 input 9 GPIO_AD_B1_04
ADC1_IN10 ch1 input 10 GPIO_AD_B1_05
ADC1_IN11 ch1 input 11 GPIO_AD_B1_06
ADC1_IN12 ch1 input 12 GPIO_AD_B1_07
ADC1_IN13 ch1 input 13 GPIO_AD_B1_08
ADC1_IN14 ch1 input 14 GPIO_AD_B1_09
ADC1_IN15 ch1 input 15 GPIO_AD_B1_10

ADC2に関しては割愛(ref manualのTable 63-2. ADC external signalsを参照)

次に仕組み的なものを雑に紹介する。


やってみる

環境としてはForlinx RT1052評価ボードを使う。
写真左の多回転トリマーがADCにつながっていますのでそのピンを使って進めていきます。(ピン的にはGPIO_AD_B0_14

自分はこの環境だが購入とかも直接問い合わせとかでめんどくさいので普通の人は公式の評価ボードであるMIMXRT1050-EVKなんかを使うのがいいと思う。

f:id:gsmcustomeffects:20190401224949p:plain
Forlinx OK1052-C 評価ボード


ハードウエアの用意が終わったところでさっそくやって行きたいわけだがSDKプロジェクトにはADCのサンプルがついている。

  • adc_12b1msps_sar_polling
  • adc_12b1msps_sar_interrupt
  • adc_etc_software_trigger_conv
  • adc_etc_hardware_trigger_conv


これらのサンプルプロジェクトはAPIの使い方について大いに参考になるが別ファイルで記述されているクロックやピンファンクションなどの初期設定で何をやればいいのかわかりにくい。
そのため今回は空プロジェクトからadc_12b1msps_sar_pollingと同様なことをやってみたいと思う。
内容としてはソフトウエアでADCを開始してADC完了後ポーリングAPIにてADCデータレジスタから値の取得を行うというものです。

やることの整理とスキームをまとめると以下のようになります。

  • プロジェクトの作成
  • クロックの設定
  • ピンの設定
  • Conversionの設定
  • Calibrationの設定
  • 割り込みやチャンネルコンフィグの設定
  • コードの出力

ざっとまとめるとこんな感じになるのでクロックの設定からやって行くことにします

プロジェクトの作成

まずは自分の環境にあったチップを選んで新規プロジェクトを作成する。

f:id:gsmcustomeffects:20190430013137p:plain
新規プロジェクト

メモリなどの設定は以下のようにした

f:id:gsmcustomeffects:20190430013226p:plain
メモリコンフィグ

クロックの設定

まずはクロックということでそれの画面に遷移する。

f:id:gsmcustomeffects:20190430013651p:plain
クロック設定にかかわる画面
IMXRT10xxのADCにはクロックソースが3つある(正確には2つ)

  • IPG clock
  • IPG clock divided by 2
  • ADACK(ad asynchronous clock)

ADACKはADCペリフェラル内蔵のクロックソースで、MCUがstopモード中でも変換が可能となっている。
リセット時はIPG clockが設定されている。
ADCモジュール上でクロックに関連は以下の図の点線で囲った部分であり、後段に共通のディバイダを備えていることがわかる。

f:id:gsmcustomeffects:20190421232935p:plain
ADC block diagram

次に実際のクロック設定だが結構楽でペリフェラルツールでプルダウンメニューから選ぶだけで良い。
ちなみにIPGを選ぶ場合はクロックが早すぎると問題があるので設定に注意したい。

f:id:gsmcustomeffects:20190415025544p:plain
ペリフェラルツールの設定例

自分の場合はこのようにした。

  • Clock source : Asynchronous Clock
  • Clock source frequency : 10MHz
  • Clock source divider : Do not divide
  • sample time : 6 clocks(short sample mode)

Start asynchronous clock outputに関してはADCx_GCのADACKENに解説がありどちらでも動くには動く。

  • OFF:変換がアクティブ時にON
  • ON:asynchronous clockが常にONになるため初回変換時に数クロック分早くなる。(クロック起動サイクル分がなくなるため)

クロックについてさらに詳しく知りたければリファレンスマニュアルのCCMの章を読むといいだろう。

ピンの設定

特にこうしろってのはないので自分の使いたいピンを設定してください

f:id:gsmcustomeffects:20190429234550p:plain
ピンコンフィグ

Conversion設定

次にConversionの設定です。

Conversion Timeの計算方法はリファレンスマニュアルにも書いてあるが
\begin{equation}
ConversionTime = SFCAdder + AverageNum * (BCT + LSTAdder)
\end{equation}

で計算できる。変換部分を詰めるなら知っとくべきだが補助ツールでだいたいの設定はできてしまうので今回の例では設定部分だけ書く

f:id:gsmcustomeffects:20190429211654p:plain
ペリフェラル設定(Conversion)

Calibration設定

最後にADCの精度確保のために初期キャリブレーションを行う必要がある。

これに関してはペリフェラルツールだとチェック入れるだけ

f:id:gsmcustomeffects:20190429233243p:plain
ペリフェラル設定(キャリブレーション

割り込みやチャンネルコンフィグの設定

f:id:gsmcustomeffects:20190430010958p:plain
割り込み+コンフィグ設定

コード出力および記述

これで一通りの設定が終わったのでコードを出力する。

f:id:gsmcustomeffects:20190430014322p:plain
コードの出力

初期化部分が出力できたらコードを書いていく。

書くとは言っても結構コンパクトにおさまる。ADC結果をprintfしていく感じ

f:id:gsmcustomeffects:20190430015452p:plain
コードの記述と解説

UART出力を確認するとこんな感じ

f:id:gsmcustomeffects:20190430015842p:plain
Teratermでの出力

割り込みについて

割り込みに関してはツールでチェック入れるだけでできて簡単なのでコードとして何が出力されるか示す。
EnableIRQ APIをコールしARMコア自体に割り込みの有無を教える

EnableIRQ(ADC1_IRQn);
EnableIRQ(ADC2_IRQn);//使うほうを有効にする。両方有効ならそれでもいい

チャンネル設定構造体メンバに割り込み有効

adcChannelConfigStruct.enableInterruptOnConversionCompleted = true;
ADC_SetChannelConfig(DEMO_ADC_BASE, DEMO_ADC_CHANNEL_GROUP, &adcChannelConfigStruct);

ハンドラの定義

void ADC1_IRQHandler(void)//ADC2を使う場合は2のハンドラを使う
{
    g_AdcConversionDoneFlag = true;
    /* Read conversion result to clear the conversion completed flag. */
    g_AdcConversionValue = ADC_GetChannelConversionValue(DEMO_ADC_BASE, DEMO_ADC_CHANNEL_GROUP);
    g_AdcInterruptCounter++;
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
  exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
    __DSB();
#endif
}

メモ

その他の機能について

サンプルプログラムとしてはSDK付属の物がいくつかあるのでそれを参考にするとハードウエアトリガーで動かすことも可能。
以下4つがSDKに含まれているので使う際は参考にするといい

  • adc_12b1msps_sar_polling
  • adc_12b1msps_sar_interrupt
  • adc_etc_software_trigger_conv
  • adc_etc_hardware_trigger_conv

2019年版 STM32環境構築方法

以前よりSTM32について結構な数の記事を書いてきました。
最近ではAtollicTrueStudioが無料になったり、CubeMXが5.0になったり
いろいろ変わった経緯もあり2019年版と題して新しく書いたほうが良いのでは?となったわけです。

そんなわけで僕なりの2019年度版環境構築を書いてみたという記事です。


目次的なやつ

AtollicTrueStudioのインストール

まずIDE統合開発環境)についてですがAtollicTrueStudioを使うのでそれのインストールをしていきます。
Macの人はSW4STM32しかないけど・・・Eclipseなので同じように進めればいいと思う。

atollic.com

上記のサイトに行き、ダウンロードしてもらってあとはインストールしてもらえればいいと思う

CubeMXのインストール

STM32にはマイコンを楽に使うためのライブラリ群がありSTはそれをCubeFxx(xxは数字F7ならCubeF7のような感じ)と呼んでいる。
んでそいつを内包しGUIペリフェラルを設定するためのツールとしてCubeMXというものを提供している。

www.st.com

ページの下のほうにダウンロードリンクがあるのでダウンロードする(登録が必要だったはず
んでインストール

STM32CubeProgのインストール

以前STM32Utilityとして提供していたものとその周辺のソフトが一体になった書き込みソフトとしてSTM32CubeProgが新しく提供された
デバッガとつながらくなった時に何かと便利なのでこれも入れておくのがいいでしょう

www.st.com


評価ボードを買う

次に実際に動かすハードウエアの話
STM32では格安評価ボードとしてNucleoというプラットフォームを提供している。

f:id:gsmcustomeffects:20190311010721p:plainf:id:gsmcustomeffects:20190311010644p:plainf:id:gsmcustomeffects:20190311010616p:plain
STM32 評価ボード

もちろんNucleo以外にもEVALボードやDiscoveryボードというものを用意しているがどちらかというともとからできる人向けのプラットフォーム感がある。

そんなわけで何でもいいんで好きなNucleoボードを買ってください

僕は秋月でも買えるしブレッドボードでも使えるSTM32F303K8をチョイスしてこれで説明していきます。
STM32 Nucleo Board STM32F303K8: マイコン関連 秋月電子通商-電子部品・ネット通販

CubeMXを起動する。

ここからが実際の作業です

起動するとこの画面が出てきます
f:id:gsmcustomeffects:20190311020003p:plain

Access to board selectorをクリックしてくださいそうするとこの画面が開きます。

f:id:gsmcustomeffects:20190311020131p:plain
ボードセレクト画面

次に303K8を探して選ぶとこの画面になると思います。

f:id:gsmcustomeffects:20190311020558p:plain
ボードインフォメーション

そうしましたら赤枠のStartprojectをクリックして進みます。

そうするとこういう画面が出てきます

f:id:gsmcustomeffects:20190311021026p:plain
基本画面

今回はLEDをちかちかしてみたいと思うのでピンの設定をしていきます。
PB3をアウトプットに設定、デバッグピンも設定

f:id:gsmcustomeffects:20190311024446p:plain
ピンの設定

ここでPB3に設定しているのはボード上のLEDを光らせる為です

f:id:gsmcustomeffects:20190312220730p:plain
Nucleo32の回路図(UM1956)

次にクロックの設定です。
まあ初期設定でも動くには動くんですがせっかくやるのでいじり方だけでも覚えておきましょう。
ここではメインクロックを64MHzにしています。

f:id:gsmcustomeffects:20190311021627p:plainf:id:gsmcustomeffects:20190311021923p:plain
クロックの設定(クリックで拡大できます)

次にプロジェクトの設定をします

f:id:gsmcustomeffects:20190311022511p:plainf:id:gsmcustomeffects:20190311022445p:plain
プロジェクトの設定

次にGenerateCodeをクリックしてコードを出力してください
上手くできていればこんな感じにファイルができているはずです
f:id:gsmcustomeffects:20190311023102p:plain

Atollic TrueStudioを起動する。

そうしましたら次はAtollicTrueStudioを起動します。

そして上部メニューからFile/インポートを選択し先ほど作ったプロジェクトをインポートする。

まずはプロジェクトをビルドする

f:id:gsmcustomeffects:20190311023705p:plain
プロジェクトのビルド

ビルドに成功しましたらコード追加します

f:id:gsmcustomeffects:20190311024815p:plain
コードの記述

次にデバッグボタンをクリックします。

f:id:gsmcustomeffects:20190311024922p:plain
デバッグボタンのクリック

そうすると画面が切り替わります。
画像に従ってブレークポイントを設定してあげてプログラムを進めるボタンを押してあげれば500msごとにプログラムが止まりボード上のLD3が光ったり消えたりすると思う
f:id:gsmcustomeffects:20190311025250p:plain

以上で入門は終了!

所感

2019年版の入門を書いたけど日に日にまじで楽になってるね。

軽いQ&A
Q:自分でチップ買ってきてそれに対して書き込むにはどうしたらよいの?
A:別途デバッガが必要です。

語弊があるので補足しておくとNucleo64やNucleo144はSTLINKV2-1ってのがついてるんで外部チップに向けてもデバッグしたり書き込んだりできる。

Q:どんなデバッガがあるの?おすすめは?
A:STLINKV2かV3

2018年まではSTLINKV2が主流だったけど2019年になってV3が出たので今はこれがお勧めです。

f:id:gsmcustomeffects:20190311030017p:plain
STLINKV3

ARMマイコンなので別のメーカー製のでも書き込みやデバッグはできますがSTM32CubeProgが使えるの考えると純正がいいです。

Q:STM32CubeProgは何が出来んの?
A:デバッガのファームウエアをアップデートしたり、書き込みしたりその他オプションをいじったりできます

繋いだ時こんな感じです
f:id:gsmcustomeffects:20190311030334p:plain

Q:他参考になる情報とかは?
A:僕のまとめサイトペリフェラルの説明はしてます

結構前にかいた記事もあるので適宜読み替えてくれればいいと思います。
開発環境の使い方は変わったけどAPIレベルではそんなに変わってないので以下のサイトは役立つはず

gsmcustomeffects.github.io

MIMXRT10xx series dcd table Tips part2

前回は解釈の仕方を書いたので今回はクロックに絞って全容をつらつら書く感じ。
ぶっちゃけ前回の記事で必要なことは書いたのでこれは読みたい人が読んでくれればいい。そんなmini tips的なやつ

本記事の環境は以下の通りです。

  • MIMXRT1050-EVK
  • SDK ver 2.5

クロックの設定

DCD tableを先頭から見ていくとdcd tableの0x09 ~ 0x57 bytes でクロック設定をしている。

dcd byte reg address reg name value
0x09~0x0F 0x400FC068 CCM_CCGR0 0xFFFFFFFF
0x10~0x17 0x400FC06C CCM_CCGR1 0xFFFFFFFF
0x18~0x1F 0x400FC070 CCM_CCGR2 0xFFFFFFFF
0x20~0x27 0x400FC074 CCM_CCGR3 0xFFFFFFFF
0x28~0x2F 0x400FC078 CCM_CCGR4 0xFFFFFFFF
0x30~0x37 0x400FC07C CCM_CCGR5 0xFFFFFFFF
0x38~0x3F 0x400FC080 CCM_CCGR6 0xFFFFFFFF
0x40~0x47 0x400D8030 CCM_ANALOG_PLL_SYS 0x00002001
0x48~0x4F 0x400D8100 CCM_ANALOG_PFD_528 0x001D0000
0x50~0x57 0x400FC014 CCM_CBCDR 0x00010D40

DCD tableにおけるクロックの設定を書きだすとこんな感じ

そしてこのTipで操作するクロックを示すとこんな感じ

f:id:gsmcustomeffects:20190301020411p:plain
本記事で操作する部分を示した図

CCM_CCGR(CCM Clock Gating Register)

CCM_CCGR0~CCM_CCGR6はクロックゲートレジスタのことで各モジュールのクロックを個別にON/OFFできる。
モジュールごとに2bit設けられてて設定値による効果は以下の画像の通り

f:id:gsmcustomeffects:20190228004604p:plain
Clock Activity Description

MIMXRT10xxの場合ペリフェラルが大量にあるので少し大変だがDCDでは全部ONにしているみたい。
except STOP modeとあるのでSTOPモード以外でクロックはONである。

CCM_ANALOG_PLL_SYS(Analog System PLL Control Register)

次にCCM_ANALOG_PLL_SYSの部分を見ていく

f:id:gsmcustomeffects:20190228020449p:plain
CCM_ANALOG_PLL_SYS
f:id:gsmcustomeffects:20190228020624p:plain
CCM_ANALOG_PLL_SYSn field descriptions

DCDtableにおけるセット値は0x00002001である。

ビットごとに分解してみていくと

BYPASSビットが0なのでPLLのバイパスはしないすなわちPLLを使用する設定。
BYPASS_CLK_SRCが00なので24MHzオシレータを使用する。
ENABLEが1なのでPLL出力が有効
DIV_SELECTが1なのでFout=Fref*22となる。

ここでCCM_ANALOG_PLL_SYS_NUMおよびDENOMというレジスタがあるがノーケアのためPOR(Power On Reset)初期値が使われFref * (22+(0/18))となり結局528MHzとなる。

この項で操作してるモジュールを示すとこんな感じ

f:id:gsmcustomeffects:20190301025840p:plain
CCM_ANALOG_PLL_SYS関連モジュール

CCM_ANALOG_PFD_528(528MHz Clock (PLL2) Phase Fractional Divider Control Register)

次にCCM_ANALOG_PFD_528を見ていく
PFDクロック生成のための設定をするレジスタで、fractional dividerの設定などができる。

bitごとの概要を以下の画像に示す。

f:id:gsmcustomeffects:20190301011808p:plain
CCM_ANALOG_PFD_528n register definition

PFDx_CLKGATEは1でOFF,0でON
PFDx_STABLEはread only bitで診断専用ビット。fractional dividerが有効になると値が反転する。
PFDx_FRACはfractional dividerの値

528*18/PFDx_FRAC where PFDx_FRAC is in the range 12-35.とあるので12~35の値を入れればいい

DCDtableにおけるセット値は0x001D0000であるためPFD2の部分をセットしてることになる。

field name[bit] set value description
PFD2_CLKGATE[23] 0b0 PFD2の有効
PFD2_STABLE[22] 0b0 read onlyのため関係ない
PFD2_FRAC[21-16] 0b011101 PFDx_FRAC =29

これで 528*18/29 = 327.72[MHz]となる。

CCM_CBCDR(CCM Bus Clock Divider Register)

最後にCCM Bus Clock Divider Registerについてである。
SEMCモジュールに一番近い部分のブロックでありクロックセレクトおよび分周器の設定が可能

f:id:gsmcustomeffects:20190301212223p:plain
CCM_CBCDR field descriptions


見たところ7種類ぐらい設定できるとこがある。
操作可能な部分を図で示すと以下の画像のようになる。

赤で示した部分がコア関連のクロック設定。
青で示す部分がSEMC関連。

f:id:gsmcustomeffects:20190301212508p:plain
CCM_CBCDRで操作可能なモジュール

DCD tableにおける設定値は0x00010D40となっている。

これもビットごとに分解して見ていこう

field name[bit] set value description
Reserved[31-30] 0b00 none
PERIPH_CLK2_PODF[29-27] 0b000 000なのでdivide by 1
Reserved[26] 0b0 none
PERIPH_CLK_SEL[25] 0b0 0なのでpre_periph_clk_selをセレクト
Reserved[24-19] 0b000000 none
SEMC_PODF[18-16] 0b001 001なのでdivide by 2
AHB_PODF[12-10] 0b011 011なのでdivide by 4
IPG_PODF[9-8] 0b01 01なのでdivide by 2
SEMC_ALT_CLK_SEL[7] 0b0 0なのでPLL2 PFD2がSEMC root clockとして使われる
SEMC_CLK_SEL[6] 0b1 1なのでSEMC alternative clock がSEMC clock root
Reserved[5-0] 0b000000 none

ここで上のほうは無視するとしてSEMCを見ていきます。
SEMC_ALT_CLK_SELでPLL2 PFD2、SEMC_CLK_SELでSEMC alternative clockとあるのでこのように選ばれます。

f:id:gsmcustomeffects:20190301230521p:plain
セレクターの動き

SEMC_PODFがdivide by 2なので327.72/2 = 163.86MHzとなる。
SEMCのスペックは166MHzなのでほぼ最大値でセットされるということがわかる。

f:id:gsmcustomeffects:20190301230336p:plain
SEMCのスペック

これでクロックの設定は終了。
冒頭にあるようにそのまま全容をつらつら書きましたがいかがでしたか?

正直起動関連部分なのでこういうのは公式が書いてほしいものですが
本記事で少しでも力になれれば幸いという感じです。

もちろんDCDは1000byte近くあるのでこの後にピン設定などが続きますので今後はそれを書いていこうと思います。

memo

クロックのPower modeには

  • RUN mode
  • WAIT mode
  • STOP mode

がある

詳細についてはリファレンスマニュアルのChapter 13 Clock Controller Module (CCM)を参照

ref

MIMXRT1050のリファレンスマニュアルの

  • Chapter 13 Clock Controller Module (CCM)
  • Chapter 23 External Memory Controllers
  • Chapter 24 Smart External Memory Controller