2024-11-13 08:33 AM - edited 2024-11-13 08:34 AM
I am writing my own SPI driver, and using two B-L072Z-LRWAN1 boards to test them; one as an SPI master and one as a slave. On the master device, whenever I enable the SPE bit in the function SPI_Transmit(), it never actually sets to '1', even though the configuration registers are set to the programmed values. Why is this?
Here is the initialization function for the slave device. I am using the HAL SPI handlers so I could debug the SPI registers:
void SPI2_Init_Slave(SPI_HandleTypeDef * hspi)
{
/************** STEPS TO FOLLOW *****************
1. Wait for the TXE bit to set in the Status Register
2. Write the data to the Data Register
3. After the data has been transmitted, wait for the BSY bit to reset in Status Register
4. Clear the Overrun flag by reading DR and SR
************************************************/
hspi->Instance = SPI2;
if(hspi->Instance->CR1 & SPI_CR1_SPE)
{
SPI_Disable(hspi);
}
SPI_MspInit(hspi);
//enable the clocks for the peripheral
//turn on 2-line unidirectional mode
hspi->Instance->CR1 &= ~SPI_CR1_BIDIMODE;
//turn off CRC
hspi->Instance->CR1 &= ~SPI_CR1_CRCEN;
//8-bit frame format
hspi->Instance->CR1 &= ~SPI_CR1_DFF;
//turn off software slave select
hspi->Instance->CR1 &= ~(SPI_CR1_SSM | SPI_CR1_SSI);
//Frame format: MSB first
hspi->Instance->CR1 &= ~SPI_CR1_LSBFIRST;
//clear prescalar register
hspi->Instance->CR1 &= ~SPI_BAUDRATEPRESCALER_256;
//set prescalar register
hspi->Instance->CR1 |= SPI_BAUDRATEPRESCALER_32;
//slave mode
hspi->Instance->CR1 &= ~SPI_CR1_MSTR;
//turn off RX only
hspi->Instance->CR1 &= ~SPI_CR1_RXONLY;
//set clock polarity
hspi->Instance->CR1 &= ~(SPI_CR1_CPOL);
//set clock phase to trigger on first data capture edge
hspi->Instance->CR1 &= ~(SPI_CR1_CPHA);
hspi->Instance->CR2 = 0;
}
Here is the initialization function for the master device:
void SPI2_Init_Master(SPI_HandleTypeDef * hspi)
{
/************** STEPS TO FOLLOW *****************
1. Wait for the TXE bit to set in the Status Register
2. Write the data to the Data Register
3. After the data has been transmitted, wait for the BSY bit to reset in Status Register
4. Clear the Overrun flag by reading DR and SR
************************************************/
hspi->Instance = SPI2;
if(hspi->Instance->CR1 & SPI_CR1_SPE)
{
SPI_Disable(hspi);
}
SPI_MspInit(hspi);
//enable the clocks for the peripheral
//RCC->APB2ENR |= RCC_APB1ENR_SPI2EN;
//turn on 2-line unidirectional mode
hspi->Instance->CR1 &= ~SPI_CR1_BIDIMODE;
//turn off CRC
hspi->Instance->CR1 &= ~SPI_CR1_CRCEN;
//8-bit frame format
hspi->Instance->CR1 &= ~SPI_CR1_DFF;
//turn off slave select software control
hspi->Instance->CR1 &= ~(SPI_CR1_SSM | SPI_CR1_SSI);
//Frame format: MSB first
hspi->Instance->CR1 &= ~SPI_CR1_LSBFIRST;
//clear prescalar register
hspi->Instance->CR1 &= ~SPI_BAUDRATEPRESCALER_256;
//set prescalar register
hspi->Instance->CR1 |= SPI_BAUDRATEPRESCALER_32;
//master mode
hspi->Instance->CR1 |= SPI_CR1_MSTR;
//turn off RX only
hspi->Instance->CR1 &= ~SPI_CR1_RXONLY;
//set clock polarity
hspi->Instance->CR1 &= ~(SPI_CR1_CPOL);
//set clock phase to trigger on first data capture edge
hspi->Instance->CR1 &= ~(SPI_CR1_CPHA);
hspi->Instance->CR2 = 0;
}
pin initialization:
void SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hspi->Instance==SPI1)
{
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PA7 ------> SPI1_MOSI
PA6 ------> SPI1_MISO
*/
GPIO_InitStruct.Pin = PB3_RESERVED_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
HAL_GPIO_Init(PB3_RESERVED_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = PA7_RESERVED_Pin|PA6_RESERVED_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if(hspi->Instance==SPI2)
{
/* Peripheral clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI1 GPIO Configuration
PB13 ------> SPI1_SCK
PB15 ------> SPI1_MOSI
PB14 ------> SPI1_MISO
*/
/*Configure GPIO pin : SPI_NSS_Pin. conflicting results: either AF, pull-up, PP, HS,
* or as output */
/*Configure GPIO pins : PA7_RESERVED_Pin PA6_RESERVED_Pin */
GPIO_InitStruct.Pin = SPI2_MOSI_Pin | SPI2_CLK_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SPI2_MISO_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
}
and the Tx/Rx functions:
void SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *data, uint32_t size)
{
int i = 0;
uint8_t temp;
//enable SPI if not already enabled
if(!(hspi->Instance->CR1 & SPI_CR1_SPE))
SPI_Enable(hspi);
hspi->Instance->CR1 |= SPI_CR1_SPE;
while(i<size)
{
while(!((hspi->Instance->SR)&(1<<1)));
hspi->Instance->DR = data[i];
i++;
}
/*During discontinuous communications, there is a 2 APB clock period delay between the
write operation to the SPI_DR register and BSY bit setting. As a consequence it is
mandatory to wait first until TXE is set and then until BSY is cleared after writing the last
data.
*/
while(!((hspi->Instance->SR)&(1<<1)));
while(!((hspi->Instance->SR)&(1<<7)));
// Clear the Overrun flag by reading DR and SR
temp = hspi->Instance->DR;
temp = hspi->Instance->SR;
}
void SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *data, uint32_t size)
{
/************** STEPS TO FOLLOW *****************
1. Wait for the BSY bit to reset in Status Register
2. Send some Dummy data before reading the DATA
3. Wait for the RXNE bit to Set in the status Register
4. Read data from Data Register
************************************************/
if(!(hspi->Instance->CR1 & SPI_CR1_SPE))
{
SPI_Enable(hspi);
}
while(size)
{
// wait for BSY bit to Reset. This will indicate that SPI is not busy in communication
while(((hspi->Instance->SR)&(SPI_SR_BSY)));
// send dummy data
hspi->Instance->DR = 0;
// Wait for RXNE to set. This will indicate that the Rx buffer is not empty
while(!((hspi->Instance->SR)&(SPI_SR_RXNE)));
*data++ = (hspi->Instance->DR);
size--;
}
}
void SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)
{
if(hspi->Instance->CR1 & SPI_CR1_SSM)
CS_Enable(hspi);
if(!(hspi->Instance->CR1 & SPI_CR1_SPE))
SPI_Enable(hspi);
SPI_Transmit(hspi, pTxData, Size);
SPI_Receive(hspi, pRxData, Size);
if(hspi->Instance->CR1 & SPI_CR1_SSM)
CS_Disable(hspi);
if(hspi->Instance->CR1 & SPI_CR1_SPE)
SPI_Disable(hspi);
}
and clock configuration, which is the same for master and slave:
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_6;
RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_3;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_RTC;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
Solved! Go to Solution.
2024-11-13 09:18 AM
1. Start by reading this:
- to understand why your TransmitReceive is incorrect.
2. Replace the funny sequences of &=, |= with single assignment to CR1. To setup the SPI, write 0 to CR1 (or maube not if it wasn't configured before) then write all the bits you want to set together with SPE with a single assignment.
3. SPE must be set after writing to CR2, but in your case there is no need to write to CR2 at all.
2024-11-13 09:18 AM
1. Start by reading this:
- to understand why your TransmitReceive is incorrect.
2. Replace the funny sequences of &=, |= with single assignment to CR1. To setup the SPI, write 0 to CR1 (or maube not if it wasn't configured before) then write all the bits you want to set together with SPE with a single assignment.
3. SPE must be set after writing to CR2, but in your case there is no need to write to CR2 at all.
2024-11-13 09:49 AM
1. Thanks, obviously I was misunderstanding transmit/receive.
2. This worked for me. Weirdly enough, sometimes I would get SPI communication showing up on the oscilloscope with the previous method. Then I changed the baud rate prescalar and it didn't work anymore. Changed it back, and still nothing. In the HAL functions, peripherals are enabled by setting the enable bit only using an OR mask. Why does it not work in this case?
2024-11-13 10:13 AM
Hello @JustSomeGuy ,
Try putting a break point additionally into the IP interrupt service to be sure no error occurs, and monitor the SPI register access flow. So, check the SPI register values before and after the SPI transmit tentative.
Try changing bit running NSS as hardware.