精华内容
下载资源
问答
  • 小熊派GD32开发(3)— DMA+空闲中断接收不定长数据 一、空闲中断 空闲中断是在检测到在数据收受后,总线上在一字节的时间内没有再接收到数据时发生。即串口的RXNE位被置位之后才开始检测,检测到空闲之后,串口...

    小熊派GD32开发(3)— DMA+空闲中断接收不定长数据

    一、空闲中断

    空闲中断是在检测到在数据收受后,总线上在一个字节的时间内没有再接收到数据时发生。即串口的RXNE位被置位之后才开始检测,检测到空闲之后,串口的CR1寄存器的IDLE位被硬件置1。

    这样,我们就可以通过空闲中断配合DMA接收串口的不定长数据。
    我们在上一个项目(小熊派GD32开发(2)— 第一个Hello world程序)的基础上,使用空闲中断接收数据。

    二、开启DMA传输和空闲中断

    修改USART0串口初始化函数函数,在函数中加入DMA的初始化和空闲中断

    /* USART0串口初始化函数
     * 参数:波特率
     * 返回值:无	*/
    void uart_init(uint32_t bound)
    {
    	dma_parameter_struct dma_init_struct;	/* DMA配置参数结构体 */
    	
        rcu_periph_clock_enable(RCU_GPIOA);		/* 使能 GPIOA 时钟 */ 
        rcu_periph_clock_enable(RCU_USART0);	/* 使能 USART0 时钟 */
    	rcu_periph_clock_enable(RCU_DMA0);		/* 使能 DMA0 时钟 */
    
        /* PA9  复用为 USART0_Tx */
        gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_9);
        /* PA10 复用为 USARTx_Rx */
        gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ,GPIO_PIN_10);
    					
        /* USART0 初始化配置 */
        usart_deinit(USART0);
        usart_baudrate_set(USART0, bound);						/* 设置波特率 */
        usart_receive_config(USART0, USART_RECEIVE_ENABLE);		/* 使能接收 */
        usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);	/* 使能发送 */	
    	usart_enable(USART0);
    	
    	/* USART0 DMA 接收配置*/
        dma_deinit(DMA0, DMA_CH4);
        dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;		/* 外设到内存 */
        dma_init_struct.memory_addr = (uint32_t)dma_buffer;			/* 设置内存接收基地址 */
        dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;	/* 内存地址递增 */
        dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;		/* 8位内存数据 */
        dma_init_struct.number = sizeof(dma_buffer);
        dma_init_struct.periph_addr = ((uint32_t)0x40013804);		/* 外设基地址,USART数据寄存器地址 */
        dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;	/* 外设地址不递增 */
        dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;	/* 8位外设数据 */
        dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;			/* 最高DMA通道优先级 */
        dma_init(DMA0, DMA_CH4, &dma_init_struct); 					/* 按照结构体的配置初始化DMA */  
        dma_circulation_disable(DMA0, DMA_CH4);			/* 关闭DMA循环模式 */
        dma_memory_to_memory_disable(DMA0, DMA_CH4);	/* DMA内存到内存模式不开启 */
    	dma_channel_enable(DMA0, DMA_CH4);				/* 使能DMA传输 */
    	
    	usart_dma_receive_config(USART0, USART_DENR_ENABLE);	/* USART0 DMA接收模式开启 */
        nvic_irq_enable(USART0_IRQn, 0, 0);		/* USART中断设置,抢占优先级0,子优先级0 */
        usart_interrupt_enable(USART0, USART_INT_IDLE);			/* 使能USART0空闲中断 */
    }
    

    三、在空闲中断中处理DMA的数据

    首先,我们需要定义几个变量,其中缓冲区的大小可以按照个人需求进行定义

    /* DMA接收缓冲区 */
    uint8_t dma_buffer[256];
    /* 待处理串口数据缓冲区 */
    uint8_t rx0_date_buf[256];
    /* 待处理数据个数:大于1为有数据待处理,0为没有数据待处理*/
    uint32_t USART_RX_NUM = 0;
    

    为了能在其他c文件中使用,在usart.h中添加:

    /* 待处理串口数据缓冲区 */
    extern uint8_t rx0_date_buf[];
    /* 待处理数据个数:大于1为有数据待处理,0为没有数据待处理*/
    extern uint32_t USART_RX_NUM;
    

    然后再串口中断中将DMA接收到的数据进行处理

    /* 串口0中断服务程序 */
    void USART0_IRQHandler(void)	
    {
        if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) //空闲中断
    	{
    		usart_interrupt_flag_clear(USART0,USART_INT_FLAG_IDLE);	/* 清除空闲中断标志位 */
    		usart_data_receive(USART0);								/* 清除接收完成标志位 */
    		dma_channel_disable(DMA0, DMA_CH4);						/* 关闭DMA传输 */
    		
    		USART_RX_NUM = sizeof(dma_buffer) - dma_transfer_number_get(DMA0,DMA_CH4);
    		memcpy(rx0_date_buf,dma_buffer,USART_RX_NUM); /* 转存数据到待处理数据缓冲区*/
    		rx0_date_buf[USART_RX_NUM] = '\0';	/* 添加字符串结束符 */
    		
    		/* 重新设置DMA传输 */
    		dma_memory_address_config(DMA0,DMA_CH4,(uint32_t)dma_buffer);
    		dma_transfer_number_config(DMA0,DMA_CH4,sizeof(dma_buffer));
    		dma_channel_enable(DMA0, DMA_CH4);		/* 开启DMA传输 */
        }
    }
    

    四、修改main函数,接收串口数据

    将main函数修改位如下所示,循环判断串口待处理数据缓冲区中是否有需要处理的数据,如果有的话,将数据通过串口发送,并且显示其长度,发送完后将数据计数清零。

    int main(void)
    {
    	int t = 0;
    	systick_config();	/* 配置系统时钟 */
    	LED_init();			/* 初始化 LED */	
    	uart_init(115200);	/* 初始化USART0 */
    	printf("Hello world! \r\n");
        while(1)
    	{
    		if(USART_RX_NUM > 0)
    		{
    			printf("RECV %d date:%s\r\n", USART_RX_NUM, rx0_date_buf);
    			USART_RX_NUM = 0;
    		}
    		
    		delay_1ms(10);
    		t++;
    		if(t % 200 == 0) LED(0);
    		else if(t % 200 == 100) LED(1);
        }
    }
    

    五、程序实现

    将程序编译,然后下载到开发板,我们发送几个数据,可以看到,不同长度的数据都可以成功接收到
    在这里插入图片描述

    五、代码

    完整代码我存放在码云,可以查看:https://gitee.com/william_william/GD32.git

    展开全文
  • 在项目开发的过程中,发现程序总是死在判断DMA一次传输是否完成这标志位上。进一步回退分析,发现是在I2C读的过程中,有使用到DMA去取外部I2C设备的data。 但是data并没有读完,Data为32bits,DMA在读到18bits时...

    在项目开发的过程中,发现程序总是死在判断DMA一次传输是否完成这个标志位上。进一步回退分析,发现是在I2C读的过程中,有使用到DMA去取外部I2C设备的data。

    但是data并没有读完,Data为32bits,DMA在读到18bits时,就出现读不到data bit了。导致I2C硬件模块不能进一步动作,SCK一直被拉低,没有clock输出,SDA也是如此。

    下面是通过示波器抓到的波形:

    I2C波形图

     

    在上面的波形图中,绿色的是SCK,蓝色的是SDA。

    在第一幅波形图中,有2段波形,第一段连续的I2C波形,经过确认I2C硬件和DMA配合是正常的。第二段则是有一段I2C波形,然后就SCK和SDA就都被拉低了。

    将第一幅图的第2段波形放大,就是第二副图看到的情况。可以很明显的看到SCK输出有被其他因素打断。I2C吐出几个clock,被其他因素打断了,clock线即SCK被拉低一段时间,然后clock线再继续吐出几个clock。

    直到I2C被频繁中断,clock吐不出来为止,SCK和SDA都被拉低,此时明显的I2C和DMA的配合过程被其他因素频繁的干扰打死了。

     

    通过示波器抓到的波形验证了这一点,然后再来分析代码和串口输出,发现是外部GPIO一直有中断输入,Cortex-M3 MCU频繁的响应中断,导致I2C&DMA操作被打挂了。

    有什么办法来解决这个问题?

    方法就是在I2C和DMA操作的过程开始处关闭所有中断,而在操作结束的时候重新打开中断,以免I2C&DMA操作被其他中断打断。

    ARM MDK编译环境自带的编译器ARMCC,含有内置的c函数,可供操作中断用:

    __enable_irq();

    __disable_irq();

    不过debug发现这两个函数只会在privileged mode使用。也就是说需要Cortex-M3 MCU先进入privileged mode,才能调用这两个函数。

     

    用什么方法让MCU从user mode切换到privileged mode呢,exception handler!

    可以用SVC啦,软件可以利用SVC制造一个exception,然后在exception handler中利用MCU的privileged mode来完成自己的任务。有点类似于linux里面的系统调用。

    SVC exception可以调用SVC函数,而SVC函数可以传入参数,也可以返回参数。转为系统调用而设计。

    举个例子,用户程序调用read()这个系统调用,read()会引发SVC exception,进而调用SVC函数,read()函数的参数传递给SVC函数,SVC在内核态执行硬件动作,并将SVC函数的返回结果,作为read()函数的返回,返回给用户程序。当然linux里面并不一定是SVC,这里只是做个类比。

    也就是说SVC可以完成从用户态到内核态的转变,不让用户直接操作硬件。用户只需要记住系统调用API的名字和函数即可,而不用管硬件的具体实现。

     

    所以这里我们就把I2C读的操作放在一个SVC函数里面去实现,并且在SVC函数的开始处调用__disable_irq();在函数的结束处,调用__enable_irq()。

     

    经过验证,I2C&DMA操作再也不会被中断打断了。

     

    参考资料:

    1. http://www.keil.com/pack/doc/cmsis/Core/html/group___core___register__gr.html#details
    2. cortex-M3权威指南

     

    转载于:https://www.cnblogs.com/ironx/p/5192120.html

    展开全文
  • 项目上正好用到了这功能,特此记录下来备忘,方便后人 简单说要注意的几点 串口DMA的初始化 ...初始化完引脚,时钟,DMA后记得使能对应的串口中断 然后还需要编写一接收使能的函数,如下 执...

    项目上正好用到了这个功能,特此记录下来备忘,方便后人

    简单说要注意的几个点

    1. 串口DMA的初始化
    2. 串口IDLE中断的处理
    3. 接收和发送的逻辑控制
    4. F7的D-Cache一致性的问题

    串口DMA的初始化

    我这里用的是串口3

    对应的接收和发送DMA mode用的是Normal,不是Circle

    初始化完引脚,时钟,DMA后记得使能对应的串口中断

    然后还需要编写一个接收使能的函数,如下

    执行这个函数会进入一次IDLE中断,这里需要自行处理这种情况

    可以通过标志位的方式屏蔽掉这个不正常的进入,或者清除标志位的方式都可以吧

    uint8_t UART_Init(void)
    {
        uint8_t res = HAL_OK;
        MX_DMA_Init();
        res = MX_USART1_UART_Init();
        if (res == HAL_OK)
        {
            UART1_RX_EN();
        }
        res = MX_USART3_UART_Init();
        if (res == HAL_OK)
        {
            UART3_RX_EN();
        }
    
        return res;
    }
    
    void UART3_RX_EN(void)
    {
        __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
    //这里需要补充一句Cache无效化的函数(2020-04-23)
        HAL_UART_Receive_DMA(&huart3,Uart3.Rx_Buf,UART_DMA_BUF_LEN);
    }

    串口IDLE中断的处理方式 & F7的D-Cache一致性的问题&接收和发送的逻辑控制

    上述三个问题接下来一并说了

    首先我这里定义了一个结构体的方式管理数据缓存和长度,还有有效数据标志,可能还有更机智的方法,待研究,至少目前这样的方法我觉得用的效果就已经很好了

    typedef struct
    {
        uint8_t Rx_Buf[UART_DMA_BUF_LEN];//可以定义一个宏定义管理缓冲区的长度
        uint8_t Idle_Flag;//可以理解为有效数据接收标志
        uint16_t Rx_Len;//收到数据的长度
    }ST_DMA_UART;

    如下是我的串口空闲中断的处理

     STM32 F7系列有Cache,这个加快CPU的执行速度,但是在用到涉及DMA的功能的时候就需要考虑它对代码产生的负面影响!!

    这个可以具体去搜索STM32 F7 Cache一致性的文章 读一读,这里不过多阐述

    我现在的理解就是如果涉及到DMA的操作,最好在函数开始执行之前加上指定内存地址的变量 D Cache无效化的操作

    当然这个不是绝对的,但是这个应该是一个调试代码的方向。我加了以后代码的功能正常了,即使调试的时候打断点数据也不会乱掉。。。

    这个D Cache还是挺复杂的,值得好好深入研究下

     SCB_InvalidateDCache_by_Addr((uint32_t *)Uart3.Rx_Buf,UART_DMA_BUF_LEN);

    这种处理的逻辑应该是比较常见的

    简单来说就是进入有效的IDLE中断后

    1. 清除中断标志
    2. DMA传输停止
    3. 检测到有效长度后,算出接收到的有效数据长度
    4. 修改标志位,方便主函数里面的函数处理接收到的数据
    5. 再次打开DMA接收
    void UART_Receive_IDLE(UART_HandleTypeDef *huart)
    {
        uint32_t temp;
    
        if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
        {
            __HAL_UART_CLEAR_IDLEFLAG(huart);
            HAL_UART_DMAStop(huart);
            temp = huart->hdmarx->Instance->NDTR;
            if(huart->Instance == huart3.Instance)
            {
                if(UART_DMA_BUF_LEN - temp!=0)
                {
                    Uart3.Rx_Len =  UART_DMA_BUF_LEN - temp;
                    Uart3.Idle_Flag=DMA_RECV;
                }
                
            }
        }
    }
    
    
    void USART3_IRQHandler(void)
    {
        UART_Receive_IDLE(&huart3);//
        HAL_UART_IRQHandler(&huart3);
    }

    主函数里的接收函数如下

    进来处理Cache一致性的问题

    接着判断接收是否有效,长度是否有效,在进入对于的处理机制里面,处理完以后,清除掉长度值和标志位

    uint8_t UART3_DualCPU_DataRX()
    {
    
        if ((Uart3.Idle_Flag == DMA_RECV) && (Uart3.Rx_Len != 0))
        {
             //执行对应的处理操作
             Uart3.Idle_Flag = DMA_NOT_RECV;//自己弄个宏定义反应接收的情况
             Uart3.Rx_Len = 0;
        }
    
    
    }

    然后发送函数

    进来处理Cache一致性的问题

    接着填你要发的数据

    然后调用底层发送

    底层发送函数一定要填你要发送的长度
    我这里的调试时如果不写那一句设定长度的话,数据的长度会不对

    void UART3_DualCPU_DataTX()
    {
    
        SCB_InvalidateDCache_by_Addr((uint32_t *)uart3_buf,UART3_BUF_LEN);//要加一句这个,确保数据不会因为Cache的问题乱掉
        //处理发送数据的代码
        UART3_DMA_TX(uart3_buf, UART3_BUF_LEN);
    }
    
    
    void UART3_DMA_TX(uint8_t * str,uint16_t len)
    {
    
        __HAL_DMA_SET_COUNTER(&hdma_usart3_tx,len);//这里要设置长度!!!
        HAL_UART_Transmit_DMA(&huart3, str, len);
    }

    总的来说,这个F7的串口DMA基本上就是这么弄的了

    效果很棒,基本上都是发送函数一运行完,接收方就收到了,然后再处理发回来,马上我就能收到,速度很快了

    我这个是用在两个CPU对向发送数据上的,觉得很好了

    之前也找了很多很多的代码看过

    F7的代码实现和F4的还是有很大不同的,特别是Cache的问题,要特别注意

     

    2020-4-23更新

    在操作DMA之前,无论发送还是接收,都需要加一句SCB_InvalidateDCache_by_Addr或者SCB_CleanInvalidateDCache_by_Addr,这个具体加哪一个要试一试,我这里加SCB_CleanInvalidateDCache_by_Addr,因为我的串口DMA是单次调用的,不是循环模式,所以重新开启收功能之前都需要调用一遍失能Cache函数,然后收发使用到的缓存都需要32字节对齐,这个是SCB_CleanInvalidateDCache_by_Addr或者SCB_CleanInvalidateDCache_by_Addr要求的,估计以前用了这个没起作用,应该也和这个有关,没有32字节对齐。

     

     

     

    补充一些几个关键函数的调用路径和简单的内部实现描述

    HAL_UART_Transmit_DMA

    1. 全局状态 gState = HAL_UART_STATE_BUSY_TX

    2. 调用HAL_DMA_Start_IT

    3. DMA状态hdma->State = HAL_DMA_STATE_BUSY;

    4. 开启外设DMA __HAL_DMA_ENABLE(hdma);

    5. 执行 SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);  数据就被发出去了


    UART_EndTransmit_IT

    1. 全局状态 huart->gState = HAL_UART_STATE_READY;

    2. HAL_UART_TxCpltCallback(huart);

    UART_EndRxTransfer(huart); 
    1. 接收状态恢复 huart->RxState = HAL_UART_STATE_READY;
    2. 解除中断跳转 huart->RxISR = NULL;

     

    HAL_UART_Receive_DMA

    1. 判断if (huart->RxState == HAL_UART_STATE_READY),不是READY的话之前退出

    2. 接收状态 huart->RxState = HAL_UART_STATE_BUSY_RX;

    3. 调用HAL_DMA_Start_IT

    4. DMA状态hdma->State = HAL_DMA_STATE_BUSY;

    5. 开启外设DMA __HAL_DMA_ENABLE(hdma);

     

    HAL_UART_DMAStop
    1. 判断是不是有发送请求
             如果有,停止发送
    2. 判断是不是有接收请求
             如果有,停止接收
    3. 终止DMA传输 HAL_DMA_Abort 
        调用   __HAL_DMA_DISABLE(hdma);关闭外设DMA

     

    更新20-07-19

    在调用DMA发送之前一定要做一个判断,判断上一次发送有没有完成,以免出现发送间隔过快,前一次发送没有完成后一次马上又开始导致HAL库函数出现HAL_BUSY的返回值,具体做法可以在调用前判断发送完成标志,或者在HAL库串口发送完成回调函数里面做一个标志位,完成即置位,在发送前判断改标志必须为1才可以进行发送,否则不进行
        
     

    展开全文
  • DMA有几通道,每通道都有一组寄存器,下面就以串口发送通道来解释 初始化: 主要设置CCR寄存器 1、数据传输方向 2、循环模式------是否循环操作 3、外设地址增量 4、内存地址增量 5、外设数据宽度 8位,16位...

      DMA 能完成外设到内存的 或 内存的外设的数据搬运,不用CPU参与。
      下面以串口发送为例,说明操作步骤:

    1、初始化通道的CCR寄存器

    DMA有x个通道,每个通道都有一组寄存器
    在这里插入图片描述
    1、数据传输方向
    2、循环模式------是否循环操作
    3、外设地址增量
    4、内存地址增量
    5、外设数据宽度 8位,16位还是32位
    6、内存地址宽度
    7、通道优先级
    8、是否是 存储器到存储器------复位后为0,表示外设与存储器通信,所以HAL这位不设置

    初始化就是配置上面这8条。

    用STM32CubeMX生成的DMA初始化代码 HAL_DMA_Init(DMA_HandleTypeDef *hdma) 如下:

      /* Prepare the DMA Channel configuration */
      tmp |=  hdma->Init.Direction        |              
              hdma->Init.PeriphInc           | hdma->Init.MemInc           |
              hdma->Init.PeriphDataAlignment | hdma->Init.MemDataAlignment |
              hdma->Init.Mode                | hdma->Init.Priority;
    
      /* Write to DMA Channel CR register */
      hdma->Instance->CCR = tmp;
    

      旧版的FW_V3.3.0库还多初始化了3个参数
    1、数据长度
    2、外设地址
    3、内存地址

      tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode |
                DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc |
                DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize |
                DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M;
    
      /* Write to DMAy Channelx CCR */
      DMAy_Channelx->CCR = tmpreg;
    
    /*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/
      /* Write to DMAy Channelx CNDTR */
      DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize;
    
    /*--------------------------- DMAy Channelx CPAR Configuration ----------------*/
      /* Write to DMAy Channelx CPAR */
      DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr;
    
    /*--------------------------- DMAy Channelx CMAR Configuration ----------------*/
      /* Write to DMAy Channelx CMAR */
      DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr;
    

    2、我们STM32CubeMX生成的DMA初始化代码以后,还不能用DMA,因为数据从哪里搬到哪里,搬多少个字节还没有初始化。HAL库是在我们调用类似HAL_UART_Transmit_DMA()这样的函数的时候才初始化这几个参数的。

      如果想实现旧版一样的功能,可以直接在上面的初始化代码中加入:
    hdma_usart1_tx.Instance->CNDTR=0;
    hdma_usart1_tx.Instance->CPAR = USART1_DR_Base;
    hdma_usart1_tx.Instance->CMAR = (uint32_t)Com_Data.SendBuff;
    SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);//使能DMA发送,CR3是串口1中的寄存器,不是DMA中的寄存器

      在HAL_UART_Transmit_DMA()函数中,调用HAL_DMA_Start_IT()---->DMA_SetConfig()来设置源地址,目标地址,传输字节数。

      以上配置完以后,最后需要EN通道:
    旧版EN的方法:DMA_Cmd (DMA1_Channel4,ENABLE);//即把CCR的最后一位置1
    hal库EN的方法:在HAL_DMA_Start_IT()—>__HAL_DMA_ENABLE(hdma);

    3、将串口与DMA这两个变量关联起来

    在初始化HAL_UART_MspInit()----->HAL_DMA_Init()以后,有一条语句:
    __HAL_LINKDMA(huart,hdmarx,hdma_usart1_rx);
    //相当于
    huart->hdmarx=&(hdma_usart1_rx);
    hdma_usart1_rx.Parent=huart;
      这条只是把串口变量huart1 与 DMA变量hdma_usart1_rx相互连接起来,像链表一样。后面在串口模块中使用huart->hdmarx.Init.Direction就可以访问DMA变量中的方向参数;在DMA模块中使用huart=hdma_usart1_rx.Parent;huart->TxXferCount=0;就可以设置串口发送的字节数。

    4、通过仿真可以查看地址

    通过查数据手册知道DMA的基地址是0x4002 0000
    在这里插入图片描述
    通道5即串口接收通道,偏移地址是0x08+20*(通道号-1)=88=0x58
    在这里插入图片描述

    5、串口DMA HAL库的使用

      在Init_Uart(void)初始化函数中调用下来两个函数
    A、MX_DMA_Init();-------只设置了DMA中断优先级

    B、MX_USART1_UART_Init()
    ------1)、设置串口和DMA相关寄存器:HAL_UART_Init(&huart1)
    ------------a,设置底层:HAL_UART_MspInit(huart);
    ------------------(1)串口引脚初始化
    ------------------(2)DMA通道初始化,即初始化传输的8个参数:HAL_DMA_Init(&hdma_usart1_rx)
    ------------------(3)把串口变量和DMA变量连接起来:__HAL_LINKDMA(huart,hdmarx,hdma_usart1_rx)
    ------------------(4)设置串口中断优先级
    ------------b,设置串口波特率,停止位等:UART_SetConfig(huart);
    ------------c,设置串口CR2 CR3寄存器
    ------------d,EN串口:__HAL_UART_ENABLE(huart);
    ------2)、使能串口空闲中断:__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
    ------3)、开始DMA接收:HAL_UART_Receive_DMA(&huart1, (uint8_t*)rx_buffer, RX_LEN);
    -------------------这个函数里会设置中断回调函数
    

    下面详细分析其中重要的步骤:

    A、设置DMA通道中断的优先级

    static void MX_DMA_Init(void) 
    {
      __HAL_RCC_DMA1_CLK_ENABLE();
      HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 6, 0);
      HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
      HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 6, 0);
      HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
    }
    

    (2)DMA通道初始化

      设置DMA通道的函数在文件stm32f1xx_hal_msp.c中,而DMA通道变量是定义在用户的c文件中,所以要在stm32f1xx_hal_msp.c的最前面把DMA变量声明为外部变量

    extern DMA_HandleTypeDef hdma_usart1_rx;
    extern DMA_HandleTypeDef hdma_usart1_tx;
    

    (3)为了用串口变量访问DMA变量,或用DMA变量访问串口变量,把它们连接起来

    在这里插入图片描述
    在DMA变量中,定义了一个 *Parent;指针
    在这里插入图片描述
    以下语句把它两个变量相互连接起来:

    __HAL_LINKDMA(huart,hdmarx,hdma_usart1_rx);
        //相当于
        huart->hdmarx=&(hdma_usart1_rx);
        hdma_usart1_rx.Parent=huart;
    

    2)、串口空闲中断

      串口接收数据后,当一个空闲帧被检测到时(一个byte的高电平(空闲)状态),如果IDLEIE位被设置将产生一个中断

    3)、DMA串口接收

      首先,定义一个数组用来接收串口数据,数组的长度要设置大于些

    #define RX_LEN  30
    static  char  rx_buffer[RX_LEN];
    

      其次,调用HAL_UART_Receive_DMA(&huart1, (uint8_t*)rx_buffer, RX_LEN);来接收数据

      再次,当DMA接收到一些字节时,突然有一个空闲字节,那就会产生空闲中断,在串口中断函数HAL_UART_IRQHandler(UART_HandleTypeDef *huart)中增加空闲中断代码

    	//如果是空闲中断
      tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE);
      tmp_it_source= __HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE);
      if((tmp_flag != RESET) && (tmp_it_source != RESET))
      {
    		__HAL_UART_CLEAR_IDLEFLAG(huart);//清除标志
    		tmp_flag=huart->Instance->SR;//读SR可以实现清除状态寄存器
    		tmp_flag=huart->Instance->DR;
    		HAL_UART_DMAStop(huart);
    		tmp_flag=huart->hdmarx->Instance->CNDTR;//获取未传输数据个数
    		__HAL_DMA_DISABLE(huart->hdmarx);//关闭DMA接收	
    		HAL_UART_RxIdleCallback(tmp_flag);//把未接收字节数传过去
    		__HAL_DMA_ENABLE(huart->hdmarx);//开启DMA接收,在传未接收字节数时,先关DMA,防止又接收到数据,改变了未接收字节数    
       } 
    }
    

    在空闲中断中,获取剩余未接收的字节数tmp_flag,开始我们设置的是接收RX_LEN个字节,两个相减就得到已接收的字节数。处理接收到的字节后,重新启动DMA接收。

    //串口接收完回调函数
    void HAL_UART_RxIdleCallback(uint32_t rx_count)
    {
    	uart_pak data;
    	uint32_t rx_cou;
    	rx_cou=RX_LEN-rx_count;//算出已接收字节数
    	
    	strcpy(data.str,rx_buffer);
    	data.strLen = rx_cou;
    	printFromISR(&data);//输出
    	
    	HAL_UART_Receive_DMA(&huart1, (uint8_t *)rx_buffer, RX_LEN);
    }
    

    串口中断函数是在stm32f1xx_hal_uart.c中,它的空闲中断中会调用HAL_UART_RxIdleCallback(uint32_t tmp_flag)回调函数 来处理接收到的数据,回调函数是用户在其它文件中定义的,所以在文件的开始要把它个函数声明为外部函数。
    在这里插入图片描述
    在使用函数的c文件开始声明外部函数,有一个好处,就是这个外部函数可以写在任何其它c文件里。而在头文件中声明,必须包含相应的头文件。

    展开全文
  • 中断DMA

    2021-01-13 13:52:13
    硬件中断选择 (1)配置20个中断线的屏蔽位(EXTI_IMR) (2)配置所选中断线的触发选择位(EXTI_RTSR和EXTI_FTSR) ...(1)配置20个中断/事件线屏蔽位(EXTI_IMR, EXTI_EMR) (2)设置软件中断寄存器的请求位(EXTI_SWIER)
  • 4 ADC---DMA1 ,定时器6触发中断,每2ms从DMA1取一次数据 5 通过((波峰末尾值位置--波峰初值位置)/(波峰总数--1))X定时器6中断时间,来计算所检测信号的周期 6 频率=1/周期 问题: 在调试的时候,串口...
  • ①在使用串口接受数据时,在使用串口通信外设,如GPS、WiFi、2/3/4G模块等,我们常用的是接收中断,每接收一字节进入中断,导致这段时间CPU不能做其他事情,降低了效率。STM32的DMA不需要CPU控制,让CPU能够在接收...
  • 之前在网上看到有DMA传输接收不定长数据,使用到IDLE中断。这次使用串口中断接收不定长数据,通过IDLE中断的标志位 新手上路,想试试写博客,记录一下自己学的东西。...串口3中断接收初始化,IDL
  • 问题:用ADC1连续采集几通道上的电压值,配置为规则组,没有开启ADC1的DMA的时候EOC中断是正常的。但是开启ADC1的DMA传输之后EOC中断的频率明显变慢了。开始以为是STM32F1的DMA和CM3内核共享系统数据总线,然后DMA...
  • 在32的点子哥的例程当中,我发现...于是我想,能不能自己写一个DMA的接受中断。 然后有了这样的一段中断函数的代码: void DMA1_Channel2_IRQHandler(void) { if(DMA_GetITStatus(DMA1_FLAG_TC3)==SET) { DMA_...
  • 3,打开串口中断 4,生成软件,并在软件里开启串口空闲中断 5,在串口中断函数里,检测空闲中断,获取接收的字符串长度 这里面需要注意的是,DMA的剩余长度是指,假如我们一开始调用HAL_UART_Receive_DMA...
  • 本项目代码利用HAL库中DMA中断方式,向串口1发送大量数据,芯片为STM32F407,通过串口助手来接收并显示数据。具体步骤如下: 1、配置MCU的引脚功能。 2、初始化DMA功能。 3、初始化USART1功能。 4、初始化TIM...
  • 3.打开中断 4.生成代码,生成代码选择了每外设单独使用.c/.h 5.我使用的是Keil5。打开工程,首先注释掉dma.c里的DMA接收中断,因为不需要DMA接收中断。(dma.c) 6.在usart.h文件里定义串口接收数据类型我是...
  • sdk中hw和bsp建立好后,以一跑freertos的hello world模块开始开发,以下按照截止到20170922的经验来记录。...3dma的驱动; 所以app/src下,建了三目录,分别是drivers(用于放ps侧低速接口的驱动),con
  • 在STM32的串口开发过程中,我们在接收数据时,如果按照函数定义,总会受到数据长度的限制。但是因为各种因素,总希望能够通过某些操作来越过这一限制,即接收...1.2应用程序只有在接收到3个中断中的任何一个时才必须处.
  • 在网上看了很多的教程,我移植之后,一直存在最多收发200来字节的问题,没有发现是哪里的原因,查阅了很多资料,总结出来一...4.一严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制 5.真正实现了STM32 H.
  • 本文使用STM32CubeMX,STM32F103RCT61,在Pinout中激活USART1和USART22,在Configration 中点击Connectivity-&... USART2 设置参数DMA设置如下两USART波特率并不一致4,生成初始化代码,5,在usart.h中修...
  • 一:寄存器的使用:1、wince内部对物理地址的访问:可以有3个途径。1:直接使用g_oalAddressTable(oemaddrtab_cfg.inc)的已经定义好的,物理地址对应的虚拟地址。如:DCD 0x80000000, 0x30000000, 128 ;访问虚拟...
  • DMA

    千次阅读 2011-12-09 10:45:08
     DMA原理:DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,他允许不同速度的硬件装置来沟通,而不需要依于 CPU 的大量 中断 负载。否则,CPU 需要从 来源 把每一片段的资料复制到 暂存器,...
  • 3DMA设置 DMA模式设置成循环模式,数据宽度设置成半字 4、中断设置 5、DMA中断函数处理 判断是否为DMA传输完成中断,然后关闭ADC,设置标志在主函数中处理数据 if(__HAL_DMA_GET_IT_SOURCE(&hdma_adc1,DMA_...
  • 中断驱动方式3.DMA方式4.通道控制方式5.四种方式总结一下 0.思维导图 1.程序直接控制方式 key word : 轮询 完成一次读/写操作的流程图(以读操作为例) 下面以C语言代码和流程图来剖析,程序直接控制方式 分析...
  • 一、DMA和程序中断两种方式的数据通路 二、DMA与主存交换数据的三种方式 1、停止CPU访问主存 只要外部设备要和内存进行数据交换,则在数据交换的过程中,从第一数据开始,cpu就放弃了总线的控制权和内存的...
  • 今天调试ADC的DMA功能时,使用HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_Results,6);启动6个通道的读数,发现数据...我的理解是DMA中断中还会进行转换,此时使用清内存函数,刚好DMA完成了3个通道的转换,就刚好被
  • 使用DMA

    2019-01-13 15:48:36
    DMA(Direct Memory Access,直接内存存取) ,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。CPU同一时间...
  • 3、串口缓存和DMA缓存用两独立,防止接收时有冲突。 4、接收时看空闲中断信号,有信号说明接收完了,把数据拿出来,DMA读数清零来重新接收,状态改为接收完成 5、发送时要手动开启DMA传输,传输完成后会产生串口中
  • 1、初始化的时候初始化DMADMA发送完成中断,串口中断 我用的是STM32G031G8U6,所以用的是串口2...2、定义一全局变量用来指示DMA发送完成,初值为1表示发送完成 并且包含stdarg.h 文件 3、定义myprintf int myprintf
  • S3C2440-中断

    2013-10-17 15:28:03
    S3C2440包含59个中断源: 一级中断:不带有子中断的中断源。如:EIN0、TIMER0、DMA0等,共:24个 一般中断子中断:UART0_ERR/RXD/TXD等,共15个  外部中断子中断:EINT4、5~23,共20个 中断方式如下: 中断...
  • stm32(3DMA MtoM/MtoP

    2020-04-18 21:46:01
    DMA我们理解浅显一点就是数据可以不经过CPU中断从一地方传输到另一地方(直接传输),这时候CPU可以干其他的事(比如点亮一LED灯)。 STM32F4xx 系列的DMA 支持外设到存储器传输、存储器到外设传输和存储器到...
  • 为防止数据错位,不能用连续模式,而应在查询或中断中先关闭ADC转换,再开启ADC转换)so应采用扫描、非连续的方式、DMA正常模式,DMA的EOC中断或查询; 上述的配置下,ADC运行流程如下: 每规则通道采集之后,每...
  • 最近有东东需要频繁对IO口进行读取,虽然说可以用定时器中断,但是频繁地进入中断会影响性能,于是用DMA来处理。 cube和keil的工程在下方的链接中,这里就不展开了 配置好cube后,打开stm32f1xx_hal_msp.c文件,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 601
精华内容 240
关键字:

dma3个中断