Problem Statement: K32L2B ADCs don't have the capability to scan through all the connected channels in one conversion. The input channels are muxed into the ADC, So you need to set the ADCx_Rn register to the channel number you want to convert, Run the conversion, read the results and then Set the other ADCx_Rn in that ADC to the next channel to be converted. The next conversion can then be triggered. You have to tell each conversion the input channel it is to convert. Everytime setting a new channel and reading previous channel data can consume much CPU core bandwidth.
The goal of this example is to read all ADC inputs of Kinetis K32L2B in a row without the need of using CPU core for switching channels and reading individual result values.
The DMA in this example is used for controlling all the channel switching, reading the results for series of measurement into a memory buffer.
The Direct Memory Access (DMA) channels are configured for writing and reading ADC registers the following way:
The data for the DMA channel 1 is prepared in the g_adc16ChannelDataArray prepared in memory and the DMA operates in the following cycle:
Step 1 In the beginning, the DMA channel 1 transfer is started using software trigger. This configures the channel and starts the conversion.
Step 2 After the conversion is complete, the result is read by DMA channel 0 and stored to results array. Channel linking executes the Channel 1 transfer and the cycle continues.
After all needed channels are measured (DMA byte counter reaches 0), the DMA Interrupt is invoked so the user code is notified.
See the following figure describing the process:
Code:
static void ADC16_Configuration(void)
{
// adc16_config_t adcUserConfig;
ADC16_GetDefaultConfig(&adcUserConfig);
adcUserConfig.resolution = kADC16_Resolution16Bit;
adcUserConfig.enableContinuousConversion = false;
adcUserConfig.clockSource = kADC16_ClockSourceAlt0;
adcUserConfig.enableAsynchronousClock = false;
adcUserConfig.clockDivider = kADC16_ClockDivider2;
adcUserConfig.longSampleMode = kADC16_LongSampleCycle24;
adcUserConfig.enableLowPower = false;
#if ((defined BOARD_ADC_USE_ALT_VREF) && BOARD_ADC_USE_ALT_VREF)
adcUserConfig.referenceVoltageSource = kADC16_ReferenceVoltageSourceValt;
#endif
ADC16_Init(DEMO_ADC16_BASEADDR, &adcUserConfig);
ADC16_SetHardwareAverage(DEMO_ADC16_BASEADDR,kADC16_HardwareAverageCount4);
/* Configure SIM for ADC hw trigger source selection */
// SIM->SOPT7 |= 0x8EU;
#if defined(FSL_FEATURE_ADC16_HAS_CALIBRATION) && FSL_FEATURE_ADC16_HAS_CALIBRATION
/* Auto calibration */
if (kStatus_Success == ADC16_DoAutoCalibration(DEMO_ADC16_BASEADDR))
{
PRINTF("ADC16_DoAutoCalibration() Done.\r\n");
}
else
{
PRINTF("ADC16_DoAutoCalibration() Failed.\r\n");
}
#endif
/* Enable software trigger. */
ADC16_EnableHardwareTrigger(DEMO_ADC16_BASEADDR, false);
/* Enable DMA. */
ADC16_EnableDMA(DEMO_ADC16_BASEADDR, true);
}
Code:
void get_DMA_Channel_link(dma_channel_link_config_t *config)
{
config->linkType = 2;
config->channel1 = 1;
}
static void DMA_config_ch_0()
{
DMA_CreateHandle(&g_DMA_Handle, DEMO_DMA_BASEADDR, DEMO_DMA_CHANNEL);
DMA_SetCallback(&g_DMA_Handle, DMA_Callback, NULL);
DMA_PrepareTransfer(&g_transferConfig, (void *)ADC16_RESULT_REG_ADDR, sizeof(uint32_t),
&g_adc16SampleDataArray[0], sizeof(uint32_t), 20,
kDMA_PeripheralToMemory);
DMA_SubmitTransfer(&g_DMA_Handle, &g_transferConfig, kDMA_EnableInterrupt);
get_DMA_Channel_link(&channel_link);
DMA_SetChannelLinkConfig(g_DMA_Handle.base,0,&channel_link);
}
static void DMA_config_ch_1()
{
DMA_CreateHandle(&g_DMA_Handle_channel_1, DEMO_DMA_BASEADDR, 1);
// DMA_SetCallback(&g_DMA_Handle_channel_1, DMA_Callback_channel_1, NULL);
DMA_PrepareTransfer(&g_transferConfig_channel_1,&g_adc16ChannelDataArray[0], sizeof(uint8_t),
(void *)CHANNEL_ADDRESS, sizeof(uint8_t), 5,
kDMA_MemoryToPeripheral);
DMA_SubmitTransfer(&g_DMA_Handle_channel_1, &g_transferConfig_channel_1, kDMA_NoOptions);
DMA_TriggerChannelStart(g_DMA_Handle_channel_1.base,1);
}
Attached is the Project for reference.
Do let me know if any other information required.
Thanks!!