/** * \internal * \brief Test for AC comparison in single shot mode. * * This test checks the single shot comparison of the AC. * 0.5V is applied to the negative input of AC from internal voltage scaler. * 0V and 1V from DAC is applied to the positive input and the results * are verified. * * \param test Current test case. */ static void run_ac_single_shot_test(const struct test_case *test) { volatile uint32_t state = AC_CHAN_STATUS_UNKNOWN; /* Skip test if initialization failed */ test_assert_true(test, ac_init_success, "Skipping test due to failed AC initialization"); /* Test for positive input < negative input */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_ZERO_VOLT); delay_ms(1); ac_chan_trigger_single_shot(&ac_inst, AC_CHAN_CHANNEL_0); while (!ac_chan_is_ready(&ac_inst, AC_CHAN_CHANNEL_0)) { } state = ac_chan_get_status(&ac_inst, AC_CHAN_CHANNEL_0); state = state & AC_CHAN_STATUS_NEG_ABOVE_POS; test_assert_true(test, state == AC_CHAN_STATUS_NEG_ABOVE_POS, "AC comparison failed: POS < NEG not detected"); /* Test for negative input < positive input */ state = AC_CHAN_STATUS_UNKNOWN; dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_ONE_VOLT); delay_ms(1); ac_chan_trigger_single_shot(&ac_inst, AC_CHAN_CHANNEL_0); while (!ac_chan_is_ready(&ac_inst, AC_CHAN_CHANNEL_0)) { } state = ac_chan_get_status(&ac_inst, AC_CHAN_CHANNEL_0); state = state & AC_CHAN_STATUS_POS_ABOVE_NEG; test_assert_true(test, state == AC_CHAN_STATUS_POS_ABOVE_NEG, "AC comparison failed: Interrupt not detected"); ac_chan_clear_status(&ac_inst, AC_CHAN_CHANNEL_0); }
/** * \internal * \brief AC window mode test function * * This test checks the window functionality of the AC module. * Inputs are given in each region of the window (below, inside & above) * and corresponding window output states are verified. * * \param test Current test case. */ static void run_ac_window_mode_test(const struct test_case *test) { volatile uint32_t state = AC_WIN_STATUS_UNKNOWN; /* Skip test if initialization failed */ test_assert_true(test, ac_init_success, "Skipping test due to failed AC initialization"); /* Test for region-below detection */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_ZERO_VOLT); delay_ms(1); ac_chan_trigger_single_shot(&ac_inst, AC_CHAN_CHANNEL_0); while (!ac_win_is_ready(&ac_inst, AC_WIN_CHANNEL_0)) { } state = ac_win_get_status(&ac_inst, AC_WIN_CHANNEL_0); state = state & AC_WIN_STATUS_BELOW; test_assert_true(test, state == AC_WIN_STATUS_BELOW, "AC window mode: Less than lower limit not detected"); ac_win_clear_status(&ac_inst, AC_WIN_CHANNEL_0); ac_chan_clear_status(&ac_inst, AC_CHAN_CHANNEL_0); ac_chan_clear_status(&ac_inst, AC_CHAN_CHANNEL_1); /* Test for region-inside detection */ state = AC_WIN_STATUS_UNKNOWN; dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_HALF_VOLT); delay_ms(1); ac_chan_trigger_single_shot(&ac_inst, AC_CHAN_CHANNEL_0); while (!ac_win_is_ready(&ac_inst, AC_WIN_CHANNEL_0)) { } state = ac_win_get_status(&ac_inst, AC_WIN_CHANNEL_0); state = state & AC_WIN_STATUS_INSIDE; test_assert_true(test, state == AC_WIN_STATUS_INSIDE, "AC window mode: Within limit not detected"); ac_win_clear_status(&ac_inst, AC_WIN_CHANNEL_0); ac_chan_clear_status(&ac_inst, AC_CHAN_CHANNEL_0); ac_chan_clear_status(&ac_inst, AC_CHAN_CHANNEL_1); /* Test for region-above detection */ state = AC_WIN_STATUS_UNKNOWN; dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_ONE_VOLT); delay_ms(1); ac_chan_trigger_single_shot(&ac_inst, AC_CHAN_CHANNEL_0); while (!ac_win_is_ready(&ac_inst, AC_WIN_CHANNEL_0)) { } state = ac_win_get_status(&ac_inst, AC_WIN_CHANNEL_0); state = state & AC_WIN_STATUS_ABOVE; test_assert_true(test, state == AC_WIN_STATUS_ABOVE, "AC window mode: More than upper limit not detected"); ac_win_clear_status(&ac_inst, AC_WIN_CHANNEL_0); ac_chan_clear_status(&ac_inst, AC_CHAN_CHANNEL_0); ac_chan_clear_status(&ac_inst, AC_CHAN_CHANNEL_1); }
int main(void) { system_init(); //! [setup_init] configure_dac(); configure_dac_channel(); //! [setup_init] //! [main] //! [main_output_var] uint16_t i = 0; //! [main_output_var] //! [main_loop] while (1) { //! [main_loop] //! [main_write] dac_chan_write(&dac_instance, DAC_CHANNEL_0, i); //! [main_write] //! [main_inc_val] if (++i == 0x3FF) { i = 0; } //! [main_inc_val] } //! [main] }
/** * \internal * \brief ADC callback mode test function * * This test checks the callback functionality of the ADC driver. * ADC callback for buffered conversion is enabled. * Converted results are verified for expected results. * * \param test Current test case. */ static void run_adc_callback_mode_test(const struct test_case *test) { uint16_t timeout_cycles = 0xFFFF; /* Set 0.5V DAC output */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_HALF_VOLT); delay_ms(1); /* Start ADC read */ adc_read_buffer_job(&adc_inst, adc_buf, ADC_SAMPLES); do { timeout_cycles--; if (interrupt_flag) { break; } } while (timeout_cycles > 0); /* Test for timeout */ test_assert_true(test, timeout_cycles > 0, "Timeout in ADC read"); /* Test result */ for (uint8_t i = 0; i < ADC_SAMPLES; i++) { test_assert_true(test, (adc_buf[i] > (ADC_VAL_DAC_HALF_OUTPUT - ADC_OFFSET)) && (adc_buf[i] < (ADC_VAL_DAC_HALF_OUTPUT + ADC_OFFSET)), "Error in ADC conversion for 0.5V at index %d, Result: %d", i, adc_buf[i]); } }
/** * \brief Write to the DAC. * * This function converts a specific number of digital data. * The conversion should be event-triggered, the data will be written to DATABUF * and transferred to the DATA register and converted when a Start Conversion * Event is issued. * Conversion data must be right or left adjusted according to configuration * settings. * \note To be event triggered, the enable_start_on_event must be * enabled in the configuration. * * \param[in] module_inst Pointer to the DAC software device struct * \param[in] channel DAC channel to write to * \param[in] buffer Pointer to the digital data write buffer to be converted * \param[in] length Length of the write buffer * * \return Status of the operation. * \retval STATUS_OK If the data was written or no data conversion required * \retval STATUS_ERR_UNSUPPORTED_DEV The DAC is not configured as using * event trigger * \retval STATUS_BUSY The DAC is busy to convert */ enum status_code dac_chan_write_buffer_wait( struct dac_module *const module_inst, enum dac_channel channel, uint16_t *buffer, uint32_t length) { /* Sanity check arguments */ Assert(module_inst); Assert(module_inst->hw); Dac *const dac_module = module_inst->hw; while (dac_is_syncing(module_inst)) { /* Wait until the synchronization is complete */ } /* Zero length request */ if (length == 0) { /* No data to be converted */ return STATUS_OK; } #if DAC_CALLBACK_MODE == true /* Check if busy */ if (module_inst->job_status[channel] == STATUS_BUSY) { return STATUS_BUSY; } #endif /* Only support event triggered conversion */ if (module_inst->start_on_event[channel] == false) { return STATUS_ERR_UNSUPPORTED_DEV; } /* Blocks while buffer is being transferred */ while (length--) { /* Convert one data */ dac_chan_write(module_inst, channel, buffer[length]); /* Wait until Transmit is complete or timeout */ for (uint32_t i = 0; i <= DAC_TIMEOUT; i++) { if(channel == DAC_CHANNEL_0) { if (dac_module->INTFLAG.reg & DAC_INTFLAG_EMPTY0) { break; } else if (i == DAC_TIMEOUT) { return STATUS_ERR_TIMEOUT; } } else if(channel == DAC_CHANNEL_1) { if (dac_module->INTFLAG.reg & DAC_INTFLAG_EMPTY1) { break; } else if (i == DAC_TIMEOUT) { return STATUS_ERR_TIMEOUT; } } } } return STATUS_OK; }
void analogout_write_u16(dac_t *obj, uint16_t value) { MBED_ASSERT(obj); uint16_t count_val; count_val = (uint16_t)((value * (float)MAX_VAL_10BIT) / 0xFFFF); /*Normalization to the value 0xFFFF*/ dac_chan_write(&dac_instance, DAC_CHANNEL_0, count_val); }
void analogout_write_u16(dac_t *obj, uint16_t value) { MBED_ASSERT(obj); uint16_t count_val; count_val = (uint16_t)((value * (float)MAX_VAL_12BIT) / 0xFFFF); /*Normalization to the value 0xFFFF*/ dac_chan_write(&(obj->dac_instance), obj->channel, count_val); }
/* Timer 0 interrupt handler */ void TC0_Handler(void) { TC0->COUNT16.INTFLAG.bit.MC0 = 1; #if WAVE_MODE==SINE_WAVE dac_chan_write(&dac_inst, 0, sine_wave_buf[arr_index++]); if (arr_index == 360) { arr_index = 0; } #elif WAVE_MODE==SAW_TOOTH_WAVE dac_chan_write(&dac_inst, 0, sawtooth_wave_buf[arr_index++]); if (arr_index == 256) { arr_index = 0; } #elif WAVE_MODE==TRIANGLE_WAVE dac_chan_write(&dac_inst, 0, triangle_wave_buf[arr_index++]); if (arr_index == 256) { arr_index = 0; } #endif }
/** * \internal * \brief AC callback mode test function * * This test changes the positive input from 0V to 1V to detect the * rising edge and again from 1V to 0V to detect the falling edge. * * \param test Current test case. */ static void run_ac_callback_mode_test(const struct test_case *test) { uint16_t timeout_cycles = 100; /* Skip test if initialization failed */ test_assert_true(test, ac_init_success, "Skipping test due to failed AC initialization"); /* Set input to 0V */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_ZERO_VOLT); /* Wait for AC output */ delay_ms(1); /* Test for rising edge detection */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_ONE_VOLT); /* Wait for AC output */ delay_ms(1); do { timeout_cycles--; if (interrupt_flag) { break; } } while (timeout_cycles > 0); test_assert_true(test, timeout_cycles, "Error: Timeout in rising edge detection"); /* Test for falling edge detection */ timeout_cycles = 100; interrupt_flag = false; dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_ZERO_VOLT); delay_ms(1); do { timeout_cycles--; if (interrupt_flag) { break; } } while (timeout_cycles > 0); test_assert_true(test, timeout_cycles, "Error: Timeout in falling edge detection"); }
/** * \internal * \brief Setup Function: ADC window mode test. * * This function initializes the ADC in window mode. * Upper and lower threshold values are provided. * It also registers & enables callback for window detection. * * \param test Current test case. */ static void setup_adc_window_mode_test(const struct test_case *test) { enum status_code status = STATUS_ERR_IO; interrupt_flag = false; /* Set 0.5V DAC output */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_HALF_VOLT); delay_ms(1); /* Skip test if ADC initialization failed */ test_assert_true(test, adc_init_success, "Skipping test due to failed initialization"); /* Disable ADC before initialization */ adc_disable(&adc_inst); struct adc_config config; adc_get_config_defaults(&config); config.positive_input = ADC_POSITIVE_INPUT_PIN2; config.negative_input = ADC_NEGATIVE_INPUT_GND; #if (SAML21) config.reference = ADC_REFERENCE_INTREF; config.clock_prescaler = ADC_CLOCK_PRESCALER_DIV16; #else config.reference = ADC_REFERENCE_INT1V; #endif config.clock_source = GCLK_GENERATOR_3; #if !(SAML21) config.gain_factor = ADC_GAIN_FACTOR_1X; #endif config.resolution = ADC_RESOLUTION_12BIT; config.freerunning = true; config.window.window_mode = ADC_WINDOW_MODE_BETWEEN_INVERTED; config.window.window_lower_value = (ADC_VAL_DAC_HALF_OUTPUT - ADC_OFFSET); config.window.window_upper_value = (ADC_VAL_DAC_HALF_OUTPUT + ADC_OFFSET); /* Re-initialize & enable ADC */ status = adc_init(&adc_inst, ADC, &config); test_assert_true(test, status == STATUS_OK, "ADC initialization failed"); status = adc_enable(&adc_inst); test_assert_true(test, status == STATUS_OK, "ADC enabling failed"); /* Register and enable window mode callback */ adc_register_callback(&adc_inst, adc_user_callback, ADC_CALLBACK_WINDOW); adc_enable_callback(&adc_inst, ADC_CALLBACK_WINDOW); /* Start ADC conversion */ adc_start_conversion(&adc_inst); }
void analogout_write(dac_t *obj, float value) { MBED_ASSERT(obj); uint16_t count_val = 0; if (value < 0.0f) { count_val = 0; } else if (value > 1.0f) { count_val = MAX_VAL_10BIT; } else { count_val = (uint16_t)(value * (float)MAX_VAL_10BIT); } dac_chan_write(&dac_instance, DAC_CHANNEL_0, count_val); }
void analogout_write(dac_t *obj, float value) { MBED_ASSERT(obj); uint16_t count_val = 0; if (value < 0.0f) { count_val = 0; } else if (value > 1.0f) { count_val = MAX_VAL_12BIT; } else { count_val = (uint16_t)(value * (float)MAX_VAL_12BIT); } dac_chan_write(&(obj->dac_instance), obj->channel, count_val); }
/** * \internal * \brief ADC window mode test function * * This test gives an input voltage outside the window and checks * whether the callback is triggered or not. * * \param test Current test case. */ static void run_adc_window_mode_test(const struct test_case *test) { uint16_t timeout_cycles = 0xFFFF; /* Set 1V DAC output */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_ONE_VOLT); delay_ms(1); do { timeout_cycles--; if (interrupt_flag) { break; } } while (timeout_cycles > 0); test_assert_true(test, timeout_cycles > 0, "Timeout in window detection"); }
/** * \internal * \brief ADC average mode test function * * This test performs the ADC averaging by starting a conversion. * 0.5V is applied as input from DAC. * Converted result is verified for expected results. * * \param test Current test case. */ static void run_adc_average_mode_test(const struct test_case *test) { uint16_t adc_result = 0; /* Set 0.5V DAC output */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_HALF_VOLT); delay_ms(1); /* Start an ADC conversion */ adc_start_conversion(&adc_inst); while (adc_read(&adc_inst, &adc_result) != STATUS_OK) { } /* Test result */ test_assert_true(test, (adc_result > (ADC_VAL_DAC_HALF_OUTPUT - ADC_OFFSET)) && (adc_result < (ADC_VAL_DAC_HALF_OUTPUT + ADC_OFFSET)), "Error in ADC average mode conversion at 0.5V input"); }
/** * \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 */ } } }
/** * \internal * \brief Test for ADC conversion in polled mode. * * This test checks the polled mode functionality of the ADC. * 0.5V and 1V inputs are applied to the ADC via DAC and the ADC * outputs are verified for expected results. * * \param test Current test case. */ static void run_adc_polled_mode_test(const struct test_case *test) { uint16_t adc_result = 0; /* Skip test if ADC initialization failed */ test_assert_true(test, adc_init_success, "Skipping test due to failed initialization"); /* Set 0.5V on DAC output */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_HALF_VOLT); delay_ms(1); /* Start an ADC conversion */ adc_start_conversion(&adc_inst); while (adc_read(&adc_inst, &adc_result) != STATUS_OK) { } /* Test result */ test_assert_true(test, (adc_result > (ADC_VAL_DAC_HALF_OUTPUT - ADC_OFFSET)) && (adc_result < (ADC_VAL_DAC_HALF_OUTPUT + ADC_OFFSET)), "Error in ADC conversion at 0.5V input (Expected: ~%d, Result: %d)", ADC_VAL_DAC_HALF_OUTPUT, adc_result); adc_flush(&adc_inst); /* Set 1V on DAC output */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, DAC_VAL_ONE_VOLT); delay_ms(1); /* Start an ADC conversion */ adc_start_conversion(&adc_inst); while (adc_read(&adc_inst, &adc_result) != STATUS_OK) { } /* Test result */ test_assert_true(test, adc_result > (ADC_VAL_DAC_FULL_OUTPUT - ADC_OFFSET), "Error in ADC conversion at 1V input (Expected: ~%d, Result: %d)", ADC_VAL_DAC_FULL_OUTPUT, adc_result); uint16_t adc_prev_result = 0; /* Ensure ADC gives linearly increasing conversions for linearly increasing inputs */ for (uint16_t i = 0; i < DAC_VAL_ONE_VOLT; i++) { adc_flush(&adc_inst); /* Write the next highest DAC output voltage */ dac_chan_write(&dac_inst, DAC_CHANNEL_0, i); delay_ms(1); /* Start an ADC conversion */ adc_start_conversion(&adc_inst); while (adc_read(&adc_inst, &adc_result) != STATUS_OK) { } /* Test result */ test_assert_true(test, (adc_result + ADC_OFFSET) >= adc_prev_result, "Error in ADC conversion at a variable input (Expected: >=%d, Result: %d)", adc_prev_result, adc_result); adc_prev_result = adc_result; } }