2021年05月08日

PIC32MX+Harmony3+ SPI通信

今回はPIC32MX250F128BをターゲットにMPLAB X v5.45+Harmony3を使用してSPI通信を行いました。その際にポイントとなる設定について紹介します。SPI接続のSRAM(IS62WVS5128FB)の書き込み、読み込みを行いました。なお、ターゲットへの書き込みはPickit3を使用しまし、MPLAB Xには予め、MPLAB Harmony 3 Launcher(旧名称 MPLAB Harmony Configurator 3)をインストールしています。

@プロジェクトファイル作成
MPLAB XのFile→New ProjectからMPLAB Harmony 3のプロジェクトを作成します。

hspi1.jpg

AHarmony 3設定
Tools→Embedded→Harmony Configurator 3を選択します。左下のAvailable ComponentsからSPI1をクリックし、SPI1を追加します。SPI1が追加されると左上(Active Components)及び中央(Project Graph)にSPI1が表示されます。

中央(Project Graph)のSPI1をクリックし、右のConfiguration Optionsの設定項目を必要に応じて変更します。通常はデフォルトで問題ありません。SPIのデフォルトクロック周波数が低いため、1MHz程度に変更します。

また、今回はデバッグ用途にprintfを使用してuartからデバッグ情報を出力するため、UART2とSTDIOを追加しています。必要に応じてUART2のボーレートを右のConfiguration Optionsから変更してください。

hspi2.jpg

hspi3.jpg

Bピンアサイン設定
MHC→Tools→Pin Configurationを選択します。Pin DiagramからSPI1のSDO1、SDI1、SCK1を割り付けします。また、Pin SettingsからCSとしてRA0をGPIO設定しました。Pin TableからRA0を出力に設定し、通信を安定させるためにプルアップを有効にしました。

hspi4.jpg

hspi5-1.jpg



hspi5.jpg

Cコード生成
設定完了後にGenerate Codeでコードを生成します。

hspi6.jpg

DSPI通信関数
SPI通信をするためのコードを追加します。

hspi7.jpg



SPI通信関数はSource Files/config/default/peripheral/spi/spi_master内のplib_spi1_master.cで定義されています。SPI初期化関数は既にinitialization.cに追加されているため、接続するデバイスに応じてSPI読み書き関数を追加します。SPI読み書きを同時にする場合はSPI1_WriteRead関数、読み込みのみの場合はSPI1_Read、書き込みのみの場合はSPI1_Writeの関数を使用します。なお、SPI1のConfiguration Options、Enable Interrupts設定はデフォルトで有効となっています。そのため、書き込み、読み込みした後はSPI1_IsBusy関数でSPI処理の完了を確認するか、SPI処理後に呼び出す関数をSPI1_CallbackRegisterで事前に登録する必要があります。SPI1_CallbackRegisterのサンプルはこちらに上がっているため、詳細の説明は省略します。今回はSPI1_IsBusy関数でSPI処理の完了を確認しました。

また、SPI通信で必要なCSは自動に設定(通信するときのみCSが自動でLOWになる)できますが、今回は手動でIO操作にしました。ピンアサイン設定でRA0を出力に設定したため、自動でHeader Files/config/default/peripheral/gpio内のplib_gpio.hにGPIO_RA0_Set関数やGPIO_RA0_Clear関数が追加されます。追加されたGPIO関数でCSを操作します。SRAMと通信するための全体のコードは下記です。main.cに下記のコードを追加しました。

#define SRAM_WRITE_CMD 2
#define SRAM_READ_CMD 3


void SramWrite(unsigned char *data, unsigned short length)
{
 unsigned char wcmd[4];
 wcmd[0]=SRAM_WRITE_CMD;
 wcmd[1]=0;//High Addr
 wcmd[2]=0;//Mid Addr
 wcmd[3]=0;//Low Addr

 GPIO_RA0_Clear();
 SPI1_Write(wcmd,4);
 while(SPI1_IsBusy()==true);
 SPI1_Write(data,length);
 while(SPI1_IsBusy()==true);
 GPIO_RA0_Set();
}

void SramRead(unsigned char *data, unsigned short length)
{
 unsigned char wcmd[4];
 wcmd[0]=SRAM_READ_CMD;
 wcmd[1]=0;//High Addr
 wcmd[2]=0;//Mid Addr
 wcmd[3]=0;//Low Addr
 
 GPIO_RA0_Clear();
 SPI1_Write(wcmd,4);
 while(SPI1_IsBusy()==true);
 SPI1_Read(data,length);
 while(SPI1_IsBusy()==true);
 GPIO_RA0_Set();
}


SPI1CONCLRやSPI1CONSET、LATACLR等のレジスタを操作してSPI通信やIO操作を行っていましたが、Harmony3でハードレイヤーが抽象化され、コード自動生成時点で最低限の関数が既に実装されています。設定によってはコード自動生成直後にも関わらず、ビルドエラーが発生してコードの修正が必要な場合がありますが、以前に比べると大分、改善しているように感じました。なお、SDカードといったHarmony3の機能や設定によってはSPI1_ReadやSPI1_Writeといった関数名が変わるため、注意が必要です。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年05月01日

STM32マイコンUART拡張機能

STM32F7、L4、H7シリーズではUART通信をする際に非常に便利な拡張機能があります。今回はUART拡張機能について紹介します。例えばUARTを利用する際に基板のTXとRXを間違えて逆転させてしまったり(回路上でTXとRXをクロスさせることを忘れていた)、フォトカプラを接続するので論理を逆転させたい(通常は論理反転のバッファICやトランジスタ等で追加ハードウェアで論理反転させる)、DMA受信時のエラー処理を無効にしたい(デフォルトではSTM32のUART受信でフレームエラー等が発生するとクリアするまで受信できない)といったことが多々あると思います。そのような場合にUART拡張機能を利用することでハードウェア追加や修正なしにソフトウェア側の設定変更のみで切り替えることが可能です。

UART拡張機能は下記の通りです。

名称機能
UART_ADVFEATURE_TXINVERTTX論理反転
UART_ADVFEATURE_RXINVERTRX論理反転
UART_ADVFEATURE_DATAINVERTデータビット論理反転
UART_ADVFEATURE_SWAPTX、RX入れ替え
UART_ADVFEATURE_RXOVERRUNDISABLE受信エラー無効
UART_ADVFEATURE_DMADISABLEONERRORDMA受信エラー無効
UART_ADVFEATURE_AUTOBAUDRATEオートボーレート有効
UART_ADVFEATURE_MSBFIRSTMSB有効


UART拡張機能の定義はstm32XX_hal_uart.hで下記のように定義されています。

/*!< No advanced feature initialization */
#define UART_ADVFEATURE_NO_INIT 0x00000000U

/*!< TX pin active level inversion */
#define UART_ADVFEATURE_TXINVERT_INIT 0x00000001U

/*!< RX pin active level inversion */
#define UART_ADVFEATURE_RXINVERT_INIT 0x00000002U

/*!< Binary data inversion */
#define UART_ADVFEATURE_DATAINVERT_INIT 0x00000004U

/*!< TX/RX pins swap */
#define UART_ADVFEATURE_SWAP_INIT 0x00000008U

/*!< RX overrun disable */
#define UART_ADVFEATURE_RXOVERRUNDISABLE_INIT 0x00000010U

/*!< DMA disable on Reception Error */
#define UART_ADVFEATURE_DMADISABLEONERROR_INIT 0x00000020U

/*!< Auto Baud rate detection initialization */
#define UART_ADVFEATURE_AUTOBAUDRATE_INIT 0x00000040U

/*!< Most significant bit sent/received first */
#define UART_ADVFEATURE_MSBFIRST_INIT 0x00000080U


実際に使用する場合はCubeMXで設定するか、下記のコードをUART初期化関数MX_USARTX_UART_Initに追記して使用します。CubeMXで設定する場合は下記のようにUARTの設定項目のAdvanced Featuresから有効/無効を設定します。

uart.jpg

コード上から修正する場合はデフォルトでUART_ADVFEATURE_NO_INITとなっているため、必要な項目を追加して各項目を有効化して使用します。

デフォルト
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

追加例
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_TXINVERT_INIT | UART_ADVFEATURE_SWAP_INIT;
huart1.AdvancedInit.TxPinLevelInvert = UART_ADVFEATURE_TXINV_ENABLE;
huart1.AdvancedInit.Swap = UART_ADVFEATURE_SWAP_ENABLE;

STM32F7、L4、H7シリーズのみ対応しているのが残念ですが、UART拡張機能の中でもTXとRXの入れ替え機能は非常に便利だと思いました。TXとRXをクロスさせて回路設計することを忘れていたり、クロスさせたはずが回路上で間違えていたり、クロスケーブルがなく、ストレートケーブルしか準備できていなかったり、UARTのTX、RX絡みでよくあるトラブルがソフトウェア上で解決できるのは非常に便利です。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年04月10日

PIC32MX + Harmony3 + MSD(USB Host) 設定ポイント

ここ5年くらいはSTM32マイコンを中心に開発、展開していました。しかし、昨今の半導体供給不足で主要なSTM32マイコンが入手困難となっています。運よく在庫がある場合でも価格が上がっている場合が多々あります。STM32マイコンがこれまで通りに安定して入手可能になるには少なくとも半年(2021年末)、長くて数年(2022~23年以降)は要すると思われます。

このような状況を受けて、STM32マイコンの代替として今更ながら古巣のPICマイコンに少し戻ってきました。PICマイコンも半導体供給不足の影響を多少受けていますが、STM32マイコンほど長納期化していません。主にdsPIC33F、30Fを使用していた10年近く前に比べると自動コード生成ツール(Harmony Configurator)、内部可視化(Data Visualizer)等のツール群が充実してきています。

今回はPIC32MX250F128BをターゲットにMPLAB X v5.45+Harmony3を使用してUSBメモリへの書き込み(MSD:Mass Strage Device)を行いました。その際にポイントとなる設定について紹介します。なお、ターゲットへの書き込みはPickit3を使用しました。


全体の手順については既にMicrochipのgithubサイトに詳しく書かれているため、設定のポイントに絞って紹介します。
MPLAB Xには予め、MPLAB Harmony 3 Launcher(旧名称 MPLAB Harmony Configurator 3)をインストールしてあります。


@File System設定
Use File System Auto Mount Featureにチェックを入れます。また、Media TypeにSYS_FS_MEDIA_TYPE_MSDを選択します。この設定を忘れるとSYS_FS_EventHandlerSet関数やSYS_FS_MEDIA_MANAGER_EventHandlerSet関数が生成されず、app.cのサンプルプログラムのビルドで失敗します。


filesystem_setting.jpg


AHeap Size設定
こちらのMicrochipのgithubサイトでは500byte以上とありましたが、500byteでは動作しませんでした。1024byte程度が良さそうです。

system_heap_setting.jpg


BCrystal設定
PIC32MX2XXの場合はこちらのMicrochipのgithubサイトとは異なり、クロック設定箇所はシンプルです。一方でUSBを使用する場合は外付け発振子が必須となります。最終的に設定上部のUSB Clockが48000000Hz(48MHz)になっていればOKです。USB Clockが0Hzといった48MHz以外の場合は設定を見直す必要があります。

crystal_setting.jpg


以上の設定を終えた段階で、プロジェクトフォルダsrc内のapp.c、app.hを下記のサンプルコードに置き換えます。
Harmony3\usb_apps_host\apps\msd_basic\firmware\src

置き換え後、USBメモリをPIC32マイコンに接続し、ファームを書き込むとUSBメモリにサンプルファイルが保存されれば成功です。

result.jpg

今回のコード、プロジェクトファイルは
です。

STM32マイコンのCubeMXのようにMPLAB Harmonyによって最初のセットアップの敷居が下がっていることを実感できました。一方でMPLAB Harmonyの情報が少なく、バージョンによってUIや設定項目が異なるため、情報収集に苦労する面がありました。今後、CubeMXの様に洗練されたUIになることに期待です。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年03月13日

VinculumU SPIパフォーマンス

以前にFTDIのVinculumUを紹介しました。VinculumUはUSBホスト機能を備えたマイコンで最大2つのUSBポートを使用することができます。開発環境のVinculum II Toolchainを使用することで書き込むプログラムによって様々な機能のUSBホストを実現することが可能です。

今回はVinculumU を用いてSPIマスタのパフォーマンスを評価してみました。最終的にはUSBから取得したデータをVinculumU のSPIマスタを介して外付けメモリに転送することを検討しています。検討に際して想定よりもパフォーマンスが出ないことが分かったため、設定による違いをまとめてみました。

前提条件としてvos_initではVOS_Q=10、VOS_TICK=1、NUMBER_OF_DEVICES=4、VOS_48MHZ_CLOCK_FREQUENCY、VOS_IOCTL_SPI_MASTER_SET_DATA_DELAY=0で実験しています。また、CSとCLKを測定して1回のSPI通信サイクルを確認しました。

まず、DMAを無効化した状態で8byteのデータをSPIマスタから書き込む操作を行いました。

vos_dev_write(hSPI_MASTER, wcmd, 4, &num);
vos_dev_read(hSPI_MASTER, dummy, 4, &num);
vos_dev_write(hSPI_MASTER, data, 4, &num);
vos_dev_read(hSPI_MASTER, dummy, 4, &num);

■DMA未使用
VOS_IOCTL_COMMON_DISABLE_DMA、クロック3MHz

1_No_DMA_3MHz_8byte.png

黄色がCLK、赤がCSです。CSが0の期間は約820usecとなりました。1byte毎に分かれて送出されていることが分かります。また、各byte毎に50usec程度の間をおいてから次のbyteが送出されています。また、4byteの書き込み後、次の4byteを書き込むまでに200usec以上要していることが分かりました。

■DMA使用 DMA_ACQUIRE_AS_REQUIREDモード、クロック3MHz
DMA転送を有効にしてDMAモードをDMA_ACQUIRE_AS_REQUIREDで同様に書き込みを行いました。

2_DMA_AS_3MHz_8byte.png

DMAを有効にすると1byte毎でなく、4byteまとめて送出されてるようになりました。CSが0の期間は約750usecとなりました。


■DMA使用DMA_ACQUIRE_AND_RETAINモード、クロック3MHz
DMAモードをDMA_ACQUIRE_AND_RETAINで同様に書き込みを行いました。

3_DMA_AND_3MHz_8byte.png

DMA_ACQUIRE_AND_RETAINモードでは4byteと次の4byteとの間隔が短くなり、CSが0の期間は約500usecとなりました。

■DMA使用 DMA_ACQUIRE_AS_REQUIREDモード、クロック20MHz
SPI通信のクロックを上げてDMA_ACQUIRE_AS_REQUIREDモードで同様に書き込みを行いました。

4_DMA_AS_20MHz_8byte.png

クロックを上げると4byteと次の4byteとの間隔が短くなり、CSが0の期間は約380usecとなりました。

■DMA使用 DMA_ACQUIRE_AS_REQUIREDモード、クロック20MHz、まとめて書き込み

4Byteを2回書き込みでなく、8byteを1回の書き込みに変更してみました。
vos_dev_write(hSPI_MASTER, data, 8, &num);
vos_dev_read(hSPI_MASTER, dummy, 8, &num);

5_DMA_AS_20MHz_8byteOnce.png

CSが0の期間は約200usecとなりました。

■DMA使用 DMA_ACQUIRE_AS_REQUIREDモード、クロック20MHz、まとめて書き込み、Writeのみ
VinculumUのSPIマスタは書き込み関数と読み込み関数がそれぞれ分かれています。通常は書き込み関数と読み込み関数の2つを使用してSPI通信の読み書きをします。書き込みのみの場合はvos_dev_write関数のみで構いませんが、読み込みの場合はvos_dev_write関数を実行してからvos_dev_read関数で値を読み込む必要があります。今回はメモリ書き込みのみを想定した場合、厳密には読み込み処理は不要です。書き込み関数のみにして同様に測定しました。

vos_dev_write(hSPI_MASTER, data, 8, &num);
//vos_dev_read(hSPI_MASTER, dummy, 8, &num);

6_DMA_AS_20MHz_8byteOnceWriteOnly.png

CSが0の期間は約100usecとなりました。

■結果まとめ
以上の結果からSPI通信を短時間で実行するためにはDMA転送の有効化、DMA_ACQUIRE_AS_REQUIREDよりもDMA_ACQUIRE_AND_RETAIN、クロックを高速にすることが重要であることが分かりました。また、vos_dev_write関数、vos_dev_read関数を細切れに呼び出すよりも分けずにまとめて呼び出すことが同様に短時間化に重要だと分かりました。

VinculumUは独自RTOSで実装されており、各処理の切り替え、関数呼び出しのオーバーヘッドが大きいようです。最後の結果でもCSが下がってからクロックが出るまで約40usec要しています。同様にクロックがで終わってからCSが上がるまでに約50usec要しています。VOS_Q、VOS_TICKの値を様々に変更してタイムスライスを小さくしても劇的な変化はありませんでした。VinculumU のSPIマスタは関数呼び出しのオーバーヘッドが非常に大きいことを考慮する必要があることが分かりました。

なお、今回の結果とは別に書き込みのみで書き込みデータを増やしてCSが0の区間を測定すると下記のような結果になりました。

書き込みデータ8byte192byte384byte512byte640byte800byte944byte
処理時間100usec200usec260usec310usec350usec410usec460usec

CS変化前後の処理オーバーヘッドの時間は変わらず、データ数の増加に合わせて処理時間が長くなる結果となりました。

これらのことからVinculumU のSPIマスタは高速な読み書き用途としてあまり向いていないことが分かりました。実装する際は関数呼び出しのオーバーヘッドを考慮してコーディングする必要があると分かりました。特にUSBのIsochronous転送の場合、最短1msecごとにデータが転送されます。少なくとも1msecより短い時間で1回の転送データを書き込む必要があり、SPI通信の処理速度が求められます。今後はIsochronous転送でどれくらいのデータ処理ができるか検討したいと思います。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年01月16日

QSPI通信を用いたSRAM読み書き

STM32マイコンのペリフェラル機能の1つ、QSPIがあります。今回はQSPIでNOR Flashではなく、SRAM(IS62WVS5128FB)を読み書きする方法を紹介します。データ削除等が必要なNOR FlashよりもSRAMは処理がシンプルで、読み書きのみです。

SPIは4つの信号線 (CS:Cable Select、SCK:Serial Clock、MISO:Master In Slave Out、MOSI:Master Out Slave In) で構成されています。一方、QSPIは6つの信号線(CS、SIO0、SIO1、SIO2、SIO3、SCK)で構成されています。SPIはMISO、MOSIの2本の通信バスに対して、QSPIはSIO0、SIO1、SIO2、SIO3の4本の通信バスに拡張されています。そのため、1クロックで送受信できるデータが2倍となり、より高速に通信できるようになっています。QSPIは主にNOR Flash、SRAM等のメモリ向けのインタフェースとして多く採用されています。

QSPI通信はSPI通信と互換があり、多くのQSPI対応デバイスは起動直後はSPIデバイスとして動作します。初期化や設定が完了してからQSPIモードに切り替えて高速に通信します。また、SPIのMISO、MOSIの2本のバスは通信方向が固定されていますが、QSPIは通信状態に応じて通信方向が変化します。通信状態をやり取りするためにSPIと異なり、インストラクションを送ってからアドレスやデータのやり取りを行います。多くの場合はインストラクション+アドレス+データの3つで構成されています。

STM32マイコンのHALライブラリでは、HAL_QSPI_Commandでインストラクション+アドレス+データ設定を行い、送信の場合のデータはHAL_QSPI_Transmitで送信し、受信の場合はHAL_QSPI_Receiveで受信します。

具体的にデータをメモリから読み込む場合は下記のような流れで設定します。
QSPI_CommandTypeDef sCommand;
HAL_StatusTypeDef res=0;
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;//インストラクションのバス数 1~4
sCommand.Instruction = READ_CMD;//インストラクション
sCommand.AddressMode = QSPI_ADDRESS_1_LINE;//アドレスのバス数 1~4
sCommand.Address = address;//アドレス
sCommand.AddressSize = QSPI_ADDRESS_24_BITS;//アドレス長
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;//オルタネイトバイト(拡張用、通常は未使用)
sCommand.DataMode = QSPI_DATA_1_LINE;//データのバス数 1~4
sCommand.DummyCycles = 0;//アドレスを設定してからデータの送受信までに待機が必要な場合に設定
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;//DDRモード設定
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;//DDRモード時遅延設定
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;//コマンドモード
sCommand.NbData = read_length;//データ長さ
res+=HAL_QSPI_Command(&QSPIHandle, &sCommand, 0xFFF);//インストラクション+アドレス+データ設定
res+=HAL_QSPI_Receive(&QSPIHandle, read_data, 0xFFF);//受信処理

上記の例では一般的なインダイレクトモードの例を紹介しました。STM32マイコンのQSPIではインダイレクトモード、ステータスポーリングモード、メモリマップモードの3種類あります。

インダイレクトモード:インストラクション+アドレス+データで送受信する一般的処理モードです。
ステータスポーリングモード:メモリのフォーマット等、時間を要する処理の場合に処理の完了有無を定期的に自動で確認することができます。
メモリマップモード:アドレスで直接、データを読み込み可能なモードです。ポイントは読み込み専用で書き込み不可。フォントや画像といった固定のデータを読み出す場合に向いています。

SRAM(IS62WVS5128FB)はDDRモードに対応していませんが、DDRモードに対応しているメモリの場合、クロックの立上がりと立下りの両方でデータのやり取りができるため、さらに倍のデータをやり取りできます。今回は読み込みのみ紹介しましたが、SRAM(IS62WVS5128FB)とQSPIで読み書きする全体コードはこちらにアップしました。


最近のSTM32L5等ではQSPIの代わりにOctalSPIが搭載されています。OctalSPIはバスが8本となりさらに高速に通信することが可能です。残念ながらQSPIのメモリマップモードは読み込み専用ですが、OctalSPIのメモリマップモードはFMCやFSMCのメモリマップモード同様に読み書き可能です。OctalSPIも今後紹介したいと思います。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする