// Enable or disable a channel. Use AnalogCheckReady to make sure the ADC is ready before calling this. void AnalogInEnableChannel(AnalogChannelNumber channel, bool enable) { if ((unsigned int)channel < numChannels) { if (enable) { activeChannels |= (1u << channel); #if SAM3XA || SAM4S adc_enable_channel(ADC, GetAdcChannel(channel)); #if SAM4S adc_set_calibmode(ADC); // auto calibrate at start of next sequence #endif if (GetAdcChannel(channel) == ADC_TEMPERATURE_SENSOR) { adc_enable_ts(ADC); } #elif SAM4E || SAME70 afec_ch_config cfg; afec_ch_get_config_defaults(&cfg); afec_ch_set_config(GetAfec(channel), GetAfecChannel(channel), &cfg); afec_channel_set_analog_offset(GetAfec(channel), GetAfecChannel(channel), 2048); // need this to get the full ADC range afec_channel_enable(GetAfec(channel), GetAfecChannel(channel)); #if SAM4E afec_start_calibration(GetAfec(channel)); // do automatic calibration #endif #endif } else { activeChannels &= ~(1u << channel); #if SAM3XA || SAM4S adc_disable_channel(ADC, GetAdcChannel(channel)); if (GetAdcChannel(channel) == ADC_TEMPERATURE_SENSOR) { adc_disable_ts(ADC); } #elif SAM4E || SAME70 afec_channel_disable(GetAfec(channel), GetAfecChannel(channel)); #endif } } }
/** * \brief Configure to trigger ADC by PWM Event Line. */ static void configure_pwm_trigger(void) { /* PWM frequency in Hz. */ #define PWM_FREQUENCY 2 /* Maximum duty cycle value. */ #define MAX_DUTY_CYCLE 1000 /* Enable PWMC peripheral clock. */ pmc_enable_periph_clk(ID_PWM); /* Disable PWM channel 0. */ pwm_channel_disable(PWM, PWM_CHANNEL_0); gpio_configure_pin(PIN_PWMC_PWMH0_TRIG, PIN_PWMC_PWMH0_TRIG_FLAG); /* Set clock A to run at PWM_FREQUENCY * MAX_DUTY_CYCLE (clock B is not used). */ pwm_clock_t pwm_clock_setting = { .ul_clka = PWM_FREQUENCY * MAX_DUTY_CYCLE, .ul_clkb = 0, .ul_mck = sysclk_get_cpu_hz() }; pwm_init(PWM, &pwm_clock_setting); /* Configure PWMC for channel 0 (left-aligned). */ pwm_channel_t pwm_trigger_channel = { .channel = PWM_CHANNEL_0, .alignment = PWM_ALIGN_LEFT, .polarity = PWM_LOW, .ul_prescaler = PWM_CMR_CPRE_CLKA, .ul_period = MAX_DUTY_CYCLE, .ul_duty = MAX_DUTY_CYCLE / 2 }; pwm_channel_init(PWM, &pwm_trigger_channel); pwm_cmp_t pwm_comparison_setting = { .unit = PWM_CMP_UNIT_0, .b_enable = true, .ul_value = MAX_DUTY_CYCLE / 2, .b_pulse_on_line_0 = true }; pwm_cmp_init(PWM, &pwm_comparison_setting); /* Enable PWM channel 0. */ pwm_channel_enable(PWM, PWM_CHANNEL_0); /* Set PWM Event Line 0 trigger. */ #if SAM3S || SAM3XA || SAM4S adc_configure_trigger(ADC, ADC_TRIG_PWM_EVENT_LINE_0, 0); #elif SAM3U #ifdef ADC_12B adc12b_configure_trigger(ADC12B, ADC12B_TRIG_PWM_EVENT_LINE_0); #else adc_configure_trigger(ADC, ADC_TRIG_PWM_EVENT_LINE_0); #endif #endif } #endif /** * \brief Read converted data through PDC channel. * * \param p_adc The pointer of adc peripheral. * \param p_s_buffer The destination buffer. * \param ul_size The size of the buffer. */ #if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C static uint32_t adc_read_buffer(Adc * p_adc, uint16_t * p_s_buffer, uint32_t ul_size) { /* Check if the first PDC bank is free. */ if ((p_adc->ADC_RCR == 0) && (p_adc->ADC_RNCR == 0)) { p_adc->ADC_RPR = (uint32_t) p_s_buffer; p_adc->ADC_RCR = ul_size; p_adc->ADC_PTCR = ADC_PTCR_RXTEN; return 1; } else { /* Check if the second PDC bank is free. */ if (p_adc->ADC_RNCR == 0) { p_adc->ADC_RNPR = (uint32_t) p_s_buffer; p_adc->ADC_RNCR = ul_size; return 1; } else { return 0; } } } #elif SAM3U #ifdef ADC_12B static uint32_t adc12_read_buffer(Adc12b * p_adc, uint16_t * p_s_buffer, uint32_t ul_size) { /* Check if the first PDC bank is free. */ if ((p_adc->ADC12B_RCR == 0) && (p_adc->ADC12B_RNCR == 0)) { p_adc->ADC12B_RPR = (uint32_t) p_s_buffer; p_adc->ADC12B_RCR = ul_size; p_adc->ADC12B_PTCR = ADC12B_PTCR_RXTEN; return 1; } else { /* Check if the second PDC bank is free. */ if (p_adc->ADC12B_RNCR == 0) { p_adc->ADC12B_RNPR = (uint32_t) p_s_buffer; p_adc->ADC12B_RNCR = ul_size; return 1; } else { return 0; } } } #else static uint32_t adc_read_buffer(Adc * p_adc, uint16_t * p_s_buffer, uint32_t ul_size) { /* Check if the first PDC bank is free. */ if ((p_adc->ADC_RCR == 0) && (p_adc->ADC_RNCR == 0)) { p_adc->ADC_RPR = (uint32_t) p_s_buffer; p_adc->ADC_RCR = ul_size; p_adc->ADC_PTCR = ADC_PTCR_RXTEN; return 1; } else { /* Check if the second PDC bank is free. */ if (p_adc->ADC_RNCR == 0) { p_adc->ADC_RNPR = (uint32_t) p_s_buffer; p_adc->ADC_RNCR = ul_size; return 1; } else { return 0; } } } #endif #endif /** * \brief Start ADC sample. * Initialize ADC, set clock and timing, and set ADC to given mode. */ static void start_adc(void) { /* Enable peripheral clock. */ #if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C uint32_t i; pmc_enable_periph_clk(ID_ADC); #elif SAM3U #ifdef ADC_12B pmc_enable_periph_clk(ID_ADC12B); #else pmc_enable_periph_clk(ID_ADC); #endif #endif /* Initialize ADC. */ /* * Formula: ADCClock = MCK / ( (PRESCAL+1) * 2 ) * For example, MCK = 64MHZ, PRESCAL = 4, then: * ADCClock = 64 / ((4+1) * 2) = 6.4MHz; */ #if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C /* Formula: * Startup Time = startup value / ADCClock * Startup time = 64 / 6.4MHz = 10 us */ adc_init(ADC, sysclk_get_cpu_hz(), 6400000, ADC_STARTUP_TIME_4); #elif SAM3U #ifdef ADC_12B /* Formula: * Startup Time = (startup value + 1) * 8 / ADCClock * Startup time = (7 + 1) * 8 / 6.4MHz = 10 us */ adc12b_init(ADC12B, sysclk_get_cpu_hz(), 6400000, STARTUP_TIME, OFF_MODE_STARTUP_TIME); #else /* Formula: * Startup Time = (startup value + 1) * 8 / ADCClock * Startup time = (3 + 1) * 8 / 3.2MHz = 10 us */ adc_init(ADC, sysclk_get_cpu_hz(), 3200000, STARTUP_TIME); #endif #endif memset((void *)&g_adc_sample_data, 0, sizeof(g_adc_sample_data)); /* Set ADC timing. */ #if SAM3S || SAM3XA || SAM4S /* Formula: * Transfer Time = (TRANSFER * 2 + 3) / ADCClock * Tracking Time = (TRACKTIM + 1) / ADCClock * Settling Time = settling value / ADCClock * * Transfer Time = (1 * 2 + 3) / 6.4MHz = 781 ns * Tracking Time = (1 + 1) / 6.4MHz = 312 ns * Settling Time = 3 / 6.4MHz = 469 ns */ adc_configure_timing(ADC, TRACKING_TIME, ADC_SETTLING_TIME_3, TRANSFER_PERIOD); #elif SAM3N || SAM4C adc_configure_timing(ADC, TRACKING_TIME); #elif SAM3U /* Formula: * Sample & Hold Time = SHTIM/ADCClock * * Sample & Hold Time = 6 / 6.4 = 938 ns */ #ifdef ADC_12B adc12b_configure_timing(ADC12B, SAMPLE_HOLD_TIME); #else adc_configure_timing(ADC, SAMPLE_HOLD_TIME); #endif #endif #if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C /* Enable channel number tag. */ adc_enable_tag(ADC); /* Enable/disable sequencer. */ if (g_adc_test_mode.uc_sequence_en) { /* Set user defined channel sequence. */ adc_configure_sequence(ADC, ch_list, 2); /* Enable sequencer. */ adc_start_sequencer(ADC); /* Enable channels. */ for (i = 0; i < 2; i++) { adc_enable_channel(ADC, (enum adc_channel_num_t)i); } /* Update channel number. */ g_adc_sample_data.uc_ch_num[0] = ch_list[0]; g_adc_sample_data.uc_ch_num[1] = ch_list[1]; } else { /* Disable sequencer. */ adc_stop_sequencer(ADC); /* Enable channels. */ adc_enable_channel(ADC, ADC_CHANNEL_POTENTIOMETER); #if SAM3S || SAM3XA || SAM4S || SAM4C adc_enable_channel(ADC, ADC_TEMPERATURE_SENSOR); #endif /* Update channel number. */ g_adc_sample_data.uc_ch_num[0] = ADC_CHANNEL_POTENTIOMETER; #if SAM3S || SAM3XA || SAM4S || SAM4C g_adc_sample_data.uc_ch_num[1] = ADC_TEMPERATURE_SENSOR; #else g_adc_sample_data.uc_ch_num[1] = ADC_CHANNEL_POTENTIOMETER; #endif } #elif SAM3U #ifdef ADC_12B adc12b_enable_channel(ADC12B, ADC_CHANNEL_POTENTIOMETER); #else adc_enable_channel(ADC, ADC_CHANNEL_POTENTIOMETER); #endif g_adc_sample_data.uc_ch_num[0] = ADC_CHANNEL_POTENTIOMETER; g_adc_sample_data.uc_ch_num[1] = ADC_CHANNEL_POTENTIOMETER; #endif #if SAM3S || SAM3XA || SAM4S || SAM4C /* Enable the temperature sensor. */ adc_enable_ts(ADC); #endif /* Set gain and offset (only single ended mode used here). */ #if SAM3S || SAM3XA || SAM4S adc_disable_anch(ADC); /* Disable analog change. */ #endif if (g_adc_test_mode.uc_gain_en) { #if SAM3S || SAM3XA || SAM4S adc_enable_anch(ADC); /* gain = 2 */ adc_set_channel_input_gain(ADC, ADC_CHANNEL_POTENTIOMETER, ADC_GAINVALUE_2); #elif SAM3U #ifdef ADC_12B adc12b_set_input_gain(ADC12B, ADC_GAINVALUE_2); #endif #endif } else { #if SAM3S || SAM3XA || SAM4S /* gain = 1 */ adc_set_channel_input_gain(ADC, ADC_CHANNEL_POTENTIOMETER, ADC_GAINVALUE_0); #elif SAM3U #ifdef ADC_12B adc12b_set_input_gain(ADC12B, ADC_GAINVALUE_0); #endif #endif } if (g_adc_test_mode.uc_offset_en) { #if SAM3S || SAM3XA || SAM4S adc_enable_anch(ADC); adc_enable_channel_input_offset(ADC, ADC_CHANNEL_POTENTIOMETER); #elif SAM3U #ifdef ADC_12B adc12b_enable_input_offset(ADC12B); #endif #endif } else { #if SAM3S || SAM3XA || SAM4S adc_disable_channel_input_offset(ADC, ADC_CHANNEL_POTENTIOMETER); #elif SAM3U #ifdef ADC_12B adc12b_disable_input_offset(ADC12B); #endif #endif } /* Set Auto Calibration Mode. */ #if SAM3S8 || SAM3SD8 || SAM4S if (g_adc_test_mode.uc_auto_calib_en) { adc_set_calibmode(ADC); while (1) { if ((adc_get_status(ADC) & ADC_ISR_EOCAL) == ADC_ISR_EOCAL) break; } } #endif #if SAM3S8 || SAM4S || SAM3N || SAM3SD8 /* Set power save. */ if (g_adc_test_mode.uc_power_save_en) { adc_configure_power_save(ADC, 1, 0); } else { adc_configure_power_save(ADC, 0, 0);; } #elif SAM3U || SAM4C #ifdef ADC_12B /* Set power save. */ if (g_adc_test_mode.uc_power_save_en) { adc12b_configure_power_save(ADC12B, 1, 0); } else { adc12b_configure_power_save(ADC12B, 0, 0);; } #else /* Set power save. */ if (g_adc_test_mode.uc_power_save_en) { adc_configure_power_save(ADC, 1); } else { adc_configure_power_save(ADC, 0);; } #endif #endif #if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C /* Transfer with/without PDC. */ if (g_adc_test_mode.uc_pdc_en) { adc_read_buffer(ADC, g_adc_sample_data.us_value, BUFFER_SIZE); /* Enable PDC channel interrupt. */ adc_enable_interrupt(ADC, ADC_IER_RXBUFF); } else { /* Enable Data ready interrupt. */ adc_enable_interrupt(ADC, ADC_IER_DRDY); } /* Enable ADC interrupt. */ NVIC_EnableIRQ(ADC_IRQn); #elif SAM3U #ifdef ADC_12B /* Transfer with/without PDC. */ if (g_adc_test_mode.uc_pdc_en) { adc12_read_buffer(ADC12B, g_adc_sample_data.us_value, BUFFER_SIZE); /* Enable PDC channel interrupt. */ adc12b_enable_interrupt(ADC12B, ADC12B_IER_RXBUFF); } else { /* Enable Data ready interrupt. */ adc12b_enable_interrupt(ADC12B, ADC12B_IER_DRDY); } /* Enable ADC interrupt. */ NVIC_EnableIRQ(ADC12B_IRQn); #else /* Transfer with/without PDC. */ if (g_adc_test_mode.uc_pdc_en) { adc_read_buffer(ADC, g_adc_sample_data.us_value, BUFFER_SIZE); /* Enable PDC channel interrupt. */ adc_enable_interrupt(ADC, ADC_IER_RXBUFF); } else { /* Enable Data ready interrupt. */ adc_enable_interrupt(ADC, ADC_IER_DRDY); } /* Enable ADC interrupt. */ NVIC_EnableIRQ(ADC_IRQn); #endif #endif /* Configure trigger mode and start convention. */ switch (g_adc_test_mode.uc_trigger_mode) { case TRIGGER_MODE_SOFTWARE: #if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C adc_configure_trigger(ADC, ADC_TRIG_SW, 0); /* Disable hardware trigger. */ #elif SAM3U #ifdef ADC_12B adc12b_configure_trigger(ADC12B, ADC12B_TRIG_SW); #else adc_configure_trigger(ADC, ADC_TRIG_SW); #endif #endif break; case TRIGGER_MODE_ADTRG: #if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C gpio_configure_pin(PINS_ADC_TRIG, PINS_ADC_TRIG_FLAG); adc_configure_trigger(ADC, ADC_TRIG_EXT, 0); #elif SAM3U #ifdef ADC_12B gpio_configure_pin(PINS_ADC12B_TRIG, PINS_ADC12B_TRIG_FLAG); adc12b_configure_trigger(ADC12B, ADC12B_TRIG_EXT); #else gpio_configure_pin(PINS_ADC_TRIG, PINS_ADC_TRIG_FLAG); adc_configure_trigger(ADC, ADC_TRIG_EXT); #endif #endif break; case TRIGGER_MODE_TIMER: configure_time_trigger(); break; #if SAM3S || SAM3U || SAM3XA || SAM4S case TRIGGER_MODE_PWM: configure_pwm_trigger(); break; #endif #if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C case TRIGGER_MODE_FREERUN: adc_configure_trigger(ADC, ADC_TRIG_SW, 1); break; #endif default: break; } } /** * \brief Systick handler. */ void SysTick_Handler(void) { gs_ul_ms_ticks++; } #if SAM3S || SAM3N || SAM3XA || SAM4S || SAM4C /** * \brief Interrupt handler for the ADC. */ void ADC_Handler(void) { uint32_t i; uint32_t ul_temp; uint8_t uc_ch_num; /* With PDC transfer */ if (g_adc_test_mode.uc_pdc_en) { if ((adc_get_status(ADC) & ADC_ISR_RXBUFF) == ADC_ISR_RXBUFF) { g_adc_sample_data.us_done = ADC_DONE_MASK; adc_read_buffer(ADC, g_adc_sample_data.us_value, BUFFER_SIZE); /* Only keep sample value, and discard channel number. */ for (i = 0; i < NUM_CHANNELS; i++) { g_adc_sample_data.us_value[i] &= ADC_LCDR_LDATA_Msk; } } } else { /* Without PDC transfer */ if ((adc_get_status(ADC) & ADC_ISR_DRDY) == ADC_ISR_DRDY) { ul_temp = adc_get_latest_value(ADC); for (i = 0; i < NUM_CHANNELS; i++) { uc_ch_num = (ul_temp & ADC_LCDR_CHNB_Msk) >> ADC_LCDR_CHNB_Pos; if (g_adc_sample_data.uc_ch_num[i] == uc_ch_num) { g_adc_sample_data.us_value[i] = ul_temp & ADC_LCDR_LDATA_Msk; g_adc_sample_data.us_done |= 1 << i; } } } } }