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の
問題に切り込んでいこうと思います!
-
免責・連絡先は↑のリンクを
↓SNSもやってます↓
powered by まめわざ- ARM/STM32 (116)
- OpenOCD (27)
- ARM/NxP (34)
- ARM/Cypress (5)
- ARM/Others (3)
- ARM/Raspi (1)
- AVR (13)
- FPGA (4)
- GPS/GNSS (19)
- MISC (81)
- STM8 (2)
- Wirelessなアレ (16)
- おきぱ (1)
- ブラウザベンチマーク (28)
- 日本の自然歩道 (26)
- STM32U0はぢめました
⇒ ねむい (08/07) - STM32U0はぢめました
⇒ ひかわ (07/28) - STM32H5を使ってみる3 -待ち受ける初見殺しの罠たち-
⇒ ねむい (05/17) - STM32H5を使ってみる3 -待ち受ける初見殺しの罠たち-
⇒ どじょりん (05/16) - STM32H5を使ってみる3 -待ち受ける初見殺しの罠たち-
⇒ どじょりん (05/16) - いろいろ試す61(と今年の反省会)
⇒ ねむい (01/02) - いろいろ試す61(と今年の反省会)
⇒ ひかわ (01/02) - いろいろ試す61(と今年の反省会)
⇒ ひかわ (01/01) - STM32H5を使ってみる3 -待ち受ける初見殺しの罠たち-
⇒ ねむい (12/31) - STM32H5を使ってみる3 -待ち受ける初見殺しの罠たち-
⇒ ひかわ (12/31)
- November 2024 (1)
- October 2024 (1)
- September 2024 (1)
- August 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (1)
- April 2024 (1)
- March 2024 (1)
- February 2024 (2)
- January 2024 (1)
- December 2023 (4)
- November 2023 (2)
- October 2023 (2)
- September 2023 (1)
- August 2023 (2)
- July 2023 (1)
- June 2023 (2)
- May 2023 (3)
- April 2023 (1)
- March 2023 (1)
- February 2023 (1)
- January 2023 (1)
- December 2022 (2)
- November 2022 (1)
- October 2022 (1)
- September 2022 (1)
- August 2022 (1)
- July 2022 (1)
- June 2022 (1)
- May 2022 (1)
- April 2022 (1)
- March 2022 (1)
- February 2022 (1)
- January 2022 (1)
- December 2021 (2)
- November 2021 (2)
- October 2021 (1)
- September 2021 (1)
- August 2021 (1)
- July 2021 (1)
- June 2021 (1)
- May 2021 (1)
- April 2021 (1)
- March 2021 (1)
- February 2021 (1)
- January 2021 (1)
- December 2020 (3)
- November 2020 (1)
- October 2020 (1)
- September 2020 (1)
- August 2020 (1)
- July 2020 (1)
- June 2020 (2)
- May 2020 (1)
- April 2020 (1)
- March 2020 (1)
- February 2020 (1)
- January 2020 (1)
- December 2019 (3)
- November 2019 (1)
- October 2019 (1)
- September 2019 (2)
- August 2019 (1)
- July 2019 (1)
- June 2019 (1)
- May 2019 (1)
- April 2019 (1)
- March 2019 (1)
- February 2019 (1)
- January 2019 (1)
- December 2018 (3)
- November 2018 (2)
- October 2018 (1)
- September 2018 (1)
- August 2018 (1)
- July 2018 (1)
- June 2018 (1)
- May 2018 (1)
- April 2018 (2)
- March 2018 (1)
- February 2018 (1)
- January 2018 (1)
- December 2017 (2)
- November 2017 (2)
- October 2017 (1)
- September 2017 (1)
- August 2017 (1)
- July 2017 (1)
- June 2017 (1)
- May 2017 (1)
- April 2017 (1)
- March 2017 (2)
- February 2017 (2)
- January 2017 (2)
- December 2016 (7)
- November 2016 (2)
- October 2016 (2)
- September 2016 (1)
- August 2016 (1)
- July 2016 (1)
- June 2016 (1)
- May 2016 (2)
- April 2016 (1)
- March 2016 (2)
- February 2016 (1)
- January 2016 (1)
- December 2015 (3)
- November 2015 (1)
- October 2015 (3)
- September 2015 (2)
- August 2015 (2)
- July 2015 (3)
- June 2015 (3)
- May 2015 (4)
- April 2015 (2)
- March 2015 (4)
- February 2015 (1)
- January 2015 (3)
- December 2014 (3)
- November 2014 (2)
- October 2014 (1)
- September 2014 (2)
- August 2014 (2)
- July 2014 (3)
- June 2014 (2)
- May 2014 (1)
- April 2014 (1)
- March 2014 (4)
- February 2014 (4)
- January 2014 (3)
- December 2013 (5)
- November 2013 (4)
- October 2013 (3)
- September 2013 (2)
- August 2013 (2)
- July 2013 (2)
- June 2013 (3)
- May 2013 (2)
- April 2013 (2)
- March 2013 (2)
- February 2013 (2)
- January 2013 (3)
- December 2012 (4)
- November 2012 (2)
- October 2012 (2)
- September 2012 (4)
- August 2012 (1)
- July 2012 (3)
- June 2012 (2)
- May 2012 (3)
- April 2012 (3)
- March 2012 (2)
- February 2012 (3)
- January 2012 (3)
- December 2011 (5)
- November 2011 (3)
- October 2011 (2)
- September 2011 (2)
- August 2011 (2)
- July 2011 (2)
- June 2011 (2)
- May 2011 (2)
- April 2011 (2)
- March 2011 (2)
- February 2011 (2)
- January 2011 (3)
- December 2010 (7)
- November 2010 (1)
- October 2010 (1)
- September 2010 (1)
- August 2010 (3)
- July 2010 (4)
- May 2010 (1)
- April 2010 (2)
- March 2010 (2)
- February 2010 (2)
- January 2010 (3)
- December 2009 (3)
- November 2009 (8)
- October 2009 (7)
- September 2009 (5)
- August 2009 (4)
- July 2009 (6)
- June 2009 (6)
- May 2009 (14)
- January 1970 (1)
Copyright(C) B-Blog project All rights reserved.