How to generate interrupts from QRK_PWM timer?

I’d like to use one of the four Quark timers to generate a periodic interrupt (in this case, at 8KHz, y’know, for audio). While I can get the timer to generate a nice square wave at the desired frequency, I can’t get it to actually generate an interrupt.

Context
I’m creating a PWM DAC for audio. This requires two timers: a PWM timer to create the PWM signal, and a sample clock that tics once every sample period (e.g. 44100Hz or 8000Hz). The sample clock needs to generate an interrupt, and the interrupt routine feeds the next sample to the PWM timer.

I have created a version of this that uses a Quark timer (in the Quark/x86 core) for PWM and CurieTimerOne (i.e. one of the timers in the ARC core) for the sample clock. It works great until someone tries to use the Servo library or any of the other libraries that commandeer CurieTimerOne.

For this reason, I’d like to avoid the using the ARC-based timers (e.g CurieTimerOne) and instead generate my sample clock using a Quark/x86-based PWM timers.

What I’ve NOT tried
I have not enabled interrupts on the Quark timer per se (i.e. I am setting QRK_PWM_CONTROL_INT_MASK in the timer control register), simply because if I DID enable interrupts, I don’t know where the interrupt would vector to and how to direct it to my ISR.

What I HAVE tried
I tried calling attachInterrupt(digitalPinToInterrupt(TIMER_PIN), timerIsr, RISING) on the pin to which the timer is connected. This doesn’t result in calls to timerIsr. It doesn’t surprise me: I suspect a pin needs to be configured as an input in order to generate interrupts.

So I’m stumped.

The following code generates a nice square wave on pin 3 at the desired frequency. But evidently it does not call the timerIsr routine (i.e. the onboard LED doesn’t flash).

#include <Arduino.h>
#include <aux_regs.h>
#include <interrupt.h>
#include <conf.h>
#include <scss_registers.h>

// ================================================================
// Globals

// set to one of four Quark timers in setup_timer
uint32_t g_timerChan;

// incremented in ISR
uint32_t g_timerCount = 0;

// ================================================================
// Constants

const int SAMPLE_RATE = 8192;

// This will be our output pin.  Possible choices: [3, 5, 6, 9]
const int TIMER_PIN = 3;

// probably defined somewhere, but I couldn't find it
const uint32_t QRK_PWM_CLOCK_FREQ = 32000000;

void setup_io_pins() {
  // pinMode(TIMER_PIN, OUTPUT); // seems to have no effect.  use SET_PIN below...
  pinMode(LED_BUILTIN, OUTPUT);
}

// ================================================================
// ================================================================

void setup_timer(int pin) {
  PinDescription *p = &g_APinDescription[pin];
  uint32_t offset;

  // convert pin to channel: 3=>0, 5=>1, 6=>2, 9=>3  
  if (p->ulPwmChan == INVALID) {
    // Serial.println("Invalid pin number for TIMER.  Must be one of 3, 5, 6, 9");
    return;
  };
  g_timerChan = p->ulPwmChan;

  // configure for Timer mode, no interrupts, periodic, enabled.
  offset = (g_timerChan * QRK_PWM_N_REGS_LEN) + QRK_PWM_N_CONTROL;
  MMIO_REG_VAL(QRK_PWM_BASE_ADDR + offset) =
    // QRK_PWM_CONTROL_PWM_OUT |      ... not PWM mode (timer mode)
    QRK_PWM_CONTROL_INT_MASK |        // disable interrupts (for now)
    QRK_PWM_CONTROL_MODE_PERIODIC |   // user defined count
    QRK_PWM_CONTROL_ENABLE;           // enable timer

  // Set timer count.  (NB: SAMPLE_RATE * 2 since timer output toggles when count is reached)
  uint32_t count = QRK_PWM_CLOCK_FREQ / (SAMPLE_RATE * 2);
  offset = (g_timerChan * QRK_PWM_N_REGS_LEN) + QRK_PWM_N_LOAD_COUNT1;
  MMIO_REG_VAL(QRK_PWM_BASE_ADDR + offset) = count;

  // assign timer to an output pin
  SET_PIN_PULLUP(p->ulSocPin, 0);
  SET_PIN_MODE(p->ulSocPin, PWM_MUX_MODE);
  p->ulPinMode = PWM_MUX_MODE;
}

// ================================================================
// interrupts

// Toggle the onboard LED once every 4096 interrupts.
void timerIsr() {
  g_timerCount++;
  digitalWrite(LED_BUILTIN, (g_timerCount & 0x1000) ? 1 : 0);
};

void setup_interrupts() {
  attachInterrupt(digitalPinToInterrupt(TIMER_PIN), timerIsr, RISING);
}

// ================================================================
// ================================================================
// entry point

void setup() {
  setup_io_pins();
  setup_timer(TIMER_PIN);
  setup_interrupts();
}

void loop() {
  delay( 1000 );
}

Minor update: as a hack, I configured pin 4 as an input, connected pin 3 (the timer output) to it, and turned pin 4 into an interrupt source:

const int INTERRUPT_PIN = 4; // connect timer pin to interrupt pin!

void setup_io_pins() {
  pinMode(INTERRUPT_PIN, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);
}

void timerIsr() {
  g_timerCount++;
  digitalWrite(LED_BUILTIN, (g_timerCount & 0x1000) ? 1 : 0);
};

void setup_interrupts() {
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), timerIsr, RISING);
}

And now it calls my timerIsr.

But there must be a better way! :-\

Hi @rdpoor, have you tried the CurieTimerOne library? https://github.com/01org/corelibs-arduino101/blob/master/libraries/CurieTimerOne/examples/CurieTimer1Interrupt/CurieTimer1Interrupt.ino

Hi @enyquist:

I have successfully used the CurieTimerOne (which uses one of the two timers in the ARC subsystem). It works, but if I use it in my library function, it will be incompatible with the Servo library which also depends on CurieTimerOne.

So I'm looking for a way to NOT use CurieTimerOne to generate sample clock interrupts. And I've modified my original post to make that clear. Regardless, thanks for the suggestion!

OK, I see. Are you using the Arduino IDE? or are you using the Curie ODK intel.com/curieodk

Are you using the Arduino IDE? or are you using the Curie ODK intel.com/curieodk

Oho! I did not know about the Curie ODK until just now!

Since the crux of my question was essentially "how do I get a Quark interrupt to be serviced on an ARC sketch?", I can see that the M and Z trees may do exactly what I need, namely run code on the Quark core and not just the ARC core.

And thanks for the pointer: exploring the Curie ODK should keep me out of trouble for a few days... :slight_smile:

Hey There! Did you find a solution to the above? Preferably while still using the Arduino IDE? I found myself in the exact same situation. I have so far tried using:

 interrupt_connect(SOC_PWM_INTERRUPT, led_change);
 interrupt_enable(SOC_PWM_INTERRUPT);

To no avail.

Hi,

first of all, thank you rdpoor for this contribution, it worked perfectly for me!

But now i just updated to Core 2.0.2, which now generates an error:


error: assignment of member '_PinDescription::ulPinMode' in read-only object

p->ulPinMode = PWM_MUX_MODE;

^

exit status 1
assignment of member '_PinDescription::ulPinMode' in read-only object

Can anyone help with this error?

Thynk you very much in advance!

@FranzXF,

the _PinDescription struct was turned into a const object to save precious RAM for BLE central feature. To change the pinmuxing, you can now use

pinmuxMode[pin] = PWM_MUX_MODE;

Hello facchinm, thank you for the quick & informative reply!
(i noticed that the new release does consume a significant amount of memory, so saving it is a great idea!)