Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
9 years, 7 months ago.
Trouble with display time and something else
Hi.
I`m trying to create a simple home security system.
I`m using STM32 Nucleo F401-RE with HD44780 20x4 LCD display with I2C and matrix keypad 4x3. In first line of the display I`m presenting state of the alarm(armed, disarmed, alarm triggered). Second line is used to display " * " when user input code to arm or disarm the alarm. This functions are working well, but if I want to display time in fourth line(I`m using this simple program http://developer.mbed.org/handbook/Time ) program is not working properly. " * " shows on fourth line not second as it has to be, and not every time when I press button on keypad the " * " appears. Also sometimes on the screen appears strange symbols.
I was trying to use Ticker and even the RTOS but there is no result and always when I`m trying to display on LCD state of alarm and the time I`ve got problem as above.
Please helm, if Anyone know where I`m making a mistakes.
Sorry for my poor english, I hope that You understand what is my problem.
main.cpp
#include "mbed.h" #include "TextLCD.h" #include "PinDetect.h" #include "keycode.h" #include "rtos.h" PinDetect sensor( PA_4 ); bool state = false; void clock(void const *args) { while(1) { lcd.locate(0,3); time_t seconds = time(NULL); //lcd.printf("Time = %s", ctime(&seconds)); char buffer[9]; strftime(buffer, 9, "%H:%M:%S", localtime(&seconds)); lcd.printf("%s", buffer); lcd.locate(0,1); Thread::wait(1000); } } void alarm_state() { int pass = secure(); if(pass == 0 && !state) { state = true; lcd.locate(0,0); lcd.printf(" armed "); lcd.locate(0,1); lcd.printf(" "); } else if(pass == 0 && state) { state = false; lcd.locate(0,0); lcd.printf(" disarmed "); lcd.locate(0,1); lcd.printf(" "); } else if(pass != 0) { lcd.locate(0,1); lcd.printf(" wrong code "); wait(1); lcd.locate(0,1); lcd.printf(" "); } } void alarm() { if(state) { lcd.locate(0,0); lcd. printf(" alarm "); } } main() { set_time(1256729737); Thread threadclock); lcd.setBacklight(TextLCD::LightOn); sensor.attach_deasserted(&alarm); sensor.setSampleFrequency(); while(1) { alarm_state(); Thread::wait(500); } }
keycode.h
#include "../keypad/keypad.h" char code[] = "8888"; char code2[] = "9999"; int getCode() { lcd.locate(0,1); char codeTry[] = ""; int previous = -1; int a = -1; while(strlen(codeTry) < strlen(code)) { int cycles = 0; while ((a == previous && cycles < 20) || a == -1) { //keep looping until different to last, or held down a = keyNum(); if(a == 10) { sprintf(codeTry, ""); lcd.locate(0,1); lcd.printf(" "); lcd.locate(0,1); } cycles++; wait(0.01); } if(a != 11 && a != 10) { sprintf(codeTry, "%s%d", codeTry, a); lcd.printf("*"); } previous = a; wait (0.1); if(a==11) a = -1; } if((strcmp(code2, codeTry) == 0)) { return 1; } return 0; } int secure() { int b; int lock = getCode(); do {b = keyNum();} while(b!=11) ; // "#" confirmation of the code if(lock == 1) { // if code OK return 0; }; return -1; }
1 Answer
9 years, 7 months ago.
Hi Wacław, I think your problem is that you use parallel threads and interrupt driven functions to access the LCD. One thread or function may be in the middle of writing something to the display when it gets interrupted by the sensor pin being pressed, which results in a re-locate of the cursor and writing some other text. The LCD lib will now get confused because the cursor does not return to the original location before the interrupt occurred. In fact it may be even worse since the I2C methods used to access the display are broken up right in the middle of some ongoing communications (garbled characters). You need a more careful deconflicted access to the display to make this work. Probably best to have a single method that is in control of all access to the display and use some shared variables and/or flags to provide this method with information from the other threads on what it needs to display (eg timestring, alarm state).
Thanks for a quick answer.
Unfortunately even without using interrupt from sensor (or any other interrupt) problem still exist. It`s a good idea that LCD is confused but I have no idea what is wrong.
I`m still trying to resolve this problem but still nothing.
posted by 27 Mar 2015Its not just the interrupt on sensor. You also have threads for clock and alarm_state that could access the display in parallel. Best approach is to restructure code and allow only one single function access to the display. It should be activated at regular intervals and then update lines 0..3 as needed.
posted by 27 Mar 2015I think its more than I can do. Unfortunately I have no idea how to do this.
Than You for your answer
posted by 27 Mar 2015Something like should work (not yet tested)
// Host PC Communication channels Serial pc(USBTX, USBRX); // tx, rx //shared variables bool armedstate = false; bool alarmtriggered = false; char codestring[10] = ""; int pass = 0; // all display access in this single function // could add some flags to prevent unnecessary updates when nothing changed void displayUpdate() { //Show Alarm state lcd.locate(0,0); if (alarmtriggered) { // "01234567890123456789" lcd.printf(" alarm "); } else if (pass == -1) { lcd.printf(" wrong code "); } else if (armedstate) { lcd.printf(" armed "); } else { lcd.printf(" disarmed "); } //Show keycodes lcd.locate(0,1); if (strlen(codestring) == 0) { lcd.printf(" "); // clear the display when there is nothing to show } else { lcd.printf("%s", codestring); // show the string with *** } //Show empty line lcd.locate(0,2); lcd.printf(" "); // clear the display //Show Time lcd.locate(0,3); time_t seconds = time(NULL); //lcd.printf("Time = %s", ctime(&seconds)); char buffer[9]; strftime(buffer, 9, "%H:%M:%S", localtime(&seconds)); lcd.printf("%s", buffer); } void alarm() { if(armedstate) { alarmtriggered = true; } } Ticker displaytick; char command; main() { set_time(1256729737); lcd.setBacklight(TextLCD::LightOn); sensor.attach_deasserted(&alarm); sensor.setSampleFrequency(); displaytick.attach(&displayUpdate, 0.5); // update display every 0.5 second while(1) { #if(1) //final code pass = secure(); // wait for keycodes and check: // pass==1 for code ok, pass == -1 for wrong code, pass == 0 otherwise #else //try this to test command = pc.getc(); pc.printf("\n\r"); switch (command) { case '*' : // key read pass = 0; if (strlen(codestring) < 4) { // make sure to stay within array boundaries sprintf(codestring, "%s%c", codestring, '*'); } else { sprintf(codestring, ""); } break; case '1' : // code ok pass = 1; break; case 'x' : // code wrong pass = -1; break; case 'a' : // alarmtrigger pass = 0; alarm(); break; default : pass = 0; // no valid key read break; } #endif if ((pass == 1) && !armedstate) { armedstate = true; } else if ((pass == 1) && armedstate) { armedstate = false; } else if (pass == -1) { wait(1); // wrong code, delay before next attempt pass = 0; // erase error report on display } } }
and the rest
#include "../keypad/keypad.h" char code[] = "8888"; char code2[] = "9999"; int getCode() { char codeTry[] = ""; int previous = -1; int a = -1; while(strlen(codeTry) < strlen(code)) { int cycles = 0; while ((a == previous && cycles < 20) || a == -1) { //keep looping until different to last, or held down a = keyNum(); if(a == 10) { sprintf(codeTry, ""); sprintf(codestring, ""); // clear string } cycles++; wait(0.01); } if(a != 11 && a != 10) { sprintf(codeTry, "%s%d", codeTry, a); if (strlen(codestring) < 4) { sprintf(codestring, "%s%c", codestring, '*'); // add * to string } } previous = a; wait (0.1); if(a==11) a = -1; } if((strcmp(code2, codeTry) == 0)) { return 1; } return 0; } int secure() { int b; int lock = getCode(); do {b = keyNum();} while(b!=11) ; // "#" confirmation of the code if(lock == 1) { // if code OK return 1; }; return -1; }
This code almost work. Problem is that every 0.5 second (ticker refresh) armedstate is changing and on the display I can see "armed" or "disarmed". I think that this happens because every 0.5 second armedstate is changing but I dont know why because I cannot enter any code.
posted by 28 Mar 2015Unfortunately this doesn`t work too. Now when I enter code after second digit, state is changing from disarmed to armed, and when I put next digit it is changing from armed to alarm. Also it has no effect when I enter all 4 digit and press # for accept the code.
Thank You for your involvement in solving the problem.
posted by 28 Mar 2015The codestring array size was not correct and this leads to problems. That should be fixed now. Note that the secure() and getCode() part does not look very clean and easy. You may want to fix that. For testing I have used keyboard keys instead. This code is still included, but disabled (part starts with command = pc.getc()). You can try this out yourself.
posted by 28 Mar 2015Now this code works until I press the button and change alarmtriggered state. Then program is stop working (clock is running). Also doesn`t work cleaning screen in second line (" * " after code entry). Manualy cleaning second line (by pressing * on keypad) works fine. Tommorow I will try to modify the code to run as it should.
Thanks very much for help and if You have any suggestions how to improve secure() and getCode() I would be glad if You show me the way how to do this.
edit Let`s say that I deal with cleaning second line (I`ve add " sprintf(codestring, ""); " in secure()).
Now I have to deal with suspending program when alarmtriggered is true.
posted by 29 Mar 2015