STM32 ADC+DMA+TIM触发采样实战:避坑指南与源码解析

news2025/5/17 18:49:15

知识点1【TRGO的介绍】

1、TRGO的概述

TRGO:Trigger Output(触发输出),是定时器的一种功能。

它可以作为外设的启动信号,比如ADC转换,DAC输出,DMA请求等。

对于ADC来说,可以通过TRGO信号触发ADC开始转换

2、定时器TRGO的生成过程

以通用定时器为例,TRGO信号的来源可以是:更新事件,比较事件,其他事件。如下图

通过TIM_SelectOutputTrigger()进行配置

3、补充

(1)ADC持续转换的两种方式

1、ContinuousConvMode = ENABLE + ExternalTrig = None

启动连续采集,适合实时采样

2、ContinuousConvMode = DISABLE + ExternalTrig = TIMx_TRGO

每次触发采样一次,适合周期性定点采样

每次定时器触发都会扫描一次所有通道

(2)DMA—TC中断的触发时机

当ADC完成一次完整的通道扫描,并且DMA将所有通道的结果都搬运到内存后,才会触发DMA传输完成中断(TC)

知识点2【代码练习】

本代码是一个将ADC(多通道),DMA(中断),TIM(TRGO)相结合的案例

实现的功能是ADC每秒转换一次,但这里我只有一个光敏元件,因此只有一个通道有数据。

main.c

#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include "delay.h"
#include "usart.h"
#include "adc.h"
#include "dma.h"
#include "tim.h"
u16 ADC3_Recv_Data;
int flag;

#define ADC_NUM 3

//ADC + DAM(中断) + TRGO
//多通道接收数据 用数组保存
u16 data_adc_mult[ADC_NUM] = {0};

//需要配置的有
//ADC3 IN6 IN5 IN4:PF8 PF7 PF6  ok
//USART1 TX:PA9,RX:PA10 ok
//DMA2 CH5 
//TIM3 TRGO ok

int main(void)
{
	//中断向量组配置
	Systick_Init(72000);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	Usart1_Init(9600);
	ADC3_Interrupt_DMA_TIM_GPIO_Init();
	ADC3_Interrupt_DMA_TIM_Init();
	DMA2_IN5_ADC3_Init();
	TIM3_TRGO_Init();
	while(1)
	{	
	}
}

adc.c

#include "adc.h"
#define ADC_NUM 3

void ADC3_Interrupt_DMA_TIM_Init(void)
{
	ADC_InitTypeDef ADC3_InitStruct;
	
	//时钟 分频
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3,ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	//ADC3初始化 配置为外部触发
	ADC_StructInit(&ADC3_InitStruct); 
	ADC3_InitStruct.ADC_ContinuousConvMode = DISABLE;
	ADC3_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
	ADC3_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
	ADC3_InitStruct.ADC_Mode = ADC_Mode_Independent;
	ADC3_InitStruct.ADC_NbrOfChannel = ADC_NUM;
	ADC3_InitStruct.ADC_ScanConvMode = ENABLE;
	ADC_Init(ADC3,&ADC3_InitStruct);
	
	//通道配置
	ADC_RegularChannelConfig(ADC3,ADC_Channel_6,1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC3,ADC_Channel_5,2,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC3,ADC_Channel_4,3,ADC_SampleTime_55Cycles5);
	
	//使能ADC3 + DAM
	ADC_Cmd(ADC3,ENABLE);
	ADC_DMACmd(ADC3,ENABLE);
	ADC_ExternalTrigConvCmd(ADC3, ENABLE);//外部触发
	
	//校验
	ADC_ResetCalibration(ADC3);
	while(ADC_GetResetCalibrationStatus(ADC3) == SET);
	ADC_StartCalibration(ADC3);
	while(ADC_GetCalibrationStatus(ADC3) == SET);
	
	//开始转换
	//ADC_SoftwareStartConvCmd(ADC3,ENABLE);
	//由于是外部触发 因此这句话不需要写
}

void ADC3_Interrupt_DMA_TIM_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIOF_InitStruct;
	
	//时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
	
	//配置PF8 PF7 PF6为模拟输入
	GPIO_StructInit(&GPIOF_InitStruct);
	GPIOF_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
	GPIOF_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
	GPIOF_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOF,&GPIOF_InitStruct);
}

dma.c

#include "dma.h"
extern u16 data_adc_mult[ADC_NUM];

void DMA2_IN5_ADC3_Init(void)
{
	DMA_InitTypeDef DMA2_InitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	//时钟配置
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2,ENABLE);
	
	//初始化
	DMA2_InitStruct.DMA_BufferSize = ADC_NUM;
	DMA2_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA2_InitStruct.DMA_M2M = DMA_M2M_Disable;
	DMA2_InitStruct.DMA_MemoryBaseAddr = (u32)data_adc_mult;
	DMA2_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //ADC数据12位
	DMA2_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA2_InitStruct.DMA_Mode = DMA_Mode_Circular;
	//这里需要设置循环模式的原因:
		//每次触发都需要 重新将DMA的计数器复位
	DMA2_InitStruct.DMA_PeripheralBaseAddr = (u32)&ADC3->DR;
	DMA2_InitStruct.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_HalfWord;
	DMA2_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA2_InitStruct.DMA_Priority = DMA_Priority_High;
	DMA_Init(DMA2_Channel5,&DMA2_InitStruct);
	
	//中断使能
	DMA_ITConfig(DMA2_Channel5,DMA_IT_TC,ENABLE);
	
	NVIC_InitStruct.NVIC_IRQChannel = DMA2_Channel4_5_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStruct);
	
	//使能DMA
	DMA_Cmd(DMA2_Channel5,ENABLE);
}

//中断处理函数
void DMA2_Channel4_5_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA2_IT_TC5) == SET)
	{
		int i = 0;
		DMA_ClearITPendingBit(DMA2_IT_TC5);
		
		for(i = 0;i < ADC_NUM ;i++)
		{
			printf("%f ",DataProcess_light(data_adc_mult[i]));
		}
		printf("\\n");
	}
}

tim.c

**#include "tim.h"
void TIM3_TRGO_Init(void)
{
	TIM_TimeBaseInitTypeDef TIM3_InitStrct;
	
	//时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	//定时器初始化
	TIM_TimeBaseStructInit(&TIM3_InitStrct);
	TIM3_InitStrct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM3_InitStrct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM3_InitStrct.TIM_Period = 10000 - 1;
	TIM3_InitStrct.TIM_Prescaler = 7200 - 1;
	TIM_TimeBaseInit(TIM3,&TIM3_InitStrct);
	
	//开启外部触发
	TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update);
	
	//使能定时器
	TIM_Cmd(TIM3,ENABLE);
}**

usart.c

#include "usart.h"
#include "led.h"

//串口2初始化
void Usart2_Init(u32 Baud)
{
	GPIO_InitTypeDef GPIOA_Pin2_InitStruct;
	GPIO_InitTypeDef GPIOA_Pin3_InitStruct;
	USART_InitTypeDef USART2_InitStruct;
	
	
	//时钟配置  USART,收(PA3)发(PA2)端口 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_StructInit(&GPIOA_Pin3_InitStruct);
	GPIOA_Pin3_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIOA_Pin3_InitStruct.GPIO_Pin = GPIO_Pin_3;
	GPIOA_Pin3_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	//收PA3端口配置
	GPIO_Init(GPIOA,&GPIOA_Pin3_InitStruct);
	
	GPIO_StructInit(&GPIOA_Pin2_InitStruct);
	GPIOA_Pin2_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIOA_Pin2_InitStruct.GPIO_Pin = GPIO_Pin_2;
	GPIOA_Pin2_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	//发PA2端口配置
	GPIO_Init(GPIOA,&GPIOA_Pin2_InitStruct);
	
	//串口初始化
	USART2_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART2_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART2_InitStruct.USART_BaudRate = Baud;
	USART2_InitStruct.USART_Parity = USART_Parity_No;
	USART2_InitStruct.USART_StopBits = USART_StopBits_1;
	USART2_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART2,&USART2_InitStruct);
	
	//使能串口
	USART_Cmd(USART2,ENABLE);
}

//串口1初始化
void Usart1_Init(u32 Baud)
{
	GPIO_InitTypeDef GPIOB_InitStruct;
	USART_InitTypeDef USART1_InitStruct;
	//时钟配置 USART1,TX:PA9,RX:PA10
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//端口配置
	GPIOB_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIOB_InitStruct.GPIO_Pin = GPIO_Pin_9;
	GPIOB_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOB_InitStruct);
	
	GPIOB_InitStruct.GPIO_Pin = GPIO_Pin_10;
	GPIOB_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIOB_InitStruct);
	
	//串口初始化
	USART1_InitStruct.USART_BaudRate = Baud;
	USART1_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART1_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART1_InitStruct.USART_Parity = USART_Parity_No;
	USART1_InitStruct.USART_StopBits = USART_StopBits_1;
	USART1_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1,&USART1_InitStruct);
	
	//使能串口
	USART_Cmd(USART1,ENABLE);
}

//串口1发送数据函数发送数据
void USART1_Trans(u8 c)
{
	
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	USART_SendData(USART1,c);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
}

int fputc(int c,FILE *stream)
{
	USART1_Trans((u8)c);
	return c;
}

delay.c

#include "delay.h"

u32 delay_ms = 0;

//系统定时器初始化函数
void Systick_Init(u32 ticks)
{
	SysTick_Config(ticks);
}

//延时函数
void Delay_ms(u32 ms)
{
	u32 ticks = delay_ms + ms;
	while(ticks > delay_ms);
}

//系统定时器中断服务函数
void SysTick_Handler(void)
{
	delay_ms++;
}

错误

1、DMA_BufferSize理解错误

2、DMA_Mode_Circular未配置循环模式

3、ADC_ExternalTrigConvCmd(ADC3, ENABLE);

未开启ADC3的外部触发转换
 

结束

代码重在练习!

代码重在练习!

代码重在练习!

今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!

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

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

相关文章

(1-4)Java Object类、Final、注解、设计模式、抽象类、接口、内部类

目录 1. Object类 1.1 equals 1.2 toString&#xff08;&#xff09; 2.final关键字 3.注解 4. 设计模式 4.1 单例模式 4.1.1 饿汉式 4.1.3 饿汉式 VS 懒汉式 5. 抽象类&抽象方法 6. 接口 7.内部类 7.1 成员内部类 7.2 静态内部类 7.3 方法内部类 7.4 匿名内…

在服务器上安装AlphaFold2遇到的问题(3)_cat: /usr/include/cudnn_version.h: 没有那个文件或目录

[rootlocalhost ~]# cat /usr/include/cudnn_version.h cat: /usr/include/cudnn_version.h: 没有那个文件或目录这个错误表明系统找不到 cudnn_version.h 头文件&#xff0c;说明 cuDNN 的开发文件&#xff08;头文件&#xff09;没有正确安装。以下是完整的解决方案&#xff…

实验-实现向量点积-RISC-V(计算机组成原理)

目录 一、实验内容 二、实验步骤 三、源代码 四、实现效果 五、实验环境 六、实验小结与思考 一、实验内容 首先&#xff0c;我们用一个简单的“向量点积”运算作为热身。你将拿到一个不完整的汇编代码“task2-向量点积”&#xff0c;我们的目标是按照C语言描述的功能&a…

描述性统计工具 - AxureMost 落葵网

描述性统计工具是用于汇总和分析数据&#xff0c;以更好地了解数据特征的工具1。以下是一些常见的描述性统计工具简介&#xff1a; 描述性统计工具 Excel 基本统计函数&#xff1a;提供了丰富的函数用于计算描述性统计量。例如&#xff0c;AVERAGE 函数用于计算平均值&#xf…

麒麟桌面系统文件保险箱快捷访问指南:让重要文件夹一键直达桌面!

往期文章链接&#xff1a;统信操作系统自定义快捷键配置音量调节功能指南 Hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇麒麟桌面操作系统上配置文件保险箱内文件夹桌面快捷方式的文章&#xff0c;欢迎大家分享点赞&#xff0c;点个在看和关注吧&#xff01;在日常…

从硬件角度理解“Linux下一切皆文件“,详解用户级缓冲区

目录 前言 一、从硬件角度理解"Linux下一切皆文件" 从理解硬件是种“文件”到其他系统资源的抽象 二、缓冲区 1.缓冲区介绍 2.缓冲区的刷新策略 3.用户级缓冲区 这个用户级缓冲区在哪呢&#xff1f; 解释关于fork再加重定向“>”后数据会打印两份的原因 4.内核缓冲…

游戏站的几种形式

游戏站点的主要形式&#xff1a;单品游戏站、游戏盒子站与单类型游戏盒子站 随着互联网的普及和游戏产业的快速发展&#xff0c;游戏站点作为玩家获取游戏资源和信息的重要平台&#xff0c;呈现出多种形式。本文将分析三种常见的游戏站点形式&#xff1a;单品游戏站、游戏盒子站…

打造智能化军工软件工厂,破解版本管理难题

在数字化浪潮席卷全球的当下&#xff0c;军工行业正经历着前所未有的软件工业化转型。作为这一进程的核心支撑&#xff0c;软件工厂模式正在重塑军工领域的研发体系。然而&#xff0c;传统版本管理方式已难以适应现代军工软件研发的复杂需求&#xff0c;成为制约行业发展的关键…

SpringbBoot nginx代理获取用户真实IP

为了演示多级代理场景&#xff0c;我们分配了以下服务器资源&#xff1a; 10.1.9.98&#xff1a;充当客户端10.0.3.137&#xff1a;一级代理10.0.4.105&#xff1a;二级代理10.0.4.129&#xff1a;三级代理10.0.4.120&#xff1a;服务器端 各级代理配置 以下是各级代理的基本配…

allure报告自定义logo和名称

根据pytest框架&#xff0c;做自动化测试的时候&#xff0c;选择的是allure测试报告&#xff0c;这个报告是目前所有报告中功能最强大最好用的测试报告之一 我们在使用这个测试报告的时候&#xff0c;怎么样去把allure的logo和名称替换成自己公司或者自己的logo呢&#xff1f;…

【AI论文】对抗性后期训练快速文本到音频生成

摘要&#xff1a;文本到音频系统虽然性能不断提高&#xff0c;但在推理时速度很慢&#xff0c;因此对于许多创意应用来说&#xff0c;它们的延迟是不切实际的。 我们提出了对抗相对对比&#xff08;ARC&#xff09;后训练&#xff0c;这是第一个不基于蒸馏的扩散/流模型的对抗加…

测试工程师如何学会Kubernetes(k8s)容器知识

Kubernetes(K8s)作为云原生时代的关键技术之一&#xff0c;对于运维工程师、开发工程师以及测试工程师来说&#xff0c;都是一门需要掌握的重要技术。作为一名软件测试工程师&#xff0c;学习Kubernetes是一个有助于提升自动化测试、容器化测试以及云原生应用测试能力的重要过程…

遥感图像露天矿区检测数据集VOC+YOLO格式1542张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1542 标注数量(xml文件个数)&#xff1a;1542 标注数量(txt文件个数)&#xff1a;1542 …

每日Prompt:迷你 3D 建筑

提示词 3D Q版迷你风格&#xff0c;一个充满奇趣的迷你星巴克咖啡馆&#xff0c;外观就像一个巨大的外带咖啡杯&#xff0c;还有盖子和吸管。建筑共两层&#xff0c;大大的玻璃窗清晰地展示出内部温馨而精致的设计&#xff1a;木质的家具、温暖的灯光以及忙碌的咖啡师们。街道…

el-breadcrumb 面包屑第一项后面怎么写没有分隔符

<el-breadcrumb separator"/"><el-breadcrumb-item>当前位置&#xff1a;</el-breadcrumb-item><el-breadcrumb-item :to"{ path: / }">首页</el-breadcrumb-item><el-breadcrumb-item><a href"/">活…

MYSQL 高可用

目录 一 什么是MYSQL高可用 1.1 什么是MySQL高可用 1.2方案组成 1.3 优势 2.1 案例环境 二 案例实施 1.安装mysql数据库 &#xff08;1 基础环境 &#xff08;2二进制安装进行bash (3 设置配置文件 MYSQL 的配置文件跟上面编译安装的配置文件类似 &#xff08;4. 配…

【GaussDB迁移攻略】DRS支持CDC,解决大规模数据迁移挑战

目录 1 背景介绍 2 CDC的实现原理 3 DRS的CDC实现方式 4 DRS的CDC使用介绍 5 总结 1 背景介绍 随着国内各大行业数字化转型的加速&#xff0c;客户的数据同步需求越来越复杂。特别是当需要将一个源数据库的数据同时迁移到不同的目标库场景时&#xff0c;华为云通常会创建…

HoloTime:从一张图片生成可交互的4D虚拟世界——突破静态生成模型,重构VR/AR内容生产范式

引言:静态生成模型的局限与HoloTime的突破 在空间智能与虚拟内容生成领域,传统生成模型(如扩散模型)面临两大瓶颈: 静态输出:仅能生成固定视角的3D场景或局部物体动画。沉浸感缺失:无法构建用户可“走进去”的动态4D空间(时间+空间)。HoloTime 通过“图像→全景视频→…

【深度学习】#11 优化算法

主要参考学习资料&#xff1a; 《动手学深度学习》阿斯顿张 等 著 【动手学深度学习 PyTorch版】哔哩哔哩跟李牧学AI 目录 深度学习中的优化挑战局部极小值鞍点梯度消失 凸性凸集凸函数 梯度下降一维梯度下降学习率局部极小值 多元梯度下降 随机梯度下降随机梯度更新动态学习率…

根据台账批量制作个人表

1. 前期材料准备 1&#xff09;要有 人员总的信息台账 2&#xff09;要有 个人明白卡模板 2. 开始操作 1&#xff09;打开 人员总的信息台账&#xff0c;选择所需要的数据模块&#xff1b; 2&#xff09;点击插入&#xff0c;选择数据透视表&#xff0c;按流程操作&…