記錄一下,方便以後翻閱~
主要內容:
1) PWM原理複習;
2) DAC PWM數模轉換原理;
3) 相關實驗代碼解讀。
實驗功能:系統啓動後,PA8輸出PWM波形,經二階RC濾波後轉化爲DAC輸出,按WK_UP鍵輸出電壓變大,按KEY1鍵,輸出電壓降低。每次按鍵,ADC採集輸出電壓值並傳至串口調試助手上。
官方資料:《STM32中文參考手冊V10》第12章——數字模擬轉換DAC和第14章——通用定時器
硬件連接
定時器1通道1輸出PWM,通過PA8輸出,經過二階RC濾波後輸出電壓。
1. PWM複習
也可參考《STM32學習心得十八:通用定時器基本原理及相關實驗代碼解讀》實驗二部分知識,基本一樣。
1.1 PWM工作原理複習
脈衝寬度調製(PWM——Pulse Width Modulation)簡稱脈寬調製,是利用微處理器的數字輸出來對模擬電路進行控制的一種技術。簡單一點,就是對脈衝寬度的控制。
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:打開。
1.2 計數模式有哪些?
1) 向上計數模式:計數器從0計數到自動加載值(TIMx_ARR計數器的值),然後重新從0開始計數並且產生一個計數器溢出事件;
2) 向下計數模式:計數器從自動裝入的值 (TIMx_ARR計數器的值)開始向下計數到0,然後從自動裝入的值重新開始並且產生一個計數器向下溢出的事件;
3) 中央對齊模式(向上/向下計數) :計數器從0開始計數到自動加載的值(TIMx_ARR寄存器的值−1),產生一個計數器溢出事件,然後向下計數到1並且產生一個計數器下溢事件,然後再從0開始重新計數。
1.3 STM32哪些定時器可用於PWM輸出?
由上圖可知,除了基本定時器TIM6和TIM7,其他的定時器都可以用來產生PWM輸出。其中,高級定時器TIM1和TIM8可以同時產生多達7路的PWM輸出。而通用定時器也能同時產生多達4路的PWM輸出。
1.4 STM32 定時器輸出通道引腳整理(可參考數據手冊)
1.5 對自動重載的預裝載寄存器的理解
自動裝載寄存器是預先裝載的,寫或讀自動重裝載寄存器將訪問預裝載寄存器。根據在TIMx_CR1寄存器中的自動裝載預裝載使能位(ARPE)的設置,預裝載寄存器的內容被立即或在每次的更新事件UEV時傳送到影子寄存器。
影子寄存器保存的是定時器當前的計數值(或者溢出值),這個值是立即生效的值,這個計數值是從預裝載寄存器(ARR)傳過來的,但ARR什麼時候把計數值傳給影子寄存器呢?這兒就有個預裝載使能位(ARPE):當ARPE=0的時候,你寫入ARR的值馬上就傳到影子寄存器,也就立即生效當ARPE=1的時候,ARR的值就是直接傳過去了,而是等到定時器更新事件發生,才把這個值傳到影子寄存器,也就起到一個緩衝作用。
舉例說明:
如上圖所示,當ARPE=1時,自動加載寄存器值從F5改爲36時,計數器寄存器值從F0增加至F5時產生更新事件,說明自動加載寄存器值從F5改爲36時就已生效。
如上圖所示,當ARPE=0時,自動加載寄存器值從FF改爲36時,計數器寄存器值從31增加至36時產生更新事件,說明自動加載寄存器值從F5改爲36時未即使生效,要等下個比較週期生效。
簡單的說:
ARPE=1,ARR立即生效;
APRE=0,ARR下個比較週期生效。
1.6 PWM輸出配置一般步驟(以TIM3_CH2爲例)
1.6.1 使能定時器3和相關IO口時鐘:
使能定時器3時鐘:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
使能GPIOB時鐘:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
1.6.2 初始化IO口爲複用推輓輸出。函數:GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
1.6.3 開啓AFIO時鐘(因爲要把PB5(對應LED0)作定時器的PWM輸出引腳,所以要重映射配置),同時設置重映射。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //部分重映射,即TIM3_CH2對應PB5引腳//
1.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),只有高級定時器纔有//
1.6.4.1 uint16_tTIM_Prescaler參數解讀
用來確定最後計數器的時鐘頻率,其值與CK_PSC頻率相關。
而CK_PSC頻率與選擇哪個時鐘源有關,如下圖所示,一般選擇內部時鐘CK_INT爲時鐘源,即CK_INT=CK_PSC。
CK_INT時鐘計算方法如下圖所示:
當APB1的分頻係數是1時,通用定時器的時鐘等於APB1時鐘的1倍,否則是2倍。
舉例:默認調用SystemInit函數情況下:
SYSCLK=72M
AHB時鐘=72M
APB1時鐘=36M
所以APB1的分頻係數=AHB/APB1時鐘=2;
所以,通用定時器時鐘CK_INT=2*36M=72M。
1.6.4.2 uint16_tTIM_ClockDivision參數解讀
針對控制寄存器1(TIM3_CR1)的位[8:9],設定時鐘分頻因子CKD。若其值爲01,則tDTS=2tCK_INT,說明要連續採樣2次電平,且都是預期電平值,才能說明是一次有效觸發,產生一次觸發輸入捕獲中斷(起濾波作用)。
1.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; 比較輸出極性//
1.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爲有效電平,否則爲無效電平。
1.6.5.2 uint16_t TIM_Pulse參數解讀
針對捕獲/比較寄存器 1(TIM3_CCR1),若CC1通道配置爲輸出,CCR1包含了裝入當前捕獲/比較1寄存器的值(預裝載值);若CC1通道配置爲輸入,CCR1包含了由上一次輸入捕獲1事件(IC1)傳輸的計數器值。
1.6.5.3 uint16_t TIM_OCPolarity參數解讀
針對捕獲/比較使能寄存器(TIM3_CCER),設置極性,即高電平有效或低電平有效。
1.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,因爲軟件不可能在一個相同的時刻同時更新多個寄存器,結果造成多個通道的時序不能同步,如果再加上其它因素(例如中斷),多個通道的時序關係有可能是不可預知的。
1.6.7 使能定時器:
TIM_Cmd(TIM3, Enable);
1.6.8 不斷改變比較值CCRx,達到不同的佔空比效果:
TIM_SetCompare2(TIM3, uint16_t Compare2);
//針對捕獲/比較寄存器2(TIM3_CCR2),位[0:15],設置捕獲/比較2(CCR2)的值//
2. DAC PWM數模轉換原理
PWM本質上其實就是是一種週期一定,而高低電平佔空比可調的方波。
上圖中,N爲ARR-1個計數,n爲CCR寄存器的值,T爲週期時間,
PWM波形可以用分段函數表示爲式:
其中:
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。
3. 相關實驗代碼解讀
這裏會將PWM輸出實驗和PWM DAC輸出實驗代碼進行對比,其實兩個實驗的代碼基本一樣。
3.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
3.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);
}
3.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相關文件。
實驗結果
舊知識點
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學習心得二十五:光敏傳感器原理及實驗。