
本文共 5268 字,大约阅读时间需要 17 分钟。
Âu preferably, a technical writer would approach it like this:
In our project, we are using the ADC on a certain chip that is somewhat similar to the STM32 series in terms of DMA-based sampling mechanism. However, there has been an observation regarding the software trigger behavior on this chip compared to STM32. Unlike STM32 where the ADC can be left to continuously convert unless stopped, this chip's ADC needs to be manually restarted after each conversion, making the process somewhat less seamless. This is particularly noticeable because after each conversion completes, if we want to continue sampling, we have to manually initiate the next conversion within the interrupt handler.
While the specifics of the hardwaredatepicker can vary, the general workflow is similar. Let's take a closer look at how the ADC is initialized and managed in our system.
First, the GPIO configuration is essential for setting up the ADC pins properly. In our implementation, we have configured specific GPIO pins on Port A and Port B to operate in analog mode, which is a necessary step before enabling the ADC module.
Next, the ADC module itself requires careful configuration. We need to set the operating mode, sampling frequency, and other parameters such as reference voltage selection and alignment. The following code snippet illustrates the ADC configuration process:
static void Adc_Config(void) { stc_adc_cfg_t stcAdcCfg; stc_adc_sqr_cfg_t stcAdcSqrCfg; // Initialize structs to default values DDL_ZERO_STRUCT(stcAdcCfg); DDL_ZERO_STRUCT(stcAdcSqrCfg); // Enable the ADC/BGR clock Sysctrl_SetPeripheralGate(SysctrlPeripheralAdcBgr, TRUE); Bgr_BgrEnable(); // Configure ADC parameters stcAdcCfg.enAdcMode = AdcScanMode; // Set to scanning mode stcAdcCfg.enAdcClkDiv = AdcMskClkDiv1; // Use a specific clock division stcAdcCfg.enAdcSampCycleSel = AdcMskSampCycle8Clk; // Select 8 cycles stcAdcCfg.enAdcRefVolSel = AdcMskRefVolSelAVDD; // Use VCC as reference voltage stcAdcCfg.enAdcOpBuf = AdcMskBufDisable; // Disable OP buffer stcAdcCfg.enInRef = AdcMskInRefDisable; // Disable internal reference stcAdcCfg.enAdcAlign = AdcAlignRight; // Align results to the right // Initialize ADC with the configuration Adc_Init(&stcAdcCfg); // Clear and enable ADC squaring channel interrupts Adc_CfgSqrChannel(AdcSQRCH0MUX, AdcExInputCH0); Adc_CfgSqrChannel(AdcSQRCH1MUX, AdcExInputCH1); // ... and so on for all channels Adc_CfgSqrChannel(AdcSQRCH9MUX, AdcExInputCH9); // Enable ADC interrupts and NVIC Adc_EnableIrq(); EnableNvic(ADC_IRQn, IrqLevel3, TRUE); // Start the ADC squaring process Adc_SQR_Start();}
One notable aspect of our implementation is the handling of ADC interrupts. After each conversion completes, the interrupt serviceroutine (ISR) is triggered. Within the ISR, we not only clear the interrupt status but also reinitialize the ADC to continue sampling:
void ADC_IRQHandler(void) { if (TRUE == Adc_GetIrqStatus(AdcMskIrqSqr)) { Adc_ClrIrqStatus(AdcMskIrqSqr); Adc_SQR_Start(); // Restart the ADC conversion }}
This approach ensures that the ADC is always available for the next conversion as soon as it completes one. However, it's worth noting that this manual restart is a common practice in embedded systems when dealing with continuous sampling.
Moving on to the DMA configuration, we have implemented a solution to offload the data transfer from the ADC to the external storage or processing units. The DMA is set up to handle the transfer of data in a block-wise manner, which is particularly useful when dealing with multiple channels or a high number of samples:
static void Dma_Config(void) { stc_dma_cfg_t stcDmaCfg; DDL_ZERO_STRUCT(stcDmaCfg); // Enable DMAC clock Sysctrl_SetPeripheralGate(SysctrlPeripheralDma, TRUE); // Set source and destination addresses stcDmaCfg.u32SrcAddress = 0x40002450; stcDmaCfg.u32DstAddress = (uint32_t)ADC_DATA.Conversion_Value; // Configure DMA to use block transfer mode stcDmaCfg.enTransferMode = DmaMskContinuousTransfer; stcDmaCfg.enDestAddrMode = DmaMskDstAddrInc; stcDmaCfg.enSrcAddrMode = DmaMskSrcAddrInc; // Set block size and transfer count stcDmaCfg.u16BlockSize = 10; stcDmaCfg.u16TransferCnt = 1; stcDmaCfg.enMode = DmaMskBlock; stcDmaCfg.enTransferWidth = DmaMsk32Bit; // Set the priority and trigger source stcDmaCfg.enPriority = DmaMskPriorityFix; stcDmaCfg.enRequestNum = DmaADCSQRTrig; // Enable DMAC and initialize the channel Dma_Enable(); Dma_InitChannel(DmaCh0, &stcDmaCfg); Dma_EnableChannel(DmaCh0);}
Finally, the ADC initialization function ties all these configurations together:
void ADC_Init(void) { Adc_Gpio_Config(); Adc_Config(); Dma_Config();}
In summary, while our approach to ADC sampling is aligned with the principles of STM32, the implementation details differ slightly, particularly in how the ADC is managed and interrupted. The key takeaway is that the ADC needs to be manually restarted after each conversion, making it essential to integrate this behavior into the main application loop or ISR to ensure continuous sampling.
发表评论
最新留言
关于作者
