STM32 SPI Transmit/Receive Using HAL - Page 1
Forum section: electronics / general

author: jmsigler
author_rank: Regular Contributor
timestamp: October 31, 2016, 01:41:44 am
content: Hello,I am working with an STM32F446 and trying to use the spi peripheral using HAL libs, but my program keeps crashing. Below is how I am using the HAL_SPI_TransmitReceive() function. Could someone comment on the correctness of this code? For reference, I am using the free tool chain on eclipse and have other things like UART and GPIO working fine (I think).Code://Set cs pin lowHAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET);//Transmit 1 byte and receive 3. Actually receive 4, but the first doesn't matter.HAL_SPI_TransmitReceive(&hspi1, Cmd, data, 4, 0xFF);HAL_Delay(1);//Set cs pin highHAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_SET);

author: newbrain
author_rank: Super Contributor
timestamp: October 31, 2016, 07:07:34 am
content: Show us a bit more, don't be shy....This code could be fine, or not, but it's not possible to comment unless at least the variable declarations and initial SPI setup are shown.First shot in the dark: are Cmd and data correctly sized and typed?And what kind of "crash" is happening?Unhandled exception, infinite loop, or...?

author: jmsigler
author_rank: Regular Contributor
timestamp: October 31, 2016, 07:48:45 am
content: Sure thing, here is an abridged version of the code. I've attached the full code below too if it helps. I am getting a hard fault on the uC. The weird thing is that it is almost always after the same amount of time (about 8min 40s) that it will crash. This is leading me to believe that I am filling a buffer/stack somewhere or a timer is tripping and causing a fault?Code:intmain(int argc, char* argv[]){uint8_t Cmd = {0, 0, 0, 0};uint8_t data = {0, 0, 0, 0};uint32_t D2;__HAL_SPI_ENABLE(&hspi1);Cmd = 0x00;HAL_Delay(1);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET);HAL_SPI_TransmitReceive(&hspi1, Cmd, data, 4, 0xFF);HAL_Delay(1);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_SET);D2 = ((uint16_t)data << 16) | ((uint16_t)data << 8) | ((uint16_t)data);}//These functions are also being called at startup along with GPIO init which is structured the same.void SystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct;RCC_ClkInitTypeDef RCC_ClkInitStruct;__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = 16;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLM = 8;RCC_OscInitStruct.PLL.PLLN = 168;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 7;RCC_OscInitStruct.PLL.PLLR = 2;HAL_RCC_OscConfig(&RCC_OscInitStruct);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_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);/* SysTick_IRQn interrupt configuration */HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);}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 = 10;if (HAL_SPI_Init(&hspi1) != HAL_OK){Error_Handler();}}void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi){GPIO_InitTypeDef GPIO_InitStruct;if(hspi->Instance==SPI1){/* USER CODE BEGIN SPI1_MspInit 0 *//* USER CODE END SPI1_MspInit 0 *//* Peripheral clock enable */__HAL_RCC_SPI1_CLK_ENABLE();/**SPI1 GPIO ConfigurationPA5 ------> SPI1_SCKPA6 ------> SPI1_MISOPA7 ------> SPI1_MOSI*/GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USER CODE BEGIN SPI1_MspInit 1 *//* USER CODE END SPI1_MspInit 1 */}else if(hspi->Instance==SPI3){/* USER CODE BEGIN SPI3_MspInit 0 *//* USER CODE END SPI3_MspInit 0 *//* Peripheral clock enable */__HAL_RCC_SPI3_CLK_ENABLE();/**SPI3 GPIO ConfigurationPC10 ------> SPI3_SCKPC11 ------> SPI3_MISOPC12 ------> SPI3_MOSI*/GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);/* USER CODE BEGIN SPI3_MspInit 1 *//* USER CODE END SPI3_MspInit 1 */}}

author: tatus1969
author_rank: Super Contributor
timestamp: October 31, 2016, 10:19:36 am
content: can't find anything suspicious. Try to trace back the code location of that exception. If your toolchain does not help you with it, you can look at the CPU regs and stacks.

author: newbrain
author_rank: Super Contributor
timestamp: October 31, 2016, 12:07:27 pm
content: No, not much to raise suspicion, at first look. I might go through the code more carefully later.I have some quick comments on the code, though:It seems to be affected by a bit of "cargo cult programming", with repetitions and superfluous calls.Many "stm32f4XXX" useless includes, stm32f4_hal.h is enough, it includes all that's needed.Why initialization code is not in main? I assume __initialize_hardware is called in the startup code, but that's an unneeded dependency (none of the startups I use does that).There's no need for the __HAL_ppp_ENABLE(), it's done by HAL when calling the high level functions.The delays after using HAL_SPI_TransmitReceive() or ..Receive() are redundnant: the HAL waits for RXNE and BSY to make sure that reception and transmission have completed. The same goes when using ...Transmit(), HAL will check the BSY flag in addition to TXE.The SystemCoreClockUpdate() call is redundant: it's already taken care of in SystemClock_Config(), by the included HAL calls.What's the point of casting to uint16_t when calculating D2?The format string in snprintf uses %i, but Temp is unsigned.Very minor issue, style preference: why increment Cmd at the beginning of the cycle (hence needing to start one step below) rather than at the end?Last, but not least, and actually quite important: Why the pragmas? What are the problems they are hiding?Then a couple of questions:Is the abridged version of the code behaving in the same way?If not, why are you pinpointing the SPI calls (e.g. stack trace)?

author: jmsigler
author_rank: Regular Contributor
timestamp: October 31, 2016, 04:04:40 pm
content: Quote from: newbrain on October 31, 2016, 12:07:27 pmThanks for the help. A lot of the code was generated by CubeMX and some by the stm32f4 template included with the gnuarmeclipse plugin. I'm not sure why they do initalize hardware, but I have been following that as convention. I will try changing the project structure and see if that does anything. Same with the pragmas, those were included with the template and I don't know enough about what they are doing to change them. Some of the other things you mentioned are just me being dumb.I pinpointed SPI because I had thought the UART was behaving nominally, although I tried a program that just sent continuous UART messages and that crashed as well. I was hoping that this was because I was not correctly checking for the transmission to end, but if you are saying HAL does this, then its probably not whats causing the crashing.Out of curiosity, what tool chain do you recommend for STM/ARM development? I highly doubt this is the tool chains fault, but it would be nice to have a sanity check and make 100% sure its the code and not something external.

author: newbrain
author_rank: Super Contributor
timestamp: October 31, 2016, 10:58:51 pm
content: Quote from: jmsigler on October 31, 2016, 04:04:40 pmYes, the (skid?) marks of CubeMX are there, and I checked the startup code for gnuarmeclipse, that actually calls __initialize_hardware, so no problems there.I find it a bit pointless, but they are doing it (I can imagine) to have the same startup code for different ARM MCUs.The pragmas suppress some warnings, I'm (up to a point) fine with the unused parameters one, but the other two are quite important to keep enabled.QuoteI disagree. Your code is quite reasonable, structured and commented.And also looks correct. I've seen (and done) much worse.My cargo cult programming comment was due to mostly to what I now understand were artifacts of the template project you've used.QuoteA program that just sends out bytes on the UART should not crash.In fact, I've just adapted your program to run on a Nucleo L476 (only minor changes were needed) and it's being going on for quite a long time now (I have no SPI slave, so I just toggle the MISO line with a jumper, and the printout changes.I have no idea if the calculation are correct, but the logic works.Is the hardware OK? Noisy/unstable supply? EMI? Other code interfering?I also removed the delays between the SPI and the GPIO call, and you can see the result in the attached screenshot: the GPIO pin goes high after the transmission is complete (spikes-a-plenty: GPIO pins are on "Ludicrous speed", and I just used wires...).QuoteI agree, don't think it's the fault of the toolchain.As for which environment to use I'm a bit of a oddball here, as I like Visual Studio (free, community edition) + VisualGDB (commercial tool, 30d demo available), it just works for me and supports most ARM and some non-ARM (e.g. ESP8266) platforms.I also useVSCode(also on Linux) and self written makefiles/scripts with OpenOCD and the gnu-arm-none-eabi compiler.CubeMX is used just to generate the initialization code, then I might or might not use the HAL, depending on needs and inspiration.For kicks, I used Atollic True Studio Lite (free) for testing the code tonight, but my allergy to Eclipse kicked in after a while.If you are into Eclipse, thenSystem Workbench, ST's own IDE, works OK; you could find it easier to handle than gnuarmeclipse as it's directly supported by CubeMX.

author: jmsigler
author_rank: Regular Contributor
timestamp: November 01, 2016, 02:50:20 am
content: Thanks for taking the time to check it yourself. It is possible it is the hardware (it is custom), but I think I remember getting the same with a nucleo, so Ill try that again and see if I get the same results.Here is the uart code that will crash:Code:intmain(int argc, char* argv[]){char msg[] = "Speed test\n";__HAL_UART_ENABLE(&huart3);while (1){HAL_UART_Transmit(&huart3, (uint8_t*)msg, strlen(msg), 20);}}It runs for a while and then stops on SIGTRAP: Trace/breakpoint trap. The UART lines look fine to me. There is a bit of ringing, but not enough that I would think it would cause problems.

author: tatus1969
author_rank: Super Contributor
timestamp: November 01, 2016, 06:03:00 am
content: SIGTRAP is a good hint, because it suggests that your program isn't actually crashing. Try to run your code with debugger detached, does it make difference? Google gave me thishttp://embdev.net/topic/236161Have you tried to trace back the code location that the micro was at when the exception happened?Sometimes it also helps reducing the debugger's communication speed, you may check the SWD/JTAG signals. I once had triangular shapes on an Olimex Tiny.

author: jmsigler
author_rank: Regular Contributor
timestamp: November 01, 2016, 06:53:31 am
content: I ended up doing a blank project and importing all of the code from HAL, which was recommended by the mastering stm32 book. After doing this and restructuring the project, it looks like it fixed the issue and is working well now. I think there were a lot of files from the gnuarmeclipse template that I wasn't interfacing with correctly with and this was causing issues. Thanks for the debugging suggestions everyone!

author: newbrain
author_rank: Super Contributor
timestamp: November 01, 2016, 10:48:59 am
content: Glad to hear you solved this,so it was (in some way) the toolchain, in the end.

author: jnz
author_rank: Frequent Contributor
timestamp: November 01, 2016, 05:35:56 pm
content: Some of you guys and the insistence to not use debuggers - but then run into problems that a debugger would clear up near instantly...

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: November 03, 2016, 03:42:19 am
content: Quote from: jnz on November 01, 2016, 05:35:56 pmThough not in this case...