2019-05-18 08:19 AM
Hello,
I'm learning to use the SPI module. I'm using an STM32F446RET development board. I want to configure the SPI1 module and send some message 'out into the world'(this means I only have one master devices which sends some bytes and no slave to recieve them).
I have configured the pins in this fasion:
I have written a code that's supposed to send 1's continuously.
main.c(please ignore my comments at the end :D)
#include "init_functions.h"
#include<string.h>
void SPI_SendData(uint8_t *strbuff, uint32_t len)
{
while(len > 0)
{
while( !(SPI1->SR & 2) ); // wait untill tx buffer is not empty
SPI1->DR = *strbuff;
len--;
strbuff++; // point to next byte
}
}
void SPI_SendOnes()
{
while( (SPI1->SR & 2) == 0 ); // wait untill tx buffer is not empty
SPI1->DR = 1;
int a = SPI1->DR;
}
int main()
{
char user_data[] = "Hello world";
init_GPIOS_SPI1();
config_SPI1();
while(1)
{
// send data
SPI_SendOnes();
while( (SPI1->SR & (1 << 7)) ) // wait till SPI is not busy
{
}
// disable spi
//SPI1->CR1 &= ~( 1 << 6 );
}
return 0;
}
//SPI1->SR[bit 7] -> busy flag(0 - not busy; 1 - SPI (or I2S) is busy in communication or Tx buffer is not empty ). set/cleared by hw
//SPI1->SR[bit 1] -> transmit buffer empty(1 - tx buffer empty; 0 not empty)
//SPI1->SR[bit 0] -> recieve buffer...
//SPI1->DR -> data register(data recieved or to be transmitted). A write to the data register will write into the Tx buffer and a read
// from the data register will return the value held in the Rx buffer.
int_functions.c
#include "init_functions.h"
void init_GPIOS_SPI1(void)
{
/*
SPI1 - APB2(90Mhz)
Address range SPI_1: 0x4001 3000 - 0x4001 33FF
PA4 - SPI1_NSS
PA5 - SPI1_SCK
PA6 - SPI1_MISO
PA7 - SPI1_MOSI
*/
/* Enable GPIOA bus clock */
RCC->AHB1ENR |= 1;
/* Configure SPI pins */
//SCLK(PA5)
GPIOA->MODER |= ( 1 << 11 ); // af mode
GPIOA->MODER &= ~( 1 << 10 );
GPIOA->OTYPER &= ~( 1 << 5 ); // push-pull
GPIOA->OSPEEDR |= ( 1 << 11 ); // fast speed
GPIOA->OSPEEDR &= ~( 1 << 10 );
GPIOA->PUPDR &= ~( 1 << 10 ); // no pull up/down
GPIOA->PUPDR &= ~( 1 << 11 );
GPIOA->AFR[0] |= ( 1 << 22 ); // AF5(SPI1_SCK)
GPIOA->AFR[0] |= ( 1 << 20 );
//MOSI(PA6)
GPIOA->MODER |= ( 1 << 13 ); // af mode
GPIOA->MODER &= ~( 1 << 12 );
GPIOA->OTYPER &= ~( 1 << 6 ); // push-pull
GPIOA->OSPEEDR |= ( 1 << 13 ); // fast speed
GPIOA->OSPEEDR &= ~( 1 << 12 );
GPIOA->PUPDR &= ~( 1 << 12 ); // no pull up/down
GPIOA->PUPDR &= ~( 1 << 13 );
GPIOA->AFR[0] |= ( 1 << 26 ); // AF5(SPI1_MOSI)
GPIOA->AFR[0] |= ( 1 << 24 );
//MISO(PA7)
//NSS(PA4)
}
void config_SPI1(void)
{
RCC->APB2ENR |= ( 1 << 12 ); //SPI1 clock enable
SPI1->CR1 |= ( 1 << 15 ); // 1 line bidirectional mode selected
SPI1->CR1 |= ( 1 << 14 ); // output enabled: transmit only mode
SPI1->CR1 &= ~( 1 << 11 ); // 8-bit data frame format for transmission/reception
SPI1->CR1 &= ~( 1 << 7 ); // MSB transmitted first
SPI1->CR1 |= ( 1 << 6 ); // SPI1 enabled
SPI1->CR1 |= ( 1 << 2 ); // master configuration
SPI1->CR1 |= ( 1 << 1 ); // CK to 1 when idle
SPI1->CR1 &= ~( 1 << 0 ); // first clock transition is the first data capture edge
}
I have checked with an oscilloscope and expected to see a continous clock signal as the application is supposed to continously send 1's to the outside world. I have put the probe on the PA5(CLK) pin but it just stays low.
I then inspected the code and I have discovered that the code halts when it gets to this line the second time:
while( (SPI1->SR & 2) == 0 ); // wait untill tx buffer is not empty
Which means the data I want to send gets put into the transmit/recieve buffer but then it does not get put into the TX buffer to get transmited through the bus into the 'world'.
I cannot see what I've configured wrong. Please if possible help me correct this. Thanks.
2019-05-18 08:42 AM
Program the SPI to be in bi directional mode even if you are only connecting MOSI to the pin world.
Enable SPI only when all is configured (master config done earlier)
Try to configure NSS is by SW and it is internal low.
Any better?
For easier debug, you should initialize all the SPI registers so you don't miss anything.
2019-05-18 08:53 AM
.
2019-05-18 10:18 AM
hello. I tried all that. Still same result. Code halts there :(
2019-05-18 10:44 AM
Read out the content of SPI and relevant GPIO registers and check/post.
JW
2019-05-18 11:00 AM
I haven't used any STM32F4 chips, so maybe they're different, but I'll go out on a limb and say I think you have your test for the TXE bit in the SR register inverted. On the STM32F103 at least, that bit is set to 1 when the transmit buffer is empty and can be written to, and zero when it's full and can't.
But if that's the case, I don't understand how you get through the first call to SPI_SendOnes() and only hang the second time.
Also (on STM32F103) you don't have to check the BUSY flag. That indicates sending of data is in progress, but as soon as you put data into DR it gets transferred to the internal shift register (if not BUSY) and starts getting shifted out on MOSI. The TXE bit is immediately cleared and you can put more data in. It will be buffered (with TXE now high) until the previous data has been shifted out at which point the process repeats.
2019-05-18 11:43 AM
SPI1->CR1 |= ( 1 << 15 ); // 1 line bidirectional mode selected
This line to me is the main contributor to more debug time...
2019-05-18 12:07 PM
I have tried both with 1 line bidirectional mode as well as two line unidirectional mode. Same result. Does that matter anyway since I'm just sending messages and there is no slave to recieve/send back any data?
2019-05-19 09:25 PM
Now that you haven't configured NSS, it might be good idea to check for mode error.
2019-05-19 09:41 PM
a code extract that works on my board...
void SPIP_MasterConfigureParallelMode(SPIP_t* pSPIP) {
// reconfigure the serial SPI MISO/MOSI lines for leftmost detection
// L_MOSI as output low, R_MISO as input with pull-up, R_MOSI as input with pull-up
IO_PinSetLow(pSPIP->pL_MOSI_Det); // optional
IO_PinConfigure(pSPIP->pL_MOSI_Det); // optional
IO_PinConfigure(pSPIP->pR_MISO_Det); // disable SPI AF as input with pull-up
IO_PinConfigure(pSPIP->pR_MOSI_Det); // disable SPI AF as input with pull-up
// activate the parallel SPI MISO/MOSI lines
IO_PinConfigure((IO_Pin_t*)pSPIP->pMOSI);
IO_PinConfigure((IO_Pin_t*)pSPIP->pMISO);
}
HAL_StatusTypeDef SPIP_MasterParallelTransferStart_ISR(SPIP_t* pSPIP){
uint16_t ByteCount = 0; //pSPIP->ParallelByteCount;
ByteCount = BiggestParallelByteSize();
if(HAL_SPI_TransmitReceive_DMA(pSPIP->hspi, pSPIP->pParallelTxBuffer, pSPIP->pParallelRxBuffer, ByteCount/2) != HAL_OK) // after we will use bytesize if we want to optimize
TrapError();
return HAL_OK;
}
HAL_StatusTypeDef SPIP_MasterConfigureSpi(SPI_HandleTypeDef *hspi){
hspi->Instance = SPI2;
hspi->Init.Direction = SPI_DIRECTION_2LINES;
hspi->Init.CLKPhase = SPI_PHASE_1EDGE;
hspi->Init.CLKPolarity = SPI_POLARITY_LOW;
hspi->Init.DataSize = SPI_DATASIZE_16BIT;
hspi->Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi->Init.TIMode = SPI_TIMODE_DISABLE;
hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi->Init.CRCPolynomial = 7;
hspi->Init.CRCLength = SPI_CRC_LENGTH_8BIT;
hspi->Init.NSS = SPI_NSS_SOFT;
hspi->Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;//16;
hspi->Init.Mode = SPI_MODE_MASTER;
return (HAL_SPI_Init(hspi));
}
void SPIP_MasterConfigureDma(SPI_HandleTypeDef *hspi){
/* Configure the DMA handler for Transmission process */
pSPIP->hdma_tx->Instance = DMA1_Channel2;//DMA1_Channel5;
pSPIP->hdma_tx->Init.Request = DMA_REQUEST_SPI2_TX;//DMA_REQUEST_1;
pSPIP->hdma_tx->Init.Direction = DMA_MEMORY_TO_PERIPH;
pSPIP->hdma_tx->Init.PeriphInc = DMA_PINC_DISABLE;
pSPIP->hdma_tx->Init.MemInc = DMA_MINC_ENABLE;
pSPIP->hdma_tx->Init.PeriphDataAlignment = /*DMA_PDATAALIGN_BYTE;*/ DMA_PDATAALIGN_HALFWORD;
pSPIP->hdma_tx->Init.MemDataAlignment = /*DMA_MDATAALIGN_BYTE;*/ DMA_MDATAALIGN_HALFWORD;
pSPIP->hdma_tx->Init.Mode = DMA_NORMAL;
pSPIP->hdma_tx->Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(pSPIP->hdma_tx);
/* Associate the initialized DMA handle to the the SPI handle */
__HAL_LINKDMA(hspi, hdmatx, *(pSPIP->hdma_tx));
/* Configure the DMA handler for Reception process */
pSPIP->hdma_rx->Instance = DMA1_Channel1;//DMA1_Channel4;
pSPIP->hdma_rx->Init.Request = DMA_REQUEST_SPI2_RX;//DMA_REQUEST_1;
pSPIP->hdma_rx->Init.Direction = DMA_PERIPH_TO_MEMORY;
pSPIP->hdma_rx->Init.PeriphInc = DMA_PINC_DISABLE;
pSPIP->hdma_rx->Init.MemInc = DMA_MINC_ENABLE;
pSPIP->hdma_rx->Init.PeriphDataAlignment = /*DMA_PDATAALIGN_BYTE;*/ DMA_PDATAALIGN_HALFWORD;
pSPIP->hdma_rx->Init.MemDataAlignment = /*DMA_MDATAALIGN_BYTE;*/ DMA_MDATAALIGN_HALFWORD;
pSPIP->hdma_rx->Init.Mode = DMA_NORMAL;
pSPIP->hdma_rx->Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(pSPIP->hdma_rx);
/* Associate the initialized DMA handle to the the SPI handle */
__HAL_LINKDMA(hspi, hdmarx, *(pSPIP->hdma_rx));
}
The code use interrupt on block transfer (not shown). Here is to get the output alive. This is for the STM32L4R5