介绍
基于GD32F4平台的多/单通道ADC采样板级支持包,GD其他系列平台需要修改部分标准库函数。 特点:接口清晰,配置方便,支持1~16任意通道数量,自由决定采样顺序,采用DMA中断通知用户采样完成。
使用说明
- 在main.c实现ADC_ChannelConfig_T 通道配置结构体,结构体成员格式为“{{通道所属GPIO分组时钟,通道所属GPIO分组, 通道所属GPIO引脚}, 通道号}”,调用BSP_ADC_Init(...)进行初始化即可对ADC、DMA的全部初始化。
- main.h中配置ADC、DMA相关宏
- 采样结果存放在bsp_adc.c中实现的uint16_t USER_ADC_DMA_DATA_BUFF[],用户可以根据实际应用,对其引用
- 用户可自定义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链接: