cancel
Showing results for 
Search instead for 
Did you mean: 

No clock signal on PA5 bit(SPI1-SCK)

PPopo
Senior

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:

  • PA5: Alternate function(5-SPI1_SCK), push-pull, fast speed, no PUPD
  • PA6: Alternate function(5-SPI1_MOSI), same configuration as PA5
  • PA7(MISO) and PA4(NSS) I have not configured as I'm not using any slaves

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.

15 REPLIES 15
S.Ma
Principal

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.

.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

hello. I tried all that. Still same result. Code halts there :(

Read out the content of SPI and relevant GPIO registers and check/post.

JW

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.

S.Ma
Principal

SPI1->CR1 |= ( 1 << 15 ); // 1 line bidirectional mode selected

This line to me is the main contributor to more debug time...

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?

turboscrew
Senior III

Now that you haven't configured NSS, it might be good idea to check for mode error.

S.Ma
Principal

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