参考にした記事
STM32CubeのExample「SPI_FullDuplex_ComPolling」 <STM32Cube>\Repository\STM32Cube_FW_F4_V1.24.0\Projects\STM32F413ZH-Nucleo\Examples\SPI\SPI_FullDuplex_ComPolling
STM32CubeのExample「SPI_FullDuplex_ComDMA」 <STM32Cube>\Repository\STM32Cube_FW_F4_V1.24.0\Projects\STM32F413ZH-Nucleo\Examples\SPI\SPI_FullDuplex_ComDMA
実行環境
- Nucleo-F446RE
- STM32CubeMX Version 5.1.0
- System Workbench for STM32 - C/C++ Embedded Development Tools for MCU Version: 2.8.1.201903050911
SPI通信(ポーリング)
STM32CubeのExampleはボードを2枚使って互いに通信させていますが、SPI送信波形を見るだけにして一番シンプルな形でテストしました。
プロジェクト:
https://github.com/ryood/STM32Cube_Test/tree/master/SW4STM32/Nucleo-F446_SPI_SoftNSS_Test1
STM32CubeMXの設定
ピンのアサイン
[SPI1]-[Mode]
Mode: Transmit Only Master
Hardware NSS Signal: Disable
[Parameter Settings]
Clock Parameters
Prescaler (for Baud Rate): 64
Baud Rate: 1000.0KBits/s
SPI1はデフォルトでは、PB3がSYS_JTDO-SWOに割り当てられていて有効化できませんが、Pinout viewでPB3にSPI1_SCKを割り当ててやれば、有効化できます。
モードは送信のみの「Transmit Only Master」にしました。MISOが割り当てられず、SCKとMOSIの2線です。
CSはソフトウェア制御にしてPinout viewでPB4をGPIO_Outputに指定しました。
SPIの信号線 | STM32のピン名 | Arduinoヘッダのピン名 |
---|---|---|
SCK | PB3 | D3 |
MOSI | PB5 | D4 |
CS | PB4 | D5 |
クロック設定
SPIクロックをちょうど1MHzにしたかったので、HCLKを2のN乗の128MHzにしました。またクロック源(PLL Source Mux)を内蔵RCのHSIではなく、NucleoボードのST-Link側に乗っている8MHzの水晶から取るために、HSEに切り替えています。
SW4STM32でコードを追加(一部抜粋)
/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ uint8_t data[2] = { 0x55, 0xAA }; HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET); if (HAL_SPI_Transmit(&hspi1, data, 2, 100) != HAL_OK) { Error_Handler(); } HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET); } /* USER CODE END 3 */
0x55, 0xAAを送信しています。ビット列にすると「01010101:10101010」になります。
SPI通信のようす
ch1:MOSI(D4) ch2:SCK(D3)
SPI通信(DMA)
こちらも送信のみですが、設定は全二重(Full-Duplex Master)にしました。
プロジェクト:
https://github.com/ryood/STM32Cube_Test/tree/master/SW4STM32/Nucleo-F446_SPI_DMA_Test1
STM32CubeMXの設定
ピンのアサイン
[SPI1]-[Mode]
Mode: Full-Duplex Master
Hardware NSS Signal: Disable
[Parameter Settings]
Clock Parameters
Prescaler (for Baud Rate): 64
Baud Rate: 1000.0KBits/s
Transmit Only Masterの場合より1ピン増えて、MISOが追加されます。CSはPinout viewでPB10をGPIO_Outputに指定しました。
SPIの信号線 | STM32のピン名 | Arduinoヘッダのピン名 |
---|---|---|
SCK | PB3 | D3 |
MISO | PB4 | D5 |
MOSI | PB5 | D4 |
CS | PB10 | D6 |
DMA Settings
SPIとDMAを紐付ける設定です。
[Add]ボタンを押して、[DMA Request]で[SPI1_TX][SPI1_RX]を追加します。
DMA Request Settings
Mode: Normal
クロックの設定はポーリングの場合と同じです。
SW4STM32でコードを追加(一部抜粋)
/* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define BUFFERSIZE (2) enum { TRANSFER_WAIT, TRANSFER_COMPLETE, TRANSFER_ERROR }; /* USER CODE END PD */
/* USER CODE BEGIN PV */ /* transfer state */ __IO uint32_t wTransferState = TRANSFER_WAIT; /* USER CODE END PV */
DMA転送の状態を示すフラグです。
/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ uint8_t aTxBuffer[BUFFERSIZE] = { 0x55, 0xAA }; uint8_t aRxBuffer[BUFFERSIZE]; wTransferState = TRANSFER_WAIT; /*##-2- Start the Full Duplex Communication process ########################*/ /* While the SPI in TransmitReceive process, user can transmit data through "aTxBuffer" buffer & receive data through "aRxBuffer" */ HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET); if(HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t*)aTxBuffer, (uint8_t *)aRxBuffer, BUFFERSIZE) != HAL_OK) { /* Transfer error in transmission process */ Error_Handler(); } HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET); /*##-3- Wait for the end of the transfer ###################################*/ /* Before starting a new communication transfer, you must wait the callback call to get the transfer complete confirmation or an error detection. For simplicity reasons, this example is just waiting till the end of the transfer, but application may perform other tasks while transfer operation is ongoing. */ while (wTransferState == TRANSFER_WAIT) { } printf("SPI RX: %d\t%d\r\n", aRxBuffer[0], aRxBuffer[1]); } /* USER CODE END 3 */
こちらも0x55、0xAAを送信しています。
ソースのコメントにもありますが、DMA転送の完了をWhileループでフラグを見て待機していますが、本来はうまいことやってDMA転送完了待ち中は他の処理をするべきです。
/* USER CODE BEGIN 4 */ /** * @brief TxRx Transfer completed callback. * @param hspi: SPI handle * @note This example shows a simple way to report end of DMA TxRx transfer, and * you can add your own implementation. * @retval None */ void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { wTransferState = TRANSFER_COMPLETE; } /** * @brief SPI error callbacks. * @param hspi: SPI handle * @note This example shows a simple way to report transfer error, and you can * add your own implementation. * @retval None */ void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) { wTransferState = TRANSFER_ERROR; } /* USER CODE END 4 */
DMA転送完了の割り込みハンドラです。wTransferStateに状態を代入しているだけです。
SPI通信のようす
ch1:MOSI(D4) ch2:SCK(D3)
ch1:MOSI(D4) ch2:SCK(D3)
printf()でUART通信しているので(ブロッキングされる)、SPI通信ごとの間隔が広がっています。
Puttyで受信しているようす
スレーブがつながっていないので受信データは0:0です。
メモ:
NSS(CS)をハードウェア制御できるようですが、マスター側ではソフトウェアでGPIO Writeしてやれば用は済みます。NSSのハードウェア制御の使い方はしばし保留。
0 件のコメント:
コメントを投稿