硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1187|回复: 14
收起左侧

[DMA] 7通道ADC采集(定时器驱动)+DMA双缓冲,采集的波形不连续,还有毛刺!

[复制链接]

4

主题

10

回帖

22

积分

新手上路

积分
22
发表于 2022-6-23 19:06:47 | 显示全部楼层 |阅读模式
我要采集三相电50Hz的正弦波, 定时器设置的 10kHz来驱动ADC;定义了一个 1024*7长度的buff来存放数据
[C] 纯文本查看 复制代码
ALIGN_32BYTES(__attribute__((section(".RAM_D1")))) uint16_t adc3Data[512* 2*7];

DMA中断处理如下:

[C] 纯文本查看 复制代码
void DMA1_Stream1_IRQHandler(void)
{
    /* 传输完成中断 */
    if ((DMA1->LISR & DMA_FLAG_TCIF1_5) != RESET)
    {
        SCB_InvalidateDCache_by_Addr((uint32_t *)(&adc3Data[512*7]), 512* 2 * 2*7);
        s_Dma1Flag = 2;
        /* 清除标志 */
        DMA1->LIFCR = DMA_FLAG_TCIF1_5;
    }
    /* 半传输完成中断 */
    if ((DMA1->LISR & DMA_FLAG_HTIF1_5) != RESET)
    {
        SCB_InvalidateDCache_by_Addr((uint32_t *)(&adc3Data[0]), 512* 2 * 2 * 7);
        s_Dma1Flag = 1;
        /* 清除标志 */
        DMA1->LIFCR = DMA_FLAG_HTIF1_5;
    }
    /* 传输错误中断 */
    if ((DMA1->LISR & DMA_FLAG_TEIF1_5) != RESET)
    {
        /* 清除标志 */
        DMA1->LIFCR = DMA_FLAG_TEIF1_5;
    }
    /* 直接模式错误中断 */
    if ((DMA1->LISR & DMA_FLAG_DMEIF1_5) != RESET)
    {
        /* 清除标志 */
        DMA1->LIFCR = DMA_FLAG_DMEIF1_5;
    }
}

应用代码获取电压值:
[C] 纯文本查看 复制代码
if (s_Dma2Flag == 1)
        {
            for (int i = 0; i < V_SAMPLINGPOINT_NUMBER; i++)
            {
                fftCalcBufferUa.dataBufferSingle[i] = (3.3 * adc3Data[i * 7 + 4] / 65536) * 330;
                fftCalcBufferUb.dataBufferSingle[i] = (3.3 * adc3Data[i * 7 + 5] / 65536) * 330;
                fftCalcBufferUc.dataBufferSingle[i] = (3.3 * adc3Data[i * 7 + 6] / 65536) * 330;
                fftCalcBufferU0.dataBufferSingle[i] = (3.3 * adc3Data[i * 7 + 3] / 65536) * 330;

                fftCalcBufferIa.dataBufferSingle[i] = ((3.3 * adc3Data[i * 7] / 65536) / 500) * 2000;
                fftCalcBufferIb.dataBufferSingle[i] = ((3.3 * adc3Data[i * 7 + 1] / 65536) / 500) * 2000;
                fftCalcBufferIc.dataBufferSingle[i] = ((3.3 * adc3Data[i * 7 + 2] / 65536) / 500) * 2000;
            }
            s_Dma2Flag = 0;
            samplingComplete = 1;
        }
        else if (s_Dma2Flag == 2)
        {
            for (int i = V_SAMPLINGPOINT_NUMBER; i < V_SAMPLINGPOINT_NUMBER * 2; i++)
            {
                fftCalcBufferUa.dataBufferSingle[i - V_SAMPLINGPOINT_NUMBER] = (3.3 * adc3Data[i * 7 + 4] / 65536) * 330;
                fftCalcBufferUb.dataBufferSingle[i - V_SAMPLINGPOINT_NUMBER] = (3.3 * adc3Data[i * 7 + 5] / 65536) * 330;
                fftCalcBufferUc.dataBufferSingle[i - V_SAMPLINGPOINT_NUMBER] = (3.3 * adc3Data[i * 7 + 6] / 65536) * 330;
                fftCalcBufferU0.dataBufferSingle[i - V_SAMPLINGPOINT_NUMBER] = (3.3 * adc3Data[i * 7 + 3] / 65536) * 330;

                fftCalcBufferIa.dataBufferSingle[i - V_SAMPLINGPOINT_NUMBER] = ((3.3 * adc3Data[i * 7] / 65536) / 500) * 2000;
                fftCalcBufferIb.dataBufferSingle[i - V_SAMPLINGPOINT_NUMBER] = ((3.3 * adc3Data[i * 7 + 1] / 65536) / 500) * 2000;
                fftCalcBufferIc.dataBufferSingle[i - V_SAMPLINGPOINT_NUMBER] = ((3.3 * adc3Data[i * 7 + 2] / 65536) / 500) * 2000;
            }
            s_Dma2Flag = 0;
            samplingComplete = 1;
        }


RAM_D1内存区域的MPU设置是
MPU_ACCESS_NOT_SHAREABLE,MPU_ACCESS_NOT_CACHEABLE,MPU_ACCESS_NOT_BUFFERABLE

整个数据传输没有问题,但是采集的波形不连续, 还有一点毛刺

波形

波形


另外一个问题是 DMA的双缓冲是不是没法调试啊, 我调试进入DMA的中断发现传输完成和半传输完成的条件都是True,是不是有问题呢?



回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106826
QQ
发表于 2022-6-24 08:32:26 | 显示全部楼层
直接把ADC所使用RAM空间的读写Cache关闭了。然后测试下。
回复

使用道具 举报

13

主题

86

回帖

125

积分

初级会员

积分
125
发表于 2022-6-24 08:34:22 | 显示全部楼层
这是要做电能质量分析仪吗?没弄过一个ADC多通道的,帮顶。
回复

使用道具 举报

4

主题

10

回帖

22

积分

新手上路

积分
22
 楼主| 发表于 2022-6-24 09:48:38 | 显示全部楼层
eric2013 发表于 2022-6-24 08:32
直接把ADC所使用RAM空间的读写Cache关闭了。然后测试下。

具体 怎么操作呢? 我设置了MPU也不行吗?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106826
QQ
发表于 2022-6-24 14:00:11 | 显示全部楼层
xj_solid 发表于 2022-6-24 09:48
具体 怎么操作呢? 我设置了MPU也不行吗?

完整MPU配置发出来看下。
回复

使用道具 举报

4

主题

10

回帖

22

积分

新手上路

积分
22
 楼主| 发表于 2022-6-24 17:01:11 | 显示全部楼层
[C] 纯文本查看 复制代码
u8 MPU_Set_Protection(u32 baseaddr, u32 size, u32 rnum, u8 ap, u8 sen, u8 cen, u8 ben)
{
	MPU_Region_InitTypeDef MPU_Initure;

	HAL_MPU_Disable(); //配置MPU之前先关闭MPU,配置完成以后在使能MPU

	MPU_Initure.Enable = MPU_REGION_ENABLE; //使能该保护区域
	MPU_Initure.Number = rnum;				//设置保护区域
	MPU_Initure.BaseAddress = baseaddr;		//设置基址
	MPU_Initure.Size = size;				//设置保护区域大小
	MPU_Initure.AccessPermission = (u8)ap;	//设置访问权限,
	MPU_Initure.IsShareable = sen;			//是否共用?
	MPU_Initure.IsCacheable = cen;			//是否cache?
	MPU_Initure.IsBufferable = ben;			//是否缓冲?
	MPU_Initure.TypeExtField = MPU_TEX_LEVEL0;
	MPU_Initure.SubRegionDisable = 0x00;
	MPU_Initure.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
	HAL_MPU_ConfigRegion(&MPU_Initure);		//配置MPU
	HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); //开启MPU
	return 0;
}

//设置需要保护的存储块
//必须对部分存储区域进行MPU保护,否则可能导致程序运行异常
//比如MCU屏不显示,摄像头采集数据出错等等问题...
void MPU_Memory_Protection(void)
{
	//保护整个D1 SRAM 512KB
	MPU_Set_Protection(0x24000000,				   //基地址
					   MPU_REGION_SIZE_512KB,	   //长度
					   MPU_REGION_NUMBER1,		   // NUMER1
					   MPU_REGION_FULL_ACCESS,	   //全访问
					   MPU_ACCESS_NOT_SHAREABLE,   //允许共享
					   MPU_ACCESS_NOT_CACHEABLE,   //允许cache
					   MPU_ACCESS_NOT_BUFFERABLE); //禁止缓冲

	// 保护SRAM1,SRAM2和SRAM3
	MPU_Set_Protection(0x30000000,				   //基地址
					   MPU_REGION_SIZE_512KB,	   //长度
					   MPU_REGION_NUMBER2,		   // NUMER1
					   MPU_REGION_FULL_ACCESS,	   //全访问
					   MPU_ACCESS_NOT_SHAREABLE,   //允许共享
					   MPU_ACCESS_NOT_CACHEABLE,   //允许cache
					   MPU_ACCESS_NOT_BUFFERABLE); //禁止缓冲

	//    //保护SDRAM区域,共32M字节
	//    MPU_Set_Protection( 0XC0000000,                 //基地址
	//                        MPU_REGION_SIZE_32MB,       //长度
	//                        MPU_REGION_NUMBER2,         //NUMER2
	//                        MPU_REGION_FULL_ACCESS,     //全访问
	//                        MPU_ACCESS_NOT_SHAREABLE,   //禁止共享
	//                        MPU_ACCESS_CACHEABLE,       //允许cache
	//                        MPU_ACCESS_BUFFERABLE);     //允许缓冲
}
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106826
QQ
发表于 2022-6-25 00:07:52 | 显示全部楼层
xj_solid 发表于 2022-6-24 17:01
[mw_shl_code=c,false]u8 MPU_Set_Protection(u32 baseaddr, u32 size, u32 rnum, u8 ap, u8 sen, u8 cen,  ...

D1 RAM是不是AXI SRAM,是的话,将 MPU_TEX_LEVEL0配置为 MPU_TEX_LEVEL1,然后MPU_REGION_NUMBER从MPU_REGION_NUMBER0开始。

别的没什么问题了。另外将你的DMA和ADC配置也贴下吧

回复

使用道具 举报

6

主题

76

回帖

94

积分

初级会员

积分
94
发表于 2022-6-25 12:34:34 | 显示全部楼层
看来真是新手了。中断中做乘法,main中也不应该除以65536啊。这样的代码,出来敢用吗?
回复

使用道具 举报

4

主题

166

回帖

178

积分

初级会员

积分
178
发表于 2022-6-25 14:07:38 | 显示全部楼层
buxinshan 发表于 2022-6-25 12:34
看来真是新手了。中断中做乘法,main中也不应该除以65536啊。这样的代码,出来敢用吗?

他这中断中哪有乘法,不是几个常数吗,编译器自动处理了啊
回复

使用道具 举报

4

主题

10

回帖

22

积分

新手上路

积分
22
 楼主| 发表于 2022-6-25 15:50:20 | 显示全部楼层
buxinshan 发表于 2022-6-25 12:34
看来真是新手了。中断中做乘法,main中也不应该除以65536啊。这样的代码,出来敢用吗?

确实是新手, 希望指条明路吧
回复

使用道具 举报

4

主题

10

回帖

22

积分

新手上路

积分
22
 楼主| 发表于 2022-6-27 15:03:26 | 显示全部楼层
eric2013 发表于 2022-6-25 00:07
D1 RAM是不是AXI SRAM,是的话,将 MPU_TEX_LEVEL0配置为 MPU_TEX_LEVEL1,然后MPU_REGION_NUMBER从MPU_R ...

DMA 和 ADC的配置
dma2Handle.Instance = DMA2_Stream1;                            //数据流选择
    dma2Handle.Init.Request = DMA_REQUEST_ADC3;                    // ADC3->DMA
    dma2Handle.Init.Direction = DMA_PERIPH_TO_MEMORY;              //外设到存储器
    dma2Handle.Init.PeriphInc = DMA_PINC_DISABLE;                  //外设非增量模式
    dma2Handle.Init.MemInc = DMA_MINC_ENABLE;                      //存储器增量模式
    dma2Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:半字
    dma2Handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;    //存储器数据长度:半字
    dma2Handle.Init.Mode = DMA_CIRCULAR;                           //循环模式
    dma2Handle.Init.Priority = DMA_PRIORITY_LOW;                   //优先级低
    dma2Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;               //禁止FIFO
    dma2Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;       //禁止FIFO此位不起作用, 用于设置阈值
    dma2Handle.Init.MemBurst = DMA_MBURST_SINGLE;                  //禁止FIFO此位不起作用, 用于设置存储器突发
    dma2Handle.Init.PeriphBurst = DMA_PBURST_SINGLE;               //禁止FIFO此位不起作用, 用于外设突发
    HAL_DMA_Init(&dma2Handle);                                     //初始化

adc3Handle.Instance = ADC3;
    // adc3Handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // 4分频,ADCCLK=PER_CK/4=64/4=16MHZ
    adc3Handle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV6; //
    adc3Handle.Init.Resolution = ADC_RESOLUTION_16B;       // 16位模式
    adc3Handle.Init.ScanConvMode = ADC_SCAN_ENABLE;        //非扫描模式
    adc3Handle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;    //关闭EOC中断
    adc3Handle.Init.LowPowerAutoWait = DISABLE;            //自动低功耗关闭
    // adc3Handle.Init.ContinuousConvMode = DISABLE;       //关闭连续转换, 采用定时器触发转换
    adc3Handle.Init.ContinuousConvMode = DISABLE;
    adc3Handle.Init.NbrOfConversion = 7;             // 1个转换在规则序列中 也就是只转换规则序列1
    adc3Handle.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式
    // adc3Handle.Init.DiscontinuousConvMode = ENABLE;
    adc3Handle.Init.NbrOfDiscConversion = 1;
    adc3Handle.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T1_CC2; //定时器1的CC1触发
    // adc3Handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                //采用软件触发
    // adc3Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //上升沿触发
    adc3Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
    adc3Handle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR; // DMA循环模式接收ADC转换的数据
    adc3Handle.Init.BoostMode = DISABLE;                                        // ADC时钟未超过20MHz, 不使能boost
    adc3Handle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;                         //有新的数据直接覆盖掉旧数据
    adc3Handle.Init.OversamplingMode = DISABLE;                                 //过采样关闭

感觉是双缓冲的问题,因为我调试的时候 半传输完成和传输完成的条件都是True, 可能我的波形绘制只有一个缓冲的数据,所以不是连续的波形?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106826
QQ
发表于 2022-6-27 16:04:05 | 显示全部楼层
xj_solid 发表于 2022-6-27 15:03
DMA 和 ADC的配置
dma2Handle.Instance = DMA2_Stream1;                            //数据流选择
    ...

方便的话,贴个完整的,比如你的缓冲多大之类的,我都看不到。
回复

使用道具 举报

4

主题

10

回帖

22

积分

新手上路

积分
22
 楼主| 发表于 2022-6-27 16:42:48 | 显示全部楼层
完整的贴出来太乱了;
缓冲区定义:
#define V_SAMPLINGPOINT_NUMBER 1024
ALIGN_32BYTES(__attribute__((section(".RAM_D2")))) uint16_t adc3Data[V_SAMPLINGPOINT_NUMBER * 2 * 7];

启动DMA
HAL_ADC_Start_DMA(&adc3Handle, (uint32_t *)adc3Data, V_SAMPLINGPOINT_NUMBER * 2 * 7);

采样率 51200Hz
现在我发现采样点数设置为 1024, 波形就连续了,因为正好是1个周期, 可能是双缓冲没起作用, 丢了一半数据;
而采样是1个周期的情况下,就算丢了一半数据, 波形还是可以连上的;

我的main里面是这样处理的:
if (s_Dma2Flag == 1)
        {
            for (int i = 0; i < V_SAMPLINGPOINT_NUMBER; i++)
            {
                fftCalcBufferUa.dataBufferSingle[i] = (3.3 * adc3Data[i * 7 + 4] / 65536) * 330;
            }
            s_Dma2Flag = 0;
            samplingComplete = 1;
        }
        else if (s_Dma2Flag == 2)
        {
            for (int i = V_SAMPLINGPOINT_NUMBER; i < V_SAMPLINGPOINT_NUMBER * 2; i++)
            {
                fftCalcBufferUa.dataBufferSingle[i - V_SAMPLINGPOINT_NUMBER] = (3.3 * adc3Data[i * 7 + 4] / 65536) * 330;
            }
            s_Dma2Flag = 0;
            samplingComplete = 1;
        }

波形绘制时EMwin的Graph控件
回复

使用道具 举报

4

主题

10

回帖

22

积分

新手上路

积分
22
 楼主| 发表于 2022-6-27 16:47:59 | 显示全部楼层
另外我一直再说 调试的时候半传输完成和传输完成的条件都是True; 在main里面的 s_Dma2Flag == 1 和 s_Dma2Flag == 2条件也都是随机成立;
调试的时候是不是观察不到两个buffer的交替工作啊?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106826
QQ
发表于 2022-6-27 16:53:54 | 显示全部楼层
xj_solid 发表于 2022-6-27 16:42
完整的贴出来太乱了;
缓冲区定义:
#define V_SAMPLINGPOINT_NUMBER 1024

你这个波形绘制代码怎么保证你当前正在绘制的数据,没有被新数据覆盖,我感觉你这个地方没有处理好。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-6 07:19 , Processed in 0.211633 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表