Incorrect counter value on LCD screen - Page 1
Forum section: electronics / general

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 19, 2023, 07:31:33 am
content: I am incrementing/decrementing a counter with the help of a rotary encoder and displaying its value on a 16x2 LCD, but the count is wrong. What could be the culprit?Code:int value = 0;char buff;LCD_Goto(5, 2);sprintf(buff, "%d", value);LCD_Print(buff);i.e.: if I rotate CW, each step produces:012345678910Now If I start rotating CCW, each step produces:90807060504030201000Also, once I reach 100 and rotate CCW, the count is at 900 instead of 99!What am I doing wrong?The encoder is properly debounced in HW using RC network and a Schmitt trigger.

author: pcprogrammer
author_rank: Super Contributor
timestamp: February 19, 2023, 07:38:58 am
content: Show us the full code.The sprintf writes the value found in the variable value in decimal format to the buffer buff, which should work fine.Edit: It is better to use buffer size restricted functions like snprintf.Quotehttps://cplusplus.com/reference/cstdio/snprintf/

author: Siwastaja
author_rank: Super Contributor
timestamp: February 19, 2023, 07:45:08 am
content: You are not clearing the screen. The 0 from the "10" remains on the screen when you print shorter strings. Simplest fix is to clear by writing a few spaces that overwrite old content:sprintf(buff, "%d", value);->sprintf(buff, "%d ", value);(notice the added two spaces, which are good up to count 999)Another option is to use an LCD clear command, but that might cause some flicker, depending on the LCD, so sometimes clearing just the space needed with spaces is the best option.Yet another option is to use something like %3d instead of %d, which makes the printf itself append spaces at the beginning, right-aligning the number. Or %03d if you want zero-padding, like 007.Other comments:* If your number is never negative, use unsigned int instead, and %u instead of %d. This way it is obvious that - will be never printed* If you have a 32-bit MCU, printing anintcan produce up to 11 characters (e.g., -2000000000), which will overrun your 10-byte buffer, so:* Use snprintf(buff, sizeof buff, "%d", value) instead. Standard sprintf will overwrite other things in memory, causing any weird and hard-to-debug bugs, if the string is longer than buff, which easily happens by accident.

author: mariush
author_rank: Super Contributor
timestamp: February 19, 2023, 09:13:03 am
content: Or don't use sprintf or snprintf or other printf functions at all, write your own functionfunction takes an int as parameter, tests if it's above some threshold and if so, caps it at the maximum allowedthen keep dividing by 10 and have the remainder stored in a byte arrayex in phpCode:<?phpfunctionnumber_to_characters($number,$max_digits=3) {$characters= [];$nr=$number;if ($nr<0)$nr=0-$number;$max=0;$digits=1;for ($i=1;$i<=$max_digits;$i++) {$max=$max*10+9;if (($i==$max_digits) && ($nr>$max))$nr=$max;if ($nr>$max)$digits=$i+1;}$characters =chr($digits);// chr converts byte value to characterfor ($i=1;$i<=$digits;$i++) {$characters[$i] =chr(32);// space, not needed but good to have if you add padding/align to right}$offset=$digits;while ($nr>0) {$remainder=$nr%10;// remainder of nr/10$characters[$offset] =chr(0x30+$remainder);// "0".."9";$nr=intdiv($nr,10);// divide by 10$offset--;}return$characters;// characters holds number of actual characters;// then characters 1..n the digits as single chars;// could easily alter function to always align to right// and have a minimum length;}var_dump(number_to_characters(123,3));?>will output something likeCode:c:\Programs\php>php test1.phparray(4) {=>string(1) "♥"=>string(1) "1"=>string(1) "2"=>string(1) "3"}heart is the character for ascii code for 3 or 0x03 in the default code page in this case.note code above ignores the sign, but you could detect if number is negative and put the sign at the end in front of first character or you could have characters as number of digits, characters could be "-" or "+" or " " , then characters  to [digits+1] could be your charactersyou can customize function however you want it.

author: Siwastaja
author_rank: Super Contributor
timestamp: February 19, 2023, 09:50:20 am
content: Quote from: mariush on February 19, 2023, 09:13:03 amOr use itoa(), utoa(), which are simpler and more lightweight than sprintf and do what your own implementation would likely do.I use my own itoa()-like functions because I wanted to have it return a pointer to theendof the generated string (itoa() returns the start of the string) so that concatenating stuff is simple. Writing such primitive things from scratch may seem counterproductive, but they go from project to another so that job was only done once. By having the working implementations at hand, one can easily make small modifications when weird requirements pop up.

author: mikerj
author_rank: Super Contributor
timestamp: February 19, 2023, 11:38:22 am
content: Quote from: Siwastaja on February 19, 2023, 09:50:20 amThat's a great idea, the return values from the standard C string functions make concatenation hopelessly inefficient.

author: pcprogrammer
author_rank: Super Contributor
timestamp: February 19, 2023, 11:43:15 am
content: Quote from: Siwastaja on February 19, 2023, 09:50:20 amIn multiple MCU projects I did, I don't even use the standard C libraries. Write everything I need myself, and like Siwastaja wrote, you only do this once per type of functionality. Sometimes it is a bit of a think and search in which project you did what though.

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 19, 2023, 05:06:25 pm
content: Many thanks to all of you!Since I don't really need negative values, I changed the type for the counter to uint_8 and formatted it to 3 decimal places, it works fine now. Will keep in mind using snprintf for future use when using it for strings to avoid buffer overflows.For now I'm using a PIC16F887 which is kind of limited on program space and I'd like to use as much available from the library as possible.Code:uint8_t value = 0;sprintf(buff, "%3u", value);

author: Siwastaja
author_rank: Super Contributor
timestamp: February 19, 2023, 06:35:02 pm
content: In such use, sprintf instead of snprintf is IMHO fine because it's easy to prove (at least to yourself) that uint8_t when printed with %u cannot produce more than 4 bytes of output (including the zero terminator). Thus, buf is enough.If you get rid of sprintf completely and only use itoa/utoa, you will likely save quite a bit of program memory. printf functions are quite complicated because they can deal with gazillion of format options, and parse the format string during runtime. Unless your environment already uses a dumbed down version of sprintf, in which case the saving might not be that much, but worth trying.

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 19, 2023, 06:50:46 pm
content: Quote from: Siwastaja on February 19, 2023, 06:35:02 pmUnfortunately using utoa or itoa didn't print any value on the LCD for me.Code:char buff;uint8_t value = 5;utoa(value, buff, 10);LCD_Print(buff);