当前位置: 首页 > news >正文

STM32笔记—DMA

目录

一、DMA简介

二、DMA主要特性

 三、DMA框图

3.1 DMA处理

 3.2 仲裁器

 3.3 DMA通道

扩展:

 断言:

枚举:

 3.4 可编程的数据传输宽度、对齐方式和数据大小端

 3.5 DMA请求映像

四、DMA基本结构

4.1 DMA_Init配置

4.2 实现DMA+ADC扫描模式

实现要求

存储器映射

  ADC_DR寄存器

 代码

        4.2.1 配置时钟

        4.2.2 GPIO初始化

        4.2.3 ADC配置(ADC连续模式)

         4.2.4 DMA初始化(DMA循环模式)

         4.2.5 使能与校准

         4.2.6 ADC触发

有一定的区别:


一、DMA简介

        直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。 两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

二、DMA主要特性

        12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道;每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置;在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低)优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) ; 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程;源和目标地址必须按数据传输宽度对齐;每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。

 三、DMA框图

        没有DMA

        1.如果没有DMA,CPU传输数据还要以内核作为中转站,比如要将ADC采集的数据转移到到SRAM中,这个过程是这样的:

        内核通过DCode经过总线矩阵协调,从获取AHB存储的外设ADC采集的数据,

        然后内核再通过DCode经过总线矩阵协调把数据存放到内存SRAM中。   

        有DMA的话,

        DMA传输时外设对DMA控制器发出请求。

        DMA控制器收到请求,触发DMA工作。

        DMA控制器从AHB外设获取ADC采集的数据,存储到DMA通道中

        DMA控制器的DMA总线与总线矩阵协调,使用AHB把外设ADC采集的数据经由DMA通道存放到SRAM中,这个数据的传输过程中,完全不需要内核的参与,也就是不需要CPU的参与。

        在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。

3.1 DMA处理

        每次DMA传送由3个操作组成: ● 从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。 ● 存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。 ● 执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目。

        DMA通道x外设地址寄存器,DMA_CPARx---外设数据寄存器的基地址,作为数据传输的源或目标。DMA通道x存储器地址寄存器,DMA_CMARx---存储器地址存储器地址作为数据传输的源或目标。DMA通道x传输数量寄存器,DMA_CNDTRx---数据传输数量 (Number of data to transfer)

 3.2 仲裁器

        仲裁器根据通道请求的优先级来启动外设/存储器的访问。 优先权管理分2个阶段: ● 软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级: ─ 最高优先级 ─ 高优先级 ─ 中等优先级 ─ 低优先级 ● 硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。举个例子,通道2优先于通道4。 注意: 在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2控制器的优先级

 3.3 DMA通道

        每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输。DMA传输的数据量是可编程的,最大达到65535。包含要传输的数据项数量的寄存器,在每次传输后递减。通道配置过程下面是配置DMA通道x的过程(x代表通道号):

         1. 在DMA_CPARx寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地址将是数据传输的源或目标。

        2. 在DMA_CMARx寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数据将从这个地址读出或写入这个地址。

        3. 在DMA_CNDTRx寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。

         4. 在DMA_CCRx寄存器的PL[1:0]位中设置通道的优先级。

         5. 在DMA_CCRx寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外设和存储器的数据宽度、传输一半产生中断或传输完成产生中断。

        循环模式循环模式用于处理循环缓冲区和连续的数据传输(如ADC的扫描模式)。在DMA_CCRx寄存器中的CIRC位用于开启这一功能。当启动了循环模式,数据传输的数目变为0时,将会自动地被恢复成配置通道时设置的初值,DMA操作将会继续进行。

下文详细介绍了两者区别:

    【精选】STM32 DMA 循环模式DMA_Mode_Circular详解_dma循环模式和正常模式-CSDN博客

        DMA通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式。 当设置了DMA_CCRx寄存器中的MEM2MEM位之后,在软件设置了DMA_CCRx寄存器中的EN位启动DMA通道时,DMA传输将马上开始。当DMA_CNDTRx寄存器变为0时,DMA传输结束。存储器到存储器模式不能与循环模式同时使用。初始化代码如下所示:

/*DMA初始化*/DMA_InitTypeDef DMA_InitStructure;										//定义结构体变量DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设基地址,给定形参AddrADMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度,选择字节DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//外设地址自增,选择使能DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;							//存储器基地址,给定形参AddrBDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据宽度,选择字节DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器地址自增,选择使能DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;						//数据传输方向,选择由外设到存储器DMA_InitStructure.DMA_BufferSize = Size;								//转运的数据大小(转运次数)DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//模式,选择正常模式DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;								//存储器到存储器,选择使能DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//优先级,选择中等DMA_Init(DMA1_Channel1, &DMA_InitStructure);							//将结构体变量交给DMA_Init,配置DMA1的通道1

        方法1:DMA_Mode_Normal,正常模式,当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次,当通道配置为非循环模式时,传输结束后(即传输计数变为0)将不再产生DMA操作。要开始新的DMA传输,需要在关闭DMA通道的情况下,在DMA_CNDTRx寄存器中重新写入传输数目。配置代码如下: 

/*** 函    数:启动DMA数据转运* 参    数:无* 返 回 值:无*/
void MyDMA_Transfer(void)
{DMA_Cmd(DMA1_Channel1, DISABLE);					//DMA失能,在写入传输计数器之前,需要DMA暂停工作DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);	//写入传输计数器,指定将要转运的次数DMA_Cmd(DMA1_Channel1, ENABLE);						//DMA使能,开始工作while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	//等待DMA工作完成DMA_ClearFlag(DMA1_FLAG_TC1);						//清除工作完成标志位
}
扩展:

DMA_GetFlagStatus()函数详解:

1. 这一段就是说DMA1还是DMA2空闲。

//注意: #define FLAG_Mask       ((uint32_t)0x10000000)
//相与:同1为1,其余为0

DMA1和DMA2这一位有区别,如下:这样可以简单的判断执行哪一句。

2.  这一段就是枚举与断言

 断言:

  函数作用:

枚举:
typedef enum //此处的day可以省略
{saturday,sunday = 0,monday,tuesday,wednesday,thursday,friday
} workday; // 此处的 workday 为枚举型 enum day 的别名workday today, tomorrow; // 变量 today 和 tomorrow 的类型为枚举型 workday,即 enum day

typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus; 
这句话意思就是给enum {RESET = 0, SET = !RESET}起了别名:FlagStatus和ITStatus 

 3. 底层源码

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
{FlagStatus bitstatus = RESET;uint32_t tmpreg = 0;/* Check the parameters */assert_param(IS_DMA_GET_FLAG(DMAy_FLAG));/* Calculate the used DMAy */if ((DMAy_FLAG & FLAG_Mask) != (uint32_t)RESET){/* Get DMA2 ISR register value */tmpreg = DMA2->ISR ;}else{/* Get DMA1 ISR register value */tmpreg = DMA1->ISR ;}/* Check the status of the specified DMAy flag */if ((tmpreg & DMAy_FLAG) != (uint32_t)RESET){/* DMAy_FLAG is set */bitstatus = SET;}else{/* DMAy_FLAG is reset */bitstatus = RESET;}/* Return the DMAy_FLAG status */return  bitstatus;
}

        方法2:DMA_Mode_Circular ,循环传输模式,当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。也就是多次传输模式。在循环模式下,最后一次传输结束时,DMA_CNDTRx寄存器的内容会自动地被重新加载为其初始数值,内部的当前外设/存储器地址寄存器也被重新加载为DMA_CPARx/DMA_CMARx寄存器设定的初始基地址。  

        6. 设置DMA_CCRx寄存器的ENABLE位,启动该通道。 一旦启动了DMA通道,它既可响应连到该通道上的外设的DMA请求。

         一旦启动了DMA通道,它既可响应连到该通道上的外设的DMA请求。

         当传输一半的数据后,半传输标志(HTIF)被置1,当设置了允许半传输中断位(HTIE)时,将产生一个中断请求。

        在数据传输结束后,传输完成标志(TCIF)被置1,当设置了允许传输完成中断位(TCIE)时,将产生一个中断请求。

 3.4 可编程的数据传输宽度、对齐方式和数据大小端

        每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输。DMA传输的数据量是可编程的,最大达到65535。包含要传输的数据项数量的寄存器,在每次传输后递减。

 3.5 DMA请求映像

        从外设(TIMx[x=1、2、3、4]、ADC1、SPI1、SPI/I2S2、I2Cx[x=1、2]和USARTx[x=1、2、3])产生的7个请求,通过逻辑或输入到DMA1控制器,这意味着同时只能有一个请求有效。参见下图的DMA1请求映像。 外设的DMA请求,可以通过设置相应外设寄存器中的控制位,被独立地开启或关闭。

四、DMA基本结构

4.1 DMA_Init配置

#include "stm32f10x.h"                  // Device headeruint16_t MyDMA_Size;					//定义全局变量,用于记住Init函数的Size,供Transfer函数使用/*** 函    数:DMA初始化* 参    数:AddrA 原数组的首地址* 参    数:AddrB 目的数组的首地址* 参    数:Size 转运的数据大小(转运次数)* 返 回 值:无*/
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{MyDMA_Size = Size;					//将Size写入到全局变量,记住参数Size/*开启时钟*/RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						//开启DMA的时钟/*DMA初始化*/DMA_InitTypeDef DMA_InitStructure;										//定义结构体变量DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设基地址,给定形参AddrADMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度,选择字节DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//外设地址自增,选择使能DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;							//存储器基地址,给定形参AddrBDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据宽度,选择字节DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器地址自增,选择使能DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;						//数据传输方向,选择由外设到存储器DMA_InitStructure.DMA_BufferSize = Size;								//转运的数据大小(转运次数)DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//模式,选择正常模式DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;								//存储器到存储器,选择使能DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//优先级,选择中等DMA_Init(DMA1_Channel1, &DMA_InitStructure);							//将结构体变量交给DMA_Init,配置DMA1的通道1/*DMA使能*/DMA_Cmd(DMA1_Channel1, DISABLE);	//这里先不给使能,初始化后不会立刻工作,等后续调用Transfer后,再开始
}/*** 函    数:启动DMA数据转运* 参    数:无* 返 回 值:无*/
void MyDMA_Transfer(void)
{DMA_Cmd(DMA1_Channel1, DISABLE);					//DMA失能,在写入传输计数器之前,需要DMA暂停工作DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);	//写入传输计数器,指定将要转运的次数DMA_Cmd(DMA1_Channel1, ENABLE);						//DMA使能,开始工作while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	//等待DMA工作完成DMA_ClearFlag(DMA1_FLAG_TC1);						//清除工作完成标志位
}

4.2 实现DMA+ADC扫描模式

实现要求

存储器映射

类型

起始地址

存储器

用途

ROM

0x0800 0000

程序存储器Flash

存储C语言编译后的程序代码,常量数据

0x1FFF F000

系统存储器

存储BootLoader,用于串口下载

0x1FFF F800

选项字节

存储一些独立于程序代码的配置参数

RAM

0x2000 0000

运行内存SRAM

存储运行过程中的临时变量

0x4000 0000

外设寄存器

存储各个外设的配置参数

0xE000 0000

内核外设寄存器

存储内核各个外设的配置参数(NVIC、systick)

        ADC将菜做好了,DMA负责将菜及时的交给顾客,厨师与服务员的关系!!!那服务员从哪里端菜了?查数据手册:第一句话就是在规则组下一次只能上一个菜,需要使用服务员及时的端菜。第二句话就是告诉我们服务员端菜的地方在ADC_DR寄存器。

        那么菜最终端给的顾客是谁?根据程序要求,我们这里的顾客是SRAM。

  ADC_DR寄存器
#define ADC1                ((ADC_TypeDef *) ADC1_BASE)
#define ADC1_BASE             (APB2PERIPH_BASE + 0x2400)

#define  ADC_DR_DATA                         ((uint32_t)0x0000FFFF)        /*!< Regular data */
#define  ADC_DR_ADC2DATA                     ((uint32_t)0xFFFF0000)        /*!< ADC2 data */

 

 代码
        4.2.1 配置时钟

                DMA1\ADC1\GPIOA

/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);		//开启DMA1的时钟/*设置ADC时钟*/RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
        4.2.2 GPIO初始化
/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0、PA1、PA2和PA3引脚初始化为模拟输入
        4.2.3 ADC配置(ADC连续模式)

            ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                        

              //连续转换,使能,每转换一次规则组序列后立刻开始下一次转换

/*规则组通道配置*/ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);	//规则组序列1的位置,配置为通道0ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);	//规则组序列2的位置,配置为通道1ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);	//规则组序列3的位置,配置为通道2ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);	//规则组序列4的位置,配置为通道3/*ADC初始化*/ADC_InitTypeDef ADC_InitStructure;											//定义结构体变量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;							//模式,选择独立模式,即单独使用ADC1ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;						//数据对齐,选择右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;			//外部触发,使用软件触发,不需要外部触发ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;							//连续转换,使能,每转换一次规则组序列后立刻开始下一次转换ADC_InitStructure.ADC_ScanConvMode = ENABLE;								//扫描模式,使能,扫描规则组的序列,扫描数量由ADC_NbrOfChannel确定ADC_InitStructure.ADC_NbrOfChannel = 4;										//通道数,为4,扫描规则组的前4个通道ADC_Init(ADC1, &ADC_InitStructure);											//将结构体变量交给ADC_Init,配置ADC1
         4.2.4 DMA初始化(DMA循环模式)

        循环模式用于处理循环缓冲区和连续的数据传输(如ADC的扫描模式)。

            DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                
                //模式,选择循环模式,与ADC的连续转换一致,这样ADC每次上菜,DMA马上拿走,一直循环

	/*DMA初始化*/DMA_InitTypeDef DMA_InitStructure;定义结构体变量DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;	外设基地址,给定形参AddrADMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
外设数据宽度,选择半字,对应16为的ADC数据寄存器DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;	外设地址自增,选择失能,始终以ADC数据寄存器为源DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;	存储器基地址,给定存放AD转换结果的全局数组AD_ValueDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;	存储器数据宽度,选择半字,与源数据宽度对应DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;				存储器地址自增,选择使能,每次转运后,数组移到下一个位置DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;					数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组DMA_InitStructure.DMA_BufferSize = 4;							转运的数据大小(转运次数),与ADC通道数一致DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;				模式,选择循环模式,与ADC的连续转换一致DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;				存储器到存储器,选择失能,数据由ADC外设触发转运到存储器DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;			优先级,选择中等DMA_Init(DMA1_Channel1, &DMA_InitStructure);					将结构体变量交给DMA_Init,配置DMA1的通道1

 注意:

        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;  //外设基地址,给定形参AddrA,如果不进行强制类型转换会报错-----类型为“volatile uint32_t *”的值不能赋值给类型为“uint32_t”的实体。   

        #define MAXTIME 1000,一个简单的MAXTIME就定义好了,它代表1000,如果在程序里面写

if(i<MAXTIME){.........},编译器在处理这个代码之前会对MAXTIME进行处理替换为1000。

        #define ADC1       ((ADC_TypeDef *) ADC1_BASE),一个ADC_TypeDef结构体指针类型的变量ADC1_BASE。

 // 访问数据操作如下:
data.age = 24;          // 结构体变量通过点运算符( . )访问
pdata->age = 24;        // 指向结构体的指针通过箭头运算符( -> )访问

typedef struct          // 定义一个结构体类型:DATA
{char key[10];       // 结构体成员:keychar name[20];      // 结构体成员:nameint age;            // 结构体成员:age
}DATA;DATA data;              // 声明一个结构体变量
DATA *pdata;            // 声明一个指向结构体的指针// 访问数据操作如下:
data.age = 24;          // 结构体变量通过点运算符( . )访问
pdata->age = 24;        // 指向结构体的指针通过箭头运算符( -> )访问

结构体指针的访问变量方法
1)p->结构体成员;
2)(*p).结构体成员;

p->num 的含义是:指针变量 p 所指向的结构体变量中的 num 成员。p->num 最终代表的就是 num 这个成员中的内容。

 

         4.2.5 使能与校准

  因为:    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                
               // 存储器到存储器,选择失能,数据由ADC外设触发转运到存储器,硬件触发

        由于DMA转运有三个条件:开关控制DMA_CMD必须使能,传输计数器必须大于0(当寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。),触发源必须有触发信号。

        前文提到对于DMA正常模式,当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次,当通道配置为非循环模式时,传输结束后(即传输计数变为0)将不再产生DMA操作。且ADC单次转换下,ADC只执行一次转换。相当于没有开启ADC到DMA的通道

所以这里需要开启ADC到DMA的输出,用到下图函数:

/*DMA和ADC使能*/DMA_Cmd(DMA1_Channel1, ENABLE);							//DMA1的通道1使能ADC_DMACmd(ADC1, ENABLE);								//ADC1触发DMA1的信号使能ADC_Cmd(ADC1, ENABLE);									//ADC1使能/*ADC校准*/ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);
         4.2.6 ADC触发
/*ADC触发*/ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//软件触发ADC开始工作,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作

(当不是ADC连续模式的时候,DMA也不是循环模式时)

有一定的区别:

        加上下代码即可:最大的区别就是需要不断的开启 ADC

        ADC_SoftwareStartConvCmd(ADC1,ENABLE);

        上文连续模式下,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作。

        1. 告诉DMA我做了ADC做了4个菜;2.ADC开始做菜;3.DMA取菜(一个个拿)DMA1_FLAG_TC1: DMA1 Channel1 transfer complete flag.

void AD_GetValue(void)
{DMA_Cmd(DMA1_Channel1,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel1,4);DMA_Cmd(DMA1_Channel1,ENABLE);ADC_SoftwareStartConvCmd(ADC1,ENABLE);while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);DMA_ClearFlag(DMA1_FLAG_TC1);
}

相关文章:

STM32笔记—DMA

目录 一、DMA简介 二、DMA主要特性 三、DMA框图 3.1 DMA处理 3.2 仲裁器 3.3 DMA通道 扩展: 断言&#xff1a; 枚举&#xff1a; 3.4 可编程的数据传输宽度、对齐方式和数据大小端 3.5 DMA请求映像 四、DMA基本结构 4.1 DMA_Init配置 4.2 实现DMAADC扫描模式 实现要求…...

机器学习概论

一、机器学习概述 1、机器学习与人工智能、深度学习的关系 人工智能&#xff1a;机器展现的人类智能机器学习&#xff1a;计算机利用已有的数据(经验)&#xff0c;得出了某种模型&#xff0c;并利用此模型预测未来的一种方法。深度学习&#xff1a;实现机器学习的一种技术 2…...

卡尔曼家族从零解剖-(04)贝叶斯滤波→细节讨论,逻辑梳理,批量优化

讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解的 卡尔曼家族从零解剖 链接 :卡尔曼家族从零解剖-(00)目录最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/133846882 文末正下方中心提供了本人 联系…...

小菜React

1、Unterminated regular expression literal, 对于函数就写.ts&#xff0c;有dom元素就写.tsx 2、 The requested module /src/components/setup.tsx?t1699255799463 does not provide an export named Father export default useStore默认导出的钩子&#xff0c;组件引入的…...

新手用mac电脑,对文件的疑问和gpt回应

macOs系统安装软件的疑问 所有问题mac系统文件结构我用mac安装软件&#xff0c;不用像windows一样创建文件夹吗只能安装到Applications文件夹吗安装程序的指南和提供的安装选项是什么软件安装在Applications下的/appName文件夹&#xff0c;它的所有数据都会在该文件夹吗如果卸载…...

LeetCode|动态规划|392. 判断子序列、115. 不同的子序列、 583. 两个字符串的删除操作

目录 一、392. 判断子序列 1.题目描述 2.解题思路 3.代码实现(双指针解法) 4.代码实现&#xff08;动态规划解法&#xff09; 二、115. 不同的子序列 1.题目描述 2.解题思路 3.代码实现&#xff08;C语言版本&#xff09; 4.代码实现&#xff08;C版本&#xff09; …...

vscode 阅读 android以及kernel 源码

在Ubuntu系统中安装vscode 参考文档&#xff1a; https://blog.csdn.net/m0_57368670/article/details/127184424 1, 下载vscode https://code.visualstudio.com 2, 安装vscode $ sudo dpkg -i code_1.78.1-1683194560_amd64.deb 3, 打开vscode $ code vscode 阅读 android…...

Intel oneAPI笔记(3)--jupyter官方文档(SYCL Program Structure)学习笔记

前言 本文是对jupyterlab中oneAPI_Essentials/02_SYCL_Program_Structure文档的学习记录&#xff0c;包含对Device Selector、Data Parallel Kernel、Host Accessor、Buffer Destruction、的介绍&#xff0c;最后还有一个小关于向量&#xff08;Vector&#xff09;加法的实例 …...

verilog——移位寄存器

在Verilog中&#xff0c;你可以使用移位寄存器来实现数据的移位操作。移位寄存器是一种常用的数字电路&#xff0c;用于将数据向左或向右移动一个或多个位置。这在数字信号处理、通信系统和其他应用中非常有用。以下是一个使用Verilog实现的简单移位寄存器的示例&#xff1a; m…...

C++11 多线程学习笔记

1. thread — 线程篇 所需头文件&#xff1a;<thread> 1.1 构造函数 // 1 默认构造函数 thread() noexcept; // 2 移动构造函数&#xff0c;把other的所有权转移给新的thread对象&#xff0c;之后 other 不再表示执行线程。 thread( thread&& other ) noex…...

nn.embedding函数详解(pytorch)

提示&#xff1a;文章附有源码&#xff01;&#xff01;&#xff01; 文章目录 前言一、nn.embedding函数解释二、nn.embedding函数使用方法四、模型训练与预测的权重变化探讨 前言 最近发现prompt工程(如sam模型)&#xff0c;也有transform的detr模型等都使用了nn.Embedding函…...

gitee.com[0: xxx.xx.xxx.xx]: errno=Unknown error

git在提交或拉取代码的时候&#xff0c;遇到以下报错信息&#xff1a; Unable to connect to gitee.com[0: xxx.xx.xxx.xx]: errnoUnknown error 解决问题步骤&#xff1a; 1、找到自己的电脑上的git用户配置文件 文件位置位于&#xff1a;C:\Users\用户名\.gitconfig 比如我…...

bug: https://aip.baidubce.com/oauth/2.0/token报错blocked by CORS policy

还是跟以前一样&#xff0c;我们先看报错点&#xff1a;&#xff08;注意小编这里是H5解决跨域的&#xff0c;不过解决跨域的原理都差不多&#xff09; Access to XMLHttpRequest at https://aip.baidubce.com/oauth/2.0/token from origin http://localhost:8000 has been blo…...

简单工厂VS工厂方法

工厂方法模式–制造细节无需知 前面介绍过简单工厂模式&#xff0c;简单工厂模式只是最基本的创建实例相关的设计模式。在真实情况下&#xff0c;有更多复杂的情况需要处理。简单工厂生成实例的类&#xff0c;知道了太多的细节&#xff0c;这就导致这个类很容易出现难维护、灵…...

使用VSCODE链接Anaconda

打代码还是在VSCODE里得劲 所以得想个办法在VSCODE里运行py文件 一开始在插件商店寻找插件 但是没有发现什么有效果的 幸运的是VSCODE支持自己选择Python的编译器 打开VSCODE 按住CtrlShiftP 输入Select Interpreter 如果电脑已经安装上了Python的环境 VSCODE会默认选择普通…...

Mysql数据库 9.SQL语言 查询语句 连接查询、子查询

连接查询 通过查询多张表&#xff0c;用连接查询进行多表联合查询 关键字&#xff1a;inner join 内连接 left join 左连接 right join 右连接 数据准备 创建新的数据库&#xff1a;create database 数据库名; create database db_test2; 使用数据库&#xff1a;use 数据…...

二叉树按二叉链表形式存储,试编写一个判别给定二叉树是否是完全二叉树的算法

完全二叉树&#xff1a;就是每层横着划过去是连起来的&#xff0c;中间不会断开 比如下面的左图就是完全二叉树 再比如下面的右图就是非完全二叉树 那我们可以采用层序遍历的方法&#xff0c;借助一个辅助队列 当辅助队列不空的时候&#xff0c;出队头元素&#xff0c;入队头…...

Android自定义控件

目录 Android自定义控件一、对现有控件进行扩展二、创建复合控件1 定义属性2 组合控件3 引用UI模板 三、重写View来实现全新控件1 弧线展示图1.1 具体步骤&#xff1a; 2 音频条形图2.1 具体步骤 四、补充&#xff1a;自定义ViewGroup Android自定义控件 ref: Android自定义控件…...

Java 中的 Cloneable 接口和深拷贝

引言&#xff1a; 在 Java 中&#xff0c;深拷贝是一种常见的需求&#xff0c;它可以创建一个对象的完全独立副本。Cloneable 接口提供了一种标记机制&#xff0c;用于指示一个类实例可以被复制。本文将详细介绍 Java 中的 Cloneable 接口和深拷贝的相关知识&#xff0…...

项目实战:通过axios加载水果库存系统的首页数据

1、创建静态页面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"style/index.css"><script src"script/axios.mi…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...