查看: 1653  |  回复: 1
立创梁山派GD32F470ZGT6--使用DMA实现多通道ADC采集

主题

回复
发表于2023-06-15 19:43:22 | 只看该作者
1# 电梯直达
    测试发现ADC的引脚配置为浮空之后,如果没有接入东西,其读取的ADC值是不确定的,所以在测试的时候,一定要把对应的电压接入ADC引脚,测量的值才是准确的。

    如果单独使用ADC多通道,会有通道错乱的问题,实际查看数据手册,发现ADC采集完的规则组数据只保存在一个16位的寄存器中,如果没有及时取出数据,就会被后面的数据覆盖,导致数据错误,或者数据的通道错乱,比如通道1的数据跑到通道2。

 

所以使用直接使用DMA将采集完成的数据给搬运出去,这样就没有数据错乱问题。

上代码。

adc.c

#include "adc.h"

/***********************

采样次数  30

ADC通道    4

***********************/

uint16_t gt_adc_val[30][4];  //DMA缓冲区

// ADC Init

void ADC_DMA_Init(void)

{

   /* DMA初始化功能结构体定义 */

   dma_single_data_parameter_struct dma_single_data_parameter;

/* 使能GPIOC组时钟 */

   rcu_periph_clock_enable(RCU_GPIOC);

/* 使能ADC0时钟 */

   rcu_periph_clock_enable(RCU_ADC0);

/* 使能DMA1时钟 */

rcu_periph_clock_enable(RCU_DMA1);

/* 配置ADC时钟 */

   adc_clock_config(ADC_ADCCK_PCLK2_DIV4);

/* 配置PC1 PC2 PC3 PC4 为浮空模拟输入模式 */

   gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_1); // PC1 : ADC012_IN11

   gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_2); // PC2 : ADC012_IN12

   gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_3); // PC3 : ADC012_IN13

   gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_4); // PC4 : ADC012_IN14

/* 配置ADC为独立模式 */

   adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);

   /* 使能连续转换模式 */

   adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);

   /* 使能扫描模式 */

   adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);

/* 数据右对齐 */        

   adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);

   /* ADC0设置为规则组  一共使用2个通道 */  

   adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 4);

   /* ADC规则通道配置:ADC0的通道11,12,13,14的扫描顺序分别为0,1,2,3;采样时间:15个周期 */  

/* DMA开启之后 gt_adc_val[x][0] = PC1的数据   gt_adc_val[x][3] = PC4的数据  x的范围0-29 */

   adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_11, ADC_SAMPLETIME_15);//PC1

   adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_12, ADC_SAMPLETIME_15);//PC2

adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_13, ADC_SAMPLETIME_15);//PC3

adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_14, ADC_SAMPLETIME_15);//PC4

/* ADC0设置为12位分辨率 */  

adc_resolution_config(ADC0, ADC_RESOLUTION_12B);

/* ADC外部触发禁用, 即只能使用软件触发 */  

   adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_DISABLE);

   /* 使能规则组通道每转换完成一个就发送一次DMA请求 */  

   adc_dma_request_after_last_enable(ADC0);  

/* 使能DMA请求 */  

adc_dma_mode_enable(ADC0);

/* 使能DMA */  

adc_enable(ADC0);

/* 等待ADC稳定 */  

delay_1ms(1);

/* 开启ADC自校准 */

   adc_calibration_enable(ADC0);

   /* 清除 DMA通道0 之前配置 */

   dma_deinit(DMA1, DMA_CH0);

/* DMA初始化配置 */

dma_single_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0)); /设置DMA传输的外设地址为ADC0基地址

dma_single_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; //关闭外设地址自增

dma_single_data_parameter.memory0_addr = (uint32_t)(gt_adc_val); //设置DMA传输的内存地址为 gt_adc_val数组

dma_single_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //开启内存地址自增(因为不止一个通道)

dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_16BIT; //传输的数据位 为 16位

dma_single_data_parameter.direction = DMA_PERIPH_TO_MEMORY; //DMA传输方向为 外设往内存

dma_single_data_parameter.number = 4*30; //传输的数据长度为:4个通道 * 每个通道采集30次

dma_single_data_parameter.priority = DMA_PRIORITY_HIGH; //设置高优先级

dma_single_data_mode_init(DMA1, DMA_CH0, &dma_single_data_parameter); //将配置保存至DMA1的通道0

/* DMA通道外设选择 */

/* 数据手册的195页根据PERIEN[2:0]值确定第三个参数,例是100 则为DMA_SUBPERI4   例是010 则为DMA_SUBPERI2 */

/* 我们是ADC0功能,PERIEN[2:0]值为000,故为DMA_SUBPERI0 */

dma_channel_subperipheral_select(DMA1, DMA_CH0, DMA_SUBPERI0);

/* 使能DMA1通道0循环模式 */

dma_circulation_enable(DMA1, DMA_CH0);

/* 启动DMA1的通道0功能 */

dma_channel_enable(DMA1, DMA_CH0);

/* 开启软件触发ADC转换 */

adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);

}

//对DMA保存的数据进行平均值计算后输出

//传入参数:CHx 第几个扫描的数据

//         根据前面的配置得知:PC1为0 PC2为1 PC3为2 PC4为3

//返回数据:对应扫描的ADC值

unsigned int Get_Adc_Dma_Value(char CHx)

{

unsigned char i = 0;

unsigned int AdcValue = 0;

   /* 因为采集30次,故循环30次 */

for(i=0;i<30;i++)

{

       /*    累加    */

AdcValue+=gt_adc_val[i][CHx];

}

   /* 求平均值 */

AdcValue=AdcValue/30;

return AdcValue;

}

adc.h

#ifndef _ADC_h_

#define _ADC_h_

#include "gd32f4xx.h"

#include "usart0.h"

#include "stdio.h"

#include "systick.h"


extern uint16_t gt_adc_val[30][4];  //DMA缓冲区  


void ADC_DMA_Init(void);

unsigned int Get_Adc_Dma_Value(char CHx);

#endif


主函数

见帖子一楼



实际效果


对PC1和PC3接入3.3V,因为ADC是12位的,所以最大采集数为4096,实际采集也接近了4096。

对PC2和PC4接入GND,实际采集也接近0。注意!引脚不能浮空进行采集!不然数据是不确定的!


  



代码链接:https://pan.baidu.com/s/15G_t-jW82Ms6FLD_fTCBqw?pwd=1234

提取码:1234



主题

回复
发表于2023-06-15 19:59:36   |  只看该作者
2#

main.c

#include "gd32f4xx.h"
#include "systick.h"
#include "usart0.h"
#include "stdio.h"
#include "adc.h"
 
int main(void)
{
	unsigned char temp_buff[200]; 
	unsigned int  show_buff[4];
	
	nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);  // 优先级分组
	systick_config();	//系统滴答定时器   定时1MS
	USART1_Init();
	ADC_DMA_Init();         
	USART0_send_String((unsigned char *)"--开始--");
     while(1)  
	{        
		/*	获取数据		*/
		show_buff[0] = Get_Adc_Dma_Value(0);	//根据扫描顺序得知数组[0] = PC1的数据
		show_buff[1] = Get_Adc_Dma_Value(1);	//根据扫描顺序得知数组[1] = PC2的数据
		show_buff[2] = Get_Adc_Dma_Value(2);	//根据扫描顺序得知数组[2] = PC3的数据
		show_buff[3] = Get_Adc_Dma_Value(3);	//根据扫描顺序得知数组[3] = PC4的数据
		
		/*	串口显示数据		*/		
		sprintf((char *)temp_buff, "PC1=%d\r\n", show_buff[0]);						
		USART0_send_String(temp_buff);
		sprintf((char *)temp_buff, "PC2=%d\r\n", show_buff[1]);						
		USART0_send_String(temp_buff);
		sprintf((char *)temp_buff, "PC3=%d\r\n", show_buff[2]);						
		USART0_send_String(temp_buff);
		sprintf((char *)temp_buff, "PC4=%d\r\n", show_buff[3]);						
		USART0_send_String(temp_buff);		
		
		/*	串口显示刷新太快,加个延时    */
		delay_1ms(1000);
	}    
}





 




主题

回复
  • 温馨提示: 标题不合格、重复发帖、发布广告贴,将会被删除帖子或禁止发言。 详情请参考: 社区发帖规则
  • 您当前输入了 0 个文字。还可以输入 8000 个文字。 已添加复制上传图片功能,该功能目前仅支持chrome和火狐

禁言/删除

X
请选择禁言时长:
是否清除头像:
禁言/删除备注:
昵 称:
 
温馨提示:昵称只能设置一次,设置后无法修改。
只支持中文、英文和数字。

举报

X
请选择举报类型:
请输入详细内容:

顶部