cancel
Showing results for 
Search instead for 
Did you mean: 

SPI on STM32f207IG

smkuls
Associate II
Posted on March 09, 2014 at 12:01

This is my code for SPI and I am getting the below mentioned errors, I can't seem to resolve these. 

&sharpinclude <stm32f2xx.h>

&sharpinclude <stm32f2xx_conf.h>

&sharpinclude <stm32f2xx_spi.h>

&sharpinclude <stm32f2xx_gpio.h>

&sharpinclude <stm32f2xx_rcc.h>

// this function initializes the SPI1 peripheral

void init_SPI1(void){

GPIO_InitTypeDef GPIO_InitStruct;

SPI_InitTypeDef SPI_InitStruct;

// enable clock for used IO pins

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

/* configure pins used by SPI1

* PA5 = SCK

* PA6 = MISO

* PA7 = MOSI

*/

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;

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 SPI1 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);

// enable clock for used IO pins

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);

/* Configure the chip select pin

  in this case we will use PE7 */

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;

GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;

GPIO_Init(GPIOE, &GPIO_InitStruct);

GPIOE->BSRRL |= GPIO_Pin_7; // set PE7 high

// enable peripheral clock

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

/* configure SPI1 in Mode 0 

* CPOL = 0 --> clock is low when idle

* CPHA = 0 --> data is sampled at the first edge

*/

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

}

/* This funtion is used to transmit and receive data 

 * with SPI1

 * data --> data to be transmitted

 * returns received value

 */

uint8_t SPI1_send(uint8_t data){

SPI1->DR = data; // write data to be transmitted to the SPI data register

while( !(SPI1->SR & SPI_I2S_FLAG_TXE) ); // wait until transmit complete

while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete

while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore

return SPI1->DR; // return received data from SPI data register

}

int main(void){

uint8_t received_val = 0;

init_SPI1();

while(1){

GPIOE->BSRRH |= GPIO_Pin_7; // set PE7 (CS) low

SPI1_send(0xAA);  // transmit data

received_val = SPI1_send(0x00); // transmit dummy byte and receive data

GPIOE->BSRRL |= GPIO_Pin_7; // set PE7 (CS) high

}

}

I am getting the following errors, how to get around these:

Build target 'Target 1'

linking...

.\spi_1.axf: Error: L6218E: Undefined symbol assert_param (referred from stm32f2xx_gpio.o).

.\spi_1.axf: Error: L6218E: Undefined symbol main (referred from rtentry2.o).

Not enough information to list image symbols.

Finished: 1 information, 0 warning and 2 error messages.

''.\spi_1.axf'' - 2 Error(s), 0 Warning(s).

Target not created

#spi #stm32f2xx #stm32f2-spi
9 REPLIES 9
Posted on March 09, 2014 at 14:33

Using Keil, your project isn't correctly formed, likely missing a startup file, and with USE_FULL_ASSERT defined (stm32f2xx_conf.h?), but lacking the body function for assert_failed() in main.c

Review the construction of the template project, and clone it if required.

STM32F2xx_StdPeriph_Lib_V1.1.0\Project\STM32F2xx_StdPeriph_Template\MDK-ARM\Project.uvproj

Honestly I'd be checking TXE before stuffing data to the SPI peripheral, review also SPI example code from the firmware libraries.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
smkuls
Associate II
Posted on April 16, 2014 at 20:16

I changed my code, now I am using Atollic.

I have some doubts - can you please resolve them?

1. When I check the clock on the Oscilloscope - I don't get a square wave (it's something like a square wave with it's upper part curved), why is that happening? Is it OK to not to have a perfectly square wave?

2. What's wrong with the code below, I am trying to read the status register of the EEPROM. received_val remains 0 even after completely executing the code (I am debugging step by step)

3. However, when I start the code - it starts with a default value of 224 (does that mean that it read something during the previous execution of the code, I am asking this because I am making it 0 every time in code so how come it automatically becomes 224?) If it's correct why doesn't it reflect during the debugging?

PS - I have been stuck with this for almost about two months and I can't seem to figure out - I have tried all possible things to test SPI but none of them seem to work. 

I'd really appreciate some help. 

#include <stddef.h>

#include ''stm32f2xx.h''

 void init_SPI1(void){

  GPIO_InitTypeDef GPIO_InitStruct;

  SPI_InitTypeDef SPI_InitStruct;

  // enable clock for used IO pins

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

  /* configure pins used by SPI1

  * PA5 = SCK

  * PA6 = MISO

  * PA7 = MOSI

  * PB0 - CS

  * PB1 - HLD

  * PB2 - WP

  */

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;

  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 SPI1 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);

  // enable clock for used IO pins

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

  /* Configure the chip select pin

    in this case we will use PE7 */

  GPIO_InitStruct.GPIO_Pin =  GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;

  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;

  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;

  GPIO_Init(GPIOB, &GPIO_InitStruct);

  GPIOB->BSRRL |= GPIO_Pin_0; // set PB0 high

  GPIOB->BSRRL |= GPIO_Pin_0; // set PB1 high

  GPIOB->BSRRL |= GPIO_Pin_0; // set PB2 high

  // enable peripheral clock

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

  /* configure SPI1 in Mode 0

  * CPOL = 0 --> clock is low when idle

  * CPHA = 0 --> data is sampled at the first edge

  */

  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

 }

 uint8_t SPI1_send(uint8_t data){

  SPI1->DR = data; // write data to be transmitted to the SPI data register

  while( !(SPI1->SR & SPI_I2S_FLAG_TXE) ); // wait until transmit complete

  while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete

  while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore

  return SPI1->DR; // return received data from SPI data register

 }

int main(void)

{

uint8_t received_val = 0;

init_SPI1();

while(1)

{

GPIOB->BSRRH |= GPIO_Pin_0; // set PE7 (CS) low

received_val=SPI1_send(0xA5);

GPIOB->BSRRL |= GPIO_Pin_0; // set PE7 (CS) high

}

}

Posted on April 16, 2014 at 20:37

Well I don't know what SPI device you're using, and I don't have an SPI EEPROM/FLASH on my board.

You need to send a dummy byte to the device to get status back, when you shift the command out you get back bits at the same time, the device HASN'T received the command at this point, so you're almost certainly going to need to send at least one more byte to generate the clocks to get status back. What does the documentation show?

What is the bandwidth of the scope and probe you are using? What's the frequency of the clock signal? Use a 1X/10X probe, and switch it to 10X mode!

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
smkuls
Associate II
Posted on April 16, 2014 at 20:56

I am using an external EEPROM - http://www.atmel.in/Images/Atmel-5165-SEEPROM-AT25512-Datasheet.pdf

And so you mean to say that I must send the status read opcode again and then read from the status register?

Something like this? 

GPIOB->BSRRH |= GPIO_Pin_0; // set PE7 (CS) low

SPI1_send(0xA5);

received_val=SPI1_send(0xA5);

GPIOB->BSRRL |= GPIO_Pin_0; // set PE7 (CS) high

Does the rest of the code look fine to you?

I am also not able to figure out the clock. 

The ''clock rate'' of the device is 20MHz. 

And if I am not wrong isn't it supposed to be the same as the SPI clock? How do I make sure that it is same (do I need to change the BaudRate_PresScaler?)

I don't have access to the Oscilloscope at the moment however I will get back to you later with the details?

Posted on April 16, 2014 at 21:37

Where did you pluck 0xA5 from? Isn't the

http://forums.xilinx.com/xlnx/attachments/xlnx/ELINUX/3453/1/at25.c

command 0x05?

I'd send 0x05, 0x00

In these two months did you read the documentation?

0690X0000060589QAA.png

Most 1X/10X scope probes in 1X mode have a bandwidth BELOW 8 MHz, so at 20 MHz the signal will look pretty awful.

Yes, the 20 MHz is the bit rate for the SPI bus
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
smkuls
Associate II
Posted on April 19, 2014 at 08:27

Yes, I know that read status command is 0x05 - I just made a mistake while posting it here and yes I did read the user manual as well as the data sheet of both the board as well as the memory.

1. I checked MISO with the Oscilloscope, it always stays low (The other pins are behaving properly). 

2. I made a change - CPHA = 1, since according to the timing diagram the data is being sent at the trailing edge. (RDSR timing diagram).

3. I tried with sending a dummy byte. I still have no luck. (I sent both 00 as well as FF as the dummy byte)

4. I found another mistake - I wasn't making the HOLD and WP high (while copy-pasting I forgot to change the pin number)

5. The maximum APB2 frequency is 60MHz and APB2/4 will be 15 MHz, however the EEPROM requires 20MHz - will that be a problem?

Does the ''SPI1_send'' function seem fine to you? 

Have I initialized SPI properly?

This is the code that I finally tried with - 

#include ''stm32f2xx.h''

 void init_SPI1(void)

 {

  GPIO_InitTypeDef GPIO_InitStruct;

  SPI_InitTypeDef SPI_InitStruct;

  // enable clock for used IO pins

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

  /* configure pins used by SPI1

   * 1 - CS - PB0

   * 2 - MISO - PA6

   * 3 - WP - PB2

   * 4 - GND

   * 5 - MOSI - PA7

   * 6 - SCK - PA5

   * 7 - HLD - PB1

   * 8 - VCC

   */

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;

  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 SPI1 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 the HLD,CS and WP pins

  GPIO_InitStruct.GPIO_Pin =  GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;

  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;

  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;

  GPIO_Init(GPIOB, &GPIO_InitStruct);

  GPIOB->BSRRL |= GPIO_Pin_0; // set PB0 (CS) high

  GPIOB->BSRRL |= GPIO_Pin_1; // set PB1 (HLD) high

  GPIOB->BSRRL |= GPIO_Pin_2; // set PB2 (WP) high

  // enable peripheral clock

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

  /* configure SPI1 in Mode 0

   * CPOL = 0 --> clock is low when idle

   * CPHA = 1 --> data is sampled at the second edge

   */

  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 second 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

 }

 uint16_t SPI1_send(uint8_t data)

 {

  SPI1->DR = data; // write data to be transmitted to the SPI data register

  while( !(SPI1->SR & SPI_I2S_FLAG_TXE) ); // wait until transmit complete

  while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete

  while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore

  return SPI1->DR; // return received data from SPI data register

 }

int main(void)

{

uint16_t received_val = 0;

init_SPI1();

while(1)

{

GPIOB->BSRRH |= GPIO_Pin_0; // set PB0 (CS) low

SPI1_send(0x05); //The read status opcode

GPIOB->BSRRL |= GPIO_Pin_0; // set PB0 (CS) high

GPIOB->BSRRH |= GPIO_Pin_0; // set PB0 (CS) low

received_val=SPI1_send(0xFF); //The dummy byte

GPIOB->BSRRL |= GPIO_Pin_0; // set PB0 (CS) high

}

}

stm322399
Senior
Posted on April 19, 2014 at 10:58

I did not look at the whole thing, but am pretty sure that you do not need to deassert CS between the command and the data bytes in main:

GPIOB->BSRRH |= GPIO_Pin_0; // set PB0 (CS) low
SPI1_send(0x05); //The read status opcode
// do not release CS
//GPIOB->BSRRL |= GPIO_Pin_0; // set PB0 (CS) high
//GPIOB->BSRRH |= GPIO_Pin_0; // set PB0 (CS) low
received_val=SPI1_send(0xFF); //The dummy byte
GPIOB->BSRRL |= GPIO_Pin_0; // set PB0 (CS) high

Posted on April 19, 2014 at 15:11

Yes, one of the salient details expressed in the timing diagram is the CS is low during the transaction.

20 MHz is the maximum speed, it can run significantly slower.

I've used code materially similar to read

http://www.winbond.com/NR/rdonlyres/7EB3B29C-1B35-421C-AA24-F430B51C776A/0/W25Q16BV.pdf

parts
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on April 20, 2014 at 06:53

Also you should probably observe that you don't need to RMW the BSRRL/H registers, you simply write the bits you want to set/reset.

I would focus on sending the READ ID or JEDEC ID type commands and getting back recognizable data.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..