STM32F7实现ADC采集(软件触发+DMA传输)解决了采样结果不实时更新的问题

前面我们虽然实现了STM32F7实现ADC采集(软件触发+DMA传输),但数据只有第一次更新。
先仔细研究了下一些配置的参数:

ADC1DMA_Handler.Init.Mode

DMA的正常模式(DMA_NORMAL):采集到DMA_BufferSize 的个数后,DMA停止。
DMA的循环模式(DMA_CIRCULAR):采集到DMA_BufferSize 的个数后,重新回到设置的RAM的起点位置,如此循环。
虽然道理很好理解,但个人感觉要配合触发信号来用,通过HAL_ADC_Start_DMA软件触发需要设置起点位置和长度,否则这个参数是没有意义的。

ADC1_Handler.Init.ScanConvMode

扫描模式(ENABLE):ADC会依次扫描设置的各个rank
非扫描模式(DISABLE):ADC只会读取rank1,即使设置的DMA_BufferSize大于2,也只会读取第一个;如果没设置rank1(此时我的设置里rank2接的3V3,rank3接的gnd),读出来的结果是516这种奇怪的东西,我也不知道是个什么鬼东西……
即使需要只读取rank1的值,把NbrOfConversion不就可以了嘛~
所以这个参数我建议设置成ENABLE,我也不知道什么时候该用DISABLE。

ADC1_Handler.Init.ContinuousConvMode

开启连续转换:开启了连续转换后,adc会一直转换,直到DMA_BufferSize大小,如图是HAL_ADC_Start_DMA 64个长度的结果。
在这里插入图片描述
而不开启连续转换,长度为64的同时,DMA的传输中断都不会进。
在这里插入图片描述
而将传输长度设置为3的时候,就进入了传输完成过半的中断。
在这里插入图片描述
如果我们需要采集连续的N个次结果求平均值,用这个连续转换会很方便。
也从侧面说明,dma的传输中断是一定要有那么长的真实采集数据来才能进的。

另外一篇博文里看到了这么一个表格,希望能够辅助大家理解:
在这里插入图片描述

ADC1_Handler.Init.DMAContinuousRequests

当设置了定时器触发DMA之后,这个参数如果设置为DISABLE,就只会采集一轮数据,然后结束。
这个数据设置成了ENABLE之后,这个参数设置为ENABLE,一轮采集完成之后,感觉就会自动调用一次HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)buffer, 100);,触发DMA请求,以实现不停采集。

但是需要注意,如果开启了连续转换,又开启了DMA连续请求,adc将一直进行采集,一刻也不停歇……

ADC1_Handler.Init.EOCSelection

EOC听说是转换结束标志,这里暂时也没用到,也先埋一个坑……
填坑STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置

偶然看到一个帖子的回复

当有CPU和其它主设备【如DMA】共同访问某可缓存的二级存储器比方SRAM1,同时该存储器又具有回写属性,此时就可能发生数据一致性问题。因为该存储器的回写属性,导致通过CPU欲写入存储器的数据只是缓冲在CACHE里,而没有及时写入存储器。如果此时DMA访问该二级存储器的话,读到的数据可能跟预期不一致。
为了避免数据不一致的问题,我们需要做D-CACHE维护操作。一般有如下四种方法: 1、当对一个可缓存的二级存储器做了写数据操作之后,通过软件对D-CACHE进行清除操作,即运行SCB_CleanDCache()。这样将CACHE里的缓存内容写回到二级存储器,比如把那些DIRTY
CACHE行的数据写进SRAM1。
2、通过MPU调整可缓存存储器的存储属性,将其CACHE使用方式改为透写模式。这样保证每次写入CACHE里的内容也同时写入二级存储器,比如写进SRAM1。
3、通过MPU调整可缓存存储器的存储属性,将其共享属性改为可共享的【SHAREABLE】。此后该二级存储器将变为不可缓存。
4、通过配置CACR寄存器中的D-CACHE位,强制将所有写操作配置为透写属性。

在main函数中关闭cache,DMA采集的数据就可以更新了。
在这里插入图片描述
附上成功时候的配置,注释掉HAL_NVIC_EnableIRQ是因为中断服务函数里的打印会导致main函数无法继续执行。

#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_CIRCULAR;                          //传输一次就结束
    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 = ENABLE;              //关闭连续转换
    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 = ENABLE;           //关闭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, 100);
}

//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");
}
#include "sys.h"
#include "delay.h"
#include "usart.h"

#include "adc.h"

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]);
			buffer[i]=0;

    }
    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);
    }
}
  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
STM32F103是一款具有多通道ADC、TIM、DMA和FFT功能的微控制器。 首先,多通道ADC允许我们同时采集多个不同通道的模拟信号。这对于需要同时监测多个传感器或信号源的应用非常有用。通过配置ADC的多个通道,并设置相应的采样速率和分辨率,可以轻松实现高效的数据采集。 其次,TIM(定时器)模块允许我们生成精确的时间基准。在实时系统和定时器应用中,我们可以配置TIM来实现各种定时和计时功能。通过设置预分频器、计数器和比较器,我们可以准确地生成周期性的、定时的或脉冲宽度可变的信号。这对于控制和调度其他外设非常有用。 接下来,DMA(直接内存访问)控制器允许我们实现高速数据传输,而不需要CPU的干预。通过配置DMA通道并定义源和目标的地址、传输长度和传输方向,我们可以实现高效的数据传输操作。这对于处理大量数据、高速数据流或实时响应的应用非常有用。 最后,FFT(快速傅里叶变换)是一种用于信号处理和频谱分析的重要算法。通过使用STM32F103的FFT库,我们可以对采集到的模拟信号进行频谱分析,以提取信号的频率和幅度特征。这对于音频处理、通信系统、传感器数据分析等应用非常有用。 总之,STM32F103的多通道ADC、TIM、DMA和FFT功能为我们提供了丰富的数据采集、定时、数据传输和信号处理能力,使得我们可以设计出高度灵活、高效的嵌入式系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值