STM32L5を使ってみる2 -FatFsその他の実装-

●いつものやつを実装しよう
前回は144Pin版NUCLEO基板の下駄基板のビジュアルだけ紹介しましたが、
今回はそれを動かすためのファームウエアの要所を紹介していこうと思います。
ねむいさんの"いつもの"ことChaN師のFatFs実装を中心としております。

以下、STM32L5-NUCLEOのFatFs実装例のプロジェクトの要点を掻い摘んで
解説していきたいと思います。

●GCCビルドのためのCortex-M33向けオプション
先ずはmakefileを見ていただいたらお分かりと思いますがCortex-M33は
基本的にCortex-M4Fの進化版と思って頂けたらよいと思います。
GCCコンパイラに与えるCortex-M33コア向けオプションは以下になります。

-mcpu=cortex-m33 -mtune=cortex-m33 -mfix-cmse-cve-2021-35465

-mfix-cmse-cve-2021-35465とかいうのはArmv8-Mコアの脆弱性対策です。
2023年現在の最新のGCCコンパイラはデフォルトでONになっておりいちいち
入れる必要はないかもしれませんが念のため明示してます。




●基本の"き"、タイマーとUARTの実装
STM32に代表されるCortex-M系のコアはほぼすべてにSystickタイマーが
標準装備されております。こちらを1mSecウエイト用のタイマーとして、一方
マイコン周辺機器のタイマー機能を利用してuSecオーダーのタイマーを実装します。
今回はuSec用にはTIM5を利用しました。

以下systick.cより抜粋
	/* Making MicroSecond-Order Timer uses general purpose timer! */
/* Enable timer clock */
USEC_TIMx_CLKEN();

/* calculate TIMx(2 or 5) Prescaler clock(APB1) */
if(RCC->CFGR & RCC_CFGR_PPRE1){
if((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_HCLK_DIV2){
cal_usec_divide = SystemCoreClock/2; /* (HCLK(=SYSCLK)*2 */
}
else if((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_HCLK_DIV4){
cal_usec_divide = SystemCoreClock/4; /* (HCLK(=SYSCLK)*4)*2 */
}
else if((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_HCLK_DIV8){
cal_usec_divide = SystemCoreClock/8; /* (HCLK(=SYSCLK)*8)*2 */
}
else if((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_HCLK_DIV16){
cal_usec_divide = SystemCoreClock/16; /* (HCLK(=SYSCLK)*16)*2 */
}
}

/* usec wait timer settings */
TimHandle.Instance = USEC_TIMx;
TimHandle.Init.Period = UINT32_MAX;
TimHandle.Init.Prescaler = ((cal_usec_divide)/USEC_INTERVAL) - 1;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
/* Capture error */
while (1);
}

USEC_TIMx->CR1 &= ~(TIM_CR1_UIFREMAP); /* Disable UIF Remap(Must Need!) */
HAL_TIM_Base_Start(&TimHandle);

最近のHALライブラリは"AutoReloadPreload"構造体が追加されているので
移植の際はご注意ください。また,UIFRemapビットにもご注意ください。


UARTについてはNUCLEO-L552ZE-Q基板上ではSTLinkのVCPとはLPUART
なるもので接続されております。一般のUARTとは多少違いがありますが
やることは同じです。LPUART1、GPIOG7,8をTX,RXとします。
一応LPUART以外のポートも使用できるようにしておりますので詳細は
uart.cupport.cを参照願います(丸投げ)
っとその前にLPUART1を使う際はLPUART1へのクロック供給のほかに
"HAL_PWREx_EnableVddIO2()"の実行も忘れないようにしてください。

またリングバッファによるノンブロッキング送受信なのはいつも通り
なのでスムーズにデータのやり取りができると思います。


●SDMMCドライバの実装・FatFsとの結合
STM32L5にはSDMMCがありますのでこれを実装しますが数年前STM32H7に
実装したSDMMCのドライバ
をスライド移植するだけのお手軽です☆
ぶっちゃけ今回の移植作業で一番楽でした。


一応使用上の注意なのですがSTM32H7と同じくSTM32L5のSDMMCはDDR
モードが使用可能ですがSDMMCに供給するクロックがバイパスされて
いないと使用不可となりますのでご注意ください。

ねむいさんの作例ではデフォルトではHSI48をSDMMCにバイパスなしで
供給しているためDDRで使用することができませんorz


もしDDR対応のeMMCをお持ちの方はコアクロックを110MHzから100MHzで
動かすとHSI48を使わずコアクロックをSDMMCのクロックにするよう
切り替えますので試してみてください。

STM32L5の最大動作クロックは110MHzなんですけどこういう制約が色々
あって100MHzで動かすほうがよっぽど有利なんですよね〜
100MHzとか昨今のマイコンでは遅い部類のクロックですがまぁ初代の
STM32F1とかのMAX72MHz動作と比べたら(消費電力も合間見ると)雲泥の
差ですがすっかり贅沢になってしまいましたね〜

STM32H7の実装でも実はひっそり最高速の480MHz対応してますが
こちらもいずれ紹介しようと思います。


●SPIとシリアル接続TFT-LCD

STM32L5のSPIモジュールも言ったって普通のSPIなので実装は容易
でした…が、DMAのほうはちょっと一癖ありました。
ねむいさんが知ってる従来のDMAではなくってDMAのチャネルがマルチ
プレクスされていて柔軟な設定ができるようになっており
、逆に扱いに
苦労しましたがExampleを参考に何とかSPIでDMAを吐けるようになって
おります。以下にDMAMUXの設定を抜粋しておきます。
全体は./lib/display/mcu_depend/display_if_basis.cを参照のこと。
#ifdef USE_DISPLAY_DMA_TRANSFER
/* DMA controller clock enable */
__HAL_RCC_DMAMUX1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();

/* Configure DMA request LcdDmaHandle */
LcdDmaHandle.Instance = SPILCD_DMA_CHANNEL;
LcdDmaHandle.Init.Request = SPILCD_DMA_REQEST;
LcdDmaHandle.Init.Direction = DMA_MEMORY_TO_PERIPH;
LcdDmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
LcdDmaHandle.Init.MemInc = DMA_MINC_ENABLE;
LcdDmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
LcdDmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
LcdDmaHandle.Init.Mode = DMA_NORMAL;
LcdDmaHandle.Init.Priority = DMA_PRIORITY_MEDIUM;
if (HAL_DMA_Init(&LcdDmaHandle) != HAL_OK)
{
for(;;){
__NOP();
}
}
if (HAL_DMA_ConfigChannelAttributes(&LcdDmaHandle, DMA_CHANNEL_NPRIV) != HAL_OK)
{
for(;;){
__NOP();
}
}
__HAL_LINKDMA(&SpiHandle,hdmatx,LcdDmaHandle);

/* DMA interrupt init */
/* SPILCD_DMA_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SPILCD_DMA_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPILCD_DMA_IRQn);
#endif


また下駄基板ではST7789V2を使用したTFT-LCDモジュールを採用して
おりますがこちらのデータ線はSDIとSDOがマルチプレクスされたSDA
となっており、ST7789V2のデバイスIDを取得したいならばSTM32L5側で
SPIのMOSIとMISOを直結し読み出しの際にMISOの信号を正しく受け取る
MOSIをGPIOの入力にわざわざ切り替えてやる必要があります。
以下にそのコードを示します。
void Display_ChangeSDA_If(uint8_t sda_mode)
{
GPIO_InitTypeDef GPIO_InitStructure;

if(sda_mode == TFT_SDA_READ){
/* Enable CTRL Line GPIO Settings */
DISPLAY_GPIOCLK_EN(DISPLAY_CLK_SDI);
GPIO_InitStructure.Pin = CTRL_SDI;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStructure.Alternate = 0;
HAL_GPIO_Init(DISPLAY_PORT_SDI, &GPIO_InitStructure);
}

else {
/* Enable CTRL Line GPIO Settings */
#if defined(USE_SOFTWARE_SPI)
DISPLAY_GPIOCLK_EN(DISPLAY_CLK_SDI);
GPIO_InitStructure.Pin = CTRL_SDI;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStructure.Alternate = 0;
HAL_GPIO_Init(DISPLAY_PORT_SDI, &GPIO_InitStructure);

#else
/* Connect SPI pins to Alternate Function */
/* Restore SPI MOSI pin configuration */
DISPLAY_GPIOCLK_EN(DISPLAY_CLK_SDI);
DISPLAY_PERIF_CLK(ENABLE);
GPIO_InitStructure.Pin = CTRL_SDI;
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStructure.Alternate = SRC_SDI;
HAL_GPIO_Init(DISPLAY_PORT_SDI, &GPIO_InitStructure);
#endif
}
}





ちなみにST7789V2側のドライバはデフォルトでは上記の切り替えの
必要なくIDを決め打ちで返すようにしてますのでもの好きな方だけ
SDAマルチプレクス有効にして試してみてください。
SDAマルチプレクスの際はSTM32側のMOSIとMISOをショートしてください。



ST7789V2からIDを読み出す設定でビルドしてデバッガで追っかけてみると
こんな感じにSDAからST7789V2のID(RDID2(0xDB)で0x85が返る)が
読まれてくるのがわかります。

SPI接続で動くモジュールはこんな感じでピン数節約のためにSDIとSDOが
マルチプレクスされてることもあるので覚えておいて損はないでしょう。



というわけでいつものChaN師のファイラー起動からの…

海で働いてるいないさんのpng形式のイラスツをlibpngでデコード!
(LCDのサイズ小さいので画像の一部しか表示できませんが)

libpngのほかにbmp形式はもちろんlibjpegやgiflibでjpg、gif形式
ファイルも表示できますがSTM32L5シリーズはフラッシュメモリ容量が
512kByteしかないのでフォントファイルを乗せつつ画像デコード用の
ライブラリ全部乗せは容量上非常に苦しいです。

そんなときのために容量を食うフォントファイルは外付けのSPI-ROM
にデータを配置してアクセスしたいところですが…
こういう用途に便利なOCTO-SPIという機能がありますが解説が長く
なりそうなので次回に続きます…!

Go to top of page