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的外部触发转换

结束

代码重在练习!

代码重在练习!

代码重在练习!

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

相关推荐
XINVRY-FPGA11 小时前
XC3S1000-4FGG320I Xilinx AMD Spartan-3 SRAM-based FPGA
嵌入式硬件·机器学习·计算机视觉·fpga开发·硬件工程·dsp开发·fpga
猫猫的小茶馆14 小时前
【ARM】ARM的介绍
c语言·开发语言·arm开发·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆14 小时前
【PCB工艺】数模电及射频电路基础
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·pcb工艺
点灯小铭14 小时前
基于单片机的智能药物盒设计与实现
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
梓德原14 小时前
【基础】详细分析带隙型稳压电路的工作原理
单片机·嵌入式硬件·物联网
国科安芯15 小时前
航天医疗领域AS32S601芯片的性能分析与适配性探讨
大数据·网络·人工智能·单片机·嵌入式硬件·fpga开发·性能优化
小李做物联网16 小时前
【物联网毕业设计】60.1基于单片机物联网嵌入式项目程序开发之图像厨房监测系统
stm32·单片机·嵌入式硬件·物联网
贝塔实验室17 小时前
新手如何使用Altium Designer创建第一张原理图(三)
arm开发·单片机·嵌入式硬件·fpga开发·射频工程·基带工程·嵌入式实时数据库
@good_good_study17 小时前
STM32 ADC多通道采样实验
stm32·单片机·嵌入式硬件
Darken0317 小时前
什么是“位带”?;在STM32单片机中有什么作用?
stm32·单片机·嵌入式硬件