CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Writing to a standard 20x2 lcd: How fast can it display ?
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
scottc



Joined: 16 Aug 2010
Posts: 95

View user's profile Send private message

Writing to a standard 20x2 lcd: How fast can it display ?
PostPosted: Wed Sep 29, 2010 8:05 pm     Reply with quote

Hi guys, I have a general question on the standard 20X2 lcd display
modules that use the Hitachi hd44780 chipset running in 4bit mode.

Currently I can do a complete write to my display in about 1.3ms.
Thats the time the Logic Analyzer displays for putting text etc onto
the actual display, "complete write"

I am wondering if I configure the display for 8bit mode will I gain
anything on the 1.3ms write speed ?

My reason for asking is that I have a rotary encoder routine that updates
a variable that gets displayed on the LCD. If I spin the encoder really
fast, faster than a end user would twist the encoder shaft I drop counts.

The actual encoder routine is not dropping counts. The display just can't
keep up with the changing variable as its changing pretty darn quick.
I tested the encoder routine by writing a H_L sequence out to a pin.
and its not missing counts.

The Processor is a 18f2331 running at 8Mhz, The encoder routine and the
display routine are not using any interrupts.

I am trying to make my routine's a little more robust to give the end user
a better experience with the rotary encoder interface. The road block
is Display.

Any ideas or workarounds most welcome.

Thanks Scott
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

View user's profile Send private message Visit poster's website

PostPosted: Wed Sep 29, 2010 9:56 pm     Reply with quote

8bit mode will be faster, probably around twice as fast. However, it sounds to me like your encoder routine *is* dropping counts. If it wasn't, then when the encoder input slowed down (as it eventually must), your display routine would catch up and display the correct count.

It sounds like your display routine is holding off the encoder routine to the point where the encoder routine is missing signal changes.

You don't say if you check the encoder inputs while updating the display, but that's what I'd recommend first. Check the encoder inputs after every 4-bit transfer. That is, every couple of dozen microseconds. If that doesn't fix the problem, then changing to 8-bit mode won't achieve anything (as you're already checking the encoder inputs as fast as practical).
_________________
Andrew
scottc



Joined: 16 Aug 2010
Posts: 95

View user's profile Send private message

PostPosted: Wed Sep 29, 2010 10:13 pm     Reply with quote

Andrew, I don't check the encoder per-se but I do, a write to the display
each time the encoder increments or decrements the count variable
"Value".

Here is a sample of the routine to clarify. Can this approach be improved ?

Thanks Scott
Code:

Void Rotary_Encoder(void)
{
   Enc_A = (port_c & 0b11000000);   
                                   
   if(Enc_A != Enc_B)     {       
   if (bit_test(Enc_A,7))  {       
   if (!bit_test(Enc_B,7)) {       
   
   if (bit_test(Enc_A,7) == bit_test(Enc_B,6))
   {
      value++;              //Increment Value
     }
   else
   {
      value--;               //Decrement Value
   }
   }     
   }   
   }
   Enc_B = Enc_A; // save state to complete routine.
   Display_Stuff(); // call display function
 }

Currently I just call the encoder routine from main like so.
Code:

void main(void)
{

value=87;                     
             
while (true)
{
rotary_encoder();
}
 
}
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

View user's profile Send private message Visit poster's website

PostPosted: Thu Sep 30, 2010 1:15 am     Reply with quote

What I'd try is remove Display_Stuff from Rotary_Encoder. If Rotary_Encoder alters value, it should set a flag.

Then, your main line should call Rotary_Encoder, and if the flag is set, clear the flag, save the current value and call Display_Stuff to display the saved value.

Display_Stuff should itself be updated to call Rotary_Encoder more frequently, eg every 4-bit transfer. If the value changes during the display, the flag will be set when it returns to the main loop.

BTW, you don't seem to be using the QEI built into the '2331 to handle the encoder?
_________________
Andrew
Ttelmah



Joined: 11 Mar 2010
Posts: 19338

View user's profile Send private message

PostPosted: Thu Sep 30, 2010 2:09 am     Reply with quote

There are multiple parts to this:
1) How fast can data be transferred to the normal display memory.
2) How fast can _commands_ execute.
3) How fast can the display update.

Now, the first will be faster using 8bit mode, but not by a lot. The normal figure for the Hitachi controller, is that a single character write takes 46uSec. Even using 4bit mode, the CPU could transfer the data in perhaps 1/4 this time, so the change will not be at all large.
The second is potentially the first 'killer'. If you use something like a 'clear screen' command, this takes typically something between 1.5, and 2mSec to complete. An age. Simple 'goto' commands are reasonable, taking only about the same time as a normal character write.
The last prevents you from updating data on the display, and seeing it, at any great speed. There are two parts to this. The 'response time' of the display, and how fast it is actually scanned. Think of it like the old TV's, where frames were only updated every so often. You can write to the memory at a high speed, but the character shown, won't change till the next scan. Even then the display pixels take time to turn on/off, so if the update was 'every frame', you would probably not be able to read the result...
The response time depends on the LCD technology involved, but is typically quite slow on these text displays (10's of mSec), and the scan rate is typically something around 100KHz per column, giving a complete redraw in perhaps 1mSec, so it is the response time that really matters in this regard. This is usually slow enough, that you won't actually 'see' a character that updates more than perhaps 5 or 6 times/second.

The comments about "don't have LCD routines in the encoder handler", are dead right. You just have not got time to do this.

Ages ago, I did a system that needed rapid display 'reflection' of a fast internal counter, but was doing a lot of work on other things at the same time. The 'solution', was to approach things from the other end. I ran a timer interrupt at a high rate (200Hz), buffered the display data, and in each timer interrupt transferred just one character using 8bit LCD operations, with no waiting at all. The key was that the slowest operation on the LCD involved, still took slightly less time that the interval between interrupts. Hence I never had to check if the LCD was busy, but the interrupt routine, could just output the byte, strobe the LCD, and return immediately. This allowed the main operations to continue without problems, and the display updated as fast as it could.

Best Wishes
Ken Johnson



Joined: 23 Mar 2006
Posts: 197
Location: Lewisburg, WV

View user's profile Send private message

PostPosted: Thu Sep 30, 2010 12:20 pm     Reply with quote

Just a little more . . .

Yes, read the encoder as fast as required (sounds like you are).

But, update the display at a rate that is comfortable to the human observer. That is probably no more than a few times a second - maybe only 1 or 2. If you display different counts at, say, 10 times a second, can you read it?

I usually update a text display no more often than about .8 second intervals.

Good luck,
Ken
sturnfie



Joined: 26 Apr 2010
Posts: 17
Location: Palatine, IL

View user's profile Send private message Visit poster's website

PostPosted: Thu Sep 30, 2010 2:29 pm     Reply with quote

scottc wrote:
Andrew, I don't check the encoder per-se but I do, a write to the display each time the encoder increments or decrements the count variable
"Value".

Here is a sample of the routine to clarify. Can this approach be improved ?

Thanks Scott


The optical characteristic timing of the LCD character module I'm currently working with indicates a response time of 120ms. Undoubtedly you're able to spin the encoder faster than this, and presumably your display has a similar specification. You can write data to the display more quickly, but it will take near this specification for the bits to shift in the Hitachi controller and for the LCD pixels to set as to show the correct character. Attempting to update faster will lead to blurring of the modified character positions.

My suggestion: use an internal timer interrupt. The interrupt will exist only to increment a counter to a predefined maximum value and no further. In your code where you update the display, encapsulate the display update into a conditional statement that checks if the counter variable has reached the maximum value. Inside the statement, clear the counter (the variable must be of volatile type) and perform the display update.

Not "dropping counts" in terms of count inaccuracy is solved only running time-intensive code at a bare minimum. The display functionality is "time intensive" due to computation cycles relative to the encoder logic, but more so due to rest cycles (since the display might not be capable of refreshing as quickly as you're attempting). Don't update more than you need to, and update quickly when you do. If minimizing the total update interval still causes count inaccuracy (I can't imagine it would, not with a hand-actuated mechanical encoder system), moving to the "byte at a time" approach previously shared by another poster is a pretty clever solution.

Not "dropping counts" in terms of the display skipping numbers when attempting to keep up can be "solved" by allowing the display to blur (update faster than the optical response characteristic), by buffering the output to only display change by one integer value at a time toward the target value (depending on what you're controlling, probably a bad idea since inaccurate feedback to a user), or by overwriting the rapidly changing digits with an alternating ASCII pattern (forced speed blur).
_________________
Lucas Sturnfield
Blog - http://www.sturntech.com/blog
scottc



Joined: 16 Aug 2010
Posts: 95

View user's profile Send private message

PostPosted: Thu Sep 30, 2010 7:51 pm     Reply with quote

Thanks for your response guys,

A little history, Although I am using a PIC18F2331 Part I am not using
the dedicated QEI hardware that the chip provides as of yet. I had
a couple of reasons not to use it but in ver 4.110 of compiler there
are no defines for the qei in the 18f2331.h file. I dont have a working
example of using the QEI either so this would be a bit of an up hill
battle at the moment. I bet the qei would work real well but im not there yet Smile

The other reason is I wanted to generate some very generic routines that could be used on pretty much any pic. This way I could use the el-chepo mechanical rotary encoders on any projects that they might be needed on.

The encoder is the 3 pin type with a built in momentry push switch. the
encoder has 20 indents per one complete full turn of the shaft.

I wanted also to keep away from running a hardware ISR for the encoder
pins as this reduces the flexibility of what pins could be used. A timer
based ISR would be ok I guess.

I modified the code to get the call to the display out of the encoder
routine, It appears to work ok. Not sure if this is the best way to implement a changed flag or not.

Here is a snip.

Void Rotary_Encoder(void)
{
Enc_A = (port_c & 0b11000000);

if(Enc_A != Enc_B) {
if (bit_test(Enc_A,7)) {
if (!bit_test(Enc_B,7)) {
changed=true; //added flag
if (bit_test(Enc_A,7) == bit_test(Enc_B,6))
{
value++; //Increment Value
}
else
{
value--; //Decrement Value
}
}
}
}
Enc_B = Enc_A; // save state to complete routine.
Display_Stuff(); // call display function
}

I call this routine from main like this.

void main(void)
{
value=87; //Inital turn on value for display

while (true)
{
rotary_encoder();

if (changed == true) {

display_stuff();

changed=false;
}


}

}
--------------------------------------------------------------------------------
I can spin the shaft on the encoder pretty darn quick and it appears
that the display updates fairly well. If anyone could help clarify the
method on the changed flag I have implemented is ok that would be a big
help

Thanks Scott
sturnfie



Joined: 26 Apr 2010
Posts: 17
Location: Palatine, IL

View user's profile Send private message Visit poster's website

PostPosted: Thu Sep 30, 2010 8:05 pm     Reply with quote

scottc wrote:

Code:

Void Rotary_Encoder(void)
{
   Enc_A = (port_c & 0b11000000);   
                                   
   if(Enc_A != Enc_B)     {       
   if (bit_test(Enc_A,7))  {       
   if (!bit_test(Enc_B,7)) {       
    changed=true;          //added flag
   if (bit_test(Enc_A,7) == bit_test(Enc_B,6))
   {
      value++;              //Increment Value
     }
   else
   {
      value--;               //Decrement Value
   }
   }     
   }   
   }
   Enc_B = Enc_A; // save state to complete routine.
   Display_Stuff(); // call display function
 }


I call this routine from main like this.

Code:

void main(void)
{
    value=87;                     //Inital turn on value for display
             
while (true)
{
   rotary_encoder();
   
   if (changed == true) {
         
       display_stuff();
       
       changed=false;
   }
   
   
}
 
}


--------------------------------------------------------------------------------
I can spin the shaft on the encoder pretty darn quick and it appears
that the display updates fairly well. If anyone could help clarify the
method on the changed flag I have implemented is ok that would be a big
help

Thanks Scott


You should remove the display_stuff() call from the rotary_encoder() function. Currently, you're printing to the display irregardless, with an extra print each time you actually need to print something Smile.
_________________
Lucas Sturnfield
Blog - http://www.sturntech.com/blog
scottc



Joined: 16 Aug 2010
Posts: 95

View user's profile Send private message

PostPosted: Thu Sep 30, 2010 8:22 pm     Reply with quote

ah good catch sturnfie, I neglected to update the snip I posted, here is
what was used.
Code:

Void Rotary_Encoder(void)
{

   Enc_A = (port_c & 0b11000000);   // keep 2 bits of port C
                                    // Encoder on RC6, RC7 Port C
   if(Enc_A != Enc_B)      {        // Test 2 Bits
   if (bit_test(Enc_A,7))  {        // Test Bit on PIN RC7
   if (!bit_test(Enc_B,7)) {        // Pulse Train is rising
   changed=true;
   if (bit_test(Enc_A,7) == bit_test(Enc_B,6)) //Test both bits
   {
   
   value++;              //Increment Value
   }
   else
   {
   
   value--;               //Decrement Value
 
   }
   }     
   }   
   }
   Enc_B = Enc_A; // save state to complete routine.
   
 }

Updated to remove a delay, posted wrong snip Smile

Thanks Scott


Last edited by scottc on Thu Sep 30, 2010 8:45 pm; edited 1 time in total
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

View user's profile Send private message Visit poster's website

PostPosted: Thu Sep 30, 2010 8:31 pm     Reply with quote

The DELAY_MS in Rotary_Encoder should not be there! That will be causing the same problems as calling Display_Stuff was in the first place.

Also, Display_Stuff should also be calling Rotary_Encoder as often as possible. In case that updates the changed flag, you should set the changed flag to false before calling Display_Stuff.
_________________
Andrew
scottc



Joined: 16 Aug 2010
Posts: 95

View user's profile Send private message

PostPosted: Thu Sep 30, 2010 8:49 pm     Reply with quote

Andrew, I posted the wrong snip, I had the delay in there as a test.
I updated the privious post to reflect the change.

I will add the changed flag to the display_stuff routine and also call the encoder routine from there to see how that goes.

Hopefully I can post the correct compiled snip next time Smile

Thanks for the help

Scott
scottc



Joined: 16 Aug 2010
Posts: 95

View user's profile Send private message

PostPosted: Thu Sep 30, 2010 9:30 pm     Reply with quote

Ok, I made the following changes.
Code:

void Display_Stuff()
{
  Changed=false;
  Lcd_Config( LCD_LINE1 );
  printf(Write_LCD, "  %lu ",Value);
  Rotary_Encoder();
}

void main(void)
{
    value=87;                   
             
while (true)
{
   rotary_encoder();
   
   if (changed == true) {
        display_stuff();
        changed=false;
   }
     }
  }

It does appear to help. turning the encoder shaft by hand at a fairly
fast pace results in an encoder pulse every 12ms. This would be
quicker than a end user would turn it. If I turn it even faster which
takes the encoder pulse down to 5ms the LCD still updates fine.

Pushing the envelope by taking the encoder and placing it between my
thumb and finger I can flick twist it so it goes through its count even
faster. I'm talking a pulse every 1.5ms, the display still updates ok.

Counts on the display agree with counts on the Logic analyzer. At this
fast rate I do notice a few erratic pulses more than likely due to the
mechanical contacts in the encoder bouncing. Not really a issue tho
for the end user as its way beyond what they might subject the encoder
to "I hope" Smile

I still might have a go at trying this in 8bit LCD mode, but even running in 4bit mode I think its doing pretty good considering the cpu is not running
any ISR.

Thanks for the input guys, was trying to make this dog as robust as possible.

I appreciate you help

Scott
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

View user's profile Send private message Visit poster's website

PostPosted: Thu Sep 30, 2010 10:01 pm     Reply with quote

Try calling Rotary_Encoder from Write_LCD. Really, from whatever your lowest-level LCD function happens to be.

Also, the order of setting changed to false first is important on the chance that the encoder moves while in display_stuff and stops. The display won't update to the correct value until it moves again.
_________________
Andrew
scottc



Joined: 16 Aug 2010
Posts: 95

View user's profile Send private message

PostPosted: Thu Sep 30, 2010 10:16 pm     Reply with quote

Hummm, interesting ideas Andrew worth a shot IMHO.

You know if I was using the display in 8bit mode I could use the Busy
Flag to call the encoder after each write was complete and the display
was ready. I might for the hell of it see how 8bit mode does with this.
It would be interesting to see.

As a matter of interest, do you think I might pick up any speed if I used
the qei module instead of just using standard pins for the encoder. I am
curious to know if there are any advantages to using the hardware based
qei in the 18f chips.

Thanks Scott
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group