基于GD32F4平台的多/单通道ADC采样+DMA中断

介绍

基于GD32F4平台的多/单通道ADC采样板级支持包,GD其他系列平台需要修改部分标准库函数。 特点:接口清晰,配置方便,支持1~16任意通道数量,自由决定采样顺序,采用DMA中断通知用户采样完成。

使用说明
  1. 在main.c实现ADC_ChannelConfig_T 通道配置结构体,结构体成员格式为“{{通道所属GPIO分组时钟,通道所属GPIO分组, 通道所属GPIO引脚}, 通道号}”,调用BSP_ADC_Init(...)进行初始化即可对ADC、DMA的全部初始化。
  2. main.h中配置ADC、DMA相关宏
  3. 采样结果存放在bsp_adc.c中实现的uint16_t USER_ADC_DMA_DATA_BUFF[],用户可以根据实际应用,对其引用
  4. 用户可自定义DMA传输完成中断回调函数

环境:Keil-MDK   ARMCC v5
MCU: Cortex-M4  GD32F470ZITX

测试效果

7通道采样,串口打印

部分源码

main.c

#include "main.h"
#include "system.h"
#include "bsp_adc.h"

/*********************************************************************************************************
*		ADC通道配置说明:
*	1.将此结构体的实现,放在使用ADC模块的main.c中,初始化调用bsp_adc.c中的BSP_ADC_Init,将根据此结构体的配置进行ADC通道的配置
*	2.USER_ADC_CHANNEL_AMOUNT 由main.h中的ADC配置宏决定
*	3.结构体成员格式为“{{通道所属GPIO分组时钟,通道所属GPIO分组, 通道所属GPIO引脚}, 通道号}”
*	4.将根据成员的顺序,决定采样的顺序、DMA数组中某通道采样数据的存放位置。
*
*	例如以下配置:用于传输ADC数据的DMA产生传输完成中断后,USER_ADC_DMA_DATA_BUFF[0]存放ADC_CHANNEL_1的采样数据,
*               USER_ADC_DMA_DATA_BUFF[1]存放ADC_CHANNEL_2的采样数据,USER_ADC_DMA_DATA_BUFF[2]存放ADC_CHANNEL_3的采样数据,
*               以此类推,用户根据自己的实际应用进行获取
*
*	注意:USER_ADC_CHANNEL_AMOUNT数量要与实际配置的成员数一致。
*   注意:ADC通道所在引脚浮空,会导致被干扰,接入被采样模拟信号则正常。已知常规序列4号通道会影响5号。
**********************************************************************************************************/
ADC_ChannelConfig_T g_ADCChannelConfig[USER_ADC_CHANNEL_AMOUNT] = 
{
    {{RCU_GPIOA,GPIOA, GPIO_PIN_0}, ADC_CHANNEL_0},//采样顺序:1  采样结果存放位置USER_ADC_DMA_DATA_BUFF[0]
    {{RCU_GPIOB,GPIOB, GPIO_PIN_0}, ADC_CHANNEL_8},//采样顺序:2  采样结果存放位置USER_ADC_DMA_DATA_BUFF[1]
    {{RCU_GPIOB,GPIOB, GPIO_PIN_1}, ADC_CHANNEL_9},//采样顺序:3  采样结果存放位置USER_ADC_DMA_DATA_BUFF[2]
	{{RCU_GPIOA,GPIOA, GPIO_PIN_7}, ADC_CHANNEL_7},//采样顺序:4  采样结果存放位置USER_ADC_DMA_DATA_BUFF[3]
	{{RCU_GPIOA,GPIOA, GPIO_PIN_4}, ADC_CHANNEL_4},//采样顺序:5  采样结果存放位置USER_ADC_DMA_DATA_BUFF[4]
	{{RCU_GPIOC,GPIOC, GPIO_PIN_3}, ADC_CHANNEL_13},//采样顺序:6  采样结果存放位置USER_ADC_DMA_DATA_BUFF[5]
	{{RCU_GPIOC,GPIOC, GPIO_PIN_2}, ADC_CHANNEL_12},//采样顺序:7  采样结果存放位置USER_ADC_DMA_DATA_BUFF[6]
    // 根据需要添加更多通道
};

uint8_t g_ADCDataReady=0;//DMA传输完成ADC数据标志位
extern uint16_t USER_ADC_DMA_DATA_BUFF;//bsp_adc.c中定义的ADC数据接收数组
int main(void)
{
	/*系统嘀嗒定时器初始化*/
	systick_config();

	/*调试串口1初始化*/
	SYS_COMInit();

	BSP_ADC_Init(g_ADCChannelConfig);
    while(1)
	{
		if(SET == g_ADCDataReady)
		{
			g_ADCDataReady = RESET;
			printf("================================\r\n");
			for(uint8_t i=0;i<USER_ADC_CHANNEL_AMOUNT;i++)
			{
				printf("Channle[%d]=%d\r\n",i,BSP_ADCDataAcquire(i));
			}
			printf("\r\n");
		}
		delay_ms(500);

    }
}

/**
* @brief 重定向
* @param 字符串
* @return 无
*/
int fputc(int ch, FILE *f) 
{	  
	/*发送字符到USART1*/
	USART1_SendByte(ch);
	while(RESET == usart_flag_get(USART1, USART_FLAG_TBE));

	/*发送字符到ITM*/
	ITM_SendChar(ch);
    return ch ;
}


/*
*****************************************************************************
*  函数名 : DMA_ADCIRQHandlerCallback
*  功能说明: DMA_ADC中断服务回调函数
*  形   参 : 无
*  返回值 :  无
*****************************************************************************
*/
void DMA_ADCIRQHandlerCallback(void)
{
	if(SET == dma_interrupt_flag_get(DMA1,USER_DMA_ADC_CHANNEL,DMA_INTC_FTFIFC))
	{
		dma_interrupt_flag_clear(DMA1,USER_DMA_ADC_CHANNEL,DMA_INTC_FTFIFC);
		g_ADCDataReady = SET;
	}
}

main.h 工程配置头文件

#ifndef MAIN_H
#define MAIN_H

/*头文件区*/
/*配置宏定义区*/
/*ADC配置 BEGIN*/
#define USER_ADCx                        ADC0                     //ADC外设:ADC0 ADC1 ADC2
#define USER_ADCx_RCU                    RCU_ADC0                 //ADC外设时钟:RCU_ADC0 RCU_ADC1 RCU_ADC2
#define USER_ADC_CHANNEL_AMOUNT          (7)                      //通道数量:1~16
#define USER_ADC_CLK_METHOD              ADC_ADCCK_PCLK2_DIV8     //配置ADC时钟系数,ADC 的时钟最大不能超过 14M,详见"gd32f4xx_adc.h"
#define USER_ADC_SAMPLETIME_PERIOD       ADC_SAMPLETIME_480        //采样周期:ADC_SAMPLETIME_3 ADC_SAMPLETIME_15 ADC_SAMPLETIME_28 ADC_SAMPLETIME_56 
															      //         ADC_SAMPLETIME_84 ADC_SAMPLETIME_112 ADC_SAMPLETIME_144 ADC_SAMPLETIME_480
															   
#define USER_DMA_ADC_CHANNEL             DMA_CH0                  //DMA通道:ADC0:DMA_CH0 DMA_CH4 
                                                                  //         ADC1:DMA_CH2 DMA_CH3 
															      //         ADC2:DMA_CH0 DMA_CH3
															   
#define USER_DMA_ADC_SUBPERI             DMA_SUBPERI0             //DMA外设源:ADC0:DMA_SUBPERI0 
                                                                  //          ADC1:DMA_SUBPERI1 
															      //          ADC2:DMA_SUBPERI2
																  
#define DMA_ADC_IRQHandler               DMA1_Channel0_IRQHandler //DMA1_Channel0_IRQHandler         
                                                                  //DMA1_Channel1_IRQHandler       
                                                                  //DMA1_Channel2_IRQHandler 
				                                                  //DMA1_Channel3_IRQHandler 
                                                                  //DMA1_Channel4_IRQHandler       
																  
#define DMA_ADC_IRQn                     DMA1_Channel0_IRQn       //DMA0_Channel0_IRQn         
                                                                  //DMA0_Channel1_IRQn       
                                                                  //DMA0_Channel2_IRQn 
				                                                  //DMA0_Channel3_IRQn 
                                                                  //DMA0_Channel4_IRQn       									 
#define DMA_ADC_PRIORITY_PRE             0                        //抢占优先级
#define DMA_ADC_PRIORITY_SUB             1                        //子优先级
/*ADC配置 END*/

#endif 

bsp_adc.c

#include "bsp_adc.h"

/*宏定义区*/

/*外部引用区*/
extern ADC_ChannelConfig_T adcChannels[];//由使用该ADC模块的main.c实现

/*全局声明区*/
uint16_t USER_ADC_DMA_DATA_BUFF[16] = {0};

/*静态声明区*/
static void ADC_RCUConfig(void);
static void ADC_GPIOAndChannelConfig(rcu_periph_enum _rcu,uint32_t _channle_gpio,uint32_t _channle_pin,uint8_t _channel,uint8_t _rank);
static void ADC_DMAConfig(void);
static void ADC_Config(void);


/**
* @brief ADC初始化
* @param ADC_ChannelConfig_T配置结构体,包含引脚、ADC通道信息
* @return 无
*/
void BSP_ADC_Init(ADC_ChannelConfig_T _adcChannels[])
{
	/* ADC deinit */
	adc_deinit();
	ADC_RCUConfig();//ADC时钟配置
	
	for(uint8_t i=0;i<USER_ADC_CHANNEL_AMOUNT;i++)//ADC通道(含引脚)配置
	{
		ADC_GPIOAndChannelConfig(_adcChannels[i].ADC_GPIOConfig.Channle_RCU , _adcChannels[i].ADC_GPIOConfig.Channle_GPIOx , _adcChannels[i].ADC_GPIOConfig.Channle_Pinx,_adcChannels[i].Channel,i);
	}
	
	ADC_DMAConfig();//ADC-DMA配置
	ADC_Config();//ADC配置
}

/**
* @brief ADC时钟配置(由宏决定)
* @param 无
* @return 无
*/
static void ADC_RCUConfig(void)
{
    /* enable ADC clock */
    rcu_periph_clock_enable(USER_ADCx_RCU);
    /* config ADC clock */
    adc_clock_config(USER_ADC_CLK_METHOD);
}

/**
* @brief ADC引脚配置(由参数决定)
* @param 无
* @return 无
*/
static void ADC_GPIOAndChannelConfig(rcu_periph_enum _rcu,uint32_t _channle_gpio,uint32_t _channle_pin,uint8_t _channel,uint8_t _rank)
{
	rcu_periph_clock_enable(_rcu);
	gpio_mode_set(_channle_gpio,GPIO_MODE_ANALOG,GPIO_PUPD_NONE,_channle_pin);
	adc_routine_channel_config(USER_ADCx,_rank,_channel,USER_ADC_SAMPLETIME_PERIOD);
}

/**
* @brief ADC-DMA配置(由宏决定)
* @param 无
* @return 无
*/
static void ADC_DMAConfig(void)
{
    /* ADC_DMA_channel configuration */
    dma_single_data_parameter_struct dma_single_data_parameter; //这里使用单数据模式,每次只传输一个数据单元,因为总数据量不大,且数据个数不固定

    /* peripheral clock enable */
    rcu_periph_clock_enable(RCU_DMA1);//ADC只能用DMA1
    /* ADC DMA_channel configuration */
    dma_deinit(DMA1, USER_DMA_ADC_CHANNEL);

    /* initialize DMA single data mode */
    dma_single_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(USER_ADCx));//配置外设数据源地址,宏决定ADCx
	dma_single_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;//关闭外设增量,只需要每次对该数据源寄存器访问
	
	dma_single_data_parameter.memory0_addr = (uint32_t)USER_ADC_DMA_DATA_BUFF;//先写死,然后外部用的话需要extern
	dma_single_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;//开启内存增量,以存放不同通道的ADC数据

	dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;//ADC数据为16位的寄存器,数据目的地为16位
	
	dma_single_data_parameter.circular_mode = DMA_CIRCULAR_MODE_ENABLE;//循环模式,由ADC通知DMA进行
	dma_single_data_parameter.direction = DMA_PERIPH_TO_MEMORY;//ADC外设到RAM全局变量
	dma_single_data_parameter.number = USER_ADC_CHANNEL_AMOUNT;//宏决定传输个数
	dma_single_data_parameter.priority = DMA_PRIORITY_ULTRA_HIGH;//优先级设为超高,可改用宏
	dma_single_data_mode_init(DMA1, USER_DMA_ADC_CHANNEL, &dma_single_data_parameter);
	dma_channel_subperipheral_select(DMA1, USER_DMA_ADC_CHANNEL, USER_DMA_ADC_SUBPERI);
	
    /* enable DMA circulation mode */
    dma_circulation_enable(DMA1, USER_DMA_ADC_CHANNEL);

    /* enable DMA channel */
    dma_channel_enable(DMA1, USER_DMA_ADC_CHANNEL);
	 /* enable DMA it */
	dma_interrupt_enable(DMA1,USER_DMA_ADC_CHANNEL,DMA_INT_FTF);//DMA传输完成标志位
	nvic_irq_enable(DMA_ADC_IRQn, DMA_ADC_PRIORITY_PRE, DMA_ADC_PRIORITY_SUB);
}

/**
* @brief ADC配置(由宏决定)
* @param 无
* @return 无
*/
static void ADC_Config(void)
{	
	/* ADC param config */
	adc_special_function_config(USER_ADCx,ADC_SCAN_MODE,ENABLE);//扫描模式:扫描所有通道 
	adc_special_function_config(USER_ADCx,ADC_CONTINUOUS_MODE,ENABLE);//连续转换模式:采样完成后继续重复采样
	adc_data_alignment_config(USER_ADCx,ADC_DATAALIGN_RIGHT);//右对齐	
	adc_resolution_config(USER_ADCx,ADC_RESOLUTION_12B);//分辨率
	adc_channel_length_config(USER_ADCx,ADC_ROUTINE_CHANNEL,USER_ADC_CHANNEL_AMOUNT);//ADCx设置为规则组,使用USER_ADC_CHANNEL_AMOUNT个通道
	
	/* ADC trigger config */
    adc_external_trigger_config(ADC0, ADC_ROUTINE_CHANNEL, EXTERNAL_TRIGGER_DISABLE);
	
	/* ADC dma config */
	adc_dma_request_after_last_enable(USER_ADCx);
	adc_dma_mode_enable(USER_ADCx);
	
	/* ADC enable */
	adc_enable(USER_ADCx);
	delay_ms(1);//等待使能完成
	adc_calibration_enable(USER_ADCx);//ADCx校准复位
	
	adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL); //ADC软件触发使能
}

/**
* @brief 根据转换序列的序列号,获取转换序列中的ADC测量值
* @param 配置ADC通道结构体中的成员序号
* @return 测量并转换后的值
*/
uint16_t BSP_ADCDataAcquire(uint8_t _index)
{
	return USER_ADC_DMA_DATA_BUFF[_index];
}

/*
*****************************************************************************
*  函数名 : DMA_ADC_IRQHandler
*  功能说明: DMA ADC数据传输中断服务函数
*  形   参 : 无
*  返回值 :  无
*****************************************************************************
*/
void DMA_ADC_IRQHandler(void)
{
	DMA_ADCIRQHandlerCallback();
}

/*
*****************************************************************************
*  函数名 : DMA_ADCIRQHandlerCallback
*  功能说明: DMA_ADC中断服务回调函数
*  形   参 : 无
*  返回值 :  无
*****************************************************************************
*/
__weak void DMA_ADCIRQHandlerCallback(void)
{

}

bsp_adc.h

#ifndef BSP_DAC_H
#define BSP_DAC_H

/*包含区*/
#include "system.h"
#include "main.h"

/*宏定义区*/

/*编译提示区*/
/*ADC外设*/
#ifndef USER_ADCx
    #error "USER_ADCx is not defined!"
#endif
/*ADC外设时钟*/
#ifndef USER_ADCx_RCU
    #error "USER_ADCx_RCU is not defined!"
#endif
/*通道数量*/
#ifndef USER_ADC_CHANNEL_AMOUNT
    #error "USER_ADC_CHANNEL_AMOUNT is not defined!"
#endif
/*ADC时钟系数*/
#ifndef USER_ADC_CLK_METHOD
    #error "USER_ADC_CLK_METHOD is not defined!"
#endif
/*ADC采样周期*/
#ifndef USER_ADC_SAMPLETIME_PERIOD
    #error "USER_ADC_SAMPLETIME_PERIOD is not defined!"
#endif
/*DMA通道*/
#ifndef USER_DMA_ADC_CHANNEL
    #error "USER_DMA_ADC_CHANNEL is not defined!"
#endif
/*DMA外设源*/
#ifndef USER_DMA_ADC_SUBPERI
    #error "USER_DMA_ADC_SUBPERI is not defined!"
#endif

/*DMA_ADC中断服务函数*/
#ifndef DMA_ADC_IRQHandler
    #error "DMA_ADC_IRQHandler is not defined!"
#endif

/*DMA_ADC中断源*/
#ifndef DMA_ADC_IRQn
    #error "DMA_ADC_IRQn is not defined!"
#endif

/*DMA_ADC 抢占优先级*/
#ifndef DMA_ADC_PRIORITY_PRE
    #error "DMA_ADC_PRIORITY_PRE is not defined!"
#endif

/*DMA_ADC 子优先级*/
#ifndef DMA_ADC_PRIORITY_SUB
    #error "DMA_ADC_PRIORITY_SUB is not defined!"
#endif

/*ADC通道引脚配置结构体*/
typedef struct tag_ADC_GPIOConfig_T
{
	rcu_periph_enum Channle_RCU;//GPIO时钟
	uint32_t Channle_GPIOx;// GPIO分组
    uint32_t Channle_Pinx; // GPIO引脚号
} ADC_GPIOConfig_T;

/*ADC通道配置结构体*/
typedef struct tag_ADC_ChannelConfig_T
{
    ADC_GPIOConfig_T ADC_GPIOConfig;//通道引脚配置
    uint8_t Channel;// ADC通道号
} ADC_ChannelConfig_T;


/*声明区*/
void BSP_ADC_Init(ADC_ChannelConfig_T _adcChannels[]);
uint16_t BSP_ADCDataAcquire(uint8_t _index);
__weak void DMA_ADCIRQHandlerCallback(void);
#endif /* BSP_DAC_H */

完整工程已上传到Gitee,欢迎有相关问题的朋友评论区留言讨论。

Gitee链接:

GD32-Multi or Single ADC Channel with DMA: 基于GD32F4平台的多/单通道ADC采样板级支持包,GD其他系列平台需要修改部分标准库函数。特点:接口清晰,配置方便,支持1~16任意通道数量,自由决定采样顺序,采用DMA中断通知用户采样完成。

GD32F470是一款基于ARM Cortex-M4内核的微控制器,支持DMA(直接内存访问)功能。DMA可以在不干扰CPU的情况下,直接将数据从外设传输到内存或者从内存传输到外设。这样可以大大提高数据传输的效率,减少CPU的负担。下面是GD32F470 DMA的一些介绍和示例代码: 1. GD32F470支持多个DMA通道,每个通道可以配置不同的外设和内存地址,以及传输数据的长度和方向等参数。 2. GD32F470的DMA控制器支持循环传输和非循环传输两种模式,可以根据具体应用场景选择不同的模式。 3. GD32F470的DMA控制器还支持中断和轮询两种传输方式,可以根据具体应用场景选择不同的方式。 下面是一个GD32F470 ADC2+DMA的配置代码示例: ```c // 定义DMA传输数据的长度 #define BUFFER_SIZE 16 // 定义DMA传输的源地址和目的地址 uint16_t adc_value[BUFFER_SIZE]; uint32_t dma_dest_addr = (uint32_t)adc_value; uint32_t dma_src_addr = (uint32_t)&ADC_RDATA(ADC2); // 配置DMA通道0 dma_parameter_struct dma_init_struct; dma_deinit(DMA1, DMA_CH0); dma_struct_para_init(&dma_init_struct); dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY; dma_init_struct.memory_addr = dma_dest_addr; dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT; dma_init_struct.number = BUFFER_SIZE; dma_init_struct.periph_addr = dma_src_addr; dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; dma_init(DMA1, DMA_CH0, &dma_init_struct); // 配置ADC2 adc_parameter_struct adc_init_struct; adc_struct_para_init(&adc_init_struct); adc_init_struct.continuous_mode = ENABLE; adc_init_struct.data_alignment = ADC_DATAALIGN_RIGHT; adc_init_struct.external_trigger = ADC_EXTTRIG_REGULAR_NONE; adc_init_struct.scan_mode = ENABLE; adc_init_struct.channel_number = 1; adc_init(ADC2, &adc_init_struct); // 配置ADC2的DMA传输 adc_dma_mode_enable(ADC2); adc_dma_request_after_last_enable(ADC2); // 启动DMA传输 dma_channel_enable(DMA1, DMA_CH0); adc_software_trigger_enable(ADC2, ADC_REGULAR_CHANNEL); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值