Description
Hello everyone. I must say this community is really fantastic and encouraged me to keep going with my endless project. After many hurdles I have come across a problem I require help with.
I’m running an ESP timer that increases a value every 40ms. The program is generating a linear timecode, so at 25fps I am increasing the frame count by 1. The timecode is then displayed in segments HH:MM:SS:FF as rollers on a touch screen (not animated). A second function generates a signal according to SMPTE standards and send it out through GPIO. I also added functionality to sync two displays together via ESP-Now.
It is crucial that this timer is never interrupted lest I get inaccurate frame counts. In general, the two functions for generating the time code and generating the signal work well together with the display. Except for when I press a button.
I have a button that, on long press, “locks the screen” by disabling the menu buttons. When the long press event fires, the frame count skips. It skips a second time when I take my finger off the lock button, even long after the long press event has occurred.
It makes sense to me that the CPU is busy with something while LVGL is doing its thing. It just seems slightly weird to me because the button is not doing a whole lot of work that would keep the CPU busy. I especially don’t understand why the timer would get paused when releasing the button where I don’t execute any code at all. It is updating up to 4 rollers at once and does not cause any problems to the frame timer. Why would a button press?
Is there some way for me to ensure LVGL will never interrupt a timer? Could multi threading maybe be a path to try for me? I have no experience with multi threading so before I tread this path I would like to make sure it’s even worth giving it a go. Seems a little daunting.
What MCU/Processor/Board and compiler are you using?
ESP32-S3 on a Lilygo T-Display Long
What LVGL version are you using?
V8.4 (the display does not support v9 according to manufacturer)
LVGL is generated in SquareLine Studio 1.5
What do you want to achieve?
The frame increase timer should never be interrupted by LVGL.
What have you tried so far?
I have tried to put the frame timer on to core 0. This runs very well until of course I use ESP-Now, which also interrupts the frame timer.
The main loop contains:
void loop()
{
if (frameFlag) // timecode data bits have been sent
{
frameFlag = false;
update_TC(); // transfer data to output registers
if (!timeHold) // if not holding time
{
bump_time(); // compute next frame count
}
} // end of new TC processing
// make LVGL things happen
if (millis() - previousMillis > 10)
{
lv_timer_handler();
previousMillis = millis();
}
}
TC generation looks like this:
// ==========================================
//
void intBit()
{
// Runs every clock edge (interrupt)
// Flips the output port/bit at every whole cell time (every 2 ticks)
// Also flips it between cell edges if the current code cell is 1
// Lack of a mid-cell flip indicates a cell value of 0
static bool midFlag; // true when this is the 1-bit point
static uint8_t valu; // temp for working byte
if (midFlag) { // all decisions happen mid-cell
if (v_bitIndex == 0) { // starting a new byte
v_bitIndex = 1; // so start at mask of 00000001
valu = v_codeBytes[v_byteIndex]; // use this byte for 8 cells
}
if (valu & v_bitIndex) {
//outPort ^= outBit; // flip state if code has a 1-bit
//Serial.println(outBit);
digitalWrite(SIGNALPIN, !digitalRead(SIGNALPIN));
}
if ((v_bitIndex *= 2) == 0) { // shift left 1 place. Shift off end?
if (++v_byteIndex == 10) { // when all bytes exhausted
v_byteIndex = 0; // start over
}
if (v_byteIndex == 8) {
v_frameFlag = true; // data bytes sent, doing sync word now
}
}
}
else { // end of cell, not mid-point
//outPort ^= outBit; // just flip
//Serial.println(outBit);
digitalWrite(SIGNALPIN, !digitalRead(SIGNALPIN));
}
midFlag = !midFlag;
}
// ==========================================
//
void IRAM_ATTR bump_time()
{
if (tcDrop == STD) {
if (m_timecode.bytes[Frms] == 0) {
if (m_timecode.bytes[Secs] == 0) {
if ((m_timecode.bytes[Mins] % 10) != 0) {
m_timecode.bytes[Frms] = 2;
}
}
}
}
m_timecode.bytes[Frms]++;
if (m_timecode.bytes[Frms] < 25){return;}
m_timecode.bytes[Frms] = 0;
m_timecode.bytes[Secs]++;
if (m_timecode.bytes[Secs] < 60){return;}
m_timecode.bytes[Secs] = 0;
m_timecode.bytes[Mins]++;
if (m_timecode.bytes[Mins] < 60){return;}
m_timecode.bytes[Mins] = 0;
m_timecode.bytes[Hrs]++;
if (m_timecode.bytes[Hrs] < 24){return;}
m_timecode.bytes[Hrs] = 0;
}
// ==========================================
//
void IRAM_ATTR update_TC()
{
uint8_t idx = 0;
// merge time and user bits
//
for (uint8_t kdx = 0; kdx < 4; kdx++) {
v_codeBytes[idx] = (m_timecode.bytes[kdx] % 10) | m_uBits[idx];
idx++;
v_codeBytes[idx] = (m_timecode.bytes[kdx] / 10) | m_uBits[idx];
idx++;
}
if (tcDrop == STD) {
v_codeBytes[dfByte] |= dfBit;
}
uint8_t accum = 1; // parity of sync word
for (idx = 0; idx < 8; idx++) {
accum ^= v_codeBytes[idx];
accum ^= accum >> 4;
accum ^= accum >> 2;
accum ^= accum >> 1;
}
if (accum & 1) {
v_codeBytes[parityByte[STD]] |= parityBit; // insert phasing bit
}
}
The timer only executes this:
void void IRAM_ATTR ltcTimerFunction()
{
intBit();
}
The lock button executes this code:
void lock_screen(lv_event_t * e)
{
screenLocked = !screenLocked;
if (screenLocked){
Serial.println("screen locked");
lv_obj_clear_flag(ui_goToModes, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_style_opa(ui_goToModes, 0, LV_PART_MAIN);
lv_obj_clear_flag(ui_goToSettings, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_style_opa(ui_goToSettings, 0, LV_PART_MAIN);
lv_obj_clear_flag(ui_imgLockLeft, LV_OBJ_FLAG_HIDDEN);
}
if (!screenLocked)
{
Serial.println("screen unlocked");
lv_obj_add_flag(ui_goToModes, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_style_opa(ui_goToModes, 255, LV_PART_MAIN);
lv_obj_add_flag(ui_goToSettings, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_style_opa(ui_goToSettings, 255, LV_PART_MAIN);
lv_obj_add_flag(ui_imgLockLeft, LV_OBJ_FLAG_HIDDEN);
}
Serial.println(screenLocked);
}
The TC generation is an adaptation of Jim Mack’s “Longitudinal Bit-banging” blog post.
I’ve slaved over this project for nearly two months (which seems crazy for such a simple task). Any pointers to a solution are incredibly highly appreciated.
Cheers,
Roman