tone.cpp TODO DUEless

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