知识点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的外部触发转换
结束
代码重在练习!
代码重在练习!
代码重在练习!
今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!