/** * \brief Perform a simulation step. * * This function first reads the received frequency signal representing the * power level the plate element is actuated with, then performs a simulation * step and outputs the simulated temperature of the plate element as an analog * value via DACB on the pin marked ADC2. * * The simulation plant is a discretized version of the continuous system: * \f$ \dot{x} = \mathbf{F}*\vec{x} + \mathbf{B}*\vec{u} \f$, where: * \f[ \begin{array}{c} \mathbf{F} = \left[ \begin{array}{c c} \frac{-1}{C_\textnormal{plate} * K_\textnormal{plate}} & 0 \\ \\ 0 & \frac{-1}{C_\textnormal{pot} * K_\textnormal{pot}} \end{array} \right] \\ \vec{x} = \left[ \begin{array}{c} E_\textnormal{plate} \\ \\ E_\textnormal{pot} \end{array} \right] \\ \mathbf{B} = \left[ \begin{array}{c c} 1 & 0 \\ \\ 0 & 1 \end{array} \right] \\ \vec{u} = \left[ \begin{array}{c} \frac{T_\textnormal{environment}}{K_\textnormal{plate}} \\ \\ \frac{T_\textnormal{environment}}{K_\textnormal{pot}} \end{array} \right] \end{array} \\ \f] * in the case when the pot is OFF the plate. * * This corresponds to the plate and the pot cooling separately by Newton's law * of cooling because no power can be induced when the pot is not on the plate. * * When the pot is ON the stove, the system is similar, but \b F is modified by * adding heat transfer between the plate and the contents of the pot, and * disregarding heat loss from the plate to the environment. * Likewise \b u is modified by adding energy induction into the bottom of the * pot, simplified here as being the same point as the plate: * \f[ \begin{array}{c} \mathbf{F} = \left[ \begin{array}{c c} \frac{-1}{C_\textnormal{plate}* K_\textnormal{plate}} & \frac{1}{C_\textnormal{pot}* K_\textnormal{plate}} \\ \\ \frac{1}{C_\textnormal{plate}* K_\textnormal{plate}} & \frac{-1}{C_\textnormal{pot}* K_\textnormal{plate}} - \frac{1}{C_\textnormal{pot} * K_\textnormal{pot}} \end{array} \right] \\ \vec{u} = \left[ \begin{array}{c} \delta E_\textnormal{induced} \\ \\ \frac{T_\textnormal{environment}}{K_\textnormal{pot}} \end{array} \right] \end{array} \f] * * * \param pot Whether the pot is on or off the plate. */ void oven_plant_sim_step(enum pot_t pot) { /* Remap period of incoming signal to power */ float u = oven_plant_calculate_u_from_period( oven_plant_read_signal_period()); switch (pot) { case POT_OFF: state[0] = state[0] + u - (state[0] / C_PLATE - ENV_TEMP) / K_PLATE; state[1] = state[1] - (state[1] / C_POT - ENV_TEMP) / K_POT; break; case POT_ON: state[0] = state[0] + u - (state[0] / C_PLATE - state[1] / C_POT) / K_PLATE; state[1] = state[1] + (state[0] / C_PLATE - state[1] / C_POT) / K_PLATE - (state[1] / C_POT - ENV_TEMP) / K_POT; break; default: break; } /* Output DAC value, simulating an analog temperature sensor. */ dac_wait_for_channel_ready(&DACB, DAC_CH0); dac_set_channel_value(&DACB, DAC_CH0, state[0] * 1); return; }
/** * \brief Main application rutine */ int main (void) { /* Variables used to produce a saw tooth signal */ static uint16_t dac_data = 0; static int8_t direction = 1; board_init(); sysclk_init(); /* Calibrate the DAC */ dac_calibrate(); /* Generate a SAW tooth signal, just as a demo that the calibration works */ while (true) { dac_wait_for_channel_ready(&DACB, DAC_CH0); dac_set_channel_value(&DACB, DAC_CH0, dac_data); dac_set_channel_value(&DACB, DAC_CH1, dac_data); dac_data = dac_data + direction; /* If the output value is the top value, switch direction */ if (dac_data == 4095) { direction = -1; } /* If the output value is the lowest value, switch direction */ if (dac_data == 0) { direction = 1; } } }
/** * \brief Changes the DAC ouput value * * \param volt Value to output on DAC pins */ static void main_dac_output(int16_t volt) { /* Samples of values used: * | ADC0 | V+ | ADC1 | V- | V+ - V- | * --------------------------------------------- * | 4096 | Vcc | 0 | 0V | Vcc | * | 3072 | Vcc 3/4 | 1024 | Vcc 1/4 | Vcc/2 | * | 2048 | Vcc 1/2 | 2048 | Vcc 1/2 | 0V | * | 1024 | Vcc 1/4 | 3072 | Vcc 3/4 | -Vcc/2 | * | 0 | 0V | 4096 | Vcc | -Vcc | */ dac_wait_for_channel_ready(&DACA, DAC_CH0 | DAC_CH1); dac_set_channel_value(&DACA, DAC_CH0, (1 << 11) + (((1 << 11) * (int32_t)volt) / 3300)); dac_set_channel_value(&DACA, DAC_CH1, (1 << 11) - (((1 << 11) * (int32_t)volt) / 3300)); dac_wait_for_channel_ready(&DACA, DAC_CH0 | DAC_CH1); }
int main(void) { struct dac_config conf; uint8_t i = 0; board_init(); sysclk_init(); // Initialize the dac configuration. dac_read_configuration(&SPEAKER_DAC, &conf); /* Create configuration: * - 1V from bandgap as reference, left adjusted channel value * - one active DAC channel, no internal output * - conversions triggered by event channel 0 * - 1 us conversion intervals */ dac_set_conversion_parameters(&conf, DAC_REF_BANDGAP, DAC_ADJ_LEFT); dac_set_active_channel(&conf, SPEAKER_DAC_CHANNEL, 0); dac_set_conversion_trigger(&conf, SPEAKER_DAC_CHANNEL, 0); #if XMEGA_DAC_VERSION_1 dac_set_conversion_interval(&conf, 1); #endif dac_write_configuration(&SPEAKER_DAC, &conf); dac_enable(&SPEAKER_DAC); #if XMEGA_E // Configure timer/counter to generate events at sample rate. sysclk_enable_module(SYSCLK_PORT_C, SYSCLK_TC4); TCC4.PER = (sysclk_get_per_hz() / RATE_OF_CONVERSION) - 1; // Configure event channel 0 to generate events upon T/C overflow. sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_EVSYS); EVSYS.CH0MUX = EVSYS_CHMUX_TCC4_OVF_gc; // Start the timer/counter. TCC4.CTRLA = TC45_CLKSEL_DIV1_gc; #else // Configure timer/counter to generate events at sample rate. sysclk_enable_module(SYSCLK_PORT_C, SYSCLK_TC0); TCC0.PER = (sysclk_get_per_hz() / RATE_OF_CONVERSION) - 1; // Configure event channel 0 to generate events upon T/C overflow. sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_EVSYS); EVSYS.CH0MUX = EVSYS_CHMUX_TCC0_OVF_gc; // Start the timer/counter. TCC0.CTRLA = TC_CLKSEL_DIV1_gc; #endif /* Write samples to the DAC channel every time it is ready for new * data, i.e., when it is done converting. Conversions are triggered by * the timer/counter. */ do { dac_wait_for_channel_ready(&SPEAKER_DAC, SPEAKER_DAC_CHANNEL); dac_set_channel_value(&SPEAKER_DAC, SPEAKER_DAC_CHANNEL, sine[i]); i++; i %= NR_OF_SAMPLES; } while (1); }
/** * \internal * \brief Test differential conversion with gain in 12-bit mode using the DAC * * This test outputs a gain compensated level on DAC output that should result * in a value of half maximum positive value on the ADC. * * These values are then measured using the ADC on the pins that are connected * to the DAC channel, and the results are compared and checked to see if they * are within the acceptable range of values that passes the test. * * \param test Current test case. */ static void run_differential_12bit_with_gain_conversion_test( const struct test_case *test) { // Number of MUX inputs that are to be read const uint8_t num_inputs = 2; /* Connection between DAC outputs and ADC MUX inputs. * Only the high nibble on PORTA is possible for gain. * input 4, 6 is connected to DACA0 * input 5, 7 is connected to DACA1. */ const uint8_t channel_pos[] = {4, 6}; const uint8_t channel_neg[] = {5, 7}; /* * Go through gain level up to 8, since any higher gives too much * propagated error for sensible unit test limits. */ const uint8_t gain[] = {1, 2, 4, 8}; uint8_t gain_index; uint8_t adc_channel; uint8_t mux_index; int16_t results[2]; struct dac_config dac_conf; struct adc_config adc_conf; // Configure ADC adc_read_configuration(&ADCA, &adc_conf); adc_set_conversion_parameters(&adc_conf, ADC_SIGN_ON, ADC_RES_12, ADC_REF_BANDGAP); adc_set_clock_rate(&adc_conf, 2000UL); adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0); adc_write_configuration(&ADCA, &adc_conf); // Configure DAC dac_read_configuration(&DACA, &dac_conf); dac_set_conversion_parameters(&dac_conf, DAC_REF_BANDGAP, DAC_ADJ_RIGHT); dac_set_active_channel(&dac_conf, DAC_CH0 | DAC_CH1, 0); dac_set_conversion_trigger(&dac_conf, 0, 0); dac_set_conversion_interval(&dac_conf, 10); dac_set_refresh_interval(&dac_conf, 20); dac_write_configuration(&DACA, &dac_conf); dac_enable(&DACA); // Set negative output to zero dac_wait_for_channel_ready(&DACA, DAC_CH1); dac_set_channel_value(&DACA, DAC_CH1, DAC_MIN); for (gain_index = 0; gain_index < sizeof(gain); gain_index++) { // Set positive output to half positive range adjusted to gain dac_wait_for_channel_ready(&DACA, DAC_CH0); dac_set_channel_value(&DACA, DAC_CH0, DAC_MAX / (gain[gain_index] * 2)); /* Read all ADC pins connected to active DAC output. * All channels are converted NUM_SAMPLES times and the * final value used for the assert is an average. */ for (adc_channel = 0; adc_channel < NUM_CHANNELS; adc_channel++) { differential_signed_average(&ADCA, 1 << adc_channel, channel_pos, channel_neg, num_inputs, results, gain[gain_index]); for (mux_index = 0; mux_index < num_inputs; mux_index++) { verify_signed_result(test, ADC_SIGNED_12BIT_MAX / 2, results[mux_index], adc_channel, channel_pos[mux_index], channel_neg[mux_index], ADC_SIGNED_12BIT_MIN, ADC_SIGNED_12BIT_MAX, gain[gain_index], true); } } } }
/** * \internal * \brief Test differential conversion in 12-bit mode using the DAC * * This tests output three different values on the two DAC channels: * - 1/2 * \ref DAC_MAX on both outputs to get a differential zero * - \ref DAC_MAX on positive and \ref DAC_MIN on negative to get max positive * result * - \ref DAC_MIN on positive and \ref DAC_MAX on negative to get max negative * result * * These values are then measured using the ADC on the pins that are connected * to the DAC channel, and the results are compared and checked to see if they * are within the acceptable range of values that passes the test. * * \param test Current test case. */ static void run_differential_12bit_conversion_test( const struct test_case *test) { // Number of MUX inputs that are to be read const uint8_t num_inputs = 4; /* Connection between DAC outputs and ADC MUX inputs * input 0, 2, 4, 6 is connected to DACA0 * input 1, 3, 5, 7 is connected to DACA1. */ const uint8_t channel_pos[] = {0, 2, 4, 6}; const uint8_t channel_neg[] = {1, 3, 5, 7}; uint8_t adc_channel; uint8_t mux_index; int16_t results[4]; struct dac_config dac_conf; struct adc_config adc_conf; // Configure ADC adc_read_configuration(&ADCA, &adc_conf); adc_set_conversion_parameters(&adc_conf, ADC_SIGN_ON, ADC_RES_12, ADC_REF_BANDGAP); adc_set_clock_rate(&adc_conf, 2000UL); adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0); adc_write_configuration(&ADCA, &adc_conf); // Configure DAC dac_read_configuration(&DACA, &dac_conf); dac_set_conversion_parameters(&dac_conf, DAC_REF_BANDGAP, DAC_ADJ_RIGHT); dac_set_active_channel(&dac_conf, DAC_CH0 | DAC_CH1, 0); dac_set_conversion_trigger(&dac_conf, 0, 0); dac_set_conversion_interval(&dac_conf, 10); dac_set_refresh_interval(&dac_conf, 20); dac_write_configuration(&DACA, &dac_conf); dac_enable(&DACA); // Set outputs to same (1/2 * MAX_VALUE) to get zero dac_wait_for_channel_ready(&DACA, DAC_CH0 | DAC_CH1); dac_set_channel_value(&DACA, DAC_CH0, DAC_MAX / 2); dac_set_channel_value(&DACA, DAC_CH1, DAC_MAX / 2); /* Read all ADC pins connected to active DAC output. * All channels are converted NUM_SAMPLES times and the * final value used for the assert is an average. */ for (adc_channel = 0; adc_channel < NUM_CHANNELS; adc_channel++) { differential_signed_average(&ADCA, 1 << adc_channel, channel_pos, channel_neg, num_inputs, results, 1); for (mux_index = 0; mux_index < num_inputs; mux_index++) { verify_signed_result(test, ADC_ZERO, results[mux_index], adc_channel, channel_pos[mux_index], channel_neg[mux_index], ADC_SIGNED_12BIT_MIN, ADC_SIGNED_12BIT_MAX, 1, true); } } // Set output to max positive range for positive result dac_wait_for_channel_ready(&DACA, DAC_CH0 | DAC_CH1); dac_set_channel_value(&DACA, DAC_CH0, DAC_MAX); dac_set_channel_value(&DACA, DAC_CH1, DAC_MIN); /* Read all ADC pins connected to active DAC output. * All channels are converted NUM_SAMPLES times and the * final value used for the assert is an average. */ for (adc_channel = 0; adc_channel < NUM_CHANNELS; adc_channel++) { differential_signed_average(&ADCA, 1 << adc_channel, channel_pos, channel_neg, num_inputs, results, 1); for (mux_index = 0; mux_index < num_inputs; mux_index++) { verify_signed_result(test, ADC_SIGNED_12BIT_MAX, results[mux_index], adc_channel, channel_pos[mux_index], channel_neg[mux_index], ADC_SIGNED_12BIT_MIN, ADC_SIGNED_12BIT_MAX, 1, true); } } // Set output to max negative range for negative result dac_wait_for_channel_ready(&DACA, DAC_CH0 | DAC_CH1); dac_set_channel_value(&DACA, DAC_CH0, DAC_MIN); dac_set_channel_value(&DACA, DAC_CH1, DAC_MAX); /* Read all ADC pins connected to active DAC output. * All channels are converted NUM_SAMPLES times and the * final value used for the assert is an average. */ for (adc_channel = 0; adc_channel < NUM_CHANNELS; adc_channel++) { differential_signed_average(&ADCA, 1 << adc_channel, channel_pos, channel_neg, num_inputs, results, 1); for (mux_index = 0; mux_index < num_inputs; mux_index++) { verify_signed_result(test, ADC_SIGNED_12BIT_MIN, results[mux_index], adc_channel, channel_pos[mux_index], channel_neg[mux_index], ADC_SIGNED_12BIT_MIN, ADC_SIGNED_12BIT_MAX, 1, true); } } }
/** * \internal * \brief Test single ended conversion in 8-bit mode using the DAC * * This tests output three different values on the two DAC channels: * - 0 (output analog value is greater than 0, as the DAC cannot go that low) * - 1/2 * \ref DAC_MAX Half of the maximum value of the DAC * - \ref DAC_MAX The maximum value (VREF) of the DAC. * * These values are then measured using the ADC on the pins that are connected * to the DAC channel, using all available ADC channels and the results are * compared and checked to see if they are within the acceptable range of * values that passes the test. * * \param test Current test case. */ static void run_single_ended_8bit_conversion_test( const struct test_case *test) { // Number of MUX inputs that are to be read const uint8_t num_inputs = 4; /* Connection between DAC outputs and ADC MUX inputs * input 0, 2, 4, 6 is connected to DACA0 * input 1, 3, 5, 7 is connected to DACA1. */ const uint8_t channelgroup[2][4] = {{0, 2, 4, 6}, {1, 3, 5, 7}}; uint8_t dac_channel; uint8_t adc_channel; uint8_t mux_index; uint16_t results[4]; struct dac_config dac_conf; struct adc_config adc_conf; // Configure ADC adc_read_configuration(&ADCA, &adc_conf); adc_set_conversion_parameters(&adc_conf, ADC_SIGN_OFF, ADC_RES_8, ADC_REF_BANDGAP); adc_set_clock_rate(&adc_conf, 2000UL); adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0); adc_write_configuration(&ADCA, &adc_conf); // Configure DAC dac_read_configuration(&DACA, &dac_conf); dac_set_conversion_parameters(&dac_conf, DAC_REF_BANDGAP, DAC_ADJ_RIGHT); dac_set_active_channel(&dac_conf, DAC_CH0 | DAC_CH1, 0); dac_set_conversion_trigger(&dac_conf, 0, 0); dac_set_conversion_interval(&dac_conf, 10); dac_set_refresh_interval(&dac_conf, 20); dac_write_configuration(&DACA, &dac_conf); dac_enable(&DACA); // Set outputs as zero dac_wait_for_channel_ready(&DACA, DAC_CH0 | DAC_CH1); dac_set_channel_value(&DACA, DAC_CH0, DAC_MIN); dac_set_channel_value(&DACA, DAC_CH1, DAC_MIN); for(dac_channel = 0; dac_channel < 2; dac_channel++) { /* Read all ADC pins connected to active DAC output. * All channels are converted NUM_SAMPLES times and the * final value used for the assert is an average. */ for (adc_channel = 0; adc_channel < NUM_CHANNELS; adc_channel++) { single_ended_unsigned_average(&ADCA, 1 << adc_channel, (uint8_t *)&channelgroup[dac_channel], num_inputs, results); for (mux_index = 0; mux_index < num_inputs; mux_index++) { verify_unsigned_result(test, ADC_ZERO, results[mux_index], dac_channel, adc_channel, channelgroup[dac_channel] [mux_index], ADC_UNSIGNED_8BIT_MAX, false); } } } // Set outputs as 1/2 * MAX_VALUE dac_wait_for_channel_ready(&DACA, DAC_CH0 | DAC_CH1); dac_set_channel_value(&DACA, DAC_CH0, DAC_MAX / 2); dac_set_channel_value(&DACA, DAC_CH1, DAC_MAX / 2); for(dac_channel = 0; dac_channel < 2; dac_channel++) { /* Read all ADC pins connected to active DAC output. * All channels are converted NUM_SAMPLES times and the * final value used for the assert is an average. */ for (adc_channel = 0; adc_channel < NUM_CHANNELS; adc_channel++) { single_ended_unsigned_average(&ADCA, 1 << adc_channel, (uint8_t *)&channelgroup[dac_channel], num_inputs, results); for (mux_index = 0; mux_index < num_inputs; mux_index++) { verify_unsigned_result(test, ADC_UNSIGNED_8BIT_MAX / 2, results[mux_index], dac_channel, adc_channel, channelgroup[dac_channel] [mux_index], ADC_UNSIGNED_8BIT_MAX, false); } } } // Set outputs as MAX_VALUE dac_wait_for_channel_ready(&DACA, DAC_CH0 | DAC_CH1); dac_set_channel_value(&DACA, DAC_CH0, DAC_MAX); dac_set_channel_value(&DACA, DAC_CH1, DAC_MAX); for(dac_channel = 0; dac_channel < 2; dac_channel++) { /* Read all ADC pins connected to active DAC output. * All channels are converted NUM_SAMPLES times and the * final value used for the assert is an average. */ for (adc_channel = 0; adc_channel < NUM_CHANNELS; adc_channel++) { single_ended_unsigned_average(&ADCA, 1 << adc_channel, (uint8_t *)&channelgroup[dac_channel], num_inputs, results); for (mux_index = 0; mux_index < num_inputs; mux_index++) { verify_unsigned_result(test, ADC_UNSIGNED_8BIT_MAX, results[mux_index], dac_channel, adc_channel, channelgroup[dac_channel] [mux_index], ADC_UNSIGNED_8BIT_MAX, false); } } } }
int main(void) { struct dac_config conf; board_init(); sysclk_init(); // Initialize the dac configuration. dac_read_configuration(&OUTPUT_DAC, &conf); /* Create configuration: * - AVCC as reference, right adjusted channel value * - both DAC channels active, no internal output * - manually triggered conversions on both channels * - 2 us conversion intervals * - 10 us refresh intervals */ dac_set_conversion_parameters(&conf, DAC_REF_AVCC, DAC_ADJ_RIGHT); dac_set_active_channel(&conf, DAC_CH0 | DAC_CH1, 0); dac_set_conversion_trigger(&conf, 0, 0); #if XMEGA_DAC_VERSION_1 dac_set_conversion_interval(&conf, 10); dac_set_refresh_interval(&conf, 20); #endif dac_write_configuration(&OUTPUT_DAC, &conf); dac_enable(&OUTPUT_DAC); dac_wait_for_channel_ready(&OUTPUT_DAC, DAC_CH0 | DAC_CH1); dac_set_channel_value(&OUTPUT_DAC, DAC_CH0, 0); dac_set_channel_value(&OUTPUT_DAC, DAC_CH1, 0); dac_wait_for_channel_ready(&OUTPUT_DAC, DAC_CH0 | DAC_CH1); #if !XMEGA_E // Configure timer/counter to generate events at conversion rate. sysclk_enable_module(SYSCLK_PORT_C, SYSCLK_TC0); TCC0.PER = (sysclk_get_per_hz() / RATE_OF_CONVERSION) - 1; // Configure event channel 0 to generate events upon T/C overflow. sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_EVSYS); EVSYS.CH0MUX = EVSYS_CHMUX_TCC0_OVF_gc; // Start the timer/counter. TCC0.CTRLA = TC_CLKSEL_DIV1_gc; #else // Configure timer/counter to generate events at conversion rate. sysclk_enable_module(SYSCLK_PORT_C, SYSCLK_TC4); TCC4.PER = (sysclk_get_per_hz() / RATE_OF_CONVERSION) - 1; // Configure event channel 0 to generate events upon T/C overflow. sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_EVSYS); EVSYS.CH0MUX = EVSYS_CHMUX_TCC4_OVF_gc; // Start the timer/counter. TCC4.CTRLA = TC45_CLKSEL_DIV1_gc; #endif /* Write samples to the DAC channel every time it is ready. * Conversions are triggered by the timer/counter. */ do { /* Wait for channels to get ready for new values, then set the * value of one to 10% and the other to 90% of maximum. */ wait_for_timer(); dac_set_channel_value(&OUTPUT_DAC, DAC_CH0, 410); dac_set_channel_value(&OUTPUT_DAC, DAC_CH1, 3686); wait_for_timer(); dac_set_channel_value(&OUTPUT_DAC, DAC_CH0, 3686); dac_set_channel_value(&OUTPUT_DAC, DAC_CH1, 410); } while (1); }