处理要求:不使用DMA可以获取多通道的值吗?
答:根据这个链接网站所描述的:STM32的ADC1采集多条通道 可以不使用DMA功能吗?-电子发烧友网 (elecfans.com)
ST芯片可以,具有转换通道的函数,但GD芯片不行,不使用DMA,最多可以借助扫描模式获取两个通道的值,再多一个就需要DMA了。。
1、主要参照两个链接,一个讲解了GD板卡的ADC规则:
(49条消息) GD32F130FXP6学习笔记六:cortex-m3系列的ADC初识_无痕幽雨的博客-优快云博客
2、DMA的添加,可以参考这个链接:
(49条消息) 立创梁山派GD32F450ZGT6--使用DMA实现多通道ADC采集_老怪.的博客-优快云博客_gd32 多通道adc
void ADC_DMA_Init(void)
{
/* dma初始化结构体 */
dma_single_data_parameter_struct dma_single_data_parameter;
/* 使能dma时钟 */
rcu_periph_clock_enable(RCU_DMA1);
/* 使能规则组通道每转换完成一个就发送一次DMA请求 在扫描模式下 */
adc_dma_request_after_last_enable(ADC0);
//开启ADC0的 DMA功能
adc_dma_mode_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)(ADC_value_arry); //设置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 = 10; //传输的数据长度为:2个通道 * 每个通道采集5次
dma_single_data_parameter.priority = DMA_PRIORITY_HIGH; //设置高优先级
dma_single_data_mode_init(DMA1, DMA_CH0, &dma_single_data_parameter); //将配置保存至DMA0的通道0
/* DMA通道外设选择 */
dma_channel_subperipheral_select(DMA1, DMA_CH0, DMA_SUBPERI0);
/* 使能DMA0通道0循环模式 循环获取数据 */
//dma_circulation_enable(DMA1, DMA_CH0);
dma_circulation_disable(DMA1, DMA_CH0);
/* 启动DMA0的通道0功能 */
dma_channel_enable(DMA1, DMA_CH0);
}
其中,ADC的设置为:
adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,DISABLE);//关闭连续转换
adc_special_function_config(ADC0,ADC_SCAN_MODE,ENABLE);//使能多通道扫描
这样,DMA就会自动获取数据,并且自动递增数组地址,存入我们的缓存地址,我使用了一个全局二维数组
uint16_t ADC_value_arry[5][2] = {0};
//DMA是按顺序来获取通道值的,因此若有两
通道,则依次存入[2]中,[5]表示每次获取5个
两通道的数值,共10个数值,我们再用循环的
方式获取对应通道的值即可
该链接对应的原文:
这次调试有些久,缺乏的知识很多,最重要的是ADC的引脚配置为浮空之后,如果没有接入东西,其读取的ADC值是不确定的,所以在测试的时候,一定要把对应的电压接入ADC引脚,测量的值才是准确的。
上代码。
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_Init(void);
unsigned int Get_ADC_Value(void);
**************************/
void ADC_DMA_Init(void);
unsigned int Get_Adc_Dma_Value(char CHx);
#endif
主函数
#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\nPC2=%d\r\nPC3=%d\r\nPC4=%d\r\n",\
show_buff[0], show_buff[1], show_buff[2], show_buff[3]);
USART0_send_String(temp_buff);
/* 串口显示刷新太快,加个延时 */
delay_1ms(1000);
}
}
实际效果
对PC1和PC3接入3.3V,因为ADC是12位的,所以最大采集数为4096,实际采集也接近了4096。
对PC2和PC4接入GND,实际采集也接近0。
注意!引脚不能浮空进行采集!不然数据是不确定的!
————————————————
版权声明:本文为优快云博主「老怪.」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/qq_51930953/article/details/127542174
4、但这样使用DMA获取各个通道数值的话,会出现一个问题
网友在使用的时,也遇到了我这样的问题:GD32F130R8T6的ADC问题 - - 21ic电子技术开发论坛
就是在一个通道的值改变时,其他通道的值也会跟着改变,这个是ST与GD芯片都会有的问题,需要这样处理:
(49条消息) STM32的ADC多路采集 DMA传输 数据错位_EmbededCoder的博客-优快云博客
尚未验证。。。
STM32的ADC采样DMA方式通道间有干扰-解决思路 - 腾讯云开发者社区-腾讯云 (tencent.com)