XMC4x00: Quadrature encoder (POSIF) counts 4 steps per tick instead of 1

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
PaulFocus
Level 2
Level 2
10 replies posted First solution authored 10 sign-ins

Hello all,

I finally got my rotary encoder running on the XMC4800 EtherCAT relax board.

When I output the CCU timer register, I see the value incrementing / decrementing by 4 for every click I do on the rotarey encoder.

Is this somewhat related to the x4 clock generation stated in POSIF block diagram in Reference manual ch. 25.1.2? If so, is there a way to make the attached CCU only count single increments instead of 4?

(I'd rather use a correct approach than right-shift the timer register value by 2 bits ...)

 

Best regards,

ErnieT

0 Likes
1 Solution
amanning
Level 4
Level 4
50 sign-ins 25 replies posted 10 questions asked

Your configuration looks OK to me. I would suggest you enable the low pass filter, unless you are expecting high frequency rotations.

But your statement about 4 edges per click seems strange. Normally with each mechanical click only one of the A/B changes, so you get 1 edge per click. If your switch is giving you 4 edges per mechanical click, that is why the POSIF is counting 4. Looking at the diagram you posted shows that if you are in position 1 and turn one click you get 1 edge and end up in position 2. Turning the other way, you will get 1 edge and end up in position 4.

I think you problem is that the switch has a high electrical resolution than mechanical resolution. Have a good look at the datasheet.

View solution in original post

0 Likes
10 Replies
Aashita_R
Moderator
Moderator
Moderator
50 likes received 100 solutions authored 250 replies posted

Hi @PaulFocus ,

Could you please attach your project here so that we can check the project and app configurations at our end? At the moment, I am not certain what could be possible reason for this behavior.

Best Regards,

Aashita

 

0 Likes
lock attach
Attachments are accessible only for community members.
PaulFocus
Level 2
Level 2
10 replies posted First solution authored 10 sign-ins

The project is build with Dave 4.4.2 and runs on the XMC4800 EtherCAT relax kit. Only plain source code, no DAVE'ish click'n'build app.

Please connect the phase pins of a rotary encoder to P14.6 and P14.7. Then connect the USB cable to board debugger / flasher. Open a terminal for output on COM port (115200 Baud, standard settings) to see an output of the increment and the current position of the rotary encoder.

An example output is attached below. Please note that non-multiple increments of 4 are intermediate results: When the rotary encoder locks in to one of its 50 positions per rotation, the output will show a multiple of 4.

Screenshot 2022-02-25 152847.png

0 Likes
PaulFocus
Level 2
Level 2
10 replies posted First solution authored 10 sign-ins

I currently use the following code to workaround the problem. If anyone has a better idea, please let me know:

/** Retrieve increment performed by rotary encoder since last call to this method. */
int16_t
ROTENC_getIncrement() {

static uint16_t lastValue = 0u; /* last timer value */
static int16_t remainder = 0u; /* clicks remaining from last call due to dividing counter by 4 */

const uint16_t newValue = CCU40_CC40->TIMER;
const int16_t result = ((int16_t) lastValue - newValue) + remainder;
lastValue = newValue;

/* CCU4 counts 4 steps for single click; remember the remaining sub-clicks for next time */
remainder = result & 0x3;
return result >> 2u;
}
0 Likes
amanning
Level 4
Level 4
50 sign-ins 25 replies posted 10 questions asked

Are you sure that the electrical output from the rotary switch only gives one edge per mechanical click? The electrical output may have a high resolution than the mechanical resolution. Check the signals with an oscilloscope.

0 Likes

Just checked it. The rotary switch generates a single rising and falling edge on each input A and B. Mechanical and electrical resolution are the same.

I wonder if my timer configuration is correct. I read CCU40_CC40, but it seem the timer is not counting in quadrature mode, but counting each edge it receives. Or do I need a 2nd timer connected to CC40 which performs the actual counting?

0 Likes
amanning
Level 4
Level 4
50 sign-ins 25 replies posted 10 questions asked

Just to be sure we understand each other correctly. One mechanical click should produce one edge (positive or negative) on either Phase A or B. So there is only one change per click.

I assume you have set the PCONF register correctly for "Quadrature Decoder" and "standard Quadrature Mode".

I used a rotary switch together with the XMC4500 a few years ago and everything worked as expected.

Can you post the POSIF register dump (screen shot) from the debugger. Maybe I can see where the error is.

0 Likes
amanning
Level 4
Level 4
50 sign-ins 25 replies posted 10 questions asked

Which POSIF output have you connected to the CCU4?

 

0 Likes
PaulFocus
Level 2
Level 2
10 replies posted First solution authored 10 sign-ins

The oscilloscope tests displayed a picture similar to the one in Wikipedia (although it was not as symmetrical as in Wikipedia): https://en.wikipedia.org/wiki/Incremental_encoder#/media/File:Quadrature_Diagram.svg

A single click generates a total of two rising and falling edges on A and B. IMHO this is quadrature encoder mode, while generating a single edge per click should be the Direction Count Mode (PCONF.QDCM).

If I understand correctly, the POSIF receives quadrature encoded signals and turns them into Direction Count signals, which are then fed to the CCU timer.

The project uses POSIF0 and CCU40_CC40. The initialisation code (see link in post above for full project source):

 

#define MODULE_PTR		CCU40
#define MODULE_NUMBER 	(0U)

#define SLICE0_PTR 		CCU40_CC40
#define SLICE0_NUMBER 	(0U)

/* CCU4 Configuration for Position Count */
static XMC_CCU4_SLICE_COMPARE_CONFIG_t position_config =
{
	  .timer_mode          = XMC_CCU4_SLICE_TIMER_COUNT_MODE_EA,
	  .monoshot            = 0U,
	  .shadow_xfer_clear   = 1U,
	  .dither_timer_period = 0U,
	  .dither_duty_cycle   = 0U,
	  .prescaler_mode      = (uint32_t) XMC_CCU4_SLICE_PRESCALER_MODE_NORMAL,
	  .mcm_enable          = 0U,
	  .prescaler_initval   = 0U,
	  .float_limit         = 0U,
	  .dither_limit        = 0U,
	  .passive_level       = (uint32_t) XMC_CCU4_SLICE_OUTPUT_PASSIVE_LEVEL_LOW,
	  .timer_concatenation = 0U
};

/* Event 0: Counts up on rising edge of POSIF0.OUT0 (Quadrature Clock) */
static XMC_CCU4_SLICE_EVENT_CONFIG_t position_event0_config =
{
	.mapped_input = XMC_CCU4_SLICE_INPUT_E, /* mapped to POSIF0.OUT0 - POSITION tick */
	.edge = XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_RISING_EDGE,
	.level = XMC_CCU4_SLICE_EVENT_LEVEL_SENSITIVITY_COUNT_UP_ON_HIGH,
	.duration = XMC_CCU4_SLICE_EVENT_FILTER_DISABLED
};

/* Event 1: Count direction of POSIF0.OUT1 (Quadrature Direction)
 * OUT1 signal is asserted high when motor is rotating clockwise */
static XMC_CCU4_SLICE_EVENT_CONFIG_t position_event1_config=
{
	.mapped_input = XMC_CCU4_SLICE_INPUT_F, /* mapped to POSIF0.OUT1 - Direction */
	.edge = XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_RISING_EDGE,
	.level = XMC_CCU4_SLICE_EVENT_LEVEL_SENSITIVITY_ACTIVE_HIGH,
	.duration = XMC_CCU4_SLICE_EVENT_FILTER_DISABLED
};

/* ------------------------------------------------------------------------------------------------- */

/* POSIF Configuration */
XMC_POSIF_CONFIG_t posif_config =
{
   .mode   = XMC_POSIF_MODE_QD,    		/**< POSIF Operational mode */
   .input0 = XMC_POSIF_INPUT_PORT_B,    /**< Choice of input for Input-1 */
   .input1 = XMC_POSIF_INPUT_PORT_B,    /**< Choice of input for Input-2 */
   .input2 = XMC_POSIF_INPUT_PORT_B,   	/**< Choice of input for Input-3 */
   .filter = XMC_POSIF_FILTER_DISABLED, /**< Input filter configuration */
};

/* Defines POSIF quadrature decoder initialization data structure. */
XMC_POSIF_QD_CONFIG_t posif_qd_config =
{
  .mode                = (XMC_POSIF_QD_MODE_t)0,
  .phase_a             = 0U,	/* Phase A is active High */
  .phase_b             = 0U,	/* Phase B is active High */
  .phase_leader        = 0U,	/* Phase A is the leading signal for clockwise rotation */
  .index               = 0U		/* No index marker generation on POSIF0.OUT3 */
};

/* GPIO Init handle for inverter enable Pin */
XMC_GPIO_CONFIG_t posif_encoder_port_config =
{
  .mode             = XMC_GPIO_MODE_OUTPUT_PUSH_PULL,
  .output_level     = XMC_GPIO_OUTPUT_LEVEL_HIGH,
  .output_strength  = XMC_GPIO_OUTPUT_STRENGTH_STRONG_SHARP_EDGE,
};

/* GPIO Init handle for input Pin */
/* Port 14 digital pad input is disabled by default, this is to enable the digital pad input */
XMC_GPIO_CONFIG_t posif_encoder_inputport_config =
{
  .mode             = XMC_GPIO_MODE_INPUT_PULL_UP,
  .output_level     = XMC_GPIO_OUTPUT_LEVEL_HIGH,
  .output_strength  = XMC_GPIO_OUTPUT_STRENGTH_STRONG_SHARP_EDGE,
};

/* ------------------------------------------------------------------------------------------------- */

/** Initialize quadrature decoder. */
void
QUADDEC_init() {

	/* Enable clock, enable prescaler block and configure global control */
	XMC_CCU4_Init(MODULE_PTR, XMC_CCU4_SLICE_MCMS_ACTION_TRANSFER_PR_CR);

	/* Start the prescaler and restore clocks to slices */
	XMC_CCU4_StartPrescaler(MODULE_PTR);

	/* Start of CCU4 configurations */
	XMC_CCU4_SetModuleClock(MODULE_PTR, XMC_CCU4_CLOCK_SCU);

	/* Initialize the Slice */
	XMC_CCU4_SLICE_CompareInit(SLICE0_PTR, &position_config);

	/* Program the compare register */
	XMC_CCU4_SLICE_SetTimerPeriodMatch(SLICE0_PTR, 65535u);

	/* Enable shadow transfer */
	XMC_CCU4_EnableShadowTransfer(MODULE_PTR, (uint32_t)(XMC_CCU4_SHADOW_TRANSFER_SLICE_0| XMC_CCU4_SHADOW_TRANSFER_PRESCALER_SLICE_0));

	/* Configure CC40 event0 - Count on rising edge of Quadrature Clock (POSIF0.OUT0) */
	XMC_CCU4_SLICE_CountConfig(SLICE0_PTR, XMC_CCU4_SLICE_EVENT_0);
	XMC_CCU4_SLICE_ConfigureEvent(SLICE0_PTR, XMC_CCU4_SLICE_EVENT_0, &position_event0_config);

	/* Configure CC40 event1 - Set up count direction on (POSIF0.OUT1) */
	XMC_CCU4_SLICE_DirectionConfig(SLICE0_PTR, XMC_CCU4_SLICE_EVENT_1);
	XMC_CCU4_SLICE_ConfigureEvent(SLICE0_PTR, XMC_CCU4_SLICE_EVENT_1, &position_event1_config);

	/* POSIF Initialization */
	XMC_POSIF_Init(POSIF0, &posif_config);
	XMC_POSIF_QD_Init(POSIF0, &posif_qd_config);

	/* GPIO initialization to enable digital pad input
	 * This is needed for selected pins at Port 14; by default, the digital pad input for PORT14 are turned disabled.
	 * Therefore, we need to enable the digital pad input for POSIF0 */
	XMC_GPIO_Init(P14_6, &posif_encoder_inputport_config); /* A */
	XMC_GPIO_Init(P14_7, &posif_encoder_inputport_config); /* B */


	/* Get the CCU4 slice out of idle mode */
	XMC_CCU4_EnableClock(MODULE_PTR, SLICE0_NUMBER);

	/* Start the CCU4 Timer */
	XMC_CCU4_SLICE_StartTimer(SLICE0_PTR);

	/* Start the Encoder */
	XMC_POSIF_Start(POSIF0);
}

 

 

Image of PCONF: FSEL and QDC seem to be set correct.

PCONF.png

0 Likes
PaulFocus
Level 2
Level 2
10 replies posted First solution authored 10 sign-ins

Uses POSIF0 and CCU40. See POSIF.PCONF register, which seems OK to me (QDCM and FSEL).

PCONF.png

The full project code is attached in my 2nd post, initialization snippet'ed here:

 

#define MODULE_PTR		CCU40
#define MODULE_NUMBER 	(0U)

#define SLICE0_PTR 		CCU40_CC40
#define SLICE0_NUMBER 	(0U)

/* CCU4 Configuration for Position Count */
static XMC_CCU4_SLICE_COMPARE_CONFIG_t position_config =
{
	  .timer_mode          = XMC_CCU4_SLICE_TIMER_COUNT_MODE_EA,
	  .monoshot            = 0U,
	  .shadow_xfer_clear   = 1U,
	  .dither_timer_period = 0U,
	  .dither_duty_cycle   = 0U,
	  .prescaler_mode      = (uint32_t) XMC_CCU4_SLICE_PRESCALER_MODE_NORMAL,
	  .mcm_enable          = 0U,
	  .prescaler_initval   = 0U,
	  .float_limit         = 0U,
	  .dither_limit        = 0U,
	  .passive_level       = (uint32_t) XMC_CCU4_SLICE_OUTPUT_PASSIVE_LEVEL_LOW,
	  .timer_concatenation = 0U
};

/* Event 0: Counts up on rising edge of POSIF0.OUT0 (Quadrature Clock) */
static XMC_CCU4_SLICE_EVENT_CONFIG_t position_event0_config =
{
	.mapped_input = XMC_CCU4_SLICE_INPUT_E, /* mapped to POSIF0.OUT0 - POSITION tick */
	.edge = XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_RISING_EDGE,
	.level = XMC_CCU4_SLICE_EVENT_LEVEL_SENSITIVITY_COUNT_UP_ON_HIGH,
	.duration = XMC_CCU4_SLICE_EVENT_FILTER_DISABLED
};

/* Event 1: Count direction of POSIF0.OUT1 (Quadrature Direction)
 * OUT1 signal is asserted high when motor is rotating clockwise */
static XMC_CCU4_SLICE_EVENT_CONFIG_t position_event1_config=
{
	.mapped_input = XMC_CCU4_SLICE_INPUT_F, /* mapped to POSIF0.OUT1 - Direction */
	.edge = XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_RISING_EDGE,
	.level = XMC_CCU4_SLICE_EVENT_LEVEL_SENSITIVITY_ACTIVE_HIGH,
	.duration = XMC_CCU4_SLICE_EVENT_FILTER_DISABLED
};

/* ------------------------------------------------------------------------------------------------- */

/* POSIF Configuration */
XMC_POSIF_CONFIG_t posif_config =
{
   .mode   = XMC_POSIF_MODE_QD,    		/**< POSIF Operational mode */
   .input0 = XMC_POSIF_INPUT_PORT_B,    /**< Choice of input for Input-1 */
   .input1 = XMC_POSIF_INPUT_PORT_B,    /**< Choice of input for Input-2 */
   .input2 = XMC_POSIF_INPUT_PORT_B,   	/**< Choice of input for Input-3 */
   .filter = XMC_POSIF_FILTER_DISABLED, /**< Input filter configuration */
};

/* Defines POSIF quadrature decoder initialization data structure. */
XMC_POSIF_QD_CONFIG_t posif_qd_config =
{
  .mode                = (XMC_POSIF_QD_MODE_t)0,
  .phase_a             = 0U,	/* Phase A is active High */
  .phase_b             = 0U,	/* Phase B is active High */
  .phase_leader        = 0U,	/* Phase A is the leading signal for clockwise rotation */
  .index               = 0U		/* No index marker generation on POSIF0.OUT3 */
};

/* GPIO Init handle for inverter enable Pin */
XMC_GPIO_CONFIG_t posif_encoder_port_config =
{
  .mode             = XMC_GPIO_MODE_OUTPUT_PUSH_PULL,
  .output_level     = XMC_GPIO_OUTPUT_LEVEL_HIGH,
  .output_strength  = XMC_GPIO_OUTPUT_STRENGTH_STRONG_SHARP_EDGE,
};

/* GPIO Init handle for input Pin */
/* Port 14 digital pad input is disabled by default, this is to enable the digital pad input */
XMC_GPIO_CONFIG_t posif_encoder_inputport_config =
{
  .mode             = XMC_GPIO_MODE_INPUT_PULL_UP,
  .output_level     = XMC_GPIO_OUTPUT_LEVEL_HIGH,
  .output_strength  = XMC_GPIO_OUTPUT_STRENGTH_STRONG_SHARP_EDGE,
};

/* ------------------------------------------------------------------------------------------------- */

/** Initialize quadrature decoder. */
void
QUADDEC_init() {

	/* Enable clock, enable prescaler block and configure global control */
	XMC_CCU4_Init(MODULE_PTR, XMC_CCU4_SLICE_MCMS_ACTION_TRANSFER_PR_CR);

	/* Start the prescaler and restore clocks to slices */
	XMC_CCU4_StartPrescaler(MODULE_PTR);

	/* Start of CCU4 configurations */
	XMC_CCU4_SetModuleClock(MODULE_PTR, XMC_CCU4_CLOCK_SCU);

	/* Initialize the Slice */
	XMC_CCU4_SLICE_CompareInit(SLICE0_PTR, &position_config);

	/* Program the compare register */
	XMC_CCU4_SLICE_SetTimerPeriodMatch(SLICE0_PTR, 65535u);

	/* Enable shadow transfer */
	XMC_CCU4_EnableShadowTransfer(MODULE_PTR, (uint32_t)(XMC_CCU4_SHADOW_TRANSFER_SLICE_0| XMC_CCU4_SHADOW_TRANSFER_PRESCALER_SLICE_0));

	/* Configure CC40 event0 - Count on rising edge of Quadrature Clock (POSIF0.OUT0) */
	XMC_CCU4_SLICE_CountConfig(SLICE0_PTR, XMC_CCU4_SLICE_EVENT_0);
	XMC_CCU4_SLICE_ConfigureEvent(SLICE0_PTR, XMC_CCU4_SLICE_EVENT_0, &position_event0_config);

	/* Configure CC40 event1 - Set up count direction on (POSIF0.OUT1) */
	XMC_CCU4_SLICE_DirectionConfig(SLICE0_PTR, XMC_CCU4_SLICE_EVENT_1);
	XMC_CCU4_SLICE_ConfigureEvent(SLICE0_PTR, XMC_CCU4_SLICE_EVENT_1, &position_event1_config);

	/* POSIF Initialization */
	XMC_POSIF_Init(POSIF0, &posif_config);
	XMC_POSIF_QD_Init(POSIF0, &posif_qd_config);

	/* GPIO initialization to enable digital pad input
	 * This is needed for selected pins at Port 14; by default, the digital pad input for PORT14 are turned disabled.
	 * Therefore, we need to enable the digital pad input for POSIF0 */
	XMC_GPIO_Init(P14_6, &posif_encoder_inputport_config); /* A */
	XMC_GPIO_Init(P14_7, &posif_encoder_inputport_config); /* B */


	/* Get the CCU4 slice out of idle mode */
	XMC_CCU4_EnableClock(MODULE_PTR, SLICE0_NUMBER);

	/* Start the CCU4 Timer */
	XMC_CCU4_SLICE_StartTimer(SLICE0_PTR);

	/* Start the Encoder */
	XMC_POSIF_Start(POSIF0);
}

 

 

The generated edges look very much like this: https://en.wikipedia.org/wiki/Incremental_encoder#/media/File:Quadrature_Diagram.svg

Means: It does not generate 1 edge per click, but 4 edges (up and down on phase A, up and down on phase B). Single edge per click is IMHO "Direction Count Mode" (see POSIF.PCONF in bit QDCM).

If I understood correctly, then the POSIF will receive the phase A and B inputs, which have 4 edges per click. It then translates them into "Direction Count" signals (similar to CNC "step/direction signals") when passed to CCU timer.

0 Likes
amanning
Level 4
Level 4
50 sign-ins 25 replies posted 10 questions asked

Your configuration looks OK to me. I would suggest you enable the low pass filter, unless you are expecting high frequency rotations.

But your statement about 4 edges per click seems strange. Normally with each mechanical click only one of the A/B changes, so you get 1 edge per click. If your switch is giving you 4 edges per mechanical click, that is why the POSIF is counting 4. Looking at the diagram you posted shows that if you are in position 1 and turn one click you get 1 edge and end up in position 2. Turning the other way, you will get 1 edge and end up in position 4.

I think you problem is that the switch has a high electrical resolution than mechanical resolution. Have a good look at the datasheet.

0 Likes