精华内容
下载资源
问答
  • 在dma
    千次阅读
    2016-11-25 16:56:05

    1 前言

    客户反馈在使用STM32F205的串口工作在DMA模式时,有时能够接收数据,有时完全没有数据,但如果换成中断模式来接收又能100%正常收到数据。

    2 复现现象

    2.1 问题背景

    与客户沟通,客户使用的是STM32F2标准库V1.1.0,串口波特率为1.408Mbps,不经过串口RS232,直接连接主CPU和从MCU(STM32F205)的串口发送和接收引脚,如下图所示:

    图1

    图1

    2.2 尝试重现问题

    由于客户使用的是主从架构,实验采用两块STM3220G-EVAL评估板来重现现象。一块用来不间断发送串口数据,另一块采用串口DMA进行接收,直接通过杜邦线连接串口PIN脚并共地,不使用评估板上的RS232收发器。接收端使用STM32F2xx_StdPeriph_Examples\ USART\USART_TwoBoards的示例代码。代码片段如下:

    int main(void)
    {
      ...
      USART_Config();
      ...
      while (1)
      {
        /* Clear Buffers */
        Fill_Buffer(RxBuffer, TXBUFFERSIZE);
        Fill_Buffer(CmdBuffer, 2);
    
        DMA_DeInit(USARTx_RX_DMA_STREAM);
        DMA_InitStructure.DMA_Channel = USARTx_RX_DMA_CHANNEL;
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
        /************* USART will receive the the transaction data ****************/
        /* Transaction data (length defined by CmdBuffer[1] variable) */       
        DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RxBuffer;
        DMA_InitStructure.DMA_BufferSize =10;// (uint16_t)CmdBuffer[1];
        DMA_InitStructure.DMA_Mode =DMA_Mode_Normal;//DMA_Mode_Circular;
        DMA_Init(USARTx_RX_DMA_STREAM, &DMA_InitStructure);
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure); 
        /* Enable DMA Stream Transfer Complete interrupt */
        DMA_ITConfig(USARTx_RX_DMA_STREAM, DMA_IT_TE|DMA_IT_DME|DMA_IT_FE, ENABLE);
    
        /* Enable the DMA Stream */
        DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
        /* Enable the USART Rx DMA requests */
        USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);
    
    //  USART_Cmd(USARTx, ENABLE);
    //  while(SET ==USART_GetFlagStatus(USARTx,USART_FLAG_ORE))
    //  {
    //    Tmp =USART_ReceiveData(USARTx);
    //  }
    
    
        while ((DMA_GetFlagStatus(USARTx_RX_DMA_STREAM, USARTx_RX_DMA_FLAG_TCIF) ==     RESET)
        {
        }   
        /* Clear all DMA Streams flags */
        DMA_ClearFlag(USARTx_RX_DMA_STREAM, USARTx_RX_DMA_FLAG_HTIF | USARTx_RX_DMA_FLAG_TCIF);
    
        /* Disable the DMA Stream */
        DMA_Cmd(USARTx_RX_DMA_STREAM, DISABLE);
    
        /* Disable the USART Rx DMA requests */
        USART_DMACmd(USARTx, USART_DMAReq_Rx, DISABLE);
    
    //handle the RxBuffer data...
        //...
      }
    }
    

    USART_Config()函数如下:

    static void USART_Config(void)
    {
      USART_InitTypeDef USART_InitStructure;
      GPIO_InitTypeDef GPIO_InitStructure;
    
      /* Peripheral Clock Enable -------------------------------------------------*/
      /* Enable GPIO clock */
      RCC_AHB1PeriphClockCmd(USARTx_TX_GPIO_CLK | USARTx_RX_GPIO_CLK, ENABLE);
    
      /* Enable USART clock */
      USARTx_CLK_INIT(USARTx_CLK, ENABLE);
    
      /* Enable the DMA clock */
      RCC_AHB1PeriphClockCmd(USARTx_DMAx_CLK, ENABLE);
    
      /* USARTx GPIO configuration -----------------------------------------------*/ 
      /* Connect USART pins to AF7 */
      GPIO_PinAFConfig(USARTx_TX_GPIO_PORT, USARTx_TX_SOURCE, USARTx_TX_AF);
      GPIO_PinAFConfig(USARTx_RX_GPIO_PORT, USARTx_RX_SOURCE, USARTx_RX_AF);
    
      /* Configure USART Tx and Rx as alternate function push-pull */
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    
      GPIO_InitStructure.GPIO_Pin = USARTx_TX_PIN;
      GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStructure);
    
      GPIO_InitStructure.GPIO_Pin = USARTx_RX_PIN;
      GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStructure);
    
      /* USARTx configuration ----------------------------------------------------*/
      /* Enable the USART OverSampling by 8 */
      USART_OverSampling8Cmd(USARTx, ENABLE); 
    
      USART_InitStructure.USART_BaudRate = 1408000;//3750000;
      USART_InitStructure.USART_WordLength = USART_WordLength_8b;
      USART_InitStructure.USART_StopBits = USART_StopBits_1;
      /* When using Parity the word length must be configured to 9 bits */
      USART_InitStructure.USART_Parity = USART_Parity_No;
      USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
      USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
      USART_Init(USARTx, &USART_InitStructure);
    
      /* Configure DMA controller to manage USART TX and RX DMA request ----------*/  
      DMA_InitStructure.DMA_PeripheralBaseAddr = USARTx_DR_ADDRESS;
      DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
      DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
      DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
      DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
      DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
      DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
      DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
      DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
      DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
      DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
      /* Here only the unchanged parameters of the DMA initialization structure are
         configured. During the program operation, the DMA will be configured with 
         different parameters according to the operation phase */
    
      /* Enable USART */
      USART_Cmd(USARTx, ENABLE);
    }
    

    按如上代码,有如下现象:
    1. 代码不做修改,若先启动接收端MCU再启动发送端MCU,接收端MCU的串口能正常接收。
    2. 代码不做修改,若先启动发送端MCU再启动接收端MCU,接收端MCU的串口100%接收异常。
    3. 修改发送端代码,改为发送端MCU串口每1秒间隔发送一次,则无论启动顺序如何,接收端MCU的串口都能正常。

    3 程序分析

    由上述代码可知,程序是先在USART_Config()函数函数内初始化串口并使能,然后再在接下来的main函数的while循环内初始化DMA并使能。这个是标准库内附带的示例代码,咋一看没什么问题,但仔细一想,针对用户的使用场景,这里就会产生一个问题:由于用户的主CPU有可能在从MCU启动之前就已经有可能启动,那么在这种情况下,在初始化完串口并使能后,到DMA使能之前这段时间内,若主CPU向从MCU发送串口数据,从MCU是否能正确接收?

    从上述测试代码的结果2可以得出,若在串口初始化并使能后到DMA使能之前有数据来,MCU是不能接收的,经进一步调试,发现此时数据寄存器USART_DR存在一个数据,且在状态寄存器USART_SR中ORE值1,由此可知,串口的接收寄存器中已经接收到一个数据,但是后面的数据又来了,由于数据寄存器中的数据没有及时转移走(此时DMA还没有开启),从而导致后面的数据无法存入,所以产生了上溢错误(ORE),而一旦产生上溢错误后,就无法再触发DAM请求,及时之后再启动DMA也不行,无法触发DMA请求就无法将数据寄存器内的数据及时转移走,如此陷入死锁,这就是串口无法正常接收的原因。这时反观一下代码的结果3,这又将做如何解释?

    仔细查看测试结果3,发现这个发送端每1秒间隔发送一次,那么就会存在这个一个概率,这个发送的时间点是否刚好在接收端MCU的串口初始化并使能和DMA使能之间还是之后,这个时间窗口非常关键,如果刚好在时间窗,那么串口接收就不正常,如果在这个时间窗之后,串口接收就能正常。由于测试代码采用的是1秒间隔,对于MCU来说这个是非常大的时间长度,还是很小概率能碰中这个时间窗的,因此,测试结果看起来是都能正常,实际严格来说,还是存在刚好碰中的可能。如果间隔时间缩短,那个碰中的几率就增大。由此看来,这也就能解释测试结果3了,也能解释客户提到的有时正常有时不正常的现象了。

    4 问题处理

    处理有两种方法,第一种方法是在使能DMA后,及时将数据寄存器DR中的数据清除掉,如下代码所示:

    //...
    /* Enable the DMA Stream */
        DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
        /* Enable the USART Rx DMA requests */
        USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);
    
      while(SET ==USART_GetFlagStatus(USARTx,USART_FLAG_ORE))
      {
        Tmp =USART_ReceiveData(USARTx);
      }
      //...
    

    这里是使用读DR的方法来清除的,从参考手册中也提到使用这种方法来清除ORE标志:

    图2

    图2

    第一种方法类似于一种纠错措施,下面介绍另一种推荐的方法,如下代码所示:

    //...
    /* Enable the DMA Stream */
        DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
        /* Enable the USART Rx DMA requests */
        USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);
    
      USART_Cmd(USARTx, ENABLE);
      //...
    

    如上所示,可以先使能DMA再使能串口,这样就彻底不存在那个时间窗了,不管数据何时过来能能被DAM及时转走。这个是推荐的解决方法。

    5 结论

    标准库中的示例代码一般来说只供参考,对于大部分情况来说都是能正常工作的,但偶尔也会出现不适用的情况,此时更需要我们针对问题进行思考分析,进一步找到原因才能解决问题。对于串口使用DMA来接收的情况,这里建议一定要先使能DMA,最后使能串口,这样就能避免类似问题出现了。

    本文转自:http://www.stmcu.org/module/forum/thread-606799-1-1.html

    更多相关内容
  • 本章STM32CUBEMX配置STM32F103的ADC在DMA模式扫描多个通道,通过串口进行打印。最近弄ST和GD的课程,需要样片的可以申请。

    概述

    本章STM32CUBEMX配置STM32F103的ADC在DMA模式扫描多个通道,通过串口进行打印。
    需要GD样片的可以加Q_QUN申请:6_15061293。

    视频教学

    https://www.bilibili.com/video/BV1ST411w7hS

    STM32CUBEIDE(10)----ADC在DMA模式下扫描多个通道

    csdn课程

    课程更加详细。
    https://download.csdn.net/course/detail/35611

    代码下载

    https://download.csdn.net/download/qq_24312945/86395148

    生成例程

    使用STM32CUBEMX生成例程,这里使用NUCLEO-F103RB开发板
    在这里插入图片描述
    查看原理图,PA2和PA3设置为开发板的串口。
    在这里插入图片描述
    配置串口。

    在这里插入图片描述
    开启中断。
    在这里插入图片描述
    查看原理图,Arduino的接口A0-A5都是AD口。
    在这里插入图片描述
    ADC通道配置

    ADC1IN0(PA0)IN1(PA1)IN4(PA4)

    ADC1配置。

    在这里插入图片描述

    • ADCs_Common_Settings:
      • Mode:Independent mod 独立 ADC 模式,当使用一个 ADC 时是独立模式,使用两个 ADC 时是双模式,在双模式下还有很多细分模式可选,具体配置 ADC_CR1:DUALMOD 位。
    • ADC_Settings:
      • Data Alignment:
        • Right alignment 转换结果数据右对齐,一般我们选择右对齐模式。
        • Left alignment 转换结果数据左对齐。
      • Scan Conversion Mode:
        • Disabled 禁止扫描模式。如果是单通道 AD 转换使用 DISABLE。
        • Enabled 开启扫描模式。如果是多通道 AD 转换使用 ENABLE。
      • Continuous Conversion Mode:
        • Disabled 单次转换。转换一次后停止需要手动控制才重新启动转换。
        • Enabled 自动连续转换。
      • DiscontinuousConvMode:
        • Disabled 禁止间断模式。这个在需要考虑功耗问题的产品中很有必要,也就是在某个事件触发下,开启转换。
        • Enabled 开启间断模式。
    • ADC_Regular_ConversionMode:
      • Enable Regular Conversions 是否使能规则转换。
      • Number Of Conversion ADC转换通道数目,有几个写几个就行。
      • External Trigger Conversion Source 外部触发选择。这个有多个选择,一般采用软件触发方式。
    • Rank:
      • Channel ADC转换通道
      • Sampling Time 采样周期选择,采样周期越短,ADC 转换数据输出周期就越短但数据精度也越低,采样周期越长,ADC 转换数据输出周期就越长同时数据精度越高。
    • ADC_Injected_ConversionMode:
      • Enable Injected Conversions 是否使能注入转换。注入通道只有在规则通道存在时才会出现。
    • WatchDog:
      • Enable Analog WatchDog Mode 是否使能模拟看门狗中断。当被 ADC 转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断。

    DMA开启。
    在这里插入图片描述

    生成独立的文件。
    在这里插入图片描述

    STM32CUBEIDE配置

    在这里插入图片描述
    若需要打印浮点型,需要勾选下面的选项。
    在这里插入图片描述

    串口重定向

    在main.c中,添加头文件,若不添加会出现 identifier “FILE” is undefined报错。

    /* USER CODE BEGIN Includes */
    #include "stdio.h"
    /* USER CODE END Includes */
    

    函数声明和串口重定向:

    /* USER CODE BEGIN PFP */
    #ifdef __GNUC__									//串口重定向
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif 
    PUTCHAR_PROTOTYPE
    {
        HAL_UART_Transmit(&huart2 , (uint8_t *)&ch, 1, 0xFFFF);
        return ch;
    }
    /* USER CODE END PFP */
    

    代码

    定义变量,存放采集到的数据。

    /* USER CODE BEGIN 0 */
    uint32_t ADC1_1, ADC1_2,ADC1_3;//采集的三个通道的ADC
    uint32_t ADC1_Value[30];//DMA存放数组
    uint8_t i;
    uint8_t ADC1_Flag;//dma采集完毕中断
    /* USER CODE END 0 */
    

    使能ADC传输。

      /* USER CODE BEGIN 2 */
      HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,30);    //使用DMA传输
      /* USER CODE END 2 */
    

    主循环。

      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    	  if(ADC1_Flag==1)
    	  {
    		  ADC1_Flag=0;
    		  ADC1_1=0;
    		  ADC1_2=0;
    		  ADC1_3=0;
    		  for(i=0;i<30;)
    		  {
    			  ADC1_1+=ADC1_Value[i++];
    			  ADC1_2+=ADC1_Value[i++];
    			  ADC1_3+=ADC1_Value[i++];
    		  }
    		  printf("\n");
    		  printf("adc1_IN0(PA0)=%4.0d,ADC_IN0=%1.4f\r\n",ADC1_1/10,ADC1_1/10*3.3f/4096);
    		  printf("adc1_IN1(PA1)=%4.0d,ADC_IN1=%1.4f\r\n",ADC1_2/10,ADC1_2/10*3.3f/4096);
    		  printf("adc1_IN4(PA4)=%4.0d,ADC_IN2=%1.4f\r\n",ADC1_3/10,ADC1_3/10*3.3f/4096);
    		  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,30);    //使用DMA传输
    	  }
    		HAL_Delay(1000);
      }
      /* USER CODE END 3 */
    

    ADC回调函数。
    DMA传输的时候如果读取内存片段,会有仲裁器的问题,加了一句关闭DMA的语句HAL_ADC_Stop_DMA(&hadc1);

    
    /* USER CODE BEGIN 4 */
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
    {
    	if(hadc->Instance == ADC1){
    		ADC1_Flag=1;
    		/*
    		 * DMA传输的时候如果读取内存片段,会有仲裁器的问题,加了一句关闭DMA的语
    
    		 */
    		HAL_ADC_Stop_DMA(&hadc1);
    	}
    }
    /* USER CODE END 4 */
    

    测试结果

    输入固定电压进行测试。

    ADC1IN0(PA0)IN1(PA1)IN4(PA4)
    输入电压VCC2.0VGND

    Normal下测试结果如下。
    在这里插入图片描述

    若不试用关闭DMA的语句HAL_ADC_Stop_DMA(&hadc1);
    会造成数据错乱。
    在这里插入图片描述

    Circular可以下可以一直进行采集,不需要HAL_ADC_Stop_DMA(&hadc1)都可。
    在这里插入图片描述

    最后

    以上的代码会在Q_qun里分享。Q_qun:615061293。

    展开全文
  • 本章主要配置,ADC在DMA模式下扫描多个通道,通过串口进行打印。查阅手册可以得知,PA9、PA10为串口0的输出和输入口。需要GD样片的可以申请

    概述

    本章主要配置,ADC在DMA模式下扫描多个通道,通过串口进行打印。
    查阅手册可以得知,PA9、PA10为串口0的输出和输入口。
    需要GD样片的可以加群申请:615061293 。
    在这里插入图片描述
    ADC通道配置
    在这里插入图片描述

    视频教学

    https://www.bilibili.com/video/BV1at4y1n7jy/

    GD32F303固件库开发(11)----ADC在DMA模式下扫描多个通道

    csdn课程

    课程更加详细。
    https://download.csdn.net/course/detail/37144

    硬件准备

    这里准备了1块开发板进行验证,分别是GD32303C_START开发板。
    在这里插入图片描述

    keil配置

    microlib 进行了高度优化以使代码变得很小。 它的功能比缺省 C 库少,并且根本不具备某些 ISO C 特性。 某些库函数的运行速度也比较慢,如果要使用printf(),必须开启。
    在这里插入图片描述

    使能串口

      /* 使能GPI0A,用PA9、PA10为串口 */
        rcu_periph_clock_enable(RCU_GPIOA);
    
        /*使能串口0的时钟 */
        rcu_periph_clock_enable(RCU_USART0);
    
        /*配置USARTx_Tx(PA9)为复用推挽输出*/
        gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
     
        /*配置USARTx_RxPA9)为浮空输入 */
        gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
    
        /* USART 配置 */
        usart_deinit(USART0);//重置串口0
        usart_baudrate_set(USART0, 115200U);//设置串口0的波特率为115200
        usart_word_length_set(USART0, USART_WL_8BIT);      	// 帧数据字长
    		usart_stop_bit_set(USART0, USART_STB_1BIT);      	 	// 停止位1位
        usart_parity_config(USART0, USART_PM_NONE);       	// 无奇偶校验位
        usart_receive_config(USART0, USART_RECEIVE_ENABLE);//使能接收器
        usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);//使能发送器
        usart_enable(USART0);//使能USART
    

    串口重定向

    /* retarget the C library printf function to the USART */
    int fputc(int ch, FILE *f)
    {
        usart_data_transmit(USART0, (uint8_t)ch);
        while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
        return ch;
    }
    

    串口重定向后就可以使用printf进行打印。

    ADC通道设置

    在这里插入图片描述

    DMA设置

    在这里插入图片描述

    ADC0初始化

    
    void rcu_config(void)
    {
        /*使能GPIOA时钟 */
        rcu_periph_clock_enable(RCU_GPIOA);
        /* 使能ADC时钟 */
        rcu_periph_clock_enable(RCU_ADC0);
        /* 使能DMA0时钟 */
        rcu_periph_clock_enable(RCU_DMA0);
        /* 配置ADC速率 */
        rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6);
    }
    
    

    GPIO初始化

    void gpio_config(void)
    {
        /* config the GPIO as analog mode */
        gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_10MHZ, GPIO_PIN_0);
        gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_10MHZ, GPIO_PIN_1);
        gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_10MHZ, GPIO_PIN_2);
        gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_10MHZ, GPIO_PIN_3);
    }
    
    

    ADC0配置

     void adc_config(void)
    {
    	
    		adc_deinit(ADC0);
        /* ADC mode config */
        adc_mode_config(ADC_MODE_FREE); 
        /* 开启连续转换 */
        adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);
        /* 开启扫描模式 */
        adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
        /*数据右对齐 */
        adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
    
        /* ADC channel length config */
        adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 4);
    
        /* ADC regular channel config */ 
        adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_0, ADC_SAMPLETIME_55POINT5);
        adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5);
        adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_2, ADC_SAMPLETIME_55POINT5);
        adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_3, ADC_SAMPLETIME_55POINT5);
        
        /* ADC 软件触发(规则组) */
        adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
        adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
    
        /* ADC DMA function enable */
        adc_dma_mode_enable(ADC0);
        /* enable ADC interface */
        adc_enable(ADC0);
    
    		
        /* ADC校准和复位校准 */
        adc_calibration_enable(ADC0);
    }
    
    

    DMA0初始化

     void dma_config(void)
    {
        /* ADC_DMA_channel configuration */
        dma_parameter_struct dma_data_parameter;
        
        /*  ADC DMA0_0初始化 */
        dma_deinit(DMA0, DMA_CH0);
        
        /* initialize DMA single data mode */
        dma_data_parameter.periph_addr  = (uint32_t)(&ADC_RDATA(ADC0));//外设基地址
        dma_data_parameter.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;//内存地址增量模式
        dma_data_parameter.memory_addr  = (uint32_t)(&ADC0_Value);//数据存放地址
        dma_data_parameter.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;//内存地址增量模式
        dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;//dma外设宽度16位,半字
        dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;  
        dma_data_parameter.direction    = DMA_PERIPHERAL_TO_MEMORY;//传输模式,外设到存储(接收)
        dma_data_parameter.number       = 40;//长度
        dma_data_parameter.priority     = DMA_PRIORITY_HIGH;//优先级高
        dma_init(DMA0, DMA_CH0, &dma_data_parameter);
    
        dma_circulation_disable(DMA0, DMA_CH0);//循环模式开启dma_circulation_enable(DMA0, DMA_CH0)//dma_circulation_disable
    	
    		/* enable DMA transfer complete interrupt */
        dma_interrupt_enable(DMA0, DMA_CH0, DMA_INT_FTF);	//打开全部完成中断
        /* enable DMA channel */
        dma_channel_enable(DMA0, DMA_CH0);	/* DMA内存到内存模式不开启 */
    }
    

    DMA0_Channel0_IRQHandler()

    void DMA0_Channel0_IRQHandler(void)
    {
      if(dma_interrupt_flag_get(DMA0, DMA_CH0, DMA_INT_FLAG_FTF)==SET)
      {     
    		dma_interrupt_flag_clear(DMA0, DMA_CH0, DMA_FLAG_FTF);//清除DMA通道传输完成标志
    		dma_channel_disable(DMA0, DMA_CH0);
    
    		ADC0_Flag=1;
      }
    }
    
    

    初始化定义

    		/* system clocks configuration */
        rcu_config();
        nvic_irq_enable(DMA0_Channel0_IRQn, 0, 0);
        /* GPIO configuration */
        gpio_config();
        /* DMA configuration */
        dma_config();
        /* ADC configuration */
        adc_config();
    		/* ADC software trigger enable */
        adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
    

    数据采集

        while (1)
    		{
    			if(ADC0_Flag==1)
    			{
    				ADC0_0=0;
    				ADC0_1=0;
    				ADC0_2=0;
    				ADC0_3=0;
    				for(i=0;i<40;)
    				{
    					ADC0_0+=ADC0_Value[i++];
    					ADC0_1+=ADC0_Value[i++];
    					ADC0_2+=ADC0_Value[i++];
    					ADC0_3+=ADC0_Value[i++];
    				}		
    		  printf("\n");
    		  printf("adc1_IN0(PA0)=%4.0d,ADC_IN0=%1.4f\r\n",ADC0_0/10,ADC0_0/10*3.3f/4096);
    		  printf("adc1_IN1(PA1)=%4.0d,ADC_IN1=%1.4f\r\n",ADC0_1/10,ADC0_1/10*3.3f/4096);
    		  printf("adc1_IN2(PA2)=%4.0d,ADC_IN2=%1.4f\r\n",ADC0_2/10,ADC0_2/10*3.3f/4096);
    		  printf("adc1_IN3(PA3)=%4.0d,ADC_IN3=%1.4f\r\n",ADC0_3/10,ADC0_3/10*3.3f/4096);
    			ADC0_Flag=0;
    		adc_config();	
    		dma_memory_address_config(DMA0, DMA_CH0, (uint32_t)(&ADC0_Value));
    		dma_transfer_number_config(DMA0, DMA_CH0, 40);		
            dma_channel_enable(DMA0, DMA_CH0);
    		adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);			
    		
    			}
          delay_1ms(1000);
        }
    

    测试结果

    输入固定电压进行测试。

    在这里插入图片描述

    测试结果如下。

    在这里插入图片描述

    最后

    M6Ly9ibG9nLmNzZG4ubmV0L3FxXzI0MzEyOTQ1,size_16,color_FFFFFF,t_70#pic_center)

    展开全文
  • DMA的verilog硬件实现,此版本为东南大学2005年版本。目测可用。 网上有很多该版本,但是不全,此次为收集齐全的版本方便大家学习研究。(没有找到文档说明,代码注释较详细)
  • 本章STM32CUBEMX配置STM32F103,并且GD32F303中进行开发,同时通过GD32303C_START开发板内进行验证。

    概述

    本章STM32CUBEMX配置STM32F103,并且在GD32F303中进行开发,同时通过GD32303C_START开发板内进行验证。
    需要GD样片的可以加Q_QUN申请:6_15061293。
    本章主要配置,双ADC轮询模式扫描多个通道,通过串口进行打印。
    查阅手册可以得知,PA9、PA10为串口0的输出和输入口。
    在这里插入图片描述

    视频教学

    https://www.bilibili.com/video/BV1hG41187Ah/

    STM32CUBEMX开发GD32F303(11)----ADC在DMA模式下扫描多个通道

    csdn课程

    课程更加详细。
    https://download.csdn.net/course/detail/37152

    ADC通道配置

    ADC1IN0(PA0)IN1(PA3)IN4(PA4)

    生成例程

    这里准备了GD32303C_START开发板进行验证。
    在这里插入图片描述

    STM32CUBEMX配置

    在这里插入图片描述

    勾选中断。
    在这里插入图片描述

    ADC1配置。

    在这里插入图片描述

    • ADCs_Common_Settings:
      • Mode:Independent mod 独立 ADC 模式,当使用一个 ADC 时是独立模式,使用两个 ADC 时是双模式,在双模式下还有很多细分模式可选,具体配置 ADC_CR1:DUALMOD 位。
    • ADC_Settings:
      • Data Alignment:
        • Right alignment 转换结果数据右对齐,一般我们选择右对齐模式。
        • Left alignment 转换结果数据左对齐。
      • Scan Conversion Mode:
        • Disabled 禁止扫描模式。如果是单通道 AD 转换使用 DISABLE。
        • Enabled 开启扫描模式。如果是多通道 AD 转换使用 ENABLE。
      • Continuous Conversion Mode:
        • Disabled 单次转换。转换一次后停止需要手动控制才重新启动转换。
        • Enabled 自动连续转换。
      • DiscontinuousConvMode:
        • Disabled 禁止间断模式。这个在需要考虑功耗问题的产品中很有必要,也就是在某个事件触发下,开启转换。
        • Enabled 开启间断模式。
    • ADC_Regular_ConversionMode:
      • Enable Regular Conversions 是否使能规则转换。
      • Number Of Conversion ADC转换通道数目,有几个写几个就行。
      • External Trigger Conversion Source 外部触发选择。这个有多个选择,一般采用软件触发方式。
    • Rank:
      • Channel ADC转换通道
      • Sampling Time 采样周期选择,采样周期越短,ADC 转换数据输出周期就越短但数据精度也越低,采样周期越长,ADC 转换数据输出周期就越长同时数据精度越高。
    • ADC_Injected_ConversionMode:
      • Enable Injected Conversions 是否使能注入转换。注入通道只有在规则通道存在时才会出现。
    • WatchDog:
      • Enable Analog WatchDog Mode 是否使能模拟看门狗中断。当被 ADC 转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断。

    DMA开启。
    在这里插入图片描述

    生成独立的文件。
    在这里插入图片描述

    keil配置

    microlib 进行了高度优化以使代码变得很小。 它的功能比缺省 C 库少,并且根本不具备某些 ISO C 特性。 某些库函数的运行速度也比较慢,如果要使用printf(),必须开启。
    在这里插入图片描述

    代码

    在main.c中,添加头文件,若不添加会出现 identifier “FILE” is undefined报错。

    /* USER CODE BEGIN Includes */
    #include "stdio.h"
    /* USER CODE END Includes */
    

    函数声明和串口重定向:

    /* USER CODE BEGIN PFP */
    int fputc(int ch, FILE *f){
    	HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
    	return ch;
    }
    
    /* USER CODE END PFP */
    

    定义变量,存放采集到的数据。

    /* USER CODE BEGIN 0 */
    uint32_t ADC1_1, ADC1_2,ADC1_3;//采集的三个通道的ADC
    uint32_t ADC1_Value[30];//DMA存放数组
    uint8_t i;
    uint8_t ADC1_Flag;//dma采集完毕中断
    
    /* USER CODE END 0 */
    

    使能ADC传输。

      /* USER CODE BEGIN 2 */
      HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,30);    //使用DMA传输
      /* USER CODE END 2 */
    

    主循环。

      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    	  if(ADC1_Flag==1)
    	  {
    		  ADC1_Flag=0;
    		  ADC1_1=0;
    		  ADC1_2=0;
    		  ADC1_3=0;
    		  for(i=0;i<30;)
    		  {
    			  ADC1_1+=ADC1_Value[i++];
    			  ADC1_2+=ADC1_Value[i++];
    			  ADC1_3+=ADC1_Value[i++];
    		  }
    		  printf("\n");
    		  printf("adc1_IN0(PA0)=%4.0d,ADC_IN0=%1.4f\r\n",ADC1_1/10,ADC1_1/10*3.3f/4096);
    		  printf("adc1_IN3(PA3)=%4.0d,ADC_IN3=%1.4f\r\n",ADC1_2/10,ADC1_2/10*3.3f/4096);
    		  printf("adc1_IN4(PA4)=%4.0d,ADC_IN4=%1.4f\r\n",ADC1_3/10,ADC1_3/10*3.3f/4096);
    		  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,30);    //使用DMA传输
    	  }
    		HAL_Delay(1000);
      }
      /* USER CODE END 3 */
    

    ADC回调函数。
    DMA传输的时候如果读取内存片段,会有仲裁器的问题,加了一句关闭DMA的语句HAL_ADC_Stop_DMA(&hadc1);

    
    /* USER CODE BEGIN 4 */
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
    {
    	if(hadc->Instance == ADC1){
    		ADC1_Flag=1;
    		/*
    		 * DMA传输的时候如果读取内存片段,会有仲裁器的问题,加了一句关闭DMA的语
    
    		 */
    		HAL_ADC_Stop_DMA(&hadc1);
    	}
    }
    /* USER CODE END 4 */
    

    最后

    以上的代码会在Q群里分享。QQ群:615061293。
    或者关注微信公众号『记帖』,持续更新文章和学习资料,可加作者的微信交流学习!
    在这里插入图片描述

    测试结果

    输入固定电压进行测试。

    ADC1IN0(PA0)IN1(PA3)IN4(PA4)
    输入电压VCC2.0VGND

    Normal下测试结果如下。
    在这里插入图片描述

    若不试用关闭DMA的语句HAL_ADC_Stop_DMA(&hadc1);
    会造成数据错乱。
    在这里插入图片描述

    Circular可以下可以一直进行采集,不需要HAL_ADC_Stop_DMA(&hadc1)都可。

    在这里插入图片描述

    展开全文
  • 而且该模式下,一次DMA操作意味着在DMA操作中的一对或不可分的读和写周期。在DMA操作中,总线控制器不能把`总线的使用权分配给其他总线控制者。如果想在DMA操作中响应高优先级的总线控制,则必须用单步模式,单步...
  • zynq7000的AXI4S-DMA的驱动及测试程序
  • 该文件包含了DMA控制器的相关详细的设计...DMA(DirectMemoryAccess)控制器是一种系统内部转移数据的独特外设,可以将其视为一种能够通过一组专用总线将内部和外部存储器与每个具有DMA能力的外设连接起来的控制器。
  • DMA驱动详解

    千次阅读 2020-03-01 12:33:09
    1. DMA介绍 DMA(Direct Memory Access):即直接存储器访问, DMA 传输方式无需 CPU 直接控制传输,通过硬件为 RAM 、I/O 设备开辟一条...2.1linux中,分配释放DMA缓冲区,常用以下几个函数 /*该函数只禁止cac...
  • DMA的TCIF中断发送 IDLE中断接收
  • ARM9嵌入式系统中进行DMA通道的数据传输,在DMA传输中,利用了中断,主要是DMA中断。此函数的实现过程中,包含了很多信息有通道数、源地址、目的地址、传输数据大小、传输单元以及方式等。
  • 程序初始化了定时器1,并通过DMA传输修改PWM占空比,方便用来实现快速SPWM
  • DMA_APM32-DMA_

    2021-09-30 02:01:30
    APM32单片机DMA程序;替代STM32F030系列
  • 一个DMA控制器,实际上是采用DMA方式的外围设备与系统总线之间的接口电路,这个接口电路是中断接口的基础上再加DMA机构组成.习惯上将DMA方式的接口电路称为DMA控制器。下页图示出了一个最简单的DMA控制器组成示意...
  • 【STM32】 DMA原理,步骤超细详解,一文看懂DMA

    万次阅读 多人点赞 2020-03-19 21:50:24
    DMA传输将数据从一个地址空间复制到另一个地址空间,提供外设和存储器之间或者存储器和存储器之间的高速数据传输。 我们知道CPU有转移数据、计算、控制程序转移等很多功能,系统运作的核心就是CPU, CPU无时不刻的...
  • 一、DMA控制器介绍 DMA控制器是一种系统内部转移数据的独特外设,可以将其视为一种能够通过一组专用总线将内部和外部存储器与每个具有DMA能力的外设连接起来的控制器。它之所以属于外设,是因为它是处理器的...
  • 基于STM32F407 MCU,对RS485进行通讯测试,DMA收发数据,经测试数据稳定,功能正常
  • 本实验通过按键KEY0控制串口1以DMA方式发送数据,按下KEY0,就开始DMA传送,同时LCD上面显示传送进度。打开串口调试助手,可以收到DMA发送的内容。
  • 实现zynq平台上的dma数据传输,并实现lwip发送给上位机
  • DMA传送方式

    2020-11-13 02:27:44
     块传送方式意味着连续4个字的DMA写周期前有连续的4个字的DMA读周期,即4个字突发读,然后4个字突发写,因此传输的数据个数应该是16字节的倍数。  如果传送大小或者DMA计数值不是16的倍数,则DMA将不能完整地...
  • spi_dma演示Raspberry Pi Pico上的SPI / DMA问题按照以下步骤安装: git clone --recurse-submodules https://github.com/carlk3/spi_dma.git cd spi_dma mkdir build cd build cmake .. make
  • 汇编写的dma操作代码,可以dos下运行,通过dma操作内存
  • 2440测试程序DMA在ADS1.2环境下编译
  • dma_axi-master.zip

    2019-12-29 09:36:27
    dma_axi64.v dma_axi64_dual_core.v dma_axi64_apb_mux.v dma_axi64_reg.v dma_axi64_reg_core0.v prgen_scatter8_1.v dma_axi64_core0_top.v dma_axi64_core0.v dma_axi64_core0_wdt.v dma_axi64_core0_...
  • 基于DMA通过UART发送和接收数据的例子,注意DMA_0为接受通道,DMA_1为...当然可以将dma的read_master和writer_master同时连uart_0和sdram_0的从端口上,这样是可以用一个dma对两者读写操作,但是不能同时做双向传输。
  • 用matlab语言,DMA方法估计FCI,金融状况指数
  • Nios里实现DMA的范例程序,NiosIIIde里编译
  • 显示刷新时可以不占用CPU运算时间,利用芯片内的DMA做数据通信,节约CPU资源。提高CPU的运行较率。
  • 本系列教程将 对应外设原理,HAL库与STM32CubeMX结合一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1、芯片: STM32F407ZET6/ STM32F103ZET6 2、STM32CubeMx软件 3、IDE: MDK-Keil软件 4、STM32F1xx/...
  • 通过DMA进行数据传输,实现两块内存空间的拷贝,如果两块内存中的数据一致,蜂鸣器会响起,否则不响。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 122,079
精华内容 48,831
关键字:

在dma