STM32F4X DMA

STM32F4X DMA

  • 什么是DMA
  • STM32F4X DMA
    • DMA框图
      • DMA通道
      • DMA仲裁器
      • DMA FIFO
    • DMA传输模式
    • DMA传输方向
      • 存储器到存储器
      • 存储器到外设
      • 外设到存储器
    • DMA循环模式和普通模式
      • 循环模式(Circular)
      • 普通模式(Normal)
    • DMA源、目标寄存器增量模式
    • DMA例程
      • 存储器到存储器例程
      • 存储器到外设例程
      • 外设到存储器例程

什么是DMA

DMA又叫直接存储器访问,用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速移动数据。这样节省的CPU资源可供其它操作使用。

STM32F4X DMA

DMA框图

在这里插入图片描述

DMA通道

STM32F4X的每一个DMA控制器有8个数据流,每个数据流又有8个通道。每个通道都会对应相关的外设。
在这里插入图片描述
在这里插入图片描述

DMA仲裁器

虽然STM32F4X的DMA控制器有8个数据流,当有多个数据流产生请求时,DMA控制器每次只能处理一个数据流,这时就需要DMA仲裁器的帮忙了。仲裁器可以理解成是优先级选择器,顾名思义就是根据数据流的优先级来决定处理哪个通道。其优先级选择关系如下。

  • 在相同优先级的情况下,数据流编号低的优先级高,数据流编号高的优先级低。就是STR0>STR1>STR2>STR3>STR4>STR5>STR6>STR7。
  • 用户可以通过软件设置数据流的优先级,分别是低优先级、中等优先级、高优先级和非常高优先级。

DMA FIFO

FIFO 用于在源数据传输到目标之前临时存储这些数据,可以加快DMA的传输速度。在STM32F4X中每个DMA数据流都会有一个独立的4字FIFO,也就是16字节的FIFO。STM32F4X的DMA FIFO可以配置它的阀值,具体如下图所示。
在这里插入图片描述
上图描述了STM32F4X的DMA FIFO的阀值配置,看似很复杂,其实根据下面的例子就我们就可以知道其配置规则。
MSIZE:外设或存储器数据大小,用户可以配置成字节、半字或者字。
MBURST = INCR4: 每次传输4 * MSIZE的大小且FIFO必须是此乘积的整数倍
MBURST = INCR8: 每次传输8 * MSIZE的大小且FIFO必须是此乘积的整数倍
MBURST = INCR16: 每次传输16 * MSIZE的大小且FIFO必须是此乘积的整数倍
我们假设DMA的外设和存储器数据大小为半字,也就是2个字节,MSIZE就为2。MBURST设置为INCR4。
MSIZE = 2
MBURST = INCR4
此时传输的数据量就为MSIZE * 4 = 8字节,那么此时FIFO的阀值只能是8 * 1 = 8或者8 * 2 = 16,也就是阀值为二分之一或者是满。
上面就是FIFO的阀值配置的具体计算,我们也可以根据上面的表格进行配置。

DMA传输模式

DMA有两种传输模式,分别是突发传输和单次传输。突发传输需要跟FIFO结合使用,每次传输4 个、8 个和 16个节拍。单次传输就每次传输一个节拍数。

DMA传输方向

DMA一共有3种传输方向,分别是存储器到存储器,存储器到外设和外设到存储器。这3种传输方向都需要配置源寄存器和目标寄存器。如果把DMA比作是一条公路,那么源寄存器就是起点,目标寄存器就是终点。
在这里插入图片描述

存储器到存储器

存储器到存储器可以理解成就是MCU内部数据之间的搬运。
在这里插入图片描述
在此模式下,我们需要将源寄存器设置为需要发送数据的存储器地址,目标寄存器设置为需要接收数据的存储器地址。要注意的是使用存储器到存储器模式时,不允许循环模式和直接模式。只有 DMA2 控制器能够执行存储器到存储器的传输。

存储器到外设

存储器到外设一般用于将存储器的数据发送到外设,外设可以是串口的发送寄存器,SPI的发送寄存器等。
在这里插入图片描述
在此模式下,我们需要将源寄存器设置为需要发送数据的存储器地址,目标寄存器设置为需要接收数据的外设地址。

外设到存储器

外设到存储器一般用于将外设的数据发送到存储器,外设可以是串口的接收寄存器,SPI的接收寄存器等。
在这里插入图片描述
在此模式下,我们需要将源寄存器设置为需要发送数据的外设地址,目标寄存器设置为需要接收数据的存储器地址。

DMA循环模式和普通模式

循环模式(Circular)

  • 在循环模式下,DMA的传输是循环进行,当DMA完成一次传输后会自动进入下一次传输,形成一个循环。
  • 在循环模式下,DMA把源数据寄存器中的数据搬到目标数据寄存器,当传输条件完成后,自动进行下一次传输
  • 循环模式适用于那些周期性数据传输的场景,比如ADC的采集,音视频数据流的传输等。

普通模式(Normal)

  • 在普通模式下,DMA的传输只会执行一次,当DMA完成一次传输后就会停止。
  • 在普通模式下,DMA会把源数据寄存器中的数据搬到目标数据寄存器,当传输条件完成后,就会停止。
  • 普通模式适用于那些只传输一次数据的场景,比如初始化传感器等。

DMA源、目标寄存器增量模式

DMA的源和目标寄存器有两个增量模式,分别是递增和固定

  • 递增模式:每一次数据传输之后,其地址指针会递增(增量为用户设置的数据大小)
  • 固定模式::每一次数据传输之后,其地址指针不变。

DMA例程

存储器到存储器例程

dma.c

void bsp_dma_u8_init(DMA_Stream_TypeDef* DMAy_Streamx,u32 dam_channel,u8 *src,u8 *dst,u32 len)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_DMA1,ENABLE);   DMA_InitStructure.DMA_Channel = dam_channel;                                           //通道选择DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)src;                                //DMA外设地址DMA_InitStructure.DMA_Memory0BaseAddr = (u32)dst;                                   //DMA 存储器0地址DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;                        //存储器到外设模式DMA_InitStructure.DMA_BufferSize = len;                                       //数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;               //外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                        //存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;    //外设数据长度:8位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;            //存储器数据长度:8位DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                //使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High;                          //中等优先级DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;       // 使能FIFODMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; // FIFO阀值满DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_INC4;                    //DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC4;            //DMA_Init(DMAy_Streamx, &DMA_InitStructure);    while (DMA_GetCmdStatus(DMAy_Streamx) != DISABLE){}DMA_Cmd(DMAy_Streamx, ENABLE);}void bsp_dma_u16_init(DMA_Stream_TypeDef* DMAy_Streamx,u32 dam_channel,u16 *src,u16 *dst,u32 len)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_DMA1,ENABLE);   DMA_InitStructure.DMA_Channel = dam_channel;                                           //通道选择DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)src;                                //DMA外设地址DMA_InitStructure.DMA_Memory0BaseAddr = (u32)dst;                                   //DMA 存储器0地址DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;                        //存储器到外设模式DMA_InitStructure.DMA_BufferSize = len;                                       //数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;               //外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                        //存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;    //外设数据长度:16位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;            //存储器数据长度:16位DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                //使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High;                          //中等优先级DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;       // 使能FIFODMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO阀值满DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_INC8;                    //存储器突发单次传输DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC8;            //外设突发单次传输DMA_Init(DMAy_Streamx, &DMA_InitStructure);    while (DMA_GetCmdStatus(DMAy_Streamx) != DISABLE){}DMA_Cmd(DMAy_Streamx, ENABLE);
}void bsp_dma_u32_init(DMA_Stream_TypeDef* DMAy_Streamx,u32 dam_channel,u32 *src,u32 *dst,u32 len)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_DMA1,ENABLE);   DMA_InitStructure.DMA_Channel = dam_channel;                                           //通道选择DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)src;                                //DMA外设地址DMA_InitStructure.DMA_Memory0BaseAddr = (u32)dst;                                   //DMA 存储器0地址DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;                        //存储器到外设模式DMA_InitStructure.DMA_BufferSize = len;                                       //数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;               //外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                        //存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;    //外设数据长度:32位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;            //存储器数据长度:16位DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                //使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High;                          //中等优先级DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;      // 使能FIFODMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO阀值满DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_INC4;                    //存储器突发单次传输DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC4;            //外设突发单次传输DMA_Init(DMAy_Streamx, &DMA_InitStructure);    while (DMA_GetCmdStatus(DMAy_Streamx) != DISABLE){}DMA_Cmd(DMAy_Streamx, ENABLE);
}ErrorStatus compare_u8_buff(u8 *src,u8 *dst,u32 len)
{int i;for(i = 0;i < len;i++){if(src[i] != dst[i])return ERROR;}return SUCCESS;
}ErrorStatus compare_u16_buff(u16 *src,u16 *dst,u32 len)
{int i;for(i = 0;i < len;i++){if(src[i] != dst[i])return ERROR;}return SUCCESS;
}ErrorStatus compare_u32_buff(u32 *src,u32 *dst,u32 len)
{int i;for(i = 0;i < len;i++){if(src[i] != dst[i])return ERROR;}return SUCCESS;
}

dma_tset_data.h

u8 src_u8_buff[] =
{
//随机数个数:100;每行显示个数:10;取值范围:0到255148,	119,	56,	38,	124,	10,	219,	102,	125,	5,	
53,	188,	144,	202,	43,	70,	236,	3,	212,	145,	
125,	65,	139,	92,	146,	227,	66,	158,	214,	58,	
25,	202,	129,	235,	47,	81,	207,	148,	183,	104,	
178,	178,	79,	13,	195,	194,	142,	68,	127,	166,	
220,	167,	176,	75,	135,	29,	41,	137,	19,	234,	
182,	74,	209,	232,	83,	239,	179,	19,	156,	239,	
94,	75,	133,	193,	129,	86,	75,	253,	101,	37,	
171,	190,	198,	88,	81,	74,	226,	4,	147,	245,	
207,	179,	95,	66,	17,	172,	114,	76,	108,	117,
};u16 src_u16_buff[] =
{//随机数个数:100;每行显示个数:10;取值范围:256到6553522787,	5224,	1902,	6448,	21531,	32946,	6422,	4832,	17488,	23689,	
13175,	8272,	21386,	8319,	18916,	4179,	23261,	12460,	8750,	31562,	
5051,	6408,	12149,	675,	26984,	26904,	3901,	10656,	32431,	29389,	
23748,	11098,	15316,	28841,	20116,	29189,	30921,	22307,	21076,	4981,	
6052,	21837,	6883,	30433,	9041,	18612,	21672,	2024,	18052,	10375,	
29731,	6865,	12018,	11260,	21192,	26135,	12481,	12272,	3598,	13605,	
15933,	8142,	23747,	14430,	1996,	26033,	20130,	5728,	22564,	9677,	
10791,	8842,	32955,	18699,	23714,	23740,	27263,	6899,	12427,	7773,	
7880,	32463,	15893,	6307,	23691,	32781,	18949,	2062,	3536,	16870,	
1733,	16694,	12646,	4669,	27572,	31431,	3285,	9162,	5825,	30922,	
};u32 src_u32_buff[] =
{//随机数个数:100;每行显示个数:10;取值范围:65535到10000088361,	75899,	94546,	97752,	82130,	75054,	97136,	68892,	98163,	83019,	
76892,	94669,	66625,	86400,	70941,	89254,	86425,	83976,	97555,	68700,	
77042,	70351,	78217,	77532,	68369,	71639,	95113,	92116,	68732,	70362,	
96244,	73846,	73264,	66353,	77266,	66681,	70294,	78068,	81638,	86112,	
83636,	68963,	97925,	87294,	75680,	94852,	80553,	87098,	76859,	67086,	
74562,	72464,	71451,	82822,	80844,	97496,	70857,	94632,	68390,	70603,	
68141,	67336,	97698,	92867,	75590,	82425,	77145,	95298,	70175,	96174,	
66753,	94902,	81406,	89759,	94081,	79355,	82654,	88110,	68995,	86562,	
84255,	97675,	72779,	78705,	89469,	95189,	68232,	78762,	82239,	84866,	
92649,	70801,	68272,	93952,	78642,	66472,	76243,	91292,	69775,	68413,	
};

main.c


u8 dst_u8_buff[sizeof(src_u8_buff)/ sizeof(src_u8_buff[0])];
u16 dst_u16_buff[sizeof(src_u16_buff) / sizeof(src_u16_buff[0])];
u32 dst_u32_buff[sizeof(src_u32_buff) / sizeof(src_u32_buff[0])];
int main(void)
{int i;NVIC_PriorityGroupConfig(2);system_tick_init();bsp_usart_init(115200);bsp_dma_u8_init(DMA2_Stream0,DMA_Channel_0,src_u8_buff,dst_u8_buff,sizeof(src_u8_buff)/ sizeof(src_u8_buff[0]));while(DMA_GetFlagStatus(DMA2_Stream0,DMA_FLAG_TCIF0) != SET);if(compare_u8_buff(src_u8_buff,dst_u8_buff,sizeof(src_u8_buff)/ sizeof(src_u8_buff[0])) == SUCCESS)printf("u8 success\r\n");elseprintf("u8 error\r\n");bsp_dma_u16_init(DMA2_Stream1,DMA_Channel_1,src_u16_buff,dst_u16_buff,sizeof(src_u16_buff)/ sizeof(src_u16_buff[0]));while(DMA_GetFlagStatus(DMA2_Stream1,DMA_FLAG_TCIF1) != SET);if(compare_u16_buff(src_u16_buff,dst_u16_buff,sizeof(src_u16_buff)/ sizeof(src_u16_buff[0])) == SUCCESS)printf("u16 success\r\n");elseprintf("u16 error\r\n");bsp_dma_u32_init(DMA2_Stream2,DMA_Channel_2,src_u32_buff,dst_u32_buff,sizeof(src_u32_buff)/ sizeof(src_u32_buff[0]));while(DMA_GetFlagStatus(DMA2_Stream2,DMA_FLAG_TCIF2) != SET);if(compare_u32_buff(src_u32_buff,dst_u32_buff,sizeof(src_u32_buff)/ sizeof(src_u32_buff[0])) == SUCCESS)printf("u32 success\r\n");elseprintf("u32 error\r\n");while(1){delay_ms(1000);}}

存储器到外设例程


void bsp_dma_init(DMA_Stream_TypeDef* DMAy_Streamx,u32 dam_channel,u32 src,u32 dst,u32 len)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_DMA1,ENABLE);   DMA_InitStructure.DMA_Channel = dam_channel;                                           //通道选择DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)dst;                                //DMA外设地址DMA_InitStructure.DMA_Memory0BaseAddr = (u32)src;                                   //DMA 存储器0地址DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                        //存储器到外设模式DMA_InitStructure.DMA_BufferSize = len;                                       //数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;               //外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                        //存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;    //外设数据长度:8位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;            //存储器数据长度:8位DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                //使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High;                          //中等优先级DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;      DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;                    //存储器突发单次传输DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;            //外设突发单次传输DMA_Init(DMAy_Streamx, &DMA_InitStructure);    while (DMA_GetCmdStatus(DMAy_Streamx) != DISABLE){}DMA_Cmd(DMAy_Streamx, ENABLE);}const u8 text_to_send[]="HuJeZC2ERQ6UIRIJMCa0";	 
#define SEND_BUF_SIZE ((sizeof(text_to_send) + 2) * 1000)	//发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.
u8 string_buff[SEND_BUF_SIZE];
int main(void)
{int i,j,t,mask = 0;NVIC_PriorityGroupConfig(2);system_tick_init();bsp_usart_init(115200);j=sizeof(text_to_send);	   for(i=0;i<SEND_BUF_SIZE;i++)//填充ASCII字符集数据{if(t>=j)//加入换行符{if(mask){string_buff[i]=0x0a;t=0;}else {string_buff[i]=0x0d;mask++;}	}else//复制TEXT_TO_SEND语句{mask=0;string_buff[i]=text_to_send[t];t++;}   	   }		 bsp_dma_init(DMA2_Stream7,DMA_Channel_4,(u32)string_buff,(u32)&USART1->DR,(sizeof(string_buff) / sizeof(string_buff[0])));USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);while(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7) != SET);while(1){delay_ms(1000);}}

外设到存储器例程

#define   NUM      10000        //采集次数extern u16 adc_value[2];
extern u16 translate_end ;
void ADC_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStruct;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA,&GPIO_InitStruct);}void DMA_ADC_Config(void)
{ADC_CommonInitTypeDef ADC_CommonInitStruct,ADC_CommonInitStructure;ADC_InitTypeDef ADC_InitStruct,ADC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;                       //独立模式ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;  //两个采样阶段之间的延迟x个时钟ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_1;               //DMA使能(DMA传输下要设置使能)ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4;                    //预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz ADC_CommonInit(&ADC_CommonInitStruct);                                      //初始化 ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;                         //12位模式ADC_InitStruct.ADC_ScanConvMode =ENABLE;                                    //扫描(开启DMA传输要设置扫描)ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;                             //开启连续转换(开启DMA传输要设置连续转换)ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;    //禁止触发检测,使用软件触发ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;                         //右对齐	ADC_InitStruct.ADC_NbrOfConversion = 3;                                    //有几个通道传输就写几 (DMA传输下要设置为通道数)ADC_Init(ADC1, &ADC_InitStruct);                                            //ADC初始化ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1,  ADC_SampleTime_480Cycles);   ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2,  ADC_SampleTime_480Cycles);   ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 3,  ADC_SampleTime_480Cycles);   ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);ADC_DMACmd(ADC1, ENABLE);ADC_Cmd(ADC1, ENABLE); }void DMA_Config(void)
{DMA_InitTypeDef DMA_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);   DMA_InitStructure.DMA_Channel = DMA_Channel_0;                                           //通道选择DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;                                //DMA外设地址DMA_InitStructure.DMA_Memory0BaseAddr = (u32)adc_value;                                   //DMA 存储器0地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;                        //存储器到外设模式DMA_InitStructure.DMA_BufferSize = 3;                                       //数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;               //外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                        //存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;    //外设数据长度:16位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;            //存储器数据长度:16位DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                //使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High;                          //中等优先级DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;      DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;                    //存储器突发单次传输DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;            //外设突发单次传输DMA_Init(DMA2_Stream0, &DMA_InitStructure);        DMA_ClearITPendingBit(DMA2_Stream0,DMA_IT_TCIF0);//DMA_ClearFlag(DMA2_Stream0,DMA_IT_TC);DMA_ITConfig(DMA2_Stream0,DMA_IT_TC,ENABLE);NVIC_InitStructure.NVIC_IRQChannel=DMA2_Stream0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;                     //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01;                            //响应优先级NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_Init(&NVIC_InitStructure);while (DMA_GetCmdStatus(DMA2_Stream0) != DISABLE){}DMA_Cmd(DMA2_Stream0, ENABLE);}void DMA2_Stream0_IRQHandler(void)
{if(DMA_GetITStatus(DMA2_Stream0,DMA_IT_TCIF0) == SET){translate_end = 1;DMA_ClearITPendingBit(DMA2_Stream0,DMA_IT_TCIF0);}
}u16 adc_value[3];
u32 adc_channel1_average_value = 0,adc_channel2_average_value = 0,adc_channel3_average_value = 0;
u16 translate_end = 0;
int main(void)
{int count = 0;float adc_temp_value1,adc_temp_value2,adc_temp_value3;NVIC_PriorityGroupConfig(2);system_tick_init();bsp_usart_init(115200);ADC_GPIO_Config();DMA_ADC_Config();DMA_Config();ADC_SoftwareStartConv(ADC1);   while(1){if(translate_end){if(count <= NUM){adc_channel1_average_value += adc_value[0];adc_channel2_average_value += adc_value[1];adc_channel3_average_value += adc_value[2];count++;}else{count = 0;adc_temp_value1 = (float)(adc_channel1_average_value / NUM) * 3.3 / 4096;adc_temp_value2 = (float)(adc_channel2_average_value / NUM) * 3.3 / 4096;adc_temp_value3 = (float)(adc_channel3_average_value / NUM) * 3.3 / 4096;printf("ch1 %f ch2 %f ch3 %f\r\n", adc_temp_value1,adc_temp_value2,adc_temp_value3);adc_channel1_average_value = 0;adc_channel2_average_value = 0;adc_channel3_average_value = 0;}translate_end = 0;}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/129270.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React【组件生命周期 、组件生命周期_挂载、 组件生命周期_更新 、组件生命周期_卸载、表单_受控组件、表单_受控组件处理多个输入】(三)

文章目录 组件生命周期 组件生命周期_挂载 组件生命周期_更新 组件生命周期_卸载 表单_受控组件 表单_受控组件处理多个输入 组件生命周期 每个组件都有自己的生命周期&#xff0c;从“生”到”死“。 在这个过程当中&#xff0c;它会有不同的状态&#xff0c;针对不同的状态…

大型语言模型的幻觉研究|减轻及避免大模型LLM幻觉(二)

“ 本文及上一篇综述了最近关于语言模型中幻觉问题的研究进展&#xff0c;主要集中在ChatGPT发布后的研究。文章讨论了如何评估、追踪和消除幻觉&#xff0c;并探讨了现有挑战和未来方向。希望本文能为对LLM幻觉问题感兴趣的朋友提供有价值的资源&#xff0c;促进LLM的实际应用…

【数学建模竞赛】超详细Matlab二维三维图形绘制

二维图像绘制 绘制曲线图 g 是表示绿色 b--o是表示蓝色/虚线/o标记 c*是表示蓝绿色(cyan)/*标记 ‘MakerIndices,1:5:length(y) 每五个点取点&#xff08;设置标记密度&#xff09; 特殊符号的输入 序号 需求 函数字符结构 示例 1 上角标 ^{ } title( $ a…

Aztec的隐私抽象:在尊重EVM合约开发习惯的情况下实现智能合约隐私

1. 引言 Aztec的架构&#xff0c;不同于当前“通过EVM兼容执行环境”所实现的区块链水平扩容趋势。Aztec内部笑称其构建的为首个非zkEVM协议。 Aztec专注于实现&#xff1a; 成为理解和需要智能合约隐私的开发者的终极解决方案。 Aztec为开发者提供构建隐私优先app所需的网…

Java 复习笔记 - 面向对象进阶篇

文章目录 一&#xff0c;Static&#xff08;一&#xff09;Static的概述&#xff08;二&#xff09;静态变量&#xff08;三&#xff09;静态方法&#xff08;四&#xff09;工具类&#xff08;五&#xff09;static的注意事项 二&#xff0c;继承&#xff08;一&#xff09;继…

TortoiseSVN 详细操作指南

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 热爱技术的小郑 1、引言 考虑以下几种情况&#xff1a; 你是否在一个…

Nginx详解 五:反向代理

文章目录 1. 正向代理和反向代理1.1 正向代理概述1.1.1 什么是正向代理1.1.2 正向代理的作用1.1.3 正向代理的基本格式 1.2 反向代理概述1.2.1 什么是反向代理1.2.2 反向代理可实现的功能1.2.3 反向代理的可用模块 2. 配置反向代理2.1 反向代理配置参数2.1.1 proxy_pass2.1.2 其…

华为云云耀云服务器L实例评测|老用户回归的初印象

华为云云耀云服务器L实例评测&#xff5c;老用户回归的初印象 前言一、新面孔1. 云耀云服务器2. 服务器特色 二、上手感官体验1. 性价比感受2. 推荐宝塔面板3. CloudShell登录4. 安全性 总结 前言 其实笔者接触华为云已经很久了&#xff0c;第一次使用的云服务器就是华为云。当…

C# 反射机制

图片来自&#xff1a;https://www.cnblogs.com/tangge/p/3440605.html

探索多态的本质【C++】

文章目录 多态的构成条件虚函数虚函数的重写&#xff08;覆盖&#xff09; 虚函数重写的两个例外C11 override和final区分重载、覆盖(重写)、隐藏(重定义)抽象类接口继承和实现继承多态的原理虚函数表 动态绑定和静态绑定动态绑定静态绑定 单继承中的虚函数表多继承中的虚函数表…

视频监控平台EasyCVR分组批量绑定/取消通道功能的后端代码设计逻辑介绍

视频监控平台/视频存储/视频分析平台EasyCVR基于云边端一体化管理&#xff0c;可支持视频实时监控、云端录像、云存储、磁盘阵列存储、回放与检索、智能告警、平台级联等功能。安防监控平台在线下场景中应用广泛&#xff0c;包括智慧工地、智慧工厂、智慧校园、智慧社区等等。 …

Ubuntu20.04同时安装ROS1和ROS2

Ubuntu20.04同时安装ROS1和ROS2 Excerpt 每版的Ubuntu系统版本都有与之对应ROS版本的&#xff0c;每一版ROS都有其对应版本的Ubuntu版本&#xff0c;不可随便装&#xff0c;ubuntu20.04对应ROS1 noetic和ROS2 foxy版本。_ros1和ros2共存 Ubuntu20.04同时安装ROS1和ROS2共存 文…

Vue+NodeJS+MongoDB实现邮箱验证注册、登录

一.主要内容 邮件发送用户注册用户信息存储到数据库用户登录密码加密JWT生成tokenCookie实现快速登录 在用户注册时,先发送邮件得到验证码.后端将验证进行缓存比对,如果验证码到期,比对不正确,拒绝登录;如果比对正确,将用户的信息进行加密存储到数据库. 用户登录时,先通过用…

[H5动画制作系列] Sprite及Text Demo

参考代码: sprite.js: var canvas, stage, container; canvas document.getElementById("mainView"); function init() {stage new createjs.Stage(canvas);createjs.Touch.enable(stage);var loader new createjs.LoadQueue(false);loader.addEventListener(&q…

Docker的开源容器镜像仓库Harbor安装

概述 Docker Hub是Docker官方提供的在线Docker镜像注册中心&#xff0c;其支持Docker镜像的查询&#xff08;search&#xff09;、提交&#xff08;push&#xff09;以及获取&#xff08;pull&#xff09;。目前&#xff0c;在云原生领域中&#xff0c;CNCF提供Harbor开源版本…

创建开机自启的脚本

在启动许多ros节点时有多种方式&#xff0c;我推荐使用launch来启动所有的节点&#xff0c;这也是一种规范的方式。以后会慢慢向这个方向靠。 除此之外还可以通过创建的脚本来启动&#xff1a; 脚本位置不限&#xff0c;只需要&#xff1a; sudo gedit xxx.sh在里面添加相应的…

UI设计师的发展前景是否超越了平面设计?

这是一个现代经济学的典型话题&#xff1a;应该跟随趋势追逐风口&#xff0c;还是坚守成熟的“夕阳产业” UI 设计行业发展短短不过 20 多年&#xff0c;但平面设计这个“夕阳产业”最早可以追溯到上世纪的二三十年代。显而易见的答案是&#xff0c;更新兴的 UI 设计师得到的好…

C语言_指针(1)

文章目录 前言一、指针数组1.1利用指针数组模拟出二维数组 二、数组指针2.1数组名是数组首元素的地址2.2 二维数组传参2.3 一级指针传参2.4 二级指针传参 三. 函数指针四 . typedef 重命名 前言 指针数组是由指针组成的数组。它的每个元素都是一个指针&#xff0c;可以指向任何…

java并发编程 ConcurrentLinkedQueue详解

文章目录 1 ConcurrentLinkedQueue是什么2 核心属性详解3 核心方法详解3.1 add(E e)3.2 offer(E e)3.3 poll()3.4 size()3.5 并发情况分析 4 总结 1 ConcurrentLinkedQueue是什么 ConcurrentLinkedQueue是一个无界的并发队列&#xff0c;和LinkedBlockingQueue相比&#xff0c…

对象临时中间状态的条件竞争覆盖

Portswigger练兵场之条件竞争 &#x1f984;条件竞争之对象临时中间状态的条件竞争 Lab: Partial construction race conditions&#x1f680;实验前置必要知识点 某些框架尝试通过使用某种形式的请求锁定来防止意外的数据损坏。例如&#xff0c;PHP 的本机会话处理程序模块…