【STM32】在标准库中使用DMA
1.MDA简介
DMA全称Direct Memory Access,直接存储区访问。
DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM和IO设备开辟一个直接传输数据的通道,使得CPU的效率大大提高。
STM32F4xx系列的DMA支持外设到存储器传输、存储器到外设传输和存储器到存储器传输三种传输模式。 这里的外设一般指外设的数据寄存器,比如ADC、SPI、I2C、DCMI等等外设的数据寄存器,存储器一般是指片内SRAM、外部存储器、片内Flash等等。
外设到存储器传输就是把外设数据寄存器内容转移到指定的内存空间。比如进行ADC采集时我们可以利用DMA传输把AD转换数据转移到我们定义的存储区中, 这样对于多通道采集、采样频率高、连续输出数据的AD采集是非常高效的处理方法。
存储区到外设传输就是把特定存储区内容转移至外设的数据寄存器中,这种多用于外设的发送通信。
存储器到存储器传输就是把一个指定的存储区内容拷贝到另一个存储区空间。功能类似于C语言内存拷贝函数memcpy, 利用DMA传输可以达到更高的传输效率,特别是DMA传输是不占用CPU的,可以节省很多CPU资源。
1.1外设通道选择
STM32F4xx系列资源丰富,具有两个DMA控制器,同时外设繁多,为实现正常传输,DMA需要通道选择控制。每个DMA控制器具有8个数据流, 每个数据流对应8个外设请求。在实现DMA传输之前,DMA控制器会通过DMA数据流x配置寄存器DMA_SxCR(x为0~7,对应8个DMA数据流)的CHSEL[2:0]位选择对应的通道作为该数据流的目标外设。
外设通道选择要解决的主要问题是决定哪一个外设作为该数据流的源地址或者目标地址。
DMA1各个通道的请求映像
DMA2各个通道的请求映像每个外设请求都占用一个数据流通道,相同外设请求可以占用不同数据流通道。
1.2仲裁器
一个DMA控制器对应8个数据流,数据流包含要传输数据的源地址、目标地址、数据等等信息。如果我们需要同时使用同一个DMA控制器(DMA1或DMA2)多个外设请求时, 那必然需要同时使用多个数据流,那究竟哪一个数据流具有优先传输的权利呢?这就需要仲裁器来管理判断了。
仲裁器管理数据流方法分为两个阶段。第一阶段属于软件阶段,我们在配置数据流时可以通过寄存器设定它的优先级别, 具体配置DMA_SxCR寄存器PL[1:0]位,可以设置为非常高、高、中和低四个级别。第二阶段属于硬件阶段,如果两个或以上数据流软件设置优先级一样, 则他们优先级取决于数据流编号,编号越低越具有优先权,比如数据流2优先级高于数据流3。
1.3FIFO
每个数据流都独立拥有四级32位FIFO(先进先出存储器缓冲区)。DMA传输具有FIFO模式和直接模式。
直接模式在每个外设请求都立即启动对存储器传输。在直接模式下,如果DMA配置为存储器到外设传输那DMA会见一个数据存放在FIFO内, 如果外设启动DMA传输请求就可以马上将数据传输过去。
FIFO用于在源数据传输到目标地址之前临时存放这些数据。可以通过DMA数据流xFIFO控制寄存器DMA_SxFCR的FTH[1:0]位来控制FIFO的阈值, 分别为1/4、1/2、3/4和满。如果数据存储量达到阈值级别时,FIFO内容将传输到目标中。
FIFO对于要求源地址和目标地址数据宽度不同时非常有用,比如源数据是源源不断的字节数据,而目标地址要求输出字宽度的数据, 即在实现数据传输时同时把原来4个8位字节的数据拼凑成一个32位字数据。此时使用FIFO功能先把数据缓存起来,分别根据需要输出数据。
FIFO另外一个作用使用于突发(burst)传输。
2.DMA数据配置
2.1DMA传输模式
DMA2支持全部三种传输模式,而DMA1只有外设到存储器和存储器到外设两种模式。模式选择可以通过DMA_SxCR寄存器的DIR[1:0]位控制, 进而将DMA_SxCR寄存器的EN位置1就可以使能DMA传输。
在DMA_SxCR寄存器的PSIZE[1:0]和MSIZE[1:0]位分别指定外设和存储器数据宽度大小,可以指定为字节(8位)、半字(16位)和字(32位), 我们可以根据实际情况设置。直接模式要求外设和存储器数据宽度大小一样,实际上在这种模式下DMA数据流直接使用PSIZE,MSIZE不被使用。
2.2源地址和目标地址
DMA数据流x外设地址DMA_SxPAR(x为0~7)寄存器用来指定外设地址,它是一个32位数据有效寄存器。 DMA数据流x存储器0地址DMA_SxM0AR(x为0~7) 寄存器和DMA数据流x存储器1地址DMA_SxM1AR(x为0~7)寄存器用来存放存储器地址, 其中DMA_SxM1AR只用于双缓冲模式,DMA_SxM0AR和DMA_SxM1AR都是32位数据有效的。
当选择外设到存储器模式时,即设置DMA_SxCR寄存器的DIR[1:0] 位为“00”,DMA_SxPAR寄存器为外设地址,也是传输的源地址, DMA_SxM0AR寄存器为存储器地址,也是传输的目标地址。对于存储器到存储器传输模式,即设置DIR[1:0]位为“10”时, 采用与外设到存储器模式相同配置。而对于存储器到外设,即设置DIR[1:0]位为“01”时,DMA_SxM0AR寄存器作为为源地址,DMA_SxPAR寄存器作为目标地址。
2.3流控制器
流控制器主要涉及到一个控制DMA传输停止问题。DMA传输在DMA_SxCR寄存器的EN位被置1后就进入准备传输状态,如果有外设请求DMA传输就可以进行数据传输。 很多情况下,我们明确知道传输数据的数目,比如要传1000个或者2000个数据,这样我们就可以在传输之前设置DMA_SxNDTR寄存器为要传输数目值, DMA控制器在传输完这么多数目数据后就可以控制DMA停止传输。
DMA数据流x数据项数DMA_SxNDTR(x为0~7)寄存器用来记录当前仍需要传输数目,它是一个16位数据有效寄存器,即最大值为65535, 这个值在程序设计是非常有用也是需要注意的地方。我们在编程时一般都会明确指定一个传输数量,在完成一次数目传输后DMA_SxNDTR计数值就会自减,当达到零时就说 明传输完成。
如果某些情况下在传输之前我们无法确定数据的数目,那DMA就无法自动控制传输停止了,此时需要外设通过硬件通信向DMA控制器发送停止传输信号。 这里有一个大前提就是外设必须是可以发出这个停止传输信号,只有SDIO才有这个功能,其他外设不具备此功能。
2.4循环模式
循环模式相对应于一次模式。一次模式就是传输一次就停止传输,下一次传输需要手动控制,而循环模式在传输一次后会自动按照相同配置重新传输,周而复始直至被控制停止或传输发生错误。
通过DMA_SxCR寄存器的CIRC位可以使能循环模式。
2.5传输类型
DMA传输类型有单次(Single)传输和突发(Burst)传输。突发传输就是用非常短时间结合非常高数据信号率传输数据,相对正常传输速度, 突发传输就是在传输阶段把速度瞬间提高,实现高速传输,在数据传输完成后恢复正常速度,有点类似达到数据块“秒传”效果。为达到这个效果突发传输过程要占用AHB总线, 保证要求每个数据项在传输过程不被分割,这样一次性把数据全部传输完才释放AHB总线;而单次传输时必须通过AHB的总线仲裁多次控制才传输完成。
2.6直接模式
默认情况下,DMA工作在直接模式,不使能FIFO阈值级别。
直接模式在每个外设请求都立即启动对存储器传输的单次传输。直接模式要求源地址和目标地址的数据宽度必须一致, 所以只有PSIZE控制,而MSIZE值被忽略。突发传输是基于FIFO的所以直接模式不被支持。另外直接模式不能用于存储器到存储器传输。
在直接模式下,如果DMA配置为存储器到外设传输那DMA会见一个数据存放在FIFO内,如果外设启动DMA传输请求就可以马上将数据传输过去。
2.7双缓冲模式
设置DMA_SxCR寄存器的DBM位为1可启动双缓冲传输模式,并自动激活循环模式。双缓冲不应用与存储器到存储器的传输。双缓冲模式下, 两个存储器地址指针都有效,即DMA_SxM1AR寄存器将被激活使用。开始传输使用DMA_SxM0AR寄存器的地址指针所对应的存储区, 当这个存储区数据传输完DMA控制器会自动切换至DMA_SxM1AR寄存器的地址指针所对应的另一块存储区, 如果这一块也传输完成就再切换至DMA_SxM0AR寄存器的地址指针所对应的存储区,这样循环调用。
当其中一个存储区传输完成时都会把传输完成中断标志TCIF位置1,如果我们使能了DMA_SxCR寄存器的传输完成中断,则可以产生中断信号, 这个对我们编程非常有用。另外一个非常有用的信息是DMA_SxCR寄存器的CT位,当DMA控制器是在访问使用DMA_SxM0AR时CT=0, 此时CPU不能访问DMA_SxM0AR,但可以向DMA_SxM1AR填充或者读取数据;当DMA控制器是在访问使用DMA_SxM1AR时CT=1,此时CPU不能访问DMA_SxM1AR, 但可以向DMA_SxM0AR填充或者读取数据。另外在未使能DMA数据流传输时,可以直接写CT位,改变开始传输的目标存储区。
双缓冲模式应用在需要解码程序的地方是非常有效的。比如MP3格式音频解码播放,MP3是被压缩的文件格式, 我们需要特定的解码库程序来解码文件才能得到可以播放的PCM信号,解码需要一定的实际,按照常规方法是读取一段原始数据到缓冲区, 然后对缓冲区内容进行解码,解码后才输出到音频播放电路,这种流程对CPU运算速度要求高,很容易出现播放不流畅现象。 如果我们使用DMA双缓冲模式传输数据就可以非常好的解决这个问题,达到解码和输出音频数据到音频电路同步进行的效果。
2.8DMA中断
每个DMA数据流可以在发送以下事件时产生中断:
-
达到半传输:DMA数据传输达到一半时HTIF标志位被置1, 如果使能HTIE中断控制位将产生达到半传输中断;
-
传输完成:DMA数据传输完成时TCIF标志位被置1, 如果使能TCIE中断控制位将产生传输完成中断;
-
传输错误:DMA访问总线发生错误或者在双缓冲模式下试图访问“受限”存储器地址寄存器时TEIF标志位被置1, 如果使能TEIE中断控制位将产生传输错误中断;
-
FIFO错误:发生FIFO下溢或者上溢时FEIF标志位被置1, 如果使能FEIE中断控制位将产生FIFO错误中断;
-
直接模式错误:在外设到存储器的直接模式下,因为存储器总线没得到授权,使得先前数据没有完成被传输到存储器空间上, 此时DMEIF标志位被置1,如果使能DMEIE中断控制位将产生直接模式错误中断。
3.DMA_InitTypeDef初始化结构体
typedef struct {uint32_t DMA_Channel; //通道选择uint32_t DMA_PeripheralBaseAddr; //外设地址uint32_t DMA_Memory0BaseAddr; //存储器0地址uint32_t DMA_DIR; //传输方向uint32_t DMA_BufferSize; //数据数目uint32_t DMA_PeripheralInc; //外设递增uint32_t DMA_MemoryInc; //存储器递增uint32_t DMA_PeripheralDataSize; //外设数据宽度uint32_t DMA_MemoryDataSize; //存储器数据宽度uint32_t DMA_Mode; //模式选择uint32_t DMA_Priority; //优先级uint32_t DMA_FIFOMode; //FIFO模式uint32_t DMA_FIFOThreshold; //FIFO阈值uint32_t DMA_MemoryBurst; //存储器突发传输uint32_t DMA_PeripheralBurst; //外设突发传输
} DMA_InitTypeDef;
-
DMA_Channel:DMA请求通道选择,可选通道0至通道7,每个外设对应固定的通道, 具体设置值需要查表 DMA1各个通道的请求映像 和表 DMA2各个通道的请求映像 ; 它设定DMA_SxCR寄存器的CHSEL[2:0]位的值。例如,我们使用模拟数字转换器ADC3规则采集4个输入通道的电压数据,查表 DMA2各个通道的请求映像 可知使用通道2。
-
DMA_PeripheralBaseAddr:外设地址,设定DMA_SxPAR寄存器的值;一般设置为外设的数据寄存器地址, 如果是存储器到存储器模式则设置为其中一个存储区地址。ADC3的数据寄存器ADC_DR地址为((uint32_t)ADC3+0x4C)。
-
DMA_Memory0BaseAddr:存储器0地址,设定DMA_SxM0AR寄存器值;一般设置为我们自定义存储区的首地址。 我们程序先自定义一个16位无符号整形数组ADC_ConvertedValue[4]用来存放每个通道的ADC值, 所以把数组首地址(直接使用数组名即可)赋值给DMA_Memory0BaseAddr。
-
DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设以及存储器到存储器。 它设定DMA_SxCR寄存器的DIR[1:0]位的值。ADC采集显然使用外设到存储器模式。
-
DMA_BufferSize:设定待传输数据数目,初始化设定DMA_SxNDTR寄存器的值。 这里ADC是采集4个通道数据,所以待传输数目也就是4。
-
DMA_PeripheralInc:如果配置为DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定DMA_SxCR寄存器的PINC位的值; 一般外设都是只有一个数据寄存器,所以一般不会使能该位。ADC3的数据寄存器地址是固定并且只有一个所以不使能外设地址递增。
-
DMA_MemoryInc:如果配置为DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定DMA_SxCR寄存器的MINC位的值; 我们自定义的存储区一般都是存放多个数据的,所以使能存储器地址自动递增功能。我们之前已经定义了一个包含4个元素的数字用来存放数据, 使能存储区地址递增功能,自动把每个通道数据存放到对应数组元素内。
-
DMA_PeripheralDataSize:外设数据宽度,可选字节(8位)、半字(16位)和字(32位),它设定DMA_SxCR寄存器的PSIZE[1:0]位的值。 ADC数据寄存器只有低16位数据有效,使用半字数据宽度。
-
DMA_MemoryDataSize:存储器数据宽度,可选字节(8位)、半字(16位)和字(32位),它设定DMA_SxCR寄存器的MSIZE[1:0]位的值。 保存ADC转换数据也要使用半字数据宽度,这跟我们定义的数组是相对应的。
-
DMA_Mode:DMA传输模式选择,可选一次传输或者循环传输,它设定DMA_SxCR寄存器的CIRC位的值。 我们希望ADC采集是持续循环进行的,所以使用循环传输模式。
-
DMA_Priority:软件设置数据流的优先级,有4个可选优先级分别为非常高、高、中和低,它设定DMA_SxCR寄存器的PL[1:0]位的值。 DMA优先级只有在多个DMA数据流同时使用时才有意义,这里我们设置为非常高优先级就可以了。
-
DMA_FIFOMode:FIFO模式使能,如果设置为DMA_FIFOMode_Enable表示使能FIFO模式功能; 它设定DMA_SxFCR寄存器的DMDIS位。ADC采集传输使用直接传输模式即可,不需要使用FIFO模式。
-
DMA_FIFOThreshold:FIFO阈值选择,可选4种状态分别为FIFO容量的1/4、1/2、3/4和满;它设定DMA_SxFCR寄存器的FTH[1:0]位; DMA_FIFOMode设置为DMA_FIFOMode_Disable,那DMA_FIFOThreshold值无效。ADC采集传输不使用FIFO模式,设置改值无效。
-
DMA_MemoryBurst:存储器突发模式选择,可选单次模式、4节拍的增量突发模式、8节拍的增量突发模式或16节拍的增量突发模式, 它设定DMA_SxCR寄存器的MBURST[1:0]位的值。ADC采集传输是直接模式,要求使用单次模式。
-
DMA_PeripheralBurst:外设突发模式选择,可选单次模式、4节拍的增量突发模式、8节拍的增量突发模式或16节拍的增量突发模式, 它设定DMA_SxCR寄存器的PBURST[1:0]位的值。ADC采集传输是直接模式,要求使用单次模式。
4.DMA存储器到存储器模式实验
存储器到存储器模式可以实现数据在两个内存的快速拷贝。我们先定义一个静态的源数据,然后使用DMA传输把源数据拷贝到目标地址上,最后对比源数据和目标地址的数据,看看是否传输准确。
#ifndef __BSP_DMA_H
#define __BSP_DMA_H#ifdef __cplusplus
extern "C"{#endif#include "stm32f4xx.h"/* 相关宏定义,使用存储器到存储器传输必须使用DMA2 */
#define DMA_STREAM DMA2_Stream0
#define DMA_CHANNEL DMA_Channel_0
#define DMA_STREAM_CLOCK RCC_AHB1Periph_DMA2
#define DMA_IT_TCIF DMA_IT_TCIF0
#define DMA_IT_HTIF DMA_IT_HTIF0
#define DMA_FLAG_TCIF DMA_FLAG_TCIF0
#define DMA_FLAG_HTIF DMA_FLAG_HTIF0
#define DMA_STREAM_IRQn DMA2_Stream0_IRQn
#define DMA_STREAM_IRQHandler DMA2_Stream0_IRQHandler#define BUFFER_SIZE 32void Init_M2M_DMA(void);
#ifdef __cplusplus
}
#endif#endif
#include "bsp_dma.h"
#include "stdio.h"
#include "string.h"/* 定义aSRC_Const_Buffer数组作为DMA传输数据源
const关键字将aSRC_Const_Buffer数组变量定义为常量类型 */
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80
};
/* 定义DMA传输目标存储器 */
uint32_t aDST_Buffer[BUFFER_SIZE];void Init_M2M_DMA(void)
{/* 使能DMA时钟 */RCC_AHB1PeriphClockCmd(DMA_STREAM_CLOCK, ENABLE);/* 复位初始化DMA数据流 */DMA_DeInit(DMA_STREAM);/* 确保DMA数据流复位完成 */while (DMA_GetCmdStatus(DMA_STREAM) != DISABLE) {}DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_BufferSize=BUFFER_SIZE;//一次DMA事务传输的数据个数DMA_InitStructure.DMA_Channel=DMA_CHANNEL;DMA_InitStructure.DMA_DIR=DMA_DIR_MemoryToMemory;DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable;DMA_InitStructure.DMA_FIFOThreshold=DMA_FIFOThreshold_Full;DMA_InitStructure.DMA_Memory0BaseAddr= (uint32_t)aDST_Buffer;DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single;DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Word;DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)aSRC_Const_Buffer;DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single;DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Word;DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Enable;DMA_InitStructure.DMA_Priority=DMA_Priority_Low;DMA_Init(DMA_STREAM,&DMA_InitStructure);//配置中断控制器并使能中断NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=DMA_STREAM_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;NVIC_Init(&NVIC_InitStruct);DMA_ITConfig(DMA_STREAM,DMA_IT_TCIF|DMA_IT_HTIF,ENABLE);DMA_ClearITPendingBit(DMA_STREAM,DMA_IT_TCIF|DMA_IT_HTIF);DMA_Cmd(DMA_STREAM,ENABLE);
}void DMA_STREAM_IRQHandler(void)
{if(SET== DMA_GetFlagStatus(DMA_STREAM,DMA_FLAG_HTIF)){printf("half transfer\r\n");DMA_ClearFlag(DMA_STREAM,DMA_FLAG_HTIF);}else if(SET== DMA_GetFlagStatus(DMA_STREAM,DMA_FLAG_TCIF)){printf("transfer complete\r\n");DMA_ClearFlag(DMA_STREAM,DMA_FLAG_TCIF);}// if(SET==DMA_GetITStatus(DMA_STREAM,DMA_IT_HTIF))
// {
// //half transfer complete
// printf("half transfer\r\n");
// DMA_ClearITPendingBit(DMA_STREAM,DMA_IT_HTIF);
//
// }
// else if(SET==DMA_GetITStatus(DMA_STREAM,DMA_IT_TCIF))
// {
// //transfer complete
//
// if(0== memcmp(aSRC_Const_Buffer,aDST_Buffer,BUFFER_SIZE))
// {
// printf("transfer complete\r\n");
// }
// DMA_ClearITPendingBit(DMA_STREAM,DMA_IT_TCIF);
// }
}
编程注意事项:
- 对于存储器到存储器传输模式,源地址和目标地址的设置, 采用与外设到存储器模式相同配置。也就是源存储器地址当作外设地址。
- 我们在中断服务函数中检查ITStatus时发现,没有检测到half transfer的IT标志位,但是可以检测到transfer complete的IT标志位。但是FlagStatus都可以获取到,也不知道是啥问题。
- 如果我换为其他的DMA2_STREAM,比如DMA2_Stream1。甚至transfer complete的IT标志位都没有检测到。也不知道是啥原因。
- 对于存储器到存储器传输模式,只能选择DMA2。
2024/7/5更新
上文,我们提到在没有中断服务函数中没有检测到ITStatus,原来是因为那个DMA_ITConfig函数传参错误导致的。我们查看原函数注释发现:
/*** @brief Enables or disables the specified DMAy Streamx interrupts.* @param DMAy_Streamx: where y can be 1 or 2 to select the DMA and x can be 0* to 7 to select the DMA Stream.* @param DMA_IT: specifies the DMA interrupt sources to be enabled or disabled. * This parameter can be any combination of the following values:* @arg DMA_IT_TC: Transfer complete interrupt mask* @arg DMA_IT_HT: Half transfer complete interrupt mask* @arg DMA_IT_TE: Transfer error interrupt mask* @arg DMA_IT_FE: FIFO error interrupt mask* @param NewState: new state of the specified DMA interrupts.* This parameter can be: ENABLE or DISABLE.* @retval None*/
使能中断函数中没有明确是哪个明确到是哪一个dma stream,但是在get或者clear函数中的参数又明确到了是哪一个dma stream。
/*** @brief Checks whether the specified DMAy Streamx interrupt has occurred or not.* @param DMAy_Streamx: where y can be 1 or 2 to select the DMA and x can be 0* to 7 to select the DMA Stream.* @param DMA_IT: specifies the DMA interrupt source to check.* This parameter can be one of the following values:* @arg DMA_IT_TCIFx: Streamx transfer complete interrupt* @arg DMA_IT_HTIFx: Streamx half transfer complete interrupt* @arg DMA_IT_TEIFx: Streamx transfer error interrupt* @arg DMA_IT_DMEIFx: Streamx direct mode error interrupt* @arg DMA_IT_FEIFx: Streamx FIFO error interrupt* Where x can be 0 to 7 to select the DMA Stream.* @retval The new state of DMA_IT (SET or RESET).*/
这里需要特别注意一下。。。。所以,我们更新一下代码
DMA_ITConfig(DMA_STREAM,DMA_IT_TC|DMA_IT_HT,ENABLE);void DMA_STREAM_IRQHandler(void)
{if(SET==DMA_GetITStatus(DMA_STREAM,DMA_IT_HTIF)){//half transfer completeprintf("half transfer\r\n");DMA_ClearITPendingBit(DMA_STREAM,DMA_IT_HTIF);}else if(SET==DMA_GetITStatus(DMA_STREAM,DMA_IT_TCIF)){//transfer completeif(0== memcmp(aSRC_Const_Buffer,aDST_Buffer,BUFFER_SIZE)){printf("transfer complete\r\n");}DMA_ClearITPendingBit(DMA_STREAM,DMA_IT_TCIF);}
}
相关文章:

【STM32】在标准库中使用DMA
1.MDA简介 DMA全称Direct Memory Access,直接存储区访问。 DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和…...

多线程详解
文章目录 多线程创建方式p3一些教程 狂神说 多线程创建方式p3 代码: package com.demo1;//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程/*** 总结:注意,线程开启不一定立即执行,dCPU调度执行*/public class TestThread1 extends Thre…...
软件工程需求之:业务需求与用户需求
在软件开发项目中,"业务需求"和"用户需求"是两个核心概念,它们分别从不同的角度描述了软件应该具备的功能和特性。理解这两个概念的区别对于成功地规划和开发软件至关重要。 业务需求 业务需求主要关注于软件项目如何帮助实现企业…...

Nettyの源码分析
本篇为Netty系列的最后一篇,按照惯例会简单介绍一些Netty相关核心源码。 1、Netty启动源码分析 代码就使用最初的Netty服务器案例,在bind这一行打上断点,观察启动的全过程: 由于某些方法的调用链过深,节约篇幅…...
MySQL远程登录
root是超级管理员,默认情况下,root不能作为远程登录的用户名,远程登录前,需要将登录的数据库在本地登录,修改权限,输入: update user set host % where user root ; 回车键,再输…...

html的作业
目录 作业题目 1.用户注册 A图 B代码 2.工商银行电子汇款单 A图 B代码 3.李白诗词 A图 B代码 4.豆瓣电影 A图 B代码 学习产出: 作业题目 1.用户注册 A图 B代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset&qu…...
【TORCH】查看dataloader里的数据,通过dataloader.dataset或enumerate
文章目录 dataloader.dataset示例代码使用自定义数据集使用 MNIST 数据集 说明 enumerate示例代码说明使用 MNIST 数据集的例子 dataloader.dataset 是的,您可以直接访问 train_loader 的数据集来查看数据,而不必通过 enumerate 遍历数据加载器。可以通…...

KDTree 简单原理与实现
介绍 K-D树是一种二叉树的数据结构,其中每个节点代表一个k维点,可用于组织K维空间中的点,其中K通常是一个非常大的数字。二叉树结构允许对多维空间中的点进行非常有效的搜索,包括最近邻搜索和范围搜索,树中的每个非叶…...

[c++] 可变参数模版
前言 可变参数模板是C11及之后才开始使用,学校的老古董编译器不一定能用 相信大家在刚入门c/c时都接触过printf函数 int printf ( const char * format, ... ); printf用于将数据格式化输出到屏幕上,它的参数非常有意思,可以支持任意数量,任意类型的多参数.而如果我们想实现类…...

QWidget窗口抗锯齿圆角的一个实现方案(支持子控件)2
QWidget窗口抗锯齿圆角的一个实现方案(支持子控件)2 本方案使用了QGraphicsEffect,由于QGraphicsEffect对一些控件会有渲染问题,比如列表、表格等,所以暂时仅作为研究,优先其他方案 在之前的文章中&#…...

数据结构之“队列”(全方位认识)
🌹个人主页🌹:喜欢草莓熊的bear 🌹专栏🌹:数据结构 前言 上期博客介绍了” 栈 “这个数据结构,他具有先进后出的特点。本期介绍“ 队列 ”这个数据结构,他具有先进先出的特点。 目录…...
密码学复习
目录 基础 欧拉函数 欧拉函数φ(n)定义 计算方法的技巧 当a=a_1*a_2*……*a_n时 欧拉定理 剩余系 一些超简单密码 维吉尼亚 密钥fox 凯撒(直接偏移) 凯特巴氏(颠倒字母表) 摩斯密码(字母对应电荷线) 希尔(hill)密码 一些攻击 RSA 求uf+vg=1 快速幂模m^…...

【文献解析】一种像素级的激光雷达相机配准方法
大家好呀,我是一个SLAM方向的在读博士,深知SLAM学习过程一路走来的坎坷,也十分感谢各位大佬的优质文章和源码。随着知识的越来越多,越来越细,我准备整理一个自己的激光SLAM学习笔记专栏,从0带大家快速上手激…...

Http 实现请求body体和响应body体的双向压缩方案
目录 一、前言 二、方案一(和http header不进行关联) 二、方案二(和http header进行关联) 三、 客户端支持Accept-Encoding压缩方式,服务器就一定会进行压缩吗? 四、参考 一、前言 有时请求和响应的body体比较大,需要进行压缩,以减少传输的带宽。 二、方案一(和…...

C++(Qt)-GIS开发-简易瓦片地图下载器
Qt-GIS开发-简易瓦片地图下载器 文章目录 Qt-GIS开发-简易瓦片地图下载器1、概述2、安装openssl3、实现效果4、主要代码4.1 算法函数4.2 瓦片地图下载url拼接4.3 多线程下载 5、源码地址6、参考 更多精彩内容👉个人内容分类汇总 👈👉GIS开发 …...

誉天教育7月开班计划:为梦想插上腾飞的翅膀!
随着夏日的脚步渐近,誉天教育也迎来了新一轮的学习热潮。在这个充满活力和希望的季节里,我们精心策划了7月的开班计划,旨在为广大学子提供一个优质、高效的学习平台,助力他们追逐梦想,实现自我价值。 本月 Linux云计算…...

STM32基础篇:GPIO
GPIO简介 GPIO:即General Purpose Input/Output,通用目的输入/输出。就是一种片上外设(内部模块)。 对于STM32的芯片来说,周围有一圈引脚,有时需要对引脚进行读写(读:从外部输入一…...
HTTPS 发送请求出现TLS握手失败
最近在工作中,调外部接口,发现在clientHello步骤报错,服务端没有返回serverHello。 从网上找了写方法,都没有解决; 在idea的vm options加上参数: -Djavax.net.debugSSL,handshake 把SSL和handshake的日…...

数字化精益生产系统--IFS财务管理系统
IFS财务管理系统是一款功能丰富、高效且灵活的企业财务管理软件,广泛应用于多个行业和不同规模的企业中。以下是对IFS财务管理系统的功能设计:...

基于SpringBoot的校园台球厅人员与设备管理系统
本系统是要设计一个校园台球厅人员与设备管理系统,这个系统能够满足校园台球厅人员与设备的管理及用户的校园台球厅人员与设备管理功能。系统的主要功能包括首页、个人中心、用户管理、会员账号管理、会员充值管理、球桌信息管理、会员预约管理、普通预约管理、留言…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版
1.题目描述 2.思路 当前的元素可以重复使用。 (1)确定回溯算法函数的参数和返回值(一般是void类型) (2)因为是用递归实现的,所以我们要确定终止条件 (3)单层搜索逻辑 二…...

作为点的对象CenterNet论文阅读
摘要 检测器将图像中的物体表示为轴对齐的边界框。大多数成功的目标检测方法都会枚举几乎完整的潜在目标位置列表,并对每一个位置进行分类。这种做法既浪费又低效,并且需要额外的后处理。在本文中,我们采取了不同的方法。我们将物体建模为单…...

react-pdf(pdfjs-dist)如何兼容老浏览器(chrome 49)
之前都是使用react-pdf来渲染pdf文件,这次有个需求是要兼容xp环境,xp上chrome最高支持到49,虽然说iframe或者embed都可以实现预览pdf,但为了后续的定制化需求,还是需要使用js库来渲染。 chrome 49测试环境 能用的测试…...

Gitlab + Jenkins 实现 CICD
CICD 是持续集成(Continuous Integration, CI)和持续交付/部署(Continuous Delivery/Deployment, CD)的缩写,是现代软件开发中的一种自动化流程实践。下面介绍 Web 项目如何在代码提交到 Gitlab 后,自动发布…...