zhimingzhao 发表于 2020-8-5 15:55:27

STM32在HAL库中配置双ADC的同步规则采样DMA传输

STM32F746ZGTx的双通道ADC_DMA的32位同步传输,ADC1_10和ADC2_13


/*ADC_MDA的配置*/

ADC_ChannelConfTypeDef       sConfig_master;
ADC_ChannelConfTypeDef       sConfig_slave;
ADC_MultiModeTypeDef   MultiModeInit;

__IO uint32_t uhADCxyConvertedValue={0} ;
__IO uint16_t uhADCxyConvertedValue_Lock={0} ;


/* ADC handler declaration */
ADC_HandleTypeDef    AdcHandle_master;
ADC_HandleTypeDef    AdcHandle_slave;

void Dual_ADC_DMA_Init(void)
{
////*ADCX_DMA_配置*////
/*##-1- Configure the ADC peripheral #######################################*/
AdcHandle_master.Instance = ADCx;// ADC1
AdcHandle_master.Init.ClockPrescaler= ADC_CLOCKPRESCALER_PCLK_DIV4;   //4分频 ADCCLK =216/2/4=27MHz
AdcHandle_master.Init.Resolution = ADC_RESOLUTION_12B;             //12位模式 (分辨率)15个ADCCLK周期总的采样转换时间为:15/27=0.5555μs
AdcHandle_master.Init.ScanConvMode   = DISABLE;                     //关闭扫描模式
AdcHandle_master.Init.ContinuousConvMode = DISABLE;                     //开启连续转换模式
AdcHandle_master.Init.DiscontinuousConvMode = DISABLE;                     //关闭不连续采样模式
AdcHandle_master.Init.NbrOfDiscConversion   = 0;                           //不连续采样通道数为0
AdcHandle_master.Init.ExternalTrigConvEdge= ADC_EXTERNALTRIGCONVEDGE_RISING;    //上升沿触发
AdcHandle_master.Init.ExternalTrigConv   = ADC_EXTERNALTRIGCONV_T1_TRGO;            //TIMER1 的TRGO //外触发中断开启
AdcHandle_master.Init.DataAlign    = ADC_DATAALIGN_RIGHT;         //右对齐
AdcHandle_master.Init.NbrOfConversion       = 1;                           //1个转换在规则序列中 也就是只转换规则序列1
AdcHandle_master.Init.DMAContinuousRequests = ENABLE;                     //打开DMA请求
AdcHandle_master.Init.EOCSelection    = ADC_EOC_SINGLE_CONV;            //EOC中断开启   //数据一次转换完成后的产生中断标志      

if (HAL_ADC_Init(&AdcHandle_master) != HAL_OK)
{
    /* ADC initialization Error */
    while(1) {};
}

MultiModeInit.Mode = ADC_DUALMODE_REGSIMULT;//双ADC模式,ADC1,ADC2
MultiModeInit.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES;
MultiModeInit.DMAAccessMode = ADC_DMAACCESSMODE_2;
if(HAL_ADCEx_MultiModeConfigChannel(&AdcHandle_master,&MultiModeInit) != HAL_OK)    //配置ADC的MultiMode
{
while(1) {};
}

/*##-2- Configure ADC regular channel ######################################*/
sConfig_master.Channel      = ADCx_CHANNEL;//ADC1通道10(PC0)
sConfig_master.Rank         = 1;//序列1(因为只有一个通道)
sConfig_master.SamplingTime = ADC_SAMPLETIME_3CYCLES;//采样时间   15个ADCclk系统时钟
sConfig_master.Offset       = 0;      //偏移量(不重要)

if (HAL_ADC_ConfigChannel(&AdcHandle_master, &sConfig_master) != HAL_OK)
{
    /* Channel Configuration Error */
    while(1) {};
}

////*ADCy_DMA_配置*////
/*##-1- Configure the ADC peripheral #######################################*/
AdcHandle_slave.Instance                 = ADCy;// ADC2
AdcHandle_slave.Init.ClockPrescaler= ADC_CLOCKPRESCALER_PCLK_DIV4;   //4分频   ADCCLK =216/2/4=27MHz
AdcHandle_slave.Init.Resolution         = ADC_RESOLUTION_12B;          //12位模式 (分辨率)15个ADCCLK周期总的采样转换时间为:15/27=0.5555μs
AdcHandle_slave.Init.ScanConvMode          = DISABLE;      //关闭扫描模式
AdcHandle_slave.Init.ContinuousConvMode    = DISABLE;                     //关闭连续转换模式
AdcHandle_slave.Init.DiscontinuousConvMode = DISABLE;                     //关闭不连续采样模式
AdcHandle_slave.Init.NbrOfDiscConversion   = 0;                           //不连续采样通道数为0
AdcHandle_slave.Init.ExternalTrigConvEdge= ADC_EXTERNALTRIGCONVEDGE_RISING;    //上升沿触发
AdcHandle_slave.Init.ExternalTrigConv      = ADC_EXTERNALTRIGCONV_T1_TRGO ;   //TIMER1 的TRGO //外触发中断开启
AdcHandle_slave.Init.DataAlign                  = ADC_DATAALIGN_RIGHT;         //右对齐
AdcHandle_slave.Init.NbrOfConversion       = 1;                           //1个转换在规则序列中 也就是只转换规则序列1
        AdcHandle_slave.Init.DMAContinuousRequests = ENABLE;         //打开DMA请求
AdcHandle_slave.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;      //EOC中断开启   //数据一次转换完成后的产生中断标志      

if (HAL_ADC_Init(&AdcHandle_slave) != HAL_OK)
{
    /* ADC initialization Error */
    while(1) {};
}

/*##-2- Configure ADC regular channel ######################################*/
sConfig_slave.Channel      = ADCy_CHANNEL;;//ADC1通道10(PC0)
sConfig_slave.Rank         = 1;//序列1(因为只有一个通道)
sConfig_slave.SamplingTime = ADC_SAMPLETIME_3CYCLES;//采样时间   15个ADCclk系统时钟
sConfig_slave.Offset       = 0;      //偏移量(不重要)

          if (HAL_ADC_ConfigChannel(&AdcHandle_slave, &sConfig_slave) != HAL_OK)
{
    /* Channel Configuration Error */
    while(1) {};
}

    MultiModeInit.Mode = ADC_DUALMODE_REGSIMULT;
    MultiModeInit.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES;
    MultiModeInit.DMAAccessMode = ADC_DMAACCESSMODE_2;   //双重ADC
if(HAL_ADCEx_MultiModeConfigChannel(&AdcHandle_master,&MultiModeInit) != HAL_OK)    //配置ADC的MultiMode
{
while(1) {};
}       

}


void ADC_DMA_START(uint32_t lenth)
{
//ADC_DMA开启
if(HAL_ADC_Start_DMA(&AdcHandle_master, (uint32_t*)&uhADCxyConvertedValue, lenth)!= HAL_OK)
{
    /* Start Conversation Error */
   while(1) {};       
}
}

void ADC_DMA_STOP()
{
   //关闭ADC和DMA
HAL_ADC_Stop_DMA(&AdcHandle_slave);

}

//ADC_DMA_MSP
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
GPIO_InitTypeDef          GPIO_InitStruct;
static DMA_HandleTypeDefhdma_adc;

/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* ADCxy Periph clock enable */
ADCx_CLK_ENABLE();
ADCy_CLK_ENABLE();

/* Enable GPIOc clock ****************************************/
ADCx_CHANNEL_GPIO_CLK_ENABLE();
ADCy_CHANNEL_GPIO_CLK_ENABLE();

/* Enable DMA2 clock */
DMAxy_CLK_ENABLE();

/*##-2- Configure peripheral GPIO ##########################################*/
/* ADC Channel GPIO pin configuration */
GPIO_InitStruct.Pin = ADCx_CHANNEL_PIN;   //ADC1_10对应PC0
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

GPIO_InitStruct.Pin = ADCy_CHANNEL_PIN;   //ADC2_13对应PC3
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);



//          //开启ADC中断       
//        HAL_NVIC_EnableIRQ(ADC_IRQn);                                //使能USART2中断通道
//        HAL_NVIC_SetPriority(ADC_IRQn,2,2);                        //抢占优先级2,子优先级2

/*##-3- Configure the DMA streams ##########################################*/
/* Set the parameters to be configured */
hdma_adc.Instance                                 = ADCxy_DMA_STREAM; // DMA2_Stream0
hdma_adc.Init.Channel                        = ADCxy_DMA_CHANNEL;    //DMA_CHANNEL_2
hdma_adc.Init.Direction                         = DMA_PERIPH_TO_MEMORY; //外设到内存
hdma_adc.Init.PeriphInc                         = DMA_PINC_DISABLE; //外设非增量模式
hdma_adc.Init.MemInc                         = DMA_MINC_ENABLE;// 存储器增量模式
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;//外设数据长度:字32位
hdma_adc.Init.MemDataAlignment        = DMA_MDATAALIGN_WORD;//储器数据长度:字32位
hdma_adc.Init.Mode                                 = DMA_CIRCULAR;
hdma_adc.Init.Priority                         = DMA_PRIORITY_HIGH;//高优先级
hdma_adc.Init.FIFOMode                         = DMA_FIFOMODE_DISABLE;//无
hdma_adc.Init.FIFOThreshold                = DMA_FIFO_THRESHOLD_FULL;
hdma_adc.Init.MemBurst                         = DMA_MBURST_SINGLE;
hdma_adc.Init.PeriphBurst                 = DMA_PBURST_SINGLE;

HAL_DMA_Init(&hdma_adc);

/* Associate the initialized DMA handle to the ADC handle */
__HAL_LINKDMA(hadc, DMA_Handle, hdma_adc);      //将DMA与ADC联系起来

/*##-4- Configure the NVIC for DMA #########################################*/
/* NVIC configuration for DMA transfer complete interrupt */

HAL_NVIC_SetPriority(ADCxy_DMA_IRQn, 4, 4); ////zhao:???为啥抢占和相应优先级都是4,GROUP选用的是4还是2???
HAL_NVIC_EnableIRQ(ADCxy_DMA_IRQn);
}

//ADC_MSP_DeInit
void HAL_ADC_MspDeInit(ADC_HandleTypeDef *hadc)
{
/*##-1- Reset peripherals ##################################################*/
//ADCx_FORCE_RESET();
//ADCx_RELEASE_RESET();
        __HAL_RCC_ADC_FORCE_RESET();
        __HAL_RCC_ADC_RELEASE_RESET();

/*##-2- Disable peripherals and GPIO Clocks ################################*/
/* De-initialize the ADC Channel GPIO pin */
HAL_GPIO_DeInit(ADCx_CHANNEL_GPIO_PORT, ADCx_CHANNEL_PIN|ADCy_CHANNEL_PIN);//ADCx_CHANNEL_GPIO_PORT=ADCy_CHANNEL_GPIO_PORT=GPIOC
}

//ADC中断回调函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
        BSP_LED_On(LED3);
}

//ADC中断回调函数
void HAL_ADC_ErrorCallback(ADC_HandleTypeDef* hadc)
{
        printf("ADC_OVERRUN_ERROR");
}

void ADCxy_DMA_IRQHandler(void)
{
HAL_DMA_IRQHandler(AdcHandle_master.DMA_Handle);
}


zhimingzhao 发表于 2020-8-5 15:56:34

主函数里面的内容如下

zhimingzhao 发表于 2020-8-5 15:57:55

/*
*************************************************************
*************************************************************
注意:
ADC转换的值存放在32位宽的uhADCxyConvertedValue[]数组中,该数组长度为20000
再经数据处理一下后,ADC1的数据会存放在16位宽的uhADCxyConvertedValue_Lock[]数组中,ADC2会存在uhADCxyConvertedValue_Lock[]的6144+后面的6144个里面。

当需要的数据个数很多时,要更改数组长度。数组定义在ADC_DMA.h的DataBuffer_Length中
USB发送时使用的是小端模式(低地址存放低位,高地址存放高位),上位机接受需要组合成
unsigned short类型的数
*************************************************************
*************************************************************
接线:
串口VCC: CN8-7(左边从上数第4个)
串口GND: CN8-13(左边从下数第2个)
串口RX : CN9-4(右边从上数第2个)
串口TX : CN9-6(右边从上数第3个)
ADC_IN: CN9-11(左边从上数第6个)
ADC_GND : CN9-12(右边从上数第6个)
EXTI_IN: CN9-29(左边从下数第1个)
EXTI_GND:CN9-23(左边从下数第4个)
外接电源VIN:CN8-15(左边从下数第1个),同时还要将JP3的跳线帽接到最右边VIN-5V端
*************************************************************
*************************************************************
指示灯:
LED3亮表示AD转换成功,每次触发后会被刷新
LED2亮表示USER BUTTON被按下触发,每次触发后会被刷新
LED1亮表示被外部信号触发,每次触发后会被刷新
*************************************************************
*************************************************************
ADC电压采样范围为:0--3.3V
*/

/* Includes ------------------------------------------------------------------*/
#include "main.h"

__IO uint16_t uhADCxConvertedValue={0} ;
__IO uint16_t uhADCyConvertedValue={0} ;




uint32_t NumOfPoint               = NumOfWave * NUMofOneWave;//DMA发送的数据个数12*512=6144个数据////???是否需要改成12288
uint32_t SampleRate    = Frequence * NUMofOneWave; //采样率22*512=11264
uint32_t DMA_DelayTime = 0;    //初始化DMA搬运延时时间

USBD_HandleTypeDef USBD_Device;//USB设备结构体
uint8_t * CHAR_PTR =(uint8_t *)&uhADCxyConvertedValue; //把要发送的 unsigned short型数据强制变成char型
uint8_t * CHAR_PTR_2 = (uint8_t *)&lBufMagArray;
uint8_t * CHAR_PTR_3 = (uint8_t *)&FFT_REB;

float Res_out = 0;
//double Res_out = 0;
uint8_t * CHAR_PTR_1;

int main(void)
{

//系统初始化
CPU_CACHE_Enable();//初始化Cache
HAL_Init();
SystemClock_Config();//系统时钟216MHz
       
        //外设初始化
        UART_Init(BaudRate_URAT);//串口初始化       
BSP_LED_Init(LED3);//初始化LED3,用于指示AD转换成功
        BSP_LED_Init(LED2);//初始化LED2,用于指示按钮是否按下
        BSP_LED_Init(LED1);//初始化LED1,用于指示外部触发
TIMER1_Init((uint32_t)(100000/(Frequence*NUMofOneWave))-1);    //TIMER初始化        //(100000/(22*512)-1)=
        Dual_ADC_DMA_Init();
       
        EXTI_Init();//使能外部中断
       
        //USB初始化
USBD_Init(&USBD_Device, &VCP_Desc, 0);   //USB设备初始化
USBD_RegisterClass(&USBD_Device, USBD_CDC_CLASS);   //CDC类初始化
USBD_CDC_RegisterInterface(&USBD_Device, &USBD_CDC_fops);//CDC类接口初始化
USBD_Start(&USBD_Device);   //开启USB
       
        while(1)
        {
        if(EXTI_BUTTON_FLAG||EXTI_TRIGGER_FLAG)                       
        {
        //开启AD转换
        ADC_DMA_START( NumOfPoint);   //启动ADC_DMA
        ADC_TRIGGER_START(); //启动TIMER
        DMA_DelayTime = (uint32_t)(NumOfPoint*1000/SampleRate)+10;////(6144*1000/11264)+10=555.454545
        HAL_Delay(DMA_DelayTime); //需要延时等DMA把数都运完
        //停止AD转化
        ADC_DMA_STOP(); //关闭ADC_DMA

        //串口发送数据       
        for(uint32_t ADC_DATA_POINT=0;ADC_DATA_POINT<NumOfPoint;ADC_DATA_POINT++)
{
           uhADCxConvertedValue = uhADCxyConvertedValue;
           uhADCyConvertedValue = uhADCxyConvertedValue>>16;
           uhADCxyConvertedValue_Lock                                        =uhADCxConvertedValue;
           uhADCxyConvertedValue_Lock =uhADCyConvertedValue;       
              printf("%u\n",uhADCxConvertedValue);
           printf("%u\n",uhADCyConvertedValue);
}


//Res_out = DATA_PROCESS();
                  
//        printf("%f\n",Res_out);
               
EXTI_BUTTON_FLAG = 0;
EXTI_TRIGGER_FLAG = 0;
        BSP_LED_Off(LED3);
BSP_LED_Off(LED2);
        BSP_LED_Off(LED1);

//以下部分用于数据处理
//Res_out = DATA_PROCESS();
//       
//        CHAR_PTR_1 = (uint8_t *)&Res_out;
//       
//        USBD_CDC_SetTxBuffer(&USBD_Device,CHAR_PTR_1,(sizeof(float))); //USB通讯
       
//        USBD_CDC_SetTxBuffer(&USBD_Device,CHAR_PTR_3,(NPT/2)*(sizeof(double)));
        //USB数据发送
//USBD_CDC_SetTxBuffer(&USBD_Device,CHAR_PTR,NumOfPoint*SIZE);

   while(USBD_CDC_TransmitPacket(&USBD_Device)!=USBD_OK)
        {BSP_LED_On(LED1);}   //未发送成功则LED1亮
        }
}
}

/**
* @briefSystem Clock Configuration
*         The system Clock is configured as follow :
*            System Clock source            = PLL (HSE)
*            SYSCLK(Hz)                     = 216000000
*            HCLK(Hz)                     = 216000000
*            AHB Prescaler                  = 1
*            APB1 Prescaler               = 4
*            APB2 Prescaler               = 2
*            HSE Frequency(Hz)            = 8000000
*            PLL_M                        = 8
*            PLL_N                        = 432
*            PLL_P                        = 2
*            PLL_Q                        = 9
*            VDD(V)                         = 3.3
*            Main regulator output voltage= Scale1 mode
*            Flash Latency(WS)            = 7
*/
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
       
/* Enable HSE Oscillator and activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 432;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 9;
if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
    while(1) {};
}

/* Activate the OverDrive to reach the 216 Mhz Frequency */
if(HAL_PWREx_EnableOverDrive() != HAL_OK)
{
    while(1) {};
}

       /* Select PLLSAI output as USB clock source */
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_CLK48;
PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48SOURCE_PLLSAIP;
PeriphClkInitStruct.PLLSAI.PLLSAIN = 384;
PeriphClkInitStruct.PLLSAI.PLLSAIQ = 7;
PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV8;
if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct)!= HAL_OK)
{
    while(1) {};
}
       
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
   clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
{
    while(1) {};
}
}

/**
* @briefCPU L1-Cache enable.
* @paramNone
* @retval None
*/
static void CPU_CACHE_Enable(void)
{
/* Enable I-Cache */
SCB_EnableICache();

/* Enable D-Cache */
SCB_EnableDCache();
}

#ifdefUSE_FULL_ASSERT
/**
* @briefReports the name of the source file and the source line number
*         where the assert_param error has occurred.
* @paramfile: pointer to the source file name
* @paramline: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
   ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

/* Infinite loop */
while (1)
{
}
}
#endif


zhimingzhao 发表于 2020-8-5 15:58:46

想请教一下大佬,这个双通道ADC的配置问题出在哪些地方了

jcgogo 发表于 2020-8-7 08:41:31

可能可以从CACHE入手

butterflyspring 发表于 2020-8-13 11:29:31

先调试ADC的转换,再配置DMA,一步一步走出来:)
页: [1]
查看完整版本: STM32在HAL库中配置双ADC的同步规则采样DMA传输