formating encoder count to 2 decimal places for LCD

General discussion on mikroC PRO for PIC.
Post Reply
Author
Message
cbrun17
Posts: 45
Joined: 31 Jan 2017 17:50

formating encoder count to 2 decimal places for LCD

#1 Post by cbrun17 » 29 Dec 2017 22:47

Hello,

I am trying to display an encoder count to an LCD display using interrupt-on-change. The custom encoder outputs pulses on one of two pins depending on the direction turned. The encoder outputs 4 pulses per degree (1440 pulses per 360 degree revolution).
I'm attempting to display the value in an xx.xx format. In other words, it should display an angle of 0.25 degrees per increment such as 2.25, 36.50, 45.75,etc.. The only way I can accomplish this is by my following code. While this does work, it seems there should be a much simpler way to achieve the same thing. Any help would be greatly appreciated.

I am using a PIC18F4520 @ 20MHz.
NOTE: Yes I am aware of built in Quadrature and other features of this MCU, however, these are not an option for this specific encoder.

Code: Select all

int count1;
int countx;
int A_STEP;
char countx_txt[9];
char count1_txt[9];
int Tresult1;
int Tresult2;


void interrupt(){
 
// Evaluate encoder1
 if(INTCON.RBIF==1){

    if(!ENC1_DWN) //Turn counterclockwise
 {
      A_STEP=(A_STEP-1); //count down
      LED_DOWN = 0;
      LED_UP = 1;
 }    
         else if(!ENC1_UP) //Turn clockwise
 {
            A_STEP=(A_STEP+1); //count up
      LED_DOWN = 1;
      LED_UP = 0;
 }

 INTCON.RBIF=0; //Clear port B interrupts flag

  return;
 }


void main(){

 count1 = 0; // Default value = 0
 count2 = 0; // Default value = 0

  CustomChar0();

 do {
count1 = (abs (A_STEP))/4;         // Get absolute value of count (eliminate -) and extract whole number
countx = (count1*25%100);     // Extract each count (.25)
 
IntToStr(Count1, Count1_txt);   
IntToStr(Countx, Countx_txt);

Tresult1 = strlen(Count1_txt);                  // Calculate the character length of count1 string
Tresult2 = strlen(Countx_txt);                  // Calculate the character length of countx string 

   Lcd_Out(1,1,ltrim(rtrim(Count1_txt)));   // Display count1 number
   Tresult1 = Tresult1+1;                          // Add 1 to the character length calculation
   Lcd_Out(1, Tresult1, ".");                      // Add decimal point
   Tresult1 = Tresult1+1;                          // Add 1 to the character length calculation
   Lcd_Out(1,Tresult1,ltrim(rtrim(Countx_txt)));  // Display countx number
   Tresult2 = (Tresult1-1)+Tresult2+1;           // Add both character length results + 1 to the character length calculation
   Lcd_Chr(1,Tresult2,0);      // Display degree character

delay_ms(50);

Lcd_Out(1,1,"     ");
Lcd_Out(2,1,"     ");
      LED_DOWN = 1;
      LED_UP = 1;

 } while(1);
 } // endless loop

MARIO
Posts: 978
Joined: 18 Aug 2008 22:13
Location: Brasil

Re: formating encoder count to 2 decimal places for LCD

#2 Post by MARIO » 01 Jan 2018 15:34

Hi,

Where are the definitions of ENC1_DWN, LED_DOWN, LED_UP, ENC1_UP?

And the subroutine CustomChar0()?

May you attach the zip folder with the project?
BR
EasyPic6 and registered mkC PRO FOR PIC since 2011
You're never too old to learn something stupid.(PARAPROSDOKIAN)

cbrun17
Posts: 45
Joined: 31 Jan 2017 17:50

Re: formating encoder count to 2 decimal places for LCD

#3 Post by cbrun17 » 01 Jan 2018 21:22

Hi Mario,

Here is the zipped project you requested. Thank you.
Attachments
Angle_Display_Code.zip
Full project code.
(6.87 KiB) Downloaded 100 times

MARIO
Posts: 978
Joined: 18 Aug 2008 22:13
Location: Brasil

Re: formating encoder count to 2 decimal places for LCD

#4 Post by MARIO » 03 Jan 2018 01:22

Hi cbrun17.

Excuse me but I have no time to work on your code.

I'm trying now. But can you explain better the encoder signals?

Is ENC1_UP the logic negation of ENC1_DWN, the same idea for ENC2?

And MRST is the pulse when the encoder completes one turn?

A datasheet would be better and a schematic too.

EDIT: I see a comment in the code that the encoder is Gray-code type. Is this correct?
I'm trying another approach so I need exactly what you want and how the encoder works!
May you explain it better?
BR
EasyPic6 and registered mkC PRO FOR PIC since 2011
You're never too old to learn something stupid.(PARAPROSDOKIAN)

cbrun17
Posts: 45
Joined: 31 Jan 2017 17:50

Re: formating encoder count to 2 decimal places for LCD

#5 Post by cbrun17 » 04 Jan 2018 02:50

Hi Mario,

The operation is really quite simple. The output of both encoders are already decoded prior to the PIC controller.

There are two encoders (encoder1 & encoder2) connected to the PIC18F4520. Each encoder has two output pins. Both outputs sit high. ENC1_UP produces a 8uS low pulse only when the encoder is turned clockwise and ENC1_DWN produces an 8uS low pulse only when the encoder is turned counter clockwise.

Clockwise increments the counter1 variable and counter clockwise decrements the counter1
variable. The exact same process for encoder2
The MRST is button input that allows me to reset the variables and display to 0.0.

The code I provided works very well, however, I was looking for a way to shift bits to display count in 0.25 increments rather than splitting the counts the way I did and later formatting to the display. Just seems like a slow and cumbersome way to achieve the result. I did attempt to use double variables but I was getting garbage results.

Hope this helps.

Kind Regards,

Chris

MARIO
Posts: 978
Joined: 18 Aug 2008 22:13
Location: Brasil

Re: formating encoder count to 2 decimal places for LCD

#6 Post by MARIO » 04 Jan 2018 20:54

Hi cbrun17.

Try that:

Code: Select all

short i;
signed long count;
char *res, col;
int leng ;
int A_STEP ;
int B_STEP ;
char count_txt[12];
bit int_flag;

// Custom characters
const char  character0[] = { 7, 5, 7, 0, 0, 0, 0, 0 };  // Degree Icon

/* */
void CustomChar0()
{
    char    i;
    Lcd_Cmd ( 64 );
    for ( i = 0; i <= 7; i++ ) Lcd_Chr_CP ( character0[i] );
    Lcd_Cmd ( _LCD_RETURN_HOME );
}

// ***********************************************
#define MRST        PORTC.F0
#define ENC1_DWN    PORTB.B5
#define ENC1_UP     PORTB.B4
#define ENC2_DWN    PORTB.B7
#define ENC2_UP     PORTB.B6

// LCD Configuration section
sbit LCD_RS at              LATA4_bit;
sbit LCD_EN at              LATA5_bit;
sbit LCD_D7 at              LATA3_bit;
sbit LCD_D6 at              LATA2_bit;
sbit LCD_D5 at              LATA1_bit;
sbit LCD_D4 at              LATA0_bit;
sbit LCD_RS_Direction at    TRISA4_bit;
sbit LCD_EN_Direction at    TRISA5_bit;
sbit LCD_D7_Direction at    TRISA3_bit;
sbit LCD_D6_Direction at    TRISA2_bit;
sbit LCD_D5_Direction at    TRISA1_bit;
sbit LCD_D4_Direction at    TRISA0_bit;

/* */
void ClearAll()
{
    Lcd_Out ( 1, 1, "            " );
    Lcd_Out ( 2, 1, "            " );
    count = 0;
    A_STEP = 0;
    B_STEP = 0;
    int_flag = 0;
}

/* */
void interrupt()
{
    if ( INTCON.RBIF == 1 )
    {
        // Evaluate encoder1
        if ( !ENC1_DWN )    //A DWN Turn counterclockwise
        {
            A_STEP--;    //count1 down
        }
        else if ( !ENC1_UP )            //A UP Turn clockwise
        {
            A_STEP++;    //count1 up
        }
        // Evaluate encoder2
        if ( !ENC2_DWN )            // B DWN Turn counterclockwise
        {
            B_STEP--;    //count2 down
        }
        else if ( !ENC2_UP )        // B UP Turn clockwise
        {
            B_STEP++;    //count2 up
        }
        int_flag = 1;
        INTCON.RBIF = 0;    //Clear port B interrupts flag
    }
}

/* */
void CONFIGURE()
{
    //ADCON0 = 0x07;
    ADCON1 = 0x0F;
//    ADCON2 = 0x07;
    CMCON = 0x07;
    /*CCP1CON = 0x00;
    CCP2CON = 0x00;
    SSPCON2 = 0x00;
    SSPCON1 = 0x00;
    CVRCON = 0x00;
    CCPR1 = 0x00;
    CCPR2 = 0x00;*/

    //TRISA = 0b00000000; // CONFIGURE i/o on Port A
    TRISB = 0b11110000;            // CONFIGURE i/o on Port B
    TRISC = 0b11111110;            // CONFIGURE i/o on Port C
    TRISD = 0b00001111;            // CONFIGURE i/o on Port D
    TRISE = 0b00001111;            // CONFIGURE i/o on Port E

    // Intialize LCD
    Lcd_Init ();                    // Initialize LCD
    Lcd_Cmd ( _LCD_CLEAR );         // Clear display
    Lcd_Cmd ( _LCD_CURSOR_OFF );    // Cursor off
    Lcd_Cmd ( _LCD_CLEAR );         // CLEAR display
    LCD_Out ( 1, 1, "Angle Measurement    " );
    LCD_Out ( 2, 1, "                     " );
    delay_ms ( 3000 );
    Lcd_Cmd ( _LCD_CLEAR );         // CLEAR display
    ClearAll ();
    INTCON.RBIE = 1;                // enable RB interrupts
    INTCON.GIE = 1;                 // enable Global interrupts
    GIE_BIT = 1;                    // Global Interrupt Enable bit | 1 = Enables all unmasked interrupts
}

void format( int Step )
{
            count = abs( Step * 25 );
            LongToStr(count, count_txt);
            //res = Ltrim(count_txt);
            leng = strlen( Ltrim(count_txt) );
            col = 7;
            switch (leng)
            {
                   case 1: strcat(count_txt,".00");
                           break;
                   case 2: count_txt[4]=0;
                           for (i = 1; i >-1; i--)
                               count_txt[i+2] = count_txt[i];
                           count_txt[0] = '0';
                           count_txt[1] = '.';
                           break;
                   case 3: for (i = 3; i > 0; i--)
                               count_txt[i+1] = count_txt[i];
                           count_txt[i+1] = '.';
                           break;
                   case 4: for (i = 4; i > 1; i--)
                               count_txt[i+1] = count_txt[i];
                           count_txt[i+1] = '.';
                           col=6;
                           break;
                   //default: ClearAll();
            }
}

void main()
{
    CONFIGURE ();
    count = 0; // Default value = 0
    CustomChar0 ();
    do
    {
         if (int_flag)
         {
             format(A_STEP);
             LCD_Out(1, col, count_txt);
             LCD_Chr_CP(0);
             format(B_STEP);
             LCD_Out(2, col, count_txt);
             LCD_Chr_CP(0);
             int_flag = 0;
         }
         if ( !MRST )
             ClearAll ();
    } while ( 1 );
}   // endless loop
Let me know if this is what you want.
BR
EasyPic6 and registered mkC PRO FOR PIC since 2011
You're never too old to learn something stupid.(PARAPROSDOKIAN)

cbrun_CCI
Posts: 10
Joined: 08 Nov 2017 17:27

Re: formating encoder count to 2 decimal places for LCD

#7 Post by cbrun_CCI » 08 Jan 2018 17:01

Hi Mario,

I will give this a try and let you know. Thank you very much for your help.

cbrun_CCI
Posts: 10
Joined: 08 Nov 2017 17:27

Re: formating encoder count to 2 decimal places for LCD

#8 Post by cbrun_CCI » 10 Jan 2018 15:09

Hi Mario,

I tried your code and while it does work, I found a couple formatting issues and it doesn't seem to process both encoders properly. I think you were giving me the general idea of how to format the results another way. I understand your method and will incorporate since the code is much cleaner. Just have to resolve a couple issues I found.

I really appreciate the time you took to work on this and provide a solution for me.

Kind regards,

Chris

MARIO
Posts: 978
Joined: 18 Aug 2008 22:13
Location: Brasil

Re: formating encoder count to 2 decimal places for LCD

#9 Post by MARIO » 13 Jan 2018 11:59

Glad to know I could help you.

As I do not have an encoder like that I only simulated it.

If I can help you in something else, I'm here.
BR
EasyPic6 and registered mkC PRO FOR PIC since 2011
You're never too old to learn something stupid.(PARAPROSDOKIAN)

diode_blade
Posts: 172
Joined: 24 Aug 2014 17:55
Location: Sheffield

Re: formating encoder count to 2 decimal places for LCD

#10 Post by diode_blade » 13 Jan 2018 13:51

Hi Cbrun,
How many pulses is your rotary encoder per channel?
Lets for argument sake you have a one channel encoder to get 0.25 degree resolution you would need encoder with 360/0.25 = 1440 pulses edges per revolution.
but that is only in one direction, so in effect you would need 2880 pulses edges per rev for a two channel encoder, if I have worked that correctly

re= rising edge, fe=falling edge (each pair of vertical |........| represents 1 pulse)
chan a
re fe nxt re nxt fe
..........|..........|..........|..........|
8us 8us

chan b
re fe nxt re nxt fe
..........|..........|..........|..........|
8us 8us
ahh this is what I mean
[img]

http://www.leniwiec.org/wp-content/uplo ... 77x300.png
[/img]

you will be spending most of your time in the ISR which is not good
I hope you can see what I mean by my very crude diagram above.

if you could provide somew details of the encoder being used
Plus, I would seriously consider using an 18F4431 series with a hardware encoder interface, I have used these before and they do all the hardwork for you.
"When the bugs strike it's no good bashing your head against a brick wall, you might damage the wall"

MARIO
Posts: 978
Joined: 18 Aug 2008 22:13
Location: Brasil

Re: formating encoder count to 2 decimal places for LCD

#11 Post by MARIO » 14 Jan 2018 14:12

Hi Chris.

I was thinking about what you said and maybe one or both of 2 things can be happening:
1. Speed rotation of the encoders too high. Are you using an oscilloscope? Maybe some kind of noise or poor quality signal is the problem.
2. As there are 2 encoders, what is the chance of coincidence of signals in a way the uC cannot deal with, missing some pulses?

Is there a kind of synchronism between them?

If you wish, give me more details, maybe I can help you further!
BR
EasyPic6 and registered mkC PRO FOR PIC since 2011
You're never too old to learn something stupid.(PARAPROSDOKIAN)

cbrun17
Posts: 45
Joined: 31 Jan 2017 17:50

Re: formating encoder count to 2 decimal places for LCD

#12 Post by cbrun17 » 08 Feb 2018 17:31

I apologize for the long delay and appreciate all the responses to my post.

I have my code working perfect now. The real issue I was trying to solve was formatting the results in a clean, efficient way. I'm relatively new to coding PICs and MikroC. That said, the displayed results I have now are exactly what I was trying to accomplish. The code below is what I ended up with and it does work well. Also, just to clarify, the output of these encoders are connected to an LS7183 decoder chip. That's why I didn't need the quadrature functionality of the PIC. The outputs are simply 8uS pulses (one CW pin and one CCW pin). Makes it easy to use any PIC family for this project.
Here is the code I used in case anyone is interested:

Code: Select all

//*************************************
//* Author: Chris                                           *
//* 2/3/2018                                                 *
//* Version 2                                                 *
//*************************************

// Project is based on PIC18F4520 @ 20MHz w/2x16 LCD display
// Using custom encoders which output decoded grey code. Each encoder has two outputs,  negative 8uS pulse counter-// clock-wise and other output 8uS clock-wise
// Each encoder output connected to ports B4-B7 with 10K pull-up (Interrupt on change)
// These encoders are using LS7183 decoder chips so PIC quadrature function is not needed.

// PORTA port assignments
// 0   LCD D4
// 1   LCD D3
// 2   LCD D2
// 3   LCD D1
// 4   LCD RS
// 5   LCD E
// 6   XTAL_IN
// 7   XTAL_OUT

// PORTB port assignments:
// 0
// 1
// 2
// 3
// 4   EncoderA UP
// 5   EncoderA DOWN
// 6   EncoderB UP
// 7   EncoderB DOWN

// Global Variables
int count1;
int count2;
int countx;
int county;
int A_STEP;
int B_STEP;
int leftright;
char countx_txt[7];
char county_txt[7];
char count1_txt[7];
char count2_txt[7];

// Custom characters ***********************************************
const char character0[] = {7,5,7,0,0,0,0,0};             // Degree Icon

void CustomChar0() {
  char i;
  Lcd_Cmd(64);
  for (i = 0; i<=7; i++) Lcd_Chr_CP(character0[i]);
  Lcd_Cmd(_LCD_RETURN_HOME);
  //Lcd_Chr(pos_row, pos_char, 0);
}

// ***********************************************

// Defines****************************************
#define MRST PORTC.F0
#define ENC1_DWN PORTB.B5
#define ENC1_UP PORTB.B4
#define ENC2_DWN PORTB.B7
#define ENC2_UP PORTB.B6
//************************************************

// LCD Configuration section
sbit LCD_RS at RA4_bit;
sbit LCD_EN at RA5_bit;
sbit LCD_D7 at RA3_bit;
sbit LCD_D6 at RA2_bit;
sbit LCD_D5 at RA1_bit;
sbit LCD_D4 at RA0_bit;
sbit LCD_RS_Direction at TRISA4_bit;
sbit LCD_EN_Direction at TRISA5_bit;
sbit LCD_D7_Direction at TRISA3_bit;
sbit LCD_D6_Direction at TRISA2_bit;
sbit LCD_D5_Direction at TRISA1_bit;
sbit LCD_D4_Direction at TRISA0_bit;


void ClearAll(){

Lcd_Out(1,1,"ANGLE1:         ");
Lcd_Out(2,1,"ANGLE2:         ");

count1 = 0;
countx = 0;
count2 = 0;
county = 0;
A_STEP = 0;
B_STEP = 0;
leftright = 0;
}

void interrupt(){
 // Evaluate encoder1
 if(INTCON.RBIF==1){

    if(!ENC1_DWN)            // Turn counterclockwise
        {A_STEP--;}            // count down
     else if(!ENC1_UP)        // Turn clockwise
        {A_STEP++;}          // count up
 }
 
  // Evaluate encoder2
  if(!ENC2_DWN)              // Turn counterclockwise
        {B_STEP--;}            // count down
   else if(!ENC2_UP)          // Turn clockwise
        {B_STEP++;}          // count up
 }


 INTCON.RBIF=0; //Clear port B interrupts flag
  return;
 }

 void CONFIGURE(){
  ADCON0 = 0x07;
  ADCON1 = 0x0F;
  ADCON2 = 0x07;
  CMCON =  0x07;
  CCP1CON = 0x00;
  CCP2CON = 0x00;
  SSPCON2 = 0x00;
  SSPCON1 = 0x00;
  CVRCON = 0x00;
  CCPR1 = 0x00;
  CCPR2 = 0x00;

 TRISA = 0b00000000;    // CONFIGURE i/o on Port A
 TRISB = 0b11110000;    // CONFIGURE i/o on Port B
 TRISC = 0b11001101;    // CONFIGURE i/o on Port C
 TRISD = 0b00001111;    // CONFIGURE i/o on Port D
 TRISE = 0b1111;           // CONFIGURE i/o on Port E
  
 INTCON.RBIE = 1;    // enable RB interrupts
 INTCON.GIE = 1;      // enable Global interrupts
 GIE_BIT=1;              // Global Interrupt Enable bit | 1 = Enables all unmasked interrupts

 // Intialize LCD
  Lcd_Init();                                   // Initialize LCD
  Lcd_Cmd(_LCD_CLEAR);               // Clear display
  Lcd_Cmd(_LCD_CURSOR_OFF);     // Cursor off


   // Initial Splash screen
  LCD_Out(1, 1, "Angle Test          ");
  LCD_Out(2, 1, "Two encoders        ");
  delay_ms(3000);
  ClearAll();
  return;
 }

void main(){
CONFIGURE();

//Send Custom Characters to CGRAM
  CustomChar0();
//End
  
 count1 = 90;   // Set default value
 count2 = 0;    // Set default value
 countx = 0;    // Set default value
 county = 0;    // Set default value
 
 CustomChar0();  // Show degree icon on display

 do {
// ############ extract count values for both sides of decimal (xx.xx) #####################
    
//This encoder starts at 90 degree position and counts down in either direction. | 0-90-0|
   // Format encoder A (Angle1)
   count1 = 360-(abs (A_STEP));              // Absolute raw count A encoder value + seed
   countx = (abs (count1))*25%100;        // Extract .25 counts
   count1 = (abs (count1))/4;                   // Extract integer counts

//This encoder starts at 0 degree position and counts up in either direction. | 90-0-90|
   // Format encoder B (Angle2)
   count2 = (abs (B_STEP));                   // Absolute raw count B encoder value
   county = ((count2)*25%100);            // Extract .25 counts
   count2 = (count2)/4;                         // Extract integer counts
   
   // convert integer results to string for displaying
   intToStr(Count1, Count1_txt);          // Convert A left of decimal integer to string
   intToStr(Countx, Countx_txt);          // Convert A right of decimal integer to string
   intToStr(Count2, Count2_txt);          // Convert B left of decimal integer to string
   intToStr(County, County_txt);          // Convert B right of decimal integer to string
  
  
     //Format text results and display encoder 2 (Angle2) value
   Lcd_Out(1,7,ltrim(rtrim(Count2_txt)));         // Display count1 number
   Lcd_Out_CP(".");                                         // Add decimal point
   Lcd_Out_CP(ltrim(rtrim(County_txt)));          // Display countx number
   Lcd_Chr_Cp(0);                                           // Display degree character
   Lcd_Out_CP(" ");                                         // clear positions when display changes
      
      //Format text results and display encoder 1 (Angle1) value
   Lcd_Out(2,7,ltrim(rtrim(Count1_txt)));         // Display count2 number
   Lcd_Out_CP(".");                                        // Add decimal point
   Lcd_Out_CP(ltrim(rtrim(Countx_txt)));         // Display countx number
   Lcd_Chr_Cp(0);                                          // Display degree character
   Lcd_Out_CP(" ");                                        // clear positions when display changes
// ********************************************************************************************************


if (!MRST){ClearAll();}   // Clear screen and reset to default

  } while(1);

 } // endless loop

Post Reply

Return to “mikroC PRO for PIC General”