一: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)
{
}
}