针对stm32 DMA传输AD数据,在完成中断后如何复位的问题的思考

案发条件

ADC连续单通道模式AD采集,触发DMA获取数据。程序如下

void ADC_DMAInit(void)
{
    ADC_InitTypeDef ADC_InitStructure;

RCC_HSICmd(ENABLE); // Enable HSI Clock 
    ADC_IO_Init();

while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    ADC_DeInit(ADC1);
    //AD转换结果为8位
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_8b;
    //AD工作在单通道模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    //模数转换工作在连续模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;       
    //转换由定时器触发启动ADC_ExternalTrigConv_T2_CC2
      //ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   
    //上升沿下降沿触发ADC_ExternalTrigConvEdge_RisingFalling
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    //ADC数据右对齐
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   
    //顺序进行规则转换的ADC通道的数目
    ADC_InitStructure.ADC_NbrOfConversion = 1;               
    ADC_Init(ADC1, &ADC_InitStructure); 

    //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间(取最大值)
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_4Cycles);  // CH0

    //ADC_InterruptInit();

  ADC_ClearFlag(ADC1,ADC_FLAG_EOC);

    //使能ADC1的DMA请求
    ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
    ADC_DMACmd(ADC1, ENABLE); 

/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
    //等待ADC1准备好
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET)
  {
  }       
  //循环通道没有准备好
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_RCNR) != RESET);


    /* Start ADC1 Software Conversion */ 
   ADC_SoftwareStartConv(ADC1);
}


DMA工作在从AD到内存的单次工作模式,开启完成触发中断,程序如下

/* 针对ADC的应用来配置DMA */
void DMA_ADCInit(void)
{
    DMA_InitTypeDef  DMA_InitStructure;
    //开启DMA的第一通道的时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    //复位DMA1通道的设置
    DMA_DeInit(DMA1_Channel1);   
    //DMA对应的外设基地址,AD采样的数据存放地址
    DMA_InitStructure.DMA_PeripheralBaseAddr = ((uint32_t)ADC1_DR_ADDRESS);    
    //DMA对应的内存基地址
    DMA_InitStructure.DMA_MemoryBaseAddr =(uint32_t)g_u16ADC_DataArr;
    //DMA缓存大小
    DMA_InitStructure.DMA_BufferSize = DMA_BUFF_SIZE;                 
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    //DMA的转换模式是SRC模式,就是从外设向内存中搬运
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;              
    //接收一次数据后,设备地址是否后移
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
    //接收一次数据后,目标内存地址是否后移
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;          
    //转换结果的数据大小
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte;
    //DMA搬运的数据尺寸,16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    //DMA优先级,高
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    //M2M模式禁止
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;    
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);


    //使能DMA传输完成中断中断
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);


    /* Configure the SysTick Handler Priority: Preemption priority and subpriority */
    IRQ_DMA1_Channel1_Config();
  
    /* Enable DMA channel1 */
    DMA_Cmd(DMA1_Channel1, ENABLE);
}

想要解决的问题:

在DMA触发完成中断后,如果想重新开启AD和DMA继续采集数据。如何做?请继续向下看。

DMA中断处理程序见下

void DMA1_Channel1_IRQHandler(void)
{
    DMA_InitTypeDef  DMA_InitStructure;


    if(DMA_GetITStatus(DMA1_IT_TC1))
    {
        DMA_ClearITPendingBit(DMA1_IT_TC1);
        DMA_ClearITPendingBit(DMA1_IT_HT1);


        //复位DMA1通道的设置
        /* Disable the selected DMAy Channelx */
        DMA1_Channel1->CCR &= (uint16_t)(~DMA_CCR1_EN);
      
        //DMA缓存大小
        DMA_SetCurrDataCounter(DMA1_Channel1 , DMA_BUFF_SIZE);


     DMA_Cmd(DMA1_Channel1, ENABLE);
        ADC_ClearFlag(ADC1,ADC_SR_OVR);
        ADC_ClearFlag(ADC1,ADC_SR_STRT);
        ADC_SoftwareStartConv(ADC1);
    }

上面的中断处理程序是重新开启AD和DMA的最少处理语句,缺一不可。

带Clear的语句是清除DMA全部完成中断标志,完成一半中断标志,AD的OVR异常标志和regular通道转换完成标志。这是必须的。

两个重新开启AD和DMA的命令也不多说。

由于DMA中的剩余个数在全部完成中断里面时已经为0,需要对它进行重新配置也可以理解。

其中让人不理解的就是 DMA1_Channel1->CCR &= (uint16_t)(~DMA_CCR1_EN);和没有重新设置DMA设置内存目的地址得语句。

芯片硬件又不是我们自己设计的,这么用,估且我们赞同这种现象。但通过这个现象我们能重新理解DMA的运行机制。

我总结如下:

DMA1_Channel1->CCR &= (uint16_t)(~DMA_CCR1_EN);表明:虽然,DMA进入中断以后,去除掉了中断标志,但这是DMA已经停止,但此时通过仿真可以查看到CCR寄存器的中最低位EN是1,虽然如此,但实际上DMA已经关闭,此时必须通过先复位EN位,就是上一行写的语句,再置位EN位,这种方式来重新开启DMA。


没有重新设置DMA设置内存目的地址得语句:表明:在DMA参考手册中有一句话在DMA运行时源地址和目的地址是不可以读取的,这个例子DMA在运行时是源地址不变,目的地址在不断加2个字节。但当DMA停止后目的地址又恢复到最初初始化给它的值,或者是在DMA运行时目的地址改变的是芯片内部一个我们看不到的地址。


  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32 HAL库中,可以使用DMA方式进行串口接收AD数据。可以使用函数HAL_UART_Receive_DMA来实现。该函数的参数包括UART句柄、接收数据缓冲区的指针和待接收数据的个数。通过调用该函数,可以在DMA方式下接收一定数量的数据。\[2\] 例如,可以使用以下代码来实现DMA方式下的串口接收AD数据: ```c uint8_t adData\[10\]; // 定义接收数据缓冲区 HAL_UART_Receive_DMA(&huart1, adData, 10); // 使用DMA方式接收10个字节的数据 ``` 在接收完成后,可以在DMA中断中调用串口接收中断回调函数HAL_UART_RxCpltCallback进行后续处理。在该回调函数中,可以对接收到的AD数据进行处理。\[2\] ```c void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { // 在这里进行接收数据的处理 // adData数组中存放了接收到的AD数据 } ``` 需要注意的是,在使用DMA方式进行串口接收时,需要配置DMA通道和DMA传输方向。具体的配置可以参考STM32 HAL库的相关文档和例程。 #### 引用[.reference_title] - *1* *2* *3* [[016] [STM32] 串口HAL库轮询、中断DMA方式传输数据](https://blog.csdn.net/kouxi1/article/details/123876915)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值