|
|
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 ([email protected])
@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
|