Pages: [1] 2 3 4   Go Down
Author Topic: Using the Hardware Quadrature Encoder Channel on the DUE  (Read 15118 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 1
Posts: 11
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

NZ
Offline Offline
Jr. Member
**
Karma: 1
Posts: 91
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Germany
Offline Offline
Newbie
*
Karma: 0
Posts: 23
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@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  smiley-sad
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:

Code:
#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 smiley

Cheers

Pingflip
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Germany
Offline Offline
Newbie
*
Karma: 0
Posts: 23
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

thats great news smiley

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
Logged

Germany
Offline Offline
Newbie
*
Karma: 0
Posts: 23
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 smiley
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?
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
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
Logged

Left Coast, CA (USA)
Offline Offline
Brattain Member
*****
Karma: 361
Posts: 17263
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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..
« Last Edit: February 13, 2013, 05:08:10 pm by schwingkopf » Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
Code:
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);
}
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 38
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Germany
Offline Offline
Newbie
*
Karma: 0
Posts: 23
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@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
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 37
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 38
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

St. Petersburg, FL
Offline Offline
Jr. Member
**
Karma: 1
Posts: 61
Retired physicist
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

Pages: [1] 2 3 4   Go Up
Jump to: