天天看點

STM32使用LL庫ADC+DMA配置過程中遇到的一些問題STM32使用LL庫ADC+DMA配置過程中遇到的一些問題

STM32使用LL庫ADC+DMA配置過程中遇到的一些問題

使用LL庫配置ADC+DMA,使用了AIN3,AIN5,兩個ADC通道不連續,使用掃描模式,連續轉換,使用DMA通道1,使用了CUBE_MX配置,這個不難。

STM32使用LL庫ADC+DMA配置過程中遇到的一些問題STM32使用LL庫ADC+DMA配置過程中遇到的一些問題
STM32使用LL庫ADC+DMA配置過程中遇到的一些問題STM32使用LL庫ADC+DMA配置過程中遇到的一些問題
STM32使用LL庫ADC+DMA配置過程中遇到的一些問題STM32使用LL庫ADC+DMA配置過程中遇到的一些問題
STM32使用LL庫ADC+DMA配置過程中遇到的一些問題STM32使用LL庫ADC+DMA配置過程中遇到的一些問題

接下來是代碼:

ADC初始化

void MX_ADC1_Init(void)
{
  LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
  LL_ADC_InitTypeDef ADC_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);
  
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
  /**ADC1 GPIO Configuration  
  PA3   ------> ADC1_IN3
  */
  GPIO_InitStruct.Pin = ADC_IN3_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(SHORT_IN_GPIO_Port, &GPIO_InitStruct);
	/* 
	PA5 -----> ADC1_IN5
	*/
	GPIO_InitStruct.Pin = ADC_IN5_Pin;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
	LL_GPIO_Init(SHORT_IN_GPIO_Port, &GPIO_InitStruct);
	
	/* ADC1 DMA Init */
  
  /* ADC1 Init */
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_ADC1);

  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

  LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);

  LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);

  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_WORD);

  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_WORD);
	
  LL_ADC_EnableInternalRegulator(ADC1);
	 /** Configure Regular Channel 
  */
  //若要編譯器優化代碼,此處是關鍵
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_3 | LL_ADC_CHANNEL_5);
  /** Configure Regular Channel 
  */
//這裡要特别注意,如果使用了代碼優化,就隻使用上面的語句就可以
//  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_5);
	
  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
  */
  ADC_InitStruct.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV2;
  ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_10B;
  ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
  ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
  LL_ADC_Init(ADC1, &ADC_InitStruct);
  
	ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
  ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
  ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS;
  ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
  ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
	
  LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
  LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
  LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_FIXED);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_39CYCLES_5);
  LL_ADC_DisableIT_EOC(ADC1);
  LL_ADC_DisableIT_EOS(ADC1);
  LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD);
}
           

DMA初始化

void MX_DMA_Init(void) 
{
  /* Init with LL driver */
  /* DMA controller clock enable */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
  /* DMA interrupt init DMA 照常省略掉中斷 */ 
  /* DMA1_Channel1_IRQn interrupt configuration */
//  NVIC_SetPriority(DMA1_Channel1_IRQn, 0);
//  NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
           

main函數中使能ADC和DMA

/* Run the ADC calibration  使能ADC */
	LL_ADC_StartCalibration(ADC1);
	while( LL_ADC_IsCalibrationOnGoing(ADC1));
	LL_ADC_Enable(ADC1);
	LL_ADC_REG_StartConversion(ADC1);
	LL_mDelay(2); //延時2ms,避免DMA讀取資料通道不對
	//DMA使能 這個是關鍵,不能放到adc校準前面否則讀出來的資料會不對
	LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_1,2);
	LL_DMA_SetPeriphAddress(DMA1,LL_DMA_CHANNEL_1,LL_ADC_DMA_GetRegAddr(ADC1,LL_ADC_DMA_REG_REGULAR_DATA));
	LL_DMA_SetMemoryAddress(DMA1,LL_DMA_CHANNEL_1,(uint32_t)&AD_DMA);
	LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_1);
	
           

這裡講優化編譯時出現的問題,如果使用如下2條語句來添加通道的話,經過keil -o1級編譯優化後,則隻會得到通道3的資料,可以在debug時打開ADC外設來檢視ADC寄存器初始化了哪幾個通道,這裡我懷疑是後面初始化通道5的語句被keil優化了。

LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_3);
LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_5);
           

解決這個問題的辦法就是使用下面這個語句:

LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_3 | LL_ADC_CHANNEL_5);
           

此語句與上面2條語句等效,經過測試是可以完美解決上面的問題。

這裡還遇到另外一個問題就是可能會出現DMA采集資料和ADC通道對應不上的問題,之前在另一份代碼中3 ADC通道+DMA通過先使能ADC再使能DMA可以解決這個問題,但是在這裡問題沒有得到解決,原因目前不知,在網上的結論可能是ADC校準時會導緻DMA傳輸資料,但是我在使能ADC之前已經先校準ADC,還是會出現這個問題,後來我在使能DMA之前延時2ms,發現問題得到解決,是以我認為出現這個問題可能是因為校準ADC時産生的資料還沒有被ADC正式運轉時采樣的資料覆寫,這時候啟動DMA,DMA立馬去ADC那裡查詢到有資料,然後傳輸過來,這就導緻了通道錯亂,如果延時一下,ADC采樣的資料就會覆寫掉校準時的資料,這時候DMA去傳輸ADC的資料才是正确的。具體的請看mai函數中使能ADC和DMA那段。至于ADC使能後要延時多久,這個個人認為是延時的這個時間至少要大于ADC的采樣時間,也就是讓ADC有足夠的時間去重新整理資料寄存器。

繼續閱讀