STM32,從字面上來理解, ST 是意法半導體, M 是 Microelectronics 的縮寫, 32 表示 32 位,合起來理解, STM32 就是指 ST 公司開發(fā)的 32 位微控制器。在如今的 32 位控制器當中, STM32 可以說是最璀璨的新星,它受寵若嬌,大受工程師和市場的青睞,無芯能出其右。
51 是嵌入式學習中一款入門級的精典 MCU,因其結構簡單,易于教學。 51誕生于 70 年代,屬于傳統(tǒng)的 8 位單片機,如今,久經(jīng)歲月的洗禮,既有其輝煌又有其不足?,F(xiàn)在的市場產(chǎn)品競爭越來越激烈,對成本極其敏感,相應地對 MCU 的性能要求也更苛刻:更多功能,更低功耗,易用界面和多任務。面對這些要求, 51 現(xiàn)有的資源就顯得得抓襟見肘。所以無論是高校教學還是市場需求,都急需一款新的 MCU 來為這個領域注入新的活力。
基于這樣的市場需求, ARM 公司推出了其全新的基于 ARMv7 架構的 32 位 Cortex-M3 微控制器內核。緊隨其后, ST(意法半導體)公司就推出了基于 Cortex-M3 內核的 MCU——STM32。STM32憑借其產(chǎn)品線的多樣化、極高的性價比、簡單易用的庫開發(fā)方式,迅速在眾多 Cortex-M3 MCU 中脫穎而出,成為最閃亮的一顆新星。
STM32 一上市就迅速占領了中低端 MCU 市場,受到了市場和工程師的無比青睞,頗有星火燎原之勢。作為一名合格的嵌入式工程師,面對新出現(xiàn)的技術,我們不是充耳不聞,而是要盡快吻合市場的需要,跟上技術的潮流。如今 STM32 的出現(xiàn)就是一種趨勢,一種潮流,我們要做的就是搭上這趟快車,讓自己的技術更有競爭力。
- STM32 USART串口的應用
USART:Universal Synchronous Asynchronous Receiver and Transmitter的縮寫,即通用同步異步收發(fā)器可以靈活地與外部設備進行全雙工數(shù)據(jù)交換。
UART:(Universal Asynchronous Receiver andTransmitter),它是在 USART基礎上裁剪掉了同步通信功能,只有異步通信。簡單區(qū)分同步和異步就是看通信時需不需要對外提供時鐘輸出,我們平時用的串口通信基本都是UART。
USART 在 STM32應用最多莫過于“打印”程序信息,一般在硬件設計時都會預留一個 USART通信接口連接電腦,用于在調試程序是可以把一些調試信息“打印”在電腦端的串口調試助手工具上,從而了解程序運行是否正確、指出運行出錯位置等等。
用戶平時基本上用的都是UART,很多外設與STM32進行數(shù)據(jù)傳送時都是用的UART。
開發(fā)板與電腦對于電平的定義是不一樣的,即對邏輯1和0的電平定義不同,因此需要一個轉換器,和其他開發(fā)板連接時,由于電平一樣,可直接連接。
串口通信協(xié)議介紹: 正常情況下,沒有數(shù)據(jù)傳輸時,兩端的信號線保持高電平,要發(fā)送數(shù)據(jù)時,發(fā)送方向接收方發(fā)送一個低電平,接收方就知道對面要發(fā)送數(shù)據(jù)了。接下來發(fā)送方發(fā)送數(shù)據(jù),兩端約定好數(shù)據(jù)大小是8位還是9位,一般情況下是8位,緊跟數(shù)據(jù)位后的校驗位和停止位。
在串行通信中,用“波特率”來描述數(shù)據(jù)的傳輸速率。所謂波特率,既每秒傳送的二進制位數(shù),其單位為bps(bits per second)。它是衡量串行數(shù)據(jù)速度快慢的重要指標。國際上規(guī)定一個標準的波特率系列: 110、300、600、1200、1800、2400、4800、9600、115200、14.4Kbps、19.2Kbps、……
例如:115200bps、指每秒傳送115200位。通信雙方必須設置同樣的同學速率才能正常通信
注意:實際的數(shù)據(jù)沒這么多,還包括起始位,結束位,校驗位。
- 使用寄存器方法發(fā)送數(shù)據(jù)
位7TXE:發(fā)送數(shù)據(jù)寄存器為空(Transmit data register empty)
當TDR寄存器的內容已傳輸?shù)揭莆患拇嫫鲿r,該位由硬件置1。如果 USART_CR1寄存器中 TXEIE位=1,則會生成中斷。通過對 USART_DR寄存器執(zhí)行寫入操作將該位清零。
0:數(shù)據(jù)未傳輸?shù)揭莆患拇嫫鳌?/p>
1:數(shù)據(jù)傳輸?shù)揭莆患拇嫫鳎簿褪菙?shù)據(jù)寄存器為空。
注意:單緩沖區(qū)發(fā)送期間使用該位。。
發(fā)送數(shù)據(jù)前一定要檢查發(fā)送數(shù)據(jù)寄存器是否為空,為空才可以發(fā)送數(shù)據(jù)。
void USART1_PutChar(uint8_t ch){
while(!(USART1->SR & 1<<7));//等待TDR為空
USART1->DR = ch;//直接將數(shù)據(jù)扔給數(shù)據(jù)寄存器
}
//第7位為0,與出來的結果為0,非運算之后為1,繼續(xù)while循環(huán)
//第7位為1,與出來的結果為非0值,非運算之后為0,退出while循環(huán)
- 使用寄存器方法接收數(shù)據(jù)
位5 RXNE: 讀取數(shù)據(jù)寄存器不為空(Read data register not empty)
當 RDR移位寄存器的內容已傳輸?shù)経SART_DR寄存器時,該位由硬件置1。如果USART_CR1寄存器中 RXNEIE= 1,則會生成中斷。通過對 USART_DR寄存器執(zhí)行讀入操作將該位清零。RXNE標志也可以通過向該位寫入零來清零。建議僅在多緩沖區(qū)通信時使用此清零序列。
0:未接收到數(shù)據(jù)
1:已準備好讀取接收到的數(shù)據(jù),即讀取數(shù)據(jù)寄存器不為空
uint8_t USART1_GetChar(void){
while(!(USART1- >SR & 1< 5));//等待RDR不為空
return USART1- >DR;
}
//第5位為0,沒有得到數(shù)據(jù),與出來的結果為0,非運算之后為1,繼續(xù)while循環(huán)
//第5位為1,數(shù)據(jù)寄存器不為空,與出來的結果為非0值,非運算之后為0,退出while循環(huán)
- printf的實現(xiàn):
int fputc(int ch,FILE *p){
while(!(USART1- >SR & 1< 7));
USART1- >DR = ch;
return ch;
}//實現(xiàn)原理參考上文
- STM32 中斷系統(tǒng)專題講解
中斷能提高CPU的效率,同時能對突發(fā)事件做出實時處理。實現(xiàn)程序的并行化,實現(xiàn)嵌入式系統(tǒng)進程之間的切換。
- NVIC(內嵌向量中斷控制器:Nested Vectored Interrupt Controlle)的主要功能:
NVIC其實就是一個中斷管理的部件,這個部件和其他外設沒有區(qū)別,內部仍然是由一系列寄存器構成的,它的功能都可已通過寄存器的設置來實現(xiàn)??刂埔粋€中斷本質上就是操作寄存器。
1.中斷管理
Cortex-M4 內核支持 256 個中斷,其中包含了 16 個內核中斷和 240 個外部中斷,并且具有256 級的可編程中斷優(yōu)先級設置。但 STM32F4 并沒有使用 Cortex-M4 內核的全部東西,而是只用了它的一部分。Cortex-M4處理器中,每一個外部中斷都可以被使能或者禁止,并且可以被設置為掛起狀態(tài)或者清除狀態(tài)。
注:
ISER:使能中斷,8位剛好控制256個中斷的使能,有些中斷是不可以被屏蔽的。
ICER:清除中斷使能,8位剛好控制256個中斷的清除使能
ISPR: 掛起中斷,若中斷產(chǎn)生但沒有立即執(zhí)行,它就會被掛起(產(chǎn)生的中斷沒有當前正在處理的中斷的優(yōu)先級高就會被掛起,但是當前中斷處理完成后仍然會處理新的中斷,總之,有中斷就會被處理)
ICPR:清除掛起,中斷處理完成后應該清除掛起,表示已被處理完成,如果不清除掛起標志位,下一次CPU檢查的時候發(fā)現(xiàn)該中斷還在等待處理,就會重復觸發(fā),這不是我們想要的。也就是我們常說的清中斷。
IABR:每個外部中斷都有一個活躍狀態(tài)位,當處理器正在處理時,該位會被置1
IP:用于設置中斷的優(yōu)先級,8位寬原則上可以設置256級優(yōu)先級,但實際上STM32并沒有使用到這么多,而是使用了高4位,低4位保留,共16個可編程優(yōu)先級。
2.中斷和異常向量表
Cortex-M4 內核支持 256 個中斷,其中包含了 16 個內核中斷和 240 個外部中斷。STM32F407實際上只使用了10個內部異常和82個外部中斷。當異常或中斷發(fā)生時,處理器會把PC設置為一個特定地址,這一地址就稱為異常向量。每一類異常源都對應一個特定的入口地址,這些地址按照優(yōu)先級排列以后就組成一張異常向量表。統(tǒng)一的處理方式需要軟件去完成。采用向量表處理異常,M0處理器會從存儲器的向量表中,自動定位異常的程序入口。從發(fā)生異常到異常的處理中間的時間被縮減。
注:中斷和異常的區(qū)別:
中斷是微處理器外部發(fā)送的,通過中斷通道送入處理器內部,一般是硬件引起的,比如串口接收中斷,而異常通常是微處理器內部發(fā)生的,大多是軟件引起的,比如除法出錯異常,特權調用異常等待。不管是中斷還是異常,微處理器通常都有相應的中斷/異常服務程序。
3.支持嵌套中斷:在執(zhí)行一個中斷服務程序的時候
當前處理器正在執(zhí)行某一中斷處理程序時,在執(zhí)行期間有一優(yōu)先級更高,更緊急的中斷需要處理,會打斷當前的中斷處理程序,去執(zhí)行高優(yōu)先級中斷的處理程序,執(zhí)行完成后再繼續(xù)當前的中斷處理程序。
STM32有3個固定的優(yōu)先級,都是負值,不能改變,值越小優(yōu)先級越高,有16個可編程優(yōu)先級,用4個bit位表示。
在 NVIC 有一個專門的寄存器:中斷優(yōu)先級寄存器 NVIC_IPRx(在 F407 中,x=0...81,即82個外部中斷)用來配置外部中斷的優(yōu)先級,IPR寬度為 8bit,原則上每個外部中斷可配置的優(yōu)先級為0~255,數(shù)值越小,優(yōu)先級越高。但是絕大多數(shù) CM4芯片都會精簡設計,以致實際上支持的優(yōu)先級數(shù)減少,在 F407 中,只使用了高 4bit。
STM32還會把優(yōu)先級分為兩級,一級叫做主優(yōu)先級,第二級叫做子優(yōu)先級,主優(yōu)先級又叫做搶占優(yōu)先級,子優(yōu)先級又叫做響應優(yōu)先級。比較時首先比較主優(yōu)先級,主優(yōu)先級高則優(yōu)先級一定高,主優(yōu)先級相同時比較子優(yōu)先級,響應優(yōu)先級數(shù)值越小,則優(yōu)先級越高。
IPR中的高4 位,又分為搶占優(yōu)先級和響應優(yōu)先級。搶占優(yōu)先級在前,響應優(yōu)先級在后。而這兩個優(yōu)先級各占幾個位又要根據(jù)SCB->AIRCR中的中斷分組設置來決定。這里簡單介紹一下STM32F4的中斷分組:STM32F4 將中斷分為 5 個組,組 04。該分組的設置是由 SCB->AIRCR 寄存器的 bit108 來定義的。注意:工程開發(fā)中應當首先確定中斷優(yōu)先級分組,之后就不要再做修改了。
4.中斷優(yōu)先級總結
搶占優(yōu)先級的級別高于響應優(yōu)先級。而數(shù)值越小所代表的優(yōu)先級就越高。 同一時刻發(fā)生的中斷,優(yōu)先處理優(yōu)先級較高的中斷。高優(yōu)先級的搶占優(yōu)先級是可以打斷正在進行的低搶占優(yōu)先級中斷的。而搶占優(yōu)先級相同的中斷,高優(yōu)先級的響應優(yōu)先級不可以打斷低響應優(yōu)先級的中斷。
搶占優(yōu)先級相同就看響應優(yōu)先級,同樣數(shù)值越小優(yōu)先級越高。如果兩個中斷的搶占優(yōu)先級和響應優(yōu)先級都是一樣的話,則看哪個中斷先發(fā)生就先執(zhí)行。如果同時發(fā)生則優(yōu)先處理編號較?。▽?0個內部異常和82個外部中斷所對應的中斷入口進行編號,如EXIT5-EXIT9合用一個中斷入口,其編號為23,內部異常的中斷入口編號均為負數(shù))的那個,也就是異常向量表中排前面的先執(zhí)行。
- EXTI,外部中斷控制器
簡單來說,EXTI就是管理GPIO產(chǎn)生的中斷,是GPIO與NVIC連接的中介,由于GPIO管腳太多,需要一個統(tǒng)一管理,就是EXIT,而其他的片內外設如串口、定時器、I2C等產(chǎn)生的中斷直接被NVIC管理。
EXTI共有16個通道選擇器,每一個編號相同的GPIO管腳連接到編號相同的通道選擇器中,每個通道選擇器最終輸出一根中斷輸入線,工作時,每一個選擇器下同時只能有一個管腳被配置使用,具體配置哪一個管腳由該器件的外部中斷配置控制器SYSCFG配置。
EXTI0-EXTI4每一個都有單獨的中斷入口,而EXTI5-EXTI9合用一個中斷入口,EXTI10-EXTI15也合用一個中斷入口。
一個SYSCFG只能配置4個通道選擇器,因此需要4個SYSCFG。
- 外部中斷處理流程:
1:中斷輸入線,外部管腳產(chǎn)生的中斷由此輸入??梢允歉唠娖?,也可以是低電平,根據(jù)設定要求產(chǎn)生
2:邊沿檢測電路會根據(jù)設定中斷產(chǎn)生的方式檢測電平,判斷是否發(fā)生中斷,如上升沿觸發(fā)中斷、下降沿觸發(fā)中斷或者上升沿和下降沿均可觸發(fā)中斷
3:如果2滿足條件,會經(jīng)過一個“或”門,“或”表示由外部輸入的中斷信號可以觸發(fā)中斷,內部的事件也可以產(chǎn)生中斷,如定時器的更新事件等,不論外部內部,只有有一個就繼續(xù)下去
如果是電平,就向上走,如果是事件就向下走
4:通過一個“與” 門,與表示新來的中斷已被掛起(中斷產(chǎn)生還未處理)記錄,且次此中斷沒有被屏蔽,二者同時滿足后可交由NVIC處理,傳送至內核進行響應。、
- 按鍵中斷實例
配置GPIO為外部中斷模式,觸發(fā)方式為下降沿觸發(fā),使能外部中斷入口,設置優(yōu)先級,包括主優(yōu)先級和子優(yōu)先級。
在Cube MX中只要使能了中斷入口肯定會生成對應的中斷處理函數(shù),這些處理函數(shù)被歸納到stm32f4xx_it.c當中,這里面的文件都是中斷處理函數(shù)的入口(IRQHander,Interrupt ReQuest Hander,中斷處理請求函數(shù))
在此文件的相應的函數(shù)入口處一步步追下去,就可以得到相應的中斷處理代碼。
這里的Callback函數(shù)一般是若函數(shù),意味著用戶可以對該函數(shù)進行重新編寫完成自己的邏輯功能。將該函數(shù)復制到gpio.c中進行重新編寫,復制時不需要復制函數(shù)前的__weak(弱函數(shù)典型的標記為函數(shù)前有__weak),因此在整個工程里就會有兩個同名的函數(shù),系統(tǒng)在編譯的時候遇到兩個同名函數(shù)時,就會自動忽略有__weak標記的函數(shù),轉而去編譯用戶自己編寫的同名函數(shù)。
//gpio.c
void HALGPIO EXTI_Callback (uint16_t GPIO_ Pin){
if(GPIO_ Pin == GPIO_PIN_9){
HAL_Delay(20);//延時,用于消除抖動
if(HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_9) == GPIO_PIN_RESET){
//讀管腳為低電平表示確實被按下了
printf("KEY3 INT successn");
}
}
}
//main.c
include "gpio.c"
include "usart.c"
main(){
MX_GPIO_Init();//對GPIO口時鐘配置,中斷優(yōu)先級配置,觸發(fā)方式等的初始化
MX_USART1_UART_Init();對USART1初始化配置
while(){}
}
HAL_Delay()函數(shù)是由Systick定時器實現(xiàn)的,也涉及到一個中斷的處理過程,執(zhí)行的是系統(tǒng)內部的定時器產(chǎn)生的異常處理函數(shù),因此設置按鍵中斷的優(yōu)先級必須要比HAL_Delay()要小,否則HAL_Delay()函數(shù)無法搶占導致程序死掉。
- 串口中斷實例、
配置串口管腳參數(shù),使能串口中斷入口,設置優(yōu)先級分組,設置串口中斷優(yōu)先級。
1.HAL_UART_IRQHandler ( &huart1);追進去有相當多類型的中斷處理函數(shù),選擇串口在傳輸模式下發(fā)送完成的中斷處理函數(shù)。
用戶只需實現(xiàn)void HAL_UART_ TxCpltCallback (UART_HandleTypeDef *huart)的邏輯代碼
2.HAL_UART_IRQHandler ( &huart1);追進去有相當多類型的中斷處理函數(shù),選擇串口在接收模式下的中斷處理函數(shù)。
該中斷處理函數(shù)仍然會調用一個回調函數(shù)。
用戶只需實現(xiàn)void HAL UART RxCpltCallback(UART HandleTypeDef *huart)的邏輯代碼
串口中斷有專門的串口接收中斷函數(shù)和發(fā)送中斷函數(shù)用來觸發(fā)中斷。
HAL_UART_Transmit_IT();
HAL_UART_Receive_IT ();
//main.c
include "gpio.c"
include "usart.c"
uint8_t revbuff[2]={0};//接收緩沖區(qū),定義全局為變量用于外部引用
main(){
MX_GPIO_Init();
MX_USART1_UART_Init();//對USART1初始化配置,中斷優(yōu)先級配置
HAL_UART_Transmit_IT(&usart1,(uint8_t *)"USART SENDn",11);
//只有發(fā)送11個字符完成之后會觸發(fā)發(fā)送完成中斷,只觸發(fā)一次
HAL_UART_Receive_IT(&usart1,revbuff,2);
//只有接收到2個數(shù)據(jù)后觸發(fā)接收中斷,只觸發(fā)一次,若想多次觸發(fā),必須再次調用
while(){}
}
//usart.c
void HALUART_ TxCpltCallback (UART_HandleTypeDef *huart){
if(huart- >Instance == USART1){
printf("UART SEND endn");
}
}
extern uint8_t revbuff[2]={0};
void HAL UART RxCpltCallback(UART HandleTypeDef *huart){
if(huart- >Instance == USART1){
printf("revbuff[0]=%x,revbuff[0]n");
printf("revbuff[1]=%x,revbuff[1]n");
HAL_UART_Receive_IT(&usart1,revbuff,2);//用于多次觸發(fā)接收中斷
}
}
評論
查看更多