It's been a while since I rewrote them, but I always forgot to post.
This article will be text-only. Sorry, but it will be about code, so I don't have a lot to show, except that "it works" just as shown in the previous vidéo. Go take a look at the previous article !
So, I had that problematic misconception of my previous screen functions. Remember, the whole problem was :
The thing is, on my little 20x4 screen, space is limited. The main feature I wanted was a scrolling feature, like you would find in any serious radio device, with the following features :
I also implemented another trick : I noticed that the screen may be accessed by two functions running in parallel. That's a common case of single resource where a mutual exclusion must be implemented. I managed to get rid of that by introducting a mutex with the boolean "isLCDused". So far, I haven't seen any wrong behaviour.
To gather all the informations I need, I created a small structur, with a "Song" instance.
struct InfoScroll {
char line; // line number, 0-3
char *s_connect;
char fw;
int waiting;
};
InfoScroll Song = {0, "INIT...", 2, 900};
My RTOS function looks like this :
static void printScrollRTOSTask(void *pvParameters) {
while (1) {
vTaskDelay(10);
bool finished = false;
int i = 0;
int len = strlen(Song.s_connect);
String p_connect = String(Song.s_connect);
while (!finished)
{
// Get the song title, start at position 'i', truncate it if needed
String p_message = p_connect.substring(i, (i + LCD_WIDTH < len ? i + LCD_WIDTH : len));
// To avoid trailing at the right of the screen, fill with blanks
for (int k = len - i; k < LCD_WIDTH; k++)
p_message += " ";
while (isLCDused); // wait for the LCD to be available.
// Write that stuff
isLCDused = true;
lcd.setCursor(0, Song.line);
lcd.print(p_message);
isLCDused = false;
// Wait a bit if it's the first line...
if (i == 0)
vTaskDelay(2 * Song.waiting);
// Wait a bit and flag 'finished' if it's the last time
if (i + LCD_WIDTH >= len)
{
finished = true;
vTaskDelay(2 * Song.waiting);
}
// Go forward in the display !
i += Song.fw;
vTaskDelay(Song.waiting);
// If we have to update the title, just flag 'finished' so that the new title is taken into account at the beginning.
if (flag_screen[NEWTITLE] == 1)
{
finished = true;
flag_screen[NEWTITLE]--; // remember, we have to update 2 different locations. First is, when we CLEAN UP the whole line, Second is here.
}
}
}
}
Some additionnal quirks should be noticed.
flag_screen[NEWTITLE]--;
flag_screen[] is an array of chars that are used as flags to know which part of the screen must be updated. In some rare cases, I need to update multiple parts of the screen with only one flag. In order to do them in the right order, I decided to use chars instead of simple booleans, and use flag_screen[] as a countdown latch.
I'm rather satisfied with how I strip down the title. It's far better than previously where I played along with pointers and sometimes it messes up everything.
The whole screen refresh is done in another RTOS task, actually "mainTask". I won't write it here, it's not that interesting. I will post my code some day on some GitHub, promise. Leave a comment if you want it now !
One interesting point is that, "mainTask" doesn't have priority on taking over the LCD. The whole function is in a "if(!isLCDused)", and if it's not, the whole function will just be skipped. It's run at 5Hz, so by far sufficiently fast to actually refresh the screen in case of trouble, even for time display.
Let's talk about time ! As you may have seen in previous videos, I implemented a local time counter. At first, it was messed up because of the delays in the scrolling. Now, it works fine ! Here's my code :
static void localTimeTask(void *pvParameters) {
while (1) {
seconds++;
vTaskDelay(1000);
flag_screen[NEWTIME]++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
if (hours >= 24) {
hours = 0;
}
} //endif (minutes)
} // endif(seconds)
} //endwhile
}
Well, it may work nice, but I will try to rewrite it using the RTClock library for STM32, because it should me much more optimized and it has built-in structures to use seconds, minutes, hours, and days. Why do days matter ? Well, maybe in a future version, I would like my alarm clock to fire only on certain days, only on weekdays for example...
Finally, I made a function to monitor a button state with polling, and to take action on press, but I think it deserves a dedicated article. It's better to keep things a bit organized.
By the way, my TM1637 - controlled 7-segment clock arrived today ! Can't wait to try it out ! I think I'll probably make some kind of test program to learn how to display things on the TM1637, and how to use the EEPROM to store reboot-resistant variables (for alarm clock time !).
Exciting times ahead ! See you next time !
This article will be text-only. Sorry, but it will be about code, so I don't have a lot to show, except that "it works" just as shown in the previous vidéo. Go take a look at the previous article !
So, I had that problematic misconception of my previous screen functions. Remember, the whole problem was :
- I wrote them as part of the LiquidCrystal_I2C library, which means I had to share my modified library, which is highly unpractical and dirty,
- I wrote them with delays everywhere, which stopped the whole microcontroller, and this is a huge pain in the ass because nothing looks okay.
The thing is, on my little 20x4 screen, space is limited. The main feature I wanted was a scrolling feature, like you would find in any serious radio device, with the following features :
- The scroll should go forward with X chars (X being tweakable),
- The scroll should wait for N milliseconds between each step, with 2*N waiting at the beginning of the scrolling and at the end (to let you read calmly !)
- The display for the song title and artist (I don't split them) should be reset at every song change. That means, every time the KaRadio sends a song title, a flag is triggered, and the scrolling is interrupted, the screen cleared, and the new song begins to scroll from the beginning.
I also implemented another trick : I noticed that the screen may be accessed by two functions running in parallel. That's a common case of single resource where a mutual exclusion must be implemented. I managed to get rid of that by introducting a mutex with the boolean "isLCDused". So far, I haven't seen any wrong behaviour.
To gather all the informations I need, I created a small structur, with a "Song" instance.
struct InfoScroll {
char line; // line number, 0-3
char *s_connect;
char fw;
int waiting;
};
InfoScroll Song = {0, "INIT...", 2, 900};
My RTOS function looks like this :
static void printScrollRTOSTask(void *pvParameters) {
while (1) {
vTaskDelay(10);
bool finished = false;
int i = 0;
int len = strlen(Song.s_connect);
String p_connect = String(Song.s_connect);
while (!finished)
{
// Get the song title, start at position 'i', truncate it if needed
String p_message = p_connect.substring(i, (i + LCD_WIDTH < len ? i + LCD_WIDTH : len));
// To avoid trailing at the right of the screen, fill with blanks
for (int k = len - i; k < LCD_WIDTH; k++)
p_message += " ";
while (isLCDused); // wait for the LCD to be available.
// Write that stuff
isLCDused = true;
lcd.setCursor(0, Song.line);
lcd.print(p_message);
isLCDused = false;
// Wait a bit if it's the first line...
if (i == 0)
vTaskDelay(2 * Song.waiting);
// Wait a bit and flag 'finished' if it's the last time
if (i + LCD_WIDTH >= len)
{
finished = true;
vTaskDelay(2 * Song.waiting);
}
// Go forward in the display !
i += Song.fw;
vTaskDelay(Song.waiting);
// If we have to update the title, just flag 'finished' so that the new title is taken into account at the beginning.
if (flag_screen[NEWTITLE] == 1)
{
finished = true;
flag_screen[NEWTITLE]--; // remember, we have to update 2 different locations. First is, when we CLEAN UP the whole line, Second is here.
}
}
}
}
Some additionnal quirks should be noticed.
flag_screen[NEWTITLE]--;
flag_screen[] is an array of chars that are used as flags to know which part of the screen must be updated. In some rare cases, I need to update multiple parts of the screen with only one flag. In order to do them in the right order, I decided to use chars instead of simple booleans, and use flag_screen[] as a countdown latch.
I'm rather satisfied with how I strip down the title. It's far better than previously where I played along with pointers and sometimes it messes up everything.
The whole screen refresh is done in another RTOS task, actually "mainTask". I won't write it here, it's not that interesting. I will post my code some day on some GitHub, promise. Leave a comment if you want it now !
One interesting point is that, "mainTask" doesn't have priority on taking over the LCD. The whole function is in a "if(!isLCDused)", and if it's not, the whole function will just be skipped. It's run at 5Hz, so by far sufficiently fast to actually refresh the screen in case of trouble, even for time display.
Let's talk about time ! As you may have seen in previous videos, I implemented a local time counter. At first, it was messed up because of the delays in the scrolling. Now, it works fine ! Here's my code :
static void localTimeTask(void *pvParameters) {
while (1) {
seconds++;
vTaskDelay(1000);
flag_screen[NEWTIME]++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
if (hours >= 24) {
hours = 0;
}
} //endif (minutes)
} // endif(seconds)
} //endwhile
}
Well, it may work nice, but I will try to rewrite it using the RTClock library for STM32, because it should me much more optimized and it has built-in structures to use seconds, minutes, hours, and days. Why do days matter ? Well, maybe in a future version, I would like my alarm clock to fire only on certain days, only on weekdays for example...
Finally, I made a function to monitor a button state with polling, and to take action on press, but I think it deserves a dedicated article. It's better to keep things a bit organized.
By the way, my TM1637 - controlled 7-segment clock arrived today ! Can't wait to try it out ! I think I'll probably make some kind of test program to learn how to display things on the TM1637, and how to use the EEPROM to store reboot-resistant variables (for alarm clock time !).
Exciting times ahead ! See you next time !
Comments
Post a Comment