cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 Accelerometer issues using an RTOS (HAL SPI problem?)

Michael Stuart
Associate II
Posted on December 06, 2016 at 19:53

Hello,

I have managed to create a program (using Keil), that can turn LED's on and off based on the orientation of the MCU. However when i try to implement the program as an RTOS with threads, the program doesn't appear to run correctly.

The code that i have currently within the RTOS thread currently does the following;

  1. Initialize Setup (i.e. initialize SPI1, GPIOA, GPIOD, GPIOE)
  2. Configure control register on LIS3DSH to output required gyro/accelerometer data for given axis
  3. Continuous loop that reads the gyro/accelerometer data from the register, and will enable or disable LED's respectively.

Obviously i know it's not efficient to have all the code within the thread, but for this purpose it's okay.

This program runs perfectly as a standalone (i.e. not asRTOS / threads), howeveras anRTOS thread it appears to hang after a certain point. It will reach the point where it turns on an LED, but that LED will remain turned on despite the orientation changing.

From the testing and debugging that i have done, the problem seems to lie with the SPI communication with the register (I use SPI_HAL_Transmit / SPI_HAL_Receive functions to read/write data).

For example, i changed my code such that the control register was still configured, butinstead i madethe continuous loop a simple LED enable / disable timed loop. A snippet of the testing code is below;

...
// Communication procedure to configure control register
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
address = 0x20;
HAL_SPI_Transmit(&SPI_Params,&address,1,500);
address = 0x14;
HAL_SPI_Transmit(&SPI_Params,&address,1,500);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
// Main program loop
 
 for(;;){
 for(ii=0; ii<10000000; ii++){}
 
 GPIOD->BSRR = 1<<14;
 GPIOD->BSRR = 1<<(15+16); 
 
 for(ii=0; ii<10000000; ii++){}
 
 GPIOD->BSRR = 1<<15;
 GPIOD->BSRR = 1<<(14+16);
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

When the communicationprocedure code was enabled, the program would hang before any LEDs were enabled.

When the communication

procedure code

was disabled, the program would blink the LED's correctly.

Any ideas as to how i go about resolving the communication issues i am having using threads in an RTOS? I am pretty confused why it would work as a standalone program but not from within a thread as an RTOS.

Thanks for any help or advise.

Mike

6 REPLIES 6
Posted on December 06, 2016 at 22:23

Showing the code that works, but not the code that has the problem makes it difficult even for us psychic experts to tell you what you are doing wrong.

Other information that would be useful:  What board/CPU you are using.  What RTOS, is there just one thread in the system or more?,  What are the thread priorities and, if there are other threads what are they doing?  How are you configuring the on-chip peripherals?  CubeMX? STL? other?  And anything else you think might be relevant about your design.  If you want help you need to provide information.

Michael Stuart
Associate II
Posted on December 07, 2016 at 00:07

Hello there,

Thank you for replying, sorry i'm kinda new to this. Hopefully the information below is what you were requesting:

Keil C/C++ Preprocessor Symbol: USE_HAL_DRIVER

Board/CPU: STM32F4-Discovery (STM32F407VGTx)

RTOS: CMSIS-Core (Keil RTX Version 4.0) STM32Cube Framework (API): Classic STM32Cube HAL: Common, Cortex, DMA, GPIO, PWR, RCC, SPI

RTOS Kernel Clock Input: 168MHz (APB2 clock is set to 84MHz)

Number of threads: 1

Priority: Normal / Round Robin

Here are my .h and .c files for the program i am trying to make (with LED's enable / disabling depending on orientation of MCU).

Thread.h

extern int Init_threadIdle (void); 
void threadIdle (void const *argument);�?�?

Thread.c

#include 'cmsis_os.h' // CMSIS RTOS header file
#include 'stm32f4xx.h'
// Thread Declarations
void threadIdle (void const *argument);
osThreadId tid_threadIdle;
osThreadDef (threadIdle, osPriorityNormal, 1, 0);
/*----------------------------------------------------------------------------
 * Main Thread
 *---------------------------------------------------------------------------*/
 
int Init_threadIdle (void) {
 tid_threadIdle = osThreadCreate (osThread(threadIdle), NULL);
 if(!tid_threadIdle) return(-1);
 
 return(0);
}
void threadIdle (void const *argument) {
// Variable Declarations
SPI_HandleTypeDef SPI_Params; 
GPIO_InitTypeDef gpioA;
GPIO_InitTypeDef gpioE;
uint8_t address;
uint8_t zHigher;
// Initialize the SPI 1
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
SPI_Params.Instance = SPI1;
SPI_Params.Init.Mode = SPI_MODE_MASTER;
SPI_Params.Init.NSS = SPI_NSS_SOFT;
SPI_Params.Init.Direction = SPI_DIRECTION_2LINES;
SPI_Params.Init.DataSize = SPI_DATASIZE_8BIT; 
SPI_Params.Init.CLKPolarity = SPI_POLARITY_HIGH; 
SPI_Params.Init.CLKPhase = SPI_PHASE_2EDGE; 
SPI_Params.Init.FirstBit = SPI_FIRSTBIT_MSB; 
SPI_Params.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; // APB2 clock is set to 84MHz
HAL_SPI_Init(&SPI_Params); 
// Initialize GPIO Port A for pins 5, 6 and 7
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Enable the clock for GPIOA
gpioA.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; 
gpioA.Mode = GPIO_MODE_AF_PP; 
gpioA.Alternate = GPIO_AF5_SPI1;
gpioA.Pull = GPIO_NOPULL; 
gpioA.Speed = GPIO_SPEED_FAST;
HAL_GPIO_Init(GPIOA, &gpioA); 
// Initialize GPIO Port E for pin 3
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; 
gpioE.Pin = GPIO_PIN_3;
gpioE.Mode = GPIO_MODE_OUTPUT_PP;
gpioE.Pull = GPIO_PULLUP; 
gpioE.Speed = GPIO_SPEED_FAST;
HAL_GPIO_Init(GPIOE, &gpioE); 
GPIOE->BSRR = GPIO_PIN_3; 
__HAL_SPI_ENABLE(&SPI_Params); 
// Initialize GPIO Port D for LEDs
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; 
GPIOD->MODER |= GPIO_MODER_MODER12_0;
GPIOD->MODER |= GPIO_MODER_MODER13_0; 
GPIOD->MODER |= GPIO_MODER_MODER14_0; 
GPIOD->MODER |= GPIO_MODER_MODER15_0; 
/* Communication procedure to configure control register 4 of LIS3DSH
to set register to output z-axis data */
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
address = 0x20;
HAL_SPI_Transmit(&SPI_Params,&address,1,500);
address = 0x14;
HAL_SPI_Transmit(&SPI_Params,&address,1,500);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
// Main program loop
for(;;){
 
// Read register to return higher bit data from z-axis
 
address = 0x80|0x2D;
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_SPI_Transmit(&SPI_Params,&address,1,500);
address = 0x00; 
HAL_SPI_Receive(&SPI_Params,&address,1,500);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
zHigher = *SPI_Params.pRxBuffPtr; 
/* If loop to enable / disable LED's based on whether z-axis data
is positive or negative */
if((zHigher&0x80) == 0x80){ 
GPIOD->BSRR = 1<<12 | 1<<14; // Enable green and red
GPIOD->BSRR = 1<<(13+16) | 1<<(15+16); // Disable orange and blue
}
else{
GPIOD->BSRR = 1<<13 | 1<<15; // enable orange and blue
GPIOD->BSRR = 1<<(12+16) | 1<<(14+16); // Disable green and red
}
}
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

main.c

/*----------------------------------------------------------------------------
 * CMSIS-RTOS 'main' function template
 *---------------------------------------------------------------------------*/
#define osObjectsPublic // define objects in main module
#include 'osObjects.h' // RTOS object definitions
#include 'stm32f4xx.h'
#include 'cmsis_os.h'
#include 'Thread.h'
 
int main (void) {
osKernelInitialize (); 
Init_threadIdle();
osKernelStart (); 
while(1){};
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

====================

As it stands, the program runs and will hang at a point where the orange and blue LEDs are permanently enabled, even if i change the orientation of the MCU.I know the code in the thread works correctly since the main code in Thread.c (lines 24 to 123) will run correctlyin a standalone (non-RTOS) program, and the LEDs will enable/disable based on orientation as intended.

The reason i thought or think it is SPI related, is because if you replace the main loop in Thread.c (lines 95 - 123) with the more simple test loop in my first post (lines 14 to 25; making sure you add uint32_t ii; to the variable declarations), the RTOS does work and the thread will run with the LEDs blinking alternatively, but only if the SPI communication code to configure register 4 is disabled.

In all cases i have tried thus far, the RTOS program will always seem to hang once i have code involving or requiring the HAL_SPI_Transmit function.

I am new to this so i could be totally wrong, but it's the only common denominator that i can find at the moment.

Again, thanks for the help and advise.

Mike

Posted on December 07, 2016 at 02:03

very interesting.

One thing I'd try is making sure you are running with optimizations disabled.  If your compiler is optimizing for size or speed it may throw out some variables and code such as your LED flashing delay loops, which a good compiler would see as having no use.  I can't say why this would be different with an RTOS Version (unless you have optimizations turned on in your RTOS project and off in the non-RTOS) but I'd just do this to make debugging easier.  If this has an effect, I'd look at making your variables in these dummy loops volatile (like ii) so that the compiler doesn't eliminate them.

The next thing I would try would be to disable interrupts during the SPI transactions.  ie; disable interrupts (global-ALL interrupts) before you bring the SPI CS bit low, and re-enable interrupts after you pull the line high, thereby making this critical section atomic.  Again, there is not a dramatic difference between the RTOS version and non-RTOS, in that even in the non-RTOS version you have the SysTick interrupt happening every millisecond.  However the non-RTOS systick ISR just increments a counter and returns.  well under 1uS.  In the RTOS version, for every OS scheduler tick (1mS apart or longer) it runs the scheduler to see if it should change what task(thread) is running.  Since you only have one task it will always come back to your task, but it may take significantly longer than the Systick ISR.  And if it happens at a bad time for the multi-step SPI transaction (including all of the HAL call code) it may get in the way at the wrong time to mess up the transaction.  If this helps, there are more elegant solutions to declare code like this a 'critical section' which effectively does the same thing as disabling interrupts, but it looks better if you use the OS's defined mechanism for this.

If you don't  see any effect from these let us know and we can think some more. 

Posted on December 07, 2016 at 22:29

Thank you for that detailed reply.

I attempted to disabled the interrupts during the critical SPI transactions but unfortunately it doesn't appear to fix the issue. Hopefully i was correct in using__enable_irq(); and__disable_irq(); with the processor in privileged mode. I made changes to lines 86 - 95, and lines 103 - 112 of the main Thread.c code, below;

#include 'cmsis_os.h' // CMSIS RTOS header file
#include 'stm32f4xx.h'
// Thread Declarations
void threadIdle (void const *argument);
osThreadId tid_threadIdle;
osThreadDef (threadIdle, osPriorityNormal, 1, 0);
/*----------------------------------------------------------------------------
 * Main Thread
 *---------------------------------------------------------------------------*/
 
int Init_threadIdle (void) {
 tid_threadIdle = osThreadCreate (osThread(threadIdle), NULL);
 if(!tid_threadIdle) return(-1);
 
 return(0);
}
void threadIdle (void const *argument) {
 // Variable Declarations
 
 SPI_HandleTypeDef SPI_Params; 
 GPIO_InitTypeDef gpioA;
 GPIO_InitTypeDef gpioE;
 uint8_t address;
 uint8_t zHigher;
 // Initialize the SPI 1
 
 RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
 
 SPI_Params.Instance = SPI1;
 SPI_Params.Init.Mode = SPI_MODE_MASTER;
 SPI_Params.Init.NSS = SPI_NSS_SOFT;
 SPI_Params.Init.Direction = SPI_DIRECTION_2LINES;
 SPI_Params.Init.DataSize = SPI_DATASIZE_8BIT; 
 SPI_Params.Init.CLKPolarity = SPI_POLARITY_HIGH; 
 SPI_Params.Init.CLKPhase = SPI_PHASE_2EDGE; 
 SPI_Params.Init.FirstBit = SPI_FIRSTBIT_MSB; 
 SPI_Params.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; // APB2 clock is set to 84MHz
 
 HAL_SPI_Init(&SPI_Params); 
 
 // Initialize GPIO Port A for pins 5, 6 and 7
 
 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Enable the clock for GPIOA
 
 gpioA.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; 
 gpioA.Mode = GPIO_MODE_AF_PP; 
 gpioA.Alternate = GPIO_AF5_SPI1;
 gpioA.Pull = GPIO_NOPULL; 
 gpioA.Speed = GPIO_SPEED_FAST;
 
 HAL_GPIO_Init(GPIOA, &gpioA); 
 
 // Initialize GPIO Port E for pin 3
 
 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; 
 
 gpioE.Pin = GPIO_PIN_3;
 gpioE.Mode = GPIO_MODE_OUTPUT_PP;
 gpioE.Pull = GPIO_PULLUP; 
 gpioE.Speed = GPIO_SPEED_FAST;
 HAL_GPIO_Init(GPIOE, &gpioE); 
 GPIOE->BSRR = GPIO_PIN_3; 
 
 __HAL_SPI_ENABLE(&SPI_Params); 
 
 // Initialize GPIO Port D for LEDs
 RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; 
 
 GPIOD->MODER |= GPIO_MODER_MODER12_0;
 GPIOD->MODER |= GPIO_MODER_MODER13_0; 
 GPIOD->MODER |= GPIO_MODER_MODER14_0; 
 GPIOD->MODER |= GPIO_MODER_MODER15_0; 
 
 /* Communication procedure to configure control register 4 of LIS3DSH
 to set register to output z-axis data */
 __disable_irq(); // Disable interrupts
 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
 address = 0x20;
 HAL_SPI_Transmit(&SPI_Params,&address,1,500);
 address = 0x14;
 HAL_SPI_Transmit(&SPI_Params,&address,1,500);
 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
 __enable_irq(); // Enable interrupts
 // Main program loop
 
 for(;;){
 
 // Read register to return higher bit data from z-axis
 
 __disable_irq(); // Disable interrupts
 address = 0x80|0x2D;
 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
 HAL_SPI_Transmit(&SPI_Params,&address,1,500);
 address = 0x00; 
 HAL_SPI_Receive(&SPI_Params,&address,1,500);
 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
 zHigher = *SPI_Params.pRxBuffPtr; 
 __enable_irq(); // Enable interrupts
 /* If loop to enable / disable LED's based on whether z-axis data
 is positive or negative */
 if((zHigher&0x80) == 0x80){ 
 
 GPIOD->BSRR = 1<<12 | 1<<14; // Enable green and red
 GPIOD->BSRR = 1<<(13+16) | 1<<(15+16); // Disable orange and blue
 
 }
 else{
 
 GPIOD->BSRR = 1<<13 | 1<<15; // enable orange and blue
 GPIOD->BSRR = 1<<(12+16) | 1<<(14+16); // Disable green and red
 
 }
 }
 
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Hi Michael,

I was wondering if you found a solution to your problem since I'm having similar issues with my code to use the accelerometer?

JJol.1
Associate

You should consider to ensure spi communication frame include all bits that you need to be set during sending a byte.