Communicating between threads effectively to pause one while the other runs

91 Views Asked by At

I am aiming to make a multi-thread program, where the first thread runs a tilt switch on the STM32f4Discovery board using the on board LIS3DSH Accelerometer. and the second thread controls the operation of the user push button, when the button is pressed it pauses the tilt switch thread and blinks a red LED and when it is released the red LED goes off and the tilt switch resumes operation.

The code below includes my two header files and the main.c file. while the tilt switch now works properly on its own, with both threads initialized the tilt switch works only right after the button is pressed. I understand this means the tilt switch thread is updating only after the button is pressed as is configured to wait for the button state. however when I remove the this snippet of code from the tilt switch thread, nothing works.

        osSignalWait(0x01, osWaitForever); // Wait for signal from Button_Click_Thread

there seems to be a problem somewhere with either thread priority or with the logic that is implemented, I have tried several fixes but am not able to get this to work, any suggestions would be helpful!

Thread.c

#include "cmsis_os.h"                                           // CMSIS RTOS header file
#include "stm32f4xx.h"
#include "my_defines.h"



// Thread Declaration 
void Tilt_Switch_Thread (void const *argument);

osThreadId tid_Tilt_Switch_Thread;   // Declares an ID of the thread that will be associated with the thread 
osThreadDef (Tilt_Switch_Thread, osPriorityNormal, 1, 0); // Defination of the thread that we will be usign for the tilt switch 

void Button_Click_Thread (void const *argument);

osThreadId tid_Button_Click_Thread;  // Declares an ID of the thread that will be associated with the thread 
osThreadDef (Button_Click_Thread, osPriorityNormal, 1, 0);  



/*----------------------------------------------------------------------------
 *      Thread 2 Button_Click_Thread: 
 *---------------------------------------------------------------------------*/
 
 // Defination of the function to initialie the thread 
 int Init_Button_Click_Thread(void){
     
     tid_Button_Click_Thread = osThreadCreate(osThread(Button_Click_Thread), NULL); // Creation of the main thread object 
     
     // Check to ensure that the main thread has been created
     if(!tid_Button_Click_Thread)return -1;
     return 0; 
 
 }
 
 // Code to define the operation of the main thread as defined above 
 void Button_Click_Thread(void const *argument){
     
uint8_t LED_on = 1; // Defines parameter for LED on

uint8_t LED_off = 0; // Defines parameter for LED off

uint8_t red_LED = 14; // Defines parameter for red LED (GPIOD pin 14)
    
    // Check the state of the push button and decide action based on the state
    // If the button is pressed it checks if it has been released or is waiting to be released 
while(1){

if(((GPIOA->IDR & 0x00000001) == 0x00000001) && ((GPIOD->ODR & (1<<14)) != (1<<14))){

TIM2->CR1 &= ~1; //Disables the counter
osSignalClear(tid_Tilt_Switch_Thread, 0x01); // Pause Tilt_Switch_Thread
Blink_LED(LED_on,red_LED); // Turn red LED on
osThreadYield();

}

// Checks the state of the push-button and only turns the red LED off if the button has only just been released, which is indicated by the state of the red LED.

else if(((GPIOA->IDR & 0x00000001) != 0x00000001) && ((GPIOD->ODR & (1<<14)) == (1<<14))){

Blink_LED(LED_off,red_LED); // Turn red LED off

TIM2->CR1 |= 1; //Enables the counter
osSignalSet(tid_Tilt_Switch_Thread, 0x01); // Resume Tilt_Switch_Thread
osThreadYield();

}
osThreadYield();
}

 }


/*----------------------------------------------------------------------------
*      Thread 1 'Tilt_Switch_Thread' : 'ADD DESCRIPTION'
 *---------------------------------------------------------------------------*/
 
 // Defination of the function to initiate the thread
 
 int Init_Tilt_Switch_Thread (void) {
     
     tid_Tilt_Switch_Thread = osThreadCreate (osThread(Tilt_Switch_Thread), NULL); // Creation of the main thread object 
     
     // Check to ensure that the thread has been created 
     if(!tid_Tilt_Switch_Thread)return(-1);
     
     return 0; 
 }
 
 // Code to Define the operation of the main thread as defined above
 void Tilt_Switch_Thread(void const *argument){
     
     while(1){  
        osSignalWait(0x01, osWaitForever); // Wait for signal from Button_Click_Thread
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);  // Clearing the interrupt incase it occured during the wait period
        osThreadYield();
        Initialise_Tilt_Switch();
        osThreadYield();}
     // Call the initialise tiltswitch function to beign
     //if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0)==SET){

        // __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); // Clears the interrupt before proceeding to service the interrupt 
         //osThreadYield();
         
} //else {

      //while(1){  
        //osSignalWait(0x01, osWaitForever); // Wait for signal from Button_Click_Thread
        //__//HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);  // Clearing the interrupt incase it occured during the wait period
        //osThreadYield();
        //Initialise_Tilt_Switch();
        //osThreadYield();}
    // }
 //}
     
 

my_defines.c

   #include "stm32f4xx.h"
#include "my_defines.h"

// Defination of the declarations 
SPI_HandleTypeDef SPI_Params; 
GPIO_InitTypeDef GPIOA_Params; 
GPIO_InitTypeDef GPIOE_Params; 
GPIO_InitTypeDef GPIOE_Params_I;
uint8_t data_to_send_receive[4]; 
uint16_t data_size=1; 
uint32_t data_timeout=1000; 

uint8_t x_Reg_H; 
uint8_t y_Reg_H; 
uint8_t z_Reg_H;
int8_t x_accl;   
int8_t y_accl; 

// Function Definations 

void Initialise_LED_Button_Timer(void){
    
    // Initialize GPIO Port for LEDs

RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // Enable Port D clock
GPIOD->MODER |= GPIO_MODER_MODER12_0; // Port D.12 output - green LED
GPIOD->MODER |= GPIO_MODER_MODER13_0; // Port D.13 output - blue LED
GPIOD->MODER |= GPIO_MODER_MODER14_0; // Port D.14 output - red LED
GPIOD->MODER |= GPIO_MODER_MODER15_0; // Port D.14 output - orange LED

//Initialize GPIO for push-button

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // Enable Port A clock

//Initialize Timer 2

RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // Enable timer 2 clock
TIM2->CR1 &= ~0x00000016; /*Sets the counter as an upcounter*/
TIM2->CR1 &= ~0x00000008; /*Turn on repeat*/
TIM2->PSC = 8400-1; /*Prescaler value - the prescaler clock defaults to twice the APB1 which is running at 42MHz - so the timer clock is 84MHz*/
TIM2->ARR = 10000-1; /*sets the value in the auto-reload register*/
TIM2->EGR = 1; /*Re-initialises the timer*/
TIM2->CR1 |= 1; //Enables the counter

}

// Defination of the SPI initialisation function 

void Initialise_SPI_Tilt(void){

    // Code to initialise the SPI 

RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; //Enable the clock for SPI1 
SPI_Params.Instance = SPI1; // Selects which SPI interface to use 
SPI_Params.Init.Mode = SPI_MODE_MASTER; // Sets the STM32F407 to act as the master 
SPI_Params.Init.NSS = SPI_NSS_SOFT; // Sets the slave to be controlled by software 
SPI_Params.Init.Direction = SPI_DIRECTION_2LINES; // Sets the SPI to full-duplex 
SPI_Params.Init.DataSize = SPI_DATASIZE_8BIT; // Sets the data packet size to 8-bit 
SPI_Params.Init.CLKPolarity = SPI_POLARITY_HIGH; // Sets the idle polarity for the clock line to high 
SPI_Params.Init.CLKPhase = SPI_PHASE_2EDGE; // Sets the data line to be sampled on the second transition of the clock line 
SPI_Params.Init.FirstBit = SPI_FIRSTBIT_MSB; // Sets the transmission to MSB first 
SPI_Params.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; // Sets the clock prescaler to divide the main APB2 clock (previously set to 84MHz) by 32 to give a SPI clock of 2.625MHz, which is less the maximum value of 10MHz for the SPI. 
HAL_SPI_Init(&SPI_Params); // Configures the SPI using the specified parameters 

 
// Code to initialise pins 5-7 of GPIOA 

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Enable the clock for GPIOA 
GPIOA_Params.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; // Selects pins 5,6 and 7 
GPIOA_Params.Alternate = GPIO_AF5_SPI1; //Selects alternate function 5 which corresponds to SPI1 
GPIOA_Params.Mode = GPIO_MODE_AF_PP; //Selects alternate function push-pull mode 
GPIOA_Params.Speed = GPIO_SPEED_FAST; //Selects fast speed 
GPIOA_Params.Pull = GPIO_NOPULL; //Selects no pull-up or pull-down activation 
HAL_GPIO_Init(GPIOA, &GPIOA_Params); // Sets GPIOA into the modes specified in GPIOA_Params 

// Code to initialise pin 3 of GPIOE 

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; //Enable the clock for GPIOE 
GPIOE_Params.Pin = GPIO_PIN_3; // Selects pin 3 
GPIOE_Params.Mode = GPIO_MODE_OUTPUT_PP; //Selects normal push-pull mode 
GPIOE_Params.Speed = GPIO_SPEED_FAST; //Selects fast speed 
GPIOE_Params.Pull = GPIO_PULLUP; //Selects pull-up activation 
HAL_GPIO_Init(GPIOE, &GPIOE_Params); // Sets GPIOE into the modes specified in GPIOE_Params 

GPIOE->BSRR = GPIO_PIN_3; //Sets the serial port enable pin CS high (idle) 
__HAL_SPI_ENABLE(&SPI_Params); //Enable the SPI 

// Enable the Interrupt line from pin 0

GPIOE_Params_I.Pin = GPIO_PIN_0; // Selects pin 0 
GPIOE_Params_I.Mode = GPIO_MODE_IT_RISING; // Selects the interrupt mode and configures the interrupt to be signalled on a rising edge (low to high transition) 
GPIOE_Params_I.Speed = GPIO_SPEED_FAST; //Selects fast speed 
HAL_GPIO_Init(GPIOE, &GPIOE_Params_I); // Sets GPIOE into the modes specified in GPIOE_Params_I 
GPIOE->BSRR = GPIO_PIN_3; //Sets the serial port enable pin CS high (idle) 
__HAL_SPI_ENABLE(&SPI_Params); //Enable the SPI 

// Write a new value to control register 4 of the LIS3DSH 

data_to_send_receive[0] = 0x20; // Address for control register 4 on LIS3DSH 
GPIOE->BSRR = GPIO_PIN_3<<16; // Set the SPI communication enable line low to initiate communication 
HAL_SPI_Transmit(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Send the address of the register to be read on the LIS3DSH  
data_to_send_receive[0] = 0x17; // Set register value to give a sample rate of 3.125Hz, continuous update and z-axis enabled only 
HAL_SPI_Transmit(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Send the new register value to the LIS3DSH through the SPI channel 
GPIOE->BSRR = GPIO_PIN_3; // Set the SPI communication enable line high to signal the end of the communication process 

// Write a new value to control register 3 of the LIS3DSH

data_to_send_receive[0] = 0x23; // Address for control register 3 on the LIS3DSH 
GPIOE->BSRR = GPIO_PIN_3<<16; // Set the SPI communication enable line low to initiate communication 
HAL_SPI_Transmit(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Send the address of the register to be read on the LIS3DSH  
data_to_send_receive[0] = 0xC8; // Enable DRDY connected to Int1, sets Int1 active to high, enables int1 
HAL_SPI_Transmit(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Send the new register value to the LIS3DSH through the SPI channel 
GPIOE->BSRR = GPIO_PIN_3; // Set the SPI communication enable line high to signal the end of the communication process 


}
    
// Defination of the tilt switch functionality of the code
void Initialise_Tilt_Switch(void){
    
// This only comes on when there is  a change in the accelerometer readings

//for(;;){ 

// For the X - Axis 
    
data_to_send_receive[0] = 0x80|0x29; // Address for the MSB x-axis (H) data register on the LIS3DSH 
GPIOE->BSRR = GPIO_PIN_3<<16; // Set the SPI communication enable line low to initiate communication 
HAL_SPI_Transmit(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Send the address of the register to be read on the LIS3DSH  
data_to_send_receive[0] = 0x00; // Set a blank address because we are waiting to receive data 
HAL_SPI_Receive(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Get the data from the LIS3DSH through the SPI channel 
GPIOE->BSRR = GPIO_PIN_3; // Set the SPI communication enable line high to signal the end of the communication process 
x_Reg_H = data_to_send_receive[0]; // Read the data from the SPI data array into our internal variable. 

// For the Y- Axis
data_to_send_receive[0] = 0x80|0x2B; // Address for the MSB x-axis (H) data register on the LIS3DSH 
GPIOE->BSRR = GPIO_PIN_3<<16; // Set the SPI communication enable line low to initiate communication 
HAL_SPI_Transmit(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Send the address of the register to be read on the LIS3DSH  
data_to_send_receive[0] = 0x00; // Set a blank address because we are waiting to receive data   
HAL_SPI_Receive(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Get the data from the LIS3DSH through the SPI channel 
GPIOE->BSRR = GPIO_PIN_3; // Set the SPI communication enable line high to signal the end of the communication process 
y_Reg_H = data_to_send_receive[0]; // Read the data from the SPI data array into our internal variable. 

// For the Z-axis 
data_to_send_receive[0] = 0x80|0x2D; // Address for the MSB x-axis (H) data register on the LIS3DSH 
GPIOE->BSRR = GPIO_PIN_3<<16; // Set the SPI communication enable line low to initiate communication 
HAL_SPI_Transmit(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Send the address of the register to be read on the LIS3DSH  
data_to_send_receive[0] = 0x00; // Set a blank address because we are waiting to receive data 
HAL_SPI_Receive(&SPI_Params,data_to_send_receive,data_size,data_timeout); // Get the data from the LIS3DSH through the SPI channel 
GPIOE->BSRR = GPIO_PIN_3; // Set the SPI communication enable line high to signal the end of the communication process 
z_Reg_H = data_to_send_receive[0]; // Read the data from the SPI data array into our internal variable. 

// Converting the unsigned values to signed values 
x_accl = (int8_t) x_Reg_H;
y_accl = (int8_t) y_Reg_H;

// Contdition for no tilt detected 
if ((x_accl <= 10) && (y_accl <= 10)){
// Turn off all LEDs
    GPIOD->BSRR = (1<<(12+16));
    GPIOD->BSRR = (1<<(15+16));
    GPIOD->BSRR = (1<<(14+16));
    GPIOD->BSRR = (1<<(13+16));
    
}

// Control for the x axis 

if ((x_accl > 20)){
// positive acceleration 
    GPIOD->BSRR = (1<<14);  // Turn on Red LED
    GPIOD->BSRR = (1<<(12+16)); // Turn off green LED
}else if (x_accl < -20){
// negative acceleration 
    GPIOD->BSRR = (1<<12);  // Turn on Green LED
    GPIOD->BSRR = (1<<(14+16)); // Turn off Red LED
}else {
// no acceleration hence ignore values between 10 and -10 
    GPIOD->BSRR = (1<<(12+16));  // Turn off Green LED
    GPIOD->BSRR = (1<<(14+16)); // Turn off Red LED
}

// control for the y axis
if ((y_accl > 20)){
// positive acceleration 
    GPIOD->BSRR = (1<<13);  // Turn on blue LED
    GPIOD->BSRR = (1<<(15+16)); // Turn off orange LED
}else if (y_accl < -20){
// negative acceleration 
    GPIOD->BSRR = (1<<15);  // Turn on orange LED
    GPIOD->BSRR = (1<<(13+16)); // Turn off blue LED
}else {
// no acceleration hence ignore values between 10 and -10 
    GPIOD->BSRR = (1<<(13+16));  // Turn off blue LED
    GPIOD->BSRR = (1<<(15+16)); // Turn off orange LED
}
//}
}

// Defination of the Blink_LED function 
void Blink_LED(uint8_t LED_State, uint8_t LED_Colour){
    
    if(LED_State == 1){ // Checks to see if the request is to turn the LED on or off
GPIOD->BSRR = 1<<LED_Colour; // Turn on the green LED
}

else{
GPIOD->BSRR = 1<<(LED_Colour+16); // Turn off the green LED
}

}

// Defination of the Button state function 
void Button_State(uint8_t button_state){
    
    if (button_state == 1){
    
    
    }

}

main.c

/*----------------------------------------------------------------------------
 * CMSIS-RTOS 'main' function template
 *---------------------------------------------------------------------------*/

#define osObjectsPublic                     // define objects in main module
#include "osObjects.h"                      // RTOS object definitions
#include "stm32f4xx.h"
#include "my_defines.h"
#include "Thread.h"

/*
 * main: initialize and start the system
 */
int main (void) {
  osKernelInitialize ();                    // initialize CMSIS-RTOS

  // initialize peripherals here
    
    Initialise_LED_Button_Timer();
    Initialise_SPI_Tilt(); 

  // Initialize the threads 
    
    Init_Tilt_Switch_Thread();
    Init_Button_Click_Thread();
     
  osKernelStart ();                         // start thread execution 
    //while(1){}; // Infinite while loop to keep the program running 
}
1

There are 1 best solutions below

1
emiled On

Q : Why does the tilt switch only work after the button was pressed?

A : Because in the Init_Tilt_Switch_Thread you first wait for a signal and then initialize the tilt switch.

Solution : Move Initialise_Tilt_Switch(); before the while(1) statement in the Init_Tilt_Switch_Thread.

Apart from this, you seem to mix a lot of different mechanisms in your code: interrupts, hardware timers, RTOS, etc. If you want to experiment with an RTOS for such a simple problem (which is fine for learning but totally overkill for a real application), I would suggest sticking to the RTOS mechanisms. For instance, using blocking waits inside the threads instead of a timer, as the other thread can run during the time the first one blocks.

Take a look at a simple blinky example (e.g. This KEIL tutorial) to get acquainted with basic RTOS concepts. Then add the features you need step by step.