problem with using SPI with DMA on STM32F1 - stm32

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;
}

Related

How can I use hardware timer with cmsis-rtos2?

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.

Volatile variable is not updated in ISR in STM32F429 in Keil

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.

stm32 SPI receive only mode need a transmit?

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);

STM32F4 SPI interrupts stop firing with FreeRTOS

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.

STM32F4 PLL Precision

Im trying to configure clocks on STM32F4 Discovery for precise time measurement. I have this configuration:
int main(void)
{
NVIC_InitTypeDef nvici;
GPIO_InitTypeDef gpioi;
TIM_TimeBaseInitTypeDef timtbi;
SystemInit();
RCC_HSEConfig(RCC_HSE_ON);
RCC_PLLConfig(RCC_PLLCFGR_PLLSRC_HSE, 8, 320, 8, 8);
RCC_PLLCmd(ENABLE);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCK);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
nvici.NVIC_IRQChannel = TIM2_IRQn;
nvici.NVIC_IRQChannelPreemptionPriority = 0;
nvici.NVIC_IRQChannelSubPriority = 1;
nvici.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvici);
gpioi.GPIO_Pin = GPIO_Pin_15;
gpioi.GPIO_Mode = GPIO_Mode_OUT;
gpioi.GPIO_OType = GPIO_OType_PP;
gpioi.GPIO_Speed = GPIO_Speed_100MHz;
gpioi.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD,&gpioi);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
timtbi.TIM_Period = 20000000;
timtbi.TIM_Prescaler = 0;
timtbi.TIM_ClockDivision = 0;
timtbi.TIM_CounterMode = TIM_CounterMode_Up;
timtbi.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &timtbi);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
GPIO_SetBits(GPIOD,GPIO_Pin_15);
while(1)
{
}
}
void TIM2_IRQHandler()
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
GPIO_ToggleBits(GPIOD,GPIO_Pin_15);
}
with this i should have TIM2 sourced with 20MHz clock, but it appears to have diffrent frequency (about 10-30% diffrent). This problem appears for all other PLL configurations i tried, but when i use HSE as SYSCLK directly it works just fine. Am i doing something wrong, or is it PLL that isn't reliable?
Can't say with 100% certainty whether that's the problem, but after enabling the HSE using RCC_HSEConfig(), you should call RCC_WaitForHSEStartUp() since it takes a while for the HSE to start oscillating, and check the return code to make sure the call was successful and the HSE actually initialized.
Also, if you're using the system_stm32f4xx.c file that comes with the Standard Peripheral Library, you can scrap your PLL initialization code and just use the code that's called by SystemInit(). There are a few #defines that control the PLL configurations, near the beginning of the file (#define PLL_M, #define PLL_N and so on; their purpose should be self-evident). I always initialize my clocks using the code there, and they're always precise to within the crystal's accuracy. Note that this code assumes a 25 MHz oscillator by using PLL_M equal to 25, so you should set it to 8 for use with the STM32F4DISCOVERY board -- exactly as you've already done in your code. I'm not suggesting this because I have any prejudices against your code, but the code there has been tested far and wide, and in my experience it can be trusted.