Well, I'm still DUE-less =(, but trying to figure out how to do a Tone.cpp hacking PWM timer code out of wiring_analog.c, but the tc.c sam routines don't give any functions for enabling interrupts etc. so the following code has many TODOs, but maybe I can enjoy programming the DUE vicariously with this tease ...
/*
Tone generator
v1 use timer, and toggle any digital pin in ISR
funky duration from arduino version
TODO use FindMckDivisor?
timer selected will preclude using associated pins for PWM etc.
could also do timer/pwm hardware toggle where caller controls duration
*/
#include "Arduino.h"
#ifdef __cplusplus
extern "C" {
#endif
// 50% duty
#define TONE_DUTY 128
// timers TC0 TC1 TC2 channels 0 1 ids 0-2 3-5 6-8
#define TONE_TIMER TC0
#define TONE_CHNL 1
#define TONE_ID 1
static uint8_t pinEnabled[PINS_COUNT];
static uint8_t TCChanEnabled = 0;
static uint8_t pin_state = 1;
static Tc *chTC = TONE_TIMER;
static uint32_t chNo = TONE_CHNL;
volatile int32_t toggle_count;
static uint32_t tone_pin;
static void TC_SetCMR_ChannelA(Tc *tc, uint32_t chan, uint32_t v)
{
tc->TC_CHANNEL[chan].TC_CMR = (tc->TC_CHANNEL[chan].TC_CMR & 0xFFF0FFFF) | v;
}
static void TC_SetCMR_ChannelB(Tc *tc, uint32_t chan, uint32_t v)
{
tc->TC_CHANNEL[chan].TC_CMR = (tc->TC_CHANNEL[chan].TC_CMR & 0xF0FFFFFF) | v;
}
// frequency (in hertz) and duration (in milliseconds).
void tone(uint32_t ulPin, uint32_t frequency, int32_t duration)
{
// We use MCLK/2 as clock.
const uint32_t TC = VARIANT_MCK / 2 / frequency;
uint32_t duty = TONE_DUTY * TC / TC_MAX_DUTY_CYCLE;
tone_pin = ulPin;
toggle_count = 0; // strange wipe out previous duration
if (duration > 0 ) toggle_count = 2 * frequency * duration / 1000;
else toggle_count = -1;
uint32_t interfaceID = TONE_ID;
uint32_t chA = 1;
if (!TCChanEnabled) {
pmc_enable_periph_clk(TC_INTERFACE_ID + interfaceID);
// TODO enable interrupts and don't do PWM pin toggle
TC_Configure(chTC, chNo,
TC_CMR_TCCLKS_TIMER_CLOCK1 |
TC_CMR_WAVE | // Waveform mode
TC_CMR_WAVSEL_UP_RC | // Counter running up and reset when equals to RC
TC_CMR_EEVT_XC0 | // Set external events from XC0 (this setup TIOB as output)
TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR);
TC_SetRC(chTC, chNo, TC);
}
if (chA) {
TC_SetRA(chTC, chNo, duty);
TC_SetCMR_ChannelA(chTC, chNo, TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET);
} else {
TC_SetRB(chTC, chNo, duty);
TC_SetCMR_ChannelB(chTC, chNo, TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_SET);
}
if (!pinEnabled[ulPin]) {
pinMode(ulPin, OUTPUT);
pinEnabled[ulPin] = 1;
}
if (!TCChanEnabled) {
TC_Start(chTC, chNo);
TCChanEnabled = 1;
}
}
void noTone(uint32_t ulPin)
{
// TODO does this disable timer
if (TCChanEnabled) {
TC_Stop(chTC, chNo);
TCChanEnabled = 0;
}
digitalWrite(ulPin,LOW);
}
// timer ISR
void TC0_Handler ( void ) {
if (toggle_count != 0){
// toggle pin TODO better
digitalWrite(tone_pin,pin_state);
pin_state = 1 - pin_state;
if (toggle_count > 0) toggle_count--;
} else {
noTone(tone_pin);
}
}
#ifdef __cplusplus
}
#endif