18:HAL--DMA

news2024/9/21 18:55:31

一:DMA

DMA的基本定义:
        DMA,全称Direct Memory Access,即直接存储器访问。


        DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。


        DMA 控制器和Cortex-M3核共享系统数据总线执行直接存储器数据传输。当CPU和DMA同时访问相同的目标(RAM或外设)时,DMA请求可能会停止 CPU访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线(存储器或外设)带宽。


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

1:简历

DMA(Direct Memory Access)直接存储器存取

DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源

12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)

每个通道都支持软件触发和特定的硬件触发  (每隔DMA通道的触发源不一样)

STM32F103C8T6 DMA资源:DMA1(7个通道)

外设(外设存储器)  :  一般是外设的数据寄存器DR , 比如ADC的数据寄存器、串口的数据寄存器等等

存储器  :  这里存储器,指的就是是运行内存SRAM和程序存储器Flash ,  是我们存储变量数组和程序代码的地方

外设和存储器的数据传输---------使用特定的硬件触发(每隔DMA通道的触发源不一样)

存储器和存储器之间的数据传输------软件触发

2:存储器映像

ROM :  只读存储器,是一种非易失性、掉电不丢失的存储器

RAM : 随机存储器,是一种易失性、掉电丢失的存储器

        因为CPU是32位的。所以导址范围就是32位的范围

3:DMA基本结构

下面的参数在配置DMA时使用

方向 : 决定了数据的方向    外设--->储存器 或者 储存器---->外设  或者 储存器---->储存器

        Flash是只读储存器,  所以在储存器--->储存器的传输方向的时候 :只能选择 Flash-------------->SRAM方向的传输

        X.DMA_DIR=指定外设站点(外设寄存器)为数据源还是目的地

传输计数器: 用来指定,我总共需要转运几次的,  这个传输计数器是一个自减计数器

        比如给它写一个5,   那DMA就只能进行5次数据转运 ,  转运过程中,每转运一次,计数器的数就会减1,   当传输计数器减到0之后,DMA就不会再进行数据转运了 ,  它减到0之后,之前自增的地址,也会恢复到起始地址的位置

        X.DMA_BufferSize=传输计数器

自动重装器 :  传输计数器减到0之后 , 是否要恢复到最初的值

        比如最初传输计数器给5,  如果不使用自动重装器,那转运5次后,DMA就结束了.   如果使用自动重装器,那转运5次,  计数器减到0后,就会立即重装到初始值5,  决定了转运的模式        

        不重装,就是正常的单次模式

        重装,就是循环模式

        X.DMA_Mode=自动重装器

起始地址 : 决定了数据从那里来到那里去的

因为stm32是32位的单片机,他的一个内存单元是32位的, 所以起始地址填入的都为32位

数据宽度 :  指定一次转运要按多大的数据宽度来进行

        字节Byte(uint8_t)、半字HalfWord(uint16_t)和字Word(uint32_t)

软件触发和循环模式,不能同时用

        软件触发 : 软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA,  早日把传输计数器清零,完成这一轮的转换

        软件触发就是尽快把传输计数器清零 .  循环模式是清零后自动重装 ,  如果同时用的话,那DMA就停不下来了

4: DMA转运的条件 

1: 开关控制,DMA _Cmd函数必须使能

2: 传输计数器必须大于0

3: 触发源,必须有触发信号

        触发一次,转运一次,传输计数器自减一次 ,  当传输计数器等于0,且没有自动重装时 ,   无论是否触发,DMA都不会再进行转运了,   此时就需要DMA_Cmd函数,给DISABLE,关闭DMA.   再为传输计数器写入一个大于0的数,  再DMA_Cmd,给ENABLE,开启DMA才可以正常工作.

        注意,写传输计数器时,必须要先关闭DMA,再进行,   不能在DMA开启时,写传输计数器

5:DMA请求

特定的硬件触发 : 每个通道的硬件触发源都是不同的 , 需要用ADC1来触发的话一那就必须选择通道1;   定时器2的更新事件来触发的话,那就必须选择通道2

        因为每个通道的硬件触发源都不同 ,  如果你想使用某个硬件触发源的话,  就必须使用它所在的通道

软件触发 :  使用软件触发的话,那通道就可以任意选择了.  每个通道的软件触发都是一样的

6:方向

DMA的传输方向:

X.Init.Direction=

外设:相对于DMA自己,以外的东西全部统一称呼为外设。(寄存器,另外一块STM32中的数据,其他外设的数据寄存器DR.)

DMA_MEMORY_TO_PERIPH(外设到存储区):这里的外设指的是STM32中的寄存器,存储区是在keil中写的数组。

uint8_t send_address[16]={32,11,22,12,45,12,12,45,53,85,94,78,52,65,32,165};

MDA通道6配置--发送数据
DMA_HandleTX.Instance=DMA1_Channel6;
传输方向:内存(数组)--->外设   把我们内存中的数组(send_address)
发送到外设中去 (寄存器) 
DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
我们send_address为uint8_t 一个字节的长度
DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
我们发送send_address为依次发送,所以存储区地址自增
DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增

DMA_HandleTX.Init.Mode=DMA_NORMAL;
外设(寄存器)的数据宽度
DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
我们发送的外设(寄存器)为一个地址所以不能自己增加
DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
__HAL_LINKDMA(&I2C_Handle,hdmatx,DMA_HandleTX);   //双向链接
//__HAL_LINKDMA(hi2c,hdmatx,DMA_HandleTX);   //双向链接
HAL_DMA_Init(&DMA_HandleTX);			
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);

DMA_PERIPH_TO_MEMORY(存储区到外设):这里的外设指的是STM32中的寄存器,存储区是在keil中写的数组。

//MDA通道7配置---接收数据
DMA_HandleRX.Instance=DMA1_Channel7;
//传输方向:外设  ---> 内存(数组)
DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
DMA_HandleRX.Init.Mode=DMA_NORMAL;
DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
__HAL_LINKDMA(&I2C_Handle,hdmarx,DMA_HandleRX);   //双向链接
//__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
HAL_DMA_Init(&DMA_HandleRX);			
HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);

DMA两种模式:
1、根据需要选择DAM模式:
(1)循环模式—DMA_Mode = DMA_Mode_Circular
(2)正常模式—DMA_Mode = DMA_Mode_Normal

  • 单次传输(Normal Mode):在这种模式下,DMA控制器在接收到传输请求后,会执行一次指定的数据传输任务,并在传输完成后停止。如果需要再次传输,必须重新配置并启动DMA。
  • 循环传输(Circular Mode):循环传输模式下,DMA控制器在完成一次传输后,会自动重新加载传输参数(如源地址、目标地址和传输长度),并继续执行下一次传输,直到被明确停止。这种模式特别适用于需要连续传输大量数据的应用场景。

7:寄存器

二:F407

A:介绍

STM32F407VET6 : DMA的资源有--DMA1,DMA2。

DMA1:挂载在APB1上面----42MHZ

DMA2:挂载在APB2上面----84MHZ

B:hal配置

f407hal的配置过程

__HAL_LINKDMA 是一个在STM32 HAL库中常用的宏定义,用于在硬件抽象层(HAL)中建立两个句柄(Handle)之间的链接。这种链接通常用于将DMA(直接内存访问)句柄与其对应的设备句柄(如UART、SPI等)关联起来,以便于管理和控制DMA传输。

这个宏定义的工作原理如下:

  • __HANDLE__:这是指向设备句柄的指针,比如UART句柄、SPI句柄等。这个句柄包含了控制该设备所需的所有配置和状态信息。
  • __PPP_DMA_FIELD__:这是一个成员变量的名称,该成员变量位于__HANDLE__指向的结构体中,用于存储指向DMA句柄的指针。这里的PPP是一个占位符,代表具体的外设(Peripheral),比如UART、SPI等。因此,在实际使用中,这个字段会根据外设的不同而有所变化,比如对于UART可能是hdmatx(用于发送的DMA句柄)或hdmarx(用于接收的DMA句柄)。
  • __DMA_HANDLE__:这是DMA句柄的名称,它包含了控制DMA传输所需的所有配置和状态信息。

三:HAL配置

下面为F1的配置过程

 下面为全部通用的

四:历程

下面全部为F407的历程

15:HAL----ADC模数转化器-CSDN博客

A:扫描模式+DMA转运

开启ADC的扫描模式,开启6个规则组的通道,使用DMA转运。每个ADC只有一个DR数据寄存器,使用开扫描模式必须使用DMA。(每获取一次通道中的值,使用DMA进行转运走,DR马上开始下一个通道的转化。)

#include "stm32f4xx.h"                  // Device header
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

ADC_HandleTypeDef g_adc_handle;   /* ADC句柄 */
DMA_HandleTypeDef g_dma_adc_handle;
ADC_ChannelConfTypeDef ADC_Channel[6];
/**
 * @brief       ADC初始化函数
 *   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
 *              我们使用12位精度, ADC采样时钟=21M, 转换时间为: 采样周期 + 12个ADC周期
 *              设置最大采样周期: 480, 则转换时间 = 492 个ADC周期 = 23.42us
 * @param       无
 * @retval      无
 */
 uint16_t ADC_DMA_data[36];
void adc_init(void)
{			
	
    g_adc_handle.Instance = ADC1;
    g_adc_handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;        /* 4分频,ADCCLK = PCLK2/4 = 84/4 = 21Mhz */
    g_adc_handle.Init.Resolution = ADC_RESOLUTION_12B;                      /* 12位模式 */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                      /* 右对齐 */
    g_adc_handle.Init.ScanConvMode = ENABLE;                               /* 打开扫描模式 */
    g_adc_handle.Init.ContinuousConvMode = ENABLE;                         	/* 打开连续转化 */
    g_adc_handle.Init.NbrOfConversion = 6;                                  /* 扫描模式下规则组的数量*/
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;                      /* 规则组间断模式 */
    g_adc_handle.Init.NbrOfDiscConversion = 0;                              /* 规则组间断模式下每次间断的通道数量 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                /* 软件触发 */
    g_adc_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* 使用软件触发 */
    g_adc_handle.Init.DMAContinuousRequests = ENABLE;                      /* 打开DMA请求 */
    HAL_ADC_Init(&g_adc_handle);                                            /* 初始化 */
		
	
		ADC_Channel[0].Channel=ADC_CHANNEL_0;     //通道编号
		ADC_Channel[0].Rank=1;     //排名
		ADC_Channel[0].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us

		//配置规则组通道
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[0]);
	 
		ADC_Channel[1].Channel=ADC_CHANNEL_1;     //通道编号
		ADC_Channel[1].Rank=2;     //排名
		ADC_Channel[1].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[1]);
		
		ADC_Channel[2].Channel=ADC_CHANNEL_2;     //通道编号
		ADC_Channel[2].Rank=3;     //排名
		ADC_Channel[2].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[2]);
			
		ADC_Channel[3].Channel=ADC_CHANNEL_3;     //通道编号
		ADC_Channel[3].Rank=4;     //排名
		ADC_Channel[3].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[3]);
		
		ADC_Channel[4].Channel=ADC_CHANNEL_4;     //通道编号
		ADC_Channel[4].Rank=5;     //排名
		ADC_Channel[4].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[4]);
		
		ADC_Channel[5].Channel=ADC_CHANNEL_5;     //通道编号
		ADC_Channel[5].Rank=6;     //排名
		ADC_Channel[5].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[5]);
		
		
		//发生36次转运,触发DMA中断,进入回调函数
		HAL_ADC_Start_DMA(&g_adc_handle,(uint32_t*)ADC_DMA_data,36);

}

/**
 * @brief       ADC底层驱动,引脚配置,时钟使能
                此函数会被HAL_ADC_Init()调用
 * @param       hadc:ADC句柄
 * @retval      无
 */
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC1)
    {
        GPIO_InitTypeDef gpio_init_struct;
        __HAL_RCC_ADC1_CLK_ENABLE();      /* 使能ADCx时钟 */
        __HAL_RCC_GPIOA_CLK_ENABLE() ;
				__HAL_RCC_DMA2_CLK_ENABLE();                    /* DMA1时钟使能 */
        /* AD采集引脚模式设置,模拟输入 */
        gpio_init_struct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;   //模拟输入
        gpio_init_struct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);
			

			/* DMA配置 */
			g_dma_adc_handle.Instance = DMA2_Stream4;                             /* 设置DMA数据流 */
			g_dma_adc_handle.Init.Channel = DMA_CHANNEL_0;                          /* 设置DMA通道 */
			g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 外设(DR寄存器)到存储器(数组)模式 */
			g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
			g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
			g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
			g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
			g_dma_adc_handle.Init.Mode = DMA_CIRCULAR;                                /* 循环模式模式 */
			g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
			HAL_DMA_Init(&g_dma_adc_handle);                                        /* 初始化DMA */
			__HAL_LINKDMA(&g_adc_handle,DMA_Handle,g_dma_adc_handle);  //链接


			
			HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 3, 3);    /* 设置中断优先级为3,子优先级3 */
			HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);            /* 使能DMA中断 */

			
			
    }
}



/*
打开DMA2_Stream4的中断
*/
void DMA2_Stream4_IRQHandler()
{
	HAL_DMA_IRQHandler(&g_dma_adc_handle);
}

/*
ADC的中断回调函数
也是DMA的回调函数

*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
			  uint8_t i,j;
		uint32_t num;
		if(hadc->Instance==ADC1)
		{
				for(i=0;i<6;i++)
			  {
					num=0;
					for(j=0;j<6;j++)
					{
						num+=ADC_DMA_data[j*6+i];
					}
					printf("ADC1_IN[%d]:%f V \r\n",i,(num/6.0)*(3.3/4096.0));   //串口1输出数字量	
				}		
				delay_ms(1000);
		}
}
#include "stm32f4xx.h"                  // Device header
#include "sys.h"
#include "delay.h"
#include "LED.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "adc.h"






int main(void)
{		
	
	
    HAL_Init();                                 /* 初始化HAL库 */        
		Clock_HSE_Init(8,336,RCC_PLLP_DIV2,7);      /* 设置时钟,168Mhz */
    delay_init(168);                            /* 延时初始化 */
          
		Uart_Init(115200);
		adc_init();

    while(1)
    {
			
			
				
				
		}
}

B:注入

A实验的基础上加入了注入组的。

#include "stm32f4xx.h"                  // Device header
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

ADC_HandleTypeDef g_adc_handle;   /* ADC句柄 */
ADC_InjectionConfTypeDef ADC_Injection;  //注入组句柄
DMA_HandleTypeDef g_dma_adc_handle;
ADC_ChannelConfTypeDef ADC_Channel[6];
/**
 * @brief       ADC初始化函数
 *   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
 *              我们使用12位精度, ADC采样时钟=21M, 转换时间为: 采样周期 + 12个ADC周期
 *              设置最大采样周期: 480, 则转换时间 = 492 个ADC周期 = 23.42us
 * @param       无
 * @retval      无
 */
 uint16_t ADC_DMA_data[36];
void adc_init(void)
{			
	
    g_adc_handle.Instance = ADC1;
    g_adc_handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;        /* 4分频,ADCCLK = PCLK2/4 = 84/4 = 21Mhz */
    g_adc_handle.Init.Resolution = ADC_RESOLUTION_12B;                      /* 12位模式 */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                      /* 右对齐 */
    g_adc_handle.Init.ScanConvMode = ENABLE;                               /* 打开扫描模式 */
    g_adc_handle.Init.ContinuousConvMode = ENABLE;                         	/* 打开连续转化 */
    g_adc_handle.Init.NbrOfConversion = 6;                                  /* 扫描模式下规则组的数量*/
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;                      /* 规则组间断模式 */
    g_adc_handle.Init.NbrOfDiscConversion = 0;                              /* 规则组间断模式下每次间断的通道数量 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                /* 软件触发 */
    g_adc_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* 使用软件触发 */
    g_adc_handle.Init.DMAContinuousRequests = ENABLE;                      /* 打开DMA请求 */
    HAL_ADC_Init(&g_adc_handle);                                            /* 初始化 */
		
	
		ADC_Channel[0].Channel=ADC_CHANNEL_0;     //通道编号
		ADC_Channel[0].Rank=1;     //排名
		ADC_Channel[0].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us

		//配置规则组通道
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[0]);
	 
		ADC_Channel[1].Channel=ADC_CHANNEL_1;     //通道编号
		ADC_Channel[1].Rank=2;     //排名
		ADC_Channel[1].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[1]);
		
		ADC_Channel[2].Channel=ADC_CHANNEL_2;     //通道编号
		ADC_Channel[2].Rank=3;     //排名
		ADC_Channel[2].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[2]);
			
		ADC_Channel[3].Channel=ADC_CHANNEL_3;     //通道编号
		ADC_Channel[3].Rank=4;     //排名
		ADC_Channel[3].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[3]);
		
		ADC_Channel[4].Channel=ADC_CHANNEL_4;     //通道编号
		ADC_Channel[4].Rank=5;     //排名
		ADC_Channel[4].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[4]);
		
		ADC_Channel[5].Channel=ADC_CHANNEL_5;     //通道编号
		ADC_Channel[5].Rank=6;     //排名
		ADC_Channel[5].SamplingTime=ADC_SAMPLETIME_56CYCLES;   //周期:56  56+12=68个     (1/21)*68=3.23us
		HAL_ADC_ConfigChannel(&g_adc_handle,&ADC_Channel[5]);
		
		//		//注入组配置---配置为自动注入
	ADC_Injection.InjectedChannel=ADC_CHANNEL_6;               //需要配置的通道
	ADC_Injection.InjectedRank=ADC_INJECTED_RANK_1;             //注入组的排名
	ADC_Injection.InjectedSamplingTime=ADC_SAMPLETIME_56CYCLES;   //采用周期
	ADC_Injection.InjectedOffset=0;                             //偏移量
	ADC_Injection.InjectedNbrOfConversion=4;                        	//注入组的转化通道:max=4  
	ADC_Injection.InjectedDiscontinuousConvMode=DISABLE;          //租入组间断模式;不可能同时使用自动注入和间断模式
	ADC_Injection.AutoInjectedConv=ENABLE;                        //自动注入
	ADC_Injection.ExternalTrigInjecConv=ADC_INJECTED_SOFTWARE_START;   //触发方式:软件,自动注入下必须禁止注入通道的外部触发
	HAL_ADCEx_InjectedConfigChannel(&g_adc_handle,&ADC_Injection);


	
		//注入组配置
	ADC_Injection.InjectedChannel=ADC_CHANNEL_7;               //需要配置的通道
	ADC_Injection.InjectedRank=ADC_INJECTED_RANK_2;             //注入组的排名
	ADC_Injection.InjectedSamplingTime=ADC_SAMPLETIME_56CYCLES;   //采用周期
	ADC_Injection.InjectedOffset=0;                           //偏移量
	HAL_ADCEx_InjectedConfigChannel(&g_adc_handle,&ADC_Injection);
			//注入组配置
	ADC_Injection.InjectedChannel=ADC_CHANNEL_8;               //需要配置的通道
	ADC_Injection.InjectedRank=ADC_INJECTED_RANK_3;             //注入组的排名
	ADC_Injection.InjectedSamplingTime=ADC_SAMPLETIME_56CYCLES;   //采用周期
	ADC_Injection.InjectedOffset=0;                           //偏移量
	HAL_ADCEx_InjectedConfigChannel(&g_adc_handle,&ADC_Injection);
			//注入组配置
	ADC_Injection.InjectedChannel=ADC_CHANNEL_9;               //需要配置的通道
	ADC_Injection.InjectedRank=ADC_INJECTED_RANK_4;             //注入组的排名
	ADC_Injection.InjectedSamplingTime=ADC_SAMPLETIME_56CYCLES;   //采用周期
	ADC_Injection.InjectedOffset=0;                           //偏移量
	HAL_ADCEx_InjectedConfigChannel(&g_adc_handle,&ADC_Injection);

	//HAL_ADCEx_Calibration_Start(&ADC_HandleType);                   //自校准
	

	HAL_ADCEx_InjectedStart_IT(&g_adc_handle);      //中断方式开启注入组
		//发生36次转运,触发DMA中断,进入回调函数
		HAL_ADC_Start_DMA(&g_adc_handle,(uint32_t*)ADC_DMA_data,36);

}

/**
 * @brief       ADC底层驱动,引脚配置,时钟使能
                此函数会被HAL_ADC_Init()调用
 * @param       hadc:ADC句柄
 * @retval      无
 */
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC1)
    {
        GPIO_InitTypeDef gpio_init_struct;
        __HAL_RCC_ADC1_CLK_ENABLE();      /* 使能ADCx时钟 */
        __HAL_RCC_GPIOA_CLK_ENABLE() ;
				  __HAL_RCC_GPIOB_CLK_ENABLE() ;
				__HAL_RCC_DMA2_CLK_ENABLE();                    /* DMA1时钟使能 */
        /* AD采集引脚模式设置,模拟输入 */
        gpio_init_struct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
				/GPIO_PIN_6|GPIO_PIN_7;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;   //模拟输入
        gpio_init_struct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);
			
			gpio_init_struct.Mode=GPIO_MODE_ANALOG;  //模拟输入
			gpio_init_struct.Pin=GPIO_PIN_0|GPIO_PIN_1;
			HAL_GPIO_Init(GPIOB,&gpio_init_struct);

			/* DMA配置 */
			g_dma_adc_handle.Instance = DMA2_Stream4;                             /* 设置DMA数据流 */
			g_dma_adc_handle.Init.Channel = DMA_CHANNEL_0;                          /* 设置DMA通道 */
			g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 外设(DR寄存器)到存储器(数组)模式 */
			g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
			g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
			g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
			g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
			g_dma_adc_handle.Init.Mode = DMA_CIRCULAR;                                /* 循环模式模式 */
			g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
			HAL_DMA_Init(&g_dma_adc_handle);                                        /* 初始化DMA */
			__HAL_LINKDMA(&g_adc_handle,DMA_Handle,g_dma_adc_handle);  //链接


			
			HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 3, 3);    /* 设置中断优先级为3,子优先级3 */
			HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);            /* 使能DMA中断 */

			HAL_NVIC_SetPriority(ADC_IRQn, 3, 3);    /* 设置中断优先级为3,子优先级3 */
			HAL_NVIC_EnableIRQ(ADC_IRQn);            /* 使能DMA中断 */

			
    }
}


//注入组是以ADC中断执行的
void ADC_IRQHandler()
{
	HAL_ADC_IRQHandler(&g_adc_handle);

}


/*
打开DMA2_Stream4的中断
*/
void DMA2_Stream4_IRQHandler()
{
	HAL_DMA_IRQHandler(&g_dma_adc_handle);
}

/*
ADC的中断回调函数
也是DMA的回调函数

*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
			  uint8_t i,j;
		uint32_t num;
		if(hadc->Instance==ADC1)
		{
				for(i=0;i<6;i++)
			  {
					num=0;
					for(j=0;j<6;j++)
					{
						num+=ADC_DMA_data[j*6+i];
					}
					printf("ADC1_IN[%d]:%f V \r\n",i,(num/6.0)*(3.3/4096.0));   //串口1输出数字量	
				}		
				delay_ms(1000);
		}
}


//注入租中断回调函数			
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc)
{
		if(hadc->Instance==ADC1)
		{
			printf("---------注入组完成中断--------\r\n");
			printf("ADC1_IN[6]:%f V \r\n",HAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_1)*(3.3/4096.0));   
			printf("ADC1_IN[7]:%f V \r\n",HAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_2)*(3.3/4096.0));   
			printf("ADC1_IN[8]:%f V \r\n",HAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_3)*(3.3/4096.0));   
			printf("ADC1_IN[9]:%f V \r\n",HAL_ADCEx_InjectedGetValue(hadc,ADC_INJECTED_RANK_4)*(3.3/4096.0));  
			printf("-----------------------------\r\n");			
			__HAL_ADC_ENABLE_IT(hadc, ADC_IT_JEOC);	
			
			//HAL_ADC_IRQHandler函数中会关闭我们的注入组的中断标志为所以我们需要自己在打开一下
		}
		delay_ms(1000);

}
#include "stm32f4xx.h"                  // Device header
#include "sys.h"
#include "delay.h"
#include "LED.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "adc.h"






int main(void)
{		
	
	
    HAL_Init();                                 /* 初始化HAL库 */        
		Clock_HSE_Init(8,336,RCC_PLLP_DIV2,7);      /* 设置时钟,168Mhz */
    delay_init(168);                            /* 延时初始化 */
          
		Uart_Init(115200);
		adc_init();

    while(1)
    {
			
			
				
				
		}
}


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

猫咪浮毛引起呼吸问题?希喂、小米、有哈宠物空气净化器性能对比

相信每个铲屎官都会碰到猫咪掉毛的问题&#xff0c;掉落堆积的猫毛除了带来的清扫负担&#xff0c;还存在着极大的健康隐患。毛发主要分为两种&#xff0c;大颗粒的猫毛可以被我们肉眼所看见&#xff0c;通常会沉在地面上、床上。这类猫毛我们可以用粘毛器、吸尘器等工具进行清…

反向沙箱-安全上网解决方案

随着信息化的发展&#xff0c;企业日常办公越来越依赖互联网。终端以及普通PC终端在访问互联网过程中&#xff0c;会遇到各种各样不容忽视的风险&#xff0c;例如员工主动故意的数据泄漏&#xff0c;后台应用程序偷偷向外部发信息&#xff0c;木马间谍软件的外联&#xff0c;以…

录屏时摄像头无法识别?如何录屏时打开摄像头,解决方案及录屏软件推荐

在数字时代&#xff0c;无论是游戏玩家、在线教育者还是企业培训师&#xff0c;录屏软件都已成为日常工作和娱乐中不可或缺的工具。但有时候想录制人物摄像头画面的时候&#xff0c;当录屏软件无法识别到摄像头时&#xff0c;这无疑会给用户带来不小的困扰。本文将提供一系列解…

ts格式转mp4,四款亲测好用软件推荐!

在这个数字视频时代&#xff0c;我们经常会遇到各种视频格式兼容性问题&#xff0c;尤其是从网络下载的高清电影或电视剧集&#xff0c;很多时候都是以TS格式存储。然而&#xff0c;当我们想要在移动设备、社交媒体或视频编辑软件中播放、上传时&#xff0c;MP4格式因其广泛的兼…

SEO之网站结构优化(十五-CMS系统)

初创企业搭建网站的朋友看1号文章&#xff1b;想学习云计算&#xff0c;怎么入门看2号文章谢谢支持&#xff1a; 1、我给不会敲代码又想搭建网站的人建议 2、“新手上云”能够为你开启探索云世界的第一步 博客&#xff1a;阿幸SEO~探索搜索排名之道 15、CMS系统 现在的网站绝…

2024 年高教社杯全国大学生数学建模竞赛题目-C 题 农作物的种植策略

根据乡村的实际情况&#xff0c;充分利用有限的耕地资源&#xff0c;因地制宜&#xff0c;发展有机种植产业&#xff0c;对乡村经济 的可持续发展具有重要的现实意义。选择适宜的农作物&#xff0c;优化种植策略&#xff0c;有利于方便田间管理&#xff0c;提 高生产效益&#…

python-第三方库-[yarl、yaml]

python-第三方库-[yarl、yaml] 一: yarl1> yarl 介绍2> yarl.URL 介绍1. yarl.URL 的语法格式2. yarl.URL demo3. yarl.URL.build()4. yarl.URL().with_*()5. yarl.URL().update_query()6> url / &%组合 二&#xff1a;yaml1> yaml 介绍2> yaml 基本规则1. …

智能客服的四大优势,提升企业服务效率

在这个信息化快速发展的时代&#xff0c;客户服务的重要性越来越凸显。传统的客服方式已经无法满足企业日益增长的服务需求&#xff0c;于是智能客服服务应运而生。智能客服服务不仅改变了企业与客户的互动方式&#xff0c;还提高了服务效率和客户满意度。本文将深入探讨智能客…

La-Z-Boy 标签制作注意事项

在制作标签之前&#xff0c;供应商需要通过EDI向La-Z-Boy发送提前发货通知&#xff08;ASN&#xff09;。ASN中的每个明细行将会至少对应一个运输编号&#xff08;shipment ID&#xff09;&#xff0c;这个信息将会被体现在运输标签上&#xff0c;和标签上的条形码一起&#xf…

log4j漏洞原理以及复现

kali:192.168.222.165 本机&#xff1a;192.168.225.18 原理 Log4j为了输出日志时能输出任意位置的Java对象&#xff0c;引入了Lookup接口&#xff0c;这个Lookup接口可以看作是JNDI的一种实现&#xff0c;允许按照具体的名称逻辑查找对象的位置&#xff0c;并输出对象的内容…

使用nssm将fastapi做成服务解决cmd使用命令启动卡顿问题

下载nssm 点击下载 我使用的是64位的操作系统所以选择64位win64 点击进去在地址栏里面输入cmd回车 输入 nssm.exe install "想要做成的服务名称" "python路径精确到python.exe" "程序路径要绝对路径"然后就做出了服务并且启动 可以在服务管…

YOLOv9改进策略【注意力机制篇】| 引入Shuffle Attention注意力模块,增强特征图的语义表示

一、本文介绍 本文记录的是基于Shuffle Attention注意力模块的YOLOv9目标检测改进方法研究。Shuffle Attention模块通过独特的设计原理&#xff0c;在保持轻量级的同时实现了高效的特征注意力机制&#xff0c;增强了网络的表示能力。本文对YOLOv9的RepNCSPELAN4模块进行二次创…

Elasticsearch 中的相关性和得分

在Elasticsearch中&#xff0c;相关性&#xff08;Relevance&#xff09;和得分&#xff08;Score&#xff09;是搜索引擎技术中非常重要的概念&#xff0c;它们直接影响搜索结果的排序。 相关性&#xff08;Relevance&#xff09; 相关性是指搜索结果与用户查询的相关程度。…

Java System.getenv 和 System.getProperty 区别

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

全新界面,原生Mac和Linux支持的WinBox 4 发布了!

简介 WinBox 4 终于来了&#xff01;&#xff01;​ ​ 适用于 Windows、macOS 和 Linux的原生程序。&#xff08;不需要开wine之类的软件了&#xff09; 更现代化的软件界面&#xff0c;终于不是上个世纪的风格了。 支持深色模式&#xff0c;不能亮瞎狗眼了。 官方&#…

echarts多个环形图

echarts图表集 var dataValue [{name:今日待分配方量,value:49}, {name:今日已分配方量,value:602}, {name:今日完成方量,value:1037}]var piedata1 [{name: 1#拌和机,value: 20},{name: 2#拌和机,value: 22},{name: 3#拌和机 ,value: 17},{name: 4#拌和机,value: 18},{name…

品牌做渠道开发有何意义,重要性在哪里?

品牌做渠道开发&#xff0c;就是为了把企业的产品和服务&#xff0c;传递给目标消费者&#xff0c;从而建立并维护了一系列的分销渠道。 渠道开发不仅涉及了销售渠道&#xff08;电商平台、线下实体店、分销商、代理商&#xff09;等&#xff0c;还包括制定渠道策略&#xff0…

【区块链 + 人才服务】基于 FISCO BCOS 联盟链的电子证书认证平台 | FISCO BCOS应用案例

传统电子证书认证存在一些弊端&#xff0c;比如由于数据权限过于集中&#xff0c;可能存在私自篡改数据的问题&#xff0c;从而导致数据不可信。其次&#xff0c;因为数据存储于中心服务器&#xff0c;存在单点故障或易被不法分子恶意攻击的风险&#xff0c;造成数据完整性的问…

最新!《第七届人力资源共享服务中心研究报告》重磅来袭 | 附下载

9月5日&#xff0c;“ALL IN 2024 人力资源服务展-上海站”在上海跨国采购会展中心隆重举办。展会上&#xff0c;由人力资源智享会&#xff08;以下简称“智享会”&#xff09;与法大大联合出版的《第七届人力资源共享服务中心研究报告》正式发布。该报告立足行业变革趋势、对话…

基于Android Studio的行程记录APK开发指南(三)---界面设计及两种方法获取用户位置

前言 本系列教程我们来看看如何使用Android Studio去开发一个APK用于用户的实时行程记录 第一期&#xff1a;基于Android Studio的用户行程记录APK开发指南(一)&#xff1a;项目基础配置与速通Kotlin-CSDN博客第二期&#xff1a;基于Android Studio的行程记录APK开发指南(二):…