I'm trying to make an SPI communication between a F410 MCU and a RPi using SPI.
I post below the code that currently works (without FreeRTOS usage):
main.c
volatile int tx_done = 0;
volatile int rx_done = 0;
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
tx_done = 1;
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
rx_done = 1;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI5_Init();
MX_USART2_UART_Init();
const uint8_t BUF_SIZE = 16 * sizeof(uint8_t);
uint8_t buf[16];
// For UART debug
uint8_t dbg_buffer[64];
while (1) {
memset(buf, 0, BUF_SIZE);
HAL_StatusTypeDef ret = HAL_SPI_Receive_IT(&hspi5, (uint8_t*)&buf, BUF_SIZE);
while (rx_done == 0) {};
rx_done = 0;
sprintf((char*) dbg_buffer, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d]\r\n",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
buf[7], buf[8], buf[9], buf[10], buf[11], buf[12],
buf[13], buf[14], buf[15]);
HAL_UART_Transmit(&huart2, dbg_buffer, strlen((char const*) dbg_buffer), 50);
HAL_SPI_Transmit_IT(&hspi5, (uint8_t*) &buf, BUF_SIZE);
while (tx_done == 0) {};
tx_done = 0;
}
}
stm32f4xx_it.c
/**
* #brief This function handles TIM1 trigger and commutation interrupts and TIM11 global interrupt.
*/
void TIM1_TRG_COM_TIM11_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim11);
}
/**
* #brief This function handles SPI5 global interrupt.
*/
void SPI5_IRQHandler(void)
{
HAL_SPI_IRQHandler(&hspi5);
}
spi.c
/* SPI5 init function */
void MX_SPI5_Init(void)
{
hspi5.Instance = SPI5;
hspi5.Init.Mode = SPI_MODE_SLAVE;
hspi5.Init.Direction = SPI_DIRECTION_2LINES;
hspi5.Init.DataSize = SPI_DATASIZE_8BIT;
hspi5.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi5.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi5.Init.NSS = SPI_NSS_HARD_INPUT;
hspi5.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi5.Init.TIMode = SPI_TIMODE_DISABLE;
hspi5.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi5.Init.CRCPolynomial = 15;
if (HAL_SPI_Init(&hspi5) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
[...]
HAL_NVIC_SetPriority(SPI5_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(SPI5_IRQn);
}
This working fine with my test code on the other (raspberry pi) side, sending a SPI frame every second, waiting 100ms and reading the answer from the F410.
Now, when I activate FreeRTOS, I move the while(1) loop content to a task, and creates it with
BaseType_t task1 = xTaskCreate(task_1, "task_1", 512, NULL, tskIDLE_PRIORITY, &xHandle);
and osKernelStart(). I also use the TIM11 as Timebase Source (under SYS tab on CubeMX as advised by the software itself)
Then I have the following behavior: If I place breakpoints inside both Tx/Rx SPI interrupt, I found that a couple (3-4 ?) of them are fired, then never again. If I stop my code I see I'm stucked in the
while (rx_done == 0) {};
loop, confirming that I don't get SPI RX interrupts anymore whereas there is still frame coming on the SPI bus.
To dig a little into that theory, I made another test with this in my task:
while(1) {
memset(buf, 0, 16);
HAL_StatusTypeDef ret = HAL_SPI_Receive_IT(&hspi5, (uint8_t*)&buf, 16);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
// Wait for RX interrupt, task is suspended to give processing time to (incoming) others tasks
vTaskSuspend(NULL);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
and in my Rx interrupt, I simply call
xTaskResumeFromISR(task1Handle);
With this code, the first packet sent is read correctly by the STM32F4, and task print it on USART2 and suspend itself again. From then, (checked with a breakpoint inside), the Rx interrupt is never called again, so the task resume inside neither, and my code is frozen...
It really looks like there is a messing between FreeRTOS and STM32 HAL SPI/interrupt handling ?
Any help will be gladly accepted !
Do you call NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); as described in the red text on this page: https://www.freertos.org/RTOS-Cortex-M3-M4.html ?
If you are using an STM32 with the STM32 driver library then ensure all the priority bits are assigned to be preempt priority bits by calling NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); before the RTOS is started.
Related
I know very well how to setup timer with hal API in stm32cubemx but I'm new to keil-rtx and cubemx does not support it. I wanna have a timer with less than 1 ms interval.
I want to know how to use hardware timer with rtos2?
I'm using stm32f407 microcontroller.
I've already increased kernel tick frequency and configured RTOS timer but it's not a good solution.
Here's an example of using the timer on the STM32F407 microcontroller with a 1 millisecond period:
#include "stm32f4xx.h"
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
// Code to be executed every 1 millisecond
}
}
void TIM3_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 999;
TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t)(SystemCoreClock / 1000000) - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* TIM IT enable */
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
/* Enable the TIM3 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
}
int main(void)
{
/* TIM3 configuration */
TIM3_Config();
while (1)
{
// main loop code
}
}
This code sets up TIM3 as a timer with a 1 millisecond period. The interrupt service routine (ISR) TIM3_IRQHandler will be called every 1 millisecond and execute the code inside it.
You can use any timer you want.
Read the reference manual of your STM and you can program it bare metal or using STM-supplied libraries.
I am struggling with getting the value from Timer interrupt in STM32F429. I am using Keil. The HAL I am using is not the one from CubeMX but it is old version library. Due to some reasons, I must continue to work in this library.
I have declared the counter value in interrupt as "volatile". But the value I get is rubbish or it is not increasing.
The timer 4 is set to run execute ISR as 1us.
To verify it, I have toggled GPIOA Pin 1. And it toggles at 1us interval. But I need to up count variable "time4_tick" and it is not increasing.
I put two delays of 1000 ms and 2000 ms between start and stop of Timer 4. The count is not updating properly. I declared the variable volatile to not optimize it but it didn't solve my problem. Looks like something unknown is going here.
My code is below. What could be the problem here.
/********** Timer 4 interrupt *******/
__IO uint64_t time4_tick = 0;
void Sys_Timer4(void) // 1us
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_Cmd(TIM4, DISABLE);
TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE); // Enable TIM3 Update interrupt
TIM_TimeBaseStructure.TIM_Period = 21-1; //
TIM_TimeBaseStructure.TIM_Prescaler = 4-1; // 21*4/84 MHz = 1 usec
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_SetCounter(TIM4,0);
TIM_Cmd(TIM4, ENABLE);
TIM_ClearITPendingBit(TIM4, TIM_IT_Update); //Added this
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); // Enable TIM2 Update interrupt
// Enable the TIM4 Interrupt
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
}
/********** Timer 4 IRQ Handler*******/
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
time4_tick++;
GPIO_ToggleBits(GPIOA, GPIO_Pin_1);
}
}
/********** Main program loop *******/
int main(void)
{
// Other initializations
while(1)
{
TIM_Cmd(TIM4, ENABLE);
Delay_ms(1000);
TIM_Cmd(TIM4, DISABLE);
dbgmsg("time4_tick 1: %d\n", time4_tick);
time4_tick = 0;
TIM_Cmd(TIM4, ENABLE);
Delay_ms(2000);
TIM_Cmd(TIM4, DISABLE);
dbgmsg("time4_tick 2: %d\n", time4_tick);
time4_tick = 0;
// Other tasks
}
}
time4_tick should have different values after delay of 1000 ms and 2000 ms but it is showing same value. What is the problem here ?
dbgmsg("time4_tick 1: %d\n", time4_tick);
time4_tick = 0;
You zero it every time. Delete this line, then check. If it does not update then it means that the interrupt handler was not invoked.
sorry in advance, i am new at this kind of projects.
I tried to implement a connection to an ADC (LTC1609) via SPI. I just want to get the data from the ADC as quick as possible. The ADC has just a output-line, so i chose the "read only mode". But i also tried to use the HAL_SPI_TransmitReceive funktion because i remembered that the spi exchange starts with writeing in the transmit register (at least with the HCS12).
The ADC needs also some additional signals changes between pulling down the NSS and starting the spi connection so i put the NSS in software mode.
The Cofiguration looks like this:
static void MX_SPI1_Init(void)
{
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
I write a funktion to read from ADC and store the data.
void LTC1609_ADU_Read(uint8_t *data)
{
uint8_t buffer_rx[2];
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 0); // NSS low (Pin PA4) Start
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, 0); // ADU R/NC low (Pin PG6) Start conversion
delay_hns (1); // wait 1us (10 x 100ns)
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, 1); // ADU R/NC high (Pin PG6)
if(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8) == 1) // /BUSY is high? -> start SPI
{
if(HAL_SPI_Receive(&hspi1, buffer_rx,2,10)!= HAL_OK);
{
SPI_error = HAL_SPI_GetError(&hspi1); // get the SPI error
Error_Handler(); // stop in error
}
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 1); // Set ADU /CS high (Pin PA4) fin
*data = buffer_rx[1]; // store received data
}
In the while(1) i called it like this with a delay of 100 ms for testing:
LTC1609_ADU_Read(&buffer_A_rx);
Problem:
The uC (STM32L4R5ZIT6P) dont start the SPI clk to trigger out the data from ADC. Also the Clock polarity is somtimes low (like i want) and sometimes high [could also be a messuring problem, i messure it with a sheep logic analyser 24Mhz 8CH from amazon].
If i start the programm, it runs into the error handler and the errorcode is 0x20. I found this explaining:
HAL_SPI_ERROR_FLAG 0x00000020U /*!< Flag: RXNE,TXE, BSY */
Can anyone give me a tip on what could be the reason? Did iam doing something wrong or could the hardware be damaged? Thanks in advance!
screenshot signals without clk
One problem that I see in your code is that you check the BUSY pin immediately after setting R/C high and abort the transaction if BUSY is low.
According to the datasheet, BUSY line may be low for up to 3 us (t3).
Try waiting for the busy pin to go high, for example by adding a loop as shown below.
void LTC1609_ADU_Read(uint8_t *data)
{
uint8_t buffer_rx[2];
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 0); // NSS low (Pin PA4) Start
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, 0); // ADU R/NC low (Pin PG6) Start conversion
delay_hns(1); // wait 1us (10 x 100ns)
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, 1); // ADU R/NC high (Pin PG6)
uint8_t retries = 0;
do {
delay_hns(1);
} while (HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8) == 0 && ++retries < 5);
if (HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8) == 1) // /BUSY is high? -> start SPI
{
if (HAL_SPI_Receive(&hspi1, buffer_rx, 2, 10) != HAL_OK)
{
SPI_error = HAL_SPI_GetError(&hspi1); // get the SPI error
Error_Handler(); // stop in error
}
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 1); // Set ADU /CS high (Pin PA4) fin
*data = buffer_rx[1]; // store received data
}
Also, there is a stray ; in your code, on this line:
if(HAL_SPI_Receive(&hspi1, buffer_rx,2,10)!= HAL_OK);
I have a problem with triggering NSS pin, when transmitting SPI with DMA.
I use a CubeMX to generate whole core of project.
Time before triggering NSS to low, and sending data(also between end of transmission, and NSS to high) is too long. How can i make this times shorter?
I tried to use
HAL_DMA_PollForTransfer(&hdma_spi1_tx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
for detecting end of SPI DMA transmission but once it worked, and later when i changed something in CubeIDE it completely stoped working whole program...
int dmabusy = 0;
while (1)
{
uint8_t testing[] = {5, 10, 15, 20, 25, 30};
//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
dmaBusy = 1;
GPIOB->BSRR = GPIO_BSRR_BS12;
HAL_SPI_Transmit_DMA(&hspi1, testing, 6);
while(dmaBusy == 1);
GPIOB->BSRR = GPIO_BSRR_BR12;
//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
for(int i=0; i<80000; i++){ // OPOZNIACZ START
asm("NOP");
} // OPOZNIACZ STOP
}
/* USER CODE END 3 */
}
void HAL_SPI_TxCpltCallback (SPI_HandleTypeDef * hspi){
dmaBusy=0;
}
I am trying to set up a communication between my STM32F4 - Discovery with Open 407V-D development board and a peripheral using UART3 as a RS-485 bus.
I have problem with my communication becouse Rx state of UART remain busy.
Could somebody please explain me what am I doing wrong?
Should I somehow edit HAL_UART_IRQHandler or what setting am I missing?
Here is my code:
#include "main.h"
#include "stm32f4xx_hal.h"
UART_HandleTypeDef huart3;
uint8_t Ocular_1_RxBuffer[4];
uint8_t Ocular_1_TxBuffer[2] = {0x01,0x86};
__IO ITStatus UartReady;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART3_UART_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART3_UART_Init();
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET); //set RS 485 into transmit mode
while (1)
{
int Timeout = 1000000;
while(huart3.gState != HAL_UART_STATE_READY) //wait for UART
{
Timeout--;
if(Timeout == 0)
Error_Handler();
}
Timeout = 1000000;
if(HAL_UART_Transmit_IT(&huart3, (uint8_t*)Ocular_1_TxBuffer, 2) != HAL_OK) //Send request
{
Error_Handler();
}
while(huart3.RxState != HAL_UART_STATE_READY) //wait for UART
{
Timeout--;
if(Timeout == 0)
Error_Handler();
}
Timeout = 1000000;
if(HAL_UART_Receive_IT(&huart3, (uint8_t*)Ocular_1_RxBuffer, 4) != HAL_OK) //Response
{
Error_Handler();
}
while(UartReady == RESET) //Wait for response
{
Timeout--;
if(Timeout == 0)
Error_Handler();
}
}
}
I have successfully received response from my peripheral device, but my code generate Error_Handler() after HAL_UART_RxCpltCallback() function.
Could somebody please explain this behavior to me?
My callback functions:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
{
/* Set transmission flag: transfer complete */
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
UartReady = RESET;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
/* Set transmission flag: transfer complete */
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
UartReady = SET;
}
Please mention the number of bytes you are receiving in current scenario.?
Some debugging tips are -
Try increasing the size of your buffer and check if you are receiving any data.
Make sure you are re initialising your buffer after 4 bytes are read. If not the buffer can overflow and may lead to error handler.
Make sure you transmitter always sends 4 bytes.
Confirm if your buad rate matches on both devices. Also settings like parity and all are same in receiver and transmitter.
After every 4 bytes you need to call the HAL_UART_Receive_IT() again to configure and wait for next interrupt.
Add Error callback too, and confirm if execution moves to this callback. If then add prints in driver to find out whats the error cause, whether its like overrun error / Noise error / Parity Error etc.