  | 
	  | 
		 
	 
	
		| View previous topic :: View next topic   | 
	 
	
	
		| Author | 
		Message | 
	 
	
		
			silelis
 
 
  Joined: 12 Jun 2007 Posts: 68 Location: Poland, podlaskie district 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				| ELM402 rotary decoder software emulator | 
			 
			
				 Posted: Mon Sep 26, 2016 1:06 pm     | 
				     | 
			 
			
				
  | 
			 
			
				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 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Fri Apr 28, 2017 5:51 am     | 
				     | 
			 
			
				
  | 
			 
			
				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));
 
}
 
 | 	 
  | 
			 
		  | 
	 
	
		  | 
	 
	
		 | 
	 
 
  
	 
	    
	   | 
	
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
  
		 |