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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
**#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

cpp 复制代码
#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

cpp 复制代码
#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的外部触发转换

结束

代码重在练习!

代码重在练习!

代码重在练习!

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

相关推荐
djxbbdjedhb8 小时前
软件按键消抖的几种方式
单片机·嵌入式硬件
努力做小白8 小时前
STM32WB55官方OTA例程
stm32·单片机·嵌入式硬件
恒锐丰小吕8 小时前
EG27324 带关断功能双路MOS驱动芯片技术解析
嵌入式硬件·硬件工程
恒锐丰小吕9 小时前
屹晶微 EG1163(S) 高压大电流降压开关电源芯片技术解析
嵌入式硬件·硬件工程
SKYDROID云卓小助手10 小时前
无人设备遥控器之差分信号抗干扰技术
网络·stm32·单片机·嵌入式硬件·算法
恒锐丰小吕12 小时前
矽塔 SA8883 6.0-45.0V 8A有刷直流电机驱动芯片技术解析
嵌入式硬件·硬件工程
MARIN_shen12 小时前
PCB之电源完整性之电源网络的PDN仿真CST---08
网络·单片机·硬件工程·pcb工艺
小龙报13 小时前
《VScode搭建教程(附安装包)--- 开启你的编程之旅》
c语言·c++·ide·vscode·单片机·物联网·编辑器
FanXing_zl13 小时前
基于整数MCU的FOC电机控制深度解析:从浮点到定点的工程实践
单片机·嵌入式硬件·mcu·软件开发·定点计算
DIY机器人工房15 小时前
科普:华为星闪是什么?华为星闪(英文名 NearLink)是国际星闪无线短距通信联盟发布的新型无线短距通信标准技术。
stm32·嵌入式硬件·华为·嵌入式·diy机器人工房·嵌入式面试题