nano curiosity 4809: TCB0 FRQ mode not getting capture value

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

 

 

Hi All,

 

I've made progress on working with clocks on the mega4809 on the Microchip Nano Curiososity board.

 

I have the 32768 Hz external crystal driving the RTC PIT, routing the PIT / 64 via EVSYS to TCB0, and using TCB0 to drive some I/O and calibration.

The code will first flash the led rapidly (~ 4 hz) until the operator opens a terminal connection and executes a button-press.

Then I expect to see the following in my terminal:

start

   1 nnnn

   2 nnnn

   3 nnnn

where nnnn are non-zero numbers of approximately the same number.  These should be the count from one PIT/64 interrupt to the next.  What I'm getting is:

start

   1     0

   2     0

   3     0

So I'm guessing the TCB0.CCMP is not capturing the count as I expect.

Any clues why I'm getting 0 all the time?

 

EDIT: had wrong section from document: was 21.3.3.1.6, is 21.3.3.1.4

 

 

 

The code:

/* clkcal/main.c
 * minicom -b 9600 -D /dev/ttyACM0
 */
#ifdef F_CPU
#undef F_CPU
#endif
#define F_CPU 5000000L
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

void nano_button_wait() {
  uint8_t st = 1;			/* switch state */
  uint8_t lc;				/* led counter */

  TCB0.CCMP = 333;			/* debounce time */
  TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm;
  PORTF.DIRSET = PIN5_bm;		/* LED output */

  while (st) {
    switch (st) {
    case 1: if ((PORTF.IN & PIN6_bm) == 0) st = 2; break;
    case 2: if ((PORTF.IN & PIN6_bm) != 0) st = 0; break;
    }
    if (lc++ == 0) PORTF.OUTTGL = PIN5_bm; /* toggle LED */
    while ((TCB0.INTFLAGS & 0x01) == 0);   /* wait for bounce */
    TCB0.INTFLAGS = 0x01;		   /* reset flag */
  }

  PORTF.DIRCLR = PIN5_bm;
  TCB0.CTRLA = 0x00;			/* DISABLE */
  TCB0.CCMP = 0;
}

void init_sys_clk() {
 _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSC20M_gc);
 _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm);
 while ((CLKCTRL.MCLKSTATUS & 0x11) != 0x10); /* wait for stable OSC20M */
}

void init_rtc_pit() {
 _PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, 0x33);
 while ((CLKCTRL.MCLKSTATUS & 0x41) != 0x40); /* wait for stable XOSC32K */

 while (RTC.STATUS != 0);
 RTC.CLKSEL = RTC_CLKSEL_TOSC32K_gc;	/* external crystal for RTC/PIT */
 RTC.CTRLA = RTC_PRESCALER_DIV1_gc;	/* RTC/PIT DIV1 scaling */

 while ((RTC.PITSTATUS & 0x1) != 0);	/* wait for steady PITCTRLA */
 RTC.PITCTRLA = RTC_PERIOD_OFF_gc | RTC_PITEN_bm;
}

void config_evsys() {
  EVSYS.CHANNEL7 = 0x0B;	       /* RTC_PIT DIV64 -> channel7 */
  EVSYS_USERTCB0 = EVSYS_CHANNEL_CHANNEL7_gc; /* channel7 -> TCB0 */
}

void init_tcb0() {
  TCB0.EVCTRL = TCB_EDGE_bm | TCB_CAPTEI_bm; /* enable event input */
  TCB0.CTRLB = TCB_CNTMODE_FRQ_gc;	/* capture count on event */
  TCB0.INTCTRL = TCB_CAPT_bm;		/* and interrupt */
  TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm;
}

#define XCLK_TICK 0x0080
volatile uint16_t tcb0_ccnt = 0;  /* captured count */
volatile uint8_t ready_flag = 0;  /* capt count is ready to process */
volatile uint32_t intsec = 0;	  /* integer seconds */
volatile uint16_t subsec = 0;	  /* sub second, 0x8000 = 1/2 sec */

ISR(TCB0_INT_vect) {
  TCB0.INTFLAGS = RTC_PI_bm;		/* reset INTR flag */

  /* error estimate */
  tcb0_ccnt = TCB0.CCMP;
  ready_flag = 1;

  /* time tracking : assumes XCLK_TICK wraps 16 bits every second */
  subsec += XCLK_TICK;
  if (subsec == 0) intsec += 1;
}

#define BAUD_FROM_RATE(RATE) ((4UL * (F_CPU))/(RATE))

void uart_init(USART_t *usart, uint16_t baud) {
  usart->BAUD = baud;
  usart->CTRLC = USART_SBMODE_2BIT_gc | USART_CHSIZE_8BIT_gc;
}

void reset() {
  asm volatile ("jmp 0");
}

int main(void) {
  USART_t *usart = &USART3;
  int8_t nch = 0;			/* number of chars to xmit */
  int8_t chx = 0;			/* index of next char to xmit */
  char buf[64];				/* char buffer for xmit */

  nano_button_wait();			/* wait for button-press */

  PORTF.DIRSET = PIN5_bm;		/* set LED/PF5 as output */
  PORTF.OUTSET = PIN5_bm;		/* LED off */

  uart_init(usart, BAUD_FROM_RATE(BAUDRATE));
  PORTB.PIN0CTRL = PORT_PULLUPEN_bm;	/* enable pullup on PB0 */
  PORTB.OUTSET = PIN0_bm;		/* needed? */
  PORTB.DIRSET = PIN0_bm;
  usart->CTRLB = USART_TXEN_bm;		/* enable TX */

  init_sys_clk();
  init_rtc_pit();
  init_tcb0();
  config_evsys();

  _PROTECTED_WRITE(WDT.CTRLA, WDT_PERIOD_8KCLK_gc); /* 8 sec watchdog */

  sei();

  strcpy(buf, "start\r\n"); nch = strlen(buf); chx = 0;
  while (1) {
    wdt_reset();			/* reset watchdog */

    /* push data out if ready */
    if (chx < nch) {
      if ((usart->STATUS & USART_DREIF_bm) != 0) {
	usart->TXDATAL = buf[chx++];
      }
    }

    if (ready_flag) {
      ready_flag = 0;

      /* compute OSC20M error -- later */

      if (subsec == 0) {
	PORTF.OUTTGL = PIN5_bm;		/* toggle LED */
	if (chx < nch) reset();		/* deadline for xmit expired */
	nch = snprintf(buf, sizeof(buf),
		       "%4d %4d\r\n", intsec, tcb0_ccnt) % 128;
	chx = 0;
      }
    }

  }
}

/* --- last line --- */

 

 

 

This topic has a solution.
Last Edited: Tue. Aug 18, 2020 - 03:17 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried all sorts of stuff to sample both TCB0.CCMP and TCB0.CNT and I'm always getting zero.   This is driving me nuts.  The interrupt is obviously executing, but I'm not getting any counts. 

I did remove the write to TCB0.INTFLAGS (to reset the intr flag) in the ISR as the read of TCB0.CCNT will reset the intr flag.

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

It looks like if I eliminate the UART code (reporting) it works.

I removed that code and set LED flash period to be dependent on whether tcb0_ccnt is zero or not.  It's looking to be non-zero now.

I'm off to try to understand why.

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

I have it working now.   There was no problem with the application code.  nnnn was coming out to be 733 or 734.  Off for more fun.

It turned out that removing the use of snprintf makes it work.   I don't know why that is hosed: maybe something to do with building my own avr-libc.

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

So here are results.  I am running my mcu at 5 MHz.  The RTC PIT is clocked by a 32768 Hz crystal, and is generating an event 32768/64 = 512 times per second.

Therefore with a perfect OSC20M and perfect XOSC32K and no delays in the signal chain I expect the TCB to report a count of 5000000/512 = 9765.25.   I get ...

 

 0001 09743
 0002 09741
 0003 09737
 0004 09744
 0005 09740
 0006 09740
 0007 09741
 0008 09740
 0009 09737
 0010 09742
 0011 09741                                                                     
 0012 09739                                                                     
 0013 09743                                                                     
 0014 09740                                                                     
 0015 09740                                                                     
 0016 09740                                                                     
 0017 09735                                                                     
 0018 09738                                                                     

Of course there are delays, the OSC20M is not perfect and the XOSC32K is not perfect.  But the results are looking OK now.  Off to check delays.  And work on my 1 PPS input.

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


That's a good distribution, I think. The number of samples is low.

 

 

It seems the main oscillator is running about 0.25% slow. Since the calibration steps are 0.75% according to the datasheet, this value is the best possible result.

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

El Tangas wrote:
It seems the main oscillator is running about 0.25% slow.

Or the control oscillator is fast?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

theusch wrote:
Or the control oscillator is fast?

 

Well, yes, I should have said slow relative to the crystal.

 

I unconsciously omitted it because I have a bias: I have a curiosity nano board too, and the crystal in mine happens to be very well calibrated relative to my scope's timebase.

So I automatically assumed the crystal was the reference (which it probably is, but that's not the point). Interesting how these biases creep up in your mind without you noticing it...

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

Yes.  This is looking to estimate the number of clk_per ticks per PIT tick.

 

Here is a version of the code that computes a running average of the clk_per counts between PITs.

Remember that given a sequence of n measurements, the average average tick count after n measurements x(n) is given by x(n) = (n-1)/n*x(n-1) + 1/n *u(n) where u(n) is the n-th measurement (with x(-1) = 0).  I am low-passing this by using x(n) = 255/256*x(n-1) + 1/256 u(n), or equivalently x(n) = x(n-1) + 1/256*[u(n) - x(n-1)].

 

The code below takes generates a new average from sampling the count once per second.  More data is attached, but here is the start.  I start at 9765.

START
 001 9740 9764+231/256
 002 9745 9764+212/256
 003 9743 9764+191/256
 004 9743 9764+170/256
 005 9744 9764+150/256
 006 9744 9764+130/256
 007 9740 9764+106/256
 008 9743 9764+085/256
 009 9740 9764+061/256
 010 9742 9764+039/256

The first column is number of seconds, the second is the measurement, and the third is the average, with integer and fraction (of 256) clk_per ticks per PIT tick.

 

The code:

#ifdef F_CPU
#undef F_CPU
#endif
#define F_CPU 5000000L
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

/* Wait for user button press to start application code. */
void nano_button_wait() {
  uint8_t st = 1;			/* switch state */
  uint8_t lc;				/* led counter */

  TCB0.CCMP = 333;			/* debounce time */
  TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm;
  PORTF.DIRSET = PIN5_bm;		/* LED output */

  while (st) {
    switch (st) {
    case 1: if ((PORTF.IN & PIN6_bm) == 0) st = 2; break;
    case 2: if ((PORTF.IN & PIN6_bm) != 0) st = 0; break;
    }
    if (lc++ == 0) PORTF.OUTTGL = PIN5_bm; /* toggle LED */
    while ((TCB0.INTFLAGS & 0x01) == 0);   /* wait for bounce */
    TCB0.INTFLAGS = 0x01;		   /* reset flag */
  }

  /* Restore MCU state. */
  PORTF.DIRCLR = PIN5_bm;
  TCB0.CTRLA = 0x00;
  TCB0.CCMP = 0;
  TCB0.INTFLAGS = 0x01;
}

void init_sys_clk() {
 _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSC20M_gc);
 _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm);
 while ((CLKCTRL.MCLKSTATUS & 0x11) != 0x10); /* wait for stable OSC20M */
}

#define BAUD_FROM_RATE(RATE) ((4UL * (F_CPU))/(RATE))

void init_uart() {
  USART_t *usart = &USART3;

  usart->BAUD = BAUD_FROM_RATE(9600);
  usart->CTRLC = USART_SBMODE_2BIT_gc | USART_CHSIZE_8BIT_gc;
  PORTB.PIN0CTRL = PORT_PULLUPEN_bm;	/* enable pullup on PB0 */
  PORTB.OUTSET = PIN0_bm;		/* needed? */
  PORTB.DIRSET = PIN0_bm;
  usart->CTRLB = USART_TXEN_bm;		/* enable TX */
}

void init_wdt() {
  _PROTECTED_WRITE(WDT.CTRLA, WDT_PERIOD_8KCLK_gc); /* 8 sec watchdog */
}

void reset() {
  _PROTECTED_WRITE(RSTCTRL.SWRR, 1);	/* s/w reset */
}

/* === application =============== */

#if 1
#define CHAN7_PITEV_CODE 0x0B
#define PITEV_TICK 0x0080
#else
error "pit event tick val not defined"
#endif

void init_rtc_pit() {
 _PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, 0x33);
 while ((CLKCTRL.MCLKSTATUS & 0x41) != 0x40); /* wait for stable XOSC32K */

 while (RTC.STATUS != 0);
 RTC.CLKSEL = RTC_CLKSEL_TOSC32K_gc;	/* external crystal for RTC/PIT */
 RTC.CTRLA = RTC_PRESCALER_DIV2_gc;	/* DIV2 or more for calib */

 while ((RTC.PITSTATUS & 0x1) != 0);	/* wait for steady PITCTRLA */
 RTC.PITCTRLA = RTC_PERIOD_OFF_gc | RTC_PITEN_bm;
}

void config_evsys() {
  EVSYS.CHANNEL7 = CHAN7_PITEV_CODE;   /* RTC_PIT DIV64 -> channel7 */
  EVSYS_USERTCB0 = EVSYS_CHANNEL_CHANNEL7_gc; /* channel7 -> TCB0 */
}

void init_tcb0() {
  TCB0.EVCTRL = /*TCB_FILTER_bm|TCB_EDGE_bm|*/TCB_CAPTEI_bm; /* enable input */
  TCB0.CTRLB = TCB_CCMPEN_bm | TCB_CNTMODE_FRQ_gc; /* capture count on event */
  TCB0.INTCTRL = TCB_CAPT_bm;			   /* and interrupt */
  TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm;
}

volatile uint8_t ready_flag = 0;  /* capt count is ready to process */
volatile uint16_t tcb0_ccnt = 1;  /* captured count */
volatile uint32_t intsec = 0;	  /* integer seconds */
volatile uint16_t subsec = 0;	  /* sub second, 0x8000 = 1/2 sec */

ISR(TCB0_INT_vect) {
  /* error estimate */
  tcb0_ccnt = TCB0.CCMP;		/* clears INTR flag */
  ready_flag = 1;

  /* time tracking : assumes PITEV_TICK wraps 16 bits every second */
  subsec += PITEV_TICK;
  if (subsec == 0) intsec += 1;
}

struct {
  uint8_t scnt;			/* sub-integer count */
  uint16_t icnt;		/* integer count */
} sx_mean;

void sx_init() {
  sx_mean.icnt = 9765;
  sx_mean.scnt = 0;
}

void ss_update1(uint8_t *px, uint16_t ccnt) {
  /* hard code, not the best way */
  asm("   movw r30,r24\n"
      "	  ldd r19,Z+0\n"
      "	  ldd r20,Z+1\n"
      "	  ldd r21,Z+2\n"
      "	  clr r24\n"
      "	  sub r22,r20\n"
      "	  sbc r23,r21\n"
      "	  brpl 1f\n"
      "	  com r24\n"
      "1: add r22,r19\n"
      "	  adc r23,r20\n"
      "	  adc r24,r21\n"
      "	  std Z+0,r22\n"
      "	  std Z+1,r23\n"
      "	  std Z+2,r24\n"
      : /* no outputs */
      : /* no inputs */
      : "r19", "r20", "r21", "r22", "r23", "r24", "r25", "cc");
}

void sx_update(uint16_t ccnt) {
  ss_update1(&sx_mean.scnt, ccnt);
}

int main(void) {
  USART_t *usart = &USART3;
  int8_t nch = 0;		     /* number of chars to xmit */
  int8_t chx = 0;		     /* index of next char to xmit */
  char buf[64]; 		     /* char buffer for xmit */

  nano_button_wait();			/* wait for button-press */

  PORTF.DIRSET = PIN5_bm;		/* set LED/PF5 as output */
  PORTF.OUTSET = PIN5_bm;		/* LED off */

  init_sys_clk();

  init_rtc_pit();
  init_tcb0();
  config_evsys();

  init_uart();
  sx_init();

  init_wdt();
  sei();

  strcpy(buf, "START\r\n"); nch = strlen(buf); chx = 0;
  while (1) {
    wdt_reset();			/* reset watchdog */

    /* push data out if ready */
    if (chx < nch) {
      if ((usart->STATUS & USART_DREIF_bm) != 0) {
	usart->TXDATAL = buf[chx++];
      }
    }

    if (ready_flag) {
      ready_flag = 0;

      //sx_update(tcb0_ccnt); /* 512 / sec */

      if (subsec == 0) {
	PORTF.OUTTGL = PIN5_bm;		/* toggle LED */
      }
      //if ((subsec == 0) && (intsec % 2) == 0) {
      if (subsec == 0) {
	sx_update(tcb0_ccnt);		/* 1 / sec */

	if (chx < nch) reset();		/* deadline for xmit expired */

	nch = 0;
	buf[nch++] = ' ';
	//buf[nch++] = ((intsec/1000) % 10) + '0';
	buf[nch++] = ((intsec/100) % 10) + '0';
	buf[nch++] = ((intsec/10) % 10) + '0';
	buf[nch++] = (intsec % 10) + '0';
	buf[nch++] = ' ';
	//buf[nch++] = ((tcb0_ccnt/10000) % 10) + '0';
	buf[nch++] = ((tcb0_ccnt/1000) % 10) + '0';
	buf[nch++] = ((tcb0_ccnt/100) % 10) + '0';
	buf[nch++] = ((tcb0_ccnt/10) % 10) + '0';
	buf[nch++] = (tcb0_ccnt % 10) + '0';

	buf[nch++] = ' ';
	buf[nch++] = ((sx_mean.icnt/1000) % 10) + '0';
	buf[nch++] = ((sx_mean.icnt/100) % 10) + '0';
	buf[nch++] = ((sx_mean.icnt/10) % 10) + '0';
	buf[nch++] = (sx_mean.icnt % 10) + '0';

	buf[nch++] = '+';
	buf[nch++] = ((sx_mean.scnt/100) % 10) + '0';
	buf[nch++] = ((sx_mean.scnt/10) % 10) + '0';
	buf[nch++] = (sx_mean.scnt % 10) + '0';
	buf[nch++] = '/'; buf[nch++] = '2'; buf[nch++] = '5'; buf[nch++] = '6'; 

	buf[nch++] = '\r'; buf[nch++] = '\n'; buf[nch] = '\0';
	chx = 0;
      }
    }

  }
}

 

Attachment(s): 

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

It seems there is a problem with the attached data, it has zero size when I dl it.

 

But thanks for sharing the code, I'm collecting my own data now.

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


I have collected and analysed some data.

 

Raw data and moving average:

 

Histogram:

 

Histogram rejecting first 600 seconds (10 minutes):

 

So my conclusion is the MCU in my specific environment (which is just a curiosity nano floating around connected to the laptop USB and a serial/USB adapter) took about 10 minutes to warm-up and stabilize, after that I get a seemingly perfect bell shaped distribution of data centered at about 9771.5 counts.

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

Data re-uploaded.   Nice plots.  Thanks for the analysis.   Note that the equation is being processed once per second.  If you change the code to run it every capture it should converge more quickly.

 

I had my nano on plugged into my laptop and saw drift over an hour.  I assume that is thermal.  (Heat wave here w/ ac vent overhead.)