Example #1
0
void IRsend::enableIROut(int khz) {
  // Enables IR output.  The khz value controls the modulation frequency in kilohertz.
  // The IR output will be on pin 3 (OC2B).
  // This routine is designed for 36-40KHz; if you use it for other values, it's up to you
  // to make sure it gives reasonable results.  (Watch out for overflow / underflow / rounding.)
  // TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B
  // controlling the duty cycle.
  // There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A)
  // To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin.
  // A few hours staring at the ATmega documentation and this will all make sense.
  // See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details.
  #if defined IR_USE_SAM
	//Disable write protection on the power management controller so we can enable timers and pwm later.
	pmc_set_writeprotect(false); 
  #endif
  
  // Disable the Timer2 Interrupt (which is used for receiving IR)
  TIMER_DISABLE_INTR; //Timer2 Overflow Interrupt
  
  #ifndef IR_USE_SAM
  pinMode(TIMER_PWM_PIN, OUTPUT);
  digitalWrite(TIMER_PWM_PIN, LOW); // When not sending PWM, we want it low
  #endif
  
  // COM2A = 00: disconnect OC2A
  // COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted
  // WGM2 = 101: phase-correct PWM with OCRA as top
  // CS2 = 000: no prescaling
  // The top value for the timer.  The modulation frequency will be SYSCLOCK / 2 / OCR2A.
  TIMER_CONFIG_KHZ(khz);
}
Example #2
0
void tone(uint32_t ulPin, uint32_t frequency, int32_t duration)
{
		const uint32_t rc = VARIANT_MCK / 256 / frequency; 
		tone_pin = ulPin;
		toggle_count = 0;  // strange  wipe out previous duration
		if (duration > 0 ) toggle_count = 2 * frequency * duration / 1000;
		 else toggle_count = -1;

		if (!TCChanEnabled) {
 			pmc_set_writeprotect(false);
			pmc_enable_periph_clk((uint32_t)TONE_IRQ);
			TC_Configure(chTC, chNo,
				TC_CMR_TCCLKS_TIMER_CLOCK4 |
				TC_CMR_WAVE |         // Waveform mode
				TC_CMR_WAVSEL_UP_RC ); // Counter running up and reset when equals to RC
	
			chTC->TC_CHANNEL[chNo].TC_IER=TC_IER_CPCS;  // RC compare interrupt
			chTC->TC_CHANNEL[chNo].TC_IDR=~TC_IER_CPCS;
			 NVIC_EnableIRQ(TONE_IRQ);
                         TCChanEnabled = 1;
		}
		if (!pinEnabled[ulPin]) {
			pinMode(ulPin, OUTPUT);
			pinEnabled[ulPin] = 1;
		}
		TC_Stop(chTC, chNo);
                TC_SetRC(chTC, chNo, rc);    // set frequency
		TC_Start(chTC, chNo);
}
Example #3
0
// initialization
void IRrecv::enableIRIn() {
  #if defined IR_USE_SAM
	//Disable write protection on the power management controller so we can enable timers and pwm later.
	pmc_set_writeprotect(false); 
  #endif
  
  #ifndef IR_USE_SAM
    cli();
  #endif
  // setup pulse clock timer interrupt
  //Prescale /8 (16M/8 = 0.5 microseconds per tick)
  // Therefore, the timer interval can range from 0.5 to 128 microseconds
  // depending on the reset value (255 to 0)
  TIMER_CONFIG_NORMAL();

  //Timer2 Overflow Interrupt Enable
  TIMER_ENABLE_INTR;

  TIMER_RESET;

  #ifndef IR_USE_SAM
  sei();  // enable interrupts
  #endif

  // initialize state machine variables
  irparams.rcvstate = STATE_IDLE;
  irparams.rawlen = 0;

  // set pin modes
  pinMode(irparams.recvpin, INPUT);
}
Example #4
0
void Platform::InitialiseInterrupts()
{
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk((uint32_t)TC3_IRQn);
  TC_Configure(TC1, 0, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4);
  TC1->TC_CHANNEL[0].TC_IER=TC_IER_CPCS;
  TC1->TC_CHANNEL[0].TC_IDR=~TC_IER_CPCS;
  SetInterrupt(STANDBY_INTERRUPT_RATE);
}
Example #5
0
// Constructor
DueTimer::DueTimer(int _timer){
	timer = _timer;

	// Initialize timer
	Timer t = Timers[timer];
	pmc_set_writeprotect(false);
	pmc_enable_periph_clk((uint32_t)Timers[timer].irq);
    TC_Configure(t.tc, t.channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4);
}
 /**************************************************************************
 Initialize the timer counter.
 **************************************************************************/
 void configure_tc_RightWheel(void)
 {
	 pmc_set_writeprotect(false);
	 pmc_enable_periph_clk(ID_TC7);
	 tc_init(TC2,1,TC_CMR_TCCLKS_TIMER_CLOCK3);		 /* TC2, channel 1, TCLK3 and capturemode */
	 tc_set_block_mode(TC2,1);
	 
	 
 }
Example #7
0
DueTimer& DueTimer::setFrequency(double frequency){
	/*
		Set the timer frequency (in Hz)
	*/

	// Prevent negative frequencies
	if(frequency <= 0) { frequency = 1; }

	// Remember the frequency — see below how the exact frequency is reported instead
	//_frequency[timer] = frequency;

	// Get current timer configuration
	Timer t = Timers[timer];

	uint32_t rc = 0;
	uint8_t clock;

	// Tell the Power Management Controller to disable 
	// the write protection of the (Timer/Counter) registers:
	pmc_set_writeprotect(false);

	// Enable clock for the timer
	pmc_enable_periph_clk((uint32_t)t.irq);

	// Find the best clock for the wanted frequency
	clock = bestClock(frequency, rc);

	switch (clock) {
	  case TC_CMR_TCCLKS_TIMER_CLOCK1:
	    _frequency[timer] = (double)VARIANT_MCK / 2.0 / (double)rc;
	    break;
	  case TC_CMR_TCCLKS_TIMER_CLOCK2:
	    _frequency[timer] = (double)VARIANT_MCK / 8.0 / (double)rc;
	    break;
	  case TC_CMR_TCCLKS_TIMER_CLOCK3:
	    _frequency[timer] = (double)VARIANT_MCK / 32.0 / (double)rc;
	    break;
	  default: // TC_CMR_TCCLKS_TIMER_CLOCK4
	    _frequency[timer] = (double)VARIANT_MCK / 128.0 / (double)rc;
	    break;
	}

	// Set up the Timer in waveform mode which creates a PWM
	// in UP mode with automatic trigger on RC Compare
	// and sets it up with the determined internal clock as clock input.
	TC_Configure(t.tc, t.channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | clock);
	// Reset counter and fire interrupt when RC value is matched:
	TC_SetRC(t.tc, t.channel, rc);
	// Enable the RC Compare Interrupt...
	t.tc->TC_CHANNEL[t.channel].TC_IER=TC_IER_CPCS;
	// ... and disable all others.
	t.tc->TC_CHANNEL[t.channel].TC_IDR=~TC_IER_CPCS;

	return *this;
}
/*------------------------------------------------------------------------------
Name:           configurePMC
parameters:     -
description:    initializes the Power Management controller
------------------------------------------------------------------------------*/
void BldcControl::configurePMC(void)
{
    pmc_set_writeprotect(false);
    
    /* enable PWM peripheral */
    pmc_enable_periph_clk(PWM_IRQn);
    
    /* enable PIO of inputs */
    pmc_enable_periph_clk(PIOA_IRQn);
    pmc_enable_periph_clk(PIOD_IRQn);
}
Example #9
0
void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t frequency) {
	pmc_set_writeprotect(false);
	pmc_enable_periph_clk((uint32_t)irq);
	TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4);
	uint32_t rc = VARIANT_MCK/128/frequency; //128 because we selected TIMER_CLOCK4 above
	tc->TC_CHANNEL[channel].TC_RA = rc/2;
	tc->TC_CHANNEL[channel].TC_RC = rc;
	TC_Start(tc, channel);
	tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS;
	tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS;
	NVIC_EnableIRQ(irq);
}
Example #10
0
void BaseDMD::begin()
{
  beginNoTimer(); // Do any generic setup

  NVIC_DisableIRQ(TC7_IRQn);
  register_running_dmd(this);
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk(TC7_IRQn);
  // Timer 7 is TC2, channel 1
  TC_Configure(TC2, 1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4); // counter up, /128 divisor
  TC_SetRC(TC2, 1, 2500); // approx 4ms at /128 divisor
  TC2->TC_CHANNEL[1].TC_IER=TC_IER_CPCS;

  NVIC_ClearPendingIRQ(TC7_IRQn);
  NVIC_EnableIRQ(TC7_IRQn);
  TC_Start(TC2, 1);
}
Example #11
0
void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
  Tc *tc = TimerConfig[timer_num].pTimerRegs;
  IRQn_Type irq = TimerConfig[timer_num].IRQ_Id;
  uint32_t channel = TimerConfig[timer_num].channel;

  pmc_set_writeprotect(false);
  pmc_enable_periph_clk((uint32_t)irq);
  NVIC_SetPriority(irq, TimerConfig [timer_num].priority);

  TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1);

  TC_SetRC(tc, channel, VARIANT_MCK / 2 / frequency);
  TC_Start(tc, channel);

  // enable interrupt on RC compare
  tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPCS;

  NVIC_EnableIRQ(irq);
}
Example #12
0
/*------------------------------------------------------------------------------
Name:           startTimer
parameters:     tc - timer counter
                channel - timer channel
                irq - isr request
                frequency - frequency of inetrrupts
description:    initializes timer for periodic interrupt generation
------------------------------------------------------------------------------*/
void BldcControl::configureTimerInterrupt(Tc *tc,
                                          uint32_t channel,
                                          IRQn_Type irq,
                                          uint32_t frequency)
{
        pmc_set_writeprotect(false);
        pmc_enable_periph_clk((uint32_t)irq);
        TC_Configure(tc,
                     channel,
                     TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC |
                        TC_CMR_TCCLKS_TIMER_CLOCK4);
        uint32_t rc = VARIANT_MCK/128/frequency; //128 because we selected
                                                 //TIMER_CLOCK4 above
        TC_SetRA(tc, channel, rc/2); //50% high, 50% low
        TC_SetRC(tc, channel, rc);
        TC_Start(tc, channel);
        tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS;
        tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS;
        NVIC_EnableIRQ(irq);
}
Example #13
0
// Set up all timer interrupts
void HAL::setupTimer() {
  uint32_t     tc_count, tc_clock;

  pmc_set_writeprotect(false);

  // set 3 bits for interrupt group priority, 1 bits for sub-priority
  //NVIC_SetPriorityGrouping(4);

#if USE_ADVANCE
  // Timer for extruder control
  pmc_enable_periph_clk(EXTRUDER_TIMER_IRQ);  // enable power to timer
  //NVIC_SetPriority((IRQn_Type)EXTRUDER_TIMER_IRQ, NVIC_EncodePriority(4, 4, 1));
  NVIC_SetPriority((IRQn_Type)EXTRUDER_TIMER_IRQ, 6);

  // count up to value in RC register using given clock
  TC_Configure(EXTRUDER_TIMER, EXTRUDER_TIMER_CHANNEL, TC_CMR_WAVSEL_UP_RC | TC_CMR_WAVE | TC_CMR_TCCLKS_TIMER_CLOCK3);

  TC_SetRC(EXTRUDER_TIMER, EXTRUDER_TIMER_CHANNEL, (F_CPU_TRUE / 32) / EXTRUDER_CLOCK_FREQ); // set frequency 43 for 60000Hz
  TC_Start(EXTRUDER_TIMER, EXTRUDER_TIMER_CHANNEL);           // start timer running

  // enable RC compare interrupt
  EXTRUDER_TIMER->TC_CHANNEL[EXTRUDER_TIMER_CHANNEL].TC_IER = TC_IER_CPCS;
  // clear the "disable RC compare" interrupt
  EXTRUDER_TIMER->TC_CHANNEL[EXTRUDER_TIMER_CHANNEL].TC_IDR = ~TC_IER_CPCS;

  // allow interrupts on timer
  NVIC_EnableIRQ((IRQn_Type)EXTRUDER_TIMER_IRQ);
#endif
  // Regular interrupts for heater control etc
  pmc_enable_periph_clk(PWM_TIMER_IRQ);
  //NVIC_SetPriority((IRQn_Type)PWM_TIMER_IRQ, NVIC_EncodePriority(4, 6, 0));
  NVIC_SetPriority((IRQn_Type)PWM_TIMER_IRQ, 15);

  TC_FindMckDivisor(PWM_CLOCK_FREQ, F_CPU_TRUE, &tc_count, &tc_clock, F_CPU_TRUE);
  TC_Configure(PWM_TIMER, PWM_TIMER_CHANNEL, TC_CMR_WAVSEL_UP_RC | TC_CMR_WAVE | tc_clock);

  TC_SetRC(PWM_TIMER, PWM_TIMER_CHANNEL, (F_CPU_TRUE / tc_count) / PWM_CLOCK_FREQ);
  TC_Start(PWM_TIMER, PWM_TIMER_CHANNEL);

  PWM_TIMER->TC_CHANNEL[PWM_TIMER_CHANNEL].TC_IER = TC_IER_CPCS;
  PWM_TIMER->TC_CHANNEL[PWM_TIMER_CHANNEL].TC_IDR = ~TC_IER_CPCS;
  NVIC_EnableIRQ((IRQn_Type)PWM_TIMER_IRQ);

  // Timer for stepper motor control
  pmc_enable_periph_clk(TIMER1_TIMER_IRQ );
  //NVIC_SetPriority((IRQn_Type)TIMER1_TIMER_IRQ, NVIC_EncodePriority(4, 7, 1)); // highest priority - no surprises here wanted
  NVIC_SetPriority((IRQn_Type)TIMER1_TIMER_IRQ,2); // highest priority - no surprises here wanted

  TC_Configure(TIMER1_TIMER, TIMER1_TIMER_CHANNEL, TC_CMR_WAVSEL_UP_RC |
               TC_CMR_WAVE | TC_CMR_TCCLKS_TIMER_CLOCK1);

  TC_SetRC(TIMER1_TIMER, TIMER1_TIMER_CHANNEL, (F_CPU_TRUE / TIMER1_PRESCALE) / TIMER1_CLOCK_FREQ);
  TC_Start(TIMER1_TIMER, TIMER1_TIMER_CHANNEL);

  TIMER1_TIMER->TC_CHANNEL[TIMER1_TIMER_CHANNEL].TC_IER = TC_IER_CPCS;
  TIMER1_TIMER->TC_CHANNEL[TIMER1_TIMER_CHANNEL].TC_IDR = ~TC_IER_CPCS;
  NVIC_EnableIRQ((IRQn_Type)TIMER1_TIMER_IRQ);

  // Servo control
#if FEATURE_SERVO
#if SERVO0_PIN > -1
  SET_OUTPUT(SERVO0_PIN);
  WRITE(SERVO0_PIN, LOW);
#endif
#if SERVO1_PIN > -1
  SET_OUTPUT(SERVO1_PIN);
  WRITE(SERVO1_PIN, LOW);
#endif
#if SERVO2_PIN > -1
  SET_OUTPUT(SERVO2_PIN);
  WRITE(SERVO2_PIN, LOW);
#endif
#if SERVO3_PIN > -1
  SET_OUTPUT(SERVO3_PIN);
  WRITE(SERVO3_PIN, LOW);
#endif
  pmc_enable_periph_clk(SERVO_TIMER_IRQ );
  //NVIC_SetPriority((IRQn_Type)SERVO_TIMER_IRQ, NVIC_EncodePriority(4, 5, 0));
  NVIC_SetPriority((IRQn_Type)SERVO_TIMER_IRQ,4);

  TC_Configure(SERVO_TIMER, SERVO_TIMER_CHANNEL, TC_CMR_WAVSEL_UP_RC |
               TC_CMR_WAVE | TC_CMR_TCCLKS_TIMER_CLOCK1);

  TC_SetRC(SERVO_TIMER, SERVO_TIMER_CHANNEL, (F_CPU_TRUE / SERVO_PRESCALE) / SERVO_CLOCK_FREQ);
  TC_Start(SERVO_TIMER, SERVO_TIMER_CHANNEL);

  SERVO_TIMER->TC_CHANNEL[SERVO_TIMER_CHANNEL].TC_IER = TC_IER_CPCS;
  SERVO_TIMER->TC_CHANNEL[SERVO_TIMER_CHANNEL].TC_IDR = ~TC_IER_CPCS;
  NVIC_EnableIRQ((IRQn_Type)SERVO_TIMER_IRQ);
#endif
}
Example #14
0
static int8_t toneBegin(uint8_t _pin)
{
  int8_t _timer = -1;

  // if we're already using the pin, the timer should be configured.  
  for (int i = 0; i < AVAILABLE_TONE_PINS; i++) {
    if (tone_pins[i] == _pin) {
      return pgm_read_byte(tone_pin_to_timer_PGM + i);
    }
  }
  
  // search for an unused timer.
  for (int i = 0; i < AVAILABLE_TONE_PINS; i++) {
    if (tone_pins[i] == 255) {
      tone_pins[i] = _pin;
      _timer = pgm_read_byte(tone_pin_to_timer_PGM + i);
      break;
    }
  }
  
  if (_timer != -1)
  {
    // Set timer specific stuff
    // All timers in CTC mode
    // 8 bit timers will require changing prescalar values,
    // whereas 16 bit timers are set to either ck/1 or ck/64 prescalar
    switch (_timer)
    {
      #if defined(TCCR0A) && defined(TCCR0B)
      case 0:
        // 8 bit timer
        TCCR0A = 0;
        TCCR0B = 0;
        bitWrite(TCCR0A, WGM01, 1);
        bitWrite(TCCR0B, CS00, 1);
        timer0_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer0_pin_mask = digitalPinToBitMask(_pin);
        break;
      #endif

      #if defined(TCCR1A) && defined(TCCR1B) && defined(WGM12)
      case 1:
        // 16 bit timer
        TCCR1A = 0;
        TCCR1B = 0;
        bitWrite(TCCR1B, WGM12, 1);
        bitWrite(TCCR1B, CS10, 1);
        timer1_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer1_pin_mask = digitalPinToBitMask(_pin);
        break;
      #endif

      #if defined(TCCR2A) && defined(TCCR2B)
      case 2:
        // 8 bit timer
        TCCR2A = 0;
        TCCR2B = 0;
        bitWrite(TCCR2A, WGM21, 1);
        bitWrite(TCCR2B, CS20, 1);
        timer2_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer2_pin_mask = digitalPinToBitMask(_pin);
        break;
      #endif

      #if defined(TCCR3A) && defined(TCCR3B) &&  defined(TIMSK3)
      case 3:
        // 16 bit timer
        TCCR3A = 0;
        TCCR3B = 0;
        bitWrite(TCCR3B, WGM32, 1);
        bitWrite(TCCR3B, CS30, 1);
        timer3_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer3_pin_mask = digitalPinToBitMask(_pin);
        break;
      #endif

      #if defined(TCCR4A) && defined(TCCR4B) &&  defined(TIMSK4)
      case 4:
        // 16 bit timer
        TCCR4A = 0;
        TCCR4B = 0;
        #if defined(WGM42)
          bitWrite(TCCR4B, WGM42, 1);
        #elif defined(CS43)
          #warning this may not be correct
          // atmega32u4
          bitWrite(TCCR4B, CS43, 1);
        #endif
        bitWrite(TCCR4B, CS40, 1);
        timer4_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer4_pin_mask = digitalPinToBitMask(_pin);
        break;
      #endif

      #if defined(TCCR5A) && defined(TCCR5B) &&  defined(TIMSK5)
      case 5:
        // 16 bit timer
        TCCR5A = 0;
        TCCR5B = 0;
        bitWrite(TCCR5B, WGM52, 1);
        bitWrite(TCCR5B, CS50, 1);
        timer5_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer5_pin_mask = digitalPinToBitMask(_pin);
        break;
      #endif

      #if defined(TC0) && defined(TC1) &&  defined(TC2)
      case 6:
        // 16 bit timer
        pmc_set_writeprotect(false);
        pmc_enable_periph_clk((uint32_t)TC3_IRQn);

        TC_Configure(TC1, 0,
            TC_CMR_TCCLKS_TIMER_CLOCK4 |
            TC_CMR_WAVE |          // Waveform mode
            TC_CMR_WAVSEL_UP_RC ); // Counter running up and reset when equals to RC

        TC1->TC_CHANNEL[0].TC_IER=TC_IER_CPCS;  // RC compare interrupt
        TC1->TC_CHANNEL[0].TC_IDR=~TC_IER_CPCS;
        NVIC_EnableIRQ(TC3_IRQn);
        timer6_pin_port = portOutputRegister(digitalPinToPort(_pin));
        timer6_pin_mask = digitalPinToBitMask(_pin);
        break;
      #endif
    }
  }

  return _timer;
}
Example #15
0
static void configure_time_trigger_for_ssc(uint32_t ssc_trigger_hz)
{
	
#if 0
	gpio_configure_pin(PIO_PA12_IDX, PIO_PERIPH_B);
	pmc_enable_periph_clk(ID_PWM);

	/* Disable PWM channels */
	pwm_channel_disable(PWM, PWM_CHANNEL_1);

	/* Set PWM clock A as PWM_FREQUENCY*PERIOD_VALUE (clock B is not used) */
	pwm_clock_t clock_setting = {
		.ul_clka = 1000 * 100,
		.ul_clkb = 0,
		.ul_mck = sysclk_get_cpu_hz()
	};
	pwm_init(PWM, &clock_setting);
	
	pwm_channel_t g_pwm_channel;

	/* Period is left-aligned */
	g_pwm_channel.alignment = PWM_ALIGN_LEFT;
	/* Output waveform starts at a low level */
	g_pwm_channel.polarity = PWM_LOW;
	/* Use PWM clock A as source clock */
	g_pwm_channel.ul_prescaler = PWM_CMR_CPRE_CLKA;
	/* Period value of output waveform */
	g_pwm_channel.ul_period = 100;
	/* Duty cycle value of output waveform */
	g_pwm_channel.ul_duty = 50;
	g_pwm_channel.channel = PWM_CHANNEL_1;
	pwm_channel_init(PWM, &g_pwm_channel);
	
	/* Disable channel counter event interrupt */
	pwm_channel_disable_interrupt(PWM, PWM_CHANNEL_1, 0);
	
	pwm_channel_enable(PWM, PWM_CHANNEL_1);
#endif


	
	uint32_t ul_div = 0;
	uint32_t ul_tc_clks = 0;
	uint32_t ul_sysclk = sysclk_get_cpu_hz();

	pmc_set_writeprotect(false);

	// Enable peripheral clock.
	pmc_enable_periph_clk(CONF_SSC_CLOCK_SOURCE_ID);

	// TIOA configuration 
	// gpio_configure_pin(PIN_TC0_TIOA0, PIN_TC0_TIOA0_FLAGS);
	// tc_set_writeprotect(TC2, true);
	tc_set_writeprotect(CONF_SSC_CLOCK_TC, false);

	// Configure TC for a 1Hz frequency and trigger on RC compare.
	tc_find_mck_divisor(ssc_trigger_hz, ul_sysclk, &ul_div, &ul_tc_clks, ul_sysclk);
	tc_init(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL, ul_tc_clks | TC_CMR_WAVSEL_UP_RC | TC_CMR_WAVE | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET);
	uint32_t tmp_val = (ul_sysclk / ul_div) / ssc_trigger_hz;
	
	tc_write_ra(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL, tmp_val / 2);
	tc_write_rc(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL, tmp_val);

	// Start the Timer.
	tc_start(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL);

}

void tm_stick_init(uint32_t bus_speed_hz) {
	if(g_tm_stick_data.mutex == NULL) {
		g_tm_stick_data.mutex =  xSemaphoreCreateMutex();
	}
	
	if(g_tm_stick_data.rtos_task_semaphore == NULL) {
		vSemaphoreCreateBinary(g_tm_stick_data.rtos_task_semaphore);
	}
	
	configure_time_trigger_for_ssc(1000);
	
	sysclk_enable_peripheral_clock(ID_SSC);
	ssc_reset(SSC);
	// Do not go over 1MHz, the pulse will be too long and extend over to the next clock's rising edge (The spec. sheet of 4021BCM does not really list the operation times of 3.3V operation.
	// I tested a couple of speeds.... at 1.25MHz, I found that it sometimes misses some pulses.
	if(bus_speed_hz > TM_STICK_MAX_BUS_SPEED){
		bus_speed_hz = TM_STICK_MAX_BUS_SPEED;
	}
	ssc_set_clock_divider(SSC, bus_speed_hz, sysclk_get_cpu_hz()); 
	clock_opt_t rx_clk_opt = {
		.ul_cks = SSC_RCMR_CKS_MCK,
		.ul_cko = SSC_RCMR_CKO_TRANSFER,
// TODO: Fix the SSC clock for some reason shift 1/2 clock pulse position.
// This makes the button results shift one position to the right. So we have to change rising/falling edge to "correct" it (oh, well, "hack" around it).
// Don't know why.
// One possible patch up is to use a pin to signal which one is desired to determine which of the following settings to use..
#if defined(CONF_BOARD_ARDUINO_DUE) 
		.ul_cki = 0, //SSC_RCMR_CKI,
#else
		.ul_cki = SSC_RCMR_CKI,
#endif
		.ul_ckg = SSC_RCMR_CKG_CONTINUOUS,
		.ul_start_sel = SSC_RCMR_START_RF_FALLING,
		.ul_period = 0,
		.ul_sttdly = 0
	};
	
	data_frame_opt_t rx_data_frame_opt = {
		.ul_datlen = 23,
		.ul_msbf = 0,  //SSC_RFMR_MSBF,
		.ul_fsos = SSC_RFMR_FSOS_NONE,
		.ul_datnb = 0,
		.ul_fsedge = SSC_RFMR_FSEDGE_NEGATIVE,
		.ul_fslen = 0,
		.ul_fslen_ext = 0
	};
	
	ssc_set_receiver(SSC, &rx_clk_opt, &rx_data_frame_opt);
	ssc_enable_interrupt(SSC, SSC_IER_RXRDY);
	
	// It is VERY IMPORTANT to set the priority of the interrupt to conform to what FreeRTOS needs because we are calling FreeRTOS interrupt-safe APIs (those *FromISR) from within interrupt handlers.
	// If we don't do this, if would work at the beginning, and then eventually the whole thing will come crashing down.
	// And it's not gonna crash even after millions of interrupts are handled. It will come crashing down in some very weird place after you start using another interrupt handler, like the pio handlers, and only after some handlling...
	// And, it will appear random. You press the button a couple of times, it dies. Then, at other times, you press and hold the button, etc. etc. Each time will be different.
	// You would be suspecting your stack overflowed, your code does a wild pointer, etc. etc. It's very difficult to debug. Trust me, you don't wanna go there.
	NVIC_ClearPendingIRQ(SSC_IRQn);
	NVIC_SetPriority(SSC_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
	
	NVIC_EnableIRQ(SSC_IRQn); 
	
	ssc_enable_rx(SSC);
}

void SSC_Handler( void ) {	
	portBASE_TYPE xHigherTaskWoken = pdFALSE;
	uint32_t* in_data_buf = (uint32_t*)g_tm_stick_data.data;
	uint32_t in_data = 0;
	static uint32_t previous_in_data = 0;
	static bool is_first_time = true;
	// calculate the mask needed to wipe off extra high bit junk.
	static uint32_t mask = 0xFFFFFFFF;
	if(is_first_time) {
		for(int i = 4; i > TM_STICK_NUM_DATA_BYTES; i--) {
			mask = mask >> 8;
		}
	}

	if(ssc_is_rx_ready(SSC) == SSC_RC_YES) {
		xSemaphoreTakeFromISR(g_tm_stick_data.mutex, &xHigherTaskWoken);
		in_data = SSC->SSC_RHR;
		// in_data >>= 1; // No idea why it always reads 25bits (one too many bit at the end LSB), instead of 24 I told it to. Therefore, we shift it off.
		in_data = ~in_data; // reverse it. So, now 1 is on, 0 is off.
		in_data &= mask;
		
		// Glitch filtering below.
		if(is_first_time
			||  (previous_in_data ^ in_data) == 0) {
			is_first_time = false;
			*in_data_buf = in_data;
		}
		previous_in_data = in_data;
		xSemaphoreGiveFromISR(g_tm_stick_data.mutex, &xHigherTaskWoken);
	
		xHigherTaskWoken = pdFALSE;
		xSemaphoreGiveFromISR(g_tm_stick_data.rtos_task_semaphore, &xHigherTaskWoken); // if there is an RTOS waiting for the ADC task mutex, wake it up.
		portEND_SWITCHING_ISR(xHigherTaskWoken);
	}
}

#ifdef __cplusplus
}