void Adafruit_ZeroTimer::setPeriodMatch(uint32_t period, uint32_t match, uint8_t channum) { if (_countersize == TC_COUNTER_SIZE_8BIT) { config_tc.counter_8_bit.period = period; tc_set_top_value(&tc_instance, period); setCompare(channum, match); } else if (_countersize == TC_COUNTER_SIZE_16BIT) { setCompare(0, period); setCompare(1, match); } else if (_countersize == TC_COUNTER_SIZE_32BIT) { setCompare(0, period); setCompare(1, match); } }
/* * \brief Initialize and start timer for tick * * Function that sets up a timer to use for os tick. The same timer is also * used as the sleep timer. * The timer runs at 48MHz, i.e. with no prescaler on GCLK0. Wavegen function * Match Frequency is chosen to reload the count register on every CC0 match. * 8 bit counter mode must not be chosen. * The function is weakly defined in freeRTOS, and redefined here. */ void vPortSetupTimerInterrupt(void) { // Struct for configuring TC struct tc_config tcconf; // Set up configuration values tc_get_config_defaults(&tcconf); tcconf.counter_size = TC_COUNTER_SIZE_32BIT; tcconf.run_in_standby = true; tcconf.clock_prescaler = TC_CLOCK_PRESCALER_DIV1; tcconf.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ; // Initialize the TC tc_init(&tc, TICK_TC, &tcconf); // Register and enable callback for freeRTOS tick handler tc_register_callback(&tc, (tc_callback_t) xPortSysTickHandler, TC_CALLBACK_CC_CHANNEL0); tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); // Set top value equal to one os tick tc_set_top_value(&tc, TIMER_RELOAD_VALUE_ONE_TICK); // Enable the timer tc_enable(&tc); }
/** * \brief Configures the DAC in event triggered mode. * * Configures the DAC to use the module's default configuration, with output * channel mode configured for event triggered conversions. * * \param dev_inst Pointer to the DAC module software instance to initialize */ static void configure_dac(struct dac_module *dac_module) { struct dac_config config; struct dac_chan_config channel_config; /* Get the DAC default configuration */ dac_get_config_defaults(&config); /* Switch to GCLK generator 0 */ config.clock_source = GCLK_GENERATOR_0; dac_init(dac_module, DAC, &config); /* Get the default DAC channel config */ dac_chan_get_config_defaults(&channel_config); /* Set the channel configuration, and enable it */ dac_chan_set_config(dac_module, DAC_CHANNEL_0, &channel_config); dac_chan_enable(dac_module, DAC_CHANNEL_0); /* Enable event triggered conversions */ struct dac_events events = { .on_event_start_conversion = true }; dac_enable_events(dac_module, &events); dac_enable(dac_module); } /** * \brief Configures the TC to generate output events at the sample frequency. * * Configures the TC in Frequency Generation mode, with an event output once * each time the audio sample frequency period expires. * * \param dev_inst Pointer to the TC module software instance to initialize */ static void configure_tc(struct tc_module *tc_module) { struct tc_config config; tc_get_config_defaults(&config); config.clock_source = GCLK_GENERATOR_0; config.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ; tc_init(tc_module, TC3, &config); /* Enable periodic event output generation */ struct tc_events events = { .generate_event_on_overflow = true }; tc_enable_events(tc_module, &events); /* Set the timer top value to alter the overflow frequency */ tc_set_top_value(tc_module, system_gclk_gen_get_hz(GCLK_GENERATOR_0) / sample_rate); tc_enable(tc_module); } /** * \brief Configures the event system to link the sample timer to the DAC. * * Configures the event system, linking the TC module used for the audio sample * rate timing to the DAC, so that a new conversion is triggered each time the * DAC receives an event from the timer. */ static void configure_events(struct events_resource *event) { struct events_config config; events_get_config_defaults(&config); config.generator = EVSYS_ID_GEN_TC3_OVF; config.path = EVENTS_PATH_ASYNCHRONOUS; events_allocate(event, &config); events_attach_user(event, EVSYS_ID_USER_DAC_START); } /** * \brief Main application routine */ int main(void) { struct dac_module dac_module; struct tc_module tc_module; struct events_resource event; /* Initialize all the system clocks, pm, gclk... */ system_init(); /* Enable the internal bandgap to use as reference to the DAC */ system_voltage_reference_enable(SYSTEM_VOLTAGE_REFERENCE_BANDGAP); /* Module configuration */ configure_tc(&tc_module); configure_dac(&dac_module); configure_events(&event); /* Start the sample trigger timer */ tc_start_counter(&tc_module); while (true) { while (port_pin_get_input_level(SW0_PIN) == SW0_INACTIVE) { /* Wait for the button to be pressed */ } port_pin_toggle_output_level(LED0_PIN); for (uint32_t i = 0; i < number_of_samples; i++) { dac_chan_write(&dac_module, DAC_CHANNEL_0, wav_samples[i]); while (!(DAC->INTFLAG.reg & DAC_INTFLAG_EMPTY)) { /* Wait for data buffer to be empty */ } } while (port_pin_get_input_level(SW0_PIN) == SW0_ACTIVE) { /* Wait for the button to be depressed */ } } }
/* * \brief Configure sleep timer and sleep * * Function to configure timer for sleep, and calculate time slept. */ void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { // Are we running tickless now? if (!tickless_enable) return; // Reconfigure the timer to act as sleep timer tc_disable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_unregister_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_register_callback(&tc, empty_callback, TC_CALLBACK_CC_CHANNEL0); tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); // Check that the offset is not greater than the range of the timer if (xExpectedIdleTime > TIMER_MAX_POSSIBLE_SUPPRESSED_TICKS) { xExpectedIdleTime = TIMER_MAX_POSSIBLE_SUPPRESSED_TICKS; } // Set sleep time, -1 because we want to wake up before the last tick tc_set_top_value(&tc, (xExpectedIdleTime - 1) * TIMER_RELOAD_VALUE_ONE_TICK); // Clear overflow interrupt flag tc.hw->COUNT32.INTFLAG.bit.OVF = 1; // Check if we still should sleep if (eTaskConfirmSleepModeStatus() == eAbortSleep) { // Reset the timer to act as SysTick tc_disable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_unregister_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_register_callback(&tc, (tc_callback_t) xPortSysTickHandler, TC_CALLBACK_CC_CHANNEL0); tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_set_top_value(&tc, TIMER_RELOAD_VALUE_ONE_TICK); } else { if (xExpectedIdleTime > 0) { // Data sync barrier before sleep __asm volatile ("dsb"); // Go to sleep __asm volatile ("wfi"); // If OVF interrupt flag is set, we know the timer has wrapped if (tc.hw->COUNT32.INTFLAG.bit.OVF) { vTaskStepTick(xExpectedIdleTime - 1); } // We do not know how long we've slept else { // Calculate from Counter how long we've slept // Reset counter to less than one os tick // This might result in a tiny drift in time. uint32_t count_val = tc_get_count_value(&tc); vTaskStepTick(count_val / TIMER_RELOAD_VALUE_ONE_TICK); tc_set_count_value(&tc, count_val % TIMER_RELOAD_VALUE_ONE_TICK); } } // Reset the timer to act as SysTick tc_disable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_unregister_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_register_callback(&tc, (tc_callback_t) xPortSysTickHandler, TC_CALLBACK_CC_CHANNEL0); tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_set_top_value(&tc, TIMER_RELOAD_VALUE_ONE_TICK); // Make sure that the counter hasn't passed the CC before callback was registered if ( tc_get_count_value(&tc) > TIMER_RELOAD_VALUE_ONE_TICK ) { // If so, reload count value, and step one tick */ tc_set_count_value(&tc, tc_get_count_value(&tc) % TIMER_RELOAD_VALUE_ONE_TICK); vTaskStepTick(1); } }