STM32F0多通道ADC的校準順序與DMA亂序問題的本質
聲明:本段轉載:https://www.cnblogs.com/chihirosan/p/5458673.html
-
問題描述
通過 uint16_t ConvData[8]保存DMA搬運的ADC轉換數值,但是這個數組數值的順序總是和ADC不是順序對應的。比如用7個通道的ADC,- 當設置ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Backward,是對應順序是:0->0,1->7,2->6…7->1 ;
- 當設置ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward,是對應順序是:0->7,1->0,2->1…7->6 。
-
問題原因
F0的ADC在使用之前需要校準。這個7位的校準值也是放在ADC_DR中的,它也會觸發DMA請求。
可以參照F0的ADC-DMA例程,先做ADC校準、然后再設置DMA,再使能ADC的DMA。 -
實例代碼
void ADC1_DMA_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;DMA_InitTypeDef DMA_InitStructure;ADC_InitTypeDef ADC_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = 0x00ff;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA, &GPIO_InitStructure);RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);ADC_DeInit(ADC1); //ADC恢復默認設置ADC_StructInit(&ADC_InitStructure); //初始化ADC結構ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位精度ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //規定模式裝換工作在連續模式ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //數據對其為右對齊ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward; // ADC_ScanDirection_Backward; //ADC的掃描方向ADC_Init(ADC1, &ADC_InitStructure);ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_1, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_2, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_3, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_4, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_5, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_6, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_7, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_GetCalibrationFactor(ADC1); /* ADC Calibration */ADC_Cmd(ADC1, ENABLE); /* Enable ADCperipheral[PerIdx] */while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY)); /* Wait the ADCEN falg *///設置DMA要在校準ADC之后DMA_DeInit(DMA1_Channel1); /* DMA1 Channel1 Config */DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) 0x40012440; //ADC1->DR; //外設地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) RegularConvData_Tab; //內存地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外設作為數據傳輸的來源DMA_InitStructure.DMA_BufferSize = 8; //DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設地址寄存器不變DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內存地址寄存器不變DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //數據寬度為16位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //數據寬度為16位DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA_Mode_Circular;DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA_Priority設定DMA通道x的軟件優先級DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x沒有設置為內存到內存傳輸DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_Cmd(DMA1_Channel1, ENABLE);/* DMA1 Channel1 enable */DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular); /* Enable ADC_DMA */ADC_DMACmd(ADC1, ENABLE);ADC_StartOfConversion(ADC1); /* ADC1 regular Software Start Conv */}
原問題現象與深層原因
-
DMA在ADC中的應用
- ADC多通道掃描時,DMA用于自動搬運轉換結果。但STM32F0系列的ADC包含校準值存儲機制。
- 校準階段:調用
ADC_GetCalibrationFactor()
時,會將一個7位的校準值寫入ADC_DR
寄存器(與轉換結果共用同一地址),并觸發DMA請求。
-
DMA亂序的成因
- 過早啟用DMA(如在校準前)會導致DMA將校準值當做首個有效數據搬運,后續通道數據依次錯位。
- 數值錯位實例:
- 若ADC通道0的值在
ADC_DR
中的實際存儲順序應為第1個,但因校準值的介入,DMA搬運時該值會出現在數組的第二個位置。
- 若ADC通道0的值在
-
解決方案的工程意義
- 校準后配置DMA:確保DMA僅搬運有效轉換結果。關鍵代碼如下:
ADC_GetCalibrationFactor(ADC1); // 先校準 DMA_Init(DMA1_Channel1, ...); // 后配置DMA ADC_DMACmd(ADC1, ENABLE); // 最后使能ADC的DMA請求
- 校準后配置DMA:確保DMA僅搬運有效轉換結果。關鍵代碼如下:
-
ADC掃描方向的補充解釋
ADC_ScanDirection
參數定義通道掃描的物理順序:- Upward模式:從通道編號低到高掃描(如0→1→2)。
- Backward模式:從編號高到低掃描(如2→1→0)。
此參數需要與DMA數組的預期存儲順序一致,否則需軟件層調整數組索引。
擴展思考:其他引發DMA錯位的可能
- 未清除DMA緩存:DMA傳送前后未重置緩存區,殘留數據可能導致混淆。
- 中斷搶占沖突:若ADC中斷優先級低于其他中斷,可能因響應延遲導致數據覆蓋。
總結
- 編碼規范的重要性:結構體聲明的位置不僅是語法問題,更影響代碼可移植性。
- 硬件機制的深度理解:結合芯片手冊分析異常(如STM32F0校準值特性),能快速定位隱蔽問題。