SECTION 2
先說TC。即Transmission Complete。發送一個字節后才進入中斷,這里稱為“發送后中斷”。和原來8051的TI方式一樣,都是發送后才進中斷,需要在發送函數中先發送一個字節觸發中斷。發送函數如下
/*******
功能:中斷方式發送字符串.采用判斷TC的方式.即 判斷 發送后中斷 位.
輸入:字符串的首地址
輸出:無
*******/
void USART_SendDataString( u8 *pData )
{
pDataByte = pData;
USART_ClearFlag(USART1, USART_FLAG_TC);//清除傳輸完成標志位,否則可能會丟失第1個字節的數據.網友提供.
USART_SendData(USART1, *(pDataByte++) ); //必須要++,不然會把第一個字符t發送兩次
}
中斷處理函數如下
/********
* Function Name : USART1_IRQHandler
* Description : This function handles USART1 global interrupt request.
* Input : None
* Output : None
* Return : None
*********/
void USART1_IRQHandler(void)
{
if( USART_GetITStatus(USART1, USART_IT_TC) == SET )
{
if( *pDataByte == '\0' )//TC需要 讀SR+寫DR 方可清0,當發送到最后,到'\0'的時候用個if判斷關掉
USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打開的,導致會不停進入中斷. clear掉即可,不用關掉TCIE
else
USART_SendData(USART1, *pDataByte++ );
}
}
其中u8 *pDataByte;是一個外部指針變量
在中斷處理程序中,發送完該字符串后,不用關閉TC的中斷使能TCIE,只需要清掉標志位TC;這樣就能避免TC == SET 導致反復進入中斷了。
串口初始化函數如下
/*********
名稱: USART_Config
功能: 設置串口參數
輸入: 無
輸出: 無
返回: 無
**********/
void USART_Config()
{
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;//輸入加輸出模式
USART_InitStructure.USART_Clock = USART_Clock_Disable;//時鐘關閉
USART_InitStructure.USART_CPOL = USART_CPOL_Low;
USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
USART_Init(USART1, &USART_InitStructure);//設置到USART1
USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才產生中斷. 開TC中斷必須放在這里,否則還是會丟失第一字節
USART_Cmd(USART1, ENABLE); //使能USART1
}
這里請問一個問題:開TC中斷USART_ITConfig()如果放在我的USART_SendDataString()中再開,會丟失字符串的第一字節。必須放在串口初始化函數中才不會丟。不知道為什么??
這里筆者可以給出解釋,你看下SECTION1 就可以知道為什么呢,你這樣做的原理和SECTION1講解的差不多,就相當于延時,而你后面沒有丟失數據的主要原因就是你代碼中有這么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除傳輸完成標志位,否則可能會丟失第1個字節的數據.網友提供.
再說判斷TXE。即Tx DR Empty,發送寄存器空。當使能TXEIE后,只要Tx DR空了,就會產生中斷。所以,發送完字符串后必須關掉,否則會導致重復進入中斷。這也是和TC不同之處。
發送函數如下:
/*******
功能:中斷方式發送字符串.采用判斷TC的方式.即 判斷 發送后中斷 位.
輸入:字符串的首地址
輸出:無
*******/
void USART_SendDataString( u8 *pData )
{
pDataByte = pData;
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要發送寄存器為空,就會一直有中斷,因此,要是不發送數據時,把發送中斷關閉,只在開始發送時,才打開。
}
中斷處理函數如下:
/********
* Function Name : USART1_IRQHandler
* Description : This function handles USART1 global interrupt request.
* Input : None
* Output : None
* Return : None
********/
void USART1_IRQHandler(void)
{
if( USART_GetITStatus(USART1, USART_IT_TXE) == SET )
{
if( *pDataByte == '\0' )//待發送的字節發到末尾NULL了
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因為是 發送寄存器空 的中斷,所以發完字符串后必須關掉,否則只要空了,就會進中斷
else
USART_SendData(USART1, *pDataByte++ );
}
}
在串口初始化函數中就不用打開TXE的中斷了(是在發送函數中打開的)如下:
/************
名稱: USART_Config
功能: 設置串口參數
輸入: 無
輸出: 無
返回: 無
************/
void USART_Config()
{
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;//輸入加輸出模式
USART_InitStructure.USART_Clock = USART_Clock_Disable;//時鐘關閉
USART_InitStructure.USART_CPOL = USART_CPOL_Low;
USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
USART_Init(USART1, &USART_InitStructure);//設置到USART1
USART_Cmd(USART1, ENABLE); //使能USART1
}
SECTION 3
在USART的發送端有2個寄存器,一個是程序可以看到的USART_DR寄存器(下圖中陰影部分的TDR),另一個是程序看不到的移位寄存器(下圖中陰影部分Transmit Shift Register)。
對應USART數據發送有兩個標志,一個是TXE=發送數據寄存器空,另一個是TC=發送結束;對照下圖,當TDR中的數據傳送到移位寄存器后,TXE被設置,此時移位寄存器開始向TX信號線按位傳輸數據,但因為TDR已經變空,程序可以把下一個要發送的字節(操作USART_DR)寫入TDR中,而不必等到移位寄存器中所有位發送結束,所有位發送結束時(送出停止位后)硬件會設置TC標志。
另一方面,在剛剛初始化好USART還沒有發送任何數據時,也會有TXE標志,因為這時發送數據寄存器是空的。
TXEIE和TCIE的意義很簡單,TXEIE允許在TXE標志為'1'時產生中斷,而TCIE允許在TC標志為'1'時產生中斷。
至于什么時候使用哪個標志,需要根據你的需要自己決定。但我認為TXE允許程序有更充裕的時間填寫TDR寄存器,保證發送的數據流不間斷。TC可以讓程序知道發送結束的確切時間,有利于程序控制外部數據流的時序。
SECTION 4
總的來說,STM32單片機的串口還是很好理解的,編程也不算復雜。當然我更愿意希望其中斷系統和51單片機一樣的簡單。
對于接收終端,就是RXNE了,這只在接收完成后才產生,在執行USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)代碼時不會進入ISR。但麻煩的就是發送有關的中斷了:TXE或者TC,根據資料和測試的結果,TXE在復位后就是置1的,即在執行USART_ITConfig(USART1, USART_IT_TXE, ENABLE)后會立即產生中斷請求。因此這造成一個麻煩的問題:如果沒有真正的發送數據,TXE中斷都會發生,而且沒有休止,這將占用很大部分的CPU時間,甚至影響其他程序的運行!
因此建議的是在初始化時不好啟用TXE中斷,只在要發送數據(尤其是字符串、數組這樣的系列數據)時才啟用TXE。在發送完成后立即將其關閉,以免引起不必要的麻煩。
對于發送,需要注意TXE和TC的差別——這里簡單描述一下,假設串口數據寄存器是DR、串口移位寄存器是SR以及TXD引腳TXDpin,其關系是DR->SR->TXDpin。當DR中的數據轉移到SR中時TXE置1,如果有數據寫入DR時就能將TXE置0;如果SR中的數據全部通過TXDpin移出并且沒有數據進入DR,則TC置1。并且需要注意TXE只能通過寫DR來置0,不能直接將其清零,而TC可以直接將其寫1清零。
對于發送單個字符可以考慮不用中斷,直接以查詢方式完成。
對于發送字符串/數組類的數據,唯一要考慮的是只在最后一個字符發送后關閉發送中斷,這里可以分為兩種情況:對于發送可顯示的字符串,其用0x00作為結尾的,因此在ISR中就用0x00作為關閉發送中斷(TXE或者TC)的條件;第二種情況就是發送二進制數據,那就是0x00~0xFF中間的任意數據,就不能用0x00來判斷結束了,這時必須知道數據的具體長度。
這里簡單分析上面代碼的執行過程:TXE中斷產生于前一個字符從DR送入SR,執行效果是后一個字符送入DR。對于第一種情況,如果是可顯示字符,就執行USART_SendData來寫DR(也就清零了TXE),當最后一個可顯示的字符從DR送入SR之后,產生的TXE中斷發現要送入DR的是字符是0x00——這當然不行——此時就關閉TXE中斷,字符串發送過程就算結束了。當然這時不能忽略一個隱含的結果:那就是最后一個可顯示字符從DR轉入SR后TXE是置1的,但關閉了TXE中斷,因此只要下次再開啟TXE中斷就會立即進入ISR。對于第二種情況,其結果和第一種的相同。
對于第一種情況,其程序可以這么寫:其中TXS是保存了要發送數據的字符串,TxCounter1是索引值:
extern __IO uint8_t TxCounter1;
extern uint8_t *TXS;
extern __IO uint8_t TxLen;
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
{
if(TXS[TxCounter1]) //如果是可顯示字符
{ USART_SendData(USART1,TXS[TxCounter1++]);}
else //發送完成后關閉TXE中斷,
{ USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}
}
}
對于第二種情況,和上面的大同小異,其中TXLen表示要發送的二進制數據長度:
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //對USART_DR的寫操作,將該位清零。
{
if(TxCounter1
else //發送完成后關閉TXE中斷
{ USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}
}
}
事實上第一種情況是第二種的特殊形式,就是說可以用第二種情況去發送可顯示的字符——當然沒人有閑心去數一句話里有多少個字母空格和標點符號!
在使用時,只要將TXS指向要發送的字符串或者數組,設置TxLen為要發送的數據長度,然后執行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即開始發送過程。用戶可以檢查TxCounter1來確定發送了多少字節。比如以第二種情況為例:
uint32_t *TXS;
uint8_t TxBuffer1[]="0123456789ABCDEF";
uint8_t DST2[]="ASDFGHJKL";
__IO uint8_t TxLen = 0x00;
TxLen=8; //發送8個字符,最終發送的是01234567
TXS=(uint32_t *)TxBuffer1; //將TXS指向字符串TxBuffer1
TxCounter1=0; //復位索引值
USART_ITConfig(USART1, USART_IT_TXE,ENABLE); //啟用TXE中斷,即開始發送過程
while(TxCounter1!=TxLen); //等待發送完成
TXS=(uint32_t *)TxBuffer2; //同上,最終發送的是ASDFGHJK
TxCounter1=0;
USART_ITConfig(USART1, USART_IT_TXE,ENABLE);
while(TxCounter1!=TxLen);
以上就是我認為的最佳方案,但串口中斷方式數據有多長就中斷多少次,我認為還是占用不少CPU時間,相比之下DMA方式就好多了,因為DMA發送字符串時最多中斷兩次(半傳輸完成,全傳輸完成),并且將串口變成類似16C550的器件。
上一篇:STM32自學之串口中斷模式
下一篇:最后一頁
推薦閱讀
史海拾趣
在電子行業中,技術的創新和產品的品質是企業生存和發展的關鍵。Connor-Winfield深知這一點,因此在發展過程中始終注重技術創新和品質提升。公司投入大量資源進行研發,不斷推出具有創新性和高性能的產品。同時,公司還建立了嚴格的質量管理體系,確保每一件產品都符合最高的品質標準。這些努力使得Connor-Winfield的產品在市場上贏得了良好的口碑。
作為一家以技術創新為核心競爭力的公司,D3 Semiconductor始終保持對新技術、新應用的關注和研究。公司不斷投入研發資源,推出了一系列具有創新性的半導體產品,滿足了市場的多樣化需求。這種持續創新的精神推動了D3 Semiconductor的不斷發展,也使其在競爭激烈的電子行業中保持領先地位。
Amphenol Nexus Technologies深知,在電子行業中,合作與共贏是實現持續發展的重要途徑。因此,公司積極與產業鏈上下游的合作伙伴建立緊密的合作關系,共同推動行業的發展。通過與供應商、客戶以及同行業企業的合作,Amphenol Nexus Technologies不斷提升自身的競爭力,實現了業務的快速增長和市場的持續擴張。
以上便是關于Amphenol Nexus公司在電子行業發展的五個故事。這些故事展示了公司在不同歷史階段的發展軌跡、領導層的智慧和決策、技術創新的努力以及合作共贏的理念。正是這些因素的共同作用,使得Amphenol Nexus Technologies能夠在激烈的市場競爭中脫穎而出,成為電子連接器行業的佼佼者。
隨著技術的不斷進步和市場需求的不斷變化,Fagor Electrónica開始將其業務范圍擴展到更廣泛的電子和數字領域。公司不斷推出新的產品和服務,以滿足不同客戶的需求。同時,Fagor Electrónica還積極拓展國際市場,將其產品銷往世界各地。這一時期的多元化發展和市場拓展為公司帶來了更大的商業機會和更廣闊的市場前景。
國博電子的前身可以追溯到2000年成立的上海華信集成電路有限公司,起初專注于射頻芯片的研發與生產。隨著技術的積累和市場的拓展,公司逐步從單一的射頻芯片企業成長為覆蓋射頻芯片、模塊、組件的完整產業平臺。這一跨越不僅體現了公司在技術上的深厚積累,也反映了其對市場需求的敏銳洞察和快速響應能力。通過不斷的技術創新和產品研發,國博電子成功在無線通信、移動通信等領域占據了一席之地。
隨著業務的迅速增長,Astema意識到供應鏈管理的重要性。公司開始優化供應鏈管理流程,提高生產效率和產品質量。同時,Astema還與供應商建立了緊密的合作關系,確保原材料供應的穩定性和可靠性。這些舉措使得Astema在激烈的市場競爭中保持了穩定的運營。
我的平臺是PXA270+wince5.0+64M SDRAM 我再platform中增加了wince自帶的MSN,但是運行的時候總是出錯。 一開始我的內存是自動分配個一半的,MSN啟動非常慢,幾乎啟動不起來,我覺得可能是MSN要求的內存太大。就將program內存調整到了接近50M,MSN ...… 查看全部問答∨ |
|
s3c2451的wince6.0的block0img.nb0生成不能用問題 我在wince6.0上遇到這樣問題的:s3c2450在VS2005中編譯出來的blockimg0.nb0不能使用,請問有沒有遇到這樣的問題呀? 我懷疑是.bib文件中設置的問題,可修改也沒什么實質性的變化?… 查看全部問答∨ |
同一個進程之間的線程如何通訊 觸摸屏驅動,LCD驅動,同為GWES加載,是否可以認為是GWES進程中的多個線程? 同一個進程之間的線程通訊是否和不同進程間的通訊相同? 普通流接口驅動 以DLL形式存在 被同一個進程(device. ...… 查看全部問答∨ |
這算是芯片的bug嗎?STM32103c8的VDDA引腳引發的問題. 我的 STM32 103c8 的VDDA引腳,在電路上未連接任何電平.懸空了.結果引發了幾個問題.1.芯片不能復位:通過rc復位.電平始終是在0.7v附近,用手觸摸復位引腳附近的引腳,發現復位電平升高到3.3v,(可能觸及到vdda了).2.jtag不能下載程 ...… 查看全部問答∨ |
|
便攜式電源產品中的電池充電器發展趨勢━━ 高功率和寬范圍輸入 背景 便攜式電源應用領域寬泛而多樣。產品涵蓋了從平均功耗僅幾 μW 的無線傳感器節點到采用好幾百瓦時電池組的車載式醫療或數據采集系統等眾多門類。然而,盡管品種繁多,但它們卻呈現出了相對一致的發展趨勢 ━━ 設計人員不斷 ...… 查看全部問答∨ |