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