It is currently 23 Jul 2019 03:43

All times are UTC + 1 hour




Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: 05 Sep 2009 22:46 
Offline

Joined: 29 Sep 2007 14:35
Posts: 172
Location: France
Hello,

I just started a new project based on PIC 16F628A running at 8 MHz, and two optical rotary encoders (ENC1 and ENC2). Encoders are A/B quadrature outputs, one is connected to PORTB[4..5], other is connected to PORTB[6..7], edge change detection is made with interrupts.

This project allow PWM duty setting with ENC1 or/and ENC2, ENC1 is handled by a first user and ENC2 is handled by a second user. During test / debbuging, only one encoder is used. The principle is simple, PWM duty is incremented when user(s) turn its encoder in clockwise direction, and PWM duty is decremented when user(s) turn its encoder in anti-clockwise direction. For this, I used code provided on this forum, thanks to all that have participated to it.

Simulation has been made under Proteus. After correct results in software part, I went to the hardware side. But that doesn't work properly...

Code is following :
(this is not the full code - full code allow choose PWM or analog voltage throw R/2R ladder, I simplified it to make it more easy to read)

program electronique_alim_ajust_014c_16f628_Test;

var
  Old1, Old2, New1, New2: byte;
  i, iValue: byte;
  bValueChanged: boolean;
  Tmp1, Tmp2, bUp: boolean;

procedure Main_Init;
begin
  CMCON := 7;
  TRISA := %00110000;
  TRISB := %11110000;
  iValue := 0;
  Old1 := (PORTB and %11000000);
  Old2 := (PORTB and %00110000);
  INTCON := %10001000;
  bValueChanged := false;
  //CCP1CON := CCP1CON and %00001111;
  PWM1_Init(1000);
  PWM1_Set_Duty(0);
  PWM1_Start;
end;

procedure Interrupt;
begin
  if TestBit(INTCON, RBIF) = 1 then
  begin

    // for debug purposes only
    PORTA.0 := PORTB.4;
    PORTA.1 := PORTB.5;
    PORTA.2 := PORTB.6;
    PORTA.3 := PORTB.7;

    // first optical encoder
    New1 := (PORTB and %11000000);
    if New1 <> Old1 then
    begin
      Tmp1 := New1.7;
      Tmp2 := Old1.6;
      bUp := (Tmp1 xor Tmp2);
      // if (New1.7 xor Old1.6) then // work in MP V8, don't work in MP pro V2.50
      if bUp then
      begin
        PORTB.0 := 1;
        if iValue < 255 then
        begin
          inc(iValue);
          bValueChanged := true;
        end;
      end
      else
      begin
        PORTB.0 := 0;
        if iValue > 0 then
        begin
          dec(ivalue);
          bValueChanged := true;
        end;
      end;
      Old1 := New1;
    end;

    // second optical encoder
    New2 := (PORTB and %00110000);
    if New2 <> Old2 then
    begin
      Tmp1 := New2.5;
      Tmp2 := Old2.4;
      bUp := (Tmp1 xor Tmp2);
      // if (New2.5 xor Old2.4) then // work in MP V8, don't work in MP pro V2.50
      if bUp then
      begin
        PORTB.0 := 1;
        if iValue < 255 then
        begin
          inc(iValue);
          bValueChanged := true;
        end;
      end
      else
      begin
        PORTB.0 := 0;
        if iValue > 0 then
        begin
          dec(iValue);
          bValueChanged := true;
        end;
      end;
      Old2 := New2;
    end;

    ClearBit(INTCON, RBIF);
  end;

end;

procedure PWM_Welcome;
begin
  INTCON := $00;
  for i := 1 to 31 do
  begin
    PORTA.0 := PORTA.0 xor 1;
    PORTA.1 := PORTA.1 xor 1;
    PWM1_Set_Duty(i * 8);
    Delay_ms(100);
  end;
  Delay_ms(1000);
  PWM1_Set_Duty(0);
  Delay_ms(100);
  PORTA.0 := PORTB.4;
  PORTA.1 := PORTB.5;
  INTCON := %10001000;
end;

begin
  Main_Init;
  PWM_Welcome;
  while true do
  begin
    //Delay_ms(10);
    if bValueChanged then
    begin
      bValueChanged := false;
      PWM1_Set_Duty(iValue);
    end
    else
      nop;
  end;
end.


What I already made :
- I made tests with two differents encoders, one "slow" (Grayhill) and other "fast" (Copal).
- I included little piece of code at the beginning of the interrupt routine to show on PORTA[0..3] the state of PORTB[4..7] to replicate encoders outputs state and show them on leds.
- PORTB.0 is set or cleared according encoder rotation direction.
- I captured with digital scope encoders outputs state during rotating, signal is "clean" and seems to be not perturbed by noise (asymetrical outputs but very short connections).

What I see is :
- the leds connected to PORTA[0..3] show the real state of the encoders outputs, all seems OK at this point.
- the led connected to PORTB.0 don't reflect the rotation direction, probleme seems to be there. Stay off when rotate anti-clockwise, but don't light on all the time when rotate clockwise.
- PWM init seems OK as PWM_Welcome allow delivering PWM output with incremental duty on PORTB.3.
- if I let interrupts activated before PWM_Welcome, leds connected on RA0 and RA1 don't blink as they should be in the "for" loop.

Can someone tell me if my code is correct ?
A big thanks in advance !

Remy


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 11:16 
Offline

Joined: 08 Jun 2009 18:31
Posts: 375
Location: Romania
Hi, the problem with PortB.0 seems to be from the setting/clearing it from both encoders:
// first optical encoder
    New1 := (PORTB and %11000000);
    if New1 <> Old1 then
    begin
      Tmp1 := New1.7;
      Tmp2 := Old1.6;
      bUp := (Tmp1 xor Tmp2);
      // if (New1.7 xor Old1.6) then // work in MP V8, don't work in MP pro V2.50
      if bUp then
      begin
        PORTB.0 := 1;
        if iValue < 255 then
        begin
          inc(iValue);
          bValueChanged := true;
        end;
      end
      else
      begin
        PORTB.0 := 0; 
and
// second optical encoder
    New2 := (PORTB and %00110000);
    if New2 <> Old2 then
    begin
      Tmp1 := New2.5;
      Tmp2 := Old2.4;
      bUp := (Tmp1 xor Tmp2);
      // if (New2.5 xor Old2.4) then // work in MP V8, don't work in MP pro V2.50
      if bUp then
      begin
        PORTB.0 := 1;
        if iValue < 255 then
        begin
          inc(iValue);
          bValueChanged := true;
        end;
      end
      else
      begin
        PORTB.0 := 0;
and you use ivalue as a counter for both encoders. Hope this helps. :D


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 12:35 
Offline

Joined: 22 Apr 2005 17:40
Posts: 1985
Location: France 87
New1.7 xor Old1.6 will resolve as 1 or 0, if will decide on true and false , if you change to If( New1.7 xor Old1.6)=1 then it should work.
I have seen that some simple encoders produce rather assymmetric pulses, you best keep the interrupt as simpel and fast as possible, i found this old little unit that might be of help for you, changing it to MP Pro should not be a problem
Unit dual_encoder;
{ this unit provides 2 quadrature-encoder channels on PORTB bit 6/7 , 4/5

  }

var x_position,
    y_position : integer;
    motion     : boolean;

implementation

var oldx : byte;
    newx : byte;
    oldy : byte;
    newy : byte;


procedure dual_encoder_interrupt;
begin
  if TestBit(INTCON,RBIF) = 1 then
  begin
    newx := (PORTB and %11000000);
    if newx <> oldx then
    begin
      if (newx.7 xor oldx.6)=1 then inc(x_position)
      else dec(x_position);
      oldx := newx;
    end;
    newy := (PORTB and %00110000);
    if newy <> oldy then
    begin
      if (newy.5 xor oldy.4)=1 then inc(y_position)
      else dec(y_position);
      oldy := newy;
    end;
    motion := true;
    Clearbit(Intcon,rbif);
  end;
end;


procedure init_encoder;
begin
  // configure PORTB
  //TRISB := TRISB OR %11000000;   // single channel
  TRISB := TRISB OR %11110000;  // dual channel
  // initialize variable's
  x_position := 0;
  y_position := 0;
  motion := false;
  oldx := (PORTB and %11000000) ;
  oldy := (PORTB and %00110000);
  INTCON := %10001000;   // enabele GIE and RBIE
end;

end.

_________________
Au royaume des aveugles, les borgnes sont rois.


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 12:41 
Offline

Joined: 08 Jun 2009 18:31
Posts: 375
Location: Romania
Hi, here are the changes which I was talking about:
program electronique_alim_ajust_014c_16f628_Test;

var
  Old1, Old2, New1, New2: byte;
  i, iValue1, iValue2: byte; // added iValue1, iValue2
  bValueChanged1, bValueChanged2: boolean; //added  bValueChanged1, bValueChanged2
  Tmp1, Tmp2, bUp: boolean;

procedure Main_Init;
begin
  CMCON := 7;
  TRISA := %00110000;
  TRISB := %11110000;
  iValue := 0;
  Old1 := (PORTB and %11000000);
  Old2 := (PORTB and %00110000);
  INTCON := %10001000;
  bValueChanged := false;
  //CCP1CON := CCP1CON and %00001111;
  PWM1_Init(1000);
  PWM1_Set_Duty(0);
  PWM1_Start;
end;

procedure Interrupt;
begin
  if TestBit(INTCON, RBIF) = 1 then
  begin

    // for debug purposes only
    PORTA.0 := PORTB.4;
    PORTA.1 := PORTB.5;
    PORTA.2 := PORTB.6;
    PORTA.3 := PORTB.7;

    // first optical encoder
    New1 := (PORTB and %11000000);
    if New1 <> Old1 then
    begin
      Tmp1 := New1.7;
      Tmp2 := Old1.6;
      bUp := (Tmp1 xor Tmp2);
      // if (New1.7 xor Old1.6) then // work in MP V8, don't work in MP pro V2.50
      if bUp then
      begin
        PORTB.0 := 1;
        if iValue1 < 255 then //changed to iValue1
        begin
          inc(iValue1);  //changed to iValue1
          bValueChanged1 := true; //changed to bValueChanged1
        end;
      end
      else
      begin
        PORTB.0 := 0;
        if iValue1 > 0 then  //changed to iValue1
        begin
          dec(ivalue1);  //changed to iValue1
          bValueChanged1 := true; //changed to bValueChanged1
        end;
      end;
      Old1 := New1;
    end;

    // second optical encoder
    New2 := (PORTB and %00110000);
    if New2 <> Old2 then
    begin
      Tmp1 := New2.5;
      Tmp2 := Old2.4;
      bUp := (Tmp1 xor Tmp2);
      // if (New2.5 xor Old2.4) then // work in MP V8, don't work in MP pro V2.50
      if bUp then
      begin
        PORTB.1 := 1;  //Display the second encoder result on a different pin
        if iValue2 < 255 then  //changed to iValue2
        begin
          inc(iValue2); //changed to iValue2
          bValueChanged2 := true; //changed to bValueChanged2
        end;
      end
      else
      begin
        PORTB.1 := 0;  //Display the second encoder result on a different pin
        if iValue2 > 0 then //changed to iValue2
        begin
          dec(iValue2); //changed to iValue2
          bValueChanged2 := true; //changed to bValueChanged2
        end;
      end;
      Old2 := New2;
    end;

    ClearBit(INTCON, RBIF);
  end;

end;

procedure PWM_Welcome;
begin
  INTCON := $00;
  for i := 1 to 31 do
  begin
    PORTA.0 := PORTA.0 xor 1;
    PORTA.1 := PORTA.1 xor 1;
    PWM1_Set_Duty(i * 8);
    Delay_ms(100);
  end;
  Delay_ms(1000);
  PWM1_Set_Duty(0);
  Delay_ms(100);
  PORTA.0 := PORTB.4;
  PORTA.1 := PORTB.5;
  INTCON := %10001000;
end;

begin
  Main_Init;
  PWM_Welcome;
  while true do
  begin
    //Delay_ms(10);
   
    //Change the same PWM1_Set_Duty from TWO different sources
    if bValueChanged1 then  //changed to bValueChanged1
    begin
      bValueChanged1 := false; //changed to bValueChanged1
      PWM1_Set_Duty(iValue1); //changed to iValue1
    end
    else
      nop;
     
    if bValueChanged2 then  //changed to bValueChanged2
    begin
      bValueChanged2 := false; //changed to bValueChanged2
      PWM1_Set_Duty(iValue2); //changed to iValue2
    end
    else
      nop;
  end;
end.


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 12:58 
Offline

Joined: 29 Sep 2007 14:35
Posts: 172
Location: France
Thanks VCC and jpc for your help, really appreciated.

PortB.0 status (just implemented for visual verification) can be changed by both parts of the interrupt routine, that is what I want. Normally, I think that there is no interference during test as only one encoder is used at a time, and only RB4/RB5 or RB6/RB7 couple change and not the other. After preliminary tests, I'll remove PORTB.0 from this place.

iValue is a variable shared by both parts of the int routine, this is wanted, too. Two users can "play" with the system, each with its owm encoder, allowing the two users to do the same thing : inc or dec iValue, associated to analog voltage or PWM duty value. To be more clear, the system will be integrated in a Boeing 747 clone, and pilote and co-pilote should have access on a same command (160 leds panel light dimming). But I'll try do it from two variables, as suggested.

I'll test suggested code provided by you two, and report result here.

Thanks again !
Remy


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 13:04 
Offline

Joined: 29 Sep 2007 14:35
Posts: 172
Location: France
Hum...
I can't use two separated variable iValue1 and iValue2 as the last value modified by one of the two users should be modified by the other, to avoid large changes on light variation.


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 13:04 
Offline

Joined: 08 Jun 2009 18:31
Posts: 375
Location: Romania
chimimic wrote:
Thanks VCC and jpc for your help, really appreciated.

PortB.0 status (just implemented for visual verification) can be changed by both parts of the interrupt routine, that is what I want. Normally, I think that there is no interference during test as only one encoder is used at a time, and only RB4/RB5 or RB6/RB7 couple change and not the other. After preliminary tests, I'll remove PORTB.0 from this place.

iValue is a variable shared by both parts of the int routine, this is wanted, too. Two users can "play" with the system, each with its owm encoder, allowing the two users to do the same thing : inc or dec iValue, associated to analog voltage or PWM duty value. To be more clear, the system will be integrated in a Boeing 747 clone, and pilote and co-pilote should have access on a same command (160 leds panel light dimming). But I'll try do it from two variables, as suggested.

I'll test suggested code provided by you two, and report result here.

Thanks again !
Remy
Hi, the problem using only one variable is that you modify it from the "else" part of the code. :D


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 13:06 
Offline

Joined: 29 Sep 2007 14:35
Posts: 172
Location: France
OK, I see...
I'll try to find another solution for it.
Thanks ! :D


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 14:03 
Offline

Joined: 29 Sep 2007 14:35
Posts: 172
Location: France
Following modified code now works correctly and as I want for PWM part :

program electronique_alim_ajust_014c_16f628_Test;

// modified 06/09/2009

var
  Old1, Old2, New1, New2: byte;
  i, iValue: byte;
  bInc, bValueChanged: boolean;
  Tmp1, Tmp2, bUp: boolean;

procedure Main_Init;
begin
  CMCON := 7;
  TRISA := %00110000;
  TRISB := %11110000;
  iValue := 0;
  Old1 := (PORTB and %11000000);
  Old2 := (PORTB and %00110000);
  INTCON := %10001000;
  bValueChanged := false;
  //CCP1CON := CCP1CON and %00001111;
  PWM1_Init(1000);
  PWM1_Set_Duty(0);
  PWM1_Start;
end;

procedure Interrupt;
begin
  if TestBit(INTCON, RBIF) = 1 then
  begin

    // for debug purposes only
    {
    PORTA.0 := PORTB.4;
    PORTA.1 := PORTB.5;
    PORTA.2 := PORTB.6;
    PORTA.3 := PORTB.7;
    }
   
    // first optical encoder
    New1 := (PORTB and %11000000);
    if New1 <> Old1 then
    begin
      Tmp1 := New1.7;
      Tmp2 := Old1.6;
      bUp := (Tmp1 xor Tmp2);
      if bUp then
      //if (New1.7 xor Old1.6) = 1 then // work in MP V8, don't work in MP pro V2.50
        bInc := true
      else
        bInc := false;
      bValueChanged := true;
      Old1 := New1;
    end;

    // second optical encoder
    New2 := (PORTB and %00110000);
    if New2 <> Old2 then
    begin
      Tmp1 := New2.5;
      Tmp2 := Old2.4;
      bUp := (Tmp1 xor Tmp2);
      if bUp then
      //if (New2.5 xor Old2.4) = 1 then // work in MP V8, don't work in MP pro V2.50
        bInc := true
      else
        bInc := false;
      bValueChanged := true;
      Old2 := New2;
    end;

    ClearBit(INTCON, RBIF);
  end;

end;

procedure PWM_Welcome;
begin
  INTCON := $00;
  for i := 1 to 31 do
  begin
    PORTA.0 := PORTA.0 xor 1;
    PORTA.1 := PORTA.1 xor 1;
    PWM1_Set_Duty(i * 8);
    Delay_ms(100);
  end;
  Delay_ms(1000);
  PWM1_Set_Duty(0);
  Delay_ms(100);
  PORTA.0 := PORTB.4;
  PORTA.1 := PORTB.5;
  INTCON := %10001000;
end;

begin
  Main_Init;
  PWM_Welcome;
  while true do
  begin
    //Delay_ms(10);
    if bValueChanged then
    begin
      PORTB.0 := bInc;
      if bInc then
      begin
        if iValue < 255 then
          inc(iValue);
      end
      else
      begin
        if iValue > 0 then
          dec(iValue);
      end;
      bValueChanged := false;
      PWM1_Set_Duty(iValue);
    end
    else
      nop;
  end;
end.


Just moved "too long code" from interrupt routine to main proc.

The following line compile fine but don't work in MP pro V2.50 (works fine in V8) :

if (New1.7 xor Old1.6) = 1 then


and adding temp var allow "xor" code to work

Tmp1 := New1.7;
Tmp2 := Old1.6;
bUp := (Tmp1 xor Tmp2);
if bUp then


Thanks again for your help ;-)


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 14:10 
Offline

Joined: 08 Jun 2009 18:31
Posts: 375
Location: Romania
Hi, try to define bUp as bit, not Boolean. Hope this helps. :D


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 14:24 
Offline

Joined: 29 Sep 2007 14:35
Posts: 172
Location: France
Thanks for this new suggestion.
But actually work with bUp as boolean, it's when I don't use Tmp1, Tmp2 and bUp that doesn't work.


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 14:36 
Offline

Joined: 08 Jun 2009 18:31
Posts: 375
Location: Romania
chimimic wrote:
Thanks for this new suggestion.
But actually work with bUp as boolean, it's when I don't use Tmp1, Tmp2 and bUp that doesn't work.
Hi, I was thinking to get rid of Tmp1 and Tmp2 using
bUp := New1.7 xor Old1.6;
:)


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 15:05 
Offline

Joined: 29 Sep 2007 14:35
Posts: 172
Location: France
VCC wrote:
I was thinking to get rid of Tmp1 and Tmp2 using
bUp := New1.7 xor Old1.6;

Good idea, I didn't understand this, sorry :)


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 15:19 
Offline

Joined: 08 Jun 2009 18:31
Posts: 375
Location: Romania
chimimic wrote:
VCC wrote:
I was thinking to get rid of Tmp1 and Tmp2 using
bUp := New1.7 xor Old1.6;

Good idea, I didn't understand this, sorry :)
Hi, you didn't understand my explanation, because it was too short :oops:
VCC wrote:
Hi, try to define bUp as bit, not Boolean. Hope this helps. Very Happy


Top
 Profile  
 
 Post subject:
PostPosted: 06 Sep 2009 15:25 
Offline

Joined: 29 Sep 2007 14:35
Posts: 172
Location: France
Absolutly no problem !

I'll put on my web site detailled article of this project (with schematic and full source code), as I do for all my projects.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 1 guest


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 post attachments in this forum

Search for:
Jump to: