STM32 多路ADC同时扫描采样

news2024/9/21 12:35:31

背景

在项目实际应用中,刚好有需求需要使用多路ADC同时采样,这里就选择STM32 ADC多路ADC同时采样,这里简单说明下配置过程,以及使用步骤

原理图

如下图所示,使用四路ADC输入

ADC_Voltage -> 电压信号的采样,外部输入信号,交流电的输入信号,正选信号

ADC_Current -> 电流电流的采样,外部输入信号,交流电的输入信号,正选信号

ADC_Compensation ->      热敏电阻的采样,温度补偿

SCR_NTC ->      同样的热敏电阻的采样,温度补偿

一共使用上述四路ADC输入信号,进入STM32F103C8T6进行采样

外部输入电流、电压采用信号,这里做个保护电路

 NTC热敏电阻采样电路如下

 这里不仔细说明NTC补偿温度的过程,网上也比较多资料,这里侧重说明一下STM ADC的配置

软件设计

采用STM32F103C8T6 使用固件库开发方式

ADC初始化如下

定义DMA搬运ADC采样原始数据的到RAM的地址空间

#define SAMPL_TIMES_PRE_CHANNEL 1000 //每通道采样次数
#define NUM_OF_CHANNEL 4  //供4个通道

uint16_t __IO AD_Value[SAMPL_TIMES_PRE_CHANNEL][NUM_OF_CHANNEL]; //ADC转换结果,DMA目标地址

由于主时钟倍频到72MHZ,这里使用ADC1,首先6分频,主要是不需要特别高的采样率

1路、2路ADC信号,是正选信号,需要匹配其频率,这里经过计算的配置如下

3路、4路ADC信号,无所谓频率,所以只需要匹对1路、2路即可

这里采用独立模式连续多通道扫描模式,针对原理图中设计的

0、1、6、7通道进行扫描

根据需要也可以选择是否打开扫描完成后的中断,实际使用过程中,没有打开,因为感觉没啥用

如果有需要的话也是可以打开的。

打开扫描完成后的中断,需要注意,清除中断标志


void bspNTCAdcInit(void)
{
//	NVIC_InitTypeDef   NVIC_InitStructure;  
	ADC_InitTypeDef ADC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	/* Configure ADC   PA1 PA2 PA6 PA7  */
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	
	/* 配置ADC时钟,为PCLK2的8分频,即72/8=9MHz,ADC最大不超过14MHz */
	//RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 , ENABLE);
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);

	/* ADC1 configuration ------------------------------------------------------*/
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	   /* 独立模式 */
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;			   /* 连续多通道模式 */
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;	   /* 连续转换 */
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  /* 转换不受外界决定 */
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;		       /* 右对齐 */
	ADC_InitStructure.ADC_NbrOfChannel = NUM_OF_CHANNEL;					   /* 扫描通道数 */
	ADC_Init(ADC1, &ADC_InitStructure);

	/* ADC1 regular channe0 configuration */ 
	//通过查找数据手册可知,PA5对应的是channel_5        转换时间为:(239.5+12.5)/9 = 28us
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 3, ADC_SampleTime_239Cycles5); /*  ADC1,ADC通道x,规则采用顺序值为1,采样时间为239.5周期 */ 
	ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 4, ADC_SampleTime_239Cycles5); /*  ADC1,ADC通道x,规则采用顺序值为2,采样时间为239.5周期 */ 
	/* Enable the temperature sensor and vref internal channel */ 
	//ADC_TempSensorVrefintCmd(ENABLE);
	ADC_DMACmd(ADC1, ENABLE);//开启ADC的DMA支持
	/* Enable ADC1 */
	ADC_Cmd(ADC1, ENABLE);
	/* Enable ADC1 reset calibaration register */ 
	ADC_ResetCalibration(ADC1);
	/* Check the end of ADC1 reset calibration register */
	while(ADC_GetResetCalibrationStatus(ADC1));
	/* Start ADC1 calibaration */
	ADC_StartCalibration(ADC1);
	/* Check the end of ADC1 calibration */
	while(ADC_GetCalibrationStatus(ADC1));  
	/* Start ADC1 Software Conversion */ 
	//ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	
	DMA_DeInit(DMA1_Channel1); //将DMA的通道1寄存器重设为缺省值
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA外设ADC基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value; //DMA内存地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //内存作为数据传输的目的地
	DMA_InitStructure.DMA_BufferSize = NUM_OF_CHANNEL * SAMPL_TIMES_PRE_CHANNEL; //DMA通道的DMA缓存大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设寄存器地址不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道x拥有高优先级
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure); //

//	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;	  
//	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 8;// 设置抢占优先级
//	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	
//	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//	NVIC_Init(&NVIC_InitStructure);

	DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA通道	
//	DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

对采样的数据进行均值滤波

这里根据自己的需要,对采样的数据进行均值滤波

可以一起计算滤波,也可以分开计算,这个计算过程比较消耗资源,所以,我这里分开计算了

使用哪一路的ADC进行计算哪一路

只有需要更新ADC时才计算实际ADC的数值



void filter_0_1(void)
{
	uint64_t sum = 0;
	uint16_t count , i;
	
	i = 0;
	for(count = 0; count < SAMPL_TIMES_PRE_CHANNEL; count += 1)
	{
		sum += AD_Value[count][i] * AD_Value[count][i];
		//sum += AD_Value[count][i];
	}
	after_filter[i]=sum / SAMPL_TIMES_PRE_CHANNEL;
	sum=0;
	//printf("i:%d,%4.2fv\r\n", i,((float)after_filter[i]/4095)*3.3);

	i = 1;
	for(count = 0; count < SAMPL_TIMES_PRE_CHANNEL; count += 1)
	{
		sum += AD_Value[count][i] * AD_Value[count][i];
		//sum += AD_Value[count][i];
	}
	after_filter[i]=sum / SAMPL_TIMES_PRE_CHANNEL;
	//printf("i:%d,%4.2fv\r\n", i,((float)after_filter[i]/4095)*3.3);
}
//static u8 ntct_fresh = 1;
void filter_2_3(void)
{
	uint32_t sum = 0;
	uint16_t count , i;
	

	i = 2;
	//if(ntct_fresh == 1)
	{
		for(count = 0; count < SAMPL_TIMES_PRE_CHANNEL; count += 1)
		{
			sum += AD_Value[count][i];
		}
		after_filter[i]=sum / SAMPL_TIMES_PRE_CHANNEL;
	}
	sum=0;
	//printf("i:%d,%4.3fv\r\n", i,((float)after_filter[i]/4095)*3.3);	
	
//	i = 3;
//	for(count = 0; count < SAMPL_TIMES_PRE_CHANNEL; count += 1)
//	{
//		sum += AD_Value[count][i];
//	}
//	after_filter[i]=sum / SAMPL_TIMES_PRE_CHANNEL;
//	//printf("i:%d,%4.3fv\r\n", i,((float)after_filter[i]/4095)*3.3);	
}

如果打开DMA中断标识,需要清除中断标志

void DMA1_Channel1_IRQHandler(void)
{
	portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
//	uint16_t msg = MSG_SYS_ADC;
	
	if(DMA_GetFlagStatus(DMA1_FLAG_TC1)==SET)
	{
		DMA_ClearFlag(DMA1_FLAG_TC1);

		//xQueueSendFromISR( tempctrlxQueue, &msg, &xHigherPriorityTaskWoken );
	}
	portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何查看linux分区挂载在哪个目录?

一&#xff0c;简介 在linux系统中&#xff0c;如何查看磁盘分区是挂载在哪个目录呢&#xff1f;今天介绍一种方法&#xff0c;供参考。 二&#xff0c;图形化界面查看分区挂载方法 2.1 打开disk工具 2.2 点击查看对应的分区 看一个分区&#xff1a; 如上图所示&#xff0…

百度地图网页设计

一、百度地图api 1、百度搜索——百度地图API 进入——开放平台——开发文档——JavaScript API JavaScript API 首先是GL版本&#xff0c;是三维的效果&#xff0c;我们一般使用二维&#xff0c;选择下面v3.0版本 2、开发指南——注册账号 跟着提示来申请密钥就可。 二、…

Linux基本指令【下】

目录 一、时间相关指令 date显示 时间戳 二、cal指令 三、find指令 &#xff08;重要&#xff09;-name 四、grep指令 五、zip\unzip指令 六、tar指令&#xff1a;打包/解压&#xff0c;不打开它&#xff0c;直接看内容 七、bc指令 八、uname -r指令 九、几个重要…

ConstrainLayout(约束布局)属性详解

layout_constraintLeft_toLeftOf layout_constraintRight_toRightOf 这两个基本上用不上因为 layout_constraintStart_toStartOf就相当于layout_constraintLeft_toLeftOf layout_constraintEnd_toEndOf就相当于layout_constraintRight_toRightOf app:layout_constraintBottom_t…

【VMware】Ubantu 22.04配置静态IP

文章目录 一、VMware 虚拟网络配置VMnet8 网络设置注意 关于取消勾选使用本地DHCP服务将IP地址分配给虚拟机VMnet8 NAT设置 网关IP 二、虚拟机 网络适配器三、启动虚拟机 配置网络查看网卡名设置静态IP Reference 一、VMware 虚拟网络配置 VMnet8 网络设置 子网IP子网掩码 编…

22 外部排序

外部排序 外部排序的基本内容 前面介绍过的排序方法都是在内存中进行的&#xff08;称为内部排序&#xff09;。外部排序是一种无法全部装入内存的大规模数据集的排序算法。与在内存中处理数据的内部排序相比&#xff0c;外部排序要复杂的多&#xff0c;主要因为是其需要解决…

【c语言】二进制文件的读写操作

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

1.1 IAR新建工作空间 及 新建工程

目录 新建工作空间 新建工程 新建工作空间 &#xff08;1&#xff09;创建一个名字为Workspace的文件夹&#xff0c;如图所示。 &#xff08;2&#xff09;运行IAR EW for 8051 10.10.1&#xff0c;如图所示。 &#xff08;3&#xff09;依次选择File和New Workspace&#xf…

【IDEA使用指南】如何将 IDEA 开发工具作为可视化工具来操作数据库?

如何将 IDEA 开发工具作为可视化工具来操作数据库&#xff1f; 步骤1&#xff1a;找到数据源配置的工具栏。 如下图所示&#xff0c;找到 “View -> Tool Windows -> Database”&#xff0c;点击“Database”。 步骤2&#xff1a;选择数据源类型。 在开发工具右侧会…

CSS背景色渐变

从上到下渐变&#xff1a;background: linear-gradient(red, pink); 从左到右渐变&#xff1a;background: linear-gradient(to right, red , pink); 对角&#xff08;从左上角到右下角&#xff09;渐变&#xff1a;background: linear-gradient(to bottom right, red , pink);…

【Linux】进程信号详解(三)

文章目录 一、可重入函数二、volatile三、SIGCHLD信号 一、可重入函数 假设有一个不带头的单链表&#xff0c;要进行头插操作&#xff0c;在我们数据结构阶段都已经学习过&#xff0c;我们可以有以下的步骤&#xff1a; 要将node1头插到单链表中&#xff0c;调用insert函数&…

什么是数据仓库

数据仓库的概念可以追溯到20世纪80年代&#xff0c;当时IBM的研究人员开发出了“商业数据仓库”。本质上&#xff0c;数据仓库试图提供一种从操作型系统到决策支持环境的数据流架构模型。数据仓库概念的提出&#xff0c;是为了解决与这个数据流相关的各种问题&#xff0c;主要是…

flutter 在动图上添加文字

前言 有这样一个场景&#xff0c;在一个展示很多文字的App中背景图片可以自定义&#xff0c;当然也可以是动态的&#xff0c;但是这个主页是可以分享出去的&#xff0c;也就是我需要在一个动态的背景上写上文字并保存为一张新的图片并分享出去。 实现 前置准备 需要导入一个…

【计算机视觉 | 目标检测】Objects365 :最新大规模高质量目标检测数据集

文章目录 一、前言二、数据集的规模三、数据集的质量四、泛化能力五、结语 一、前言 2019 年 4 月&#xff0c;在北京举行的智源学者计划启动暨联合实验室发布会上&#xff0c;北京旷视科技有限公司与北京智源人工智能研究院共同发布了全球最大的目标检测数据集 &#xff1a; …

ChatGPT国内免费访问

背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具&#xff0c;近期的热度直接沸腾&#x1f30b;。 作为一个程序员&#xff0c;我也忍不住做了一个基于ChatGPT的网站&#xff0c;免费&#xff01;免梯子&#xff01;&#xff01;国内可直接对话ChatGPT&#xff0c;也…

使用Python和Scrapy实现抓取网站数据

Scrapy是一个功能强大的网络爬虫框架&#xff0c;允许开发者轻松地抓取和解析网站内容&#xff0c;这篇文章主要为大家介绍了如何使用Python的Scrapy库进行网站数据抓取&#xff0c;需要的可以参考一下 在本文中&#xff0c;我们将介绍如何使用Python的Scrapy库进行网站数据抓…

00后才是内卷界的扛把子,被卷的头皮发麻....

人们都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;前一周时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪20K&#xff0c;都快接近我了。后来听同事说才知道人家是个卷王&#xff0c;从早干到晚就差搬张床…

哈希表应用——布隆过滤器

注&#xff1a;布隆过滤是用来处理海量数据且允许存在误判 目录 布隆过滤器提出 布隆过滤器概念 布隆过滤器的理论知识 布隆过滤器的实现 布隆过滤器的删除 布隆过滤器优点 布隆过滤器缺陷 布隆过滤器的应用场景 哈希切分 布隆过滤器/哈希切分面试题 布隆过滤器提出 …

免交互Here Document

文章目录 免交互Here Document1 定义2 语法格式2.1 免交互方式实现对行数的统计2.2 通过 read 命令接收输入并打印2.3 通过 passwd 给用户设置密码2.4 支持变量替换2.5 多行注释 3 expect4 实例4.1 su切换用户4.2 嵌入执行模式4.3 实现ssh自动登录 免交互Here Document 1 定义…

Linux CentOS7中yum的使用更新yum源

我们在windows中我们是经常需要下载一些我们需要的软件&#xff0c;那么我们在Linux中理所当然也是需要的&#xff0c;那么我们如何安装软件呢&#xff1f;&#xff1f; Linux中软件安装的方法 1.源代码安装&#xff1a;源代码安装就是直接自己安装源代码&#xff0c;并且是自…