版本 86453b53518c8c86545ba045a3588707db005c7d
Changes from 86453b53518c8c86545ba045a3588707db005c7d to 9203d0ad4cb023ab42ba410d1646e710981c6ae0
---
title: SPI
categories: STM32, Communcation_Protocol, 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
- 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
===
Questions
=========
怎麼知道要使用哪些腳位?
------------------------
.. 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
為何要定義為 low-active?
--------------------------
在 (最高速 BUS) 上最低速的裝置為何?
--------------------------
有沒有 bypass 的可能?
----------------------
在設計 LA 時,要怎麼判斷何 MOSI, MISO?
---------------------------------------
為何不能一次和多個 slave 端通訊?
---------------------------------
.. Why we need CRC?
.. speed rate of Motion sensor
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