SPI transmission errors. RX buffer full and data mismatch - c

I'm transmitting my data in SPI. uint8_t data[5] = {0xa,0xb,0xc,0xd,0xe}; When I see the output in logic analyzer, the data is mis matched. I dont know what might be the root of the problem. I will share my code and configuration here. Please have a look.
void SPI_Transmit (uint8_t *data, int size)
{
SPI1->CR1 |= (1<<6);
int i=0;
while (i<size)
{
while (!((SPI1->SR)&(1<<1))) {}; // wait for TXE bit to set -> This will indicate that the buffer is empty
SPI1->DR = data[i]; // load the data into the Data Register
i++;
}
// Clear the Overrun flag by reading DR and SR
while (!((SPI1->SR)&(1<<1))) {}; // wait for TXE bit to set -> This will indicate that the buffer is empty
while (((SPI1->SR)&(1<<7))) {}; // wait for BSY bit to Reset -> This will indicate that SPI is not busy in communication
while (((SPI1->SR)&(1<<0)))
{
uint8_t temp = SPI1->DR;
};
uint8_t temp1 = SPI1->SR;
SPI1->CR1 &= ~(1<<6);
}
Here I'm transmitting my data and waiting for my TX empty flag to set and Busy flag to reset. Also, after I transmittting my data, my RX buffer (RXFIFO) is full and RX not empty flag is set. So I have to read data register until RXNE is reset.
I understand SPI communicate through register shifting, for every data sent there will be some received data. But my communication is in Master transmit only mode. I'm only using MOSI not MISO. So I dont understand why there is data in my RX buffer even when I'm using only MOSI wire. I also tested transmitting data without any slave device, still I receive data in RX buffer. I dont understand where this RX data is coming from. The status registe shows RXNE=1 and FRLVL=11.
After programming my SPI transmit function according to my conditions, I transmit the data. When I see output in logic analyzer, the data is mismatch. The Baud Rate is 625.0KBits/s and sampling rate is 2MS/s.
Here is my SPI configuration
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}

One of the most important thing to work with SPI is phasing. With two parameters CPOL and CPHA the phasing should be configured. In your problem in these two are named as CLKPolarity and CLKPhase in hspi1.Init respectively. These two parameters includes 4 states (0,0 - 0,1 - 1,0 - 1,1). It's better to try each state. For more information please check Here, section: Clock Polarity and Clock Phase.

Related

STM32H7 SPI Nor Flash Device is always busy IS25LP128F

I am working on SPI Nor Flash IC IS25LP128F using Little FS as my file system. I Set 400Mhz as SPI kernel Clock, and my Prescaler value at 8. Hence 50 MHz SPI clock runs. At this case, my Filesystem operation takes places successfully at time, some times the code hangs in the SPI routine for a long time and states, NOR Device is busy.
According to Datasheet 80Mhz SPI clock, normal Read and 166Mhz SPI clock Fast read is given for IS25LP128F. I changed the kernel clk, to get respective output clock. Even then, the problem remains the same.
At 50Mhz sometimes to write a single line(64 byte) takes 4 minutes to complete. I have tried with 100Mhz, 80Mhz, 50Mhz, all the prescaler values, but only for 50 Mhz runs successfully sometimes (not all the time success)
Without device busy(txData=0 has to come), the filesystem, have to be taken place.
Pls help me out.
Datasheet
Output
/*Received Txdata always busy*/
uint8_t readStatusReg(void)
{
uint8_t txData;
CS_ACTIVE;
txData=CMD_RDSR;
HAL_SPI_Transmit(&hspi1, &txData, 1, 1000);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Receive(&hspi1, &txData, 1, 1000);
CS_DEACTIVE;
return txData;
}
/*Clock Config */
static void HWI_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
}

STM32, master and slave devices not responding to each other

Dear stack overflow users,
I have built a device with a master device, and a network of 10 slaves. All of them communicate via 4 wire SPI. Right now I am writing the program for both boards, and they don't seem to be working, I do not get expected responses.
I have a master board, and 10 of identical slave boards. The protocol is simple - as with SPI any transaction is initiated by the master device, and a command is sent. The selected slave then receives aforemetioned command, sets a busy flag pin high, and checks if it's valid. After parsing the command the busy bin is released, and if the command is valid, same byte as received is sent to the master, otherwise an error marker is sent. After that, any necessary data exchanges are executed. I've tried configuring the IO's as regular portf, and their Alternative Functions, also I tried resetting the SPI periph after each transaction and nothing seems to be working.
This is what I get:
https://imgur.com/a/MICEx2f
The channels are from the top, respectively:
MOSI,MISO,CLK, and busy flag. I get no response from the slave, no matter what. The command is interpreted correctly (debug data from UART), however nothing is sent back.
This is the SPI part of code for the SLAVE device:
uint8_t spi_sendrecv(uint8_t byte)
{
// poczekaj az bufor nadawczy bedzie wolny
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, byte);
// poczekaj na dane w buforze odbiorczym
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI1);
}
uint8_t SPI_get_cmd_ack(void)
{
uint8_t cmd;
uint8_t valid_flag;
//In cas if the BF pin was left high
BF_OUT_low();
//Let's wait for some data
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
cmd = SPI_I2S_ReceiveData(SPI1);
//cmd = SPI_get_command();
//Check the cmd
BF_OUT_high();
valid_flag = SPI_check_for_valid_cmd(cmd);
//SPI_reset_flush();
BF_OUT_low();
if(valid_flag == CMD_RET_STATUS_VALID)
{
spi_sendrecv(cmd);
return cmd;
}
else
{
spi_sendrecv(CMD_ERROR);
return CMD_ERROR;
}
}
And this is the MASTER part:
//Sends a command to a slave device
//Param1: slave device no, from 0 to 9
//Param2: command to send
//Retval: command send success or failure:
//DATA_TRANSFER_OK or DATA_TRANSFER_ERR
uint8_t SPI_send_command(uint8_t slave_no, uint8_t cmd)
{
uint8_t cnt = 0;
uint8_t rx_cmd;
//SPI_reset();
//Select the correct slave
SPI_select_slave(0);
delay_ms(0);
SPI_select_slave(slave_no);
delay_ms(0);
//Transmit the cmd
SPI_sendrecv(cmd);
//SPI_reset();
//Wait for the busy flag indication
while(SPI_get_busy_flag(slave_no) == Bit_RESET)
{
if(cnt < SPI_RETRY_COUNT)
{
++cnt;
delay_ms(1);
}
else
{
SPI_select_slave(0);
return DATA_TRANSFER_ERR;
}
}
//Same for the busy flag on:
while (SPI_get_busy_flag(slave_no) == Bit_SET)
{
if(cnt < SPI_RETRY_COUNT)
{
++cnt;
delay_ms(1);
}
else
{
SPI_select_slave(0);
return DATA_TRANSFER_ERR;
}
}
rx_cmd = SPI_sendrecv(0);
//SPI_reset();
if(rx_cmd == cmd) return DATA_TRANSFER_OK;
else return DATA_TRANSFER_ERR;
}
And here are the initialization parts of the code, slave and master respectively:
void SPI_init(void)
{
GPIO_InitTypeDef SPI_GPIO;
SPI_InitTypeDef SPI;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//GPIOA5 SCK
//GPIOA6 MISO
//GPIOA7 MOSI
SPI_GPIO.GPIO_Mode = GPIO_Mode_AF;
SPI_GPIO.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
SPI_GPIO.GPIO_PuPd = GPIO_PuPd_DOWN;
SPI_GPIO.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &SPI_GPIO);
SPI_GPIO.GPIO_Pin = GPIO_Pin_15;
SPI_GPIO.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &SPI_GPIO);
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);
//Busy flag
SPI_GPIO.GPIO_Mode = GPIO_Mode_OUT;
SPI_GPIO.GPIO_OType = GPIO_OType_PP;
SPI_GPIO.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOC, &SPI_GPIO);
/*SPI_GPIO.GPIO_Mode = GPIO_Mode_IN;
SPI_GPIO.GPIO_PuPd = GPIO_PuPd_UP;
SPI_GPIO.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOA, &SPI_GPIO);*/
SPI.SPI_CPHA = SPI_CPHA_1Edge;
SPI.SPI_CPOL = SPI_CPOL_Low;
SPI.SPI_DataSize = SPI_DataSize_8b;
SPI.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI.SPI_FirstBit = SPI_FirstBit_MSB;
SPI.SPI_Mode = SPI_Mode_Slave;
SPI.SPI_NSS = SPI_NSS_Hard;
SPI_Init(SPI1, &SPI);
SPI_Cmd(SPI1, ENABLE);
SPI_aux_tim_conf();
}
static void SPI_IO_conf(void)
{
//Struct
GPIO_InitTypeDef SPI_IO;
//CLK
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOE, ENABLE);
//Conf
SPI_IO.GPIO_Mode = GPIO_Mode_AF;
//5 - SCK, 6 - MISO, 7- MOSI
SPI_IO.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_6;
SPI_IO.GPIO_PuPd = GPIO_PuPd_DOWN;
SPI_IO.GPIO_OType = GPIO_OType_PP;
SPI_IO.GPIO_Speed = GPIO_Speed_25MHz;
//Init
GPIO_Init(GPIOA, &SPI_IO);
//Connect to SPI periph
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
//For busy flag checking
SPI_IO.GPIO_Mode = GPIO_Mode_IN;
SPI_IO.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |GPIO_Pin_12 |GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
SPI_IO.GPIO_PuPd = GPIO_PuPd_DOWN;
SPI_IO.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOE, &SPI_IO);
SPI_IO.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOB, &SPI_IO);
}
static void SPI_periph_conf(void)
{
//Struct
SPI_InitTypeDef SPI_conf;
//CLK
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//Conf
//SysClk = 84000000
//84/64 = 1,3125MHz
SPI_conf.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_conf.SPI_CPHA = SPI_CPHA_1Edge;
SPI_conf.SPI_CPOL = SPI_CPOL_Low;
//SPI_conf.SPI_CRCPolynomial =
SPI_conf.SPI_DataSize = SPI_DataSize_8b;
SPI_conf.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_conf.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_conf.SPI_Mode = SPI_Mode_Master;
SPI_conf.SPI_NSS = SPI_NSS_Soft;
//Conf, enable
SPI_Init(SPI1, &SPI_conf);
SPI_Cmd(SPI1, ENABLE);
//SPI_Cmd(SPI1, DISABLE);
}
As You can see on the oscillogram, there is no response from the Slave, The expected response is the same command that was sent in the previous cycle by the master. Eg, I send a 0x01 presence command, and the slave should respond with the same byte, after that, any other exchanges should occur, which are not implemented yet.
Best regards, Marek
From your images it seems that CLK is kept low after sending data. In SPI the Master is the sole governor of the clock.
From STM32F411xC/E reference manual, p 578:
BUSY flag
This BSY flag is set and cleared by hardware (writing to this flag has no effect). The BSY flag indicates the state of the communication layer of the SPI.
When BSY is set, it indicates that the SPI is busy communicating. There is one exception in master mode / bidirectional receive mode (MSTR=1 and BDM=1 and BDOE=0) where the BSY flag is kept low during reception.
The BSY flag is useful to detect the end of a transfer if the software wants to disable the SPI and enter Halt mode (or disable the peripheral clock). This avoids corrupting the last transfer. For this, the procedure described below must be strictly respected.
The BSY flag is also useful to avoid write collisions in a multimaster system.
The BSY flag is set when a transfer starts, with the exception of master mode / bidirectional receive mode (MSTR=1 and BDM=1 and BDOE=0).
It is cleared:
when a transfer is finished (except in master mode if the communication is continuous)
when the SPI is disabledwhen a master mode fault occurs (MODF=1)
When communication is not continuous, the BSY flag is low between each communication.
When communication is continuous:
in master mode, the BSY flag is kept high during all the transfers
in slave mode, the BSY flag goes low for one SPI clock cycle between each transfer
Note:Do not use the BSY flag to handle each data transmission or reception. It is better to use the TXE and RXNE flags instead
So I think your waiting for busy flag in master after sending data can lock indefinitely. Try this (the code uses plain CMSIS, but it should be understandable):
GPIOB->BSRR |= GPIO_BSRR_BR6; //slave select
while(! (SPI1->SR & SPI_SR_TXE)); //wait for Tx buffer empty
SPI1->DR = 0x01; //send 0x01
while(! (SPI1->SR & SPI_SR_RXNE)); //wait for Rx buffer not empty (receive 0x0 sent by the slave during our sending 0x01 since it's 4-wire SPI)
uint8_t tmp = SPI1->DR; //we don't need that value, but need to read DR in order to reset RXNE flag
SPI1->DR = 0x0; //we need to trigger send in order to receive
while(! (SPI1->SR & SPI_SR_RXNE)); //wait for Rx buffer not empty (our response)
response = SPI1->DR;
while(SPI1->SR & SPI_SR_BSY); //now we can wait for SPI to end communications
GPIOB->BSRR |= GPIO_BSRR_BS6; //slave deselect
Thank You for the help. After long hours I managed to get it working, by resetting the SPI peripheral in th Slave device after each transaction:
void SPI_reset_flush(void)
{
//Reset the periph and registers
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE);
SPI_aux_tim_wait();
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE);
SPI_aux_tim_wait();
SPI_Cmd(SPI1, ENABLE);
}
12.04.2019
Actually, I think the mentioned solution is not the best. The problem was, I was not waiting for the SPI buffers to empty, this resulted in random data being send, and I lost synchronisation between devices. I since the rewrote the code, and stuck to the TX/RX procedures in a Reference Manuals.
Best Regards, Marek

STM8 interrupt serial receive

I am new to STM8, and trying to use a STM8S103F3, using IAR Embedded Workbench.
Using C, I like to use the registers directly.
I need serial on 14400 baud, 8N2, and getting the UART transmit is easy, as there are numerous good tutorials and examples on the net.
Then the need is to have the UART receive on interrupt, nothing else will do.
That is the problem.
According to iostm8s103f3.h (IAR) there are 5 interrupts on 0x14 vector
UART1_R_IDLE, UART1_R_LBDF, UART1_R_OR, UART1_R_PE, UART1_R_RXNE
According to Silverlight Developer: Registers on the STM8,
Vector 19 (0x13) = UART_RX
According to ST Microelectronics STM8S.h
#define UART1_BaseAddress 0x5230
#define UART1_SR_RXNE ((u8)0x20) /*!< Read Data Register Not Empty mask */
#if defined(STM8S208) ||defined(STM8S207) ||defined(STM8S103) ||defined(STM8S903)
#define UART1 ((UART1_TypeDef *) UART1_BaseAddress)
#endif /* (STM8S208) ||(STM8S207) || (STM8S103) || (STM8S903) */
According to STM8S Reference manual RM0016
The RXNE flag (Rx buffer not empty) is set on the last sampling clock edge,
when the data is transferred from the shift register to the Rx buffer.
It indicates that a data is ready to be read from the SPI_DR register.
Rx buffer not empty (RXNE)
When set, this flag indicates that there is a valid received data in the Rx buffer.
This flag is reset when SPI_DR is read.
Then I wrote:
#pragma vector = UART1_R_RXNE_vector //as iostm8s103f3 is used, that means 0x14
__interrupt void UART1_IRQHandler(void)
{ unsigned character recd;
recd = UART1_SR;
if(1 == UART1_SR_RXNE) recd = UART1_DR;
etc.
No good, I continually get interrupts, UART1_SR_RXNE is set, but UART1_DR
is empty, and no UART receive has happened. I have disabled all other interrupts
I can see that can vector to this, and still no good.
The SPI also sets this flag, presumably the the UART and SPI cannot be used
together.
I sorely need to get this serial receive interrupt going. Please help.
Thank you
The problem was one bit incorrectly set in the UART1 setup.
The complete setup for the UART1 in the STM8S103F3 is now(IAR):
void InitialiseUART()
{
unsigned char tmp = UART1_SR;
tmp = UART1_DR;
// Reset the UART registers to the reset values.
UART1_CR1 = 0;
UART1_CR2 = 0;
UART1_CR4 = 0;
UART1_CR3 = 0;
UART1_CR5 = 0;
UART1_GTR = 0;
UART1_PSCR = 0;
// Set up the port to 14400,n,8,2.
UART1_CR1_M = 0; // 8 Data bits.
UART1_CR1_PCEN = 0; // Disable parity.
UART1_CR3 = 0x20; // 2 stop bits
UART1_BRR2 = 0x07; // Set the baud rate registers to 14400
UART1_BRR1 = 0x45; // based upon a 16 MHz system clock.
// Disable the transmitter and receiver.
UART1_CR2_TEN = 0; // Disable transmit.
UART1_CR2_REN = 0; // Disable receive.
// Set the clock polarity, clock phase and last bit clock pulse.
UART1_CR3_CPOL = 0;
UART1_CR3_CPHA = 0;
UART1_CR3_LBCL = 0;
// Set the Receive Interrupt RM0016 p358,362
UART1_CR2_TIEN = 0; // Transmitter interrupt enable
UART1_CR2_TCIEN = 0; // Transmission complete interrupt enable
UART1_CR2_RIEN = 1; // Receiver interrupt enable
UART1_CR2_ILIEN = 0; // IDLE Line interrupt enable
// Turn on the UART transmit, receive and the UART clock.
UART1_CR2_TEN = 1;
UART1_CR2_REN = 1;
UART1_CR1_PIEN = 0;
UART1_CR4_LBDIEN = 0;
}
//-----------------------------
#pragma vector = UART1_R_RXNE_vector
__interrupt void UART1_IRQHandler(void)
{
byte recd;
recd = UART1_DR;
//send the byte to circular buffer;
}
You forget to add global interrupt flag
asm("rim") ; //Enable global interrupt
It happens at non isolated connections whenever you connect your board's ground with other source's ground (USB<->TTL converter connected to PC etc.), In this case microcontroller is getting noise due to high value SMPS's Y capacitor etc.
Simply connect your RX and TX line's via 1K resistor and put 1nF (can be deceased for high speed) capacitors on these lines and to ground (micro controller side) to suppress noises.

STM32F4 SPI issue

I'am using STM32F407vg and i'am trying to write data in SPI data register, the following code shows the configuration function
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
* PA4 = NSS
* PA5 = SCK
* PA6 = MISO
* PA7 = MOSI
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5|GPIO_Pin_4;
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_PinSource4, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
//Set chip select high
GPIOA->BSRRL |= GPIO_Pin_4; // set PE4 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_StructInit(&SPI_InitStruct); // set default config
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 ; // 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
}
and this is the function that send data
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
}
I think that i have set the good configuration (the choice of pins is not wrong in my opinion and also the configuration of the bus used ) the following pictures are extracted from the STM32F407 DataSheet
The problem is that the data in the DR registry is different than the data passed as argument .I don't know why this happen .Could anyone guide me to the right point
The SPI DR register is not a normal memory location where writing and reading access the same storage.
Instead, writing loads the output shift register, while reading reads the received input. Depending on device details, reading may also "claim" the input, clearing it from the register until another word is received.
For these reasons, trying to watch the SPI DR with a debugger is not only not going to give you the information you seek, it may even be damaging to the data you would otherwise receive.
After reading the SPI section from this book my problem is solved, i just connect SPI MOSI to SPI MISO pin (PA6 and PA7) then used the following function :
int spiReadWrite(SPI_TypeDef* SPIx, uint8_t *rbuf,
const uint8_t *tbuf, int cnt, enum spiSpeed speed)
{
int i;
SPIx->CR1 = (SPIx->CR1 & ~SPI_BaudRatePrescaler_256) |
speeds[speed];
for (i = 0; i < cnt; i++){
if (tbuf) {
SPI_I2S_SendData(SPIx, *tbuf++);
} else {
SPI_I2S_SendData(SPIx, 0xff);
}
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
if (rbuf) {
*rbuf++ = SPI_I2S_ReceiveData(SPIx);
} else {
SPI_I2S_ReceiveData(SPIx);
}
}
return i;
}
by using a debugger i can see that the data sent (from tbuf) are succesfully received (in rbuf)
note that SCLK_freq = APB2_freq / (BaudRatePrescaler + 1)

STM32 DMA transfer error

I use STM32F407VTG6 controller and try to receive data from SPI using DMA. Then I want to process data on DMA Complete Transfer Interrupt. But when Complete Transfer Interrupt is occurred I see that TEIF (transfer error interrupt flag) is set. After this DMA can't be started. This is part of my code:
static void DmaInit()
{
DMA_InitTypeDef dma;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
// DMA for Rx
dma.DMA_Channel = DMA_Channel_3;
dma.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
dma.DMA_Memory0BaseAddr = 0; // will be set later
dma.DMA_DIR = DMA_DIR_PeripheralToMemory;
dma.DMA_BufferSize = 1; // will be set later
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
dma.DMA_Mode = DMA_Mode_Normal;
dma.DMA_Priority = DMA_Priority_High;
dma.DMA_FIFOMode = DMA_FIFOMode_Disable;
dma.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_DeInit(DMA2_Stream2);
DMA_Init(DMA2_Stream2, &dma);
// Enable DMA Interrupt on complete transfer
NVIC_EnableIRQ(DMA2_Stream2_IRQn);
DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE);
}
// It run on external interrupt
static void DmaStart(uint32_t bufferSize, uint32_t* rxBuffer)
{
// Start DMA for reading
DMA2_Stream2->NDTR = bufferSize;
DMA2_Stream2->M0AR = (uint32_t)rxBuffer;
DMA_Cmd(DMA2_Stream2, ENABLE);
}
static void SpiInit()
{
SPI_InitTypeDef spi;
// Enable clock for SPI
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// SPI settings
SPI_StructInit(&spi);
spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
spi.SPI_Mode = SPI_Mode_Master;
spi.SPI_DataSize = SPI_DataSize_8b;
spi.SPI_CPOL = SPI_CPOL_Low;
spi.SPI_CPHA = SPI_CPHA_2Edge;
spi.SPI_NSS = SPI_NSS_Soft;
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
spi.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &spi);
SPI_Cmd(SPI1, ENABLE);
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
}
I just finished my SDIO+DMA this days, some notes maybe useful for you:
Clear Flags before enabling the stream
DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_FEIF2|DMA_FLAG_DMEIF2|DMA_FLAG_TEIF2|DMA_FLAG_HTIF2|DMA_FLAG_TCIF2);
Clear EN bit in the DMA_SxCR Register, Wait Until the EN bit is read as 0 before DMA_Init()
DMA_Cmd(DMA2_Stream2, DISABLE);
while (DMA2_Stream2->CR & DMA_SxCR_EN);
When use DMA_FIFOMode_Disable (Direct Mode), data width is determined by DMA_PeripheralDataSize (PSIZE), DMA_MemoryDataSize (MSIZE) is ignored
Memory address must be aligned to your selected data width (HalfWord)
Reference:
STM32F407XX Reference Manual - Chapter 10 DMA Controller
STM32F4XX Standard Peripheral Library

Resources