0

How to use a Counter on Feather M0 to Count Signal from Pin
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

How to use a Counter on Feather M0 to Count Signal from Pin

by LuckyResistor on Sun Mar 12, 2017 8:17 pm

Hi

I got currently a little stuck with something very simple. I would like to measure the frequency of a rectangle signal on one pin of a Adafruit Feather M0 (M0 Basic Proto). The signal is connected to Pin 6 (PA20).

My idea is to use one of the counters of the ATSAMD21G18 to count each raising edge. So I can just wait for e.g. 10ms and can easily read the frequency from the counter. I already did this for the same signal with a ATtiny13 and it worked perfectly.

The biggest problem I have, is I do not really know how the Feather M0 is configured for the Arduino IDE. I have no clue if I need to set the clock for the timer first, or if there is any initialisation I missed. I could write the project in Atmel Studio, but I would like to keep it simple, so other people can modify it using the Arduino IDE.

My current setup code for using TC4 to count the signal at PA20 looks like this:

Code: Select all | TOGGLE FULL SIZE
void setupCounter()
{
  auto counter = &(TC4->COUNT16);
  // Diable the counter.
  counter->CTRLA.bit.ENABLE = 0;
  while (counter->STATUS.bit.SYNCBUSY == 1) {}
  // 16bit counter mode
  counter->CTRLA.bit.PRESCSYNC = 0;
  counter->CTRLA.bit.RUNSTDBY = 1;
  counter->CTRLA.bit.PRESCALER = 0;
  counter->CTRLA.bit.WAVEGEN = 0;
  counter->CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val;
  // Increment counter on event.
  counter->EVCTRL.reg = 0;
  counter->EVCTRL.bit.TCEI = 1;
  counter->EVCTRL.bit.EVACT = TC_EVCTRL_EVACT_COUNT_Val;
  // Interrupts
  counter->INTENCLR.reg = TC_INTENCLR_MC0|TC_INTENCLR_MC1|TC_INTENCLR_SYNCRDY|TC_INTENCLR_ERR|TC_INTENCLR_OVF;
  // Read
  counter->READREQ.reg = TC_READREQ_RCONT|TC_READREQ_ADDR(TC_COUNT16_COUNT_OFFSET);
  // Enable the counter.
  counter->CTRLA.bit.ENABLE = 1;

  // Configure events for the counter:
  // Select channel 0 (n+1) for user TC4
  EVSYS->USER.bit.CHANNEL = 1; // Channel 0
  EVSYS->USER.bit.USER = 0x16; // TC4
  EVSYS->CHANNEL.bit.CHANNEL = 0; // Channel 0
  EVSYS->CHANNEL.bit.PATH = EVSYS_CHANNEL_PATH_SYNCHRONOUS_Val; // Synchronous
  EVSYS->CHANNEL.bit.EDGSEL = EVSYS_CHANNEL_EDGSEL_RISING_EDGE_Val; // On rising edge.
  EVSYS->CHANNEL.bit.EVGEN = 0x10; // External interrupt 4, PA20

  // Now enable the external interrupt on PA20
  EIC->EVCTRL.bit.EXTINTEO4 = 1; // Enable an event for this pin.
  EIC->INTENCLR.bit.EXTINT4 = 0; // No interrupt for this pin.
  EIC->CONFIG[0].bit.SENSE4 = EIC_CONFIG_SENSE4_RISE_Val; // Event on rise.
  EIC->CONFIG[0].bit.FILTEN4 = 1; // Enable filtering.
  EIC->CTRL.bit.ENABLE = 1; // Enable the event system.
}


My idea is, to use event channel 0 for a event on the raising edge on this pin. Each event should increase the counter.
The code to read the counter looks more or less like this:

Code: Select all | TOGGLE FULL SIZE
counter->COUNT.bit.COUNT = 0;
delay(10);
const uint16_t currentFrequency = counter->COUNT.bit.COUNT;


If I read the state of the pin, using `digitalRead(6)`, I can "see" this rectangle signal and can easily sample it. The frequency of the signal is somewhere between 60-120kHz and should be easy to capture. With the current code, the counter stays at zero all the time.

So, what did I miss? Or what did I wrong?

Any help is welcome!

LuckyResistor
 
Posts: 23
Joined: Fri Jan 30, 2015 7:52 pm

Re: How to use a Counter on Feather M0 to Count Signal from

by LuckyResistor on Tue Mar 14, 2017 9:13 am

Meanwhile I found out how to achieve this, I just like to share the solution in case someone finds this forum post. The documentation for the SAM D21 microcontroller lacks a clear documentation about the nature of the "events" in the event system. I tried to use the edge detection at the external interrupt peripheral, or in the event system, but it seems it only works if everything smart is disabled.

This is the code to setup the counter and read/reset it. It uses TCC2 as counter and PA07 with EXTINT7 as input. I removed all comments to make the code shorter.

Code: Select all | TOGGLE FULL SIZE
auto counter = TCC2;

void setupCounter()
{
  PM->APBCMASK.bit.TCC2_ = 1;
  PM->APBCMASK.bit.EVSYS_ = 1;
  PM->APBAMASK.bit.EIC_ = 1;

  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN|GCLK_CLKCTRL_GEN_GCLK0|GCLK_CLKCTRL_ID(GCM_EIC);
  while (GCLK->STATUS.bit.SYNCBUSY) {}
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN|GCLK_CLKCTRL_GEN_GCLK0|GCLK_CLKCTRL_ID(GCM_TCC2_TC3);
  while (GCLK->STATUS.bit.SYNCBUSY) {}
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN|GCLK_CLKCTRL_GEN_GCLK0|GCLK_CLKCTRL_ID(GCM_EVSYS_CHANNEL_0);
  while (GCLK->STATUS.bit.SYNCBUSY) {} 

  PORT->Group[0].PMUX[7/2].bit.PMUXO = MUX_PA07A_EIC_EXTINT7;
  PORT->Group[0].PINCFG[7].bit.DRVSTR = 0;
  PORT->Group[0].PINCFG[7].bit.PULLEN = 0;
  PORT->Group[0].PINCFG[7].bit.INEN = 1;
  PORT->Group[0].PINCFG[7].bit.PMUXEN = 1;

  EVSYS->USER.reg = EVSYS_USER_USER(EVSYS_ID_USER_TCC2_EV_0)|EVSYS_USER_CHANNEL(2);
  while (!EVSYS->CHSTATUS.bit.USRRDY1) {};
  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS|EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_7)|EVSYS_CHANNEL_CHANNEL(1);
  while (EVSYS->CHSTATUS.bit.CHBUSY1) {};

  EIC->CTRL.bit.SWRST = 1;
  while (EIC->STATUS.bit.SYNCBUSY == 1) {}
  EIC->CTRL.bit.ENABLE = 0;
  EIC->EVCTRL.bit.EXTINTEO7 = 1;
  EIC->CONFIG[0].bit.SENSE7 = EIC_CONFIG_SENSE7_HIGH_Val;
  while (EIC->STATUS.bit.SYNCBUSY == 1) {}
  EIC->CTRL.bit.ENABLE = 1;
  while (EIC->STATUS.bit.SYNCBUSY == 1) {}

  counter->CTRLA.bit.SWRST = 1;
  while (counter->SYNCBUSY.bit.SWRST == 1) {}
  counter->CTRLA.bit.ENABLE = 0;
  counter->CTRLA.bit.CPTEN0 = 1;
  counter->CTRLA.bit.CPTEN1 = 1;
  counter->EVCTRL.bit.TCEI0 = 1;
  counter->EVCTRL.bit.EVACT0 = TCC_EVCTRL_EVACT0_COUNTEV_Val;
  counter->CTRLA.bit.ENABLE = 1;
  while (counter->SYNCBUSY.bit.ENABLE == 1) {}
}

void resetCounter()
{
  counter->COUNT.bit.COUNT = 0;
  while (counter->SYNCBUSY.bit.COUNT == 1) {}
}

uint16_t readCounter()
{
  counter->CTRLBSET.bit.CMD = TCC_CTRLBSET_CMD_READSYNC_Val;
  while (counter->SYNCBUSY.bit.COUNT == 1) {}
  const uint16_t value = counter->COUNT.bit.COUNT;
  return value;
}


Basically I first enable the clocks for all components and enable them using the power manager. Next I configure the pin for EXTINT7.
Before I setup the external interrupt controller and timer, I first setup the event manager and configure channel 1 to asynchronously pass the event from EXTINT7 to the first event input of TCC2.
Now I setup the external interrupt controller to send an event if PA07 is high - everything else will not work.
Next I configure TCC2 to count on each event.

Be aware I use software reset of these peripherals before I configure them, and they lose all previous settings. I do this, because I like to make sure there is no other configuration present in them and I start from the reset values.

If you have low frequencies, TCC2 would also support direct capture of the pulse length and signal ratio (PWM) - so you could read the "frequency" of the signal directly from the capture register. I use counting, because the frequency can be really high and this way I can determine the frequency as precise I like.

LuckyResistor
 
Posts: 23
Joined: Fri Jan 30, 2015 7:52 pm

Re: How to use a Counter on Feather M0 to Count Signal from

by PaulRowntree on Sat Mar 18, 2017 12:20 pm

Well, just a few days later ... somebody DID find your code! You have just saved me an enormous amount of time! Thank you!
I want to build a flow sensor using hall effect switches, and it looks like your code is just what I need.


Q : are the pointers like counter, GCLK, PM, PORT etc all globally accessible, or are there includes I need ? Just starting down this path, and new to the MO ..

Cheers!

PaulRowntree
 
Posts: 375
Joined: Sun Apr 03, 2016 12:41 am

Re: How to use a Counter on Feather M0 to Count Signal from

by LuckyResistor on Sat Mar 18, 2017 1:14 pm

These are global pointers, defines in the `CMSIS` package from Atmel. Most of the headers are already included in the Arduino environment, but if you need special ones, like the `tc.h`, you have to include it separately. I did not found exactly how the proper way doing this for the Arduino IDE would be, I just included the absolute path to the header.

The example code should compile without special header includes. It only requires the correct SAM D21 support via the Adafruit libraries.

I found all the headers for the Feather M0 Proto (Atmel SAM D21 G18) in this path:
`.../packages/arduino/tools/CMSIS-Atmel/1.1.0/CMSIS/Device/ATMEL/samd21/include`

LuckyResistor
 
Posts: 23
Joined: Fri Jan 30, 2015 7:52 pm

Re: How to use a Counter on Feather M0 to Count Signal from

by msloat450 on Fri Jan 25, 2019 12:47 pm

I think you might be doing something that I am trying to do. Maybe you can help. I have a feather MO adalogger which is the same as thing, I think. Sorry, I am new to Arduino.

I am having issues with getting the right frequency. I have my input pin a A5. and I have a ttl logic signal going there. As my signal goes up (about 500hz) the micro will lock up. I was wondering is someone here can help.

msloat450
 
Posts: 15
Joined: Mon Jan 15, 2018 11:01 pm

Re: How to use a Counter on Feather M0 to Count Signal from

by jdelcamp11 on Sat Aug 24, 2019 6:36 pm

I have modified Luckey Resistor's code above to allow using almost all pins (pass the desired pin in the call).
I could not get the code for pins 5, A1, and A2 to compile and commented out the offending lines.
The rest all work. But my goal was to have it count while sleeping. So I added the sleep functionality (and display counts on OLED because the serial port gets disabled for sleep). The problem is it does not count while sleeping. It does count during the wake period.
To be honest my understanding of this code is pretty rudimentary.
Per the datasheet counting while the processor sleeps is possible. But I don't know where to enable this. My guess is TCC2 is disabled by default during sleep. Or possibly somewhere in his code he is turning this peripheral off.

Does anyone know how to count pulses while sleeping the MCU?
Here is my code:
Code: Select all | TOGGLE FULL SIZE
    /* based on code from Luckey Resistor */
    /* pin 5  PA15 eint15  does not compile
     * pin 6  PA20 eint4
     * pin 9  PA07 eint7
     * pin 10 PA18 eint2
     * pin 11 PA16 eint0
     * pin 12 PA19 eint3
     * pin 13 PA17 eint1 - not tested
     * A0 => 14 PA02 ent2
     * A1 => 15 PB08 eint8  note port B  does not compile
     * A2 => 16 PB09 eint9  note port B  does not compile
     * A3 => 17 PA04 eint4
     * A4 => 18 PA05 eint5
     * A5 => 19 PB02 eint2  note port B  - works 
   */
   
  #define BotName "SleepTest"
  #define Version "08/24/19"
 
  #define pin 6        // pin to attach to counter
  int counts;
  auto counter = TCC2;
 
  #include <SPI.h>
  #include <Wire.h>
  #include <Adafruit_GFX.h>
  #include <Adafruit_SSD1306.h>
  Adafruit_SSD1306 display = Adafruit_SSD1306();
    // 32u4, M0, M4, and 328p
  #define BUTTON_A 9
  #define BUTTON_B 6
  #define BUTTON_C 5
  char cmd, LCmd  = '_';
  bool slpFlag = 0;  // 1=enable 0=disable   sleep mode   send "B" to toggle 
 
  #include <RTCZero.h>
  RTCZero rtc;   
  long actTime, lastButton = 0 ; // unix time - actTime is current time,
//  end of clock section   ****/

//========================================================
void setup()
 {
  Serial.begin(38400);    // Send data back on the Zero's native port
  //while(!Serial);         // Wait for the Serial port to be ready
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  Serial.print("BotName "); Serial.println(BotName);
  Serial.print("Version "); Serial.println(Version);
  pinMode(pin, INPUT_PULLUP);
 
//****  START RTC   *********
  rtc.begin();
 
  //=====  setup oled ========================================= 
Serial.println("OLED begin");
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32)
pinMode(BUTTON_A, INPUT_PULLUP);
pinMode(BUTTON_B, INPUT_PULLUP);
pinMode(BUTTON_C, INPUT_PULLUP);
  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
display.display();
delay(1000);
  // Clear the buffer.
display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
  // display startup info
display.setCursor(0,0);
display.print("BotName "); display.println(BotName);
display.print("Version "); display.println(Version);
display.display(); // actually display this   

//=====  setup counter   ============================== 
  setupCounter(pin);
  resetCounter();
 }
 
//========================================================
void loop()
 {
  actTime = rtc.getEpoch();
 
    //****  is it time to read sensors group 1
  static unsigned long prevSensor;
  const int sensorFreq = 15;  //seconds
  if(actTime - prevSensor > sensorFreq )
  {
    Serial.print("Time "); Serial.print(actTime); Serial.print("\t");
    prevSensor = actTime;     
    counts = readCounter();
    Serial.print("Pin "); Serial.print(pin); Serial.print("\t");
    Serial.print("TCC2 count: "); Serial.println(counts);  // Print the result 
    //Serial.println(REG_TCC2_COUNT, DEC);   // Print the result
    resetCounter();
 
      // Clear the Oled buffer and deisplay new data
    display.clearDisplay();
    display.display(); 
    display.setCursor(0,0);
    display.print("Pin ");
    display.print(pin);
    display.print("  counts = ");
    display.println(counts);
    delay(10);
    yield();
    display.display();
  }
  if((actTime - lastButton) > 2 )
    { 
     if (! digitalRead(BUTTON_A)) {lastButton = actTime; decode('A');}
     if (! digitalRead(BUTTON_B)) {lastButton = actTime; decode('B');}
     if (! digitalRead(BUTTON_C)) {lastButton = actTime; decode('C');}         
    }
  if (slpFlag)
     { sleepnow();
       wake(); }
 } // end of loop

//======================================================
//                Subroutines
//======================================================
//*******************************************
void alarmMatch()   // Do something when timer interrupt called
  { rtc.disableAlarm(); }
  // will restart loop from sleepnow()

void wake()
 {
   digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on
   SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
   USBDevice.attach();
   Serial.begin(38400);
   delay(2000); // give time for Serial to startup   
 }
 
void sleepnow()
  {
    digitalWrite(LED_BUILTIN, LOW);  // turn the LED off
    Serial.println(F("\r\nGoing to sleep - close comm window"));
    Serial.println(F("Reopen comm window when LED comes on"));
       
    rtc.setAlarmSeconds(30);       // Wakes on the 30th second of the minute NOT every 30 secs!
    rtc.enableAlarm(rtc.MATCH_SS); // Match seconds only
    rtc.attachInterrupt(alarmMatch);
    USBDevice.detach();     // Safely detach the USB prior to sleeping
    delay(20);              // give time for everything to finish
    SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
    rtc.standbyMode();      // Sleep until next alarm match
  }

//========================================================
void setupCounter(int Pin)
    {
      PM->APBCMASK.bit.TCC2_ = 1;
      PM->APBCMASK.bit.EVSYS_ = 1;
      PM->APBAMASK.bit.EIC_ = 1;

      GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |
                          GCLK_CLKCTRL_GEN_GCLK0 |
                          GCLK_CLKCTRL_ID(GCM_EIC);
      while (GCLK->STATUS.bit.SYNCBUSY) {}
      GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |
                          GCLK_CLKCTRL_GEN_GCLK0 |
                          GCLK_CLKCTRL_ID(GCM_TCC2_TC3);
      while (GCLK->STATUS.bit.SYNCBUSY) {}
      GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |
                          GCLK_CLKCTRL_GEN_GCLK0 |
                          GCLK_CLKCTRL_ID(GCM_EVSYS_CHANNEL_0);
      while (GCLK->STATUS.bit.SYNCBUSY) {}
   
    // using referenced pin  -- this works
      PORT->Group[0].PMUX[7/2].bit.PMUXO = MUX_PA07A_EIC_EXTINT7;
      PORT->Group[g_APinDescription[Pin].ulPort].PINCFG[g_APinDescription[Pin].ulPin].bit.DRVSTR = 0;
      PORT->Group[g_APinDescription[Pin].ulPort].PINCFG[g_APinDescription[Pin].ulPin].bit.PULLEN = 0;
      PORT->Group[g_APinDescription[Pin].ulPort].PINCFG[g_APinDescription[Pin].ulPin].bit.INEN = 1;
      PORT->Group[g_APinDescription[Pin].ulPort].PINCFG[g_APinDescription[Pin].ulPin].bit.PMUXEN = 1;
   
      EVSYS->USER.reg = EVSYS_USER_USER(EVSYS_ID_USER_TCC2_EV_0) |
                        EVSYS_USER_CHANNEL(2);
      while (!EVSYS->CHSTATUS.bit.USRRDY1) {};
   
 //------------------------------------------------------------------
 //            pin specific code here
 //------------------------------------------------------------------     
 // pin9  PA07   eint7
      if (Pin == 9) {
      EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                           EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_7)|
                           EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO7 = 1;
      EIC->CONFIG[0].bit.SENSE7 = EIC_CONFIG_SENSE7_HIGH_Val;       
      }
   
   // pin 6  PA20 eint4
      if (Pin == 6) {
      EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                           EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_4)|
                           EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO4 = 1;
      EIC->CONFIG[0].bit.SENSE4 = EIC_CONFIG_SENSE4_HIGH_Val;       
      }
   
   // pin 5  PA15 eint15  This does not compile
   if (Pin == 5) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_15)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO15 = 1;
      //EIC->CONFIG[0].bit.SENSE15 = EIC_CONFIG_SENSE15_HIGH_Val;       
      }
 //* pin 10 PA18 eint2 
   if (Pin == 10) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_2)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO2 = 1;
      EIC->CONFIG[0].bit.SENSE2 = EIC_CONFIG_SENSE2_HIGH_Val;       
      }
 // pin 11 PA16 eint0
   if (Pin == 11) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_0)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO0 = 1;
      EIC->CONFIG[0].bit.SENSE0 = EIC_CONFIG_SENSE0_HIGH_Val; 
      }
       
 // pin 12 PA19 eint3
   if (Pin == 12) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_3)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO3 = 1;
      EIC->CONFIG[0].bit.SENSE3 = EIC_CONFIG_SENSE3_HIGH_Val;       
      }
 // A0 => 14 PA02 ent2
   if (Pin == 14) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_2)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO2 = 1;
      EIC->CONFIG[0].bit.SENSE2 = EIC_CONFIG_SENSE2_HIGH_Val;       
      }
// A0 => 14 PA02 ent2
   if (Pin == 14) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_2)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO2 = 1;
      EIC->CONFIG[0].bit.SENSE2 = EIC_CONFIG_SENSE2_HIGH_Val;       
      }
 // A1 => 15 PB08 ent8    this does not compile  *******************
   if (Pin == 15) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_8)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO8 = 1;
      //EIC->CONFIG[0].bit.SENSE8 = EIC_CONFIG_SENSE8_HIGH_Val;       
      }
 // A2 => 16 PB09 ent9   this does not compile  *********************
   if (Pin == 16) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_9)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO9 = 1;
      //EIC->CONFIG[0].bit.SENSE9 = EIC_CONFIG_SENSE9_HIGH_Val;       
      }
 // A3 => 17 PA04 ent4
   if (Pin == 17) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_4)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO4 = 1;
      EIC->CONFIG[0].bit.SENSE4 = EIC_CONFIG_SENSE4_HIGH_Val;       
      }
 // A4 => 18 PA05 ent5
   if (Pin == 18) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_5)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO5 = 1;
      EIC->CONFIG[0].bit.SENSE5 = EIC_CONFIG_SENSE5_HIGH_Val;       
      }
 // A5 => 19 PB02 ent2
   if (Pin == 19) {
       EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |
                            EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_2)|
                            EVSYS_CHANNEL_CHANNEL(1);                     
      while (EVSYS->CHSTATUS.bit.CHBUSY1) {};
      EIC->CTRL.bit.SWRST = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 0;
      EIC->EVCTRL.bit.EXTINTEO2 = 1;
      EIC->CONFIG[0].bit.SENSE2 = EIC_CONFIG_SENSE2_HIGH_Val;       
      }
       
  //---------------------------------------------------------     
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}
      EIC->CTRL.bit.ENABLE = 1;
      while (EIC->STATUS.bit.SYNCBUSY == 1) {}

      counter->CTRLA.bit.SWRST = 1;
      while (counter->SYNCBUSY.bit.SWRST == 1) {}
      counter->CTRLA.bit.ENABLE = 0;
      counter->CTRLA.bit.CPTEN0 = 1;
      counter->CTRLA.bit.CPTEN1 = 1;
      counter->EVCTRL.bit.TCEI0 = 1;
      counter->EVCTRL.bit.EVACT0 = TCC_EVCTRL_EVACT0_COUNTEV_Val;
      counter->CTRLA.bit.ENABLE = 1;
      while (counter->SYNCBUSY.bit.ENABLE == 1) {}
    }
   
//========================================================
    void resetCounter()
    {
      counter->COUNT.bit.COUNT = 0;
      while (counter->SYNCBUSY.bit.COUNT == 1) {}
    }

//========================================================
    uint16_t readCounter()
    {
      counter->CTRLBSET.bit.CMD = TCC_CTRLBSET_CMD_READSYNC_Val;
      while (counter->SYNCBUSY.bit.COUNT == 1) {}
      const uint16_t value = counter->COUNT.bit.COUNT;
      return value;
    }

//*======================================================
void decode(char a)
  {
    LCmd = a;
    display.setCursor(0,12);
    //Serial.print("lastButton "); Serial.println(lastButton);
    switch(a) // Perform an action depending on the character received
     {     
       // special controls
       case 'A':         
             display.println("Button A");
             Serial.println("Button A");
             break;       
       case 'B': //           
             display.println("Button B");             
             Serial.println("Button B");
             break;       
       case 'C': // toggle sleep flag
             display.println("Button C");
             Serial.println("Button C");
             slpFlag=!slpFlag;             
             break;       
       default: //do nothing;
           break;
    } // ** end of decode switch case  ****   
    delay(10);
    display.display();
} // ** end of decode       ****/


jdelcamp11
 
Posts: 155
Joined: Mon Nov 17, 2014 12:30 am

Re: How to use a Counter on Feather M0 to Count Signal from

by adafruit_support_mike on Sun Aug 25, 2019 2:51 pm

You can use the Event system and DMA to pass data from one peripheral to another, but the TC and TCC peripherals don’t count events or signals. They can capture a timestamp when a signal occurs, or measure the time between signals, but that’s all.

For an arbitrary counter, you pretty much need the ALU.

adafruit_support_mike
 
Posts: 58856
Joined: Thu Feb 11, 2010 2:51 pm

Re: How to use a Counter on Feather M0 to Count Signal from

by jdelcamp11 on Sun Aug 25, 2019 3:57 pm

I appreciate your response. But as I mentioned, I don't really have a grasp on the hardware code level.
As best I can understand from your response the TC and TCC can't or shouldn't be used for counters.
Sorry but I did not really understand any of the rest of your response.
Do you know of any examples / tutorials for counting asyncronous pulses on the SAMD21 while sleeping?

jdelcamp11
 
Posts: 155
Joined: Mon Nov 17, 2014 12:30 am

Re: How to use a Counter on Feather M0 to Count Signal from

by LuckyResistor on Sun Aug 25, 2019 4:44 pm

But my goal was to have it count while sleeping. So I added the sleep functionality (and display counts on OLED because the serial port gets disabled for sleep). The problem is it does not count while sleeping. It does count during the wake period.
To be honest my understanding of this code is pretty rudimentary.


It is theoretically possible, but very complicated. If you put the MCU into sleep mode, in the default configuration, it also stops the bus clocks. So, putting the MCU into the "Standby" mode will also stop the processing of the timers and pin interrupts.

You can only put the MCU into one of the "Idle" modes, this will save some power but keeps the clocks running, and therefore let the timers process the input. If you look in the specification (complete), read chapter PM about the power manager. There you find table 16-4 with the different sleep modes and which clocks are running.

So, to keep it short, either let the MCU running, which is the simplest way – or, get the required understanding of the direct hardware programming. You need to either use a different sleep mode, or you have to enable the clock on-demand system. In both cases, you need to understand how to directly configure the MCU.

LuckyResistor
 
Posts: 23
Joined: Fri Jan 30, 2015 7:52 pm

Re: How to use a Counter on Feather M0 to Count Signal from

by jdelcamp11 on Sun Aug 25, 2019 5:46 pm

Thanks. This helps me understand.
I think I will go to a backup plan and use a low-power processor such as attiny85 to handle the pulse counting and just query it for the data by I2C each wake cycle.

jdelcamp11
 
Posts: 155
Joined: Mon Nov 17, 2014 12:30 am

Re: How to use a Counter on Feather M0 to Count Signal from

by LuckyResistor on Sun Aug 25, 2019 5:57 pm

I remember I saw some dedicated pulse counting chips, with I2C interface. If you like to conserve power, you may use one of these.

LuckyResistor
 
Posts: 23
Joined: Fri Jan 30, 2015 7:52 pm

Please be positive and constructive with your questions and comments.