mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik SPI mit STM32


Autor: Torsten Albrecht (torsten_a)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
ich versuche mit einem STM32F103 einen SPI FRAM „FM25H20“ anzusprechen. 
Meine Versuche blieben bisher erfolglos.
Ich habe leider keine Ahnung was fehlt bzw. falsch ist.  Meine erste 
Vermutung ist, dass der Takt bzw. die Frequenz für den SPI2 Bus nicht 
richtig initialisiert wurde. Oder aber dass die SPI Initialisierung 
unvollständig ist. Vielleicht aber auch eine fehlerhafte Verwendung der 
„SPI-Routinen“.
Der FRAM verlangt den SPI Mode 0 oder 3 (CPOL=0, CPHA=0 oder 
CPOL=1,CPHA=1).
Mit einem Atmega 2560 kann ich diesen FRAM-Typ erfolgreich via SPI 
ansprechen - nur leider kann der C-Code nicht 1zu1 importiert werden…:(
Vielleicht  kann mir jemand weiter helfen und sieht wo es klemmt. Ich 
muss noch dazu sagen, dass ich erst seit 1er Woche mit einem STM32F103 
zu tun habe und keine Erfahrung habe.

meine main() mit RCC_Configuration():
int main (void) {

    // System Clocks Configuration 
    RCC_Configuration();        

    // I2C is using for test/debugging
    I2C_Addr_configuration();
    I2C1_Configuration();         

    spi2_init();                // initialize SPI
    test_fram();                // Test Routine
   
    NVIC_Configuration();       
    init_control_unit();

    while(1) {
        //....
    }
}

void RCC_Configuration (void) {              

        /* RCC system reset(for debug purpose) */
        RCC_DeInit();

        /* Enable HSE */
        RCC_HSEConfig(RCC_HSE_ON);

        /* Wait till HSE is ready */
        HSEStartUpStatus = RCC_WaitForHSEStartUp();

        if(HSEStartUpStatus == SUCCESS)
        {
            /* HCLK = SYSCLK */
            RCC_HCLKConfig(RCC_SYSCLK_Div1); 

            /* PCLK2 = HCLK */
            RCC_PCLK2Config(RCC_HCLK_Div1); 

            /* PCLK1 = HCLK/2 */
            RCC_PCLK1Config(RCC_HCLK_Div2);

            /* Flash 2 wait state: required at operation of 72MHz */
            FLASH_SetLatency(FLASH_Latency_2);
            /* Enable Prefetch Buffer */
            FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

            /* PLLCLK = 8MHz * 9 = 72 MHz */
            RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);

            /* Enable PLL */ 
            RCC_PLLCmd(ENABLE);

            /* Wait till PLL is ready */
            while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
            {
            }
            /* Select PLL as system clock source */
            RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

            /* Wait till PLL is used as system clock source */
            while(RCC_GetSYSCLKSource() != 0x08)
            {
            }
        }
        
        // Enable peripheral Clocks
        /* Enable GPIO clock */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
    
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
        /* Enable SPI clock  */
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  
        
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
       
}


SPI2 Initialisierung:
void spi2_init (void) {

 // RCC 
 // already done --> in RCC_Configuration();
 /* Enable GPIO clock */
 //RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
 /* Enable SPI clock  */
 //RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

 // GPIO 
 GPIO_InitTypeDef GPIO_InitStructure_NSS;
 // SPI2 NSS @ PB12 
 GPIO_InitStructure_NSS.GPIO_Pin   = GPIO_Pin_12;
 GPIO_InitStructure_NSS.GPIO_Mode  = GPIO_Mode_Out_PP;
 GPIO_InitStructure_NSS.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOB, &GPIO_InitStructure_NSS);

 // set slave select to high  
 GPIO_WriteBit(GPIOB, GPIO_Pin_12, 1);

 // GPIO 
 GPIO_InitTypeDef GPIO_InitStructure;
 // SPI2 SCK/MISO/MOSI @ PB13/PB14/PB15 
 GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
 GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOB, &GPIO_InitStructure);

 // SPI 
 SPI_InitTypeDef SPI_InitStructure;

 SPI_InitStructure.SPI_Direction         = SPI_Direction_2Lines_FullDuplex;
 SPI_InitStructure.SPI_Mode              = SPI_Mode_Master;
 SPI_InitStructure.SPI_DataSize          = SPI_DataSize_8b;
 SPI_InitStructure.SPI_CPOL              = SPI_CPOL_High;  // SPI_Mode 0
 SPI_InitStructure.SPI_CPHA              = SPI_CPHA_1Edge; // SPI_Mode 0
 SPI_InitStructure.SPI_NSS               = SPI_NSS_Soft;
 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;    //SPI_BaudRatePrescaler_256; // 36MHz/256=140kHz < 400kHz
 SPI_InitStructure.SPI_FirstBit          = SPI_FirstBit_MSB;
 SPI_InitStructure.SPI_CRCPolynomial     = 7;
 SPI_Init(SPI2, &SPI_InitStructure);

 SPI_CalculateCRC(SPI2, DISABLE);

 SPI_Cmd(SPI2, ENABLE);

}

Und die Funktionen fram_spi_write_byte() , fram_spi_read_byte() und 
test_fram():
/**************************************************************************
*               FRAM_SPI_WRITE_BYTE FUNCTION
*
* Description : This function writes one byte into FRAM.
* Arguments   : 16 bit address, 8 bit data
* Returns     :  None
* Note        : Internal Function
**************************************************************************/
void fram_spi_write_byte(unsigned int addr, unsigned char data_byte)
{
    //SPI start
    //SPI_SSOutputCmd(SPI2, ENABLE);
    GPIO_WriteBit(GPIOB, GPIO_Pin_12, 0);
  
    //Sets WEL bit. WREN must precede WRITE opcode.   
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, 0x06);
    //while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    

    //SPI end
    //SPI_SSOutputCmd(SPI2, DISABLE);
    GPIO_WriteBit(GPIOB, GPIO_Pin_12, 1);

    //delay(1);

    //SPI start
    //SPI_SSOutputCmd(SPI2, ENABLE);
    GPIO_WriteBit(GPIOB, GPIO_Pin_12, 0);

    //0x02 is WRITE opcode      
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, 0x02);    
    //while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);

    /* set address */
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, 0x00);    
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, (unsigned char)((addr & 0xFF00) >> 8));    
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, (unsigned char)(addr & 0x00FF));    
    //while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);

    //write data
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, data_byte);    
    //while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);

    /* terminate spi data transfer */
    //SPI_SSOutputCmd(SPI2, DISABLE);
    GPIO_WriteBit(GPIOB, GPIO_Pin_12, 1);  
}


/**************************************************************************
*               FRAM_SPI_READ_BYTE FUNCTION
*
* Description : This function reads one byte from FRAM.
* Arguments   : 16 bit address
* Returns     :  8 bit data
* Note        : Internal Function
**************************************************************************/
unsigned char fram_spi_read_byte(unsigned int addr)
{
    unsigned char data_byte = 0;
  
    //SPI start
    //SPI_SSOutputCmd(SPI2, ENABLE);
    GPIO_WriteBit(GPIOB, GPIO_Pin_12, 0);

    //0x03 is READ opcode
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, 0x03);    
    //while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);

    /* set address */
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, 0x00);    
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, (unsigned char)((addr & 0xFF00) >> 8));    
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    SPI_I2S_SendData(SPI2, (unsigned char)(addr & 0x00FF));    
    //while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);

     
    //read data    
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);
    data_byte = SPI_I2S_ReceiveData(SPI2); 
    //while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE)==RESET);

    /* terminate spi data transfer */
    //SPI_SSOutputCmd(SPI2, DISABLE);
    GPIO_WriteBit(GPIOB, GPIO_Pin_12, 1);
  
    return data_byte;  
}


void test_fram(void) {
  unsigned char i;

  fram_spi_write_byte(0, 0x01);
  fram_spi_write_byte(1, 0x02);
  fram_spi_write_byte(2, 0x03);
  fram_spi_write_byte(3, 0x04);
  fram_spi_write_byte(4, 0x05);

  for(i=0;i<5;i++){
    global_rd_ram_buff[i] = fram_spi_read_byte(i);
  }

}

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> GPIO_InitTypeDef GPIO_InitStructure;
> // SPI2 SCK/MISO/MOSI @ PB13/PB14/PB15
> GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
> GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
> GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
> GPIO_Init(GPIOB, &GPIO_InitStructure);

MISO muss ein Input sein.

Autor: Torsten Albrecht (torsten_a)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das habe ich auch schon geändert.
GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_14; //MISO
GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;

Es gibt ja einige Beispielprojekte in denen SPI verwendet wird - mal 
findet man MISO als Input und mal als GPIO_Mode_AF_PP. Scheint ja beides 
zu funktionieren??!


in der Funktion fram_spi_read_byte() habe ich jetzt auch das richtige 
Register-Flag "RXNE" eingesetzt
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE)==RESET);
data_byte = SPI_I2S_ReceiveData(SPI2); 

die NVIC_Configuration() lautet:
void NVIC_Configuration (void) {  
    
    NVIC_InitTypeDef NVIC_InitStructure;

    #ifdef  VECT_TAB_RAM  
      /* Set the Vector Table base location at 0x20000000 */ 
      NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); 
    #else  /* VECT_TAB_FLASH  */
      /* Set the Vector Table base location at 0x08000000 */ 
      NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);   
    #endif

      /* 1 bit for pre-emption priority, 3 bits for subpriority */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
      
      // Configure and enable I2C2 interrupt -------------------------------------
      NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQChannel;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);
 /*         
      // Enable the USART3 Interrupt 
      NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQChannel;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);
  */
}
also auch kein USART3 kann hier Einfluss nehmen....


Hat jemand eine Idee was noch fehlerhaft ist? Die Funktionen 
fram_spi_write_byte() , fram_spi_read_byte() und
test_fram() habe ich so auch beim ATMEGA umgesetzt, bis auf die 
Anpassungen auf den STM MC:

while (SPI_I2S_GetFlagStatus(SPI2, xxxx)==RESET);
SPI_I2S_SendData(SPI2, xxx);

und da läuft es...  --aber ausschließen kann ich das natürlich auch 
nicht.



Gibt es einen Unterschied bzw. wo liegt der Unterschied bei der 
Verwendung von
//SPI start
SPI_SSOutputCmd(SPI2, ENABLE);
statt
//SPI start
GPIO_WriteBit(GPIOB, GPIO_Pin_12, 0);
????

Autor: Torsten Albrecht (torsten_a)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
SPI_SSOutputCmd(SPI2, ENABLE);
is using by hardware NSS management

dazu muss SPI_NSS_Hard aktiviert werden:
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;

Autor: torsten_a (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe jetzt versucht das Statusregister vom FRAM auszulesen und das 
hat auch nicht funktioniert.

Bei allen Lese-Versuchen bekomme ich immer 255 zurück.

Habe auch schon testweise CPOL und CHPA geändert?

Hat jemand SPI Beispiele oder kann mir bei meinen Code weiterhelfen?

Autor: fliflop (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
hallo,

bei mir funktioniert der folgende code einwandfrei.

Autor: torsten_a (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Muss beim "STM32" beim Lesen auch ein DummyByte austakten???
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);SPI_I2S_SendData(SPI1,0xFF);

habe die Veränderungen übernommen geht aber nicht :(

Trotzdem Danke!!!

Autor: fliflop (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hi,
ob das dummyread wirklich sein muss, weiß ich nicht, dass müsste man mal 
nachlesen falls es stört. werden überhaupt die richtigen pins 
konfiguriert, sind die spi pins nicht pa4-pa7 ?
gruß steffen

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Muss beim "STM32" beim Lesen auch ein DummyByte austakten???

Wie soll das Datenbyte sonst vom Slave zum Master kommen?
Der Master gibt den Takt vor. Das kann er nur wenn er
Dummybytes sendet.

Bevor man den ARM verstanden hat kommentiert man natürlich
erst mal alles wichtige weg was man nicht versteht.

    SPI_I2S_SendData(SPI2, data_byte);
>>>>    //while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET);

    /* terminate spi data transfer */
    GPIO_WriteBit(GPIOB, GPIO_Pin_12, 1);

CS wieder hochziehen bevor das Byte raus ist, ist keine
gute Idee.

Autor: Stephan Van den boom (Firma: FH Gelsenkirchen) (devilx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Bist du mittlerweile weiter gekommen?

Autor: Tim R. (mugen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin Moin,

auch wenn dieser Thread alt ist, dann schreibe ich doch hier einen 
kleinen Hinweis. Die Code-Zeile von fliflop beinhaltet einen Fehler in 
der TX Funktion:
  int tmp=0;
  SPI_I2S_SendData(SPI1,tx);
  while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);
  tmp=SPI_I2S_ReceiveData(SPI1);
  return tmp;

In den obigen Code wird zwar das byte/word richtig gesendet, allerdings 
wird nicht auf die Empfangsdaten gewartet. Bei einer sequentiellen SPI 
Übermittlung hat man dann immer einen zu alten Wert.

Richtig müsste sein:
  int tmp=0;
  SPI_I2S_SendData(SPI1,tx);
  while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);
  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == 1);
  tmp=SPI_I2S_ReceiveData(SPI1);
  return tmp;

Diese Lösung beansprucht allerdings eine längere Verarbeitungszeit, da 
erst nach Aussendung die while-schleife verlassen wird.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.