ASF4 32-bit TC rolling over after 16-bits

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using the ATSAME54P20A microprocessor on the SAME54 Xplained board with the DAC Example using Timer Event and DMA example firmware off of Atmel start - https://start.atmel.com/#examples/SAME54XplainedPro

This program initializes timer counter 0 (TC0) using the following code. As you can see, TC0 is initialized in 32-bit mode (on the line with | 0x2 << TC_CTRLA_MODE_POS);. The data sheet confirms writing a 0x2 to the mode register of the timer counter should set it to 32-bit mode.

 

/**
 * \brief Initialize TC interface
 */
int8_t TIMER_0_init()
{

	if (!hri_tc_is_syncing(TC0, TC_SYNCBUSY_SWRST)) {
		if (hri_tc_get_CTRLA_reg(TC0, TC_CTRLA_ENABLE)) {
			hri_tc_clear_CTRLA_ENABLE_bit(TC0);
			hri_tc_wait_for_sync(TC0, TC_SYNCBUSY_ENABLE);
		}
		hri_tc_write_CTRLA_reg(TC0, TC_CTRLA_SWRST);
	}
	hri_tc_wait_for_sync(TC0, TC_SYNCBUSY_SWRST);

	hri_tc_write_CTRLA_reg(TC0,
	                       0 << TC_CTRLA_CAPTMODE0_Pos       /* Capture mode Channel 0: 0 */
	                           | 0 << TC_CTRLA_CAPTMODE1_Pos /* Capture mode Channel 1: 0 */
	                           | 0 << TC_CTRLA_COPEN0_Pos    /* Capture Pin 0 Enable: disabled */
	                           | 0 << TC_CTRLA_COPEN1_Pos    /* Capture Pin 1 Enable: disabled */
	                           | 0 << TC_CTRLA_CAPTEN0_Pos   /* Capture Channel 0 Enable: disabled */
	                           | 0 << TC_CTRLA_CAPTEN1_Pos   /* Capture Channel 1 Enable: disabled */
	                           | 0 << TC_CTRLA_ALOCK_Pos     /* Auto Lock: disabled */
	                           | 0 << TC_CTRLA_PRESCSYNC_Pos /* Prescaler and Counter Synchronization: 0 */
	                           | 0 << TC_CTRLA_ONDEMAND_Pos  /* Clock On Demand: disabled */
	                           | 0 << TC_CTRLA_RUNSTDBY_Pos  /* Run in Standby: disabled */
	                           | 0 << TC_CTRLA_PRESCALER_Pos /* Setting: 0 */
	                           | 0x2 << TC_CTRLA_MODE_Pos);  /* Operating Mode: 0x2 */

	hri_tc_write_CTRLB_reg(TC0,
	                       0 << TC_CTRLBSET_CMD_Pos           /* Command: 0 */
	                           | 0 << TC_CTRLBSET_ONESHOT_Pos /* One-Shot: disabled */
	                           | 0 << TC_CTRLBCLR_LUPD_Pos    /* Setting: disabled */
	                           | 0 << TC_CTRLBSET_DIR_Pos);   /* Counter Direction: disabled */

	hri_tc_write_WAVE_reg(TC0, 1); /* Waveform Generation Mode: 1 */

	// hri_tc_write_DRVCTRL_reg(TC0,0 << TC_DRVCTRL_INVEN1_Pos /* Output Waveform 1 Invert Enable: disabled */
	//		 | 0 << TC_DRVCTRL_INVEN0_Pos); /* Output Waveform 0 Invert Enable: disabled */

	// hri_tc_write_DBGCTRL_reg(TC0,0); /* Run in debug: 0 */

	hri_tccount32_write_CC_reg(TC0, 0, 0xbb); /* Compare/Capture Value: 0xbb */

	// hri_tccount32_write_CC_reg(TC0, 1 ,0x0); /* Compare/Capture Value: 0x0 */

	// hri_tccount32_write_COUNT_reg(TC0,0x0); /* Counter Value: 0x0 */

	hri_tc_write_EVCTRL_reg(
	    TC0,
	    0 << TC_EVCTRL_MCEO0_Pos       /* Match or Capture Channel 0 Event Output Enable: disabled */
	        | 0 << TC_EVCTRL_MCEO1_Pos /* Match or Capture Channel 1 Event Output Enable: disabled */
	        | 1 << TC_EVCTRL_OVFEO_Pos /* Overflow/Underflow Event Output Enable: enabled */
	        | 0 << TC_EVCTRL_TCEI_Pos  /* TC Event Input: disabled */
	        | 0 << TC_EVCTRL_TCINV_Pos /* TC Inverted Event Input: disabled */
	        | 0);                      /* Event Action: 0 */

	// hri_tc_write_INTEN_reg(TC0,0 << TC_INTENSET_MC0_Pos /* Match or Capture Channel 0 Interrupt Enable: disabled */
	//		 | 0 << TC_INTENSET_MC1_Pos /* Match or Capture Channel 1 Interrupt Enable: disabled */
	//		 | 0 << TC_INTENSET_ERR_Pos /* Error Interrupt Enable: disabled */
	//		 | 0 << TC_INTENSET_OVF_Pos); /* Overflow Interrupt enable: disabled */

	hri_tc_write_CTRLA_ENABLE_bit(TC0, 1 << TC_CTRLA_ENABLE_Pos); /* Enable: enabled */

	return 0;
}

The same function sets the value of the CC0 register (each timer counter has two separate counters that can be used, CC0 and CC1) to 0xbb, or 187. This works fine, and the counter does in fact count up to 187, trigger an overflow event, and then start back at zero. Setting this register to any value less than 16-bits works correctly. Writing any number larger than 16 bits results in the driver truncating the number at 16 bits, as you can see in the debugging image below, where I modified the code to write the value 0xFFFFF, yet 0xFFFF is written.

Debug image.

I found this previous post which was about the exact same issue in version 3 of the ASF:  https://www.avrfreaks.net/forum/samc21-32-bit-timer-not-counting-more-16-bits

The solution there required modifying the tc.c file of the ASF3 drivers (there was an indexing bug in the file related to the mclk configuration). It seems possible that this same bug was carried over into ASF4, but I cannot find any tc.c file in my drivers, and when I search the entire codebase for the offending lines, I do not get any results. So if this bug is still in the code somewhere, I'm having a hard time finding it, and if it isn't, I'm not sure what the problem is. The ASF4 function that performs the actual write to the processor register is in hri_tc_e54.h and is given below.

static inline void hri_tccount32_write_CC_reg(const void *const hw, uint8_t index, hri_tccount32_cc_reg_t data)
{
	TC_CRITICAL_SECTION_ENTER();
	((Tc *)hw)->COUNT32.CC
.reg = data; hri_tc_wait_for_sync(hw, TC_SYNCBUSY_CC0 | TC_SYNCBUSY_CC1); TC_CRITICAL_SECTION_LEAVE(); }

It's pretty simple - it just writes the given 32-bit uint to the COUNT32.CC

.reg value of the TC struct (I checked the hri_tccount32_cc_reg_t and it is a typedef for a uint32_t). I looked into the tc.h file, which defines the TC struct being written to, but the COUNT32.CC
.reg is clearly defined as a uint32_t, as shown below.

/* -------- TC_COUNT32_CC : (TC Offset: 0x1C) (R/W 32) COUNT32 COUNT32 Compare and Capture -------- */
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
typedef union {
  struct {
    uint32_t CC:32;            /*!< bit:  0..31  Counter/Compare Value              */
  } bit;                       /*!< Structure used for bit  access                  */
  uint32_t reg;                /*!< Type      used for register access              */
} TC_COUNT32_CC_Type;
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */

One final note, I tried manually writing 0xFFFFF to the CC0 memory space through the debugger, thinking it could be a write-protection problem. But writing to it manually still results in the value being truncated, even with that memory location displaying as a 32-bit value.

 

Any idea what might be happening here?

This topic has a solution.
Last Edited: Tue. Jan 7, 2020 - 09:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Scubajoel527 wrote:
SAME54

 

That's not an AVR !

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
That's not an AVR !

 

Relax Andy, unlike you we mere humans make boo boos.

 

I'll move it to the ASF for ARM forum smiley

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The slave TC (TC1)  is not getting a clock, fixed here

void TIMER_0_CLOCK_init(void)
{
	hri_mclk_set_APBAMASK_TC0_bit(MCLK);
	hri_mclk_set_APBAMASK_TC1_bit(MCLK); // Added
	hri_gclk_write_PCHCTRL_reg(GCLK, TC0_GCLK_ID, CONF_GCLK_TC0_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
}

Could also add a hri_gclk_write_PCHCTRL_reg call for TC1 but it is redundant (for SAME54) since TC0 and TC1 share the GCLK index.

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jim - thanks for moving the thread. I didn't realize I was in the wrong forum.

 

Here's an update on this problem. If instead of writing 0x2 to the timer counter mode register (which should configure it to 32-bit mode) we write 0x0 (which should configure it to 16-bit mode), 32-bit writes to the CC0 register will work. The debugger image below shows this.

32-bit timer counter resolution

 

As you can see, with the timer counter configured to 16-bit mode, I was able to successfully write 0xFFFFF to the 32-bit counter register. Unfortunately, when I tested the frequency of the output waveform on my oscilloscope, I got the same ~28 Hz frequency as I got writing a 0xFFFF to the register. So it appears that when configured to 32-bit mode, the microprocessor locks the CC0 register to 16 bits, and in the 16-bit mode the microprocessor releases the lock on the CC0 register, which is backwards from what it should be doing. Wherever the actual timer comparison happens on the chip (not sure if this is in hardware or software), when in 16-bit mode it seems to only read the first 16-bits from the CC0 register. I thought maybe if I configured the timer to 16-bit mode, wrote a 32-bit value to the register, then configured it to 32-bit mode I might be able to get around this, but upon switching back to 32-bit mode the chip clears the upper 16-bits of the register, effectively preventing the user from ever using the full 32-bit capability. I guess I will see if I can report this to microchip as a bug.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Disregard my previous post - Lars solution fixed everything. I will have to read some more about the slave clocks. Thanks for the solution Lars! This was driving me crazy.