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