Using the Hardware Quadrature Encoder Channel on the DUE

Hi,
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.

Could someone point me to any existing low level calls that might make this easier? I know, for instance, the Arduino code writes to the DAC using:
dacc_write_conversion_data(DACC_INTERFACE, 0x0000);

Is there a similar function for the decoder channels and where might I find these calls.
THANKS IN ADVANCE.

dreschel

Did you get the encoder to work ?
It appear that only 1 of the the 2 Quadrature channels pins have been connected to pins.

@fiddler:
All three QDEC pins are on a connector, the pinout is as dreschel wrote

@dreschel:

I came to the same point like you and nothing further :frowning:
This is a header file from Keil, it defines allot of registers and peripheral base adresses: http://www.keil.com/dd/docs/arm/atmel/sam3x/sam3x.h

This part could be interesting for you:

#define TC_BMR_QDEN (0x1u << 8) /**< \brief (TC_BMR) Quadrature Decoder ENabled */
#define TC_BMR_POSEN (0x1u << 9) /**< \brief (TC_BMR) POSition ENabled */
#define TC_BMR_SPEEDEN (0x1u << 10) /**< \brief (TC_BMR) SPEED ENabled */
#define TC_BMR_QDTRANS (0x1u << 11) /**< \brief (TC_BMR) Quadrature Decoding TRANSparent */
#define TC_BMR_EDGPHA (0x1u << 12) /**< \brief (TC_BMR) EDGe on PHA count mode */
#define TC_BMR_INVA (0x1u << 13) /**< \brief (TC_BMR) INVerted phA */
#define TC_BMR_INVB (0x1u << 14) /**< \brief (TC_BMR) INVerted phB */
#define TC_BMR_INVIDX (0x1u << 15) /**< \brief (TC_BMR) INVerted InDeX */
#define TC_BMR_SWAP (0x1u << 16) /**< \brief (TC_BMR) SWAP PHA and PHB */
#define TC_BMR_IDXPHB (0x1u << 17) /**< \brief (TC_BMR) InDeX pin is PHB pin */
#define TC_BMR_FILTER (0x1u << 19) /**< \brief (TC_BMR)  */
#define TC_BMR_MAXFILT_Pos 20
#define TC_BMR_MAXFILT_Msk (0x3fu << TC_BMR_MAXFILT_Pos) /**< \brief (TC_BMR) MAXimum FILTer */
#define TC_BMR_MAXFILT(value) ((TC_BMR_MAXFILT_Msk & ((value) << TC_BMR_MAXFILT_Pos)))
/* -------- TC_QIER : (TC Offset: 0xC8) QDEC Interrupt Enable Register -------- */
#define TC_QIER_IDX (0x1u << 0) /**< \brief (TC_QIER) InDeX */
#define TC_QIER_DIRCHG (0x1u << 1) /**< \brief (TC_QIER) DIRection CHanGe */
#define TC_QIER_QERR (0x1u << 2) /**< \brief (TC_QIER) Quadrature ERRor */
/* -------- TC_QIDR : (TC Offset: 0xCC) QDEC Interrupt Disable Register -------- */
#define TC_QIDR_IDX (0x1u << 0) /**< \brief (TC_QIDR) InDeX */
#define TC_QIDR_DIRCHG (0x1u << 1) /**< \brief (TC_QIDR) DIRection CHanGe */
#define TC_QIDR_QERR (0x1u << 2) /**< \brief (TC_QIDR) Quadrature ERRor */
/* -------- TC_QIMR : (TC Offset: 0xD0) QDEC Interrupt Mask Register -------- */
#define TC_QIMR_IDX (0x1u << 0) /**< \brief (TC_QIMR) InDeX */
#define TC_QIMR_DIRCHG (0x1u << 1) /**< \brief (TC_QIMR) DIRection CHanGe */
#define TC_QIMR_QERR (0x1u << 2) /**< \brief (TC_QIMR) Quadrature ERRor */
/* -------- TC_QISR : (TC Offset: 0xD4) QDEC Interrupt Status Register -------- */
#define TC_QISR_IDX (0x1u << 0) /**< \brief (TC_QISR) InDeX */
#define TC_QISR_DIRCHG (0x1u << 1) /**< \brief (TC_QISR) DIRection CHanGe */
#define TC_QISR_QERR (0x1u << 2) /**< \brief (TC_QISR) Quadrature ERRor */
#define TC_QISR_DIR (0x1u << 8) /**< \brief (TC_QISR) Direction */

it's from line 6484 to line 6514.

I do not know if this header file works with the arduino IDE or Atmel Studio or if it only works with the keil compiler but maybe it is a starting point.

I tried to implement the Quadrature Decoder in Atmel Studio but I cant program the Due from within Atmel Studio(yet) and since I'm not really great in programming this could be quite a task for me anyway

I hope some one here on the forum will come up with an example so we can use this neat feature of this nice new Arduino :slight_smile:

Cheers

Pingflip

It seems that this file \hardware\arduino\sam\system\CMSIS\Device\ATMEL\sam3xa\include\component\component_tc.h present the same information.

thats great news :slight_smile:

I also found it in "Contents/Resources/Java/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component/components_tc.h"

I am trying to figure out how to include all necessary files with all the typedefs and stuff to not get compiling errors

I'm currently working my self through the datasheet and the Timer/Counter relating header and source files and I think I'm almost there :slight_smile:
But: which T/C channel should I use to not generate conflicts with a PWM output or any other peripheral.

Does any one know which channels are already in use by the PWM and / or other functions?

I got the hardware quadrature encoder running partially...

Using the following code and a motor with a 2 trace quadrature encoder I manage to make the REG_TC0_CV0 register measure the position of the motor. To use the hardware quadrature encoder the two traces need to be connected to digital pins 2 and 13 which correspond to the TIOA0 and TIOB0 peripheral pin function of the SAM3x8e. I didn't test using the Index pin for revolution counting yet...

const int quad_A = 2;
const int quad_B = 13;
const unsigned int mask_quad_A = digitalPinToBitMask(quad_A);
const unsigned int mask_quad_B = digitalPinToBitMask(quad_B);  

void setup() {
    Serial.begin(115200);  
    delay(100);
    
    // activate peripheral functions for quad pins
    REG_PIOB_PDR = mask_quad_A;     // activate peripheral function (disables all PIO functionality)
    REG_PIOB_ABSR |= mask_quad_A;   // choose peripheral option B    
    REG_PIOB_PDR = mask_quad_B;     // activate peripheral function (disables all PIO functionality)
    REG_PIOB_ABSR |= mask_quad_B;   // choose peripheral option B 
    
    // activate clock for TC0
    REG_PMC_PCER0 = (1<<27);
    // 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<<9)|(1<<8);
    // enable the clock (CLKEN=1) and reset the counter (SWTRG=1) 
    // SWTRG = 1 necessary to start the clock!!
    REG_TC0_CCR0 = 5;    
    
void loop() {
  Serial.println(REG_TC0_CV0,DEC);
  delay(500);
}

The problem I encountered is that the TC0_CV0 register only counts half of the position steps that could actually be measured when evaluating all slopes on quad_A and quad_B. I investigated further and the problem seems to be that the slope detection on the digital pin 13 doesn't work. The position value is changed only when a slope on the digital pin 2 is send. A slope on pin 13 changes nothing.
However the level detection on pin 13 seems to work fine because otherwise the quadrature encoder wouldn't count all the way up when turning the motor in one direction. If the level at pin 13 was fixed, the counter would count one up at a positive slope on pin 2 and then down on the following negative slope, so the position value would alternate between two values...

anyone ideas that could explain this behaviour? maybe the quadrature encoder is not build to evaluate all slopes (which would be kinda stupid)?

Matthias

anyone ideas that could explain this behaviour? maybe the quadrature encoder is not build to evaluate all slopes (which would be kinda stupid)?

But possible until proven otherwise I guess? Shouldn't the datasheet shed light on how it responds to the four transitions of each encoder step is handled?

Lefty

I checked, but I couldn't find a clear statement. In Figure 37-15 the wire going out the Quadrature Decoder block is labeled "PHEdges" (and not particularly PHAEdges or PHBedges, they name the quadrature signals PHA and PHB in the datasheet), which kind of makes me think there should be edges on both detected.

update: from the datasheet:
" Position channel 0 accumulates the edges of PHA, PHB input signals giving a high accuracy on motor position..."
and
"After filtering, the quadrature signals are analyzed to extract the rotation direction and edges of
the 2 quadrature signals detected in order to be counted by timer/counter logic downstream."

sounds like edges should be detected on both signals.. will run some more tests tomorrow..

just solved the problem.. the EDGPHA bit in the TC_BMR register controls whether the edges are detected on both PHA and PHB or just on PHA. I had this bit set to 0 before which according to the datasheet was the good choice.

("0: edges are detected on both PHA and PHB. 1: edges are detected on PHA only.")

BUT!! the description in the datasheet is WRONG!! its inverted!

so the code that works fine is the following:

const int quad_A = 2;
const int quad_B = 13;
const unsigned int mask_quad_A = digitalPinToBitMask(quad_A);
const unsigned int mask_quad_B = digitalPinToBitMask(quad_B);  

void setup() {
    Serial.begin(115200);  
    delay(100);
    
    // activate peripheral functions for quad pins
    REG_PIOB_PDR = mask_quad_A;     // activate peripheral function (disables all PIO functionality)
    REG_PIOB_ABSR |= mask_quad_A;   // choose peripheral option B    
    REG_PIOB_PDR = mask_quad_B;     // activate peripheral function (disables all PIO functionality)
    REG_PIOB_ABSR |= mask_quad_B;   // choose peripheral option B 
    
    // activate clock for TC0
    REG_PMC_PCER0 = (1<<27);
    // 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<<9)|(1<<8)|(1<<12);
    // enable the clock (CLKEN=1) and reset the counter (SWTRG=1) 
    // SWTRG = 1 necessary to start the clock!!
    REG_TC0_CCR0 = 5;    
    
void loop() {
  Serial.println(REG_TC0_CV0,DEC);
  delay(500);
}

so for us dummies, does this code print the accumulated count from quadrature input on digital pins 2 and 13? not too good at complicated datasheets. also, are there only two pins that can be used as quadrature input and are they D2 and D13? i have a TFT shield that is wired to pin 2 among many others and can probably cut and jump to make pin 2 available but would prefer to not. my not very good interpretation of the data sheets suggests that three timers are available for quad processing but only two are brought out on the DUE board. is this correct?
The Timer Counter (TC) embeds a quadrature decoder logic connected in front of the 3 timers

and driven by TIOA0, TIOB0 and TIOA1 inputs. When enabled, the quadrature decoder per-
forms the input lines filtering, decoding of quadrature signals and connects to the
3 timers/counters in order to read the position and speed of the motor through user interface.

@schwingkopf:

great work, works really nice

@spencoid:

unfortunately you have to use Pin2, Pin13 and PinA7 for the quadrature encoder (PinA7 is the third pin/index Pin for the necoder).
I dont know if there is something like virtual pins on the SAM3X like on the XMega line. If there is something like that, the TIOA0, TIOB0 and TIOA1 lines could be routed to other pins I think

Ok i dont have a DUE yet, but im coding ahead of time. how about handling 2 or 3 encoders? and how can i level shift encoders? i have multiple 5v encoders. extremely fast 250lines.

i don't know if you can do it fast enough for high speed operation but the last encoders i bought (made by Bourns) have a chip select line so you could conceivably stick them all on a bus and sweep through them looking for signal changes. the programming might get a little confusing or might even be impossible.

A hex buffer, CD4050, will work nicely. Power the Vss pin with 3V3 to establish the output level. The inputs will take 5V signals.

As far as I understand the due has only one hardware encoder, but you can still use interrupt versions of software encoders to handle almost as many encoders as you wish.

in terms of speed you will then be limited by your interrupt handlers. If you programm cleverly you can get the time to handle the interrupts well below 1us (I can provide code if necessary), meaning that you can detect slopes at rates above 1MHz. If you use more than one encoder you just divide the 1MHz by the number of encoders you'd like to use.

I don't know exactly how fast the hardware encoder can go but since it uses the master clock (84MHz) as a reference I guess it can take at least more than 10MHz slope rate, probably even much faster.

For your application this would mean that you connect the encoder with the highest speed demands on the hardware encoder and you programm two interrupt based software encoders for the other 2. Those would then be able to detect slope rates of more than 500kHz.

Would that fit your "extremely fast" application?

for level shifting you can use chips like 74LVCC4245 and others. they're convenient to handle and super fast (10ns delay)

the buffer is a better way but i am using my 5 volt encoders with a voltage divider on the output. just a 10k and 20k resistor in series. easier to stick in as an afterthought than wiring in the buffer.

I am using a 500 pulse/rev encoder from maxon motors (I think they are rebranded AvagoTech Encoders), rated for 5V and 3,3kOhm Pullups on the signal lines. I use it with 3.3V and 10k Pullups and it works just fine. Maybe just give it a try.

Thank you all. Im going to buy the next Arduino Due ASAP. and replace my MEGA 1280. and get 3 buffers. its going to be interesting. next question how can i get a 5v pwm signal out?

Use one of the same buffer chips but connect Vss to 5V. As long as your input signal crosses around the 50% point, the output will switch states. Use the B version of the chip as it's switching level is better defined. Check the data sheet on AllDataSheet.com.