2022年05月07日

PIC32 C++プロジェクト

今回はPIC32マイコンでC++ (CPP) プロジェクトを作成する方法について紹介します。といっても記事にする程でないほど簡単でした。

STM32マイコンのSW4STM32やCubeIDEではC++プロジェクトへの変換作業等が必要でしたが、MPLAB X(v5.45) + XC32(v2.50) +Harmony3では特に変換作業等必要なく、cppファイルと.hppファイルを追加するだけでした。また、ライブラリやプロジェクト、コンパイラ等はC++に対応済のため、追加設定は不要です。そのため、非常に簡単に実装できます。

追加するcppファイルと.hppファイルとして、ラッパーとなるwrapper.cpp、wrapper.hpp、LED点滅用の関数LedBlink.cpp、LedBlink.hppをプロジェクトに追加します。また、app.cからラッパー関数を呼び出すためのcpploop();を追加しました。各ファイルの詳細についてはこちらにアップロードしたため、割愛します。

通常のプロジェクト同様にLedBlink.cpp内でブレイクポイントを置いてデバッグもできました。

PIC32CPP.JPG

思った以上にすんなりC++ (CPP) プロジェクトを動かすことができたため、今後はパワフルなPIC32マイコンを活かして、機械学習系のライブラリ等にも応用してみたいと思います。
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2022年03月12日

PIC32 ウォッチドッグタイマ

 今回はPIC32マイコンのWDTの設定方法を紹介します。WDT(ウォッチドッグタイマ)は名の通り、MPUの動作を見張る番犬です。WDTを有効化するとMPUに異常な処理が発生した場合や意図しない処理の無限ループに入ってしまった場合に異常としてMPUをリセットさせることができます。PIC32マイコンにはWDTの他にDMT(デッドマンタイマ)も搭載されています。目的と用途が異なるため、ここでは詳細は省略しますが、WDTはスリープ等からの復帰にも使用でき、汎用的でざっくりとした時間間隔での監視が可能です。一方、DMTはスリープ復帰には使用できませんが、設定した命令数(処理)に対して細かく監視間隔を設定できます。DMTは少し特殊な用途となるため、今回はWDTのみ紹介します。

 WDTの動作原理として、監視の時間間隔を予め設定し、監視を開始します(WDT_Enable)。そのあと、コード上で定期的にWDTカウントリフレッシュ関数WDT_Clearを呼び出します。正常な場合は予め設定した監視時間よりも前にWDT_Clear関数で正常に動いていることをWDTに知らせます。何かしらの異常が発生した場合は予め設定した監視時間になってもWDT_Clear関数による知らせがWDTにないため、監視時間を超えた時点でWDTが強制的にMPUのリセットを実行する仕組みです。正常動作にも関わらず、監視時間に達してリセットされてしまうということがないように、ある程度余裕のある監視時間を設定すべきです。ただ、余裕にしすぎると万一、異常な状態になった場合にリセットがかかるまでに時間を要することになるため、バランスを見て設定する必要があります。例えば、長い処理ではリフレッシュ関数を所々に予め入れておくといった対応が想定されます。

Harmoney3のProject GraphのSystem項目から設定することができます。


wdt0.jpg

WDTの設定については特に分かりずらく、WDTのパラメータ設定はDEVCFG1から設定します。WDTPSのパラメータを変更することでリセットまでの監視時間を変更することが可能です。

wdt1.jpg

 一方、WDTの有効、無効はDEVCFG1とは別のWDTの項目にチェックを入れることで自動的にWDT関連の関数ライブラリコードが生成されるようになります。WDTPSのパラメータの設定を秒数としてWDT項目から確認することができます。

wdt2.jpg

 パラメータ設定DEVCFG1と有効可否設定WDTが分かれているため、初めてだと戸惑いました。WDTの有効、無効は他のペリフェラル機能と同様にAvailable Componentsに項目を入れた方が直感的で分かりやすいと思いました。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2022年02月26日

PIC32MZEFシリーズのブートローダ機能

 PIC32MZ2048EFGマイコンのブートローダ機能を検討したため、今回はその際に戸惑った点等を備忘録として紹介します。ブートローダ機能を利用するとPICkit4やSNAP等の専用の書き込み治具がなくても、SDカードやUART等からファームウェアを容易に書き換えできるようになります。何かしらの製品に組み込んだ際にユーザ側でファームアップデートすることが想定される場合に便利です。今回は事前にブートローダを書き込んだPIC32MZマイコンにファームウェアの入ったSDカードを挿入すると自動的にファームウェアが書き込まれるブートローダを作成しました。Microchipから様々なブートローダのドキュメントが提供されていますが、いまいち全容を把握するのに苦労したため、ポイントを絞って紹介したいと思います。詳細は各ドキュメントを参照してください。


まずはブートローダ機能を開発する際のポイントを先に紹介します。

■ポイント
@ブートローダ用のプロジェクトとアプリケーションのプロジェクトは分けて作成する
 →リンカファイルが異なるため、ミスを減らすためにプロジェクトを分ける。また、アプリケーション用のプロジェクトでは通常のバイナリ(hex)ファイルの他にbinファイルを生成するスクリプトを追加することでブートローダ読み込み用のbinファイルを追加で生成させる。

Aリンカファイル作成方法
 →Harmony3+bootloaderで生成したプロジェクトでブートローダ用のリンカファイルbtl.ldはそのまま使用可能。一方、アプリケーション側のリンカファイルはp32MZ2048EFG100.ldを編集必須。また、Harmony3+bootloaderで生成したプロジェクトのリンカ設定はbtl.ldファイルでなく、p32MZ2048EFG100.ldファイルとなっているため、p32MZ2048EFG100.ldをプロジェクトから除外してブートローダ用のリンカファイルbtl.ldに置き換えること。

Bクロック設定やデバイスコンフィグは同じ設定にする
 →ブートローダとアプリケーションの切り替え動作が不安定になる。

Cブートローダ用のプロジェクトは最低限の機能に絞る
 →機能を盛り込むとブートローダファームがブートローダ領域に入らない。SDを読み込み専用に設定し、コンソールデバッグ機能は使用しない。

Dブートローダのファームは直接書き込み可能
 →MPLAB IDEからデバッガを介して書き込み可能。既定ではフラッシュ全削除後に書き込みされるため、通常は書き込み直後にブートローダが起動し、アプリケーションの書き込みモードに入る。

Eアプリケーションのファームは直接書き込まずにブートローダから書き込み
 →直接書き込むとフラッシュ全削除してアプリケーションだけが書き込まれる。ブートローダが消されてしまうため、アプリケーション領域に処理をジャンプさせることができず、アプリケーションが起動しない。

■ブートローダ機能のシーケンス
ブートローダ起動→トリガチェック→(書き換えモードの時)ファーム書き換え処理→ソフトリセット
                →(通常起動の時)アプリケーション起動処理→アプリケーション起動

■トリガ種類
ブートローダが起動後にファーム書き換えモードに入るか、通常のアプリケーション起動モードに入るかはIO状態やメモリ上の変数状態で切り替えることができます。

・a)IO状態トリガ
 PIC32マイコンをリセットさせて、起動時のIO状態を見て切り替えます
・b)メモリ上の変数状態トリガ
 アプリケーションが起動している状態でユーザ操作によってファーム書き換えモードにします。ファーム書き換えモードでは特定のメモリ領域に変数を書き込んで部分的なシステムリセット(特定のメモリ領域はリセットされない)をかけることでブートローダを再度起動させて、特定のメモリ領域の変数を見て書き換えモードに入るか、通常のアプリケーション起動モードに入るか切り替えます。




■ブートローダ側の開発流れ
1. MPLAB ] IDEのHarmony3にBootloaderパッケージを追加する。
 Tools→Embedded→MPLAB Harmoney3 Content Managerをクリック。bootloaderとbootloader_apps_sd〜にチェックを入れてパッケージを追加します。

bootloader1.jpg

bootloader2.jpg


2. Harmoney3プロジェクトを作成し、Filesytemタイプのbootloaderを追加する。
 メディアタイプにSDカードを選択し、b)メモリ上の変数状態トリガを有効にするために16バイトの領域を確保する。また、ファイルシステムの設定は自動マウントを有効化して、読み込み専用に設定する。また、32GB以上のSDカードにも対応させる場合はexFatにチェックを入れる。また、クロック設定やデバイスコンフィグはアプリケーション側と共通の設定をします。

bootloader3.jpg

bootloader4.jpg

bootloader5.jpg


3. コード生成後にリンカファイルを設定する。
 Harmoney3プロジェクトでbootloaderを追加すると自動的にbtl.ldとp32MZ2048EFG100.ldの2つのリンカファイルがプロジェクトに追加される。ただ、ビルド設定ではp32MZ2048EFG100.ldが有効となるため、p32MZ2048EFG100.ldをプロジェクトから除外して、btl.ldだけにする。

bootloader7.jpg


4. ファイルシステムコード追記する。
 デフォルトではff.hのビルドに失敗するため、ff.hの38行目くらいに#define __STDC_VERSION__ 199902を定義する。

5. ブートローダコードを実装する。
 詳細はコードをアップロードしてありますのでそちらを参照してください。起動直後にブートローダ機能を実行するか、アプリケーションを起動させるかの切り替えがinitialization.cにあります。

■アプリケーション側の開発流れ
1. 通常通りにMPLAB ] IDEのHarmony3のプロジェクトを作成します。また、クロック設定やデバイスコンフィグはブートローダ側と共通の設定をします。また、リンカファイルを変更して独自のリンカファイルを使用するため、Harmony3のプロジェクトグラフ内のsystem→Device&Project Confguration→Toolchain selectionのAdd linker file to projectのチェックを外します。

bootloader9.jpg

2. リンカファイルp32MZ2048EFG100.ldを書き換えます。

・PROVIDE(_ebase_address = 0x9D000000);の下にPROVIDE(_ebase_vector_offsets = 0x1000);を追加。
・_RESET_ADDR = 0xBFC00000;を_RESET_ADDR = 0xBD000000;に変更。
・_BEV_EXCPT_ADDR = 0xBFC00380;と_DBG_EXCPT_ADDR = 0xBFC00480;を削除。
・_SIMPLE_TLB_REFILL_EXCPT_ADDR、_CACHE_ERR_EXCPT_ADDR 、_GEN_EXCPT_ADDR に_ebase_address を追加して下記のように書き換える。
_SIMPLE_TLB_REFILL_EXCPT_ADDR = _ebase_address + _ebase_vector_offsets + 0;
_CACHE_ERR_EXCPT_ADDR = _ebase_address + _ebase_vector_offsets + 0x100;
_GEN_EXCPT_ADDR = _ebase_address + _ebase_vector_offsets + 0x180;

・メモリ領域(MEMORY)の書き換えとconfig領域の削除
(変更)
kseg0_program_mem (rx) : ORIGIN = 0x9D001000, LENGTH = 0x200000 - 0x1000
kseg1_boot_mem : ORIGIN = 0xBD000000, LENGTH = 0x480
kseg1_boot_mem_4B0 : ORIGIN = 0xBD0004B0, LENGTH = 0x1000 - 0x04B0
(追記)
/* Reserve 16 Bytes to Store Bootloader Trigger Pattern */
kseg0_data_mem (w!x) : ORIGIN = 0x80000000 + 16, LENGTH = 0x80000 - 16
(削除)
アプリケーション側ではコンフィグを行わないため、config領域を削除します。
config_BFC0FF40からboot2lastpageまでを削除します。

・SECTION領域(SECTIONS)のconfig削除
.config_BFC0FF40から始まるSECTIONSを削除する。

・SECTION領域(SECTIONS)の例外処理を削除
ブートローダ側に含まれるため、下記の例外処理を削除する。
.bev_excpt _BEV_EXCPT_ADDR :
{
KEEP(*(.bev_handler))
} > kseg1_boot_mem

・割込みオフセットに_ebase_vector_offsetsを追記

* Interrupt vector table with vector offsets */
.vectors _ebase_address + _ebase_vector_offsets + 0x200 :

・ブートローダ側に含まれるため、下記を削除する
/* The startup code is in the .reset.startup section.
* Keep this here for backwards compatibility with older
* C32 v1.xx releases.
*/
.startup ORIGIN(kseg0_boot_mem) :
{
KEEP(*(.startup))
} > kseg0_boot_mem

・ デバッグ例外処理とコンフィグはブートローダ側に含まれるため、除く処理を追加する。
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.discard) }の下に下記を追加する。
/DISCARD/ : { *(._debug_exception) }
/DISCARD/ : { *(.config_*) }

最終的に書き換え前(書換前リンカファイル.ld)と書き換え後(書換後リンカファイル.ld)のファイルはこのようになります。

3. アプリケーション側で必要に応じて、ソフトウェア的にファーム書き換えモードにするコードを追加します。BTL_TRIGGER_PATTERN で定義された特殊な値をBTL_TRIGGER_RAM_START に書き込んでソフトリセットさせることでリセット後にブートローダが起動し、SDカードからファームを書き換える処理を走らせることができます。

#include "sys/kmem.h"

#define BTL_TRIGGER_RAM_START KVA0_TO_KVA1(0x80000000)
#define BTL_TRIGGER_PATTERN (0x5048434DUL)
#define DCACHE_CLEAN_BY_ADDR(start, sz)
static uint32_t *ramStart = (uint32_t *)BTL_TRIGGER_RAM_START;

SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Bootloader Triggered!\r\n");
ramStart[0] = BTL_TRIGGER_PATTERN;
ramStart[1] = BTL_TRIGGER_PATTERN;
ramStart[2] = BTL_TRIGGER_PATTERN;
ramStart[3] = BTL_TRIGGER_PATTERN;
DCACHE_CLEAN_BY_ADDR(ramStart, 16);
APP_SystemReset();

4. プロジェクト設定でbinファイルを生成するコードを追加します。
アプリケーション側のビルド後にhexファイルからbinファイルを生成します。
${MP_CC_DIR}/xc32-objcopy -I ihex -O binary ${DISTDIR}/${PROJECTNAME}.${IMAGE_TYPE}.hex ${DISTDIR}/${PROJECTNAME}.${IMAGE_TYPE}.bin

bootloader8.jpg


これでブートローダ機能の設定および実装は完了です。先にブートローダ側のファームをPIC32マイコンに書き込んで、アプリケーション側のbinファイルをimage.binにファイル名を書き換えてSDカードに入れるとファームが書き換えられ、アプリケーションが実行されます。プロジェクトファイルおよび全体のコードはこちら(code.zip)です。

なお、パッケージを追加するとC:\Users\(ユーザ名)\Harmony3\bootloader_apps_sdcard\apps\sdcard_bootloader内にブートローダ側のbootloaderとアプリケーション側のtest_appがサンプルコードが追加されます。ただ、サンプルプログラム(PIC32MZDA)とREADMEだけでは情報少なく苦労したため、試行錯誤しながら何とか動かすことができました。SDカード以外にもUARTやフラッシュメモリ等からも書き換えできますが、専用ソフトが不要でユーザ側のOS環境に依らず負担が少ないSDカードからの書き換えが汎用性が高く良いと思いました。また、アプリケーション側のリンカファイルを書き換えるとそのままでは簡単にデバッグやブートローダ無の単体動作できないため、元のリンカファイルも残しておいて、読み込むリンカファイルを切り替えるとよいと思います(Lodableに追加する別のデバッグ方法もありますが...)。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年11月06日

PIC32マイコンSYS_CONSOLE

今回はPIC32MX270F256BをターゲットにMPLAB X v5.45+Harmony3+XC32 v2.50を使用して、SYS_CONSOLEの使用方法について紹介します。

マイコンのデバッグをする場合、デバッガーを使って確認する方法の他にUART等を介してシリアルコンソールに情報を表示させる方法があります。個人的にはデバッガーよりもUARTとシリアルコンソールを使ったデバッグの方が早く、必要な情報を取り出せるため、よく利用しています。PIC32のHarmony3で利用可能なSYS_CONSOLEはシリアル出力のラッパー機能となっています。UARTの関数を直接呼び出してシリアル出力するよりも汎用性高く、利用しやすくなっています。システムに応じてコンソールの出力先のUARTを変更したり、USBCDCの出力に変更することも容易です。また、コンソールのデバッグレベルを使い分けることで、必要なコンソール出力のみに制限したりすることも容易にできます。


Harmony3ではSystem ServiceのConsole、Debugとしてライブラリが準備されています。今回はConsoleをUART2に接続して利用しました。Harmony3のプロジェクトグラフは下記の通りです。

sys_console1.jpg

必要に応じてUART2のボーレートやピンアサインの設定を行います。

出力のレベルはSYS_ERROR_FATAL、SYS_ERROR_ERROR、SYS_ERROR_WARNING、SYS_ERROR_INFO、SYS_ERROR_DEBUG から選択できます。デフォルトはSYS_ERROR_DEBUGとなっており、プロジェクトグラフのDebugから出力レベルを変更できます。コードではconfiguration.h内で下記のように定義が出力されています。

#define SYS_DEBUG_ENABLE
#define SYS_DEBUG_GLOBAL_ERROR_LEVEL SYS_ERROR_DEBUG

例えば、デバッグ時はSYS_ERROR_DEBUGにして、デバッグ終了後はSYS_ERROR_INFOにすると、SYS_ERROR_INFO以下のコンソールが出力されるようになり、SYS_ERROR_DEBUGのコンソール出力は出力されなくなります。コンソール出力をコメントアウトしたりせずに一括で変更できるため、非常に便利です。


コンソール出力の使い方は下記の通りです。
ヘッダのインクルードを追加します。
#include "system/debug/sys_debug.h"

出力関数は下記の通りです。
SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "\r\nInitOK\r\n");


コンソール入力を利用する場合は下記の変数をグローバル変数等に定義します。
SYS_CONSOLE_HANDLE consoleHandle;
ssize_t consoleReadSize;
char consoleBuffer[130];

入力関数は下記の通りです。入力がない場合はconsoleReadSizeが0になります。入力があった場合はそのbyte数がconsoleReadSizeに入り、データはconsoleBufferに格納されます。

consoleReadSize = SYS_CONSOLE_Read( consoleHandle, consoleBuffer, sizeof(consoleBuffer) );

UARTのコンソールはPIC32MXではデフォルトでDMA&リングバッファとして設定されているため、マイコンのメイン処理に対してコンソール出力の影響を抑えてデバッグすることが可能です。

これまで毎週更新していましたが、半導体不足でプロジェクトが停滞気味のため、今後のブログ更新はペースを減らして随時記事を投稿する予定です。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年10月23日

PIC32マイコンSDカード読み書き

今回はPIC32MX270F256BをターゲットにMPLAB X v5.45+Harmony3+XC32 v2.50を使用して、PIC32マイコンにSDカードを接続して読み書きする方法について紹介します。

Harmony3を用いることでSPI接続で簡単にSDカードの読み書きを実装することが可能です。Harmony3のプロジェクトグラフは下記の通りです。

sd_spi1.jpg

今回はSPI1をSDカード用に割り当てました。

sd_spi4.jpg

SPI1の設定はデフォルトのままで問題ありません。

sd_spi3.jpg

SD Card設定ではSDカードのチップセレクトをどのポートにするか設定します。他の設定はそのままで問題ありません。



sd_spi2.jpg
FILE SYSTEMの設定はそのままで問題ありませんが、32GB以上のSDカードを使用する場合は「Enable exFAT ~」にチェックを入れます。

sd_spi5.jpg

最後にSPI1のIOとSDのCSのポートを忘れずに設定します。SPI1のIOとCSをGPIO OUTPUTに設定します。なお、SDI1とSDO1をプルダウン有効にしていますが、必須ではありません。

Harmony3の設定完了後、Generate Codeボタンでコードを生成します。コード生成後、app.cのAPP_Tasks内にファイル書き込みのテストコードを追加します。


SYS_FS_HANDLE fileHandle;
char hello[] = "Hello World !!";
int res;
//SDカードマウント
res=SYS_FS_Mount("/dev/mmcblka1", "/mnt/myDrive1", FAT, 0, NULL);
if(res!= 0)
{
  SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Mount ERR\r\n",res);
  return;
}
else
{
 SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Mount OK\r\n");
}


//ファイルオープン
fileHandle = SYS_FS_FileOpen("TestData.txt", (SYS_FS_FILE_OPEN_WRITE));
if(fileHandle == SYS_FS_HANDLE_INVALID)
{
  SYS_DEBUG_PRINT(SYS_ERROR_WARNING, "FileOpen Error\r\n");
  return;
}
//ファイル書き込み
res= SYS_FS_FileWrite(fileHandle, hello, sizeof(hello));
if(res!= -1) //OK
{
  SYS_DEBUG_PRINT(SYS_ERROR_WARNING, "File Write OK\r\n",);
}
else//NG
{
  SYS_DEBUG_PRINT(SYS_ERROR_WARNING, "FileWrite NG\r\n");
  return;
}
//ファイルクローズ
SYS_FS_FileClose(fileHandle);


マウントからファイル操作、ファイルクローズまでの一連の流れは上記の通りです。

ディレクトリを生成する場合は
SYS_FS_DirectoryMake(dirname);
でdirnameに文字列を渡すことでフォルダの生成ができます。

生成したフォルダに移動する場合は
SYS_FS_DirectoryChange(dirname);
でdirnameに文字列を渡すことでそのフォルダに移動します。

逆にルートフォルダに戻る場合は
SYS_FS_DirectoryChange("/");
でルートフォルダに戻ることが可能です。


FILE SYSTEMの設定で「Enable exFAT ~」にチェックを入れることで32GB以上のSDカードに対応できますが、そのままではビルドに失敗します。ff.hで「#error exFAT feature wants C99 or later」のエラーが発生します。XC32コンパイラはexFATで使用するint64等の変数定義に対応していますが、XC32内でstdバージョン定義がされていないようです。

ff.hの37行目に
#define __STDC_VERSION__ 199902L
を追記して強制的に定義を変更します。


ファイルシステムの時刻がデフォルトは固定となっているため、RTC機能等を実装している場合はapp.cやmain.c等のユーザー側で下記の関数を追加することでファイル生成時やフォルダ生成時に日時が反映されます。なお、GetRtcDateTimeは自作の関数で必要に応じて外付けのRTC等から日時を取得する関数を実装してください。

DWORD get_fattime(void)
{
  SYS_FS_TIME time;
  time.packedTime = 0;
  uint8_t year, month, day, hour, min, sec;

  GetRtcDateTime( &year, &month, &day, &hour, &min, &sec);
  // All FAT FS times are calculated based on 0 = 1980
  time.discreteTime.year = (2000+ year - 1980);
  time.discreteTime.month = month;
  time.discreteTime.day = day;
  time.discreteTime.hour = hour;
  time.discreteTime.minute = min;
  time.discreteTime.second = sec;
  return (time.packedTime);
}


設定によってはすんなりビルドが通らない等ありましたが、比較的簡単にPIC32マイコンにSDカードを接続して読み書きすることができました。その他の注意点としてSDカードの読み書きとconsoleを有効にしただけでプログラムメモリ120kB程度(最適化レベル1)が既に使用済となりました。SDカードの読み書きをする場合は256kB以上のプログラムメモリが大きなPIC32マイコンを選択する必要があります。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする