Go Down

Topic: Using the Hardware Quadrature Encoder Channel on the DUE (Read 18380 times) previous topic - next topic

garthn

Pin connections need to be adjusted according to those used in the sketch (or whatever pins are used)

exedor

#31
Jun 11, 2013, 02:38 am Last Edit: Jun 11, 2013, 07:32 pm by exedor Reason: 1
Quote

I would like to start working with the quadrature encoder hardware in the DUE. It appears you can use the pins TIOA0, TIOB0 and TIOA1 to decode 2 channel quadrature plus an index. (SAM3X manual p. 855) If my reading of the pinout is correct, this would be pins 2, 13 and AD7.


As a point of clarification I would restate that it would be pins 2, 13, and A7 since technically AD7 as can be found on the wonderful pin-out diagram that Gray Nomad put together and the SAM3X8e datasheet that the SAM3X8e pin AD7 is actually on A0 which is how I originally interpreted the post that started this thread :(

I've checked and the original code for using the bit masks to select peripherals and such is not working.  You can verify that by looking at the status registers after executing the commands.  Additionally the quad decoding still works after commenting out all the mask _PDR AND _ABSR variables and assignment statements.  I'm even wondering if turning clocks on and off is working.  I think the Arduino init code/IDE is probably turning on write protect registers after initializing and before calling our setup function.


Designservicecorp

#32
Jun 17, 2013, 07:02 pm Last Edit: Jun 18, 2013, 10:35 pm by Designservicecorp Reason: 1
For those that are still trying to get the encoder Index to work, here is what I did:

I started with the code from Schwinghopf in Reply#6 and as Exedor suggested in Reply#31, I removed the peripheral function setup and found it still worked. After reading through the SAM3x document many times I eventually discovered that the line: REG_PMC_PCER0 = (1<<27); was not turning on all of TC0 but rather only turning on channel0 of TC0. In order to start counting indexes, channel1 of TC0 needs to be turned on also.

Another issue was that I had to connect the Index wire to A6 (not A7 as the documentation shows). Not sure why this is, but since I wasn't using A6 for anything else I didn't look into it.

In the code below, I enabled interrupts for the Index (Z). Every index, REG_TC0_CV1 will reset back to zero. If you want it to continue counting, remove the interrupt (last 4 lines of setup() ).

Hope this helps.

Code: [Select]


volatile int z_Total=0;

void setup() {

   // Setup Quadrature Encoder with Marker

 REG_PMC_PCER0 = (1<<27); // activate clock for TC0

 REG_PMC_PCER0 = (1<<28); // activate clock for TC1

 // select XC0 as clock source and set capture mode

 REG_TC0_CMR0 = 5;

  // activate quadrature encoder and position measure mode, no filters

 REG_TC0_BMR = (1<<8)|(1<<9)|(1<<12);

 // enable the clock (CLKEN=1) and reset the counter (SWTRG=1)

 REG_TC0_CCR0 = 5;  

 REG_TC0_CCR1 = 5;

 //Remark out the next 4 lines to remove index interrupt and
  // accumulate index count
  REG_TC0_CMR1 = (1<<8); // Set rising edge of Z
 REG_TC0_IER1=0b10000000; // enable interrupt on Z
 REG_TC0_IDR1=0b01111111; // disable other interrupts
 NVIC_EnableIRQ(TC1_IRQn);

}

void loop() {
//REG_TC0_CV0 Stores count from encoder
//REG_TC0_CV1 Stores count from index if interrupts are off

}

void TC1_Handler() {

 z_Total++;
 long dummy=REG_TC0_SR1; // vital - reading this clears some flag
                           // otherwise you get infinite interrupts
}


exedor

#33
Jun 18, 2013, 09:44 am Last Edit: Jun 18, 2013, 06:18 pm by exedor Reason: 1
I'm not able to read the value on REG_TC0_CV1 even when interrupts are off.  How did you turn interrupts off?  I'm wondering if rather than actually using the index line, it is instead recognizing an "external" trigger which happens to be coming through A6 rather than the actual index line.  From the data sheet it sounds like the TC0_CV0 register is supposed to reset to 0 each time an index pulse comes through.




Designservicecorp


I updated my earlier post to include remarking out one more line (REG_TC0_CMR1 =) in order to allow the accumulation of index pulses.

I'll also go back and rearrange the code to make it easier to see the interrupt lines.

schwingkopf

with the updated version of your code I still dont see the index pulses being counted on REG_TC0_CV1.

just to clearify the functionality of the hardware quadrature decoder logic:
every index pulse should increase or decrease the value in the REG_TC0_CV1 register by one and at the same time restart or zero the position value in the REG_TC0_CV0 register. Please dont state you have solved the problem until you really see this behaviour!

As far as I understand your code, it just counts manually the index pulses using the interrupt handler, which is definitely not the same as the automatic counting using the hardware decoder logic.

Concerning the fact that the index pin might be on A6 and not on A7: In the figure 37-15 in the datasheet the index pin is drawn to be connected to TIOB1 (and not to TIOA1 as stated thoughout the rest of the datasheet) which corresponds to A6.

Designservicecorp

#36
Jun 19, 2013, 12:45 am Last Edit: Jun 19, 2013, 12:46 am by Designservicecorp Reason: 1
Did you remark out the 4 lines to stop the interrupt on the index? I copied the code to my DUE after editing my post and tried it both ways(with/without interrupts) and it functions. With interrupts enabled CV1 stays at zero but the interrupt handler occurs each index. With the interrupts remarked out CV1 counts up each index.

I'm running an encoder emulator box that only drives one direction, so I can't attest to lowering REG_TC0_CV1 only that mine adds each index. I'll have to swap my A & B wires and see what happens.

As for zeroing REG_TC0_CV0 on each index, I understood that to be a setting that needed to be enabled. My project didn't require it, so I didn't look into setting it up.

Thanks for spotting the A6 figure. This is not the worst document I have had the privilege of deciphering, but it's real close.

BTW, I never stated I solved the problem. Only sharing what I did to get the index to accumulate.

Designservicecorp


You got me curious on the direction issue.

I reloaded the code after remarking out the index interrupt and added a print statement for CV1 and started it.

I let the value increase to about 80 rotations and then pulled and swapped the A & B wires. The index count did start to decrease so direction does work. The number after passing zero became very large, however.

exedor

#38
Jun 19, 2013, 01:16 am Last Edit: Jun 19, 2013, 01:33 am by exedor Reason: 1
Thanks for that.  It still isn't doing what the data sheet says it will do.  According to the datasheet, when an index pulse comes along, it will supposedly reset the value in the TC0_CV0 register and start the count over at 0.  Um, nope.  That ain't happening!  Either data sheet lies or we have peripheral usage/configuration issues.  The fact that pulses are coming in A6 tells me something is amiss....nevertheless, I'm grateful to have at least something.   As I understand it, the index pulses are designed to help keep your stuff in sync.  I have seen for extremely fast pulse rates, pulses do get dropped and I've seen a tiny amount of drift.  I'm working with an extremely high precision application and that is a problem so I was really hoping to get the hardware quadrature decoder working as documented.  It's almost like the clock is actually using TIOB1 and not TIOA1 like the data sheet says or maybe for that port, the other peripheral is selected??

Also, for anyone looking to obtain the direction, you can do this:

Code: [Select]
dir = (REG_TC0_QISR >> 8) & 0x1

Assuming you have of course declared "dir"
I have it declared as a regular int and it is 0 when clockwise and 1 when counter-clockwise assuming you have ChA connected to pin 2 and ChB connected to pin 13.


Designservicecorp


For those that want the index to trigger a reset of the A/B count register this works using the code from my earlier post (Reply #32):

Replace the line: REG_TC0_CMR0 = 5;

With this:

Code: [Select]
REG_TC0_CMR0 = (1<<0)|(1<<2)|(1<<8)|(1<<10);

This works with interrupts enabled on the index or not.


exedor

#41
Jun 20, 2013, 04:43 am Last Edit: Jun 20, 2013, 04:48 am by exedor Reason: 1
I bow to the master an with humble respect!  You da man!

Seriously though, thank you.  I will have to try playing around with the edge triggering because in my test, for very slow rotations of the motor, the index pulse fails to increment the index counter and thereby creates some seriously erroneous readings.



Designservicecorp

#42
Jun 26, 2013, 12:25 am Last Edit: Jun 26, 2013, 12:29 am by Designservicecorp Reason: 1
I have been attempting to use Channel2 as a clock to determine speed and I believe I finally got it.

The code below is from the initial program I posted (Reply #32) but with Channel2 enabled and set to TCLK4. I also added more remarks and cleaned it up a bit.

Index interrupts must be enabled for the speed portion to work.

Code: [Select]

/*
 Quadrature Encoder with Marker via hardware
*/

volatile int z_Total=0;
volatile double TPR=0;

void setup() {

 Serial.begin(115200);  
 delay(100);

 // Setup Quadrature Encoder

 REG_PMC_PCER0 = (1<<27)|(1<<28)|(1<<29); // activate clock for TC0,TC1,TC2

 // select XC0 as clock source and set capture mode
 REG_TC0_CMR0 = 5;
     //Resets the count every index. Rem out the line above,
       //and use the line below instead.
     //REG_TC0_CMR0 = (1<<0)|(1<<2)|(1<<8)|(1<<10);

 REG_TC0_CMR2 = (1<<0)|(1<<1); // Set to TCLK4

 // activate quadrature encoder and position measure mode, no filters
 REG_TC0_BMR = (1<<8)|(1<<9)|(1<<12);

 // Enable the clock (CLKEN=1) and reset the counter (SWTRG=1)
 REG_TC0_CCR0 = 5;  
 REG_TC0_CCR1 = 5;
 REG_TC0_CCR2 = 5;

 //Remark out these 4 lines to disable Index interrupts
 REG_TC0_CMR1 = (1<<8); // Set rising edge of Z
 REG_TC0_IER1=0b10000000; // enable interrupt on Z
 REG_TC0_IDR1=0b01111111; // disable other interrupts
 NVIC_EnableIRQ(TC1_IRQn);

}

void loop() {

 //REG_TC0_CV0 Stores count from encoder
 Serial.println(REG_TC0_CV0);

 //REG_TC0_CV1 Stores count from index if interrupts are off
   //if interrupts are on, CV1 will always reset every cycle to zero.
 if (REG_TC0_CV1 == 0)
 {
   Serial.print(z_Total);
   Serial.println("         -With Int");
 }
 else
 {
   Serial.print(REG_TC0_CV1);
   Serial.println("         -No Int");
 }
 //TPR holds the quantity of ticks every Index. Index interrupts must be on.
 //TCLK4 is set so 128 is the divisor.
 Serial.print((F_CPU/128/TPR)*60);Serial.println(" RPM");
 Serial.println("-------");

 delay(500);

}



void TC1_Handler() {
 //This won't fire unless the Index interrupt is enabled above
 z_Total++;

 long dummy=REG_TC0_SR1; // vital - reading this clears some flag
                           // otherwise you get infinite interrupts

 TPR=REG_TC0_CV2; //Store ticks
 REG_TC0_CCR2 = 4; //Reset counter
}

jchalo99

OK sorry to bring up a dieing thread but...

I am about to wire up my arduino DUE to my table and i want to make sure i am getting this right. So from the Encoder i have 3.3v in (where 5v should be) and gnd connected. i take channel A+, B+, and Z+ and connect them directly to 2,13,A6 respectively. (my encoder has A-,B-, Z- aswell).

than i can upload the below code straight from arduino 1.5._?
Code: [Select]
/*
  Quadrature Encoder with Marker via hardware
*/

volatile int z_Total=0;
volatile double TPR=0;

void setup() {

  Serial.begin(115200); 
  delay(100);

  // Setup Quadrature Encoder

  REG_PMC_PCER0 = (1<<27)|(1<<28)|(1<<29); // activate clock for TC0,TC1,TC2

  // select XC0 as clock source and set capture mode
  REG_TC0_CMR0 = 5;
      //Resets the count every index. Rem out the line above,
        //and use the line below instead.
      //REG_TC0_CMR0 = (1<<0)|(1<<2)|(1<<8)|(1<<10);

  REG_TC0_CMR2 = (1<<0)|(1<<1); // Set to TCLK4

  // activate quadrature encoder and position measure mode, no filters
  REG_TC0_BMR = (1<<8)|(1<<9)|(1<<12);

  // Enable the clock (CLKEN=1) and reset the counter (SWTRG=1)
  REG_TC0_CCR0 = 5; 
  REG_TC0_CCR1 = 5;
  REG_TC0_CCR2 = 5;

  //Remark out these 4 lines to disable Index interrupts
  REG_TC0_CMR1 = (1<<8); // Set rising edge of Z
  REG_TC0_IER1=0b10000000; // enable interrupt on Z
  REG_TC0_IDR1=0b01111111; // disable other interrupts
  NVIC_EnableIRQ(TC1_IRQn);

}

void loop() {

  //REG_TC0_CV0 Stores count from encoder
  Serial.println(REG_TC0_CV0);

  //REG_TC0_CV1 Stores count from index if interrupts are off
    //if interrupts are on, CV1 will always reset every cycle to zero.
  if (REG_TC0_CV1 == 0)
  {
    Serial.print(z_Total);
    Serial.println("         -With Int");
  }
  else
  {
    Serial.print(REG_TC0_CV1);
    Serial.println("         -No Int");
  }
  //TPR holds the quantity of ticks every Index. Index interrupts must be on.
  //TCLK4 is set so 128 is the divisor.
  Serial.print((F_CPU/128/TPR)*60);Serial.println(" RPM");
  Serial.println("-------");

  delay(500);

}



void TC1_Handler() {
  //This won't fire unless the Index interrupt is enabled above
  z_Total++;

  long dummy=REG_TC0_SR1; // vital - reading this clears some flag
                            // otherwise you get infinite interrupts

  TPR=REG_TC0_CV2; //Store ticks
  REG_TC0_CCR2 = 4; //Reset counter
}

Designservicecorp


Code-wise you're correct with the pin numbers. Not sure about voltage because you didn't mention what encoder you're using.

I used an encoder with A+, A-, B+, B-, Z+, Z- and ran it into a Maxim MAX3096 422 receiver. This allowed me to run the encoder with 24vdc and interface it to the DUE 3.3v. It also gives great noise immunity.

Go Up