STM32F103系列单片机学习笔记1方便以后查看
生活随笔
收集整理的這篇文章主要介紹了
STM32F103系列单片机学习笔记1方便以后查看
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
系統(tǒng)中斷管理:NVIC
我的理解——管理系統(tǒng)內(nèi)部的中斷,負(fù)責(zé)打開和關(guān)閉中斷。
基礎(chǔ)應(yīng)用 1,中斷的初始化函數(shù),包括設(shè)置中斷向量表位置,和開啟所需的中斷兩部分。所
有程序中必須的。
用法: void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;//中斷管理恢復(fù)默認(rèn)參數(shù)
#ifdef VECT_TAB_RAM
//如果 C/C++ Compiler\Preprocessor\Defined symbols 中的定義了 VECT_TAB_RAM(見程序庫
更改內(nèi)容的表格)
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //則在 RAM 調(diào)試
#else //如果沒有定義 VECT_TAB_RAM
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);//則在 Flash 里調(diào)試
#endif //結(jié)束判斷語句
//以下為中斷的開啟過程,不是所有程序必須的。
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//設(shè)置 NVIC 優(yōu)先級分組,方式。
//注:一共 16 個優(yōu)先級,分為搶占式和響應(yīng)式。兩種優(yōu)先級所占的數(shù)量由此代碼確定,
NVIC_PriorityGroup_x 可以是 0、1、2、3、4,分別代表搶占優(yōu)先級有 1、2、4、8、16 個和
響應(yīng)優(yōu)先級有 16、8、4、2、1 個。規(guī)定兩種優(yōu)先級的數(shù)量后,所有的中斷級別必須在其中
選擇,搶占級別高的會打斷其他中斷優(yōu)先執(zhí)行,而響應(yīng)級別高的會在其他中斷執(zhí)行完優(yōu)先執(zhí)
行。 //NVIC_InitStructure.NVIC_IRQChannel = 中斷通道名;
//開中斷,中斷名稱見函數(shù)庫
//NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//搶占優(yōu)先級
//NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
//響應(yīng)優(yōu)先級
//NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//啟動此通道的中斷
//NVIC_Init(&NVIC_InitStructure); //中斷初始化
}
閱讀 rcc:單片機時鐘管理。
我的理解——管理外部、內(nèi)部和外設(shè)的時鐘,設(shè)置、打開和關(guān)閉這些時鐘。
基礎(chǔ)應(yīng)用 1:時鐘的初始化函數(shù)過程——
用法:void RCC_Configuration(void) //時鐘初始化函數(shù)
{ ErrorStatus HSEStartUpStatus; //等待時鐘的穩(wěn)定 RCC_DeInit(); //時鐘管理重置 RCC_HSEConfig(RCC_HSE_ON); //打開外部晶振 HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待外部晶振就緒 if (HSEStartUpStatus == SUCCESS) {
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
//flash 讀取緩沖,加速 FLASH_SetLatency(FLASH_Latency_2); //flash 操作的延時 RCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB 使用系統(tǒng)時鐘 RCC_PCLK2Config(RCC_HCLK_Div2); //APB2(高速)為 HCLK 的一半 RCC_PCLK1Config(RCC_HCLK_Div2); //APB1(低速)為 HCLK 的一半
//注:AHB 主要負(fù)責(zé)外部存儲器時鐘。PB2 負(fù)責(zé) AD,I/O,高級 TIM,串口 1。APB1 負(fù)責(zé) DA,
USB,SPI,I2C,CAN,串口 2345,普通 TIM。 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
//PLLCLK = 8MHz * 9 = 72 MHz RCC_PLLCmd(ENABLE); //啟動 PLL
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){}
//等待 PLL 啟動
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//將 PLL 設(shè)置為系統(tǒng)時鐘源
while (RCC_GetSYSCLKSource() != 0x08){} //等待系統(tǒng)時鐘源的啟動 } //RCC_AHBPeriphClockCmd(ABP2 設(shè)備 1 | ABP2 設(shè)備 2 |, ENABLE); //啟動 AHP 設(shè)備 //RCC_APB2PeriphClockCmd(ABP2 設(shè)備 1 | ABP2 設(shè)備 2 |, ENABLE); //啟動 ABP2 設(shè)備 //RCC_APB1PeriphClockCmd(ABP2 設(shè)備 1 | ABP2 設(shè)備 2 |, ENABLE); //啟動 ABP1 設(shè)備
} 1、閱讀 exti:外部設(shè)備中斷函數(shù)
我的理解——外部設(shè)備通過引腳給出的硬件中斷,也可以產(chǎn)生軟件中斷,19 個上升、下降
或都觸發(fā)。EXTI0~EXTI15 連接到管腳,EXTI 線 16 連接到 PVD(VDD 監(jiān)視)
,EXTI 線 17 連接
到 RTC(鬧鐘),EXTI 線 18 連接到 USB(喚醒)。 基礎(chǔ)應(yīng)用 1,設(shè)定外部中斷初始化函數(shù)。按需求,不是必須代碼。 用法: void EXTI_Configuration(void)
{
EXTI_InitTypeDef EXTI_InitStructure; //外部設(shè)備中斷恢復(fù)默認(rèn)參數(shù)
EXTI_InitStructure.EXTI_Line = 通道 1|通道 2;
//設(shè)定所需產(chǎn)生外部中斷的通道,一共 19 個。
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //產(chǎn)生中斷
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
//上升下降沿都觸發(fā)
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //啟動中斷的接收
EXTI_Init(&EXTI_InitStructure); //外部設(shè)備中斷啟動
} 2、閱讀 dma:通過總線而越過 CPU 讀取外設(shè)數(shù)據(jù)
我的理解——通過 DMA 應(yīng)用可以加速單片機外設(shè)、存儲器之間的數(shù)據(jù)傳輸,并在傳輸期間
不影響 CPU 進行其他事情。這對于入門開發(fā)基本功能來說沒有太大必要,這個內(nèi)容先行跳
過。 3、閱讀 systic:系統(tǒng)定時器
我的理解——可以輸出和利用系統(tǒng)時鐘的計數(shù)、狀態(tài)。
基礎(chǔ)應(yīng)用 1,精確計時的延時子函數(shù)。推薦使用的代碼。 用法:
static vu32 TimingDelay;//全局變量聲明 void SysTick_Config(void)//systick 初始化函數(shù)
{ SysTick_CounterCmd(SysTick_Counter_Disable);//停止系統(tǒng)定時器 SysTick_ITConfig(DISABLE); //停止 systick 中斷 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //systick 使用 HCLK 作為時鐘源,頻率值除以 8。 SysTick_SetReload(9000);//重置時間 1 毫秒(以 72MHz 為基礎(chǔ)計算) SysTick_ITConfig(ENABLE);//開啟 systic 中斷
} void Delay (u32 nTime) //延遲一毫秒的函數(shù)
{ SysTick_CounterCmd(SysTick_Counter_Enable); //systic 開始計時 TimingDelay = nTime; //計時長度賦值給遞減變量 while(TimingDelay != 0); //檢測是否計時完成 SysTick_CounterCmd(SysTick_Counter_Disable); //關(guān)閉計數(shù)器 SysTick_CounterCmd(SysTick_Counter_Clear); //清除計數(shù)值
} void TimingDelay_Decrement(void)
//遞減變量函數(shù),函數(shù)名由“stm32f10x_it.c”中的中斷響應(yīng)函數(shù)定義好了。
{ if (TimingDelay != 0x00) //檢測計數(shù)變量是否達到 0 { TimingDelay‐‐; //計數(shù)變量遞減 }
} 注:建議熟練后使用,所涉及知識和設(shè)備太多,新手出錯的可能性比較大。新手可用簡化的
延時函數(shù)代替:
void Delay(vu32 nCount)//簡單延時函數(shù)
{ for(; nCount != 0; nCount‐‐);(循環(huán)變量遞減計數(shù))
}
當(dāng)延時較長,又不需要精確計時的時候可以使用嵌套循環(huán):
void Delay(vu32 nCount) //簡單的長時間延時函數(shù) {int i; //聲明內(nèi)部遞減變量 for(; nCount != 0; nCount‐‐) //遞減變量計數(shù)
{for (i=0; i<0xffff; i++)} //內(nèi)部循環(huán)遞減變量計數(shù)
} 4、閱讀 gpio:I/O 設(shè)置函數(shù)
我的理解——所有輸入輸出管腳模式設(shè)置,可以是上下拉、浮空、開漏、模擬、推挽模式,
頻率特性為 2M,10M,50M。也可以向該管腳直接寫入數(shù)據(jù)和讀取數(shù)據(jù)。 基礎(chǔ)應(yīng)用 1,gpio 初始化函數(shù)。所有程序必須。 用法:void GPIO_Configuration(void)
{ GPIO_InitTypeDef GPIO_InitStructure; //GPIO 狀態(tài)恢復(fù)默認(rèn)參數(shù) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_標(biāo)號 | GPIO_Pin_標(biāo)號 ;
//管腳位置定義,標(biāo)號可以是 NONE、ALL、0 至 15。
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;//輸出速度 2MHz GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模擬輸入模式 GPIO_Init(GPIOC, &GPIO_InitStructure); //C 組 GPIO 初始化
//注:以上四行代碼為一組,每組 GPIO 屬性必須相同,默認(rèn)的 GPIO 參數(shù)為:ALL,2MHz,
FLATING。如果其中任意一行與前一組相應(yīng)設(shè)置相同,那么那一行可以省略,由此推論如果
前面已經(jīng)將此行參數(shù)設(shè)定為默認(rèn)參數(shù)(包括使用 GPIO_InitTypeDef GPIO_InitStructure 代碼),
本組應(yīng)用也是默認(rèn)參數(shù)的話,那么也可以省略。以下重復(fù)這個過程直到所有應(yīng)用的管腳全部
被定義完畢。 ......
} 基礎(chǔ)應(yīng)用 2,向管腳寫入 0 或 1 用法:GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);//寫入 1
sw 笨笨的 STM32 筆記之七:讓它跑起來,基本硬件
功能的建立
SW笨笨 發(fā)表于 2009 年 02 月 27 日 09:00 閱讀(46) 評論(0) 分類: 個人日記
舉報 0、 實驗之前的準(zhǔn)備
a) 接通串口轉(zhuǎn)接器
b) 下載 IO 與串口的原廠程序,編譯通過保證調(diào)試所需硬件正常。 1、 flash,lib,nvic,rcc 和 GPIO,基礎(chǔ)程序庫編寫
a) 這幾個庫函數(shù)中有一些函數(shù)是關(guān)于芯片的初始化的,每個程序中必用。為保障程序品
質(zhì),初學(xué)階段要求嚴(yán)格遵守官方習(xí)慣。注意,官方程序庫例程中有個 platform_config.h 文件,
是專門用來指定同類外設(shè)中第幾號外設(shè)被使用,就是說在 main.c 里面所有外設(shè)序號用 x 代
替,比如 USARTx,程序會到這個頭文件中去查找到底是用那些外設(shè),初學(xué)的時候參考例程
別被這個所迷惑住。
b) 全部必用代碼取自庫函數(shù)所帶例程,并增加逐句注釋。
c) 習(xí)慣順序——Lib(debug),RCC(包括 Flash 優(yōu)化),NVIC,GPIO
d) 必用模塊初始化函數(shù)的定義:
void RCC_Configuration(void); //定義時鐘初始化函數(shù)
void GPIO_Configuration(void); //定義管腳初始化函數(shù)
void NVIC_Configuration(void); //定義中斷管理初始化函數(shù)
void Delay(vu32 nCount); //定義延遲函數(shù)
e) Main 中的初始化函數(shù)調(diào)用:
RCC_Configuration(); //時鐘初始化函數(shù)調(diào)用
NVIC_Configuration(); //中斷初始化函數(shù)調(diào)用
GPIO_Configuration(); //管腳初始化函數(shù)調(diào)用
f) Lib 注意事項:
屬于 Lib 的 Debug 函數(shù)的調(diào)用,應(yīng)該放在 main 函數(shù)最開始,不要改變其位置。 g) RCC 注意事項:
Flash 優(yōu)化處理可以不做,但是兩句也不難也不用改參數(shù)......
根據(jù)需要開啟設(shè)備時鐘可以節(jié)省電能
時鐘頻率需要根據(jù)實際情況設(shè)置參數(shù)
h) NVIC 注意事項
注意理解占先優(yōu)先級和響應(yīng)優(yōu)先級的分組的概念
i) GPIO 注意事項
注意以后的過程中收集不同管腳應(yīng)用對應(yīng)的頻率和模式的設(shè)置。 作為高低電平的 I/O,所需設(shè)置:RCC 初始化里面打開 RCC_APB2
PeriphClockCmd(RCC_APB2Periph_GPIOA);GPIO 里面管腳設(shè)定:IO 輸出(50MHz,Out_PP);
IO 輸入(50MHz,IPU); j) GPIO 應(yīng)用 GPIO_WriteBit(GPIOB, GPIO_Pin_2, Bit_RESET);//重置
GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);//寫入 1
GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x00);//寫入 0
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) ;//讀入 IO
k) 簡單 Delay 函數(shù)
void Delay(vu32 nCount)//簡單延時函數(shù)
{for(; nCount != 0; nCount‐‐);} 實驗步驟: RCC
初 始 化 函 數(shù) 里 添 加 : RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 |
RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB , ENABLE); 不用其他中斷,NVIC 初始化函數(shù)不用改 GPIO 初始化代碼:
//IO 輸入,GPIOB 的 2、10、11 腳輸出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;//管腳號 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //輸出速度 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //輸入輸出模式 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 簡單的延遲函數(shù):
void Delay(vu32 nCount) //簡單延時函數(shù)
{ for (; nCount != 0; nCount‐‐);} //循環(huán)計數(shù)延時 完成之后再在 main.c 的 while 里面寫一段: GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);//寫入 1
Delay(0xffff);
GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x00);//寫入 0
Delay(0xffff); 就可以看到連接在 PB2 腳上的 LED 閃爍了,單片機就跑起來了。 sw 笨笨的 STM32 筆記之八:來跟 PC 打個招呼,基本串口通訊
a) 目的:在基礎(chǔ)實驗成功的基礎(chǔ)上,對串口的調(diào)試方法進行實踐。硬件代碼順利完成之
后,對日后調(diào)試需要用到的 printf 重定義進行調(diào)試,固定在自己的庫函數(shù)中。
b) 初始化函數(shù)定義:
void USART_Configuration(void); //定義串口初始化函數(shù)
c) 初始化函數(shù)調(diào)用:
void UART_Configuration(void); //串口初始化函數(shù)調(diào)用
初始化代碼:
void USART_Configuration(void) //串口初始化函數(shù)
{
//串口參數(shù)初始化 USART_InitTypeDef USART_InitStructure; //串口設(shè)置恢復(fù)默認(rèn)參數(shù) //初始化參數(shù)設(shè)置 USART_InitStructure.USART_BaudRate = 9600; //波特率 9600 USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字長 8 位 USART_InitStructure.USART_StopBits = USART_StopBits_1; //1 位停止字節(jié) USART_InitStructure.USART_Parity = USART_Parity_No; //無奇偶校驗 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無
流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//打開 Rx 接收和 T
x 發(fā)送功能 USART_Init(USART1, &USART_InitStructure); //初始化 USART_Cmd(USART1, ENABLE); //啟動串口
} RCC 中打開相應(yīng)串口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE); GPIO 里面設(shè)定相應(yīng)串口管腳模式
//串口 1 的管腳初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //管腳 9 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出 GPIO_Init(GPIOA, &GPIO_InitStructure); //TX 初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //管腳 10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入 GPIO_Init(GPIOA, &GPIO_InitStructure); //RX 初始化 d) 簡單應(yīng)用:
發(fā)送一位字符
USART_SendData(USART1, 數(shù)據(jù)); //發(fā)送一位數(shù)據(jù)
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
{} //等待發(fā)送完畢
接收一位字符
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)
{} //等待接收完畢
變量= (USART_ReceiveData(USART1)); //接受一個字節(jié) 發(fā)送一個字符串 先定義字符串:char rx_data[250]; 然后在需要發(fā)送的地方添加如下代碼 int i; //定義循環(huán)變量 while(rx_data!='\0') // 循環(huán)逐字輸出,到結(jié)束字 '\0' {USART_SendData(USART1, rx_data); // 發(fā)送字符 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){} // 等待字符發(fā)送完畢 i++;} e) USART 注意事項:
發(fā)動和接受都需要配合標(biāo)志等待。
只能對一個字節(jié)操作,對字符串等大量數(shù)據(jù)操作需要寫函數(shù) 使用串口所需設(shè)置: RCC 初始化里面打開 RCC_APB2PeriphClockCmd
(RCC_APB2Periph_USARTx);GPIO 里面管腳設(shè)定:串口 RX ( 50Hz , IN_FLOATING );串口 TX ( 5
0Hz , AF_PP ); f) printf 函數(shù)重定義(不必理解,調(diào)試通過以備后用)
( 1 ) 需要 c 標(biāo)準(zhǔn)函數(shù):
#include "stdio.h"
( 2 ) 粘貼函數(shù)定義代碼
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch) // 定義為 putchar 應(yīng)用
( 3 ) RCC 中打開相應(yīng)串口
( 4 ) GPIO 里面設(shè)定相應(yīng)串口管腳模式
( 6 ) 增加為 putchar 函數(shù)。
int putchar(int c) //putchar 函數(shù) { if (c == '\n'){putchar('\r');} // 將 printf 的 \n 變成 \r USART_SendData(USART1, c); // 發(fā)送字符 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){} // 等待發(fā)送結(jié)束 return c; // 返回值
} ( 8 ) 通過,試驗成功。 printf 使用變量輸出 :%c 字符, %d 整數(shù), %f 浮點數(shù) ,%s 字符串,
/n 或 /r 為換行。注意:只能用于 main.c 中。 3 、 NVIC 串口中斷的應(yīng)用
a) 目的:利用前面調(diào)通的硬件基礎(chǔ),和幾個函數(shù)的代碼,進行串口的中斷輸入練習(xí)。因
為在實際應(yīng)用中,不使用中斷進行的輸入是效率非常低的,這種用法很少見,大部分串口的
輸入都離不開中斷。
b) 初始化函數(shù)定義及函數(shù)調(diào)用:不用添加和調(diào)用初始化函數(shù),在指定調(diào)試地址的時候已
經(jīng)調(diào)用過,在那個 NVIC_Configuration 里面添加相應(yīng)開中斷代碼就行了。
c) 過程:
i. 在串口初始化中 USART_Cmd 之前加入中斷設(shè)置:
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//TXE 發(fā)送中斷, TC 傳輸完成中斷, RXNE
接收中斷, PE 奇偶錯誤中斷,可以是多個。
ii. RCC 、 GPIO 里面打開串口相應(yīng)的基本時鐘、管腳設(shè)置
iii. NVIC 里面加入串口中斷打開代碼:
NVIC_InitTypeDef NVIC_InitStructure;// 中斷默認(rèn)參數(shù) NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;// 通道設(shè)置為串口 1 中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 中斷占先等級 0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 中斷響應(yīng)優(yōu)先級 0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 打開中斷
NVIC_Init(&NVIC_InitStructure); // 初始化 iv. 在 stm32f10x_it.c 文件中找到 void USART1_IRQHandler 函數(shù),在其中添入執(zhí)行代碼。
一般最少三個步驟:先使用 if 語句判斷是發(fā)生那個中斷,然后清除中斷標(biāo)志位,最后給字符
串賦值,或做其他事情。
void USART1_IRQHandler(void) // 串口 1 中斷
{
char RX_dat; // 定義字符變量 if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) // 判斷發(fā)生接收中斷 {USART_ClearITPendingBit(USART1, USART_IT_RXNE); // 清除中斷標(biāo)志 GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)0x01); // 開始傳輸 RX_dat=USART_ReceiveData(USART1) & 0x7F; // 接收數(shù)據(jù),整理除去前兩位 USART_SendData(USART1, RX_dat); // 發(fā)送數(shù)據(jù) while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){}// 等待發(fā)送結(jié)束 }
} d) 中斷注意事項:
可以隨時在程序中使用 USART_ITConfig(USART1, USART_IT_TXE, DISABLE); 來關(guān)閉中斷響應(yīng)。
NVIC_InitTypeDef NVIC_InitStructure 定義一定要加在 NVIC 初始化模塊的第一句。
全局變量與函數(shù)的定義:在任意 .c 文件中定義的變量或函數(shù),在其它 .c 文件中使用 extern+
定義代碼再次定義就可以直接調(diào)用了。 sw 笨笨的 STM32 筆記之九:打斷它來為我辦事,EXI
T (外部 I/O 中斷)應(yīng)用
a) 目的:跟串口輸入類似,不使用中斷進行的 IO 輸入效率也很低,而且可以通過 E
XTI 插入按鈕事件,本節(jié)聯(lián)系 EXTI 中斷。
b) 初始化函數(shù)定義:
void EXTI_Configuration(void); //定義 IO 中斷初始化函數(shù)
c) 初始化函數(shù)調(diào)用:
EXTI_Configuration();//IO 中斷初始化函數(shù)調(diào)用簡單應(yīng)用:
d) 初始化函數(shù):
void EXTI_Configuration(void)
{ EXTI_InitTypeDef EXTI_InitStructure; //EXTI 初始化結(jié)構(gòu)定義 EXTI_ClearITPendingBit(EXTI_LINE_KEY_BUTTON);//清除中斷標(biāo)志 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);//管腳選擇 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource4); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource6); EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//事件選擇 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//觸發(fā)模式 EXTI_InitStructure.EXTI_Line = EXTI_Line3 | EXTI_Line4; //線路選擇 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//啟動中斷 EXTI_Init(&EXTI_InitStructure);//初始化
} e) RCC 初始化函數(shù)中開啟 I/O 時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); GPIO 初始化函數(shù)中定義輸入 I/O 管腳。
//IO 輸入,GPIOA 的 4 腳輸入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉輸入 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化
f) 在 NVIC 的初始化函數(shù)里面增加以下代碼打開相關(guān)中斷: NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel; //通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//占先級 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //響應(yīng)級 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //啟動 NVIC_Init(&NVIC_InitStructure); //初始化 g) 在 stm32f10x_it.c 文件中找到 void USART1_IRQHandler 函數(shù),在其中添入執(zhí)行代
碼。一般最少三個步驟:先使用 if 語句判斷是發(fā)生那個中斷,然后清除中斷標(biāo)志位,最后給
字符串賦值,或做其他事情。 if(EXTI_GetITStatus(EXTI_Line3) != RESET) //判斷中
斷發(fā)生來源 { EXTI_ClearITPendingBit(EXTI_Line3); //
清除中斷標(biāo)志 USART_SendData(USART1, 0x41); /
/發(fā)送字符“a” GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)(1‐GPIO_ReadOutputDataBit(GPIOB, GPIO_
Pin_2)));//LED 發(fā)生明暗交替
}
h) 中斷注意事項:
中斷發(fā)生后必須清除中斷位,否則會出現(xiàn)死循環(huán)不斷發(fā)生這個中斷。然后需要對中斷類型進
行判斷再執(zhí)行代碼。
使用 EXTI 的 I/O 中斷,在完成 RCC 與 GPIO 硬件設(shè)置之后需要做三件事:初始化 EXTI、NVIC 開中斷、編寫中斷執(zhí)行代碼。 sw 笨笨的 STM32 筆記之十:工作工作,PWM 輸出
a) 目的:基礎(chǔ) PWM 輸出,以及中斷配合應(yīng)用。輸出選用 PB1,配置為 TIM3_CH4,
是目標(biāo)板的 LED6 控制腳。
b) 對于簡單的 PWM 輸出應(yīng)用,暫時無需考慮 TIM1 的高級功能之區(qū)別。
c) 初始化函數(shù)定義:
void TIM_Configuration(void); //定義 TIM 初始化函數(shù)
d) 初始化函數(shù)調(diào)用:
TIM_Configuration(); //TIM 初始化函數(shù)調(diào)用
e) 初始化函數(shù),不同于前面模塊,TIM 的初始化分為兩部分——基本初始化和通道
初始化:
void TIM_Configuration(void)//TIM 初始化函數(shù)
{ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定時器初始化結(jié)構(gòu) TIM_OCInitTypeDef TIM_OCInitStructure;//通道輸出初始化結(jié)構(gòu) //TIM3 初始化 TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //周期 0~FFFF TIM_TimeBaseStructure.TIM_Prescaler = 5; //時鐘分頻 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //時鐘分割 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //基本初始化 TIM_ITConfig(TIM3, TIM_IT_CC4, ENABLE);//打開中斷,中斷需要這行代碼 //TIM3 通道初始化 TIM_OCStructInit(& TIM_OCInitStructure); //默認(rèn)參數(shù) TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //工作
狀態(tài) TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //設(shè)定為輸出,
需要 PWM 輸出才需要這行代碼 TIM_OCInitStructure.TIM_Pulse = 0x2000; //占空長
度 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //高電平 TIM_OC4Init(TIM3, &TIM_OCInitStructure); //通道初
始化 TIM_Cmd(TIM3, ENABLE); //啟動 TIM3
} f) RCC 初始化函數(shù)中加入 TIM 時鐘開啟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM3, ENABLE);
g) GPIO 里面將輸入和輸出管腳模式進行設(shè)置。信號:AF_PP,50MHz。
h) 使用中斷的話在 NVIC 里添加如下代碼: //打開 TIM2 中斷 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel; //通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//占先級 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //響應(yīng)級 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //啟動 NVIC_Init(&NVIC_InitStructure); //初始
化 中斷代碼:
void TIM2_IRQHandler(void)
{ if (TIM_GetITStatus(TIM2, TIM_IT_CC4) != RESET) //判斷中斷來源 { TIM_ClearITPendingBit(TIM2, TIM_IT_CC4); //清除中斷標(biāo)志 GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)(1‐GPIO_ReadOutputDataBit(GPIOB, GPIO
_Pin_11)));//變換 LED 色彩 IC4value = TIM_GetCapture4(TIM2); //獲取捕捉數(shù)值 }
} i) 簡單應(yīng)用:
//改變占空比
TIM_SetCompare4(TIM3, 變量); j) 注意事項:
管腳的 IO 輸出模式是根據(jù)應(yīng)用來定,比如如果用 PWM 輸出驅(qū)動 LED 則應(yīng)該將相應(yīng)管腳設(shè)
為 AF_PP,否則單片機沒有輸出。 sw 笨笨的 STM32 筆記之十一:捕捉精彩瞬間,脈沖方波長度捕獲
a) 目的:基礎(chǔ) PWM 輸入也叫捕獲,以及中斷配合應(yīng)用。使用前一章的輸出管腳 P
B1(19 腳),直接使用跳線連接輸入的 PA3(13 腳),配置為 TIM2_CH4,進行實驗。
b) 對于簡單的 PWM 輸入應(yīng)用,暫時無需考慮 TIM1 的高級功能之區(qū)別,按照目前
我的應(yīng)用目標(biāo)其實只需要采集高電平寬度,而不必知道周期,所以并不采用 PWM 輸入模式,
而是普通脈寬捕獲模式。
c) 初始化函數(shù)定義:
void TIM_Configuration(void); //定義 TIM 初始化函數(shù)
d) 初始化函數(shù)調(diào)用:
TIM_Configuration(); //TIM 初始化函數(shù)調(diào)用
e) 初始化函數(shù),不同于前面模塊,TIM 的 CAP 初始化分為三部分——計時器基本初
始化、通道初始化和時鐘啟動初始化:
void TIM_Configuration(void)//TIM2 的 CAP 初始化函數(shù)
{ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定時器初始化結(jié)構(gòu) TIM_ICInitTypeDef TIM_ICInitStructure; //通道輸入初始化結(jié)構(gòu) //TIM2 輸出初始化 TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //周期 0~FFFF TIM_TimeBaseStructure.TIM_Prescaler = 5; //時鐘分頻 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //時鐘分割 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//模式 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);//基本初始化 //TIM2 通道的捕捉初始化 TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;//通道選擇 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;//下降沿 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//管腳與寄存器對應(yīng)關(guān)系 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分頻器 TIM_ICInitStructure.TIM_ICFilter = 0x4; //濾波設(shè)置,經(jīng)歷幾個周期跳變認(rèn)定波形
穩(wěn)定 0x0~0xF TIM_ICInit(TIM2, &TIM_ICInitStructure); //初始化 TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2); //選擇時鐘觸發(fā)源 TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);//觸發(fā)方式 TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable); //啟動定時器的被動
觸發(fā) TIM_ITConfig(TIM2, TIM_IT_CC4, ENABLE); //打開中斷 TIM_Cmd(TIM2, ENABLE); //啟動 TIM2
} f) RCC 初始化函數(shù)中加入 TIM 時鐘開啟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM3, ENABLE);
g) GPIO 里面將輸入和輸出管腳模式進行設(shè)置。IN_FLOATING,50MHz。
h) 使用中斷的話在 NVIC 里添加如下代碼: //打開 TIM 中斷(與前一章相同) NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; i) 簡單應(yīng)用:
變量 = TIM_GetCapture4(TIM2); j) 注意事項:
i. 由于我的需求只跟高電平寬度有關(guān),所以避免了使用 PWM 輸入模式,這樣可以
每個管腳捕捉一路信號。如果使用 PWM 模式,每一路需要占用兩個寄存器,所以一個定時
器只能同時使用兩路 PWM 輸入。
ii. 由于捕捉需要觸發(fā)啟動定時器,所以 PWM 輸出與捕捉不容易在同一個 TIM 通道
上實現(xiàn)。如果必須的話只能增加計數(shù)溢出的相關(guān)代碼。
iii. 有些程序省略了捕捉通道的初始化代碼,這是不對的
iv. 在基本計時器初始化代碼里面注意選擇適當(dāng)?shù)挠嫈?shù)器長度,最好讓波形長度不要
長于一個計數(shù)周期,否則需要增加溢出代碼很麻煩。一個計數(shù)周期的長度計算跟如下幾個參
數(shù)有關(guān):
(1) RCC 初始化代碼里面的 RCC_PCLKxConfig,這是 TIM 的基礎(chǔ)時鐘源與系統(tǒng)時鐘
的關(guān)系。
(2) TIM 初始化的 TIM_Period,這是計數(shù)周期的值
(3) TIM 初始化的 TIM_Prescaler,這是計數(shù)周期的倍頻計數(shù)器,相當(dāng)于調(diào)節(jié)計數(shù)
周期,可以使 TIM_Period 盡量大,提高計數(shù)精度。 sw 笨笨的 STM32 筆記之十二:時鐘不息工作不止,
systic 時鐘應(yīng)用 a) 目的:使用系統(tǒng)時鐘來進行兩項實驗——周期執(zhí)行代碼與精確定時延遲。
b) 初始化函數(shù)定義:
void SysTick_Configuration(void);
c) 初始化函數(shù)調(diào)用:
SysTick_Configuration();
d) 初始化函數(shù):
void SysTick_Configuration(void)
{ SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//時鐘除 8 SysTick_SetReload(250000); //
計數(shù)周期長度 SysTick_CounterCmd(SysTick_Counter_Enable); //啟動計時器 SysTick_ITConfig(ENABLE); //
打開中斷
}
e) 在 NVIC 的初始化函數(shù)里面增加以下代碼打開相關(guān)中斷:
NVIC_SystemHandlerPriorityConfig(SystemHandler_SysTick, 1, 0);//中斷等級設(shè)置,一般設(shè)置的
高一些會少受其他影響
f) 在 stm32f10x_it.c 文件中找到 void SysTickHandler 函數(shù)
void SysTickHandler(void)
{
執(zhí)行代碼
}
g) 簡單應(yīng)用:精確延遲函數(shù),因為 systic 中斷往往被用來執(zhí)行周期循環(huán)代碼,所以
一些例程中使用其中斷的啟動和禁止來編寫的精確延時函數(shù)實際上不實用,我自己編寫了精
確計時函數(shù)反而代碼更精簡,思路更簡單。思路是調(diào)用后,變量清零,然后使用時鐘來的曾
變量,不斷比較變量與延遲的數(shù)值,相等則退出函數(shù)。代碼和步驟如下:
i. 定義通用變量:u16 Tic_Val=0; //變量用于精確計時
ii. 在 stm32f10x_it.c 文件中相應(yīng)定義:
extern u16 Tic_Val;//在本文件引用 MAIN.c 定義的精確計時變量
iii. 定義函數(shù)名稱:void Tic_Delay(u16 Tic_Count);//精確延遲函數(shù)
iv. 精確延時函數(shù):
void Tic_Delay(u16 Tic_Count) //精確延時函數(shù)
{ Tic_Val=0; //變量清零 while(Tic_Val != Tic_Count){printf("");}//計時
}
v. 在 stm32f10x_it.c 文件中 void SysTickHandler 函數(shù)里面添加 Tic_Val++;//變量遞增 vi. 調(diào)用代碼:Tic_Delay(10); //精確延時
疑問:如果去掉計時行那個沒用的 printf("");函數(shù)將停止工作,這個現(xiàn)象很奇
vii.
怪 sw 笨笨的 STM32 筆記之十三:惡搞,兩只看門狗
a) 目的:
了解兩種看門狗(我叫它:系統(tǒng)運行故障探測器和獨立系統(tǒng)故障探測器,新手往往被這個并
不形象的象形名稱搞糊涂)之間的區(qū)別和基本用法。 b) 相同:
都是用來探測系統(tǒng)故障,通過編寫代碼定時發(fā)送故障清零信號(高手們都管這個代碼叫做“喂
狗” ),告訴它系統(tǒng)運行正常。一旦系統(tǒng)故障,程序清零代碼( “喂狗” )無法執(zhí)行,其計數(shù)器就
會計數(shù)不止,直到記到零并發(fā)生故障中斷(狗餓了開始叫喚),控制 CPU 重啟整個系統(tǒng)(不
行啦,開始咬人了,快跑......)。
c) 區(qū)別:
獨立看門狗 Iwdg——我的理解是獨立于系統(tǒng)之外,因為有獨立時鐘,所以不受系統(tǒng)影響的系
統(tǒng)故障探測器。(這條狗是借來的,見誰偷懶它都咬!)主要用于監(jiān)視硬件錯誤。
窗口看門狗 wwdg——我的理解是系統(tǒng)內(nèi)部的故障探測器,時鐘與系統(tǒng)相同。如果系統(tǒng)時鐘
不走了,這個狗也就失去作用了。
(這條狗是老板娘養(yǎng)的,老板不干活兒他不管!)主要用于
監(jiān)視軟件錯誤。 d) 初始化函數(shù)定義:鑒于兩只狗作用差不多,使用過程也差不多初始化函數(shù)栓一起了,
用的時候根據(jù)情況刪減。
void WDG_Configuration(void);
e) 初始化函數(shù)調(diào)用:
WDG_Configuration();
f) 初始化函數(shù)
void WDG_Configuration() //看門狗初始化
{
//軟件看門狗初始化 WWDG_SetPrescaler(WWDG_Prescaler_8); //時鐘 8 分頻 4ms
// (PCLK1/4096)/8= 244 Hz (~4 ms) WWDG_SetWindowValue(65); //計數(shù)器數(shù)值 WWDG_Enable(127); //啟動計數(shù)器,設(shè)置喂狗時間
// WWDG timeout = ~4 ms * 64 = 262 ms WWDG_ClearFlag(); //清除標(biāo)志位 WWDG_EnableIT(); //啟動中斷 //獨立看門狗初始化 IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//啟動寄存器讀寫 IWDG_SetPrescaler(IWDG_Prescaler_32);//40K 時鐘 32 分頻 IWDG_SetReload(349); //計數(shù)器數(shù)值 IWDG_ReloadCounter(); //重啟計數(shù)器 IWDG_Enable(); //啟動看門狗
} g) RCC 初始化:只有軟件看門狗需要時鐘初始化,獨立看門狗有自己的時鐘不需要但是
需要 systic 工作相關(guān)設(shè)置。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); h) 獨立看門狗使用 systic 的中斷來喂狗,所以添加 systic 的中斷打開代碼就行了。軟件看
門狗需要在 NVIC 打開中斷添加如下代碼: NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQChannel; //通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //占先中斷等級 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //響應(yīng)中斷優(yōu)先級 NVIC_Init(&NVIC_InitStructure); //打開中斷 i) 中斷程序,軟件看門狗在自己的中斷中喂狗,獨立看門狗需要使用 systic 的定時中斷來
喂狗。以下兩個程序都在 stm32f10x_it.c 文件中。
void WWDG_IRQHandler(void)
{ WWDG_SetCounter(0x7F); //更新計數(shù)值
WWDG_ClearFlag(); //清除標(biāo)志位
} void SysTickHandler(void)
{ IWDG_ReloadCounter(); //重啟計數(shù)器(喂狗)
} j) 注意事項:
i. 有狗平常沒事情可以不理,但是千萬別忘了喂它,否則死都不知道怎么死的!
ii. 初始化程序的調(diào)用一定要在 systic 的初始化之后。
iii. 獨立看門狗需要 systic 中斷來喂,但是 systic 做別的用處不能只做這件事,所以我寫
了如下幾句代碼,可以不影響 systic 的其他應(yīng)用,其他 systic 周期代碼也可參考:
第一步:在 stm32f10x_it.c 中定義變量
int Tic_IWDG; //喂狗循環(huán)程序的頻率判斷變量 第二步:將 SysTickHandler 中喂狗代碼改為下面:
Tic_IWDG++; //變量遞增
if(Tic_IWDG>=100) //每 100 個 systic 周期喂狗
{ IWDG_ReloadCounter();//重啟計數(shù)器(喂狗) Tic_IWDG=0; //變量清零
}
總結(jié)
以上是生活随笔為你收集整理的STM32F103系列单片机学习笔记1方便以后查看的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 作者:石在辉(1983-),男,中移(苏
- 下一篇: python-pygame激动时刻你我共