Interrupts vs Thread conflict in mbed for STM32?

20 Jun 2018

Hello everyone!

I'm studying mbed for STM32 and have some problem with using interrupts and threads in single program.

What is going on in code:
3 buttons are used to control states of 3 digital outputs, Encoder has 2 interrupts : to control PWM (on one pin), second interrupt (encoder button) switches states of all 3 outputs (which are attached to buttons). The same time I read temperature from 3 temperature sensors (on individual data pins). PWM forming frequency data and read temperatures are transferred to display.

Problem
When temperature reading is inside of main() function - everything works amazingly. But when I'm trying to move all temperature reading to thread outside of the main() function the program compiles, but microcontroller stops (freezes) at the moment when tread starts. After that microcontroller doesn't respond to anything (interrupts do not work).

Is there any conflict if using interrupts and thread in a single program?

Please, help with that.

Libraries and references
Thread using instructions taken from here: https://docs.mbed.com/docs/mbed-os-api-reference/en/latest/APIs/tasks/rtos/

Used Hardware

  • HD44780 20x4 LCD in 4-bit mode
  • 3 x DS18B20 temperature sensors
  • BluePill STM32F103C8T6

Code

Code without threads

#include "stm32f103c8t6.h"
#include "mbed.h"
#include "TextLCD.h"
#include "DS1820.h"

// ------------------------------------------------------------------------------------------------
class Motor{
    public:
        void on()  { _out = 1; };
        void off() { _out = 0; };
        void flip(){ _out = !_out;};
        bool state() { return _out;};

        Motor (PinName button_pin, PinName output_pin) :
            // since _out and _interrupt instances are "private" - they have to be initialized with an initializer list!
            _out(output_pin), _interrupt(button_pin) {   
                // now extra actions that has to be done during initializing
                off();
                _interrupt.fall(callback(this, &Motor::flip));                
            };

    private:
        DigitalOut _out;
        InterruptIn _interrupt;
};

// ------------------------------------------------------------------------------------------------

DigitalOut led(PC_13);

InterruptIn encoder_A(PA_10);   // contact A of encoder
InterruptIn encoder_S(PA_12);   // contact Switch of encoder
DigitalIn   encoder_B(PA_11);   // contact B of encoder

Motor motor_1(PA_15, PB_11);     // control button input, output pin
Motor motor_2(PB_3, PB_10);
Motor motor_3(PB_4, PB_1);

PwmOut step(PA_1);

DS1820 ds18b20[3] = {DS1820(PA_2), DS1820(PA_3), DS1820(PA_4)};

float temperatures[3];

// For TextLCD in 4-bit mode:

// TextLCD lcd( p11, p12, p30, p29, p28, p27, TextLCD::LCD20x4 ); // rs, e, d4...d7; RW - wire to GND!
// wire RW to ground!
// place the potentiometer between V0 / VSS / VDD to correct the contrast
// For VDD ~5.0 V: VSS can be wired via 2 x 4K7 resistors (in paralel) to GND
TextLCD lcd(PB_12, PB_13, PB_14, PB_15, PA_8, PA_9, TextLCD::LCD20x4 );


// Just input values ----------------------------------------------------------------------------------------------

float RPM = 10.0;               // default value, later shoud be sotred in flash mamory or EEPROM
int SPRmotor = 400;             // Steps Per Revolution of motor : defined by motor model
                                // NEMA 17 : 42BYGHM809 : 400 bipolar
                                // NEMA 23 : 57BYGH420-2 : 200 bipolar stepper; 2A per phase
int MSM = 16;                   // 1; 2; 4; 8; 16 : depends on driver
                                // Big Easy Driver : Allegro A4988 : <2 A per phase : 1, 2, 4, 8, 16
                                // Easy driver : A3967SLBT : <0.7 A per phase : 1, 2, 4, 8
int pulse_width = 75;           // 1...10 microsecond for making step : defined by driver
float period;

volatile bool changed = false;  // important to be volatile!!!!, Otherwise the problem with while loop


void update_period(){
    period = 60.0/(RPM * SPRmotor *  MSM);
};


void print_RPM(){
    lcd.locate(0,0);
    lcd.printf("%2.1f ", RPM);    
    lcd.locate(6,0);
    lcd.printf("%8.3f ", period*1000000);    
    lcd.locate(9,1);
    lcd.printf("%6.4f", step.read()*100.0);    
};

// Functions, attached to interrupts ----------------------------------------------------------------------------------------------

void encoder() {   
    float lastRPM = RPM;
    if (encoder_B == encoder_A) {
        RPM = RPM + (float) 0.1;
        } else {
            RPM = RPM - (float) 0.1;
            };
    if (RPM < 0.1) {RPM = 0.1;}

    if (lastRPM != RPM) {
        changed = true;
        };
};

void encoder_switch(){    
    // if at least one motor is on, turn off all
    if (motor_1.state() |  motor_2.state() |  motor_3.state()){
        motor_1.off();
        motor_2.off();
        motor_3.off();        
    } else {
        motor_1.on();
        motor_2.on();
        motor_3.on();
    };
};

// ----------------------------------------------------------------------------------------------

int main() {
    led = 0;
    // needed for this library, otherwise - places the "_" symbol after last printed symbol
    lcd.setCursor(TextLCD::CurOff_BlkOff);

    lcd.locate(0,0);
    lcd.printf("Inititalizing...");

    for (int i = 0; i <3; i++){
        lcd.locate(0,i+1);
        lcd.printf("   Temp.Probe %d ", i+1);
        if (!ds18b20[i].begin()){
            lcd.printf("FAIL");
            } else {
            lcd.printf("PASS");
        }; 
        wait(0.1);
    };

    lcd.cls();

    encoder_A.fall(callback(encoder));
    encoder_S.fall(callback(encoder_switch)); 

    update_period();

    step.period(period);
    step.write(0.5);

    print_RPM();    

    while (true){
        led = !led;

        for (int i = 0; i <3; i++) { ds18b20[i].startConversion();};
        led = !led;

        if (changed){            
            changed = false;
            update_period();
            step.period(period);                           
            step.write(0.5);    // for some reason needs to be updates
            print_RPM(); 
        };

        led = !led;
        for (int i = 0; i <3; i++) { temperatures[i] = ds18b20[i].read();};
        led = !led;

        for (int i = 0; i <3; i++) { 
            lcd.locate(0+i*5,3);
            lcd.printf("%3.1f", temperatures[i]);     
        };
    };

};

Code with threads

#include "stm32f103c8t6.h"
#include "mbed.h"
#include "TextLCD.h"
#include "DS1820.h"

// ------------------------------------------------------------------------------------------------
class Motor{
    public:
        void on()  { _out = 1; };
        void off() { _out = 0; };
        void flip(){ _out = !_out;};
        bool state() { return _out;};

        Motor (PinName button_pin, PinName output_pin) :
            // since _out and _interrupt instances are "private" - they have to be initialized with an initializer list!
            _out(output_pin), _interrupt(button_pin) {   
                // now extra actions that has to be done during initializing
                off();
                _interrupt.fall(callback(this, &Motor::flip));                
            };

    private:
        DigitalOut _out;
        InterruptIn _interrupt;
};

// ------------------------------------------------------------------------------------------------

DigitalOut led(PC_13);

InterruptIn encoder_A(PA_10);   // contact A of encoder
InterruptIn encoder_S(PA_12);   // contact Switch of encoder
DigitalIn   encoder_B(PA_11);   // contact B of encoder

Motor motor_1(PA_15, PB_11);     // control button input, output pin
Motor motor_2(PB_3, PB_10);
Motor motor_3(PB_4, PB_1);

PwmOut step(PA_1);

DS1820 ds18b20[3] = {DS1820(PA_2), DS1820(PA_3), DS1820(PA_4)};

float temperatures[3];

Thread temperature_thread;

// For TextLCD in 4-bit mode:

// TextLCD lcd( p11, p12, p30, p29, p28, p27, TextLCD::LCD20x4 ); // rs, e, d4...d7;
// wire RW to ground!
// place the potentiometer between V0 / VSS / VDD to correct the contrast
// For VDD ~5.0 V: VSS can be wired via 2 x 4K7 resistors (in paralel) to GND
TextLCD lcd(PB_12, PB_13, PB_14, PB_15, PA_8, PA_9, TextLCD::LCD20x4 );


// Just input values ----------------------------------------------------------------------------------------------

float RPM = 10.0;               // default value, later shoud be sotred in flash mamory or EEPROM
int SPRmotor = 400;             // Steps Per Revolution of motor : defined by motor model
                                // NEMA 17 : 42BYGHM809 : 400 bipolar
                                // NEMA 23 : 57BYGH420-2 : 200 bipolar stepper; 2A per phase
int MSM = 16;                   // 1; 2; 4; 8; 16 : depends on driver
                                // Big Easy Driver : Allegro A4988 : <2 A per phase : 1, 2, 4, 8, 16
                                // Easy driver : A3967SLBT : <0.7 A per phase : 1, 2, 4, 8
int pulse_width = 75;           // 1...10 microsecond for making step : defined by driver
float period;

volatile bool changed = false;  // important to be volatile!!!!, Otherwise the problem with while loop
volatile bool measured = false;  // important to be volatile!!!!, Otherwise the problem with while loop


void update_period(){
    period = 60.0/(RPM * SPRmotor *  MSM);
};


void print_RPM(){
    lcd.locate(0,0);
    lcd.printf("%2.1f ", RPM);    
    lcd.locate(6,0);
    lcd.printf("%8.3f ", period*1000000);    
    lcd.locate(9,1);
    lcd.printf("%6.4f", step.read()*100.0);    
};

// Functions, attached to interrupts ----------------------------------------------------------------------------------------------

void encoder() {   
    float lastRPM = RPM;
    if (encoder_B == encoder_A) {
        RPM = RPM + (float) 0.1;
        } else {
            RPM = RPM - (float) 0.1;
            };
    if (RPM < 0.1) {RPM = 0.1;}

    if (lastRPM != RPM) {
        changed = true;
        };
};

void encoder_switch(){    
    // if at least one motor is on, turn off all
    if (motor_1.state() |  motor_2.state() |  motor_3.state()){
        motor_1.off();
        motor_2.off();
        motor_3.off();        
    } else {
        motor_1.on();
        motor_2.on();
        motor_3.on();
    };
};

void temperature_monitor(){
    led = !led;
    for (int i = 0; i <3; i++) { ds18b20[i].startConversion();};
    led = !led;
    wait(1);
    led = !led;
    for (int i = 0; i <3; i++) { temperatures[i] = ds18b20[i].read();};
    led = !led;
    measured = true;
};

// ----------------------------------------------------------------------------------------------

int main() {
    led = 0;
    // needed for this library, otherwise - places the "_" symbol after last printed symbol
    lcd.setCursor(TextLCD::CurOff_BlkOff);

    lcd.locate(0,0);
    lcd.printf("Inititalizing...");

    for (int i = 0; i <3; i++){
        lcd.locate(0,i+1);
        lcd.printf("   Temp.Probe %d ", i+1);
        if (!ds18b20[i].begin()){
            lcd.printf("FAIL");
            } else {
            lcd.printf("PASS");
        }; 
        wait(0.1);
    };

    lcd.cls();

    encoder_A.fall(callback(encoder));
    encoder_S.fall(callback(encoder_switch)); 

    update_period();

    step.period(period);
    step.write(0.5);
    temperature_thread.start(temperature_monitor);
    print_RPM();    

    while (true){       

        if (changed){            
            changed = false;
            update_period();
            step.period(period);                           
            step.write(0.5);    // for some reason needs to be updates
            print_RPM(); 
        };

        if (measured){
            measured = false;
            for (int i = 0; i <3; i++) { 
                lcd.locate(0+i*5,3);
                lcd.printf("%3.1f", temperatures[i]);     
            };
        };
    };    

};