当前位置: 首页 > news >正文

STM32F103入门实战:从开发环境搭建到GPIO、串口、定时器核心外设精讲

1. 从零开始:为什么STM32F10x是嵌入式入门的绝佳选择

如果你刚刚接触单片机,或者从51、Arduino这类平台过来,准备踏入“32位机”的世界,那么STM32F10x系列几乎是你绕不开的第一站。我当年也是从这个系列入手的,它就像嵌入式开发里的“新手村”,装备齐全、教程遍地,但又足够强大,能让你一路玩到“满级”。STM32F10x,特别是我们常说的“F103”,属于ARM Cortex-M3内核,性能远超8位机,价格却非常亲民。市面上大量的开发板、教程、开源项目都基于它,形成了一个极其繁荣的生态。这意味着你遇到的绝大多数问题,几乎都能在网上找到答案,这对于初学者来说,是降低学习门槛、建立信心的关键。很多人一上来就想搞最前沿的芯片,结果光搭环境、找资料就劝退了。从F10x开始,你能把精力真正集中在理解嵌入式系统的核心概念上,比如GPIO、中断、定时器、串口通信这些,而不是在复杂的工具链和稀缺的资料里挣扎。

2. 搭建你的第一个战场:开发环境与工程创建

工欲善其事,必先利其器。对于STM32开发,一套顺手的工具链是成功的一半。这里主要有两条主流路线:Keil MDK-ARMSTM32CubeIDE。我强烈建议初学者从Keil开始,并不是说CubeIDE不好,而是Keil的生态更成熟,你遇到的所有初级问题,几乎都能找到基于Keil的解决方案,包括寄存器操作和标准库。

2.1 工具链安装与芯片包管理

首先,你需要安装Keil MDK(现在叫Keil MDK-ARM)。安装过程比较简单,但有一个关键步骤:安装对应的Device Family Pack(DFP),也就是STM32F1系列的芯片支持包。很多新手安装完Keil后,发现新建工程时找不到STM32F103系列,问题就出在这里。

注意:Keil的许可证管理比较严格,对于学习用途,可以使用其代码大小限制的评估版。如果遇到编译后代码超限,通常是未正确激活或安装芯片包。

安装完Keil和F1的DFP后,你还需要一个调试下载器,最经济实惠的就是ST-Link。市面上二三十块的ST-Link V2兼容版非常多,基本都能用。你需要安装ST-Link的驱动,通常卖家会提供,或者去ST官网下载“STSW-LINK009”这个软件包,里面包含驱动。安装好后,把ST-Link通过SWD接口(SWDIO、SWCLK、GND、3.3V)连接到你的开发板,在Keil的调试设置里选择“ST-Link Debugger”,就能识别到芯片了。

2.2 新建工程的两种哲学:标准库与HAL库

新建第一个工程时,你会面临第一个重要选择:用标准外设库(Standard Peripheral Library, SPL)还是硬件抽象层库(Hardware Abstraction Layer Library, HAL)

  • 标准库(SPL):这是STM32早期的官方库,现在已经停止更新,但对于F1系列来说完全够用,并且是存量资料最多的。它更接近底层寄存器,你需要手动配置每个外设的时钟、初始化结构体。学习标准库能让你更深刻地理解外设的工作原理,知道每一行代码在操作哪个寄存器。对于打基础来说,这是最佳选择。新建工程时,你需要手动将标准库的源文件(stm32f10x_xxx.c)和头文件路径添加到项目中。
  • HAL库:这是ST现在主推的库,配合STM32CubeMX图形化配置工具使用,可以自动生成初始化代码。它的优点是移植性好,换一个STM32系列芯片,代码改动可能很小;缺点是代码比较臃肿,执行效率相对低一些,并且封装层次高,有时出了问题不好排查。

我的建议是:入门阶段,从标准库开始。虽然前期麻烦一点,需要自己搭建工程框架,但这个过程能让你彻底搞明白时钟树、外设映射、头文件包含这些基础概念。很多经典的教程,比如正点原子、野火,其早期资料都是基于标准库的,跟着学更容易。当你用标准库把GPIO、串口、定时器都玩转之后,再去看HAL库和CubeMX,就会有一种“降维打击”的感觉,理解它为什么那么设计。

这里给出一个基于标准库的新建工程核心步骤:

  1. 在Keil中创建新项目,选择你的具体芯片型号(如STM32F103C8T6)。
  2. 在项目目录下,手动创建UserLibraryStartup等文件夹。
  3. 将标准库文件复制到Library文件夹。关键文件包括:
    • CMSIS文件夹下的核心支持文件。
    • STM32F10x_StdPeriph_Driver下的src(源文件)和inc(头文件)。
    • 启动文件startup_stm32f10x_md.s(对于中等容量F103,md表示Medium Density)。
  4. 在Keil的工程管理窗口中,将这些文件分组添加到项目中。
  5. 在Keil的Options for Target->C/C++中,添加头文件包含路径,并定义全局宏USE_STDPERIPH_DRIVERSTM32F10X_MD(根据你的芯片容量定义)。
  6. 编写一个简单的main.c,包含#include “stm32f10x.h”,并写一个空的while(1)循环。编译通过,0错误,0警告,你的工程框架就搭好了。

3. 点亮第一盏灯:深入理解GPIO与时钟系统

第一个程序永远是“点灯”,但这不仅仅是让一个LED闪烁。这是你理解STM32运行机制的第一个窗口。

3.1 时钟树:芯片的脉搏

在写任何外设代码之前,必须理解时钟。STM32的时钟树比较复杂,但入门时你只需要抓住几个关键点:

  1. HSI/HSE:内部高速时钟(8MHz,精度一般)和外部高速时钟(通常接8MHz晶振,更稳定)。系统上电默认使用HSI。
  2. PLL:锁相环倍频器。可以把HSI或HSE的频率倍频,得到更高的系统时钟(SYSCLK)。对于F103,最高可以到72MHz。我们通常用HSE(8M)经过PLL 9倍频,得到72MHz的SYSCLK。
  3. AHB、APB1、APB2总线:SYSCLK经过分频,供给不同的总线。APB2是高速总线,GPIO、USART1、高级定时器挂在这上面;APB1是低速总线,最大36MHz,USART2/3、I2C、基本定时器挂在这上面。

为什么点灯前要配置时钟?因为GPIO外设挂在APB2总线上,默认情况下它的时钟可能是关闭的(为了省电)。你必须先打开APB2总线上GPIO端口的时钟,才能去配置它的引脚。在标准库中,使用RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);来打开GPIOC的时钟。

3.2 GPIO的八种模式与配置要点

STM32的GPIO功能强大,有8种模式:

  • 输入类:模拟输入、浮空输入、上拉输入、下拉输入。
  • 输出类:开漏输出、推挽输出、复用开漏输出、复用推挽输出。

对于驱动普通的LED(阴极接GPIO,阳极接VCC),我们使用推挽输出模式。它能提供较强的拉电流和灌电流能力。

配置一个GPIO引脚,在标准库中遵循以下步骤:

// 1. 定义GPIO初始化结构体 GPIO_InitTypeDef GPIO_InitStructure; // 2. 开启对应GPIO端口的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 3. 配置结构体成员 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; // 引脚13 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 输出速度,驱动LED选50MHz没问题 // 4. 初始化 GPIO_Init(GPIOC, &GPIO_InitStructure); // 5. 操作引脚 GPIO_SetBits(GPIOC, GPIO_Pin_13); // 置高,LED灭(假设低电平点亮) GPIO_ResetBits(GPIOC, GPIO_Pin_13); // 置低,LED亮

这里有一个关键细节GPIO_Speed。它配置的是IO口的翻转速度,速度越高,边沿越陡峭,功耗和噪声也越大。对于简单的LED闪烁,2MHz都绰绰有余。但在后续做串口通信、特别是PWM输出时,这个速度需要根据实际信号频率来设置,否则可能导致波形失真。

3.3 软件延时与系统滴答定时器(SysTick)

让LED闪烁,我们需要延时。最简陋的方法是写一个for循环空转,这就是软件延时。它极度不精确,且会独占CPU。

void Delay_ms(uint32_t nms) { uint32_t i, j; for(i=0; i<nms; i++) for(j=0; j<8000; j++); // 这个8000需要根据你的系统时钟频率实际调整 }

更优雅的方式是使用SysTick。这是一个24位的递减计数器,属于Cortex-M3内核的内设定时器,通常用来提供操作系统的时钟节拍。我们可以用它来实现精确的毫秒级延时。

  1. 初始化SysTick,将其时钟源设置为系统时钟(AHB),并配置重装载值。例如系统时钟72MHz,我们设置重装载值为72000,那么每次中断就是1ms。
  2. 编写中断服务函数,在里面对一个全局变量(如msTicks)进行递增。
  3. 实现一个Delay_ms()函数,其原理是:记录当前的msTicks值,然后循环等待,直到msTicks的增量达到所需的毫秒数。

使用SysTick延时,CPU在等待期间可以执行其他任务(如果是放在主循环里判断),或者进入低功耗模式,效率高得多。这是你从“单片机思维”迈向“嵌入式系统思维”的第一步。

4. 与外界对话:串口通信与调试信息输出

点亮LED后,下一个里程碑就是让芯片“说话”,也就是串口通信。这是调试和与上位机交互的最重要手段。

4.1 USART外设配置与引脚复用

STM32F103的USART1挂在APB2总线,其他USART挂在APB1。配置流程和GPIO类似,但多了一个概念:引脚复用。USART的TX、RX引脚通常是GPIO的复用功能。以USART1为例(PA9为TX,PA10为RX):

  1. 开启GPIOA和USART1的时钟。
  2. 配置PA9为复用推挽输出(GPIO_Mode_AF_PP),PA10为浮空输入或上拉输入(GPIO_Mode_IN_FLOATING/IPU)。
  3. 配置USART初始化结构体:波特率、字长、停止位、奇偶校验、硬件流控制、收发模式。
  4. 使能USART。
USART_InitTypeDef USART_InitStructure; // ... 开启时钟,配置GPIO ... USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE);

4.2 发送数据与重定向printf

配置好后,就可以用USART_SendData()函数发送一个字节了。但更常用的方法是重定向C库的printf函数到串口,这样就能像在PC上一样方便地打印调试信息。

重定向需要重写fputc函数:

#include <stdio.h> // 记得包含这个头文件 int fputc(int ch, FILE *f) { while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 等待发送完成 USART_SendData(USART1, (uint8_t) ch); return ch; }

然后在Keil的Options for Target->Target中,勾选Use MicroLIB(一个针对嵌入式优化的小型C库)。这样,你就可以在代码中直接使用printf(“Hello STM32!\r\n”);了。

注意:printf函数会显著增加代码体积,并且不是线程安全的。在正式项目中,如果资源紧张或需要实时性,建议自己封装一个轻量级的日志输出函数,而不是直接使用printf

4.3 接收数据与中断处理

发送解决了,接收同样重要。接收数据通常采用中断方式,避免主程序轮询占用CPU。

  1. 在USART初始化后,使能接收中断:USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  2. 在NVIC(嵌套向量中断控制器)中配置USART1中断的优先级并使能。
  3. 编写USART1的中断服务函数USART1_IRQHandler()。在函数内,判断中断来源是否是USART_IT_RXNE(接收寄存器非空),如果是,则读取数据USART_ReceiveData(USART1),并进行处理(例如存入环形缓冲区)。

一个常见的坑:串口接收一段时间后卡死,可能原因是溢出错误(ORE)。当接收数据过快,CPU来不及读取,新的数据就会覆盖旧数据并产生溢出错误。一旦发生ORE,需要先读取USART_ReceiveData(),再清除错误标志USART_ClearFlag(USART1, USART_FLAG_ORE),否则串口会一直卡在错误状态。在HAL库中,这个错误可能表现为HAL_UART_ERROR_FE之类的标志。

5. 精准的节奏大师:定时器基础与PWM应用

定时器是STM32最强大、最复杂的外设之一。从基本的定时中断,到输入捕获测频率,再到PWM输出控制电机、舵机,都离不开它。

5.1 基本定时器(TIM6/TIM7)与通用定时器(TIM2-TIM5)

F103的定时器分好几类,入门主要接触基本定时器和通用定时器。

  • 基本定时器:最简单,只有时基单元,主要用于产生定时中断或者DAC触发。
  • 通用定时器:功能全面,具备时基单元、输入捕获、输出比较、PWM生成、单脉冲输出等。TIM2-TIM5是16位向上/向下/中央对齐计数器的通用定时器。

时基单元是核心,包括:

  • 预分频器(PSC):对输入时钟(CK_PSC)进行分频。例如,系统时钟72MHz,PSC设为71,则计数器时钟 = 72MHz / (71+1) = 1MHz。
  • 计数器(CNT):在计数器时钟驱动下递增或递减。
  • 自动重装载寄存器(ARR):决定计数周期。当CNT计数到ARR值(向上计数)时,产生更新事件(UEV),CNT清零重新开始,同时可以触发中断或DMA。

定时时间的计算定时时间 = (ARR + 1) * (PSC + 1) / 定时器时钟频率。例如,定时器时钟72MHz,PSC=7199,ARR=9999,则定时时间 = (9999+1)(7199+1)/72MHz = 100007200/72,000,000 = 1秒。

5.2 输出比较与PWM生成

PWM(脉冲宽度调制)是控制亮度、速度、位置的利器。STM32的通用定时器可以很方便地生成PWM。

  1. 配置定时器时基(PSC, ARR),这决定了PWM的频率。PWM频率 = 定时器时钟 / ((PSC+1)*(ARR+1))
  2. 配置对应通道(如TIM3的通道1对应PA6)为PWM输出模式。在标准库中,使用TIM_OCInitTypeDef结构体,设置TIM_OCMode = TIM_OCMode_PWM1TIM_OutputState = TIM_OutputState_EnableTIM_Pulse(这个值决定了占空比)。
  3. TIM_Pulse是捕获比较寄存器(CCR)的值。占空比 =TIM_Pulse / (ARR+1)。例如ARR=999,TIM_Pulse=300,则占空比为30%。
  4. 使能定时器的预装载寄存器(TIM_OCPreloadConfig)并使能定时器。

之后,你只需要动态修改CCR的值,就能改变PWM的占空比。一个关键点:对于电机控制等需要多路同步PWM的场景(如三相逆变),会用到互补输出死区插入,这是高级定时器(TIM1/TIM8)的功能,初学者了解即可。

5.3 输入捕获测量频率与脉宽

输入捕获是定时器的另一个重要功能,可以用来测量外部信号的频率或脉宽。其原理是:当捕获引脚上出现指定边沿(上升沿或下降沿)时,定时器当前的CNT值会被锁存到捕获比较寄存器(CCR)中,并可以产生中断。

  1. 配置定时器时基,设置一个足够大的ARR,确保在测量信号周期内计数器不会溢出。
  2. 配置输入捕获通道,设置触发边沿(如上升沿)。
  3. 在中断中,读取CCR的值。这个值就是从上一次清零到本次边沿触发所经过的时钟计数。
  4. 连续捕获两个上升沿,将两次CCR的差值(考虑计数器溢出)乘以计数时钟的周期,就得到了信号的周期,其倒数即为频率。

测量高频率信号时,如果信号频率接近甚至超过定时器计数频率,直接测量误差会很大。此时可以采用测周法(测量多个周期求平均)或者使用定时器的外部时钟模式,将待测信号直接作为定时器的计数时钟源。

6. 应对突发事件:外部中断与NVIC优先级管理

GPIO中断,或者说外部中断(EXTI),是单片机响应外部异步事件的关键机制。比如按键按下、传感器信号跳变。

6.1 EXTI线路与GPIO的映射

STM32的EXTI控制器有19条中断/事件线(线0~15对应GPIO的Pin0~Pin15,线16~18连接特定外设如PVD、RTC)。同一个Pin编号的GPIO,只能有一个连接到EXTI线。例如,PA0、PB0、PC0…都连接到EXTI线0,但同一时刻只能选择其中一个作为中断源。

配置一个按键(接在PA0,下降沿触发)中断的步骤:

  1. 开启GPIOA和AFIO(复用功能IO,用于EXTI引脚选择)的时钟。
  2. 配置PA0为上拉输入(这样按键按下时产生下降沿)。
  3. 配置EXTI线0与PA0连接:GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
  4. 配置EXTI初始化结构体:选择线0,触发方式为下降沿,使能中断。
  5. 配置NVIC,设置EXTI0中断通道的优先级并使能。
  6. 编写中断服务函数EXTI0_IRQHandler(),在函数内判断是否是EXTI线0的中断,处理按键逻辑,最后必须清除中断标志位EXTI_ClearITPendingBit(EXTI_Line0);,否则会不断进入中断。

6.2 NVIC:中断的调度中心

NVIC(嵌套向量中断控制器)是Cortex-M3内核的一部分,负责管理所有中断的优先级和响应。

  • 抢占优先级 & 子优先级:STM32的中断优先级分为抢占优先级和子优先级。抢占优先级高的可以打断抢占优先级低的中断(嵌套)。抢占优先级相同的中断同时发生时,子优先级高的先执行;如果抢占和子优先级都相同,则按硬件中断编号顺序执行。
  • 优先级分组:通过NVIC_PriorityGroupConfig()设置优先级分组,来决定多少位用于抢占优先级,多少位用于子优先级。例如NVIC_PriorityGroup_2表示2位抢占优先级(0~3),2位子优先级(0~3)。这个设置整个项目只需要调用一次,通常在main函数开头。
  • 中断服务函数:每个中断都有固定的函数名,必须与启动文件中定义的向量表一致。例如EXTI0中断函数必须命名为EXTI0_IRQHandler。在函数内部,要先判断具体是哪个中断源(因为多个EXTI线可能共用一个中断向量),处理完后必须清除对应的中断标志位。

一个重要的经验:中断服务函数要尽可能短小精悍,只做最紧急的事情(如设置标志位、拷贝数据)。复杂的处理逻辑应该放到主循环中,根据中断设置的标志位来执行。这就是“前台后台”系统的基本思想,避免在中断中耗时过长,影响其他中断的响应。

7. 直接内存访问:DMA的基本概念与应用场景

当你需要大量、高速地在内存和外设之间搬运数据时(比如ADC采集一串数据存到数组,或者从内存发送一大段数据到串口),如果都用CPU来搬运,会严重消耗CPU资源。DMA(直接内存访问)就是为了解放CPU而生的。

7.1 DMA工作原理与通道资源

DMA控制器就像一个“智能的数据搬运工”。你告诉它:源地址在哪里(比如ADC数据寄存器),目标地址在哪里(比如内存中的一个数组),要搬多少数据,数据宽度是多少。然后你启动它,它就会在后台默默工作,搬完指定数量的数据后,给你发一个中断通知,整个过程几乎不占用CPU。

STM32F103有两个DMA控制器(DMA1和DMA2),每个控制器有若干通道。不同的外设会固定分配到特定的DMA通道上。例如,DMA1的通道1可以用于ADC1、TIM2_CH3等。配置时不能冲突。

7.2 以串口发送为例的DMA配置

使用DMA进行串口发送,可以极大地提高效率,尤其是在发送大量数据(如图像、文件)时。

  1. 开启USART和对应DMA通道的时钟。
  2. 配置USART,使能其DMA发送请求(USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);)。
  3. 配置DMA初始化结构体:
    • DMA_PeripheralBaseAddr:外设地址,即USART1->DR
    • DMA_MemoryBaseAddr:内存地址,即你要发送的数据数组首地址。
    • DMA_DIR:传输方向,从内存到外设。
    • DMA_BufferSize:要传输的数据量。
    • DMA_PeripheralInc:外设地址是否递增,否(固定为DR寄存器)。
    • DMA_MemoryInc:内存地址是否递增,是(依次发送数组元素)。
    • DMA_PeripheralDataSize/DMA_MemoryDataSize:数据宽度,字节/半字/字。
    • DMA_Mode:模式,可选单次传输或循环传输。
    • DMA_Priority:通道优先级。
  4. 使能DMA通道。当需要发送时,设置好内存地址和缓冲区大小,然后使能USART的DMA请求,传输就会自动开始。

使用DMA的注意事项

  • 缓存一致性:如果内存缓冲区位于CPU缓存中(在STM32F103这种没有Cache的芯片上不存在此问题,但在高级芯片上需要注意),DMA操作的是物理内存,CPU操作的是缓存,可能导致数据不一致。通常需要手动刷新或无效化缓存。
  • 传输完成中断:务必在DMA传输完成中断中,关闭DMA通道或进行下一轮操作准备,并清除中断标志。避免重复触发。
  • 内存对齐:确保源和目标地址的数据宽度对齐,否则可能导致硬件错误。

8. 模拟世界的窗口:ADC采集与数据处理基础

单片机处理的是数字信号,但现实世界很多信号是模拟的(温度、光照、电压)。ADC(模数转换器)就是连接两者的桥梁。

8.1 STM32F103的ADC特性与配置

F103的ADC是12位逐次逼近型。12位分辨率意味着可以将参考电压(通常是3.3V)分成4096(2^12)个等级,每个等级约0.8mV。配置ADC涉及以下关键参数:

  • 采样时间:ADC内部有一个采样保持电容,采样时间决定了给这个电容充电的时间。对于高内阻的信号源,需要更长的采样时间才能保证采样准确。STM32的ADC采样时间可以配置为1.5到239.5个ADC时钟周期。
  • 转换模式
    • 单次转换:触发一次,转换一个通道后停止。
    • 连续转换:开启后,自动连续转换指定通道。
    • 扫描模式:配合多通道,自动按顺序转换一组通道。
  • 触发源:可以是软件触发(ADC_SoftwareStartConvCmd),也可以是定时器触发,实现定时等间隔采样。
  • 数据对齐:12位结果可以左对齐或右对齐存放在16位数据寄存器中。右对齐更直观。

一个简单的单通道ADC读取流程(以规则通道为例):

  1. 开启ADC和GPIO(模拟输入引脚)时钟。
  2. 配置GPIO为模拟输入模式。
  3. 配置ADC初始化结构体:分辨率、扫描/连续模式、触发方式、数据对齐。
  4. 配置ADC规则通道:选择通道号、采样时间。
  5. 使能ADC,进行校准(先执行复位校准,再执行校准)。
  6. 软件触发转换,等待转换完成标志位ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC),然后读取数据ADC_GetConversionValue(ADC1)

8.2 提高ADC精度:硬件与软件滤波

ADC的读数总会存在波动和噪声。为了提高稳定性,需要滤波。

  • 硬件滤波:在ADC输入引脚加一个小的滤波电容(如0.1uF)到地,可以滤除高频噪声。对于低频信号,可以使用RC低通滤波电路。
  • 软件滤波:最简单有效的是多次采样取平均。例如连续采样64次,然后求和取平均值。更高级的可以用中值滤波、滑动平均滤波等。

一个关于参考电压的坑:STM32F103的ADC参考电压默认是VDDA(通常与VCC相连,3.3V)。如果VCC不稳定,ADC读数就会漂移。对于精度要求高的场合,建议使用独立、稳定的基准电压源芯片给VREF+引脚供电,并将ADC配置为使用外部VREF+

8.3 定时器触发与DMA搬运

在需要高速、连续采样的场合(如音频信号),单次软件触发效率太低。这时需要结合定时器和DMA:

  1. 配置一个定时器(如TIM2),产生固定频率的更新事件(PWM输出模式也可以,但通常用更新事件作为触发源)。
  2. 配置ADC为外部触发转换,触发源选择该定时器的触发输出(如ADC_ExternalTrigConv_T2_TRGO)。
  3. 配置DMA,将ADC数据寄存器(ADC1->DR)的数据自动搬运到内存中的一个大型数组。
  4. 启动定时器、使能ADC的DMA、使能ADC。
  5. 定时器会按照设定频率自动触发ADC转换,转换结果通过DMA实时存到内存。你只需要在DMA传输完成中断中,处理已经采集好的一整块数据即可。这种方式实现了“无人值守”的高速数据采集,CPU开销极低。
http://www.jsqmd.com/news/1402836/

相关文章:

  • AI代码生成工具实战指南:从环境配置到提示词工程
  • 运维工程师如何写好分布式项目简历:从操作员到架构师的思维转变
  • 2026年东莞市海晨新能源科技有限公司:磷酸铁锂粉回收的专业合规与绿色价值之选 - 卓企推荐
  • 2026精选一物一码营销公司推荐,适合快消品牌做渠道动销
  • Word交叉引用:告别手动编号,实现参考文献动态管理
  • 并发编程核心:从线程互斥到锁机制与线程安全实践
  • CSS padding属性详解与实战应用
  • 江苏一网推怎么样?客户口碑如何?江苏一网推布局AI搜索优化及GEO行业新时代!
  • Indigo Nebula AI — 漫剧一键生成平台开发记录
  • 研发流程管理实战:从BPM思想到敏捷实践,打造高效价值交付系统
  • 机器人产需共融:从手眼标定到系统可靠,备战WRC 2026能力大考
  • 基于Arduino与OpenCV的仿生眼球人脸跟踪系统实现
  • 奔驰汽车修理的技术剖析与实践指南
  • 2026化工企业必看:人员定位系统厂家如何选?附合规方案及代表厂商
  • SketchUp性能优化全攻略:从硬件配置到建模习惯解决卡顿崩溃
  • 基于CodeBuddy Agent构建智能相机推荐系统:从需求分析到规则引擎实现
  • VSCode Remote-SSH远程开发配置全攻略:从零搭建高效云端编程环境
  • Zabbix Server服务故障排查:从数据库连接到性能调优的完整指南
  • 孩子指甲总长白点要不要考虑给他补锌?2026年8款在园娃横评榜单
  • 贵阳有哪些比较靠谱的AI公司呢?
  • 2026实力之选:东莞房屋拆除施工品牌机构解析与采购参考 - 卓企推荐
  • VLAN划分技术全解析:从静态端口到动态策略的实战指南
  • C++快速读入优化技巧
  • 调试器断点失效:符号加载与源代码映射的深度解析
  • Ubuntu 20.04安装Sublime Text 4:免许可证弹窗配置与效率优化指南
  • 吃个奶酪吧
  • Cursor AI编辑器免费额度用尽?合法续杯与第三方API接入全攻略
  • AI Agent产品设计:从工具到智能伙伴的架构与实战
  • 图文教程:用国内大模型 API 跑通Codex
  • UWB人员定位系统:有线方案与无线方案,究竟该如何选择?