今日やっと SPI ペリフェラルの使い方が本当に分かった。以下の代表的な 3 つの API で考えてみる。送信:HAL_SPI_Transmit_DMA(Spi, RBuf, ui32Len)受信:HAL_SPI_Receive_DMA(Spi, RBuf, ui32Len)送受信:HAL_SPI_TransmitReceive_DMA(Spi, RBuf, ui32Len) これまでに遭遇した問題SPI マスターとして動かすとき、受信 API を呼んでも実際にはデータを受信できなかった。そこで、受信処理はすべて 送受信 API を使っていた。SPI を使う機会が少なく、機能も単純だったので、なぜなのか深く考えたことはなかった。ただ API の実装に何か問題があるのだと思っていた(下位層の上にさらに互換レイヤーを重ねていたから)。今日 spi-flash ドライバを書き直して、この問題を改めて考えた。要するにSPI はクロックラインのエッジでシフトを行うハードウェア通信だ。クロックがなければ、マスターもスレーブも実際にはデータを送らない。独自の変な非標準実装でない限りは。そして通常、クロックを生成できるのはマスターだけだ。そうなると問題は明らかだ。少なくとも STM32 では、送信時だけクロックが生成される。だから受信だけのデータでも 送受信 API を使わなければならない。受信 API が意味を持つのはスレーブとして動くときか、GPT が言っていたように外部ハードウェアがクロックを生成する場合だけだ(それは怖すぎる。本当にそんな使い方があるのか)。考察SPI 通信の特性は、絶え間ない双方向通信には向いているが、一方向の通信には向いていない。マスターとして大量の読み出しを行おうとすると問題になる。送信ラインにも同じサイズのバッファ領域を確保する必要があるからだ。STM32 の HAL 実装の問題かもしれないが、深掘りはしていない。本当に遭遇したらそのときに考えよう。 Copilot との会話(SPI クロックと受信実装の議論)日付:2026-01-21 · 参加者:stbanana(ユーザー)、Copilot(アシスタント)stbanana特定の API を呼び出したときにもクロックを生成できる設定はありますか? その場合も送受信 API を使う必要があるなら、 読み取りだけが必要なときでも、送信ラインが依然として同じサイズのバッファを占有する必要があり、問題が生じるかもしれません。 Copilot完了:SPI 読み取り方法の提案できます。ただし、「ハードウェアが自動でクロックを送るかどうか」と「ソフトウェアが明示的にクロックを送るか」の 2 つを区別する必要があります。現在の STM32H743 + HAL 構成を踏まえると、結論は次のとおりです:1)受信だけでも自動的にクロックは生成される?STM32H7 のマスターモードでは、デフォルトではできません。HAL_SPI_Receive_DMA() は RX DMA を有効にするだけで、TX を能動的に駆動しないため、通常はクロックが生成されません。一部のフレームワーク / ライブラリ関数は、RX-only のときに自動でダミーを送る場合があります(現在のコードにはありません)。2)実現可能な方法(推奨)HAL_SPI_TransmitReceive_DMA() を使い続け、ダミーの TX バッファを用意します。このバッファは大きくする必要はなく、動的確保も不要です。選択肢は次のとおり: 常駐の TX バッファ(たとえば常に0xFF など)をブロック単位で繰り返し送信する;1 バイト + DMA で、TX DMA のMemInc をオフにして同じバイトを繰り返し送信する。 3)「バッファを占有すると問題になる?」通常は問題になりません。fspiTxBuf がすでにあるので、ダミーを空送信するのに使えばよいです。重要なのは、CS 期間中はクロックを出し続ける必要があるため、TX が必ず存在しなければならないことです。大きなバッファを占有するのが心配なら、次のようにできます: ブロック単位で送受信する;ダミーバッファを固定 1 バイトにして使い回す。 推奨する実装方法(現在のコードと最も互換性が高い)既存のTransmitReceive + ダミーの方式を維持する。将来、ある API が自動的にクロックを送るようになったら、FlashSpiReceivePort() を内部でSpiTransmitReceive() を呼ぶ形に変更し、 内部で固定のダミーバッファを使えば、呼び出し側に TX を意識させずに済む。stbananaいや、今の私のコードが対象にしているのはspiflash だけです。これは統一された汎用的なやり方ではありません。spistdio は最も汎用的な使い方を提供することを目的としています。spiflash の prot 関数のラッパーはもう気にしないで、STM32 の SPI 実装に注目してください。 Copilot完了:リクエストを確認し、回答方針を整理分かりました。STM32 の SPI 実装そのものに焦点を当てます。以下、考え方と結論を示します。考え方STM32H7 の SPI マスターモードでのクロックの発生源を説明する;HAL_SPI_Receive_DMA がマスターモード / スレーブモードでどう動作が異なるかを説明する;「汎用 SPI stdio」レベルの合理的な設計提案を行う。結論STM32 のマスターモードでは、クロックはマスターが能動的に生成します。通常、SCK が駆動されるのは TX または全二重操作のときだけです。HAL_SPI_Receive_DMA() はマスターモードではクロックの生成を保証しないため、汎用の「読み取り専用」インターフェースとしては使えません (ハードウェア / ライブラリが RX-only の自動クロックに対応している場合を除く)。現在の構成にはそれは含まれていません。スレーブモードでは、クロックが外部マスターから来るため、HAL_SPI_Receive_DMA() は意味があります。「汎用 SPI stdio」への提案SpiReceive() を API の意味論として残しつつ、マスターモードでは内部を「ダミー TX + RX」(全二重)にしてクロックを保証する;スレーブモードでは、SpiReceive() は本当の RX-only として動作させる;こうすることで、インターフェースの対外仕様は一定に保たれ、意味論も安定し、内部でマスター / スレーブモードに応じてクロック戦略を選べます。必要であれば、既存のspistdio.c の枠組みに基づいて、「マスター / スレーブ自動適応」の SpiReceive() 実装案を示せます(具体的な spiflash のロジックには触れません)。stbananaそれだと大きな改善にはならない気がするので、しばらくは現状のままでお願いします。
SPI ペリフェラルのラインクロック生成
今日やっと SPI ペリフェラルの使い方が本当に分かった。
以下の代表的な 3 つの API で考えてみる。
これまでに遭遇した問題
SPI マスターとして動かすとき、
受信 APIを呼んでも実際にはデータを受信できなかった。そこで、受信処理はすべて送受信 APIを使っていた。SPI を使う機会が少なく、機能も単純だったので、なぜなのか深く考えたことはなかった。ただ API の実装に何か問題があるのだと思っていた(下位層の上にさらに互換レイヤーを重ねていたから)。
今日 spi-flash ドライバを書き直して、この問題を改めて考えた。
要するに
SPI はクロックラインのエッジでシフトを行うハードウェア通信だ。クロックがなければ、マスターもスレーブも実際にはデータを送らない。独自の変な非標準実装でない限りは。
そして通常、クロックを生成できるのはマスターだけだ。
そうなると問題は明らかだ。少なくとも STM32 では、送信時だけクロックが生成される。だから受信だけのデータでも
送受信 APIを使わなければならない。受信 APIが意味を持つのはスレーブとして動くときか、GPT が言っていたように外部ハードウェアがクロックを生成する場合だけだ(それは怖すぎる。本当にそんな使い方があるのか)。考察
SPI 通信の特性は、絶え間ない双方向通信には向いているが、一方向の通信には向いていない。マスターとして大量の読み出しを行おうとすると問題になる。送信ラインにも同じサイズのバッファ領域を確保する必要があるからだ。
STM32 の HAL 実装の問題かもしれないが、深掘りはしていない。本当に遭遇したらそのときに考えよう。
できます。ただし、「ハードウェアが自動でクロックを送るかどうか」と「ソフトウェアが明示的にクロックを送るか」の 2 つを区別する必要があります。現在の STM32H743 + HAL 構成を踏まえると、結論は次のとおりです:
を使い続け、ダミーの TX バッファを用意します。
分かりました。STM32 の SPI 実装そのものに焦点を当てます。以下、考え方と結論を示します。
がマスターモード / スレーブモードでどう動作が異なるかを説明する;
はマスターモードではクロックの生成を保証しないため、汎用の「読み取り専用」インターフェースとしては使えません
(ハードウェア / ライブラリが RX-only の自動クロックに対応している場合を除く)。現在の構成にはそれは含まれていません。
を API の意味論として残しつつ、マスターモードでは内部を「ダミー TX + RX」(全二重)にしてクロックを保証する;