74-SPI_Slave-Library (STM32F4)

Mit dieser Library kann der STM32F4 als SPI-Slave eingesetzt werden.

Der Slave simuliert ein 256-Byte großes SPI-RAM. Die SPI-Funktionen laufen per Interrupt ab. Es gibt zwei Funktionen für read/write vom internen RAM.

Ein SPI-Master kann über Kommandos ein Byte in das interne RAM vom Slave speichern, ein Byte aus dem RAM vom Slave auslesen oder eine ID-Nummer vom Slave abfragen.
(Der SPI-Mode vom Master und Slave muss dabei gleich sein !!)

Die 3 Kommdos die implementiert sind, sind alle 3 Bytes lang :

1
2
3
4
5
6
7
8
9
10
11
CMD-01 = Read ID
Master : 0x01 ,Dummy,Dummy
Slave  : Dummy,0x32 ,0x07
 
CMD-02 = Write Byte
Master : 0x02 ,Adr  ,Value
Slave  : Dummy,Dummy,Dummy
 
CMD-03 = Read Byte
Master : 0x03 ,Adr  ,Dummy
Slace  : Dummy,Dummy,Value

Im Beispiel wartet der Slave solange, bis ein Master per SPI in die RAM-Adresse 0×00 den Wert 0×12 geschrieben hat, dann wird die grüne LED eingeschaltet.

die SPI-Pins die benutzt werden sollen, müssen im C-File eingetragen werden
(im H-File kann die Größe vom RAM eingestellt werden)

es gibt 3 identische Librarys, getrennt für SPI1, SPI2 und SPI3

im Beispiel wurde SPI2 benutzt mit dieser Pinbelegung :

1
2
3
4
SCK an PB13
MOSI an PB15
MISO an PC14
SlaveSelect an PB12

Voraussetzungen :

1
2
Benutzte Module der CooCox-IDE : GPIO, SPI, MISC
Benutzte Librarys : keine

Enumeration :

1
2
3
4
5
6
7
8
9
10
typedef enum {
  SPI_SMODE_0_MSB = 0,  // CPOL=0, CPHA=0 (MSB-First)
  SPI_SMODE_1_MSB,      // CPOL=0, CPHA=1 (MSB-First)
  SPI_SMODE_2_MSB,      // CPOL=1, CPHA=0 (MSB-First)
  SPI_SMODE_3_MSB,      // CPOL=1, CPHA=1 (MSB-First)
  SPI_SMODE_0_LSB,      // CPOL=0, CPHA=0 (LSB-First)
  SPI_SMODE_1_LSB,      // CPOL=0, CPHA=1 (LSB-First)
  SPI_SMODE_2_LSB,      // CPOL=1, CPHA=0 (LSB-First)
  SPI_SMODE_3_LSB       // CPOL=1, CPHA=1 (LSB-First)
}SPI2_SMode_t;

Funktionen (für SPI2) :

1
2
3
ErrorStatus UB_SPI2_Slave_Init(SPI2_SMode_t mode);       // zum init als SPI-Slave
uint8_t UB_SPI2_Slave_ReadByte(uint8_t adr);             // zum lesen vom RAM
void UB_SPI2_Slave_WriteByte(uint8_t adr, uint8_t wert); // zum schreiben in das RAM

Beispiel :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//--------------------------------------------------------------
// File     : main.c
// Datum    : 01.02.2014
// Version  : 1.0
// Autor    : UB
// EMail    : mc-4u(@)t-online.de
// Web      : www.mikrocontroller-4u.de
// CPU      : STM32F4
// IDE      : CooCox CoIDE 1.7.4
// GCC      : 4.7 2012q4
// Module   : CMSIS_BOOT, M4_CMSIS_CORE
// Funktion : Demo der SPI-Slave-Library
// Hinweis  : Diese zwei Files muessen auf 8MHz stehen
//              "cmsis_boot/stm32f4xx.h"
//              "cmsis_boot/system_stm32f4xx.c"
//--------------------------------------------------------------
 
#include "main.h"
#include "stm32_ub_led.h"
#include "stm32_ub_spi2_slave.h"
 
int main(void)
{
  uint8_t wert;
 
  SystemInit(); // Quarz Einstellungen aktivieren
 
  // init der LEDs
  UB_Led_Init();
 
  // init vom SPI-2 im Slave-Mode
  UB_SPI2_Slave_Init(SPI_SMODE_0_MSB);
 
  while(1)
  {
    // Wert vom RAM an Adresse 0x00 auslesen
    wert=UB_SPI2_Slave_ReadByte(0x00);
 
    // LEDs je nach Inhalt schalten
    if(wert==0x12) {
      UB_Led_On(LED_GREEN);
      UB_Led_Off(LED_RED);
    }
    else {
      UB_Led_On(LED_RED);
      UB_Led_Off(LED_GREEN);
    }
  }
}

Hier die Library zum Download :

ub_stm32f4_spi_slave_v100

Hier der komplette CooCox-Projektordner zum Download :

Demo_74_SPI_Slave


8 Antworten auf 74-SPI_Slave-Library (STM32F4)

  1. sakarin sagt:

    Thank you very much for sharing this,
    I downloaded your example and modified to my stm32f3discovery board and it works. I have question about the NSS pin that I guess it is a CS pin but the NSS pin is disconnected, the SPI2_IRQHandler() still interrupt so, I worry when connect my board to another spi devices, it will has error. Please share how to make the NSS pin as a real CS pin.

    Thanks in advance.

  2. sakarin sagt:

    I sent you an email in the morning, Now I have changed

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

    //////////////////////////////// Code /////////////////////////////////

    // SPI als Alternative-Function mit PullDown
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    //GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

    /////////////////////////////////////////////////////////////////////

    and it seems to be working, the spi slave can send data when the NSS (CS) is LOW.
    Just asking you is it correct?

    Best regards,
    Sakarin

    • admin_ub sagt:

      Hi Sakarin,
      If you have only one slave than your modification is ok.
      But if you have more than one spi-slave on this bus you have to connect
      the SlaveSelect-Pin (PB12 in this demo) with the master !

  3. sakarin sagt:

    Hi,

    Thanks again for your answer.
    Now, I am trying to move the code to use DMA but it does not work. Could you share about the modification to use DMA?

    Best regards,
    Sakarin

    • admin_ub sagt:

      i dont have a spi-dma demo. but ST has one “STM32F4xx_StdPeriph_Examples\SPI\SPI_TwoBoards\SPI_DataExchangeDMA”

  4. sakarin sagt:

    Thank you very much your suggestion. I tried to understand it but it gets data on the main program. Do you have some examples to work on SPI2 with DMA interrupt?

    // SPI2_RX //
    void DMA1_Channel4_IRQHandler(void)
    {

    }

    //SPI2_TX //
    void DMA1_Channel5_IRQHandler(void)
    {

    }

    I wrote my code and it can enter to interrupts above when receive spi message but don’t know how to read the receiving data from spi in debug mode of CooCox.

    void SPI_Slave_and_DMA_Configuration(void)
    {
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef SPI_InitStructure; //Variable used to setup the SPI
    DMA_InitTypeDef DMA_InitStructure; //Variable used to setup the DMA

    // SPI-Clock enable
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

    // Clock Enable der Pins, GPIOB
    RCC_AHBPeriphClockCmd(SPI2DEV.SCK.CLK, ENABLE);
    RCC_AHBPeriphClockCmd(SPI2DEV.MOSI.CLK, ENABLE);
    RCC_AHBPeriphClockCmd(SPI2DEV.MISO.CLK, ENABLE);
    RCC_AHBPeriphClockCmd(SPI2DEV.NSS.CLK, ENABLE);

    // SPI Alternative-Functions mit den IO-Pins verbinden
    GPIO_PinAFConfig(SPI2DEV.SCK.PORT, SPI2DEV.SCK.SOURCE, GPIO_AF_5);
    GPIO_PinAFConfig(SPI2DEV.MOSI.PORT, SPI2DEV.MOSI.SOURCE, GPIO_AF_5);
    GPIO_PinAFConfig(SPI2DEV.MISO.PORT, SPI2DEV.MISO.SOURCE, GPIO_AF_5);
    GPIO_PinAFConfig(SPI2DEV.NSS.PORT, SPI2DEV.NSS.SOURCE, GPIO_AF_5);

    // SPI als Alternative-Function mit PullDown
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    //GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // ok

    // SCK-Pin
    GPIO_InitStructure.GPIO_Pin = SPI2DEV.SCK.PIN;
    GPIO_Init(SPI2DEV.SCK.PORT, &GPIO_InitStructure);
    // MOSI-Pin
    GPIO_InitStructure.GPIO_Pin = SPI2DEV.MOSI.PIN;
    GPIO_Init(SPI2DEV.MOSI.PORT, &GPIO_InitStructure);
    // MISO-Pin
    GPIO_InitStructure.GPIO_Pin = SPI2DEV.MISO.PIN;
    GPIO_Init(SPI2DEV.MISO.PORT, &GPIO_InitStructure);
    // NSS-Pin
    GPIO_InitStructure.GPIO_Pin = SPI2DEV.NSS.PIN;
    GPIO_Init(SPI2DEV.NSS.PORT, &GPIO_InitStructure);

    // Reset SPI Interface
    SPI_I2S_DeInit(SPI2);

    //== SPI2 configuration
    SPI_StructInit(&SPI_InitStructure);
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI2, &SPI_InitStructure);

    //–Enable DMA1 clock–
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    //==Configure DMA1 – Channel4== (SPI -> memory)
    DMA_DeInit(DMA1_Channel4); //Set DMA registers to default values
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; //Address of peripheral the DMA must map to
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SPIReceivedValue[0]; //Variable to which received data will be stored
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 1; //Buffer size
    // DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    // DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    // DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    // DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure); //Initialise the DMA
    DMA_Cmd(DMA1_Channel4, ENABLE); //Enable the DMA1 – Channel4

    //==Configure DMA1 – Channel5== (memory -> SPI)
    DMA_DeInit(DMA1_Channel5); //Set DMA registers to default values
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; //Address of peripheral the DMA must map to
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SPITransmittedValue[0]; //Variable from which data will be transmitted
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 1; //Buffer size
    // DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    // DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    // DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    // DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure); //Initialise the DMA
    DMA_Cmd(DMA1_Channel5, ENABLE); //Enable the DMA1 – Channel5

    // Enable SPI2
    SPI_Cmd(SPI2, ENABLE);

    // Enable the SPI2 RX & TX DMA requests
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
    }

  5. sakarin sagt:

    Thank you! now I have fixed the code and it is working as I wish.


Wie hat Dir dieser Artikel gefallen?

1 Stern2 Sterne3 Sterne4 Sterne5 Sterne (Noch keine Bewertungen)
Loading...

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert