2015-10-23 05:25 AM
I've a custom board with STM32F073C8 SOC [1] and Spansion flash memory [2] among other things. This external flash memory (Spansion) on board is connected to STM32F072C8 over SPI bus. I'm planning to use STM32 as master and spansion flash memory as slave.
The pin connection is as follows:
STM SPANSION PURPOSE
PA.4 NCS Chip select (Negative logic)
PA.5 SCK Clock
PA.6 SI MOSI (Master out, Slave in)
PA.7 SO MISO (Master in, Slave out)
PB.0 NWP Write protect (Negative logic)
The setup is as follows:
1: Enable clock for GPIOA.
2: Configure GPIOA pins PA.{4,5,6,7} in Alternate function mode (AF_1)
3: Enable clock for GPIOB
4: Configure GPIOB, PB.0 as output mode
5: Configure SPI1 with NSS (Negative slave select as 'soft')
6: Reset chip select (in this case PA.4) and read configuration register (For spansion memory, it is RDCR(0x35) )
7: Send some consecutive dummy bytes (For this, I refered some example for a different flash memory, but I think, sending the dummy bytes are for timing purpose)
However, based on the above understanding and using some sample code in public domain, I tried to implement this communication between STM and Spansion, but I don't get anything (I think, I'm getting stuck in the loop, while trying to read the SPI flag status).
Please let me know, what am i missing? Code snip is below:
/* Spansion specific commands */
#define FLASH_RDCR 0x35 /* Read Configuration Register */
/* Dummy cycles read */
#define sFLASH_DUMMY_BYTE 0xA5
#define sFLASH_CS_LOW() GPIO_ResetBits(sFLASH_CS_GPIO_PORT, sFLASH_CS_PIN)
#define sFLASH_CS_HIGH() GPIO_SetBits(sFLASH_CS_GPIO_PORT, sFLASH_CS_PIN)
#define sFLASH_SPI SPI1
#define sFLASH_SPI_CLK RCC_APB2Periph_SPI1
#define sFLASH_SPI_SCK_PIN GPIO_Pin_5 /* PA.05 */
#define sFLASH_SPI_SCK_GPIO_PORT GPIOA /* GPIOA */
#define sFLASH_SPI_SCK_GPIO_CLK RCC_AHBPeriph_GPIOA
#define sFLASH_SPI_MISO_PIN GPIO_Pin_6 /* PA.06 */
#define sFLASH_SPI_MISO_GPIO_PORT GPIOA /* GPIOA */
#define sFLASH_SPI_MISO_GPIO_CLK RCC_AHBPeriph_GPIOA
#define sFLASH_SPI_MOSI_PIN GPIO_Pin_7 /* PA.07 */
#define sFLASH_SPI_MOSI_GPIO_PORT GPIOA /* GPIOA */
#define sFLASH_SPI_MOSI_GPIO_CLK RCC_AHBPeriph_GPIOA
#define sFLASH_CS_PIN GPIO_Pin_4 /* PA.04 */
#define sFLASH_CS_GPIO_PORT GPIOA /* GPIOA */
#define sFLASH_CS_GPIO_CLK RCC_AHBPeriph_GPIOA
#define sFLASH_SPI_WREN_PIN GPIO_Pin_0 /* PB.0 */
#define sFLASH_SPI_WREN_GPIO_PORT GPIOB
#define sFLASH_WREN_GPIO_CLK RCC_AHBPeriph_GPIOB
void
sFLASH_LowLevel_Init(
void
)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA PA.{4,5,6,7}*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
/* Write protect GPIOB PB.0*/
RCC_AHBPeriphClockCmd(sFLASH_WREN_GPIO_CLK, ENABLE);
/*!< sFLASH_SPI Periph clock enable SPI1 */
RCC_APB2PeriphClockCmd(sFLASH_SPI_CLK, ENABLE);
/*!< Configure GPIOA pins: SCK,MOSI,MISO,CS */
GPIO_InitStructure.GPIO_Pin = sFLASH_SPI_SCK_PIN|sFLASH_SPI_MOSI_PIN|sFLASH_SPI_MISO_PIN|sFLASH_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_Init(sFLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_1);
/*!< Configure sFLASH_SPI pins: WREN */
GPIO_InitStructure.GPIO_Pin = sFLASH_SPI_WREN_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_Init(sFLASH_SPI_WREN_GPIO_PORT, &GPIO_InitStructure);
}
void
sFLASH_Init(
void
)
{
SPI_InitTypeDef SPI_InitStructure;
sFLASH_LowLevel_Init();
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
/*!< SPI configuration */
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_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(sFLASH_SPI, &SPI_InitStructure);
/*!< Enable the sFLASH_SPI */
SPI_Cmd(sFLASH_SPI, ENABLE);
}
uint8_t sFLASH_SendByte(uint8_t
byte
)
{
/*!< Loop while DR register in not empty */
while
(SPI_I2S_GetFlagStatus(sFLASH_SPI, SPI_I2S_FLAG_TXE) == RESET);
/*!< Send byte through the SPI1 peripheral */
SPI_SendData8(sFLASH_SPI,
byte
);
/*!< Wait to receive a byte */
while
(SPI_I2S_GetFlagStatus(sFLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET);
/*!< Return the byte read from the SPI bus */
return
SPI_ReceiveData8(sFLASH_SPI);
}
uint32_t sFLASH_ReadID(
void
)
{
uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
/*!< Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/*!< Send ''RDID '' instruction */
sFLASH_SendByte(FLASH_RDCR);
/*!< Read a byte from the FLASH */
Temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Read a byte from the FLASH */
Temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Read a byte from the FLASH */
Temp2 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
return
Temp;
}
int
main(
void
)
{
sFLASH_Init();
/* i think, it does not return */
uint32_t ret = sFLASH_ReadID();
while
(1);
}
[1] http://www.st.com/web/catalog/mmc/FM141/SC1169/SS1574/LN1823/PF259609
[2] https://www.spansion.com/Support/Datasheets/S25FL128S_256S_00.pdf
Thanks a lot for your help.
#stm32f072 #stm32 #spi #solved
2015-10-23 05:49 AM
You can't ''reset'' the chip select pin if you've set it as AF. Set it as Output. And place an external pullup resistor on it (see sFlash's datasheet for explanation, why).
JW2015-10-23 05:53 AM
You can't ''reset'' the chipselect once you've set it as AF. Set it as Output. And add an external pullup resistor on it - the memory's datasheet will tell you why.
An oscilloscope/LA is your friend. JW2015-10-23 06:34 AM
Thanks a ton for your help. I haven't connected the Pull-up resistor on the CS line yet, however, I changed the pin from AF to OUT mode and connected a Logic Analyzer and I see the timing diagram as shown in the attachment. Two things I notice are:
1: Clock pulse (SCK) has the same time period as the MISO and MOSI line. I had configured the speed of GPIOA, PA.5 as same speed as others. Should I change it something else ? <snip from the code>GPIO_InitStructure.GPIO_Pin = sFLASH_SPI_SCK_PIN|sFLASH_SPI_MOSI_PIN|sFLASH_SPI_MISO_PIN; <---- Here
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3; <--- Here
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_Init(sFLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
2: The Chip select line is at logic Low for the entire duration of data transfer (That seems good to me, and then gets a small glitch and gets back to low again. I'll connect the pull up resistor to the CS line as you suggested and will try again. I've changed the CS line GPIO initialization code toas you suggested. <snip> GPIO_InitStructure.GPIO_Pin = sFLASH_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
// Output mode
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_Init(sFLASH_CS_GPIO_PORT, &GPIO_InitStructure);
________________
Attachments : LA_STM32_SPANSION_SPI.png : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I0qU&d=%2Fa%2F0X0000000bhL%2FdignNLg9g1hHzWWSCg9k3lMXnUash7QgsI8hlEJCH14&asPdf=false
2015-10-26 03:02 PM
Is the LA quick enough to capture the SPI clock/data? If not, lower the SPI baudrate - no worry, you can go as low as you want, even to naked-eye-visible-blinking rate.
The CS pulsing high then going low is unsettling, as/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
is the last thing which should happen before the infinite loop... something is rotten... but it's up to you to find out, what (short on the board? incorrectly counted pins?)
JW
2015-11-03 02:38 PM
2015-11-03 03:27 PM
I'll have to review the specific part, but in the general sense you can read multiple bytes in a stream, provided they are within the same page, often 256 bytes. If your address is 128 bytes into the page, you can read the next 128 bytes, then you'd need to advance the address into the next page, and read up to 256 bytes from that. Same thing with writes, as the part has enough internal buffering to hold a page.
You should also make sure there is enough time between subsequent CS activations, and wait on busy status when erasing/writing.2015-11-04 05:10 AM
Thanks Clive for looking into this and helping me out.
Meanwhile, I tried to read the entire memory content by invoking READ (0x13) command, followed bythe address set to 0x0. According to the data sheet, I should be able to read the entire memory content insuch fashion. However, allI see now on MISO line is still 0xFF.I've no clue right now as to what happened to those supposedly (refer the ERASE and WRITE operationsanalyzer capture were attached before)successfulwrite operations that I did before. code snip to read the entire memory content:int
main(
void
)
{
sFLASH_Init();
sFLASH_DISABLE_WP();
#define MEM_LAST_ADDRESS 0x1000000
__IO uint32_t i;
/** flash memory data sheet, pg 89:
* The address can start at any byte location of the memory array. The
* address is automatically incremented to the next higher address in
* sequential order after each byte of data is shifted out. The entire
* memory can therefore be read out with one single read instruction and
* address 000000h provided.
*/
sFLASH_StartReadSequence(0x0);
for
(i = 0; i < MEM_LAST_ADDRESS; ++i) {
sFLASH_ReadByte();
}
sFLASH_CS_HIGH();
while
(1);
}
P.S.
I hooked up the analyzer, started capturing the datausingSaleae Logic analyzerandthen powered onmySTM I captured the data for 20 seconds, which I__think__might beenough to read the entire memory content.
2015-11-05 08:14 AM
2015-11-06 06:27 AM