GD32_ADC采样+DMA多通道扫描传输

GD32_ADC采样+DMA多通道扫描传输

文章目录

  • GD32_ADC采样+DMA多通道扫描传输
  • 前言
  • 一、资源介绍
  • 二、原理
    • 1.ADC连续扫描模式
    • 2.DMA传输
    • 3.ADC内部通道
  • 三、配置
    • 1.ADC配置
    • 2.DMA配置
    • 3.注意事项
  • 四、计算
    • 1.分压转换
    • 2.数据转换


前言

<1>、硬件平台:可运行软件程序的GD32单片机(本项目使用GD32F103CBT6硬件平台)
<2>、软件平台:基于使用标准库GD32F10x_Firmware_Library_V2.2.4固件库编写


一、资源介绍

所使用的MCU 片上集成了 12 位逐次逼近式模数转换器模块(ADC),可以采样来自于 16 个外部通道和 2 个内部通道上的模拟信号。这 18 个 ADC 采样通道都支持多种运行模式,采样转换后,转换结果可以按照最低有效位对齐或最高有效位对齐的方式保存在相应的数据寄存器中。我们主要介绍其多通道扫描转换和DMA传输功能,在多个通道轮询采集ad数据,并使用DMA分别保存各个数据值,其能大大提高ADC的工作效率。以下为ADC 模块框图:
ADC 模块框图

二、原理

1.ADC连续扫描模式

在配置多个通道采集时,如图:CH2、CH1、CH5、CH7、CH11,在扫描模式下,会对各个通道一次进行数据采样,再使用连续模式时,会对循环对上述通道进行连续数据采样,配置原理如下:

扫描运行模式可以通过将 ADC_CTL0 寄存器的 SM 位置 1 来使能。在此模式下, ADC 扫描转换所有被 ADC_RSQ0~ADC_RSQ2 寄存器选中的所有通道。一旦 ADCON 位被置 1,当相应软件触发或者外部触发产生, ADC 就会一个接一个的采样和转换常规序列通道。转换数据存储在 ADC_RDATA 寄存器中。常规序列转换结束后, EOC 位将被置 1。如果 EOCIE 位被置1,将产生中断。当常规序列工作在扫描模式下时, ADC_CTL1 寄存器的 DMA 位必须设置为1。如果 ADC_CTL1 寄存器的 CTN 位也被置 1,则在常规序列转换完之后,这个转换自动重新开始。

在这里插入图片描述
常规序列扫描运行模式的软件流程:

  1. 设置 ADC_CTL0 寄存器的 SM 位和 ADC_CTL1 寄存器的 DMA 位为 1;
  2. 配置 ADC_RSQx 和 ADC_SAMPTx 寄存器;
  3. 如果有需要,配置 ADC_CTL1 寄存器中的 ETERC 和 ETSRC 位;
  4. 准备 DMA 模块,用于传输来自 ADC_RDATA 的数据;
  5. 设置 SWRCST 位,或者给常规序列产生一个外部触发;
  6. 等待 EOC 标志位置 1;
  7. 写 0 清除 EOC 标志位。

2.DMA传输

当ADC使用连续扫描时,采样数据非常快且数据都暂存在ADC_RDATA寄存器中,我们可使用DMA自动将采样数据依次保存到内存中备用,各个通道采样数据经DMA传输到内存后的映射关系如图:

在这里插入图片描述

如图:一共五个通道需要连续扫描,我们定义一个二维数组Value[3][5]储存扫描值,3表示每个通道保存最近3次扫描的数据,5表示五个通道,该二维数组的内存分布如图所示,每个数据为两个字节大小。扫描时会依次将各通道数据保存到ADC_data寄存器,DMA会将寄存器数据依次搬迁到Value[][]数组中备用,Value空间30个字节,为当第四次扫描时会覆盖第一次扫描的数据,依次覆盖更新。

3.ADC内部通道

将 ADC_CTL1 寄存器的 TSVREN 位置 1 可以使能温度传感器通道(ADC0_IN16)和 VREFINT 通道(ADC0_IN17)。温度传感器可以用来测量器件周围的温度。传感器输出电压能被 ADC 转换成数字量。建议温度传感器的采样时间至少设置为 ts_temp µs(具体数值请参考datasheet 文档)。温度传感器不用时,复位 TSVREN 位可以将其置于掉电模式。温度传感器的输出电压随温度线性变化,由于生产过程的多样化,温度变化曲线的偏移在不同的芯片上会有不同(最多相差 45°C)。内部温度传感器更适合于检测温度的变化,而不是测量绝对温度。如果需要测量精确的温度,应该使用一个外置的温度传感器来校准这个偏移错误。内部电压参考(VREFINT)提供了一个稳定的(带隙基准)电压输出给 ADC 和比较器。 VREFINT 内部连接到 ADC0_IN17 输入通道
在这里插入图片描述

使用温度传感器:

  1. 配置温度传感器通道(ADC0_IN16)的转换序列和采样时间为 ts_temp µs
  2. 置位 ADC_CTL1 寄存器中的 TSVREN 位,使能温度传感器
  3. 置位 ADC_CTL1 寄存器的 ADCON 位,或者由外部触发启动 ADC 转换
  4. 读取内部温度传感器输出电压 Vtemperature, 并由下面公式计算出实际温度:
    温度 ( ° C ) = ( V 25 – V t e m p e r a t u r e ) / A v g S l o p e + 25 温度 (°C) = {(V_{25} – V_{temperature}) / A_{vg_Slope}} + 25 温度(°C)=(V25Vtemperature)/AvgSlope+25
    V25: 内部温度传感器在 25°C 下的电压,典型值请参考相关型号 datasheet。
    Avg_Slope:温度与内部温度传感器输出电压曲线的均值斜率,典型值请参考相关型号datasheet。

三、配置

我们使用通道ADC_CHANNEL_8和通道ADC_CHANNEL_1分别采样3.3V和12V电压,再使用通道ADC_CHANNEL_16和通道ADC_CHANNEL_17分别采样内部温度传感器和内部参考电压,一共四个通道,每个通道采集五个数据,计算时候再取平均值,因此定义一个二维数组ADC0_Vallue保存数据。

1.ADC配置

代码如下(示例):

static void ADC_Init(void)
 {
	
    uint16_t i = 0;
   /* enable ADC0 clock */
    rcu_periph_clock_enable(RCU_ADC0);
    /* config ADC clock */
    rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
	 
		/* ADC mode config */
    adc_mode_config(ADC_MODE_FREE); 
	 /* ADC scan function enable */
    adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE); 
    /* ADC continous function enable */
    adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
    /*ADC data alignment config */
    adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);
    /* ADC channel length config */
    adc_channel_length_config(ADC0,ADC_REGULAR_CHANNEL,ADC_CHANNEL_NUM); 
	/* enable the temperature sensor and Vrefint channel */
	adc_tempsensor_vrefint_enable();
    
    /* ADC regular channel config,一个通道转换时长是2.06us */
    adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_8, ADC_SAMPLETIME_239POINT5);  				//12V 
    adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_1, ADC_SAMPLETIME_239POINT5);				//3V3
    adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_16,ADC_SAMPLETIME_239POINT5);				//内部温度
    adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_17,ADC_SAMPLETIME_239POINT5);				//内部参考电压
    
    /* ADC trigger config */
    adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
    adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
    /* enable ADC0 interface */
    adc_enable(ADC0);
    /*延迟14个ADCCLK以等待ADC稳定*/    
    for(i = 1000u; i > 0; i--)
    {}
    /* ADC calibration and reset calibration */
    adc_calibration_enable(ADC0);
    /* ADC DMA function enable */
    adc_dma_mode_enable(ADC0);
    /* ADC software trigger enable */
    adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
 }

2.DMA配置

代码如下(示例):

#define CHANNEL_NUM    (4u)	
#define FILTER_NUM     (5U)

uint16_t ADC0_Vallue[FILTER_NUM][CHANNEL_NUM];

static void ADC_DMA_Init(void)
 {
	 /* ADC_DMA_channel configuration */
    dma_parameter_struct dma_data_parameter; 
	 
	rcu_periph_clock_enable(RCU_DMA0);
	 
    dma_deinit(DMA0, DMA_CH0);
		 
    /* initialize DMA single data mode */
    dma_data_parameter.periph_addr  = (uint32_t)(&ADC_RDATA(ADC0));
    dma_data_parameter.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_data_parameter.memory_addr  = (uint32_t)(&ADC0_Vallue);
    dma_data_parameter.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
    dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;  
    dma_data_parameter.direction    = DMA_PERIPHERAL_TO_MEMORY;
    dma_data_parameter.number       = ADC_FILTER_NUM * ADC_CHANNEL_NUM;
    dma_data_parameter.priority     = DMA_PRIORITY_HIGH;
    dma_init(DMA0, DMA_CH0, &dma_data_parameter);

    dma_circulation_enable(DMA0,DMA_CH0);
  
    /* enable DMA channel */
    dma_channel_enable(DMA0, DMA_CH0);
   
 }

3.注意事项

  1. ADC_CTL1 寄存器中的 ADCON 位是 ADC 模块的使能开关。如果该位为 0,则 ADC 模块保持复位状态。为了省电,当 ADCON 位为 0 时, ADC 模拟子模块将会进入掉电模式。ADC 使能后需要等待 tsu时间后才能采样 , tsu 数值详见芯片数据手册;
  2. 实际电压值最好先拿万用表测一下,避免踩了硬件的坑;

四、计算

1.分压转换

因GPIO口无法直接连接3.3v和12v,所以需要使用分压电阻使得接入到adc通的电压小于3.3v,ADC 电阻值应该将为千欧级的电阻,分压原理图如图所示:

在这里插入图片描述

这里以为12v为例,根据初中物理电阻串联分压知识,计算12V实际值:

V i n = V o u t ∗ ( ( R 2 + R 1 ) / R 1 ) V_{in} = V_{out} *((R2+R1)/R1) Vin=Vout((R2+R1)/R1)

2.数据转换

以为12bit分辨率为例,读取到的模拟电压转换后是一个12位的数字值,但这个值对于使用者没什么概念,所以需要再次转换成可读性较好的电压值,也就是用万用表量到的电压值。这里主要用到一个比例概念:一般情况下,ADC的输入电压范围在0~3.3v,所以12位满量程对应的电压值为3.3v,数字值为2^12。我们假设ADC转换后的12位的值为x,其对应的电压值为y,那么:

y x = 3.3 2 12 − 1 (1) \frac{y}{x}=\frac{3.3}{2^{12}-1} \tag{1} xy=21213.3(1)

y = x ∗ ( 3.3 / 2 12 − 1 ) y = x * (3.3 / 2^{12}-1) y=x(3.3/2121)

总结公式为:

V i n = A D V v a l ∗ ( V r e f / 2 N − 1 ) (2) V_{in} = ADV_{val} * (V_{ref}/ 2^N-1)\tag{2} Vin=ADVvalVref/2N1(2)

Vin 为实际电压值,ADCval​ 为ADC采集得到的数字信号值,Vref 为参考电压,N分辨率,
顺便一提:同样的原理通过电流转化公式和电流芯片,也可以采集电流信号

在这里插入图片描述
我们以1.2v作为参考电压标准值,17通道数据作为参数电压值,根据上述公式和数据参数,温度转换函数如下:

static uint8_t ADC_GetTemp(float* Temp)
{
  ReturnType_u8 ret = 0;
  uint32_t FilterVltg_Temp;
  uint32_t SampleVltg_1V2;
  
  SampleVltg_1V2 = (uint32_t)(ADC0_Vallue[0][3] + ADC0_Vallue[1][3] + ADC0_Vallue[2][3] + ADC0_Vallue[3][3] + ADC0_Vallue[4][3]) / 5u;
  FilterVltg_Temp = (uint32_t)(ADC0_Vallue[0][2] + ADC0_Vallue[1][2] + ADC0_Vallue[2][2] + ADC0_Vallue[3][2] + ADC0_Vallue[4][2]) / 5u;
  if(Temp != NULL_PTR)
  {
    *Temp = ((float)((1.45 - (FilterVltg_Temp * 1.2 / SampleVltg_1V2)) / 0.0041) + 25);
    ret = 1;
  }
  else
  {
     ret = 0;
  }
  
  return ret;
}

对于12V电压的计算原理也一样。

static uint8_t ADC_Get12VVltg(float* Vltg)
 {
    uint8_t ret = 0;
    uint32_t FilterVltg_Temp; 
    uint32_t SampleVltg_1V2;
   
    SampleVltg_1V2 = (uint32_t)(ADC0_Vallue[0][3] + ADC0_Vallue[1][3] + ADC0_Vallue[2][3] + ADC0_Vallue[3][3] + ADC0_Vallue[4][3]) / 5u;
    FilterVltg_Temp = (uint32_t)(ADC0_Vallue[0][0] + ADC0_Vallue[1][0] + ADC0_Vallue[2][0] + ADC0_Vallue[3][0] + ADC0_Vallue[4][0]) / 5u;
    if(Vltg != NULL_PTR)
    {
      *Vltg = (float)((uint64_t)FilterVltg_Temp * 1.2u * (1u + 11u) / SampleVltg_1V2 / 1u);
      ret = 1;
    }         
    else
    {
      ret = 0;
    }
    return ret;
 }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/150975.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【计算思维】少儿编程蓝桥杯青少组计算思维题考试真题及解析B

STEMA考试-计算思维-U8级(样题) 1.浩浩的左⼿边是&#xff08; &#xff09;。 A.兰兰 B.⻉⻉ C.⻘⻘ D.浩浩 2.2时30分&#xff0c;钟⾯上时针和分针形成的⻆是什么⻆&#xff1f;&#xff08; &#xff09; A.钝⻆ B.锐⻆ C.直⻆ D.平⻆ 3.下⾯是⼀年级同学最喜欢的《⻄游记》…

人工智能基础_机器学习037_多项式回归升维实战4_使用随机梯度下降模型_对天猫双十一销量数据进行预测_拟合---人工智能工作笔记0077

上一节我们使用线性回归模型最终拟合了双十一天猫销量数据,升维后的数据. 我们使用SGDRegressor的时候,随机梯度下降的时候,发现有问题, 对吧,怎么都不能拟合我们看看怎么回事现在 可以看到上面是之前的代码 上面是对数据的准备 这里我们还是修改,使用 poly=PolynomialFeatur…

nodejs+vue电影在线预定与管理系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

通过软件的需求分析已经获得了系统的基本功能需求&#xff0c;根据需求&#xff0c;将电影在线预定与管理系统功能模块主要分为管理员模块。 我国各行各业的发展在信息化浪潮的推动下也在不断进步&#xff0c;尤其是电影产业&#xff0c;在人们生活水平提高的同时&#xff0c;从…

旅拍摄影技巧澳大利亚、韩国旅行攻略

欢迎关注「苏南下」 在这里分享我的旅行和影像创作心得 刚刚在腾讯内部做了一场摄影分享课&#xff1a; 《旅拍摄影技巧&澳大利亚、韩国旅行攻略》 分享了早前去两个国家的一些旅行见闻和摄影心得。我发现&#xff1a;把自己学会的东西整理出来&#xff0c;再告诉给别人这件…

探索人工智能领域——每日30个名词详解【day3】

目录 前言 正文 总结 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da;。 &#x1f4e3;如需转载&#xff0c;请事先与我联系以…

c语言从入门到实战——数组指针与函数指针

数组指针与函数指针 前言1. 字符指针变量2. 数组指针变量2.1 数组指针变量是什么&#xff1f;2.2 数组指针变量怎么初始化? 3. 二维数组传参的本质4. 函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用4.3 两段有趣的代码4.3.1 typedef关键字 5. 函数指针数组6. 转移…

【华为HCIP | 华为数通工程师】ISIS 高频题(1)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

机器人导航+OPENCV透视变换示例代码

透视变换又称四点变换&#xff0c;所以不能用于5边形这样的图形变换&#xff0c;不是真正的透视变换&#xff0c;但是这个方法可以把机器人看到的图像转换为俯视图&#xff0c;这样就可以建立地图&#xff0c;要不然怎么建立地图呢。 void CrelaxMyFriendDlg::OnBnClickedOk()…

【JavaSE语法】类和对象(二)

六、 封装 6.1 封装的概念 面向对象程序三大特性&#xff1a;封装、继承、多态。而类和对象阶段&#xff0c;主要研究的就是封装特性。 封装&#xff1a;将数据和操作数据的方法进行有机结合&#xff0c;隐藏对象的属性和实现细节&#xff0c;仅对外公开接口来和对象进行交互…

PCL 提取点云边界轮廓-AC方法、平面轮廓

一、概述 PCL点云边界特征检测 &#xff08;附完整代码 C&#xff09;_pcl计算点云特征值_McQueen_LT的博客-CSDN博客 在点云的边界特征检测&#xff08;网格模型的边界特征检测已经是一个确定性问题了&#xff0c;见 网格模型边界检测&#xff09;方面&#xff0c;PCL中有一…

【C++初阶】STL详解(一)string类

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…

【Hello Go】Go语言基础类型

Go语言基础类型 基础类型命名变量变量声明变量初始化变量赋值匿名变量 常量字面常量常量定义iota枚举 基础数据类型分类 fmt包的标准输入输出格式说明输入类型转换类型取别名 基础类型 命名 Go语言中的命名遵循下面的几个规则 必须以字母或者是下划线开头不能使用Go语言中的…

Django——模板层、模型层

模板层 一. 模版语法 {{ }}: 变量相关 {% %}: 逻辑相关 1. 注释是代码的母亲 {# ... #} 2. 基本数据类型传值 int1 123 float1 11.11 str1 我也想奔现 bool1 True list1 [小红, 姗姗, 花花, 茹茹] tuple1 (111, 222, 333, 444) dict1 {username: jason, age: 18, i…

单片机的冷启动、热启动、复位

一文看懂STC单片机冷启动和复位有什么区别-电子发烧友网 单片机的冷启动、热启动和复位是不同的启动或重置方式&#xff0c;它们在系统状态和初始化方面有所不同&#xff1a; 1.冷启动&#xff08;Cold Start&#xff09;&#xff1a; 定义&#xff1a; 冷启动是指系统从完全关…

第14届蓝桥杯青少组python试题解析:22年10月选拔赛

选择题 T1. 执行print (5%3) 语句后&#xff0c;输出的结果是 ( ) 0 1 2 3 T2. 以下选项中&#xff0c;哪一个是乘法运算符?&#xff08;&#xff09; % // * ** T3. 已知x3&#xff0c;求x//2x**2的运算结果? 7.5 10 8 10.5 T4. 以下选项中&#xff0c;对下面程序的打印…

Unity地面交互效果目录

大家好&#xff0c;我是阿赵。   之前写了几篇关于地形交互、地面轨迹、脚印效果实现的博文。虽然写的篇数不多&#xff0c;但里面也包含了不少基础知识&#xff0c;比如局部UV采样、法线动态混合、曲面细分等知识&#xff0c;这些都是可以和别的效果组合在一起&#xff0c;做…

【刷题专栏—突破思维】142. 环形链表 II

前言&#xff1a;本篇博客将讲解三个OJ题&#xff0c;前两个作为铺垫&#xff0c;最后完成环形链表的节点的寻找 文章目录 一、160. 相交链表二、141. 环形链表三、142. 环形链表II 一、160. 相交链表 题目链接&#xff1a;LeetCode—相交链表 题目描述&#xff1a; 给你两个单…

排查线程阻塞问题

案例代码 package first;import java.util.concurrent.TimeUnit;public class DeadLock {private static volatile Object lock new Object();public static void main(String[] args) {new Thread(() -> {test1();}).start();new Thread(() -> {test2();}).start();}p…

队列的实现---超详细

队列的实现—超详细 文章目录 队列的实现---超详细一、队列的模型二、代码实现以及测试用例①队列初始化②入队③出队④输出队头⑤输出队尾⑥判断队列是否为空⑦队列的长度⑧队列的销毁⑨测试用例 一、队列的模型 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在…

Semantic Kernel 学习笔记2

本来想白瞟免费Bing Search API如下&#xff0c;但是报错无法链接利用免费的必应 Bing 自定义搜索打造站内全文搜索_bing_subscription_key-CSDN博客 改成按照官方推荐申请&#xff0c;并在.env文件中添加BING_API_KEY""字段。 1. 打开https://www.microsoft.com/en-…