Page 1 of 1

HAL_SAI_Receive() causes hardware timer callback to never exit?

Posted: Thu May 20, 2021 10:37 am
by Bambo
Hi,

i'm trying to sample a microphone inside a hardware timer callback, but the program gets stuck when it runs.

CPU: STM32L452RE

Here is the program:

Code: Select all

#include <SoftwareSerial.h>
#include <Arduino.h>
#include <HardwareTimer.h>

#include "stm32l4xx.h"
#include "stm32l4xx_hal.h"
#include "stm32l4xx_hal_def.h"
#include "stm32l4xx_hal_gpio.h"
#include "stm32l4xx_hal_sai.h"
#include "stm32l4xx_hal_exti.h"
#include "stm32l4xx_hal_cortex.h"

#include "Sysclock_Config.h"

void assert_failed(uint8_t* inFileName, uint32_t line) {
	char* fileNameAsString = (char*)inFileName;
	Serial2.println(F("*** [HAL ASSERT FAILED] ***"));
	Serial2.printf("%s\r\n", (char*)inFileName);
	Serial2.printf("%i\r\n", line);
}
void _Error_Handler(const char* file, int line) {
	Serial2.println(F("*** [ERROR HANDLED] ***"));
	Serial2.printf("%s\r\n", file);
	Serial2.printf("%i\r\n", line);
}


GPIO_InitTypeDef gpioInit;
SAI_HandleTypeDef saiHandle;
RCC_PeriphCLKInitTypeDef periphInit;
uint8_t	SAIDataBuffer[8] = { 0 };

HardwareTimer sampleTimer(TIM3);

void samplerCallback();

// the setup function runs once when you press reset or power the board
void setup() {
	delay(5000);

	Serial2.begin(115200);
	Serial2.println("Starting");

		/*
			Enable the GPIO B clock.
		*/
	__HAL_RCC_GPIOB_CLK_ENABLE();


	/*
		Enable the peripheral clock.
	*/

	__HAL_RCC_SAI1_CLK_ENABLE();


	/*
		Configure the GPIO pins.
	*/
	/**
	SAI1_A_Block_A GPIO Configuration
	PB15     ------> SAI1_SD_A
	PB10     ------> SAI1_SCK_A
	PB12     ------> SAI1_FS_A
	*/
	gpioInit.Pin = GPIO_PIN_15 | GPIO_PIN_10 | GPIO_PIN_12;
	gpioInit.Mode = GPIO_MODE_AF_PP;
	gpioInit.Pull = GPIO_NOPULL;
	gpioInit.Speed = GPIO_SPEED_FREQ_LOW;
	gpioInit.Alternate = GPIO_AF13_SAI1;
	HAL_GPIO_Init(GPIOB, &gpioInit);


	pinMode(PB1, OUTPUT);
	digitalWrite(PB1, LOW);


	__HAL_SAI_DISABLE(&saiHandle);

	// Block type def.
	saiHandle.Instance = SAI1_Block_A; // audio block A.

	saiHandle.Init.Protocol = SAI_FREE_PROTOCOL;
	saiHandle.Init.FirstBit = SAI_FIRSTBIT_MSB;
	saiHandle.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K;
	saiHandle.Init.AudioMode = SAI_MODEMASTER_RX; // block a must provide clock signals and receive from the data line.
	saiHandle.Init.Synchro = SAI_ASYNCHRONOUS;	// we only want to use this one audio block.
	saiHandle.Init.SynchroExt = SAI_SYNCEXT_DISABLE; // disable sychronizing the 2 audio blocks.
	saiHandle.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE; // assume to power the data?
	saiHandle.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE; // any frame length allowed.
	saiHandle.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;	// used for interrupts.
	saiHandle.Init.MonoStereoMode = SAI_STEREOMODE; // mono mode only available in transmission mode.
	saiHandle.Init.CompandingMode = SAI_NOCOMPANDING; // telecommunications specification (not needed)
	saiHandle.Init.TriState = SAI_OUTPUT_NOTRELEASED; // assume the SAI is ma

	//saiHandle.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE;
	//saiHandle.Init.DataSize = SAI_DATASIZE_32;

	// block frame parameters.
	saiHandle.FrameInit.FrameLength = 64; // 64 bit frame. (2 slots)
	saiHandle.FrameInit.ActiveFrameLength = 32; // Frame synchronization active level length. (half the frame length)
	saiHandle.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
	saiHandle.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
	saiHandle.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;

	// block slot parameters.
	saiHandle.SlotInit.FirstBitOffset = 0; // no offset in receive mode -> FBOFF <= (SLOTSZ - DS)
	saiHandle.SlotInit.SlotSize = SAI_SLOTSIZE_32B; // 32 bits per slot to contain the 32 data bits (24 data, 8 zeroed)
	saiHandle.SlotInit.SlotNumber = 2;
	saiHandle.SlotInit.SlotActive = SAI_SLOTACTIVE_ALL;

	/*
			https://www.keil.com/pack/doc/CMSIS/Driver/html/group__sai__interface__gr.html
	*/
	HAL_StatusTypeDef saiStatus = HAL_SAI_InitProtocol(&saiHandle,
		SAI_I2S_STANDARD, // runs the SAI_InitI2S() function.
		SAI_PROTOCOL_DATASIZE_24BIT,// 24 bits (24 bit is standard for I2S).
		2);			 // number of slots per frame - 1

	__HAL_SAI_ENABLE(&saiHandle); // without this line of code the first read takes 84us because it settings sai enabled.

	if (saiStatus != HAL_OK)
	{
		Serial2.println("SAI ERROR");
		while (1) {}
	}


	sampleTimer.setMode(1, TIMER_OUTPUT_COMPARE, NC); // remove this for new version of STM32Dino, required version 1.8.0.
	sampleTimer.setOverflow(16000, HERTZ_FORMAT);
	sampleTimer.detachInterrupt();
	sampleTimer.attachInterrupt(samplerCallback);
	sampleTimer.resume();
}

long elapsed = 0;

void samplerCallback()
{

	long stamp = micros();

	HAL_StatusTypeDef rxResponse;

	/*
	* Perform sampling
	*/
	rxResponse = HAL_SAI_Receive(&saiHandle, SAIDataBuffer, 2U, 10000);

	if (rxResponse != HAL_OK)
	{
		Serial2.println("Error in SAI");
		while (1) { }
	}

	elapsed = micros() - stamp;
}

void signalWatchdog()
{
	// do signal watchdog (placeholder)
}

void loop() {
	signalWatchdog();
	Serial2.println(elapsed);
}
The output when i run this program is:
11:33:44.117 -> 4Starting
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 0
11:34:45.448 -> 4
11:34:45.448 -> 11
11:34:45.448 -> 4
11:34:45.448 -> 5
11:34:45.448 -> 5
11:34:45.448 -> 4
11:34:45.448 -> 4
11:34:45.448 -> 4
11:34:45.448 -> 5
11:34:45.448 -> 10
11:34:45.448 -> 4 <------------ The printing stops here, but the program carries on running
For some reason it stops printing the elapsed time after a coupe of micros(). I'm not sure why it stops?

More links: https://community.st.com/s/question/0D5 ... n-isr-loop

Thanks!

Re: HAL_SAI_Receive() causes hardware timer callback to never exit?

Posted: Thu May 20, 2021 2:13 pm
by mlundin
Does the sound work ?

The time display discrepancies are most likely because the SAI_Receive inside the timer irq blocks the SysTick interrupts and therefore makes the micros() nonfunctional.

Also note that variables used to communicate between IRQ handlers and loop level should be marked as volatile (elapsed)

Re: HAL_SAI_Receive() causes hardware timer callback to never exit?

Posted: Thu May 20, 2021 2:30 pm
by Bambo
The sound works yeah, but the program halts because the watchdog serial is never signaled.

Code: Select all

void loop() {
	signalWatchdog(); // this is never called
	Serial2.println(elapsed);
}

Re: HAL_SAI_Receive() causes hardware timer callback to never exit?

Posted: Thu May 20, 2021 3:36 pm
by mlundin
Program seems to be overflowing serial buffers, looking at the timestamps,

the Serial2.println(elapsed) seems to get called ok,
and so should signalWatchdog(), but that function is empty so how do you know its not called ?

Re: HAL_SAI_Receive() causes hardware timer callback to never exit?

Posted: Thu May 20, 2021 3:47 pm
by Bambo
mlundin wrote: Thu May 20, 2021 3:36 pm Program seems to be overflowing serial buffers, looking at the timestamps,

the Serial2.println(elapsed) seems to get called ok,
and so should signalWatchdog(), but that function is empty so how do you know its not called ?
The serial2.println() is called fine for the first second or so, but the loop() is stopped being called afterwards.

I have editted the log in the post, it shows that the printing stops but the program carries on running. I think the ISR wont "let" the loop() execute somehow.

Here's a timeline:

11:33:44.117 to 11:34:45.448 the program prints values.
11:34:45.448+ the program is still running but no prints are happening.

I have a much larger program running the same scenario, the computer is reset by the watchdog because its loop() is never called.