Hi,
I am working on a project that requires an interrupt that is triggered regularly, every 2ms. I am using a SAMD51 board, the ItsyBitsyM4, which has plenty of TCs and TCCs. I have tried available libraries such as SAMD_TimerInterrupt and SAMD_InterruptTimer but since they both only have TC3 available for SAMD51 boards I have encountered a problem since TC3 is already used by project (or so I think from the testing I have done). I have connected to my ItsyBitsyM4 2 rotary encoder from Pololu which use the attachInterrupt function to keep track of the distance travelled by the robot. The rotary encoder sensor outputs are connected to pins 11, 4, 9 and 7, three of these pins also have access to the TC3 module through various channels eg 0,1,2.
The code below outputs counter values that keep on increasing and a DT value of 2000. However once I press the button and the motors begin to spin, DT decreases to 800 (to exactly 40% of the set interrupt time - I have tested this with other values such as 10000 and 4000). This I am guessing happens because the encoder interrupts are triggered which causes a change somewhere though I am not sure.
Motor_Drive.ino
#include <Arduino.h>
#include "config.hpp"
#include "sensors.hpp"
#include "motors.hpp"
#include "encoder.hpp"
#include "systick.hpp"
#include "movements.hpp"// Select only one to be true for SAMD21. Must must be placed at the beginning before #include "SAMDTimerInterrupt.h"
int buttonState = 0;
int startTime = 0;
uint32_t returnCode;
int switchOne = false;
int switchTwo = false;
void setup() {
Serial1.begin(38400);
Serial.begin(38400);
//systickSetup();
motorSetup();
ISRSetup();
//systickSetup();
encoderSetup();
//sensorSetup();
delay(250);
pinMode(switch1, INPUT);
pinMode(switch2, INPUT);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
digitalWrite(LED1, LOW);
digitalWrite(LED2, LOW);
}
void loop() {
switchOne = digitalRead(switch1);
switchTwo = digitalRead(switch2);
if(switchOne == HIGH){
digitalWrite(LED1, HIGH);
delay(1000);
resetEncoders();
setRightMotorVolts(3.0);
setLeftMotorVolts(3.0);
delay(1500);
digitalWrite(LED1, LOW);
stopMotors();
Serial1.println("\n1");
} else if(switchTwo == HIGH){
setLeftMotorVolts(3.0);
digitalWrite(LED2, HIGH);
delay(1500);
stopMotors();
digitalWrite(LED2, LOW);
Serial1.print("\n2");
}
if (updated) {
Serial.print("DT:");
Serial.print(dT);
Serial.print("\n");
noInterrupts();
updated = false;
interrupts();
}
}
Encoder ISR and setup
void encoderSetup(){
CriticalSection crit_sec;
pinMode(RENCA, INPUT);
pinMode(RENCB, INPUT);
attachInterrupt(digitalPinToInterrupt(RENCA), readEncoderRight, RISING);
pinMode(LENCA, INPUT);
pinMode(LENCB, INPUT);
attachInterrupt(digitalPinToInterrupt(LENCA), readEncoderLeft, RISING);
}
void readEncoderLeft(){
int outa = digitalRead(LENCA);
int outb = digitalRead(LENCB);
int leftEncoderState = (outa << 1) | outb;
if (leftEncoderState == 1 || leftEncoderState ==2 ){
encoderLeftCounter += ENCODER_LEFT_POLARITY;
} else if(leftEncoderState == 3 || leftEncoderState == 0 ){
encoderLeftCounter -= ENCODER_LEFT_POLARITY;
}
Serial.print("\nLeft");
Serial.print(encoderLeftCounter);
}
void readEncoderRight(){
int outa = digitalRead(RENCA);
int outb = digitalRead(RENCB);
int rightEncoderState = (outa << 1) | outb;
if(rightEncoderState == 1 || rightEncoderState == 2){
encoderRightCounter += ENCODER_RIGHT_POLARITY;
} else if(rightEncoderState == 3 || rightEncoderState == 0){
encoderRightCounter -= ENCODER_RIGHT_POLARITY;
}
Serial.print("\nRight");
Serial.print(encoderRightCounter);
}
The Interrupt using SAMD51_InterruptTimer
#include "sensors.hpp"
#include "samd51/include/samd51g19a.h"
#include "systick.hpp"
#include "encoder.hpp"
#include "motor_profile.hpp"
#include "motors.hpp"
#include <Arduino.h>
int counter = 0;
volatile bool toggle = true;
volatile bool updated = false;
volatile uint32_t dT = 0;
void ISRSetup(){
/*SysTick->CTRL = 0; // Disable the SysTick Module
SysTick->LOAD = 0x0003A97F; // Set the Reload Register for 2mS interrupts 239999
NVIC_SetPriority(SysTick_IRQn, 3); // Set the interrupt priority to least urgency
SysTick->VAL = 0; // Clear the Current Value register
SysTick->CTRL = 0x00000007; // Enable SysTick, Enable SysTick Exceptions, Use CPU Clock
NVIC_EnableIRQ(SysTick_IRQn);*/ //Enable SysTick interrupts globally
TC.startTimer(1000, myISR);
}
void myISR(){
updateEncoders();
uint32_t now = micros();
static uint32_t prev_tick = now;
dT = now - prev_tick;
prev_tick = now;
updated = true;
counter++;
Serial.print("counter: ");
Serial.println(counter);
}
Back to the main topic, to bypass this I want to use another TC or TCC on pin D12, as it is unused in my project. I have read the datasheet for the SAMD51 and have written some code although it seems that the interrupt never triggers as the Handler is never called and there is no output in the Serial Monitor. The code below shows a different sketch that is used to test the interrupt:
#include <Arduino.h>
volatile uint32_t interruptCount = 0;
void TCC0_3_Handler() {
Serial.println("HI");
if (TCC0->INTFLAG.bit.OVF) {
interruptCount++;
TCC0->INTFLAG.bit.OVF = 1;
}
}
void setupTCC0() {
TCC0->CTRLA.bit.ENABLE = 0;
//GCLK->PCHCTRL[TCC0_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | GCLK_PCHCTRL_CHEN;
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TCC0;
GCLK->PCHCTRL[TCC0_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK4 | GCLK_PCHCTRL_CHEN;
GCLK->PCHCTRL[TCC0_GCLK_ID].bit.GEN = 0x4;
TCC0->CTRLA.reg = TC_CTRLA_MODE_COUNT16_Val | TC_CTRLA_PRESCALER_DIV2;
TCC0->PER.reg = 96000; // F_CPU is the CPU frequency in Hz (160 MHz)
TCC0->INTENSET.bit.OVF = 1;
NVIC_EnableIRQ(TCC0_3_IRQn);
TCC0->CTRLA.bit.ENABLE = 1;
}
void setup() {
Serial.begin(9600);
setupTCC0();
__enable_irq();
}
void loop() {
if (interruptCount >= 1000) {
interruptCount = 0;
Serial.println(interruptCount);
// Perform your desired actions here
}
}
Sorry for the long post but I don't know how to explain my problem. In case this amount of code isn't comprehensive enough I will look into creating a GitHub repository.