2024-12-01 08:57 PM
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.
Solved! Go to Solution.
2024-12-02 11:18 PM - edited 2024-12-02 11:24 PM
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.
2024-12-02 03:00 AM
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,
2024-12-02 05:39 AM
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:
2024-12-02 05:40 AM
Now I realize that something is wrong about the resizeToCurrentText. I'll investigate.
2024-12-02 06:07 AM - edited 2024-12-12 02:50 AM
What is on line 59 of the Model.cpp?
Keep me updated when you have finished investigating!
Regards,
2024-12-02 11:18 PM - edited 2024-12-02 11:24 PM
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.
2024-12-02 11:50 PM
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,
2024-12-10 06:12 AM
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,
2024-12-11 10:51 PM
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.
2024-12-12 04:51 AM
Hello @starcin ,
Would this fix your issue if you put it in every setupScreen() function :
debugWidgetPointer = nullptr;
Regards,