赞
踩
DMA的基本介绍
什么是DMA (DMA的基本定义)
DMA,全称Direct Memory Access,即直接存储器访问。
DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
我们知道CPU有转移数据、计算、控制程序转移等很多功能,系统运作的核心就是CPU,
CPU无时不刻的在处理着大量的事务,但有些事情却没有那么重要,比方说数据的复制和存储数据,如果我们把这部分的CPU资源拿出来,让CPU去处理其他的复杂计算事务,是不是能够更好的利用CPU的资源呢?
因此:转移数据(尤其是转移大量数据)是可以不需要CPU参与。比如希望外设A的数据拷贝到外设B,只要给两种外设提供一条数据通路,直接让数据由A拷贝到B 不经过CPU的处理,
DMA就是基于以上设想设计的,它的作用就是解决大量数据转移过度消耗CPU资源的问题。有了DMA使CPU更专注于更加实用的操作–计算、控制等。
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
DMA传输方式:
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:
DMA传输参数
我们知道,数据传输,首先需要知道的是1 数据的源地址 2 数据传输位置的目标地址 ,3 传递数据多少的数据传输量 ,4 进行多少次传输的传输模式, DMA所需要的核心参数,便是这四个。
当用户将参数设置好,主要涉及源地址、目标地址、传输数据量这三个,DMA控制器就会启动数据传输,当剩余传输数据量为0时 达到传输终点,结束DMA传输 ,当然,DMA 还有循环传输模式 当到达传输终点时会重新启动DMA传输。
也就是说只要剩余传输数据量不是0,而且DMA是启动状态,那么就会发生数据传输。
每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置;
对于大容量的STM32芯片有2个DMA控制器 两个DMA控制器,DMA1有7个通道,DMA2有5个通道。
每个通道都可以配置一些外设的地址。
①DMA1 controller
从外设(TIMx[x=1、2、3、4]、ADC1、SPI1、SPI/I2S2、I2Cx[x=1、2]和USARTx[x=1、2、3])产生的7个DMA请求,通过逻辑或输入到DMA1控制器 其中每个通道都对应着具体的外设:
② DMA2 controller
上方的框图,我们可以看到STM32内核,存储器,外设及DMA的连接,这些硬件最终通过各种各样的线连接到总线矩阵中,硬件结构之间的数据转移都经过总线矩阵的协调,使各个外设和谐的使用总线来传输数据。
我们对他来进行分析:以ADC采集的数据进行举例
下面看有与没有DMA的情况下,ADC采集的数据是怎样存放到SRAM中的?
没有DMA
如果没有DMA,CPU传输数据还要以内核作为中转站,比如要将ADC采集的数据转移到到SRAM中,这个过程是这样的:
内核通过DCode经过总线矩阵协调,从获取AHB存储的外设ADC采集的数据,
然后内核再通过DCode经过总线矩阵协调把数据存放到内存SRAM中。
有DMA传输
有DMA传输的过程如下:
具体如下:
在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。DMA传输结束,如果有更多的请求时,外设可以启动下一个周期。
总之,每次DMA传送由3个操作组成:
- 从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元;
- 存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元;
- 执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目。
方法1:DMA_Mode_Normal,正常模式。
当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次
方法2:DMA_Mode_Circular ,循环传输模式
当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式。
仲裁器的作用是确定各个DMA传输的优先级
仲裁器根据通道请求的优先级来启动外设/存储器的访问。
优先权管理分2个阶段:
软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级:
- 最高优先级
- 高优先级
- 中等优先级
- 低优先级;
硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。
比如:如果软件优先级相同,通道2优先于通道4。
注意: 在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2控制器的优先级。
根据 DMA_SxCR 寄存器中 PINC 和 MINC 位的状态,外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值。
通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。
如果使能了递增模式,则根据在 DMA_SxCR 寄存器 PSIZE 或 MSIZE 位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增 1个数据宽度、2个数据宽度或 4个数据宽度。
DMA通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式。
当设置了DMA_CCRx寄存器中的MEM2MEM位之后,在软件设置了DMA_CCRx寄存器中的EN位启动DMA通道时,DMA传输将马上开始。当DMA_CNDTRx寄存器变为0时,DMA传输结束。存储器到存储器模式不能与循环模式同时使用。
这里要注意仅 DMA2 的外设接口可以访问存储器,所以仅 DMA2 控制器支持存储器到存储器的传输,DMA1 不支持。存储器到存储器模式不能与循环模式同时使用。
每个DMA通道都可以在DMA传输过半、传输完成和传输错误时产生中断。为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断。
使能开启,我们也可以通过查询这些位来获得当前 DMA 传输的状态。这里我们常用的是 TCIFx位,即数据流 x 的 DMA 传输完成与否标志。
此部分我们分为DMA寄存器和DMA库函数分别介绍:
DMA配置参数包括:通道地址、优先级、数据传输方向、存储器/外设数据宽度、存储器/外设地址是否增量、循环模式、数据传输量。
我们如果开启了 DMA_ISR 中这些中断,在达到条件后就会跳到中断服务函数里面去,即使 没开启,我们也可以通过查询这些位来获得当前 DMA 传输的状态。这里我们常用的是 TCIFx, 即通道 DMA 传输完成与否的标志。
注意此寄存器为只读寄存器,所以在这些位被置位之后,只 能通过其他的操作来清除。
DMA中断标志清除寄存器(DMA_IFCR)
DMA_IFCR 的各位就是用来清除 DMA_ISR 的对应位的,通过写 0 清除。在 DMA_ISR 被置位后, 我们必须通过向该位寄存器对应的位写入 0 来清除。
该寄存器控制着 DMA 的很多相关 信息,包括数据宽度、外设及存储器的宽度、通道优先级、增量模式、传输方向、中断允许、 使能等都是通过该寄存器来设置的。所以 DMA_CCRx 是 DMA 传输的核心控制寄存器
DMA通道x传输数量寄存器(DMA_CNDTRx)(x = 1…7)
这个寄存器控制 DMA 通道 x 的每次 传输所要传输的数据量。其设置范围为 0~65535。并且该寄存器的值会随着传输的进行而减少, 当该寄存器的值为 0 的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存 器的值来知道当前 DMA 传输的进度
该寄存器用来存储 STM32 外设的地 址,比如我们使用串口 1,那么该寄存器必须写入 0x40013804(其实就是&USART1_DR)。如果使 用其他外设,就修改成相应外设的地址就行了。
该寄存器和 DMA_CPARx 差不多, 但是是用来放存储器的地址的。比如我们使用 SendBuf[5200]数组来做存储器,那么我们在 DMA_CMARx 中写入&SendBuff 就可以了。
通道配置过程 下面是配置DMA通道x的过程(x代表通道号):
一旦启动了DMA通道,它既可响应连到该通道上的外设的DMA请求。 当传输一半的数据后,半传输标志(HTIF)被置1,当设置了允许半传输中断位(HTIE)时,将产生 一个中断请求。在数据传输结束后,传输完成标志(TCIF)被置1,当设置了允许传输完成中断位 (TCIE)时,将产生一个中断请求。
DMA_DeInit(DMAX_ChannelX);
功能:将DMAyChannelx寄存器的初始化为其默认值
注释:RCC_ResetCmd中对DMA无定义,因此采用的直接操纵DMA寄存器的方式。
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
功能:设置要开启的通道,还有一些结构体参数,包括外设基地址,存储器基地址,传输的数据量,增量模式,数据宽度等。
结构体内容参数如下:
- typedef struct {
- uint32_t DMA_PeripheralBaseAddr;
- /*设置DMA源地址*/
- uint32_t DMA_MemoryBaseAddr;
- /*设置DMA目的地址*/
- uint32_t DMA_DIR;
- /* 设置数据传输方向,决定是从外设读取数据到内存还送从内存读取数 据发送到外设,也就是外设是源地还是目的地
- */
- uint32_t DMA_BufferSize;
- /*设置传输大小*/
- uint32_t DMA_PeripheralInc;
- /*设置ReceiveBuff地址是否自增*/
- uint32_t DMA_MemoryInc;
- /*设置传输数据时候内存地址是否递,需要开启*/
- uint32_t DMA_PeripheralDataSize;
- /*外设的数据长度是为字节传输(8bits),半 字传输(16bits) 还是字传输(32bits) */
- uint32_t DMA_MemoryDataSize;
- /*设置内存的数据长度*/
- uint32_t DMA_Mode;
- /*设置DMA的模式,正常模式/循环模式 是否循环发送*/
- uint32_t DMA_Priority;
- /*设置 DMA 通道的优先级,有低,中,高,超高四种模式*/
- uint32_t DMA_M2M;
- /*设置是否是存储器到存储器模式传输,*/ }
- DMA_InitTypeDef;

void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
功能:使能或者失能DMA外设
例如:DMA_Cmd(DMA1_Channel1 , ENABLE);
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
功能:配置指定的DMAy通道x的中断
注释:DMA_IT_TC:传输完成 DMA_IT_HT:传输一半 DMA_IT_TE:传输错误
例如:DMA_ITConfig(DMA1_Channel1 , DMA_IT_TC , ENABLE);
- void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
-
- uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
作用:前者设置DMA通道的传输数据量(DMA处于关闭状态);后者获取当前DMA通道传输剩余数据量(DMA处于开启状态)。
DMA就是一个搬运工,可以将数据从一个位置搬运到另一个位置。
以UART为例,如果要接收数据,会触发UART中断,然后CPU介入,在中断中通过CPU将UART输入寄存器的值读出来,存放到内存中;
而DMA方式,产生UART中断后,DMA直接参与,把UART输入寄存器的值搬运到内存中,CPU只需要在去检查内存的值就好了,这样提高了CPU的效率。
① DMA初始化配置
- void dma_init()
- {
-
- DMA_InitTypeDef DMA_InitStructure;
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
-
- /*DMA配置*/
-
- DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;//串口数据寄存器地址
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; //内存地址(要传输的变量的指针)
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向(从内存到外设)
- DMA_InitStructure.DMA_BufferSize = 500; //传输内容的大小
- 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模式:一次传输,循环
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium ; //优先级:高
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输
-
- DMA_Init(DMA1_Channel4, &DMA_InitStructure); //配置DMA1的4通道
- DMA_Cmd(DMA1_Channel4,ENABLE);
- DMA_SetCurrDataCounter(DMA_CH4,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
- DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//配置DMA发送完成后产生中断
-
- }

- void DMA1_Channel4_IRQHandler(void)
- {
- if(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET)
- {
-
- DMA_ClearFlag(DMA1_FLAG_TC4);
- }
- }
main函数
- #define SEND_BUF_SIZE 500 //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.
-
- u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
- const u8 TEXT_TO_SEND[]={"STM32F1 DMA 串口实验"};
- uint16_t i;
- int main(void)
- {
- uart_init(115200); //串口初始化为115200
-
- for(i=0;i<500;i++)
- {
- SendBuff[i] =0xaf;
- }
- USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口dma传输
-
- while(1);
- =
- }

具体流程如下:
我们以USART1 的DMA传输为例
设置高速外部时钟HSE 选择外部时钟源
2设置串口
3 DMA设置
根据DMA通道预览可以知道,我们用的USART1 的TX RX 分别对应DMA1 的通道4和通道5
1.DMA Request : DMA传输的对应外设
注意: 如果你是在DMA设置界面添加DMA 而没有开启对应外设的话 ,默认为MENTOMEN
2.Channel DMA传输通道设置
DMA1 : DMA1 Channel 0~DMA1 Channel 7
DMA2: DMA2 Channel 1~DMA1 Channel 5
3.Dirction : DMA传输方向
四种传输方向:
- 外设到内存 Peripheral To Memory
- 内存到外设 Memory To Peripheral
- 内存到内存 Memory To Memory
- 外设到外设 Peripheral To Peripheral
4.Priority: 传输速度
- 最高优先级 Very Hight
- 高优先级 Hight
- 中等优先级 Medium
- 低优先级;Low
1.Normal:正常模式
当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次
2.Circular: 循环模式
传输完成后又重新开始继续传输,不断循环永不停止
Increment Address:地址指针递增。
1.左侧Src Memory 表示外设地址寄存器
功能:设置传输数据的时候外设地址是不变还是递增。如果设置 为递增,那么下一次传输的时候地址加 Data Width个字节,
2.右侧Dst Memory 表示内存地址寄存器
功能:设置传输数据时候内存地址是否递增。如果设置 为递增,那么下一次传输的时候地址加 Data Width个字节,
这个Src Memory一样,只不过针对的是内存。
串口发送数据是将数据不断存进固定外设地址串口的发送数据寄存器(USARTx_TDR)。所以外设的地址是不递增。
而内存储器存储的是要发送的数据,所以地址指针要递增,保证数据依次被发出。
串口数据发送寄存器只能存储8bit,每次发送一个字节,所以数据长度选择Byte。
要注意DMA的传输方向别弄错了,到底是PERIPHERIAL到MEMORY还是MEMORY到PERIPHERIAL或者说是Memory到Memory要配置正确。尤其是在用CubeMx配置时,这里有个默认配置是PERIPHERIAL到MEMORY。如果说你的真实意图根本不是从PERIPHERIAL到MEMORY,而你无意中使用了这个默认配置,结果可想而知,DMA传输根本没法正常运行。
4时钟源设置
我的是 外部晶振为8MHz
项目文件设置
创建工程文件
然后点击GENERATE CODE 创建工程
HAL库UARTDMA函数库介绍
1、串口发送/接收函数
HAL_UART_Transmit();串口发送数据,使用超时管理机制
HAL_UART_Receive();串口接收数据,使用超时管理机制
HAL_UART_Transmit_IT();串口中断模式发送
HAL_UART_Receive_IT();串口中断模式接收
HAL_UART_Transmit_DMA();串口DMA模式发送
HAL_UART_Transmit_DMA();串口DMA模式接收
HAL_UART_DMAPause() 暂停串口DMA
HAL_UART_DMAResume(); 恢复串口DMA
HAL_UART_DMAStop(); 结束串口DMA
1.串口DMA发送数据函数:
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口通过DMA发送指定长度的数据。
参数:
举例:
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff)); //串口发送Senbuff数组
串口DMA接收数据:
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口通过DMA接受指定长度的数据。
参数:
举例:
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Recbuff, sizeof(Recbuff)); //串口发送Senbuff数组
串口DMA恢复函数
HAL_UART_DMAResume(&huart1)
作用: 恢复DMA的传输
返回值: 0 正在恢复 1 完成DMA恢复。
测试例程1:
在main.C中添加:
- /* USER CODE BEGIN Init */
- uint8_t Senbuff[] = "\r\n**** Serial Output Message by DMA ***\r\n UART DMA Test \r\n "; //定义数据发送数组
- /* USER CODE END Init */
while循环:
- while (1)
- {
- /* USER CODE END WHILE */
- HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
- HAL_Delay(1000);
- /* USER CODE BEGIN 3 */
- }
注意:如果不开启串口中断,则程序只能发送一次数据,程序不能判断DMA传输是否完成,USART一直处于busy状态。
本例程功能:
使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
STM32 IDLE 接收空闲中断
空闲的定义是总线上在一个字节的时间内没有再接收到数据,USART_IT_IDLE空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。
这里我们使用串口空闲中断(IDLE)和 DMA接收不定长数据。
IDLE的中断产生条件:在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断
例程代码:
uart.c
- volatile uint8_t rx_len = 0; //接收一帧数据的长度
- volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
- uint8_t rx_buffer[100]={0}; //接收数据缓存数组
- void MX_USART1_UART_Init(void)
- {
-
- huart1.Instance = USART1;
- huart1.Init.BaudRate = 115200;
- huart1.Init.WordLength = UART_WORDLENGTH_8B;
- huart1.Init.StopBits = UART_STOPBITS_1;
- huart1.Init.Parity = UART_PARITY_NONE;
- huart1.Init.Mode = UART_MODE_TX_RX;
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- huart1.Init.OverSampling = UART_OVERSAMPLING_16;
- if (HAL_UART_Init(&huart1) != HAL_OK)
- {
- Error_Handler();
- }
- //下方为自己添加的代码
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
-
- //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
-
-
- }

uart.h
- extern UART_HandleTypeDef huart1;
- extern DMA_HandleTypeDef hdma_usart1_rx;
- extern DMA_HandleTypeDef hdma_usart1_tx;
- /* USER CODE BEGIN Private defines */
-
-
- #define BUFFER_SIZE 100
- extern volatile uint8_t rx_len ; //接收一帧数据的长度
- extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
- extern uint8_t rx_buffer[100]; //接收数据缓存数组
main.c
- /*
- *********************************************************************************************************
- * 函 数 名: DMA_Usart_Send
- * 功能说明: 串口发送功能函数
- * 形 参: buf,len
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装
- {
- if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数
- {
- Error_Handler();
- }
-
- }
-
-
- /*
- *********************************************************************************************************
- * 函 数 名: DMA_Usart1_Read
- * 功能说明: 串口接收功能函数
- * 形 参: Data,len
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装
- {
- HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收
- }

while循环
- while (1)
- {
- /* USER CODE END WHILE */
-
- /* USER CODE BEGIN 3 */
- if(recv_end_flag == 1) //接收完成标志
- {
-
-
- DMA_Usart_Send(rx_buffer, rx_len);
- rx_len = 0;//清除计数
- recv_end_flag = 0;//清除接收结束标志位
- // for(uint8_t i=0;i<rx_len;i++)
- // {
- // rx_buffer[i]=0;//清接收缓存
- // }
- memset(rx_buffer,0,rx_len);
- }
- HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
- }

stm32f1xx_it.c中
- #include "usart.h"
-
- void USART1_IRQHandler(void)
- {
- uint32_t tmp_flag = 0;
- uint32_t temp;
- tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
- if((tmp_flag != RESET))//idle标志被置位
- {
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
- //temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
- //temp = huart1.Instance->DR; //读取数据寄存器中的数据
- //这两句和上面那句等效
- HAL_UART_DMAStop(&huart1); //
- temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
- //temp = hdma_usart1_rx.Instance->NDTR;//读取NDTR寄存器 获取DMA中未传输的数据个数,
- //这句和上面那句等效
- rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
- recv_end_flag = 1; // 接受完成标志位置1
- }
- HAL_UART_IRQHandler(&huart1);
-
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。