STM32H7を使ってみる3 -キャッシュ・ワンダリング(前篇)-

←前回                       次回→

今から4年以上前、STM32F7ではぢめてキャッシュという概念に対峙し、分かった
つもりでお茶を濁してきましたがH7になった今どうしても正面から向き合わざるを
得ない事態になってしまいました…それも最悪な形で!!

●Cortex-M7のキャッシュにエラッタ発覚!

オイオイオイ死ぬわ私。(一般には今年春くらいに知れ渡った内容です)
要約しますとCortex-M7系マイコンでキャッシュポリシーをライトスル―にした状態で
Double-WORD(ARMの場合8バイト)の読み書きを同一のアドレスで行った時に、読み
出しの時にデータが壊れる事があるという内容…しかも現行の全リビジョン…
そしてカテゴリーAとかいうフュージョンジャックした剣崎さんでも不覚をとりかねない
クリティカルっていうか実質使用禁止設定判定なエラッタ…すざけんな!!111!
全文はこちらで読めますので各自把握しておいてください。

おきぱのSTM32F7/H7向けFatFsデモはライトバックを
使用するように すでにコードを修正済ですので安心してお使いいただけます。


STM32F7/H7側の障害情報ではF7はもちろん最新のH745/755に至るまで当エラッタが
網羅され、全滅状態ですorz


つまり、STM32F7/H7でキャッシュを使用する際はキャッシュの取り扱いが面倒な
ライトバック以外にキャッシュポリシーがとれないことを示しますorz

ぁーぁ…


●ひとまずSTM32H7のキャッシュのおさらいっ
まずはけしかん様のブログ記事も熟読の上で…

STM32F7/H7に搭載されてたキャッシュメモリは命令(I)とデータ(D)用に独立した
キャッシュが設けられております。キャッシュの方式はI/D両者ともセット・
アソシエイティブ
という回路規模とキャッシュ性能を折半した方式が採られていて、
とりわけデータ用では4ウェイセット・アソシエイティブという方式となっています。
ウエーイが4つもあってどんだけリア充なんだYO!

そしてキャッシュポリシーとしてはSTM32F7/H7は下記の3タイプ存在します。
1.ライトスルー(ライトアロケート無し固定)
 キャッシュに書き込みと同時に実メモリにも常に書きこむ。
 キャッシュの取り扱いは簡単だが動作は一番遅い。
 使い勝手一番良かったのにエラッタのせいで使用不能!!11!1

2.ライトバック・ライトアロケート有り
 キャッシュラインがいっぱいになるかCleanしないと実メモリに書きこまない。
 書き込み時キャッシュミスした場合1ライン分実メモリから読みだして
 キャッシュラインに展開する。ちょっと遅い。
 頻繁に読み書きする変数領域向けなのでフレームバッファのような書きっぱの
 用途では著しく不利になるのでむやみに設定してはならない。

3.ライトバック・ライトアロケート無し
 キャッシュラインがいっぱいになるかCleanしないと実メモリに書きこまない。
 書き込み時キャッシュミスした場合も実メモリから読みだす動作はしない。
 理論上一番早い。ライトスルーが封じられた今これが一番無難。

1〜3のいずれも読み込み側動作(リードアロケート)はすべて同様に行われる。

1.はエラッタのせいで死に設定なので2.や3.を選びます。尤もライトバック・ライト
アロケートありでもmemset等の関数で同じデータを書いていくような動作を検知して
自動的にライトアロケート無しに動的に切り替わりキャッシュが動くらしいです
(↑ほんまかいな)

詳しい解説はAPSさんのサイトにあります。
Cortex-Aのキャッシュについての解説ですがCortex-M7も動作や設定は同一です。


●で、具体的な設定はどうすればよいのか
Cortex-M7に置いてはMPU(メモリプロテクションユニット)からキャッシュポリシーの
設定を行います。ねむいさんのいつもののhw_config.cにあるMPU設定のサブルーチン
より、AXI-SRAMの設定を例に紹介します。

	/* Configure the MPU attributes as Write-Back/No-Write-Allocate for AXI-SRAM D1 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
AXI-SRAMの512kByte丸々ライトバック・ライトアロケート無しの設定をする場合
上記のようにします。
MPU_InitStruct.IsBufferable 	= MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;

↑特にこの3行は重要です。ライトバッファ有効・キャッシュ有効でライトバック
となります。また、SHAREABLEの設定は必ずMPU_ACCESS_NOT_SHAREABLEに
しておいてください!ここをMPU_ACCESS_SHAREABLEにするとキャッシュを
無効にしたのと同じになってしまいます。実に罠設定です。

ちなみにこの超重要な情報はNXPのM7コアのマイコンiMXのアプリケーションノート
明記されております。
しかしSTのマニュアルやアプリケーションノートには何所にも全く書いておらず
罠にはまってキャッシュの効力殺してるの気づいてない人多いんじゃないかと☠



SHAREABLE有効にしつつキャッシュ動作を有効させる方法もありますが今回の
システムではまだ利用価値が無いので割愛します。

また、ライトアロケート有りで行きたい場合は
MPU_TEX_LEVEL0->MPU_TEX_LEVEL1に変更します。
でもCortex-M7コアでライトアロケートありするくらいならエラッタ付き
ライトスルーにした方が10000000000倍マシです☠
こちらの比較も次回以降解説しますね。

	/* Configure the MPU attributes as Write-Back/No-Write-Allocate for AHB-SRAM1 D2 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x30000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* Configure the MPU attributes as Write-Back/No-Write-Allocate for AHB-SRAM2 D2 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x30020000;
MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* Configure the MPU attributes as Write-Back/No-Write-Allocate for AHB-SRAM3 D2 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x30040000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER3;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* Configure the MPU attributes as Write-Back/No-Write-Allocate for AHB-SRAM4 D3 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x38000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER4;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);

残りの内蔵SRAMの設定もライトバック・ライトアロケート無しの設定としました。
AHB-RAMはAXIバスマトリクス上にはないので意味なさそうですが実はキャッシュの
恩恵にあずかることができます。

	/* External-Memories cache setting */
/* Configure the MPU attributes as Write-Back/No-Write-Allocate for External SDRAM */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0xD0000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32MB; /* STM32H747I-Disco have 32Mbyte SDRAM */
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER6;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; /* MUST be No-WriteAllocation to avoid collision LTDC-Hostage */
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* External-Memories cache setting */
/* Configure the MPU attributes as Write-Back for QSPI-DirectRemapping(ReadOnly) */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x90000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_128MB; /* STM32H747I-Disco have 64*2=128MByte QSPI-ROM */
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RO_URO;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER7;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);

外部メモリ領域のSDRAMやQSPIはAXIバスでつながっているので当然のことながら
大きな影響を受けます。しっかりキャッシュ設定をしておきましょう。
また、SDRAMをLTDCのフレームバッファに使用したい場合は必ずライトアロケーション
なしを選択してください。ライトアロケーションありだといちいちCleanをしてやる
必要が出来てしまい、それを怠るとDMAの時のようにデータの一貫性が保たれ
なくなり表示がおかしくなります。フレームバッファはデータを読みだすことは
ほぼ無く書きっぱなしなのでライトアロケーションなしの方が都合が良いのです。

QSPIはメモリマップドモードのリードオンリーで使用するためどうでもよいですが
AccessPermission以外はSDRAMとキャッシュ設定を合わせておきましょう。

そしてSAI等の周期的にデータ投げる用途ではAXI-SRAMをライトアロケート有りにして
いるとキャッシュコントロールを今よりさらに厳格にしないと高ビットレートでポツポツ
ノイズが入りやがることが分かったので・・・#####
もう全部一律ライトアロケート無しでやります!111!!!

次回は上記キャッシュポリシーの設定別に実動作をさせたときの性能の比較を
行ってみたいと思います!

STM32H7を使ってみる2 -スライムが強すぎてアリアハンから出られない件-

おおねむいよしんでしまうとはなさけないっ…ていうかさ、お前このぶろぐ立ち上げて
10年間ずっと何やってきたんだ?毎年初心に帰ってないでさっさと先に進めや!!1!と
王様に副業先とほぼ同じ内容(本業は虹裏メイド)で怒られてる今現在でございますが
STM32って言う名前のスライムなんですがこれ世間一般の認識の青色のプルプル形状
しててワンパンで死ぬ雑魚
と違ってねむいさんの前に立ちはだかるそいつは液状で高速で
動き、物理攻撃が効かず強力な"溶かす"攻撃してくる最強クラスの敵なんですがねむい
さんのほうがワンパンで死んでしまうのですがこいつのせいでFatFsの実装という最初期
段階で踏みとどまって10年が経過してしまったのですが年数の経過とともにねむいさん
倒しまくって経験値増やしていった結果スライム君の方が先にレベルアップしてしまって
STM32F4とかF7とかどんどん数字が上がって挙句の果てにクロック周波数480MHz
デュアルコアH7とかにパワーアップしてしまったスライムに包囲されアリアハンは壊滅
寸前もうだめだでもよく考えたらねむいさんの職業て勇者じゃなくて虹裏メイドだから
別に無理してアリアハンから出なくてもいいんじゃねっていう弱い考えが浮かびそうに
なりますが今度こそスライムを倒すために異世界モノなろう小説風に毎回恒例の初心
表明を宣言するのでした!10年後に転生したら10年たっていた件


さて、謎の文字列は置いといてSTM32H747I-Discovery向けいつものはこちら
なります!!!!1!!!!!
まだ暫定版なので明らかなバグ以外はご意見無用とさせていただきます!!!!

上記のブツを元にプログラムの解説をしていきますのでよろしくお願いします。

●アリアハン脱出作戦開始

まずすべきことはSTM32H7のメモリ/バスのアーキテクチャを理解してプログラム
作りの土台を作ること、つまりはH7向けのリンカスクリプトとビルド環境を構築
することです。


STM32F7に比べてメモリ構成はさらに複雑になっています。しかもデュアルコア
なのでそれを意識してメモリを分け分けしないといけません。

ねむいさんの戦略としては当分の間はH7コアをシングルで動かすことを主眼に入れ、
上記画像のように組んでみました。H7コアと同じ周波数で動作できる密結合メモリの
DTCM-RAMはほぼスタック専用に、大容量&バス幅64bitのAXI-SRAMは
DATA,BSS,HEAPに割り振ります。
ITCM-RAMも容量が増えたので今回から積極的に使用していきます!
そしてAHBバスマトリクスに配置されているSRAMですがこちらはバスマトリクスが
どこで繋がってるかをしっかりわかっておかないとDMAとかでハマるので以後
個別に詳しく解説しようと思います。

また、すでに先達の使用者からよく言われていることですがF7では便利だった
DTCMはH7に上がってDMAの制約が極めてきつくなり、実質上直接DMA不可と
なっていますF**K!!!11!!!!!
一応やれないこともないのですがMasterDMAを介した面倒な間接転送となります。
これについてもいずれ別記事で詳しく解説したいです(弱い願望)。


/* Linker script Upperside for STM32H747XIH6		*/
/* Nemui Trinomius (http://nemuisan.blog.bai.ne.jp) */

OUTPUT_FORMAT ("elf32-littlearm")

/* Memory Spaces Definitions */
MEMORY
{
/* Instruction TCM SRAM */
ITCM_RAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64k
/* Data TCM SRAM */
DTCM_RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128k
/* AXI-SRAM D1 Domain */
AXI_SRAM (xrw) : ORIGIN = 0x24000000, LENGTH = 512k
/* AHB-SRAM1 D2 Domain */
SRAM1_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 128k
/* AHB-SRAM2 D2 Domain */
SRAM2_D2 (xrw) : ORIGIN = 0x30020000, LENGTH = 128K
/* SRAM3 D2 Domain */
SRAM3_D2 (xrw) : ORIGIN = 0x30040000, LENGTH = 32k
/* SRAM4 D3 Domain */
SRAM4_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64k
/* Backup SRAM */
BKP_SRAM (rw) : ORIGIN = 0x38800000, LENGTH = 4k
/* External SDRAM(FMC) */
EXT_SDRAM (xrw) : ORIGIN = 0xD0000000, LENGTH = 32M
/* Main Embedded Dual-Bank FlashROM */
FLASH_ROM (rx) : ORIGIN = 0x08000000, LENGTH = 2M
/* External Dual-Mode QSPI-ROM */
QSPI_ROM (rx) : ORIGIN = 0x90000000, LENGTH = 128M
}

/* higher address of the stack bottom,AAPCS said "stack MUST BE 8byte alignment". */
_estack = (ORIGIN(DTCM_RAM)+LENGTH(DTCM_RAM)) & ~ 0x7;

/* higher address of the heap end */
_eheap = ORIGIN(AXI_SRAM)+LENGTH(AXI_SRAM);


/* include the section management sub-script */
INCLUDE "STM32H7xxxx_FLASH.ld"


こんな感じでリンカスクリプトに落としました。
今回はスクリプトそのものも見直して意味不明なことやってた記述は排除/修正
しております!その修正内容は…


ねむいさんスタックポインタとかヒープの設定で9年間くらい謎の引き算やってた…
なんでこんなんでアライメントが揃うと思っていたのか…
(↑ARM7TDMIは設定するSRAMの最終アドレスから-8バイト引いた値をspに設定
  しないといけなかったのでCortex-Mからスタック設定の仕様が変わった
  後も仕様をよく見ないでやっていたと考えられる…
  これじゃスライムにやられるわけだ)


●クロック設定とかGPIO入出力とか
こちらに関してはSTM32H7CubeのBSPを参考にすれば問題はありません。
最大480MHzで動作するH7コアですがエラッタやSDRAMを使った時の速度的な
制約がありH7コアクロック400MHz、AXI/AHBバスクロック200MHzがつぶしが
効く設定となり、BSPのExampleではすべてこの周波数設定となってます。
これをそっくりそのまま頂いちゃいましょう!
ぇ…?CubeMX?なんスかそのバグコード生成機?
そんなもん使ってたら時間無駄にして頭がパーンしちゃいますよぅ?
/* Defines -------------------------------------------------------------------*/
/**
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 400000000 (Cortex-M7 CPU Clock,upto 480MHz)
* HCLK(Hz) = 200000000 (Cortex-M4 CPU, Bus matrix Clocks,upto 200MHz)
* AHB Prescaler = 2 (AHB/AXI Bus Matrix Clock upto 200MHz)
* D1 APB3 Prescaler = 2 (APB3 Clock upto 100MHz)
* D2 APB1 Prescaler = 2 (APB1 Clock upto 100MHz)
* D2 APB2 Prescaler = 2 (APB2 Clock upto 100MHz)
* D3 APB4 Prescaler = 2 (APB4 Clock upto 100MHz)
* HSE Frequency(Hz) = 25000000
* VCO Frequency = 800000000 (160*5)
* PLL_M = 5
* PLL_N = 160
* PLL_P = 2
* PLL_Q = 4
* PLL_R = 2
* VDD(V) = 3.3
* Flash Latency(WS) = 4
* @param None
* @retval None
*/
#define PLL1_M 5
#define PLL1_N 160
#define PLL1_P 2
#define PLL1_Q 4
#define PLL1_R 2
#define PLL1_FRACN 0
上記はコア/バス/ペリフェラルクロック用のPLL設定値のdefineです。
F7と比べるとPLLが増えてかなり変わってる感じがしますね。
hw_config.cに具体的なコードがあるのでご参考に。
また、ユーザーマニュアルではカーネルクロックなるややこしい概念が出てきますが
殆どのペリフェラルではHCLK(AHB)のクロックを指します。

static void LED_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

/* Enable GPIO_LED clock */
GPIO_LED_CLKEN();

/* Configure GPIO for LEDs as Output push-pull */
GPIO_InitStructure.Pin = LED_D1;
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(GPIO_LED1, &GPIO_InitStructure);
LED_D1_OFF();

GPIO_InitStructure.Pin = LED_D2;
HAL_GPIO_Init(GPIO_LED2, &GPIO_InitStructure);
LED_D2_OFF();
GPIO_InitStructure.Pin = LED_D3;
HAL_GPIO_Init(GPIO_LED3, &GPIO_InitStructure);
LED_D3_OFF();
GPIO_InitStructure.Pin = LED_D4;
HAL_GPIO_Init(GPIO_LED4, &GPIO_InitStructure);
LED_D4_OFF();
}

LED(GPIO)の出力設定はF7とほぼ変わりませんが100MHz越えのI/Oトグリング
対応の為"GPIO_SPEED_FREQ_VERY_HIGH"という定義が追加されてます。
特に必要が無い場合はVERYHIGHにしておきましょう。

static void KEY_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

/* Enable GPIO_LED clock */
RCC->AHB4ENR |= (RCC_AHBPeriph_GPIO_KEY_USER1);
RCC->AHB4ENR |= (RCC_AHBPeriph_GPIO_KEY_SEL);
RCC->AHB4ENR |= (RCC_AHBPeriph_GPIO_KEY_DOWN);
RCC->AHB4ENR |= (RCC_AHBPeriph_GPIO_KEY_LEFT);
RCC->AHB4ENR |= (RCC_AHBPeriph_GPIO_KEY_RIGHT);
RCC->AHB4ENR |= (RCC_AHBPeriph_GPIO_KEY_UP);

/* Configure GPIO for Key Input */
GPIO_InitStructure.Pin = KEY_USER1;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;;
GPIO_InitStructure.Alternate = 0;
HAL_GPIO_Init(GPIO_KEY_USER1, &GPIO_InitStructure);

GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Pin = KEY_SEL_PIN;
HAL_GPIO_Init(GPIO_KEY_SEL, &GPIO_InitStructure);
GPIO_InitStructure.Pin = KEY_DOWN_PIN;
HAL_GPIO_Init(GPIO_KEY_DOWN, &GPIO_InitStructure);
GPIO_InitStructure.Pin = KEY_LEFT_PIN;
HAL_GPIO_Init(GPIO_KEY_LEFT, &GPIO_InitStructure);
GPIO_InitStructure.Pin = KEY_RIGHT_PIN;
HAL_GPIO_Init(GPIO_KEY_RIGHT, &GPIO_InitStructure);
GPIO_InitStructure.Pin = KEY_UP_PIN;
HAL_GPIO_Init(GPIO_KEY_UP, &GPIO_InitStructure);
}

入力についても同様です。
H7-Discvoeryは4+1入力ジョイスティックが追加されたので物理キー入力処理も
追加しております。
/* Macros */
/* uncomment to enable joy input */
//#define USE_HWKEY_INPUT_SUPPORT

/* ITCM Fastest call for IRQ */
#define ITCM __attribute__ ((section (".itcm")))
//#define ITCM __attribute__ ((long_call, section (".itcm")))

hw_config.h内で上記の物理キー入力有効無効が切り替えられるようにしておきました。
タッチパネルがあるのでデフォルトは無効です。


●タイムベースは重要ですよね
	/* Making MilliSecond-Order Timer */
/* Select Clock Source */
SystemCoreClockUpdate();
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* Setup SysTick Timer for 1 msec interrupts */
if (SysTick_Config(SystemCoreClock/interval))
{
/* Capture error */
while (1);
}

マイコンを動かすにおいて必要不可欠なタイマですがsystickの設定についてはF7と
殆どかわりません。ほんの少しの修正で使用可能です。
ねむいさんのいつものではミリ秒オーダーのタイムベースとして動作させてます。

	/* Making MicroSecond-Order Timer uses general purpose timer! */
/* Enable timer clock */
USEC_TIMx_CLKEN();

/* calculate TIMx(2~5) Prescaler clock(D2 Domain) */
if(RCC->D2CFGR & RCC_D2CFGR_D2PPRE1){
if((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) == RCC_D2CFGR_D2PPRE1_DIV2){
cal_usec_divide = SystemCoreClock/2; /* (HCLK(=SYSCLK/2)*2)*2 */
}
else if((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) == RCC_D2CFGR_D2PPRE1_DIV4){
cal_usec_divide = SystemCoreClock/4; /* (HCLK(=SYSCLK/2)*4)*2 */
}
else if((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) == RCC_D2CFGR_D2PPRE1_DIV8){
cal_usec_divide = SystemCoreClock/8; /* (HCLK(=SYSCLK/2)*8)*2 */
}
else if((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) == RCC_D2CFGR_D2PPRE1_DIV16){
cal_usec_divide = SystemCoreClock/16; /* (HCLK(=SYSCLK/2)*16)*2 */
}
}

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;
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);

μ秒オーダーは32bitタイマを利用しています。F7/H7でTIM5のフリーランニング動作で
実現しております。ただH7ではAHBの番号が異なっているので変更は必用です。
また、バスクロックも2倍に上がっているのでそれを考慮した計算値を与えましょう。
各詳細はsystick.c参照

●リングバッファ付UARTもはや基本
ねむいさんUARTという文字列をなぜかずっとウラートと呼んでました
ソビエト連邦かな?(正解:ユーアート)
#if defined(USE_STM32H747I_DISCO)
#define UART_DEFAULT_NUM 1
#define UARTx USART1
#define USARTx_IRQHandler USART1_IRQHandler
#define USARTx_CLKEN() ¥
/* Enable GPIO clock(UART_TX) */ ¥
RCC->AHB4ENR |= (RCC_AHB4ENR_GPIOAEN); ¥
/* Enable GPIO clock(UART_RX) */ ¥
RCC->AHB4ENR |= (RCC_AHB4ENR_GPIOAEN); ¥
/* Enable UART clock */ ¥
RCC->APB2ENR |= (RCC_APB2ENR_USART1EN);
#define USART_TxPin GPIO_PIN_9
#define USART_RxPin GPIO_PIN_10
#define USART_TxPort GPIOA
#define USART_RxPort GPIOA
#define USART_TXALT GPIO_AF7_USART1
#define USART_RXALT GPIO_AF7_USART1

/* This is the ARDUINO Option(not used) */
//#define UART_DEFAULT_NUM 8
//#define UARTx USART8
//#define USARTx_IRQHandler USART8_IRQHandler

#else
#error "Select or make board settings!!"
#endif

こちらについてもF7と大きな変更はなく、H7-Discovery内のSTLink/V3のVCPに
つながっているPA9(TX),PA10(RX)を割り当てるだけです。まぁこのポートは
ブートローダーでも使うSTM32伝統のUARTポートみたいな感じですね〜

#if defined(UART_INTERRUPT_MODE)
/**************************************************************************/
/*!
@brief Handles UARTx global interrupt request.
@param None.
@retval None.
*/
/**************************************************************************/
ITCM void USARTx_IRQHandler(void)
{
uint32_t IntStat = UARTx->ISR;

if(IntStat & USART_ISR_RXNE_RXFNE)
{
/* Clear Errors */
UARTx->ICR = USART_ICR_ORECF | USART_ICR_NECF | USART_ICR_FECF | USART_ICR_PECF;

/* Advance buffer head. */
unsigned int tempRX_Head = ((&USARTx_Buf)->RX_Head + 1) & (UART_BUFSIZE-1);

/* Check for overflow. */
unsigned int tempRX_Tail = (&USARTx_Buf)->RX_Tail;
uint8_t data = UARTx->RDR;

if (tempRX_Head == tempRX_Tail) {
/* Overflow MAX size Situation */
/* Disable the UART Receive interrupt */
UARTx->CR1 &= ~(USART_CR1_RXNEIE);
}else{
(&USARTx_Buf)->RX[(&USARTx_Buf)->RX_Head] = data;
(&USARTx_Buf)->RX_Head = tempRX_Head;
}
}

if(IntStat & USART_ISR_TXE_TXFNF)
{
/* Check if all data is transmitted. */
unsigned int tempTX_Tail = (&USARTx_Buf)->TX_Tail;
if ((&USARTx_Buf)->TX_Head == tempTX_Tail){
/* Overflow MAX size Situation */
/* Disable the UART Transmit interrupt */
UARTx->CR1 &= ~(USART_CR1_TXEIE);
}else{
/* Start transmitting. */
uint8_t data = (&USARTx_Buf)->TX[(&USARTx_Buf)->TX_Tail];
UARTx->TDR = data;

/* Advance buffer tail. */
(&USARTx_Buf)->TX_Tail = ((&USARTx_Buf)->TX_Tail + 1) & (UART_BUFSIZE-1);
}
}
}
#endif

また、実際の通信では割り込みを用いてノンブロッキング(非同期)のリングバッファ
構成を取っています。割り込みルーチン本体はITCMにコードを配置して超高速に処理
できるようにしておきました。

/**************************************************************************/
/*!
@brief Copy Time-Critical codes into ITCM-RAM.
@param None.
@retval None.
*/
/**************************************************************************/
static void datacopy(unsigned int romstart, unsigned int start, unsigned int len)
{
unsigned int *pulDest = (unsigned int*) start;
unsigned int *pulSrc = (unsigned int*) romstart;
unsigned int loop;
for (loop = 0; loop < len; loop = loop + 4)
*pulDest++ = *pulSrc++;
}
/* _stitcm,_sitcm,_eitcm are MUST be decrare in likerscript file */
static void ITCM_Datainit(void)
{
unsigned int *LoadAddr, *ExeAddr, *EndAddr, SectionLen;

/* Copy ITCM Codes into ITCM-RAM */
LoadAddr = &_stitcm;
ExeAddr = &_sitcm;
EndAddr = &_eitcm;
SectionLen = (void*)EndAddr - (void*)ExeAddr;
datacopy((unsigned int)LoadAddr, (unsigned int)ExeAddr, SectionLen);
}

当たり前の事ですがITCMで動作させるためにはあらかじめコードを転送しておかないと
いけないので起動直後とかにコードを送っておきましょう。
その他細かい部分はhw_config.cに詰まっていますのでそちらをご参照ねがいます。





というわけでまだほんの少ししか紹介出来ておりませんがひとまずH7を動かすための
基本的な項目の解説を行いました。次回からはH7最大の難関であるキャッシュとDMAの
問題に切り込んでいこうと思います!

Go to top of page