STM32学习心得二十七:PWM DAC数模转换实验及PWM知识复习

记录一下,方便以后翻阅~
在这里插入图片描述

1. 内容简述

1.1主要内容

1) PWM原理复习;
2) DAC PWM数模转换原理;
3) 相关实验代码解读。

1.2 实验功能

系统启动后,PA8输出PWM波形,经二阶RC滤波后转化为DAC输出,按WK_UP键输出电压变大,按KEY1键,输出电压降低。每次按键,ADC采集输出电压值并传至串口调试助手上。

1.3 官方资料

《STM32中文参考手册V10》第12章——数字模拟转换DAC和第14章——通用定时器

1.4 硬件连接

图1 硬件原理图

图1 硬件原理图

定时器1通道1输出PWM,通过PA8输出,经过二阶RC滤波后输出电压。

2 PWM复习

也可参考《STM32学习心得十八:通用定时器基本原理及相关实验代码解读》实验二部分知识,基本一样。

2.1 PWM工作原理复习

脉冲宽度调制(PWM——Pulse Width Modulation)简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种技术。简单一点,就是对脉冲宽度的控制。
PWM工作逻辑可参考下图理解:
图2 PWM原理

图2 PWM原理

上图中,
ARR为自动重装载寄存器(TIMx_ARR)的位[0:15]设定值,
CCRx为某个捕获比较寄存器(TIMx_CRRx)的位[0:15]设定值,
黑线为计数器寄存器(TIMx_CNT)的位[0:15]实时值,记CNT。
当CNT小于CCRx时,可设输出低电平,反之输出高电平。因此可以认为:脉宽调制(PWM)信号的周期由ARR决定,占空比由CCRx决定。

以通道1为例,寄存器主要包括
1) 捕获比较(值)寄存器(TIMx_CCRx(x=1,2,3,4)),用来设置比较值;
2) 捕获/比较模式寄存器 1(TIMx_CCMR1),位[4:6],输出比较1模式(OC1M):
在PWM方式下,设置PWM模式1(110),在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1);
或在PWM方式下,设PWM模式2(111),在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平(OC1REF=1),否则为无效电平(OC1REF=0)。
3) 捕获/比较使能寄存器(TIMx_CCER),位1,输入/捕获1输出极性CC1P:
当CC1通道配置为输出时,0:高电平有效,1:低电平有效;
4) 捕获/比较使能寄存器(TIMx_CCER),位0,输入/捕获1输出使能CC1E:
当CC1通道配置为输出时,0:关闭,1:打开。

2.2 计数模式有哪些?

1) 向上计数模式:计数器从0计数到自动加载值(TIMx_ARR计数器的值),然后重新从0开始计数并且产生一个计数器溢出事件;
2) 向下计数模式:计数器从自动装入的值 (TIMx_ARR计数器的值)开始向下计数到0,然后从自动装入的值重新开始并且产生一个计数器向下溢出的事件;
3) 中央对齐模式(向上/向下计数) :计数器从0开始计数到自动加载的值(TIMx_ARR寄存器的值−1),产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器下溢事件,然后再从0开始重新计数。

2.3 STM32哪些定时器可用于PWM输出?

图3

图3

由上图可知,除了基本定时器TIM6和TIM7,其他的定时器都可以用来产生PWM输出。其中,高级定时器TIM1和TIM8可以同时产生多达7路的PWM输出。而通用定时器也能同时产生多达4路的PWM输出。

2.4 STM32 定时器输出通道引脚整理(可参考数据手册)

图4 STM32 定时器输出通道引脚整理

图4 STM32 定时器输出通道引脚整理

2.5 对自动重载的预装载寄存器的理解

自动装载寄存器是预先装载的,写或读自动重装载寄存器将访问预装载寄存器。根据在TIMx_CR1寄存器中的自动装载预装载使能位(ARPE)的设置,预装载寄存器的内容被立即或在每次的更新事件UEV时传送到影子寄存器。
影子寄存器保存的是定时器当前的计数值(或者溢出值),这个值是立即生效的值,这个计数值是从预装载寄存器(ARR)传过来的,但ARR什么时候把计数值传给影子寄存器呢?这儿就有个预装载使能位(ARPE):当ARPE=0的时候,你写入ARR的值马上就传到影子寄存器,也就立即生效当ARPE=1的时候,ARR的值就是直接传过去了,而是等到定时器更新事件发生,才把这个值传到影子寄存器,也就起到一个缓冲作用。
举例说明:
图5 计数器时序图1

图5 计数器时序图1

如上图所示,当ARPE=1时,自动加载寄存器值从F5改为36时,计数器寄存器值从F0增加至F5时产生更新事件,说明自动加载寄存器值从F5改为36时就已生效。
图6 计数器时序图2

图6 计数器时序图2

如上图所示,当ARPE=0时,自动加载寄存器值从FF改为36时,计数器寄存器值从31增加至36时产生更新事件,说明自动加载寄存器值从F5改为36时未即使生效,要等下个比较周期生效。
简单的说:
ARPE=1,ARR立即生效;
APRE=0,ARR下个比较周期生效。

2.6 PWM输出配置一般步骤(以TIM3_CH2为例)

2.6.1 使能定时器3和相关IO口时钟:

使能定时器3时钟:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

使能GPIOB时钟:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

2.6.2 初始化IO口为复用推挽输出。函数:GPIO_Init();

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;      

2.6.3 开启AFIO时钟(因为要把PB5(对应LED0)作定时器的PWM输出引脚,所以要重映射配置),同时设置重映射。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);    //部分重映射,即TIM3_CH2对应PB5引脚//

图7

图7

2.6.4 初始化定时器,ARR,PSC等:

TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitTypeStruct);
//TIM_TimeBaseInitTypeStruct结构体包括5个参数://
//uint16_t TIM_Prescaler;          针对预分频器寄存器(TIMx_PSC),值范围在0x0000到0xFFFF之间//
//uint16_t TIM_CounterMode;        计数模式,前面已讲//
//uint16_t TIM_Period;             针对自动重装载寄存器(TIMx_ARR),确定脉宽调制信号的周期,前面已讲//
//uint16_t TIM_ClockDivision;      针对控制寄存器1(TIMx_CR1)的位[8:9],设定时钟分频因子CKD,输入捕获用//
//uint8_t TIM_RepetitionCounter。  重复计数寄存器(TIMx_RCR),只有高级定时器才有//
2.6.4.1 uint16_tTIM_Prescaler参数解读

用来确定最后计数器的时钟频率,其值与CK_PSC频率相关。
图8

图8

而CK_PSC频率与选择哪个时钟源有关,如下图所示,一般选择内部时钟CK_INT为时钟源,即CK_INT=CK_PSC。
图9

图9

CK_INT时钟计算方法如下图所示:
图10

图10

当APB1的分频系数是1时,通用定时器的时钟等于APB1时钟的1倍,否则是2倍。
举例:默认调用SystemInit函数情况下:
SYSCLK=72M
AHB时钟=72M
APB1时钟=36M
所以APB1的分频系数=AHB/APB1时钟=2;
所以,通用定时器时钟CK_INT=2*36M=72M。

2.6.4.2 uint16_tTIM_ClockDivision参数解读

针对控制寄存器1(TIM3_CR1)的位[8:9],设定时钟分频因子CKD。若其值为01,则tDTS=2tCK_INT,说明要连续采样2次电平,且都是预期电平值,才能说明是一次有效触发,产生一次触发输入捕获中断(起滤波作用)。
图11

图11

2.6.5 初始化输出比较参数:

TIM_OC2Init(TIM3,&TIM_OCInitTypeStruct);
//TIM_OCInitTypeStruct结构体主要涉及4个参数://
//uint16_t TIM_OCMode;        选择PWM模式1或者模式2//
//uint16_t TIM_OutputState;   输出使能或失能//
//uint16_t TIM_Pulse;         设比较值//
//uint16_tTIM_OCPolarity;     比较输出极性//
2.6.5.1 uint16_t TIM_OCMode参数解读

在TIMx_CCMR3寄存器中的OCxM位选择PWM模式1或模式2。
设为110时为PWM模式1,在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1);
设为111时为PWM模式2,在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平。

2.6.5.2 uint16_t TIM_Pulse参数解读

针对捕获/比较寄存器 1(TIM3_CCR1),若CC1通道配置为输出,CCR1包含了装入当前捕获/比较1寄存器的值(预装载值);若CC1通道配置为输入,CCR1包含了由上一次输入捕获1事件(IC1)传输的计数器值。

2.6.5.3 uint16_t TIM_OCPolarity参数解读

针对捕获/比较使能寄存器(TIM3_CCER),设置极性,即高电平有效或低电平有效。

2.6.6 使能预装载寄存器:

TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); 
//针对捕获/比较模式寄存器1(TIM3_CCMR1),位[11],设置OC2PE值//

设计preload register和shadow register的好处是,所有真正需要起作用的寄存器(shadow register)可以在同一个时间(发生更新事件时)被更新为所对应的preload register的内容,这样可以保证多个通道的操作能够准确地同步。如果没有shadow register,或者preload register和shadow register是直通的,即软件更新preload register时,同时更新了shadow register,因为软件不可能在一个相同的时刻同时更新多个寄存器,结果造成多个通道的时序不能同步,如果再加上其它因素(例如中断),多个通道的时序关系有可能是不可预知的。

2.6.7 使能定时器:

TIM_Cmd(TIM3, Enable);

2.6.8 不断改变比较值CCRx,达到不同的占空比效果:

TIM_SetCompare2(TIM3, uint16_t Compare2);
//针对捕获/比较寄存器2(TIM3_CCR2),位[0:15],设置捕获/比较2(CCR2)的值//

3. DAC PWM数模转换原理

PWM本质上其实就是是一种周期一定,而高低电平占空比可调的方波。
图12

图12

上图中,N为ARR-1个计数,n为CCR寄存器的值,T为周期时间,
PWM波形可以用分段函数表示为式:
图13

其中:
T是单片机中计数脉冲的基本周期,也就是STM32定时器的计数频率的倒数;
N是PWM波一个周期的计数脉冲个数,也就是STM32的ARR-1的值;
n是PWM波一个周期中高电平的计数脉冲个数,也就是STM32的CCRx的值;
VH和VL分别是PWM波的高低电平电压值,k为谐波次数,t为时间。
将上式展开成傅里叶级数,得到以下公式(不用理解如何推导):
在这里插入图片描述
上式,第1项为直流分量,第2项为1次谐波分量,第3项为大于1次的高次谐波分量。直流分量与n成线性关系,并随着n从0到N,直流分量从VL到VL+VH之间变化。这是电压输出DAC所需要的。

因此,如果能把上式中除直流分量外的谐波过滤掉,则可以得到从PWM波到电压输出DAC的转换,即:PWM波可以通过一个低通滤波器进行解调。上式中的第2项的幅度和相角与n有关,频率为1/(NT),其实就是PWM的输出频率。该频率是设计低通滤波器的依据。如果能把1次谐波很好过滤掉,则高次谐波就应该基本不存在了。

通过上面的了解,我们可以得到PWM DAC的分辨率,计算公式如下:

分辨率=log2(N)

这里假设n的最小变化为1,当N=256的时候,分辨率就是8位。而STM32的定时器都是16位的,可以很容易得到更高的分辨率,分辨率越高,速度就越慢。不过我们在本章要设计的DAC分辨率为8位。

在8位分辨条件下,我们一般要求1次谐波对输出电压的影响不要超过1个位的精度,也就是3.3/256=0.01289V,那么1次谐波的值不能大于0.01289V。

假设VH为3.3V,VL为0V,那么一次谐波的最大值是2*3.3/π=2.1V,这就要求我们的RC滤波电路提供至少-20lg(2.1/0.01289)=-44dB的衰减。(这里,推测衰减量公式为:-20lg(谐波最大幅值/最小精度)=衰减值,暂没有更好的解释)

STM32的定时器最快的计数频率是72Mhz,8位分辨率的时候,PWM频率为72M/256=281.25Khz。如果是1阶RC滤波,则要求截止频率为1.77Khz,如果为2阶RC滤波,则要求截止频率为22.34Khz。

参考网上的解释
1)PWM频率为328.125Khz,那么一次谐波频率就是328.125Khz;
2)1阶RC滤波,幅频特性为:-10lg[1+(f/fp)^2];fp为截止频率。
所以对一阶滤波来说,要达到-44dB的衰减,必须-10lg[1+(f/fp)^2]=-44; 得到f/fp=158.486,即fp=328.125/158.486=2.07Khz。
3)2阶RC滤波,幅频特性为:-20lg[1+(f/fp)^2];fp为截止频率。
所以对二阶滤波来说,要达到-44dB的衰减,必须-20lg[1+(f/fp)^2]=-44; 得到f/fp=12.549,即fp=328.125/12.549=26.14Khz。

4. 相关实验代码解读

这里会将PWM输出实验和PWM DAC输出实验代码进行对比,其实两个实验的代码基本一样。

4.1 timer.h头文件代码解读

PWM DAC输出实验

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
//PWM DAC输出实验是让GPIOA,引脚8输出PWM波形,经二阶RC滤波后,输出稳定DAC值,对应配置是选择复用,高级定时器TIM1_CH1//
void TIM1_PWM_Init(u16 arr,u16 psc);
#endif

PWM输出实验

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
//PWM输出实验是让GPIOB,引脚5输出PWM波形,从而控制LED灯呼吸,对应配置是选择重映射,通用定时器TIM3_CH2//
void TIMER3_PWM_Init(u16 arr, u16 psc);
#endif

4.2 timer.c文件代码解读

PWM DAC输出实验

#include "timer.h"
#include "led.h"
//PWM DAC,定时器1通道1输出PWM波形,通过PA8输出,经过二阶RC滤波后输出电压//
void TIM1_PWM_Init(u16 arr,u16 psc)
{         
 GPIO_InitTypeDef GPIO_InitStructure;
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 TIM_OCInitTypeDef  TIM_OCInitStructure;
 //第1步,时钟使能//
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);   //使能TIM1外设时钟使能//
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA外设时钟使能// 
 //第2步,初始化GPIOA,引脚8,推挽复用输出功能,输出TIM1 CH1的PWM脉冲波形//
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;              //GPIOA, PA8//
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出//
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure); 
 //第3步,初始化定时器//
 TIM_TimeBaseStructure.TIM_Period = arr;                       //设置自动重装载周期值//
 TIM_TimeBaseStructure.TIM_Prescaler =psc;                     //设置预分频值//
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   //TIM向上计数模式//
 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;       //设置时钟分割:TDTS = Tck_tim//
 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); 
 //第4步,初始化输出比较参数// 
 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;             //CH1 PWM2模式// 
 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能//
 TIM_OCInitStructure.TIM_Pulse = 100;                          //设置待装入捕获比较寄存器的脉冲值//
 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;      //OC1 低电平有效// 
 TIM_OC1Init(TIM1, &TIM_OCInitStructure);  
 //第5步,使能预装载寄存器//
 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  
 //*****下面两行,在定时器PWM输出实验里没有*****//
 TIM_ARRPreloadConfig(TIM1, ENABLE);                           //使能TIMx在ARR上的预装载寄存器//
 TIM_CtrlPWMOutputs(TIM1,ENABLE);                              //主输出使能,高级定时器必须开启// 
 //*****上述两行,在定时器PWM输出实验里没有*****//
 //最后一步,使能TIM1// 
 TIM_Cmd(TIM1, ENABLE);                
} 

PWM输出实验

#include "timer.h"
#include "led.h"
//PWM实验,定时器3通道2输出PWM波形,从而控制LED灯//
void TIMER3_PWM_Init(u16 arr, u16 psc)
{
GPIO_InitTypeDef GPIO_InitTypeStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeStruct;
TIM_OCInitTypeDef TIM_OCInitTypeStruct;
//第1步,使能TIM3时钟,使能GPIOB时钟,使能AFIO时钟(PWM DAC实验不用)//
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);
//第2步,初始化GPIOB5为复用推挽输出,50MHz//
GPIO_InitTypeStruct.GPIO_Pin=GPIO_Pin_5;
GPIO_InitTypeStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitTypeStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitTypeStruct);
//*****开启部分使能重映射******//
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); 
//第3步,定时器参数初始化//
TIM_TimeBaseInitTypeStruct.TIM_Period=arr;
TIM_TimeBaseInitTypeStruct.TIM_Prescaler=psc;
TIM_TimeBaseInitTypeStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitTypeStruct.TIM_ClockDivision=TIM_CKD_DIV1; 
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitTypeStruct);
//第4步,初始化输出比较参数//
TIM_OCInitTypeStruct.TIM_OCMode=TIM_OCMode_PWM2;                 //CH2 PWM2模式// 
TIM_OCInitTypeStruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitTypeStruct.TIM_Pulse=100;                              //设置待装入捕获比较寄存器的脉冲值//
TIM_OCInitTypeStruct.TIM_OCPolarity=TIM_OCPolarity_Low;          //OC2 低电平有效// 
TIM_OC2Init(TIM3,&TIM_OCInitTypeStruct);
//第5步,使能预装载寄存器//
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); 
//最后一步,使能TIM3//
TIM_Cmd(TIM3,ENABLE);
}

4.3 main.c文件代码解读

PWM DAC输出实验

#include "led.h"
#include "key.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"   
#include "adc.h"
#include "timer.h"
 int main(void)
 {  
 u16 adcx;
 float temp;
 u8 t=0;  
 u16 pwmval=0;
 u8 key;
 delay_init();                  //延时函数初始化//   
 uart_init(115200);             //串口初始化为115200//
 KEY_Init();                    //KEY初始化//
 LED_Init();                    //LED端口初始化//
 Adc_Init();                    //ADC初始化//
 TIM1_PWM_Init(255,0);          //TIM1 PWM初始化, Fpwm=72M/256=281.25Khz//     
 TIM_SetCompare1(TIM1,pwmval);            
 while(1)
 {
  key=KEY_Scan(0);     
  if(key==WKUP_PRES)
  {   
   if(pwmval<250)pwmval+=10;
   TIM_SetCompare1(TIM1,pwmval);   
  }else if(key==KEY1_PRES) 
  {
   if(pwmval>10)pwmval-=10;
   else pwmval=0;
   TIM_SetCompare1(TIM1,pwmval);   
  }  
  if(t==10||key==KEY1_PRES||key==WKUP_PRES)        //手动按键改变占空比,从而改变DAC值//
  {   
   adcx=TIM_GetCapture1(TIM1);
   printf("TIM1_CCR1值:%d\r\n",adcx); 
   temp=(float)adcx*(3.3/256);    
   printf("TIM1_CCR1对应电压值:%f\r\n",temp); 
   adcx=Get_Adc_Average(ADC_Channel_1,10);
   temp=(float)adcx*(3.3/4096);
   printf("ADC读取值:%d\r\n",adcx); 
   printf("ADC对应电压大小:%f\r\n",temp);   
  }     
  delay_ms(200); 
  LED0=!LED0;  
 }
 }

PWM输出实验

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "timer.h"
 int main(void)
 {  
 u16 led0pwmval=0;
 u8 dir=1;                //方向,1时led0pwmval加,0时led0pwmval减//
 delay_init();            //延时函数初始化//  
 LED_Init();              //LED端口初始化//
 TIMER3_PWM_Init(899,0);  //不分频。PWM频率=72000000/900=80Khz//
 while(1)
 {
  delay_ms(1);            //每隔1ms改变一次占空比//
  if(dir)
    led0pwmval++;
  else 
    led0pwmval--;
  if(led0pwmval>1000)
    dir=0;
  if(led0pwmval==0)
    dir=1;           
  TIM_SetCompare2(TIM3,led0pwmval);     
 }  
 }

PWM DAC和PWM输出实验的代码基本一样,不同之处为,PWM DAC输出实验利用高级定时器TIM1的通道1输出(即GPIOA,PA8),而PWM输出实验利用通用定时器TIM3的通道2输出(即GPIOB,PB5),因此相关配置会略有不同。
再者,PWM DAC输出实验,DAC主要靠硬件设计,非代码,因此该实验案例无需stm32f10x_dac.c相关文件。

5. 实验结果

图13

图13

旧知识点

1)复习如何新建工程模板,可参考STM32学习心得二:新建工程模板
2)复习基于库函数的初始化函数的一般格式,可参考STM32学习心得三:GPIO实验-基于库函数
3)复习寄存器地址,可参考STM32学习心得四:GPIO实验-基于寄存器
4)复习位操作,可参考STM32学习心得五:GPIO实验-基于位操作
5)复习寄存器地址名称映射,可参考STM32学习心得六:相关C语言学习及寄存器地址名称映射解读
6)复习时钟系统框图,可参考STM32学习心得七:STM32时钟系统框图解读及相关函数
7)复习延迟函数,可参考STM32学习心得九:Systick滴答定时器和延时函数解读
8)复习ST-LINK仿真器的参数配置,可参考STM32学习心得十:在Keil MDK软件中配置ST-LINK仿真器
9)复习ST-LINK调试方法,可参考STM32学习心得十一:ST-LINK调试原理+软硬件仿真调试方法
10)复习如何对GPIO进行复用,可参考STM32学习心得十二:端口复用和重映射
11)复习串口通信相关知识,可参考STM32学习心得十四:串口通信相关知识及配置方法
12)复习ADC原理及一般配置步骤,可参考STM32学习心得二十三:ADC转换原理及模数转换实验STM32学习心得二十四:内部温度传感器原理及实验STM32学习心得二十五:光敏传感器原理及实验

  • 0
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天亮继续睡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值