硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1378|回复: 10
收起左侧

[有问必答] ADC+DMA+TIM3 采样只能进行一次问题

[复制链接]

0

主题

4

回帖

4

积分

新手上路

积分
4
发表于 2022-8-11 11:23:12 | 显示全部楼层 |阅读模式
本帖最后由 xiaoyaoa 于 2022-8-11 11:31 编辑

各位好,是这样的,我目标使用STM32F407ADC1采集一路波形,并显示在屏幕上。
但是我完成一次采集显示后(是正确的),再次按下按键就不行了,需要重新把ADC和DMA初始化一遍才能再次完成采样和显示我不知道是ADC的问题还是DMA的问题,但是我注释掉任意一个初始化都不能再次完成采样和显示。

配置ADC1如下
void ADC1_DMA_Init(void)
{
        ADC_InitTypeDef ADC_InitStructure;
        GPIO_InitTypeDef GPIO_InitStructure;
        ADC_CommonInitTypeDef ADC_CommonInitStructure;
        
        TIM3_TRGO_Init(21-1,8-1);    //84M/21/8=500K 采样频率
        
        /*开启ADC及GPIO时钟*/
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOA时钟
        
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;       //模拟输入
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;  //无上下拉
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
        
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);          //ADC1复位
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);        //复位结束
        
        ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
        ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;//两个采样阶段之间的延迟5个时钟
        ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
        ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz
        ADC_CommonInit(&ADC_CommonInitStructure);//初始化
        
        ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式
        ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式        
        ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_Rising; //设置为上升沿触发
        ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConv_T3_TRGO;//TRGO触发
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐        
        ADC_InitStructure.ADC_NbrOfConversion = 1;//1个转换在规则序列中 也就是只转换规则序列1
        ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
        
        ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_3Cycles);        //ADC1提高采样时间可以提高精确度        
        ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);   
        
        ADC_Cmd(ADC1, ENABLE);            //开启AD转换器        
        ADC_DMACmd(ADC1,ENABLE);
}




配置DMA如下
void DMA_ADC1_Config(void)
{
        NVIC_InitTypeDef NVIC_InitStructure;
        DMA_InitTypeDef DMA_InitStructure;  
        
        DMA_DeInit(DMA2_Stream0);    //复位DMA数据流0
        
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE); //开启DMA时钟        
        /*开启DMA响应数据流中断*/
        NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;  
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;      
        NVIC_Init(&NVIC_InitStructure);
        
        /*DMA基本配置*/
        DMA_InitStructure.DMA_Channel = DMA_Channel_0;     //选择DMA通道0
        DMA_InitStructure.DMA_BufferSize=ADC_Size;     //待传输数据数目                    
        DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;     //DMA单次传输模式               
        DMA_InitStructure.DMA_Priority=DMA_Priority_High;   //DMA优先级 高
        /*设置DMA传输方向*/
        DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralToMemory;   //从外设到存储器
        /*设置DMA外设*/
        DMA_InitStructure.DMA_PeripheralBaseAddr=(u32)&(ADC1->DR);   //DMA源地址(外设地址)
        DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single; //外设突发模式选择 单次
        DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;   //外设数据宽度为半个字(16位)
        DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;     //外设地址不自增
        /*设置DMA存储器*/
        DMA_InitStructure.DMA_Memory0BaseAddr=(u32)&ADC1_DMA_BUF;   //目标地址(存储器地址)
        DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single;    //存储器突发模式选择  单次   节拍数*数据宽度
        DMA_InitStructure.DMA_MemoryDataSize=DMA_PeripheralDataSize_HalfWord;    //存储器数据宽度为半个字(16位)
        DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;   //存储器地址递增
        /*设置DMA FIFO模式*/
        DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable;    //是否使能FIFO模式
        DMA_InitStructure.DMA_FIFOThreshold=DMA_FIFOThreshold_HalfFull;   //设置FIFO阙值
        DMA_Init(DMA2_Stream0,&DMA_InitStructure);  

        DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TC);
        DMA_ITConfig(DMA2_Stream0,DMA_IT_TC, ENABLE);                //使能传输完成中断
}
DMA中断函数如下
void  DMA2_Stream0_IRQHandler(void)
{
        if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))
        {
                ADC_DMA_EndFlag=1;                                      //DMA传输一次完成标志位
                DMA_ClearITPendingBit(DMA2_Stream0,DMA_IT_TCIF0);
        }                                                                                                                                            
}



我通过按下按键 开启定时器3(开启AD采样)和DMA数据流0 进行采样
DMA_Cmd(DMA2_Stream0,ENABLE);         //使能DMA
TIM_Cmd(TIM3,ENABLE);

检测到ADC_DMA_EndFlag标志位置1就画波形、关闭采样和DMA

if(ADC_DMA_EndFlag)
{
        DMA_Cmd(DMA2_Stream0,DISABLE);  //关闭DMA
        ADC_DMA_EndFlag=0;   //标志位清零
        DMA_ClearFlag(DMA2_Stream0,DMA_FLAG_TCIF0); //清除DMA完成标志位
        TIM_Cmd(TIM3, DISABLE);  //停止ADC1
                        
        WaveformDisplay(30,2,230,30,ADC1_DMA_BUF,sizeof(ADC1_DMA_BUF)/sizeof(ADC1_DMA_BUF[0]),4096);   //画波形
               
        DMA_ADC1_Config();    //重新初始化DMA
        ADC1_DMA_Init();         //重新初始化ADC1  这两个函数是我在main函数里对DMA和ADC进行初始化的函数
}

我不明白的是为什么必须重新初始化ADC和DMA才能再次采样
ps:我尝试了改变DMA_Mode参数,改变为DMA_Mode_Normal,在关闭DMA后重新设置数据个数 DMA_SetCurrDataCounter(DMA1_Stream0,320);,但是并不行
                                                         改变为DMA_Mode_Circular,也必须要 DMA_ADC1_Config();  ADC1_DMA_Init();  这两步,再次按下按键才能画出波形
       经过我的试验,再次按下按键,开启采样和DMA后(未经再次初始化),是因为不能进入DMA中断,使ADC_DMA_EndFlag置1,不知道是哪里配置有问题。

       望各位指教,不胜感激!



回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107439
QQ
发表于 2022-8-11 14:56:06 | 显示全部楼层
主要是DMA有几个参数必须要重新配置才可以使用。
回复

使用道具 举报

0

主题

4

回帖

4

积分

新手上路

积分
4
 楼主| 发表于 2022-8-11 15:24:24 | 显示全部楼层
eric2013 发表于 2022-8-11 14:56
主要是DMA有几个参数必须要重新配置才可以使用。

我按照网上说的,重设了数据长度但是还是不行,请问还有哪几个参数呢
如果我用循环模式是不是就不用重设了呢
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107439
QQ
发表于 2022-8-11 15:43:10 | 显示全部楼层
xiaoyaoa 发表于 2022-8-11 15:24
我按照网上说的,重设了数据长度但是还是不行,请问还有哪几个参数呢
如果我用循环模式是不是就不用重设 ...

主要是DMA源地址,目的地址和传输次数要重新配置。
然后循环模式的话,是一直在循环传输,不用重复开关了。
回复

使用道具 举报

0

主题

4

回帖

4

积分

新手上路

积分
4
 楼主| 发表于 2022-8-11 15:55:11 | 显示全部楼层
eric2013 发表于 2022-8-11 15:43
主要是DMA源地址,目的地址和传输次数要重新配置。
然后循环模式的话,是一直在循环传输,不用重复开关 ...

但是我只重设DMA的参数也不行,还必须对ADC也做一些设置,必须在完成一次采样显示后进行如下操作(排除法排除了一些语句)以及对DMA的一些重设,才能实现按下按键就采样显示。
这是为什么呢,是因为我用定时器触发的原因吗?

ADC_InitTypeDef ADC_InitStructure;
       
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);          //ADC1复位
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);        //复位结束

        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_Rising; //设置为上升沿触发
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConv_T3_TRGO;//禁止触发检测,使用软件触发
  ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
       
        ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_3Cycles);        //ADC1,ADC通道,480个周期,提高采样时间可以提高精确度          
       
        ADC_Cmd(ADC1, ENABLE);            //开启AD转换器       
        ADC_DMACmd(ADC1,ENABLE);
回复

使用道具 举报

0

主题

4

回帖

4

积分

新手上路

积分
4
 楼主| 发表于 2022-8-11 17:30:45 | 显示全部楼层
xiaoyaoa 发表于 2022-8-11 15:55
但是我只重设DMA的参数也不行,还必须对ADC也做一些设置,必须在完成一次采样显示后进行如下操作(排除法 ...

ADC的部分搞清楚了,只需加一句 ADC1->SR = 0;
SR是ADC状态寄存器
应该是将ADC1转换完成之类的状态标志位清零
回复

使用道具 举报

0

主题

4

回帖

4

积分

新手上路

积分
4
 楼主| 发表于 2022-8-11 19:24:40 | 显示全部楼层
xiaoyaoa 发表于 2022-8-11 17:30
ADC的部分搞清楚了,只需加一句 ADC1->SR = 0;
SR是ADC状态寄存器
应该是将ADC1转换完成之类的状态标志 ...

DMA需要先关闭数据流传输,清除DMA完成标志位,设置传输数据个数(次数)
DMA_Cmd(DMA2_Stream0,DISABLE);   //关闭数据流传输
TIM_Cmd(TIM3,DISABLE);   //停止ADC采样
DMA_ClearFlag(DMA2_Stream0,DMA_FLAG_TCIF0); //清除DMA完成标志位
ADC1->SR = 0;      //清零ADC状态寄存器
DMA_SetCurrDataCounter(DMA2_Stream0,320);   //设置传输数据个数(次数)为320

再按下按键,即可再次开启采样和DMA传输(好像不用设置DMA源地址和目的地址,我再试试),非常感谢硬汉哥
PS:我参考了这篇文章https://blog.csdn.net/weixin_42094842/article/details/104276684
回复

使用道具 举报

41

主题

215

回帖

338

积分

高级会员

积分
338
发表于 2022-8-11 19:29:53 | 显示全部楼层
谢谢兄弟,下周调103的dma,抄你了
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107439
QQ
发表于 2022-8-12 09:15:39 | 显示全部楼层
xiaoyaoa 发表于 2022-8-11 19:24
DMA需要先关闭数据流传输,清除DMA完成标志位,设置传输数据个数(次数)
DMA_Cmd(DMA2_Stream0,DISABL ...

如果你源地址和目的地址不变,不用动,主要是传输次数要重新配置。

为什么传输次数要重新配置,主要是因为传输次数寄存器每传输一次都会递减,传输完毕就成0了,所以下次使用要重新配置。
回复

使用道具 举报

41

主题

215

回帖

338

积分

高级会员

积分
338
发表于 2022-8-17 22:20:05 | 显示全部楼层
本帖最后由 snakeemail 于 2022-8-19 21:50 编辑

你没有用HAL库去配置?
cube自动生成代码后,只要DMA中断函数加一个标志,DMA完成后,看标志, 再开启函数HAL_DMA_Start_IT就行了。不需要那么多配置。
stm32的ADC最有用的是两个ADC同步采样,其它的都没有太大实际用处。
大部分的场景,都是开定时器,定时触发ADC,然后DMA把ADC结果放到内存,最后DMA完成后,设个标志,处理数据
定时器
CNT达到ARR寄存器的值后,
Interrupt/DMA generation on the following events:
– Update: counter overflow/underflow, counter initialization (by software or internal/external trigger)
  就是说update event,就是计数器移除,或者初始化时
– Trigger event (counter start, stop, initialization or count by internal/external trigger)
– Input capture
– Output compare
Bits 6:4 MMS[2:0]: Master mode selection
These bits allow to select the information to be sent in master mode to slave timers for
synchronization (TRGO). The combination is as follows:
000: Reset - the UG bit from the TIMx_EGR register is used as trigger output (TRGO). If the
reset is generated by the trigger input (slave mode controller configured in reset mode) then
the signal on TRGO is delayed compared to the actual reset.
001: Enable - the Counter Enable signal CNT_EN is used as trigger output (TRGO). It is
useful to start several timers at the same time or to control a window in which a slave timer is
enable. The Counter Enable signal is generated by a logic OR between CEN control bit and
the trigger input when configured in gated mode. When the Counter Enable signal is
controlled by the trigger input, there is a delay on TRGO, except if the master/slave mode is
selected (see the MSM bit description in TIMx_SMCR register).
010: Update - The update event is selected as trigger output (TRGO). For instance a master
timer can then be used as a prescaler for a slave timer. 就是说update event发生,可以作为TRGO输出
011: Compare Pulse - The trigger output send a positive pulse when the CC1IF flag is to be
set (even if it was already high), as soon as a capture or a compare match occurred.
(TRGO).
100: Compare - OC1REF signal is used as trigger output (TRGO)
101: Compare - OC2REF signal is used as trigger output (TRGO)
110: Compare - OC3REF signal is used as trigger output (TRGO)
111: Compare - OC4REF signal is used as trigger output (TRGO)
ADC
Regular simultaneous mode
This mode is performed on a regular channel group. The source of the external trigger
comes from the regular group mux of ADC1 (selected by the EXTSEL[2:0] bits in the
ADC1_CR2 register). A simultaneous trigger is provided to the ADC2.
Note: Do not convert the same channel on the two ADCs (no overlapping sampling times for the
two ADCs when converting the same channel).
At the end of conversion event on ADC1 or ADC2:
•
A 32-bit DMA transfer request is generated (if DMA bit is set) which transfers to SRAM
the ADC1_DR 32-bit register containing the ADC2 converted data in the upper
halfword and the ADC1 converted data in the lower halfword.
•
An EOC interrupt is generated (if enabled on one of the two ADC interfaces) when
ADC1/ADC2 regular channels are all converted.
Note: In regular simultaneous mode, exactly the same sampling time should be configured for the
two channels that will be sampled simultaneously by ACD1 and ADC2.
每个ADC都有独立的Regular序列

11.3.8 Scan mode 感觉是不是要设置为使能?即使只有1AD要转换
This mode is used to scan a group of analog channels.
Scan mode can be selected by setting the SCAN bit in the ADC_CR1 register. Once this bit
is set, ADC scans all the channels selected in the ADC_SQRx registers (for regular
channels) or in the ADC_JSQR (for injected channels). A single conversion is performed for
each channel of the group. After each end of conversion the next channel of the group is
converted automatically. If the CONT bit is set, conversion does not stop at the last selected
group channel but continues again from the first selected group channel.
When using scan mode, DMA bit must be set and the direct memory access controller is
used to transfer the converted data of regular group channels to SRAM after each update of
the ADC_DR register.
The injected channel converted data is always stored in the ADC_JDRx registers.

HAL_ADCEx_MultiModeStart_DMA
HAL_StatusTypeDef HAL_ADCEx_Calibration_Start (ADC_HandleTypeDef * hadc)
HAL_StatusTypeDef HAL_ADCEx_MultiModeStop_DMA (ADC_HandleTypeDef * hadc)
SerialView软件
Multimode is kept enabled after this function. To disable multimode (set with
HAL_ADCEx_MultiModeConfigChannel(), ADC must be reinitialized using HAL_ADC_Init() or
HAL_ADC_ReInit().


回复

使用道具 举报

41

主题

215

回帖

338

积分

高级会员

积分
338
发表于 2022-8-21 17:03:33 | 显示全部楼层
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;


DMA_HandleTypeDef hdma_adc1;




void ADC1_2_IRQHandler(void)
{
  /* USER CODE BEGIN ADC1_2_IRQn 0 */

  /* USER CODE END ADC1_2_IRQn 0 */
  HAL_ADC_IRQHandler(&hadc1);
  HAL_ADC_IRQHandler(&hadc2);
  /* USER CODE BEGIN ADC1_2_IRQn 1 */

  /* USER CODE END ADC1_2_IRQn 1 */
}


/**
  * @brief ADC1 Initialization Function
  * @param None
  * @retval None
  */
void MX_ADC1_Init(void)
{
  
  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */
  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE; // ? ENABLE
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_DUALMODE_REGSIMULT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_6;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

/**
  * @brief ADC2 Initialization Function
  * @param None
  * @retval None
  */
void MX_ADC2_Init(void)
{

  /* USER CODE BEGIN ADC2_Init 0 */

  /* USER CODE END ADC2_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC2_Init 1 */

  /* USER CODE END ADC2_Init 1 */
  /** Common config
  */
  hadc2.Instance = ADC2;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc2.Init.NbrOfConversion = 1;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_7;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  /* USER CODE BEGIN ADC2_Init 2 */

  /* USER CODE END ADC2_Init 2 */

}

/**
* @brief ADC MSP Initialization
* This function configures the hardware resources used in this example
* @param hadc: ADC handle pointer
* @retval None
*/
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hadc->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspInit 0 */

  /* USER CODE END ADC1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_ADC1_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**ADC1 GPIO Configuration   
    PA6     ------> ADC1_IN6
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* ADC1 DMA Init */
    /* ADC1 Init */
    hdma_adc1.Instance = DMA1_Channel1;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc1.Init.Mode = DMA_NORMAL;
    hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
      Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);

  /* USER CODE BEGIN ADC1_MspInit 1 */

  /* USER CODE END ADC1_MspInit 1 */
  }
  else if(hadc->Instance==ADC2)
  {
  /* USER CODE BEGIN ADC2_MspInit 0 */

  /* USER CODE END ADC2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_ADC2_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**ADC2 GPIO Configuration   
    PA7     ------> ADC2_IN7
    */
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN ADC2_MspInit 1 */

  /* USER CODE END ADC2_MspInit 1 */
  }

}





#define SAMPLE_CNT 100
uint32_t m_aulAdc[SAMPLE_CNT];

typedef struct ad_ctrl_t {

    bool bFinishFlag;
   
}AD_CTRL_T;

AD_CTRL_T m_stAd;




/**
  * @brief This function handles DMA1 channel1 global interrupt.
  */
void DMA1_Channel1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */

  /* USER CODE END DMA1_Channel1_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_adc1);
  /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */

  /* USER CODE END DMA1_Channel1_IRQn 1 */

  m_stAd.bFinishFlag = true;
}







void start_adc(void)
{
    m_stAd.bFinishFlag = false;

    memset(m_aulAdc, 0, sizeof(m_aulAdc));
   
   
    __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);
    //__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_TRIGGER);

    HAL_ADCEx_MultiModeStart_DMA(&hadc1, m_aulAdc, SAMPLE_CNT);
   
    HAL_TIM_Base_Start(&htim3); // ok, start timer, tell adc start work

   
   
    //HAL_TIM_Base_Start_IT(&htim3);
}

void stop_adc(void)
{
    // 1. start timer1
    HAL_TIM_Base_Stop(&htim3);
        HAL_ADCEx_MultiModeStop_DMA(&hadc1);
       
    //HAL_TIM_Base_Stop_IT(&htim3);
}

bool get_then_clr_ad_finish_flag(void)
{
    if (m_stAd.bFinishFlag == false)
    {
        return false;
    }
    else
    {
        m_stAd.bFinishFlag = false;
        return true;
    }
}

void inital_mcu_ad(void)
{
    /* DMA controller clock enable */
    __HAL_RCC_DMA1_CLK_ENABLE();
   
    // 配置ADC时钟分频   
    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      ERROR_HANDLER();
    }

    MX_ADC1_Init();
    MX_ADC2_Init();

    while(HAL_ADCEx_Calibration_Start( &hadc1)) {}

    while(HAL_ADCEx_Calibration_Start( &hadc2)) {}

   
    //HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
    //HAL_NVIC_EnableIRQ(ADC1_2_IRQn);

    /* DMA interrupt init */
    /* DMA1_Channel1_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

}

TIM_HandleTypeDef htim3;

/**
  * @brief TIM3 Initialization Function
  * @param None
  * @retval None
  */
void MX_TIM3_Init(void)
{

  __HAL_RCC_TIM3_CLK_ENABLE();


  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 72 - 1;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 1000 - 1;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    ERROR_HANDLER();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    ERROR_HANDLER();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    ERROR_HANDLER();
  }
  


}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-31 21:53 , Processed in 0.187417 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表