STM32G0 SPI sometimes randomly doesn't receive anything on the line - Page 1
Forum section: electronics / general

author: nishantnidaria
author_rank: Contributor
timestamp: December 01, 2021, 08:31:27 am
content: I am using an STM32G030C8T6 (custom PCB) as an SPI (Slave device) which receives characters and data from a Raspberry pi (Master device).What i want to do?I want to power on raspberry pi using a timer in STM32 (Raspberry pi sends the timer value via SPI to STM32 before it shuts down).What is happening?It works really well! only sometimes (could be in 10 minutes or 10 hours or 10 days) STM32 doesn't receive the right timer value so ofcourse the raspberry pi stays powered on, which i don't want.another problem is that sometimes it powers on raspberry pi but doesn't receive anything on the SPI.So i am not sure if there's a problem in the PCB or the code or both.Here's is my receive function which i call in the main while loop:Code:void SPI_Receive_Commands(void){HAL_SPI_Receive(&hspi1, (uint8_t *)spi_buf, 10, 100);if(spi_buf == 't'){spi_flag = 0; // SPI data counter off (Timer that counts 45 seconds to check if SPI bus is not receiving anything at all then reset the MCU)cpt = timer1_cpt(spi_buf); // cpt is a "volatile uint16_t cpt" in the variables fileHAL_GPIO_WritePin(GPIOB, Debug_LED_Pin, GPIO_PIN_RESET);HAL_Delay(1500);HAL_GPIO_WritePin(GPIOB, Debug_LED_Pin, GPIO_PIN_SET);printmsg("Raspberry pi is going to sleep\r\n");pi_sleep_flag = 1;shutdownPi(1);HAL_GPIO_WritePin(GPIOA, Pi_EN_Pin, GPIO_PIN_RESET); // Shutdown raspberry piprintmsg("Raspberry pi is sleeping...Zzz\r\n");pi_pwron_counter = 0;pi_pwr_flag = 0;shutdownPi(0);memset(spi_buf,'*',10);spi_flag = 1; // SPI data counter on (Timer that counts 45 seconds to check if SPI bus is not receiving anything at all then reset the MCU)}}and the timer1_cpt function code is here:Code:__uint16_t timer1_cpt(char*tab_SPI) // Next Wakeup timer{__uint16_t cpt1=0;__uint16_t cpt1_max=7200; //2hoursint i = 0;int sizeString = 0;while (tab_SPI!=0x7E){i++;}sizeString = i;cpt1 = tab_SPI[i-4]; // Wake up time onlyprintmsg("%u\r\n",cpt1);cpt1 = cpt1*60;// Conversion in secondsprintmsg("%u\r\n",cpt1);if(cpt1 > cpt1_max){printmsg("Wrong character received for next wakeup\r\n");NVIC_SystemReset(); // Restart system}cpt1 = cpt1-tab_SPI[sizeString-2]; // Offset onlyprintmsg("%u\r\n",cpt1);return cpt1;}Thanks in advance for your help and assistance

author: newbrain
author_rank: Super Contributor
timestamp: December 01, 2021, 09:24:21 am
content: Quote from: nishantnidaria on December 01, 2021, 08:31:27 amUsing the blocking function with a 100 ms timeout, there's always the possibility of a race condition: the RPi might start to transmit while the SPI is not listening.Even if the processing after the timeout is very short (a failed 'if'), you must consider that the HAL does a fair amount of work* when the function is called, meaning that there is a significant time window when this can happen.This would hold true even if one were to to use direct register access - it would help in reducing the occurrence, but would not eliminate it.There are a number of strategies to solve the problem. Probably the simplest one is (within the HAL) to use one of the non blocking receive functions (xxx_IT or xxx_DMA).The receive can be started before the main while loop, and inside it you can check a (volatile) flag set by the callback function you associated with the RX complete IRQ.If the flag is true, process the received data and restart the non blocking receive.* Many would say "useless" work. But this is not the point here.

author: Siwastaja
author_rank: Super Contributor
timestamp: December 01, 2021, 10:09:18 am
content: Don't open multiple threads on the same matter.You already got multiple good answers.The problem is, you still don't understand how SPI works.One option would be using UART instead.An SPI slave must always be ready to receiveand send, at any time. This is normally achieved by using either interrupts, DMA, or their combination. You don't need a timer interrupt, you need an SPI interrupt.

author: Siwastaja
author_rank: Super Contributor
timestamp: December 01, 2021, 12:03:08 pm
content: And BTW, you can make your life much easier with SPI if youdon'timplement aquery-responseprotocol, but insteadshare data. Sharingallthe data every time makes it super easy. This doesn't scale if you have a lot of state, but it seems this might not be a problem.Do something like this,Code:typedef struct __attribute__((packed)){uint32_t magic_number; // always set to 0xdeadbeef for sanity checkinguint16_t battery_voltage; // in millivoltsuint16_t something_else; // whatever} slave_packet_t;typedef struct __attribute__((packed)){uint32_t magic_number; // always set to 0xacdcabba for sanity-checking.uint32_t shutdown_timer; // 0 = do not shutdown. Other values = time until shutdown in seconds} master_packet_t;Verify these match in size.Or write this manually using uint8_t buffer[some fixed size] instead, if you don't like to use packed structs but prefer manual parsing of contents.Now you can just configure DMA for the slave and enable DMA completion interrupt. When you get a full transfer, look at the contents of the packet in the ISR and decide what to do.Update battery_voltage whenever you have time. If you align the members so that you can guarantee atomicity of the variable accesses, you save a lot of hassle (like implementing and verifying double buffering).