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.pswp.cn/diannao/83402.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83402.shtml
英文地址,請注明出處:http://en.pswp.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;設計并實現了一個基于用戶借閱行為和社交論壇互動的文學…