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