GD32入门到实战26--ADC(单/多通道+DMA+常见十种算法)

扫描模式等所有通道转换完成后才会产生eoc标志,每个通道转换完成会把转换值给数据寄存器,但是只有一个数据寄存器,所以我们要用到DMA对数据进行搬运

规则组类似于主流程,注入组类似中断,注入组打断规则组优先执行

信号源的输出阻抗对ADC的采样有影响表格为不同采样周期Ts​(以ADC时钟周期计)对应的时间ts​(以微秒计)和最大输入电阻RAIN max​(以千欧计),如1.5个ADC采样周期的最大输入电阻为0.15kΩ,大于这个阻值采样的会值不准确

ADC单通道转换

我们用可调电阻PC2引脚(ADC0)

我们创建Vers_drv.c

#include <stdint.h>
#include <stdio.h>
#include "gd32f30x.h"
#include "delay.h"

static void GpioInit(void)
{
    rcu_periph_clock_enable(RCU_GPIOC);
    gpio_init(GPIOC,GPIO_MODE_AIN,GPIO_OSPEED_10MHZ,GPIO_PIN_2);
}

static void AdcInit(void)
{
    
    /*使能时钟;*/
    rcu_periph_clock_enable(RCU_ADC0);
    /*设置分频系数;*/
    rcu_adc_clock_config(RCU_CKADC_CKAHB_DIV6);//6分频 120Mhz / 6 = 20Mhz
    /*设置独立模式:*/
    adc_mode_config(ADC_MODE_FREE);
    /*设置单次模式;*/
    adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,ENABLE);//连续转换
    /*设置数据对齐;*/
    adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);
    /*设置转换通道个数;*/
    adc_channel_length_config(ADC0,ADC_REGULAR_CHANNEL,1);
    /*设置转换哪一个通道以及所处序列位置:*/
    adc_regular_channel_config(ADC0 , 0, ADC_CHANNEL_12, ADC_SAMPLETIME_239POINT5);//PC2对应通道12,放在序列寄存器的0列中,239.5个周期
    /*设置选择哪一个外部触发源;*/
    adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);//软件触发
    /*使能外部触发;*/
    adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL,ENABLE);
    /*使能ADC:*/
    adc_enable(ADC0);
    /*内部校准;*/
    DelayNus(50);
    adc_calibration_enable(ADC0);
    adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);//开启转换
}

/**
***********************************************************
* @brief  ADC硬件初始化          
* @param  
* @return 
***********************************************************
*/
void VresDvrInit(void)
{
    GpioInit();
    AdcInit();
}
/**
***********************************************************
* @brief  ADC读取函数         
* @param  
* @return adc值
***********************************************************
*/
uint16_t GetAdcVal(void)
{
   // while(!adc_flag_get(ADC0, ADC_FLAG_EOC));//等待转换完成
    return adc_regular_data_read(ADC0);
}
/**
***********************************************************
* @brief  ADC测试函数         
* @param  
* @return
***********************************************************
*/
void VresDrvTest()
{
    uint16_t AdcVal = GetAdcVal();
    float Voltage = (float)AdcVal / 4095 * 3.3f;//转换成电压值
    printf("AdcVal = %d, Voltage = %.1f\n",AdcVal,Voltage);
    DelayNms(1000);
}

.h

#ifndef _VRES_DRV_H_
#define _VRES_DRV_H_

/**
***********************************************************
* @brief  ADC测试函数         
* @param  
* @return
***********************************************************
*/
void VresDrvTest();
/**
***********************************************************
* @brief  ADC读取函数         
* @param  
* @return adc值
***********************************************************
*/
uint16_t GetAdcVal(void);

/**
***********************************************************
* @brief  ADC硬件初始化          
* @param  
* @return 
***********************************************************
*/
void VresDvrInit(void);


#endif

main

#include <stdint.h>
#include <stdio.h>
#include "led_drv.h"
#include "key_drv.h"
#include "systick.h"
#include "usart_drv.h"
#include "delay.h"
#include "usb2com_app.h"
#include "hmi_app.h"
#include "rtc_drv.h"
#include "vres_drv.h"
typedef struct
{
	uint8_t run;                // 调度标志,1:调度,0:挂起
	uint16_t timCount;          // 时间片计数值
	uint16_t timRload;          // 时间片重载值
	void (*pTaskFuncCb)(void);  // 函数指针变量,用来保存业务功能模块函数地址
} TaskComps_t;/*任务调度结构体*/

static TaskComps_t g_taskComps[] =  /*任务调度结构体数组,存放各个业务功能模块调度参数*/
{/*填入各个业务功能模块*/
	{0, 1000, 1000,   HmiTask},
	{0, 200, 200, usartTask},
	/* 添加业务功能模块 */
};

#define TASK_NUM_MAX   (sizeof(g_taskComps) / sizeof(g_taskComps[0]))
/*用宏定义计算结构体数组的个数*///sizeof(g_taskComps):计算整个数组 g_taskComps 的大小(以字节为单位)。
//sizeof(g_taskComps[0]):计算数组中单个元素的大小(以字节为单位)。
//通过整个数组的大小除以单个元素的大小,得到数组中元素的数量



/*
******************************************
* @brief 任务调度函数(判断所有业务模块的标志位)
* @param 
* @return
********************************************
*/
static void TaskHandler(void)
{
	for (uint8_t i = 0; i < TASK_NUM_MAX; i++)
	{
		if (g_taskComps[i].run)                  // 判断时间片标志
		{
			g_taskComps[i].run = 0;              // 标志清零
			g_taskComps[i].pTaskFuncCb();        // 执行调度业务功能模块
		}
	}
}

/*
******************************************
* @brief 时间片递减函数 1ms-1时间片
* @param 
* @return
********************************************
*/
static void TaskScheduleCb(void)
{
	for (uint8_t i = 0; i < TASK_NUM_MAX; i++)
	{
		if (g_taskComps[i].timCount)
		{
			g_taskComps[i].timCount--;
			if (g_taskComps[i].timCount == 0)
			{
				g_taskComps[i].run = 1;
				g_taskComps[i].timCount = g_taskComps[i].timRload;
			}
		}
	}
}

static void DrvInit(void)
{
	SystickInit();
	LedDrvInit();
	KeyDrvInit();
	DelayInit();
	UsartDrv_Init();
    RtcDrvInit();
    VresDvrInit();
}
static void AppInit(void)
{
	Usb2ComAppInit();
	TaskScheduleCbReg(TaskScheduleCb);
}

#include <time.h>
int main(void)
{	
	DrvInit();
	AppInit();

    RtcTime_t rtcTime = {2025 , 9, 1, 19, 18, 0};
    SetRtcTime(&rtcTime);
	while (1)
	{
		//TaskHandler();
        VresDrvTest();
	}
}

多通道转换(DMA)

vres_drv.c

#include <stdint.h>
#include <stdio.h>
#include "gd32f30x.h"
#include "delay.h"
/**
***********************************************************
* @brief  ADC GPIO初始化          
* @param  
* @return
***********************************************************
*/
static void GpioInit(void)
{
    rcu_periph_clock_enable(RCU_GPIOC);
    gpio_init(GPIOC,GPIO_MODE_AIN,GPIO_OSPEED_10MHZ,GPIO_PIN_2);
    gpio_init(GPIOC,GPIO_MODE_AIN,GPIO_OSPEED_10MHZ,GPIO_PIN_3);
}
/**
***********************************************************
* @brief  ADC初始化          
* @param  
* @return 
***********************************************************
*/
static void AdcInit(void)
{
    /* 使能时钟;*/
	rcu_periph_clock_enable(RCU_ADC0);
	/* 设置分频系数;*/
	rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6);  // 6分频,120MHz / 6 = 20MHz
	/* 设置独立模式;*/
	adc_mode_config(ADC_MODE_FREE);
	/* 设置连续模式;*/ 
	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);
	/* 设置转换通道个数;*/ 
	adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 2);
	/* 设置转换哪一个通道以及所处序列位置;*/ 
	adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_12, ADC_SAMPLETIME_239POINT5);  // PC2对应通道12,放在序列寄存器的0序列中,239.5个周期
	adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_13, ADC_SAMPLETIME_239POINT5);  // PC2对应通道12,放在序列寄存器的0序列中,239.5个周期
	/* 设置选择哪一个外部触发源;*/ 
	adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
	/* 使能外部触发;*/ 
	adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
	/* 使能ADC的DMA功能;*/ 
	adc_dma_mode_enable(ADC0);
	/* 使能ADC;*/ 
	adc_enable(ADC0);
	/* 内部校准;*/ 
	DelayNus(50);
	adc_calibration_enable(ADC0);
	adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
}


/**
***********************************************************
* @brief  ADC DMA初始化函数         
* @param  
* @return
***********************************************************
*/
#define ADC0_RDATA_ADDR     (ADC0 + 0x4C)
static uint16_t g_adcVal[2];
static void DmaInit(void)
{
    /*使能DMA时钟*/
    rcu_periph_clock_enable(RCU_DMA0);
    /*复位DMA通道;*/
    dma_deinit(DMA0,DMA_CH0);
    dma_parameter_struct dmaStruct;//创建dma结构体变量(自带)
    dma_struct_para_init(&dmaStruct);
    /*配置传输方向;*/
    dmaStruct.direction = DMA_PERIPHERAL_TO_MEMORY;//片上外设到存储器内存
    /*配置数据源地址;*/
    dmaStruct.periph_addr =  ADC0_RDATA_ADDR;//adc数据寄存器地址
    /*配置源地址是固定的还是增长的;*/
    dmaStruct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;//源地址固定
    /*配置源数据传输位宽;*/
    dmaStruct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;//16位数据
    /*配置数据目的地址;*///(uint32_t)g_rcvDataBuf表示是将数组的地址值转换为一个 uint32_t 类型的整数
    dmaStruct.memory_addr = (uint32_t)g_adcVal;
    /*配置目的地址是固定的还是增长的;*/
    dmaStruct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;//目的地址自增
    /*配置目的数据传输位宽;*/
    dmaStruct.memory_width = DMA_MEMORY_WIDTH_16BIT;
    /*配置数据传输最大次数;*/
    dmaStruct.number = 2;
    /*配置DMA通道优先级;*/
    dmaStruct.priority = DMA_PRIORITY_HIGH;//优先级高
    /*DMA初始化*/
    dma_init(DMA0, DMA_CH0, &dmaStruct);
    /*使能DMA循环模式搬运数据A*/
    dma_circulation_enable(DMA0, DMA_CH0);
    /*使能DMA通道;*/
    dma_channel_enable(DMA0,DMA_CH0);
}



/**
***********************************************************
* @brief  ADC硬件初始化          
* @param  
* @return 
***********************************************************
*/
void VresDvrInit(void)
{
    GpioInit();
    AdcInit();
    DmaInit();
}

/**
***********************************************************
* @brief  ADC测试函数         
* @param  
* @return
***********************************************************
*/
void VresDrvTest()
{
    
    float CH1 = (float)g_adcVal[0] / 4095 * 3.3f;//转换成电压值
    float CH2 = (float)g_adcVal[1] / 4095 * 3.3f;//转换成电压值
    printf("CH1 = %.1f, CH2 = %.1f\n",CH1,CH2);
    DelayNms(1000);
}

vres_drv.h

#ifndef _VRES_DRV_H_
#define _VRES_DRV_H_

/**
***********************************************************
* @brief  ADC测试函数         
* @param  
* @return
***********************************************************
*/
void VresDrvTest();


/**
***********************************************************
* @brief  ADC硬件初始化          
* @param  
* @return 
***********************************************************
*/
void VresDvrInit(void);


#endif

A

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值