--- 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 `_ 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 ... `_ 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 ... `_ 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 ... `_ 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; } 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 `_ 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 `_ - Reference manual: `STM32F405xx, STM32F407xx , STM32F415xx and ... `_ - 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 `_ .. need improve .. _`Reference manual`: http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/DM00031020.pdf