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 CCS Technical Support

ELM402 rotary decoder software emulator

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
silelis



Joined: 12 Jun 2007
Posts: 68
Location: Poland, podlaskie district

View user's profile Send private message

ELM402 rotary decoder software emulator
PostPosted: Mon Sep 26, 2016 1:06 pm     Reply with quote

Hello, I have spent some time on ELM 402 rotary decoder software emulator.

There were 2 reasons.

First of all the ELM402 is hard to buy.
Second that this chip in retail sales costs 8 Canadian dollars.

The code I post is written to PIC12f629 and it makes this microcontroller 1:1 ELM402 replacement.

Have fun. ;-)
Code:

//main.h

#include <12F629.h>
#use delay(internal=4000000)


#FUSES nomclr
#define signal_output      // only one should be defined signal_output or UART
//#define UART             // only one should be defined signal_output or UART
#ifdef UART
   #use rs232(baud=9600,parity=N,xmit=PIN_A0,rcv=PIN_A1,bits=8,stream=PORT1)
#endif

//#define debug_slower


#define Output_invert      PIN_A2      // If GND then output stable state is GNG
#define Pulse_Width        PIN_A3      // If GND then output pulse time is short
#define signal_B           PIN_A4
#define signal_A           PIN_A5
#ifdef signal_output
   #define encoder_UP      PIN_A0
   #define encoder_DOWN    PIN_A1
#endif


/* ELM402 configuration */
#define debounce_time      5500        //us  -->   5,5 ms
#define pulse_time_short   200         //us  -->   0,2 ms
#define pulse_time_width   2000      //us  -->   2 ms


Code:

//main.c

#include <main.h>

int OLD_states;
int NEW_states;

void debounce_inputs(void)
{
   unsigned int16 COUNT_TARGET = (unsigned int16) (debounce_time/1000)+1;
   unsigned int16 count_low_signal_A=0;
   unsigned int16 count_high_signal_A=0;
   unsigned int16 count_low_signal_B=0;
   unsigned int16 count_high_signal_B=0;
   do
   {
      delay_ms(1);     
      if (input(signal_A) == 0)
      {
         count_low_signal_A++;
         count_high_signal_A = 0;
      }   
      else
      {
         count_low_signal_A = 0;
         count_high_signal_A++;
      }   
      if (input(signal_B) == 0)
      {
         count_low_signal_B++;
         count_high_signal_B = 0;
      }   
      else
      {
         count_low_signal_B = 0;
         count_high_signal_B++;
      }
   }
   while((count_low_signal_A < COUNT_TARGET) && (count_high_signal_A < COUNT_TARGET) || (count_low_signal_B< COUNT_TARGET) && (count_high_signal_B < COUNT_TARGET));
}

int get_signals_state(void)
{
   return input(signal_A) * 2 + input (signal_B);
}


void clear_output_pins(void)
{
   int16 Pulse_time;
   if (input(Pulse_Width)==0)
   {
      Pulse_time = pulse_time_short;
   }
   else
   {
      Pulse_time = pulse_time_width;
   }
   delay_us(Pulse_time);
   
   if (input(Output_invert)==0)
   { 
      #ifdef signal_output
         output_low(encoder_UP);
         output_low(encoder_DOWN);
      #endif   
      #ifdef UART
         delay_us(1);
       #endif
       
   }
   else
   {
      #ifdef signal_output
         output_high(encoder_UP);
         output_high(encoder_DOWN);
       #endif
       #ifdef UART
         delay_us(1);
       #endif
       
   }
}

void set_output(int state)
{
   switch (state) {
      case 0:
         // rotar in the same state
         break;
      case -1:
         // step back
         #ifdef signal_output
            if (input(Output_invert)==0)
               { 
                  output_high(encoder_DOWN);
               }
            else
               {
                  output_low(encoder_DOWN);
               }
         #endif
         
         #ifdef UART
            printf("minus");
         #endif
         break;
      case 1:
         // forward
         #ifdef signal_output
            if (input(Output_invert)==0)
               {
                  output_high(encoder_UP);
               }
            else
               {
                  output_low(encoder_UP);
               }
         #endif
         #ifdef UART
            printf("plus");
         #endif
         break;
      case 2:
         //error state
         OLD_states = NEW_states;
         #ifdef UART
            printf("error");
         #endif
         break;
      }
}

/*#INT_RTCC
void  RTCC_isr(void)
{

}*/

/*#INT_RA
void  RA_isr(void)
{
   debounce_inputs();
   OLD_states = NEW_states;
   NEW_states = get_signals_state();
}*/

void main()
{

   clear_output_pins();

   delay_ms(500);    //time to stabilize all  voltages
   int QEM [16] = {0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0};           // Quadrature Encoder Matrix

   debounce_inputs();
   OLD_states = NEW_states = get_signals_state();
   int Out;
   
   //enable_interrupts(INT_RA);
   //enable_interrupts(INT_RTCC);
   //enable_interrupts(GLOBAL);
   while(TRUE)
   {
   // input signals debounce
      debounce_inputs();
   // input signals debounce end   
   
   // rotary encoder Grey code decoding
      OLD_states = NEW_states;
      NEW_states = get_signals_state();
      Out = QEM [OLD_states * 4 + NEW_states];     // Out = -1 move backward, Out = +1 move forward Out= 0 same possition Out = 2 error eg. input noise
   // rotary encoder Grey code decoding END

   // output set
      set_output(Out);
      #ifdef debug_slower
         delay_ms(1500);
      #endif
      clear_output_pins();
   // output set end
   }

}


The code is without interrupts because the chip does this work only, but maybe someone will improve it and make interrupts work.
silelis



Joined: 12 Jun 2007
Posts: 68
Location: Poland, podlaskie district

View user's profile Send private message

PostPosted: Fri Apr 28, 2017 5:51 am     Reply with quote

Hello,

It is updated ELM402 emulator. Fixed some issues.

Code:

/**************************************************************************/
/*!
    @file     ELM402_emu.c
    @author   Dawid "SileliS" Bankowski (d.bankowski@gmail.com)
   
    @brief    ELM 402 software emulator.
    @section LICENSE
    Software License Agreement (BSD License)
    Copyright (c) 2015, D. Bankowski
    All rights reserved.
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
    3. Neither the name of the copyright holders nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/


//#include <18F4520.h>
//#include <12F629.h>
#include <12F683.h>
#define device getenv("DEVICE")

#define _use_UP_DOWN_      //if defined output is Voltage level (UP/DOWN) or ifndef output is UART (UART is for debug only)

#define ELM402_debounce_period   10    //10 times
#define ELM402_debounce_delay    50    //uS 50
#define ELM_startup_time_delay   50    //mS
#define ELM402_pulse_time        200   //uS
#define ELM402_pulse_width_additional_time  1800  //uS


#if device=="PIC18F4520"
   #warning device
   #warning OSCCAL exists getenv("SFR_VALID:OSCCAL")
   #if getenv("SFR_VALID:OSCCAL")!=0
      #warning at getenv("SFR:OSCCAL")
   #endif
 
 
    #warning   RCON getenv("SFR:RCON")
    #byte      RCON = getenv("SFR:RCON")
    #warning   BOR getenv("SFR:RCON").0
    #bit       BOR = RCON.0
   
   
   //#device ADC=10
   #use delay(internal=4000000)
   #FUSES nomclr, BROWNOUT

   #define rotoencoder_port_interrupt INT_RB
   
   /*ELM402 emulation definitions*/
   //input definition
   #define signal_B           PIN_B4
   #define signal_A           PIN_B5
   
   #define pulseWidth         PIN_B1
   #define outputInvert       PIN_B2
   
   //output definition
   #define output_UP           PIN_C6
   #define output_DOWN         PIN_C7
   
   #ifndef _use_UP_DOWN_
      #WARNING "TTL OUTPUT"
      #use rs232(baud=9600,parity=N,xmit=output_UP,rcv=output_DOWN,bits=8)
   #endif
   /*ELM402 emulation definitions*/
     
#endif

#if device=="PIC12F629"
   #warning device
   #warning OSCCAL exists getenv("SFR_VALID:OSCCAL")
   #if getenv("SFR_VALID:OSCCAL")!=0
      #warning at getenv("SFR:OSCCAL")
   #endif
   
    #warning   PCON getenv("SFR:PCON")
    #byte      PCON = getenv("SFR:PCON")
    #warning   BOD getenv("SFR:PCON").0
    #bit       BOD = PCON.0
 
   #use delay(internal=4000000)
   #FUSES nomclr, BROWNOUT

   #define rotoencoder_port_interrupt INT_RA
   
   
   /*ELM402 emulation definitions*/
   //input definition
   #define signal_B           PIN_A4
   #define signal_A           PIN_A5
   
   #define pulseWidth         PIN_A3
   #define outputInvert       PIN_A2
   
   //output definition
   #define output_UP           PIN_A0
   #define output_DOWN         PIN_A1
   
   #ifndef _use_UP_DOWN_
      #WARNING "TTL OUTPUT"
      #use rs232(baud=9600,parity=N,xmit=output_UP,rcv=output_DOWN,bits=8) //,stream=PORT1)
   #endif
   /*ELM402 emulation definitions*/
#endif

#if device=="PIC12F683"
   #warning device
   #warning OSCCAL exists getenv("SFR_VALID:OSCCAL") //at getenv("SFR:OSCCAL")
   #if getenv("SFR_VALID:OSCCAL")!=0
      #warning at getenv("SFR:OSCCAL")
   #endif
 
 
    #warning   PCON getenv("SFR:PCON")
    #byte      PCON = getenv("SFR:PCON")
    #warning   BOR getenv("SFR:PCON").0
    #bit       BOR = PCON.0
   
   
   //#device ADC=10
   #use delay(internal=4000000)
   #FUSES nomclr, BROWNOUT

   #define rotoencoder_port_interrupt INT_RA
   
   /*ELM402 emulation definitions*/
   //input definition
   #define signal_B           PIN_A4
   #define signal_A           PIN_A5
   
   #define pulseWidth         PIN_A3
   #define outputInvert       PIN_A2
   
   //output definition
   #define output_UP           PIN_A0
   #define output_DOWN         PIN_A1
   
   #ifndef _use_UP_DOWN_
      #WARNING "TTL OUTPUT"   
      #use rs232(baud=9600,parity=N,xmit=output_UP,rcv=output_DOWN,bits=8) //,stream=PORT1)
   #endif
   /*ELM402 emulation definitions*/
#endif


int OLD_states;
int NEW_states;
const int QEM [16] = {0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0};
signed int Out;
int pulses;
void debounce_inputs(int16 time2debounce);      //ELM402 debounce emulation - waits till there is no voltage oscillation on signal_A and signal_B
int get_signals_state(void);     //returns rotor encoder state - inputs are signal_A and signal_B


/* Rotar encoder onPort change interrupt */
#if device=="PIC18F4520"
   #INT_RB
#endif
#if device=="PIC12F629"
   #INT_RA
#endif
#if device=="PIC12F683"
   #INT_RA
#endif
void  onPORT_changes_isr(void)
{
   //disable_interrupts(rotoencoder_port_interrupt);
   //OLD_states = NEW_states;
   debounce_inputs(ELM402_debounce_period);
   //pulses=pulses+1;
   NEW_states = input_state(signal_A) * 2 + input_state (signal_B);
   //printf("%d %d \n\r", input_state(Pin_B5), input_state(Pin_B4));
   //if (QEM [OLD_states * 4 + NEW_states]!=2)
   Out = Out+ QEM [OLD_states * 4 + NEW_states];
   //printf("\033[H\033[J");
   #if device=="PIC12F629"
      if (BOD==0)
      {
         reset_cpu();
      }
   #endif
   pulses=pulses+1;
   if (pulses==4)
   { 
      if (Out>3)     //Rotor encoder increment condition
      {
         #ifndef _use_UP_DOWN_
            if (input_state(outputInvert))
            {
               printf("minus ");
            }
            else
            {
               printf("plus ");
            }
         #endif
         
         #ifdef _use_UP_DOWN_
            output_bit( output_UP, 1-input_state(outputInvert));
         #endif
         
      }
      else if (Out<-3)     //Rotor encoder decrement condition
      {
         #ifndef _use_UP_DOWN_
            if (input_state(outputInvert))
            {
               printf("plus ");
            }
            else
            {
               printf("minus ");
            }
         #endif
         
         #ifdef _use_UP_DOWN_
               output_bit( output_DOWN, 1-input_state(outputInvert));
         #endif
         
         
      }
      else //if ((Out<3)&(Out>-3)) //Rotor encoder error condition (i.e. rotor contact are corrupted)
      {
         #ifndef _use_UP_DOWN_
            printf("error ");
         #endif
         
         #ifdef _use_UP_DOWN_
            #asm
              // nop;
            #endasm
         #endif
      }
     
   Out=0;
   pulses=0;
   delay_us(ELM402_pulse_time+input_state(pulseWidth)*ELM402_pulse_width_additional_time);   
   //#todo: clear outputs
   #ifndef _use_UP_DOWN_
    //  printf("\033[H\033[J"); // terminal clear
   #endif
   
   #ifdef _use_UP_DOWN_
      output_bit( output_UP, 0+input_state(outputInvert));
      output_bit( output_DOWN, 0+input_state(outputInvert));
   #endif
   }
   OLD_states = NEW_states;
   enable_interrupts(rotoencoder_port_interrupt);
}
/* Rotar encoder onPort change interrupt */
 
#zero_ram 
void main()
{
   delay_ms(500);
   enable_interrupts(rotoencoder_port_interrupt);
   enable_interrupts(GLOBAL);
   OLD_states = NEW_states = input_state(signal_A) * 2 + input_state (signal_B);
   pulses=0;
   Out=0;
   delay_ms(ELM_startup_time_delay);
   
   /*------ Brownout reset ------*/
   /* for PIC18f4520 or PIC12f683*/
   /*#if device!="PIC12F629"
      brownout_enable (TRUE);
      BOR=1;
   #endif*/
   /* for PIC18f4520 or PIC12f683*/
   /* for PIC12f629              */
   /*#if device=="PIC12F629"
      BOD =1;
   #endif*/
   /* for PIC12f629             
   /*------ Brownout reset ------*/
   while(TRUE)
   {
   /*#if device!="PIC12F629"
      if (BOD==0)
         printf("BOR");
   #endif*/
 
  //printf("\033[H\033[J");
  //delay_ms(2500);
   }

}

void debounce_inputs(int16 time2debounce)
{

   unsigned int16 count_low_signal_A=0;
   unsigned int16 count_high_signal_A=0;
   unsigned int16 count_low_signal_B=0;
   unsigned int16 count_high_signal_B=0;
   do
   {
      delay_us(ELM402_debounce_delay);
      if (input(signal_A) == 0)
      {
         count_low_signal_A++;
         count_high_signal_A = 0;
      }   
      else
      {
         count_low_signal_A = 0;
         count_high_signal_A++;
      }   
      if (input(signal_B) == 0)
      {
         count_low_signal_B++;
         count_high_signal_B = 0;
      }   
      else
      {
         count_low_signal_B = 0;
         count_high_signal_B++;
      }
   }
   while((count_low_signal_A < time2debounce) && (count_high_signal_A < time2debounce) || (count_low_signal_B< time2debounce) && (count_high_signal_B < time2debounce));
}
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
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