hantas's blog

ブログ移転しました→ http://blog.taniho.net/

STM32でのSPI覚え書き

Qt記事少しの間だけおやすみします。

先日ついにハーフマウスの基板が届いたので,かかりっきりでいました。 こいつ,めちゃくちゃ可愛いです。(合わせて書き込み充電基板も発注しておきました)

f:id:hantas:20151212140622j:plain

さて,困難な問題は分割して取り組もう,ということで割り込みもDMAも使わないSPI通信に成功したのでログを残しておきます。 今回使用したモジュールはSPIモード3での通信を行っています。 送信8bit,受信8bitでWHO_AM_Iが帰ってくることまで確認しています。

主要部分のコードは次の通り。

// 設定

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA, GPIO_Pin_15);

    GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SPI3);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SPI3);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SPI3);

    SPI_InitTypeDef SPI_InitStructure;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSSInternalSoft_Set | SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI3, &SPI_InitStructure);
    SPI_Cmd(SPI3, ENABLE);

// 送信,受信

    uint16_t ret;
    GPIO_ResetBits(GPIOA, GPIO_Pin_15); // CSをセット
    while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET); // 送信可能になるまで待つ
    SPI_I2S_SendData(SPI3, 0x8F); // 送信(今回はWHO_AM_Iの8bitコマンド)
    while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET); // 受信可能になるまで待つ
    ret = SPI_I2S_ReceiveData(SPI3); // 空データを受信する

    while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI3, 0x00); // 空データを送信する
    while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET);
    ret = SPI_I2S_ReceiveData(SPI3); // ほしいデータを受信する
    while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET);
    GPIO_SetBits(GPIOA, GPIO_Pin_15); //CSをリセット

という感じです。 今回詰まったのは何点かあって,

  • CPOLとCPHAがわかりにくい →せいぜい4通りなので全部試せばいいよね(オイ)
  • NSS(CS)の使い方がわからない →手動で切り替える
  • 0が帰ってくる →全二重通信なので,送信時に送られてきた空データも受信してあげる必要がある

以上がポイントでした。

CPOLとCPHAの件ですが,こんなに乱暴では申し訳ないので表にまとめておきます。

SPI_CPOL SPI_CPHA CKP CKE Mode
SPI_CPOL_Low SPI_CPHA_1Edge 0 1 0
SPI_CPOL_Low SPI_CPHA_2Edge 0 0 1
SPI_CPOL_High SPI_CPHA_1Edge 1 1 2
SPI_CPOL_High SPI_CPHA_2Edge 1 0 3

次は送受信完了割り込みと,送信準備完了割り込みを使いながら通信できるように頑張ってみます。