STM32F7实现ADC采集(软件触发+DMA传输)

前面完成了STM32F7实现ADC采集(软件触发+轮询),但轮询的时候CPU得不到释放,在此基础上,这里实现DMA的传输。
由STM32F7的中文参考手册可以知道,ADC可以使用DMA2的数据流0:
在这里插入图片描述
使用CubeMX配置DMA:
在这里插入图片描述

adc.c内容如下:

#include "adc.h"
#include "delay.h"

ADC_HandleTypeDef ADC1_Handler;//ADC句柄
DMA_HandleTypeDef ADC1DMA_Handler;
ADC_ChannelConfTypeDef ADC1_ChanConf;

uint16_t buffer[128];

/*下面的函数写完才发现没有用*/
//void ADC1DMA_CpltCallback(struct __DMA_HandleTypeDef *hdma)
//{
//    printf("DMA transfer complete\r\n");
//}
//void ADC1DMA_HalfCpltCallback(struct __DMA_HandleTypeDef *hdma)
//{
//    printf("DMA Half transfer complete\r\n");
//}
//void ADC1DMA_M1CpltCallback(struct __DMA_HandleTypeDef *hdma)
//{
//    printf("DMA transfer complete Memory1\r\n");
//}
//void ADC1DMA_M1HalfCpltCallback(struct __DMA_HandleTypeDef *hdma)
//{
//    printf("DMA transfer Half complete Memory1\r\n");
//}
//void ADC1DMA_ErrorCallback(struct __DMA_HandleTypeDef *hdma)
//{
//    printf("DMA transfer error\r\n");
//}
//void ADC1DMA_XferAbortCallback(struct __DMA_HandleTypeDef *hdma)
//{
//    printf("DMA transfer Abort \r\n");
//}


//初始化ADC
//ch: ADC_channels
//通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
void MY_ADC_Init(void)
{
    __HAL_RCC_DMA2_CLK_ENABLE();
    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

    ADC1DMA_Handler.Instance = DMA2_Stream0;
    ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
    ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
    ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;               //外设非增量模式
    ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                   //存储器增量模式
    ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设数据长度:16位
    ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //存储器数据长度:16位
    ADC1DMA_Handler.Init.Mode = DMA_NORMAL;                          //传输一次就结束
    ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW;             //中等优先级
    ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;            /* 禁止FIFO*/
    /*自己增加中断回调函数*/
//    ADC1DMA_Handler.XferCpltCallback = ADC1DMA_CpltCallback;
//    ADC1DMA_Handler.XferHalfCpltCallback = ADC1DMA_HalfCpltCallback;
//    ADC1DMA_Handler.XferM1CpltCallback = ADC1DMA_M1CpltCallback;
//    ADC1DMA_Handler.XferM1HalfCpltCallback = ADC1DMA_M1HalfCpltCallback;
//    ADC1DMA_Handler.XferErrorCallback = ADC1DMA_ErrorCallback;
//    ADC1DMA_Handler.XferAbortCallback = ADC1DMA_XferAbortCallback;
    /*最后发现并没有什么用*/
    HAL_DMA_Init(&ADC1DMA_Handler);

    __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler);                //将DMA与ADC联系起来


    ADC1_Handler.Instance = ADC1;
    ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ
    ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B;           //12位模式
    ADC1_Handler.Init.ScanConvMode = DISABLE;                    //非扫描模式
    ADC1_Handler.Init.ContinuousConvMode = DISABLE;              //关闭连续转换
    ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;           //禁止不连续采样模式
    ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
    ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START;     //软件触发
    ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;           //右对齐
    ADC1_Handler.Init.NbrOfConversion = 1;                       //1个转换在规则序列中 也就是只转换规则序列1
    ADC1_Handler.Init.DMAContinuousRequests = DISABLE;           //关闭DMA请求
    ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;        
    HAL_ADC_Init(&ADC1_Handler);

    ADC1_ChanConf.Channel = ADC_CHANNEL_5;                                 //通道
    ADC1_ChanConf.Rank = 1;                                     //1个序列
    ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间
    ADC1_ChanConf.Offset = 0;
    HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置

    HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 1);

}

//ADC底层驱动,引脚配置,时钟使能
//此函数会被HAL_ADC_Init()调用
//hadc:ADC句柄
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_ADC1_CLK_ENABLE();            //使能ADC1时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟

    GPIO_Initure.Pin = GPIO_PIN_5;          //PA5
    GPIO_Initure.Mode = GPIO_MODE_ANALOG;   //模拟
    GPIO_Initure.Pull = GPIO_NOPULL;        //不带上下拉
    HAL_GPIO_Init(GPIOA, &GPIO_Initure);
}

void DMA2_Stream0_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&ADC1DMA_Handler);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
	printf("DMA transfer complete\r\n");
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
	printf("DMA Half transfer complete\r\n");
}

void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
{
	printf("DMA transfer error\r\n");
}

注意,代码里注释的部分是,虽然我们在对DMA初始化的时候,我将DMA相应的回调函数参数都设置好了,但初始化完成之后,其中有三个函数并没有指向我们设置的函数,而是指向了具有__weak属性的三个函数。
在这里插入图片描述
这是因为在进行ADC初始化的时候,重写了ADC使用的DMA的回调函数。
在这里插入图片描述
所以最终是直接重写三个函数就行了,不用再去设置。

main函数中也不需要去调用,直接打印数据就可以了。

#include "sys.h"
#include "delay.h"
#include "usart.h"

#include "adc.h"

extern ADC_HandleTypeDef ADC1_Handler;//ADC句柄
extern uint16_t buffer[128];

void show()
{
    int i;
    printf("\r\ndata:");
    for (i = 0; i < 128; i++)
    {
        if (i % 16 == 0) printf("\r\n");
        printf("%6d", buffer[i]);

    }
    printf("\r\n");
}


int main(void)
{
    Cache_Enable();                 //打开L1-Cache
    HAL_Init();                     //初始化HAL库
    Stm32_Clock_Init(432, 8, 2, 9); //设置时钟,216Mhz
    delay_init(216);                //延时初始化
    uart_init(115200);              //串口初始化

    printf("start\r\n");
    MY_ADC_Init();                  //初始化ADC1通道5
    
    while (1)
    {
        show();
        delay_ms(1000);
    }
}

实际打印结果如图:
在这里插入图片描述
当我们需要进行多个通道采集的时候,先修改CubeMX配置,改为扫描模式,修改规则通道的转换数目,修改相应的Rank内容:
在这里插入图片描述
生成完代码后,启动函数长度修改位转换长度:

HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 2);

修改后的ADC完整代码如下:

#include "adc.h"
#include "delay.h"

ADC_HandleTypeDef ADC1_Handler;//ADC句柄
DMA_HandleTypeDef ADC1DMA_Handler;
ADC_ChannelConfTypeDef ADC1_ChanConf;

uint16_t buffer[128];


//初始化ADC
//ch: ADC_channels
//通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
void MY_ADC_Init(void)
{
    __HAL_RCC_DMA2_CLK_ENABLE();
    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

    ADC1DMA_Handler.Instance = DMA2_Stream0;
    ADC1DMA_Handler.Init.Channel = DMA_CHANNEL_0;
    ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
    ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;               //外设非增量模式
    ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                   //存储器增量模式
    ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设数据长度:16位
    ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //存储器数据长度:16位
    ADC1DMA_Handler.Init.Mode = DMA_NORMAL;                          //传输一次就结束
    ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW;             //中等优先级
    ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;            /* 禁止FIFO*/

    HAL_DMA_Init(&ADC1DMA_Handler);

    __HAL_LINKDMA(&ADC1_Handler, DMA_Handle, ADC1DMA_Handler);                //将DMA与ADC联系起来


    ADC1_Handler.Instance = ADC1;
    ADC1_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=108/4=27MHZ
    ADC1_Handler.Init.Resolution = ADC_RESOLUTION_12B;           //12位模式
    ADC1_Handler.Init.ScanConvMode = ENABLE;                    //非扫描模式
    ADC1_Handler.Init.ContinuousConvMode = DISABLE;              //关闭连续转换
    ADC1_Handler.Init.DiscontinuousConvMode = DISABLE;           //禁止不连续采样模式
    ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发
    ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START;     //软件触发
    ADC1_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT;           //右对齐
    ADC1_Handler.Init.NbrOfConversion = 2;                       //1个转换在规则序列中 也就是只转换规则序列1
    ADC1_Handler.Init.DMAContinuousRequests = DISABLE;           //关闭DMA请求
    ADC1_Handler.Init.EOCSelection = ADC_EOC_SINGLE_CONV;        
    HAL_ADC_Init(&ADC1_Handler);

    ADC1_ChanConf.Channel = ADC_CHANNEL_5;                                 //通道
    ADC1_ChanConf.Rank = 1;                                     //序列1
    ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间
    ADC1_ChanConf.Offset = 0;
    HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置

    ADC1_ChanConf.Channel = ADC_CHANNEL_6;                                 //通道
    ADC1_ChanConf.Rank = 2;                                     //序列2
    ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_480CYCLES;      //采样时间
    ADC1_ChanConf.Offset = 0;
    HAL_ADC_ConfigChannel(&ADC1_Handler, &ADC1_ChanConf);       //通道配置

    HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 2);

}

//ADC底层驱动,引脚配置,时钟使能
//此函数会被HAL_ADC_Init()调用
//hadc:ADC句柄
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_ADC1_CLK_ENABLE();            //使能ADC1时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟

    GPIO_Initure.Pin = GPIO_PIN_5|GPIO_PIN_6;          //PA5
    GPIO_Initure.Mode = GPIO_MODE_ANALOG;   //模拟
    GPIO_Initure.Pull = GPIO_NOPULL;        //不带上下拉
    HAL_GPIO_Init(GPIOA, &GPIO_Initure);
}

void DMA2_Stream0_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&ADC1DMA_Handler);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
	printf("DMA transfer complete\r\n");
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
	printf("DMA Half transfer complete\r\n");
}

void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
{
	printf("DMA transfer error\r\n");
}

效果如图:
在这里插入图片描述

发布了353 篇原创文章 · 获赞 81 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/qq_27508477/article/details/105613885