2014-10-01 10:53 AM
Hello,
I am trying to use an STM32f4 discovery board to receive data in slave mode over SPI. I am using HAL drivers generated from STMCube and using interrupts to receive the data. I can read all the data fine when the master ( NOT the discovery board) sends out data lower than 6 MHz. Anything over 6 MHz the data cannot be read correctly. I am using SPI4 to read the data. Below is my initialization code.static void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
/* Enable Power Control clock */
__PWR_CLK_ENABLE();
/* The voltage scaling allows optimizing the power consumption when the device is
clocked below the maximum system frequency, to update the voltage scaling value
regarding system frequency refer to product datasheet. */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/* Enable HSE Oscillator and activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
if(HAL_PWREx_ActivateOverDrive() != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
}
SPI4_Init->Instance = spi;
SPI4_Init->Init.Mode = mode;
SPI4_Init->Init.NSS = SPI_NSS_SOFT;
SPI4_Init->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
SPI4_Init->Init.FirstBit = bit_order;
SPI4_Init->Init.CRCPolynomial = 7; // No CRC
HAL_SPI_Init(SPI4_Init);
GPIO_InitTypeDef GPIO_Init;
GPIO_Init.Pin = (GPIO_PIN_2 | GPIO_PIN_6 | GPIO_PIN_5);
GPIO_Init.Mode = GPIO_MODE_AF_PP;
GPIO_Init.Speed = GPIO_SPEED_HIGH;
GPIO_Init.Alternate = GPIO_AF6_SPI4;
HAL_GPIO_Init(gpio_module, &GPIO_Init);
__GPIOE_CLK_ENABLE();
//initialize SPI4 interrupts
HAL_NVIC_EnableIRQ(SPI4_IRQn);
//Start receiving data!
HAL_SPI_Receive_IT(SPI4_Init, data, buffersize);
Thanks for the help!
2014-10-02 12:19 AM
I suppose that incoming character are processed one by one under interrupt.
This does not surprise me when you cannot go over 6Mb, CPU is 100% busy and soon you start missing incoming data. A design with DMA will help to cross that boundary.2014-10-02 12:23 AM
Any conflicting hardware on the used pins on the DISCOVERY board? Incorrect SPI mode set?
JW2014-10-02 06:52 AM
Thank you for the responses!
I will try using DMA and see how that goes. After switching off of interrupts and receiving data synchronously in blocking mode I was able to achieve up to 8 MHz for the data but anything over that did not work correctly. I was under the impression as the APB2 clock can reach 42 MHz that that was the top speed but 8 MHz is far slower. Am I wrong in this assumption or could my settings be wrong somewhere? Also, I removed the LCD screen that comes mounted on the board to avoid conflicting hardware issues. If I can't get anything I will try and switch to a different SPI to see how that goes. I believe the spi mode is correct as it works under a certain speed. The requirements are for it to be in spi read only slave mode. I will get back to you with my findings!2014-10-02 07:07 AM
IMHO everything is fine, you simply hit the maximum speed of your system to execute your code.
Optimization may start now: * You use FULL_ASSERT with the library ? Oh! just disable it and it will save a lot of cycles. * You use a/the library to access SPI (checking flags and reading incoming data) ? Oh, write code to directly access registers in the reception loop, it will save another tons of cycles. At 8Mbit for SPI, you only produce one byte per µs. You are far, far away to saturate the internal bus with that amount of data. Saturating your CPU with code is more frequent than you may think !2014-10-02 07:14 AM
> I will try using DMA and see how that goes. After switching off of interrupts and receiving
> data synchronously in blocking mode I was able to achieve up to 8 MHz for the data but > anything over that did not work correctly. That indicates that Laurent was right. More things to try in that case is to get rid of the ''library'' or ''HAL'' and read the registers directly; and switch on compiler optimisation if it is off. Also, it would help if you would be more precise with ''does not work correctly'' - are whole bytes/words missing, or are they shifted somehow when the critical speed is reached. > I was under the impression as the APB2 clock can reach 42 MHz that that was the top > speed but 8 MHz is far slower. Am I wrong in this assumption or could my settings be wrong > somewhere? Whatever the capability of the SPI *hardware*, if it chokes on the other side of the pipe, where the processor/DMA picks the data and stores it into RAM, that would be the ultimate limitation.* I believe the spi mode is correct as it works under a certain speed.
* The requirements are for it to be in spi read only slave mode. I mean, is the clock phase (CPHA) and, more importantly, polarity (CPOL) set properly (i.e. according to the master device's specification)? It tends to ''work'' even if incorrectly set, as usually there is *some* skew between the data and clock. JW2014-10-02 08:16 AM
FULL_ASSERT was not enabled so there won't be any optimizations there but I am going to look into compiler optimizations and go with DMA first and see how that goes and if that still doesn't seem to work I will read/write to the registers directly. I have confidence one (or both) of these will work!
Interestingly enough the bits aren't shifted but rather whole bytes are missing. It seems logical to me that the individual bits would be shifted as it would be likely one bit is missed rather than a whole byte. Any ideas why this may be?The clock phase and clock polarity are set as described by the spi component. Thank you for all your help!2014-10-06 01:52 PM
I changed to direct register access and it works much better. I still cannot get over 15 MHz however. When it messes up it misses a whole byte. I still do not know why this would be the case. This is my new receiving code.
for (i = 0; i <
size
; i++) {
//Wait for rxne flag
while (((SPIx->SR)&(0x0001))==0);
//read data and clear register
pData[i]=SPIx->DR;
}
Are there any other optimizations I can do to get a better performance? Loop unrolling doesn't appear to have any effect. I don't think DMA will work for my case as I need to store only every other byte when I read in the data (not shown above).
2014-10-07 12:06 AM
Where is pData located?
JW2014-10-07 07:33 AM
pData is a pointer passed into the function. It is an unsigned char array of 145456 elements.
Thank you for all your help.