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

知识点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 okint 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 3void 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 + DAMADC_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);//使能DMADMA_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:PA10RCC_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.mzph.cn/diannao/83402.shtml

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

相关文章

Qwen3技术报告解读

https://github.com/QwenLM/Qwen3/blob/main/Qwen3_Technical_Report.pdf 节前放模型&#xff0c;大晚上的发技术报告。通义&#xff0c;真有你的~ 文章目录 预训练后训练Long-CoT Cold StartReasoning RLThinking Mode FusionGeneral RLStrong-to-Weak Distillation 模型结构…

【网络编程】十、详解 UDP 协议

文章目录 Ⅰ. 传输层概述1、进程之间的通信2、再谈端口号端口号的引出五元组标识一个通信端口号范围划分常见的知名端口号查看知名端口号协议号 VS 端口号 3、两个问题一个端口号是否可以被多个进程绑定&#xff1f;一个进程是否可以绑定多个端口号&#xff1f; 4、部分常见指令…

实现RTSP低延迟播放器,挑战与解决方案

随着低延迟直播需求的快速增长&#xff0c;RTSP&#xff08;Real-Time Streaming Protocol&#xff09;播放器逐渐成为实时视频流传输中的核心技术之一。与WebRTC&#xff08;Web Real-Time Communication&#xff09;相比&#xff0c;RTSP在实时性和网络延迟方面面临诸多挑战&…

【springcloud学习(dalston.sr1)】Eureka单个服务端的搭建(含源代码)(三)

该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍&#xff08;含源代码&#xff09;&#xff08;一&#xff09; springcloud学习&#xff08;dalston.sr1&#xff09;系统文章汇总如下&#xff1a; 【springcloud学习(dalston…

GPU与NPU异构计算任务划分算法研究:基于强化学习的Transformer负载均衡实践

点击 “AladdinEdu&#xff0c;同学们用得起的【H卡】算力平台”&#xff0c;H卡级别算力&#xff0c;按量计费&#xff0c;灵活弹性&#xff0c;顶级配置&#xff0c;学生专属优惠。 引言 在边缘计算与AI推理场景中&#xff0c;GPU-NPU异构计算架构已成为突破算力瓶颈的关键技…

探索C语言中的二叉树:原理、实现与应用

一、引言 二叉树作为一种重要的数据结构&#xff0c;在计算机科学领域有着广泛的应用&#xff0c;无论是在操作系统的文件系统管理&#xff0c;还是在数据库的索引构建中&#xff0c;都能看到它的身影。在C语言中&#xff0c;我们可以利用指针灵活地构建和操作二叉树。接下来&…

使用libUSB-win32的简单读写例程参考

USB上位机程序的编写&#xff0c;函数的调用过程. 调用 void usb_init(void); 进行初始化 调用usb_find_busses、usb_find_devices和usb_get_busses这三个函数&#xff0c;获得已找到的USB总线序列&#xff1b;然后通过链表遍历所有的USB设备&#xff0c;根据已知的要打开USB设…

vue注册用户使用v-model实现数据双向绑定

定义数据模型 Login.vue //定义数据模型 const registerData ref({username: ,password: ,confirmPassword: })使用 v-model 实现数据模型的key与注册表单中的元素之间的双向绑定 <!-- 注册表单 --><el-form ref"form" size"large" autocompl…

【Arthas实战】常见使用场景与命令分享

简介: Arthas是一款Java诊断工具&#xff0c;适用于多种场景&#xff0c;如接口响应变慢、CPU占用过高、热更新需求等。其核心命令包括实时监控面板&#xff08;dashboard&#xff09;、线程状态查看&#xff08;thread&#xff09;、方法调用链路追踪&#xff08;trace&#x…

Jenkins 最佳实践

1. 在Jenkins中避免调度过载 过载Jenkins以同时运行多个作业可能导致资源竞争、构建速度变慢和系统性能问题。分配作业启动时间可以防止瓶颈&#xff0c;并确保更顺畅的执行。如何实现&#xff1f; 在Cron表达式中使用H&#xff1a;引入抖动&#xff08;jitter&#xff09;&a…

pytest框架 - 第二集 allure报告

一、断言assert 二、Pytest 结合 allure-pytest 插件生成美观的 Allure 报告 (1) 安装 allure 环境 安装 allure-pytest 插件&#xff1a;pip install allure-pytest在 github 下载 allure 报告文件 地址&#xff1a;Releases allure-framework/allure2 GitHub下载&#x…

人工智能时代:解锁职业新身份,从“认证师”到“工程师”的进阶之路

在人工智能技术浪潮席卷全球的今天,技术的飞速迭代正在重塑职业版图。从算法优化到伦理决策,从系统测试到应用开发,AI技术不再只是程序员的专属领域,而是成为各行各业从业者必须掌握的“生存技能”。当企业争相布局AI赛道,个人如何在这场变革中抢占先机?答案或许藏在两个…

【带文档】网上点餐系统 springboot + vue 全栈项目实战(源码+数据库+万字说明文档)

&#x1f4cc; 一、项目概括 本系统共包含三个角色&#xff1a; 管理员&#xff1a;系统运营管理者 用户&#xff1a;点餐消费用户 美食店&#xff1a;上传菜品与处理订单的店铺账号 通过对这三类角色的权限与业务分工设计&#xff0c;系统实现了点餐流程的全链路数字化&a…

window nvidia-smi命令 Failed to initialize NVML: Unknown Error

如果驱动目录下的可以执行&#xff0c;那可能版本原因 "C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi"复制"C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe"替换 C:\Windows\System32\nvidia-smi.exe 或者 把C:\Windows\System3…

接触感知 钳位电路分析

以下是NG板接触感知电路的原理图。两极分别为P3和P4S&#xff0c;电压值P4S < P3。 电路结构分两部分&#xff0c;第一部分对输入电压进行分压钳位。后级电路使用LM113比较器芯片进行电压比较&#xff0c;输出ST接触感知信号。 钳位电路输出特性分析 输出电压变化趋势&a…

70、微服务保姆教程(十三)Docker容器详细讲义

一、关于Docker 1.1为什么要用docker? 随着开发的项目越来越复杂,软件越来越多,服务器越来越多,我们在开发和部署的时候会遇到很多问题,比如: 1.不同的应用程序可能会有不同的应用环境,比如Java开发的网站和php开发的网站依赖的软件就不一样,如果把他们依赖的软件都…

Python 中的 typing.ClassVar 详解

一、ClassVar 的定义和基本用途 ClassVar 是 typing 模块中提供的一种特殊类型&#xff0c;用于在类型注解中标记类变量&#xff08;静态变量&#xff09;。根据官方文档&#xff0c;使用 ClassVar[…] 注释的属性表示该属性只在类层面使用&#xff0c;不应在实例上赋值 例如&…

架构与UML4+1视图

简单对比分析 架构41视图 架构41视图是由Philippe Kruchten提出的&#xff0c;用于描述软件系统的架构。它包括以下五个视图&#xff1a; 逻辑视图&#xff1a;描述系统的功能需求&#xff0c;展示系统的静态结构&#xff0c;通常使用类图、对象图等。开发视图&#xff1a;…

Redis 八股

目录 数据类型 字符串&#xff1a; List&#xff1a; HASH&#xff1a; Set&#xff1a; Zset&#xff1a; BitMap&#xff1a;&#xff08;这个及以下是后来新增的数据结构&#xff09; HyperLogLog&#xff1a; GEO&#xff1a; Stream&#xff1a; 主要数据结构 …

基于协同过滤的文学推荐系统设计【源码+文档+部署】

基于协同过滤的文学推荐系统设计 摘要 随着信息技术的飞速发展和文学阅读需求的日益多样化&#xff0c;构建一个高效、精准的文学推荐系统变得尤为重要。本文采用Spring Boot框架&#xff0c;结合协同过滤算法&#xff0c;设计并实现了一个基于用户借阅行为和社交论坛互动的文学…