分享到plurk 分享到twitter 分享到facebook

版本 86de64f13b2fbaec184504844ba8eba0b2ea0fe7

SPI

Overview

SPI 為資料傳輸協定,有著以下幾點特色:

  • 同步式
  • 全雙工
    • 可以半雙工或單工
  • 主從式
    • dynamic change of master/ slave
    • 多個 master .. example
  • 8 master baud rate prescalers
  • slave mode frequency
  • SPI TI / motorola mode
  • MSB-first or LSB-first
  • Hardware CRC .. Why we need CRC? .. speed rate of Motion sensor
  • DMA capability

.. BIDI mode / RXONLY (p. 801) .. I2S feature

Device overview

.. image:: /embedded/SPI/STM32F40x block diagram.png STM32F40x block diagram [#]_

.. [#] DS8626: ARM Cortex-M4 32b MCU+FPU, 210DMIPS, up to 1MB Flash/192+4KB RAM, USB OTG HS/FS, Ethernet, 17 TIMs, 3 ADCs, 15 comm. interfaces & camera <http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/DM00037051.pdf>_ p.18

  • 3 SPI
    • SPI1: 37.5 Mbits/s
    • SPI2 & SPI3: 21 Mbits/s

.. Datasheet: p.32

SPI functional description

.. image:: /embedded/SPI/SPI block diagram.png SPI block diagram [#]_

.. [#] STM32F405xx, STM32F407xx , STM32F415xx and ... <http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/DM00031020.pdf>_ p.793

general

.. 介紹 pin

  • MISO: Master In / Slave Out data
  • MISO: Master Out / Slave In data
  • SCK: Serial Clock ( Master -> Slave )
  • NSS: (optional) select slave, low-driven

.. image:: /embedded/SPI/single master _ single slave application.png SPI single master/single slave application [#]_

.. [#] STM32F405xx, STM32F407xx , STM32F415xx and ... <http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/DM00031020.pdf>_ p.794

.. baud rate ref menual p.834

.. Slave select(NSS) pin management ’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

Clock phase and clock polarity ’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

.. image:: /embedded/SPI/SPI Dota clock timing diagram.png Data clock timing diagram[#]_

.. [#] STM32F405xx, STM32F407xx , STM32F415xx and ... <http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/DM00031020.pdf>_ p.796

SPI_PR1 中有兩個 bits CPOL 和 CPHA 控制取值的時間關係,

  • CPOL(clock polarity) 決定閒置時 clock 的電位,CPOL = 0 表閒置時為低電位。
  • CPHA(clock phase) 決定在 clock 的哪個 edge 取值, CPHA = 0 表示在第一個 edge (到不同電位時)取值。

Data format ’’’’’’’’’’’

  • LSB-first or MSB first
  • 一次傳送的資料量為 8-bit 或 16-bit

.. confiurataion in master mode ——————————-

.. confiurataion in slave mode ——————————-

half-duplex communication

  • BIDMODE 判斷是否為全雙工
  • RXONLY 判斷半雙工時為傳送端或接收端。

SPI 可以只做半雙工,在半雙工時,沒用到的另一隻 pin 可作為一般的 GPIO 使用。

.. RXONLY 在 BIDMODE 時有作用否?

.. data transmission and reception procedures —————————————— Start sequence in master mode - In full-duplex (BIDIMODE=0 and RXONLY=0):: begins when data are written into the SPI_DR register (Tx buffer) - In unidirectional receive-only mode (BIDIMODE=0 and RXONLY=1):: begins as soon as SPE=1 TODO: not finished

.. CRC calculation —————

Status flag

寫程式時必須要持續監視的三個 status flag:

Tx buffer empty flag(TXE) Rx buffer not empty(RXNE)

BUSY flag:用以指示SPI 正忙於傳輸的 flag

.. TODO busy flag

Disabling SPI

.. DMA support ———–

Code section

完整程式碼:

.. code-block:: c

git clone git://gitcafe.com/EricCheng/stm32_spi_demo.git
cd stm32_spi_demo/

自定義變數:

.. code-block:: c

unsigned char readData[32];
unsigned char writeData[32];
int rxCounter, txCounter;

初始化 SPI:

  • Master:

.. code-block:: c

    // enable clock for used IO pins
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);

    // enable SPI2 peripheral clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    /* configure pins used by SPI1
     * PE7 = NSS(CS)
     * PA5 = SCK
     * PA6 = MISO
     * PA7 = MOSI
     \*/
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // connect SPI2 pins to SPI alternate function
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 \|GPIO_Pin_3;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &GPIO_InitStruct);	
    

    GPIO_SetBits(GPIOE,GPIO_Pin_3);	//Let the LIS302DL disable
    GPIO_SetBits(GPIOE,GPIO_Pin_7); //Pull High

    /* configure SPI1 in Mode 0
    * CPOL = 0 --> clock is low when idle
    * CPHA = 0 --> data is sampled at the first edge
    \*/
    SPI_I2S_DeInit(SPI1);
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // set to full duplex mode, seperate MOSI and MISO lines
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // clock is low when idle
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // data sampled at first edge
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft|SPI_NSSInternalSoft_Set ; // set the NSS management to internal and pull internal NSS high
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // SPI frequency is APB2 frequency / 4
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
    SPI_Init(SPI1, &SPI_InitStruct);
    SPI_Cmd(SPI1, ENABLE); // enable SPI1	
  • Slave mode

.. code-block:: c

    // enable clock for used IO pins
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);

    // enable SPI2 peripheral clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    /* configure pins used by SPI2
    * PA15 = NSS(CS)
    * PA5 = SCK
    * PA6 = MISO
    * PA7 = MOSI
    \*/
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6 | GPIO_Pin_7 |  GPIO_Pin_15;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // connect SPI2 pins to SPI alternate function
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_SPI1);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &GPIO_InitStruct);	
    

    GPIO_SetBits(GPIOE,GPIO_Pin_3);		//Let the LIS302DL disable

    /* configure SPI1 in Mode 0
    * CPOL = 0 --> clock is low when idle
    * CPHA = 0 --> data is sampled at the first edge
    \*/
    SPI_I2S_DeInit(SPI1);
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // set to full duplex mode, seperate MOSI and MISO lines
    SPI_InitStruct.SPI_Mode = SPI_Mode_Slave; // transmit in master mode, NSS pin has to be always high
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // clock is low when idle
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // data sampled at first edge
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft ; // set the NSS management to internal and pull internal NSS high
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // SPI frequency is APB2 frequency / 4
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
    SPI_Init(SPI1, &SPI_InitStruct);
    SPI_Cmd(SPI1, ENABLE); // enable SPI1	

初始化 LED:

.. code-block:: c

/in  libstm/Utilities/STM32F4-Discovery/discoveryf4utils.c
STM_EVAL_LEDInit(LED4);
STM_EVAL_LEDInit(LED3);
STM_EVAL_LEDInit(LED5);
STM_EVAL_LEDInit(LED6);  

STM_EVAL_LEDOff(LED4);
STM_EVAL_LEDOff(LED3);
STM_EVAL_LEDOff(LED5);
STM_EVAL_LEDOff(LED6);

初始化按鍵:

.. code-block:: c

    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    /* Here the GPIOA module is initialized.
    * We want to use PA0 as an input because
    * the USER button on the board is connected
    * between this pin and VCC.
    \*/
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;	// we want to configure PA0
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; // we want it to be an input
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//this sets the GPIO modules clock speed
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // this sets the pin type to push / pull (as opposed to open drain)
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; // this enables the pulldown resistor --> we want to detect a high level
    GPIO_Init(GPIOA, &GPIO_InitStruct);	// this passes the configuration to the Init function which takes care of the low level stuff

Master 設定 - 傳送訊號

.. code-block:: c

uint32_t TimeOutLed=10000;
GPIO_SetBits(GPIOE,GPIO_Pin_7);  //Chip Select

if(action == 1)	//Led On
        SPI_I2S_SendData(SPI1,LED_ON);
else //Led Off
        SPI_I2S_SendData(SPI1,LED_OFF);

GPIO_ResetBits(GPIOE,GPIO_Pin_7);	

while(SPI_I2S_GetFlagStatus(SPI1,SPI_FLAG_TXE) == RESET);
return 1;
  • task:

.. code-block:: c

void pb_task(void \*pvParameters)
{
    uint8_t bNewLedOnOff=0 ,bOldLedOnOff=0,bLedOnOff=0;

    while(1)
    {
        if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)== Bit_SET)  //User Button Pressed
        {
            bNewLedOnOff = 1;
            if(bNewLedOnOff ^ bOldLedOnOff)
            {
                if(bLedOnOff)
                {
                    RemoteLED_OnOff(0);
                    bLedOnOff = 0;
                    STM_EVAL_LEDOff(LED3);
                }
                else
                {
                    RemoteLED_OnOff(1);
                    bLedOnOff = 1;
                    STM_EVAL_LEDOn(LED3);
                }
                bOldLedOnOff = bNewLedOnOff;
            }			
        }
        else 
                bOldLedOnOff = 0;
    }

}

Slave 設定: - 接收訊息

.. code-block:: c

uint8_t rcv_tmp = 0;
uint32_t TimeOutLed = 10000;
while(1)
{
    while(SPI_I2S_GetFlagStatus(SPI1,SPI_FLAG_RXNE)== RESET);
    rcv_tmp = (uint8_t)SPI_I2S_ReceiveData(SPI1);
    if(rcv_tmp == LED_ON)
    {
        STM_EVAL_LEDOff(LED4);
        STM_EVAL_LEDOff(LED3);
        STM_EVAL_LEDOff(LED5);
        STM_EVAL_LEDOff(LED6);
    }
    else if(rcv_tmp == LED_OFF)
    {
        STM_EVAL_LEDOn(LED4);
        STM_EVAL_LEDOn(LED3);
        STM_EVAL_LEDOn(LED5);
        STM_EVAL_LEDOn(LED6);
    }
}

main

.. code-block:: c

int main(void)
{
        uint8_t  pressed=0,new_button_state,last_button_state;
        init_SPI();
        init_LED();
        init_PB();

#ifdef MASTER
        STM_EVAL_LEDOff(LED4);
        STM_EVAL_LEDOff(LED3);
        STM_EVAL_LEDOn(LED5);
        STM_EVAL_LEDOff(LED6);
        xTaskCreate(pb_task,
                    (signed portCHAR \*) "Push Button Task",
                    512 /* stack size \*/, NULL, tskIDLE_PRIORITY + 2, NULL);
#else
        xTaskCreate(spi_recv_msg_task,
                    (signed portCHAR *) "SPI Recv Task",
                    512 /* stack size \*/, NULL, tskIDLE_PRIORITY + 2, NULL);
#endif
        /* Start running the tasks. \*/
        vTaskStartScheduler();
        STM_EVAL_LEDOff(LED5);
        return 0;
}

FAQ

怎麼知道要使用哪些腳位?

.. image:: /embeded/SPI/GPIO Alternative function PortA.png Alternate function mapping[#]_

.. [#] DS8626: ARM Cortex-M4 32b MCU+FPU, 210DMIPS, up to 1MB Flash/192+4KB RAM, USB OTG HS/FS, Ethernet, 17 TIMs, 3 ADCs, 15 comm. interfaces & camera <http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/DM00037051.pdf>_ p.58

Reference

  • Serial Peripheral Interface Bus - Wikipedia, the free encyclopedia <http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus>_
  • Reference manual: STM32F405xx, STM32F407xx , STM32F415xx and ... <http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/DM00031020.pdf>_
  • Datasheet: DS8626: ARM Cortex-M4 32b MCU+FPU, 210DMIPS, up to 1MB Flash/192+4KB RAM, USB OTG HS/FS, Ethernet, 17 TIMs, 3 ADCs, 15 comm. interfaces & camera <http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/DM00037051.pdf>_

.. need improve

.. _Reference manual: http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/DM00031020.pdf