cancel
Showing results for 
Search instead for 
Did you mean: 

Changing scene in Model.cpp fails

starcin
Associate II

I have a hardware trigger that needs to change a scene (think a Home button but I cannot use a hardware key event).

I am trying to change the scene for days but I cannot find a working solution.

The best looking solution was this: https://community.st.com/t5/stm32-mcus-touchgfx-and-gui/how-to-implement-quot-switchscreen-quot-in-touchgfx/m-p/384809

Using this method crashes the code. Well technically the code is stuck in this loop in syscalls.c:

 

void _exit (int status)
{
  _kill(status, -1);
  while (1) {}    /* Make sure we hang here */
}

 

 

My code in the model is like this:

 

void Model::tick()
{
	uint16_t buttonState = getButtonState(16);
    if (buttonState != previousButtonState)
    {
        if (modelListener != 0)
        {
            modelListener->onButtonStateChange(buttonState);
        }
        if (static_cast<ButtonEnum>(buttonState) == ButtonEnum::MENU)
        {
            static_cast<FrontendApplication*>(Application::getInstance())->gotoSplashScreenScreenNoTransition();
        }
        previousButtonState = buttonState;
    }
}

 

I also tried to increase the minimum heap size since I saw it may be the cause.

The gotoSplashScreenNoTransition() is automatically generated since it is may entrance point.

1 ACCEPTED SOLUTION

Accepted Solutions

Somehow the problem was me calling resizeToCurrentText() function of TextArea.

Let me show you my whole Model.cpp
The line 59 is the line 52 here. Where I call something from debugWidgetPointer.

 

 

#include <gui/model/Model.hpp>
#include <gui/model/ModelListener.hpp>
#include <gui/containers/Header.hpp>
#include <gui/containers/Debug.hpp>
#include <gui/common/FrontendApplication.hpp>
#include <ButtonEnum.hpp>

Model::Model() : modelListener(0) {
}

uint16_t Model::getButtonState(uint8_t buttonNum)
{
    uint16_t buttonState = 0x0000;
    HC165_CLK_GPIO_Port->ODR |= (HC165_CLK_Pin);
    HC165_EN_GPIO_Port->ODR &= ~(HC165_EN_Pin);
    HC165_EN_GPIO_Port->ODR |= (HC165_EN_Pin);
    for(int i = 0; i < buttonNum; i++)
    {
        HC165_CLK_GPIO_Port->ODR &= ~(HC165_CLK_Pin);
        if(HC165_MISO_GPIO_Port->IDR & (1<<5))
            buttonState &= ~(1 << i);
        else
            buttonState |= (1 << i);
        HC165_CLK_GPIO_Port->ODR |= (HC165_CLK_Pin);
    }
    return buttonState;
}

uint8_t Model::getBatteryLevel()
{
    return rand() % 101; // Return random battery level between 0-100
}

void Model::tick()
{
    tickCount++;
    if (headerPointer != nullptr && tickCount % 10 == 0)
    {
        uint8_t batteryLevel = getBatteryLevel();
        debugWidgetPointer->setText(batteryLevel);
        headerPointer->setBatteryLevel(batteryLevel);
    }
	uint16_t buttonState = getButtonState(16);
    if (buttonState != previousButtonState)
    {
        if (modelListener != 0)
        {
            modelListener->onButtonStateChange(buttonState);
        }
        if (debugWidgetPointer != nullptr)
        {
            debugWidgetPointer->onButtonStateChange(buttonState);
        }
        if (static_cast<ButtonEnum>(buttonState) == ButtonEnum::MENU)
        {
            static_cast<FrontendApplication*>(Application::getInstance())->gotoScreen2ScreenNoTransition();
        }
        previousButtonState = buttonState;
    }
}

 

 

I have a custom container called Debug that I add to every scene using TouchGFX UI. Then when a scene is loaded, it's presenter sends that container's pointer to model using a bind method in ModelListener:

 

 

#ifndef MODELLISTENER_HPP
#define MODELLISTENER_HPP

#include <gui/model/Model.hpp>
#include <mvp/View.hpp>
#include <touchgfx/hal/Types.hpp>

class ModelListener
{
public:
    ModelListener() : model(0) {}
    
    virtual ~ModelListener() {}

    void bind(Model* m)
    {
        model = m;
    }

    virtual void onButtonStateChange(uint16_t newButtonState) {}
    
    template<typename T>
    void bindCommonComponents(touchgfx::View<T>& view) {
        Debug* debug = static_cast<Debug*>(view.getDebugWidgetPointer());
        Header* header = static_cast<Header*>(view.getHeaderPointer());

        model->debugWidgetPointer = debug;
        model->headerPointer = header;
    }

protected:
    Model* model;
};

#endif // MODELLISTENER_HPP

 

 

Looks like if I forget to implement the call for this bind function, the debugWidgetPointer is not null but simply points to a weird place (I guess?) and cause unexpected results. I think the only thing I need is a way to nullify these pointers when a screen change happens. Do we have a single place that is called everytime screen changes? Preferably somewere that can access modellistener or model?

EDIT: I tried to nullify them in modellistener's constructor but the code still exits if I don't bind the pointer.

View solution in original post

12 REPLIES 12
GaetanGodart
ST Employee

Hello @starcin ,

 

Could you share the stack call?

Could you put breakpoints to see what is causing the issue?

Could you try to do a simple action when your button is clicked and try to call your screen change from another action to see what works and what doesn't?

 

Regards,

Gaetan Godart
Software engineer at ST (TouchGFX)
starcin
Associate II

Hi @GaetanGodart 

Thanks for the quick reply. I don't know how to get the stack call since the program didn't crash but actually navigated to an infinite loop. I am not familiar with CubeIDE.

I tried to call the goto____NoTransition function from a presenter and it went to the infinite loop again.

The thing I forgot to say is, actually the screen transition happens. I can see the new screen on my display then nothing responds. When I pause the program using IDE, I can see that it is in syscall.c -> _exit function.

 

I placed a breakpoint to my line:

static_cast<FrontendApplication*>(Application::getInstance())->gotoScreen2ScreenNoTransition();

Then I moved step by step and when I got to the _exit function, the list on the left side (I hope this is the stack trace) was like this:

starcin_0-1733146667365.png

 

starcin
Associate II

Now I realize that something is wrong about the resizeToCurrentText. I'll investigate.

What is on line 59 of the Model.cpp?

Keep me updated when you have finished investigating!

 

Regards,

Gaetan Godart
Software engineer at ST (TouchGFX)

Somehow the problem was me calling resizeToCurrentText() function of TextArea.

Let me show you my whole Model.cpp
The line 59 is the line 52 here. Where I call something from debugWidgetPointer.

 

 

#include <gui/model/Model.hpp>
#include <gui/model/ModelListener.hpp>
#include <gui/containers/Header.hpp>
#include <gui/containers/Debug.hpp>
#include <gui/common/FrontendApplication.hpp>
#include <ButtonEnum.hpp>

Model::Model() : modelListener(0) {
}

uint16_t Model::getButtonState(uint8_t buttonNum)
{
    uint16_t buttonState = 0x0000;
    HC165_CLK_GPIO_Port->ODR |= (HC165_CLK_Pin);
    HC165_EN_GPIO_Port->ODR &= ~(HC165_EN_Pin);
    HC165_EN_GPIO_Port->ODR |= (HC165_EN_Pin);
    for(int i = 0; i < buttonNum; i++)
    {
        HC165_CLK_GPIO_Port->ODR &= ~(HC165_CLK_Pin);
        if(HC165_MISO_GPIO_Port->IDR & (1<<5))
            buttonState &= ~(1 << i);
        else
            buttonState |= (1 << i);
        HC165_CLK_GPIO_Port->ODR |= (HC165_CLK_Pin);
    }
    return buttonState;
}

uint8_t Model::getBatteryLevel()
{
    return rand() % 101; // Return random battery level between 0-100
}

void Model::tick()
{
    tickCount++;
    if (headerPointer != nullptr && tickCount % 10 == 0)
    {
        uint8_t batteryLevel = getBatteryLevel();
        debugWidgetPointer->setText(batteryLevel);
        headerPointer->setBatteryLevel(batteryLevel);
    }
	uint16_t buttonState = getButtonState(16);
    if (buttonState != previousButtonState)
    {
        if (modelListener != 0)
        {
            modelListener->onButtonStateChange(buttonState);
        }
        if (debugWidgetPointer != nullptr)
        {
            debugWidgetPointer->onButtonStateChange(buttonState);
        }
        if (static_cast<ButtonEnum>(buttonState) == ButtonEnum::MENU)
        {
            static_cast<FrontendApplication*>(Application::getInstance())->gotoScreen2ScreenNoTransition();
        }
        previousButtonState = buttonState;
    }
}

 

 

I have a custom container called Debug that I add to every scene using TouchGFX UI. Then when a scene is loaded, it's presenter sends that container's pointer to model using a bind method in ModelListener:

 

 

#ifndef MODELLISTENER_HPP
#define MODELLISTENER_HPP

#include <gui/model/Model.hpp>
#include <mvp/View.hpp>
#include <touchgfx/hal/Types.hpp>

class ModelListener
{
public:
    ModelListener() : model(0) {}
    
    virtual ~ModelListener() {}

    void bind(Model* m)
    {
        model = m;
    }

    virtual void onButtonStateChange(uint16_t newButtonState) {}
    
    template<typename T>
    void bindCommonComponents(touchgfx::View<T>& view) {
        Debug* debug = static_cast<Debug*>(view.getDebugWidgetPointer());
        Header* header = static_cast<Header*>(view.getHeaderPointer());

        model->debugWidgetPointer = debug;
        model->headerPointer = header;
    }

protected:
    Model* model;
};

#endif // MODELLISTENER_HPP

 

 

Looks like if I forget to implement the call for this bind function, the debugWidgetPointer is not null but simply points to a weird place (I guess?) and cause unexpected results. I think the only thing I need is a way to nullify these pointers when a screen change happens. Do we have a single place that is called everytime screen changes? Preferably somewere that can access modellistener or model?

EDIT: I tried to nullify them in modellistener's constructor but the code still exits if I don't bind the pointer.

Hello @starcin ,

 

It is great new that you were able to track down the root cause of the issue.

We have a function that is called every time we exit a screen, this is the tearDownScreen() function (setupScreen() is called whenever we instantiate a screen).

 

Regards,

Gaetan Godart
Software engineer at ST (TouchGFX)

Hello @starcin ,

 

Have you been able to move forward with your issue?
If you did, could you select the comment that helped you the most as "best answer".

 

Regards,

Gaetan Godart
Software engineer at ST (TouchGFX)

I couldn't find a way to automate nullifying the pointers, or maybe I have another problem in that area, so I am just being careful about adding and removing stuff in my classes. But since it was actually a pointer issue and not a scene change issue, I guess we can say that I moved forward.

Hello @starcin ,

 

Would this fix your issue if you put it in every setupScreen() function :

debugWidgetPointer = nullptr;

 

Regards,

Gaetan Godart
Software engineer at ST (TouchGFX)