--- title: SPI categories: STM32, Communcation_Protocol, SPI ... Introduction ============ - SPI是一種4線同步序列資料協定,適用於可攜式裝置平臺系 - 串列外設介面一般是4線,有時亦可為3線 , 可連接memory , RTC , ADC ,DAC …etc .. image:: /single_slave.png System Overview =============== - SPI(Serial Peripheral Interface)為主從式同步串列通訊,可分為單工/半雙工/全雙工, * 單工:線路上的訊號只能做單向傳送 * 半雙工:線路上的訊號可以雙向傳送 , 但是不能同時傳送 * 全雙工:線路上的訊號可以同時雙向傳送 * 同步:傳送端和接收端共用同一個CLOCK - 所有的傳輸都會根據一個共同的頻率訊號 , 此頻率訊號產生自”主控裝置(Master端)”, 從屬裝置(Slave端)會用此頻率訊號來對收到的位串流進行同步 - 如果有多個周邊晶片被連到同一個SPI介面 , 主控裝置能透過SS pin腳的電位高低來選擇接收資料的周邊裝置 .. image:: /three_slaves.png SPI features(STM32) =================== - Full-duplex synchronous transfers on three lines - Simplex synchronous transfers on two lines with or without a bidirectional data line - 8- or 16-bit transfer frame format selection - Multimaster mode capability - dynamic change of master/slave operations - 8 master baud rate prescalers - SPI TI / motorola mode - MSB-first or LSB-first - Hardware CRC - DMA capability .. BIDI mode / RXONLY (p. 801) .. I2S feature Device overview =============== .. image:: /stm32f40x_block_diagram.png STM32F40x block diagram (STM32F407xx datasheet p.18) - 3 SPI的傳輸速度 * SPI1: 42Mbits/s * SPI2 & SPI3: 21 Mbits/s - AHB (Advanced High-performance Bus) 主要用於高性能模組間的連接 - APB1 APB2 (Advanced Peripheral Bus) 主要用於低帶寬的周邊模組間的連接 - AHB/ APB1 , AHB/ APB2 : bridge 用來做AHB協議到APB1/APB2協議的轉換 .. Datasheet: p.32 SPI functional description ========================== .. image:: /embedded/SPI/SPI block diagram.png SPI block diagram [#]_ .. [#] `STM32F407xx Reference Manual `_ p.661 general ------- .. 介紹 pin - MISO: SPI設為master,此腳接收從slave傳來的資料。SPI設為slave,此腳傳送資料到master。 - MOSI: SPI設為master,此腳傳送資料到slave。SPI設為slave,此腳接收從master傳來的資料。 - SCK: 由master提供clock輸出給slave當成通訊clock - NSS: Slave select. 該腳可以被master當做要和那個slave做構通 * software mode(SSM=1) * 透過SPI_CR1中的SSI選擇slave * hardware mode(SSM=0) * Output enable(SSOE=1):only in master mode * Output disable(SSOE=0): * master mode:allows multimaster * slave mode: 低電位時代表被選為slave,其他時候保持高電位。 .. image:: /embedded/SPI/single master _ single slave application.png SPI single master/single slave application [#]_ .. [#] `STM32F407xx Reference Manual `_ p.662 .. 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[#]_ .. [#] `STM32F407xx Reference Manual `_ p.664 SPI_CR1 中有兩個 bits CPOL 和 CPHA 控制取值的時間關係,總共有4種組合。 - CPOL(clock polarity) 決定閒置時 clock 的電位。 * CPOL = 0 表閒置時為低電位。 * CPOL = 1 表閒置時為高電位。 - CPHA(clock phase) 決定在 clock 的哪個 edge 取值。 * CPHA = 0 表示在第一個 edge (Rising,when CPOL=0.Falling,when CPOL=1.)取值。 * CPHA = 1 表示在第二個 edge (Rising,when CPOL=1.Falling,when CPOL=0.)取值。 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 .. DMA support ----------- Code section ============ .. image:: /DSC_0448.jpg Demo影片: http://youtu.be/ciwYDsaJIYE 完整程式碼: .. 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; //push-pull 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); /* 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); /* 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; if(action == 1) //Led On SPI_I2S_SendData(SPI1,LED_ON); else //Led Off SPI_I2S_SendData(SPI1,LED_OFF); 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); STM_EVAL_LEDOff(LED4); } else { RemoteLED_OnOff(1); bLedOnOff = 1; STM_EVAL_LEDOn(LED3); STM_EVAL_LEDOn(LED4); } 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; } Question question ================= 同步非同步之定義: --------------- - 非同步串列傳輸 傳送端與接收端只需約定是以X速率(clock rate)來傳輸, 接收端的接收時脈(Receiver Clock,RxC)產生方式和傳送者的位元傳輸時脈(Transmitter Clock,TxC)是互相獨立無關的 此種傳輸方式是允許傳送與接收時脈的頻率不那麼同步(允許一定程度的誤差)的情況下進行,故稱為非同步傳輸 .. image:: /asynchronization.png - 同步串列傳輸 對接收端而言並沒有自己的時脈產生電路,而是依據傳送端送過來的時脈來接收資料 (即傳送端和接收端共用同一個CLOCK),傳送端以一條導線送出資料,同時以另一條導線送出傳送時脈,提供接收端之同步訊號 .. image:: /synchronization.png SPI通訊過程 ----------- .. image:: /TXE_RXNE_BSY behavior in Master _full-duplex mode(P.672).png (STM32F407xx Reference ManualP.672) - 當data寫到master的SPI_DR(TX Buffer)時(TXE=0), 傳輸開始 - master傳送第一個bit的時候,剩下的7個bit會被並行load到shift register中(TXE=1)[同時,slave端的SPI_DR裡的data並行被load到 shift register中] - 然後這些bits會經由MOSI依序傳輸到slave的shift register裡 [同時,master的shift register也從MISO一個個收到slave傳來的8個bit] - master的shift register收到8個bits後 , 會將這8bits並行寫入SPI_DR(RX buffer) (RXNE=1) 為什麼TXE和RXNE 要 set by hardware , reset by software? ------------------------------------------------------ - 和SPI資料傳輸流程有關係,當寫資料到SPI_DR時(clear the TXE flag),等到TXE=1時再將下一筆要傳的資料寫入SPI_DR。RXNE=1時,讀取SPI_DR中的資料,並且清除RXNE flag。 為什麼要有CPOL,CPHA分高地電位? ---------------------------- - 資料傳輸時,Master與slave需要設定相同的``CPOL``和``CPHA``定義取資料的時間點,確保資料正確的傳輸。 Bus和prescaler的關係 ------------------- - The maximum allowed frequency of the high-speed ``APB2`` domain is 84 MHz. Themaximum allowed frequency of the low-speed ``APB1`` domain is 42 MHz,在SPI—CR1的BR[2:0]中設定除頻。 shift register 除了shift 資料以外,還有什麼作用? --------------------------------------------- - 預先定義好MSB與LSB的排放位置。 為什麼 slave的NSS要接地,master的不用? ----------------------------------- - 因為如果在hardware mode下,master nss被pull low的話, master mode 會發生錯誤而slave mode 的 nss 要保持低電位 slave mode 才會動作 所以要接地 master和slave在設定上有什麼不同? ----------------------------- - master * 要用BR[2:0]來選baud rate * hardware 模式下 NSS pin 要保持高電位,software 模式下 ssi=1 - slave * 不能選baud rate * hardware 模式下 NSS pin 要保持低電位,software 模式下 ssi=0 Error flags ----------- - Master mode fault (MODF) * when the master device has its NSS pin pulled low (in NSShardware mode) or SSI bit low (in NSS software mode), this automatically sets the MODF bit. - Overrun condition * An overrun condition occurs when the master device has sent data bytes and the slave device has not cleared the RXNE bit resulting from the previous data byte transmitted. - CRC error * The CRCERR flag in the SPI_SR register is set if the value received in the shift register does not match the receiver SPI_RXCRCR value. 怎麼知道要使用哪些腳位? --------------------- .. image:: /alternate function mapping.png Alternate function mapping[#]_ .. [#] `Datasheet `_ p.61 .. image:: /Alternate function mapping continued.png STM32F407xx Datasheet p.64 為何要定義為low-active? ---------------------- 當系統一有電源時,此時所有的Pin腳會由Low Level 轉換成系統設定的High Level,Slave device 會因為這樣而 誤以為它是被觸發而開始動作.為了避免這類的問題發生通常chip select都會設定Low-Active. 在 (最高速 BUS) 上最低速的裝置為何? -------------------------------- 這樣取決於STM32所可以接受的通訊方式,如果以STM32F4來講,最低速的應該是USART。 有沒有 SPI 串接的可能? --------------------- 可參考`SPI - Serial Peripheral Interface`_ 這種做法是用在Master的Chip Select腳位很少的情況所使用的。這樣也可以節省硬體的線路,不用在Layout 多餘的線路。 但這必須要Master 與 Slave軟體上的搭配才可行。 在設計 LA 時,要怎麼判斷何 MOSI, MISO? ------------------------------------ 因為MASTER的MOSI是主動發送訊號線,所以可以設定LA查看哪一條線被先傳送訊號,就可以判定MOSI。 為何不能一次和多個 slave 端通訊? ------------------------------ 因為會造成多個Slave會一起傳送資料,這樣會造成匯流排互相搶資源,而造成Master會誤判資料。Master同一時間只能與一個Slave作資料的溝通。 Why we need CRC? -------------------- CRC(Cyclic redundancy check),是一種錯誤偵測的方法。是防止在傳輸資料或儲存資料過程當中因為意外事件而造成原始資料變動,所設計的方法。 只要在傳輸資料後面加入已經運算出CRC碼,傳送給對方。對方收到後,透過原始資料運算出CRC碼,比對對方所寫入的CRC碼是否一致就可以知道資料有沒 有因為意外事件而造成資料的變動。 Speed rate of Motion sensor ------------------------------ LIS302DL 資料傳送速度是100Hz 或 400Hz。 Reference ========= - `Serial Peripheral Interface Bus - Wikipedia, the free encyclopedia `_ - `Shift register- Wikipedia, the free encyclopedia `_ - `STM32F407xx Datasheet`_ - `STM32F407xx Reference Manual`_ - `Synchronization Asynchronization `_ .. need improve .. _`Reference manual`: http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/DM00031020.pdf .. https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi/receiving-data