在上一節(jié)串口通訊中使用了查詢方式, 在少量數據傳輸應用中, 這種方法基本可行, 但如果通迅數據量比較大的話會對系統(tǒng)實時性造成很大的影響, 所以在實際工程運用中, 查詢的方法并不多見.
一個較好的方法就是利用空間換時間, 用一個緩存區(qū)加中斷進行數據發(fā)送和接收, 以減少不必要的等待的時間, 提高系統(tǒng)的實時性.
為了提高空間利用率, 最常用的方法是采用一個環(huán)形隊列做為接收/發(fā)送緩存, 配合中斷, 可很好的完成數據接收/傳送, 在時間和空間中取得一個平衡. 有關環(huán)形隊列的知識, 這里不多做介紹, 可參考相關資料.
使用環(huán)形隊列做為緩存.
發(fā)送邏輯: 當有一個字符串要發(fā)送時, 待發(fā)送的字符串送入FIFO緩存, 然后打開串口中斷, 在中斷服務函數中從FIFO取出數據, 逐一發(fā)送.
STM32的USART中斷.
在STM32中, 每個USART有多個中斷事件, 共用一個中斷向量. 其中比較常用的事件標志有三個: TXE, TC, RXNE.
數據發(fā)送的過程: 待發(fā)送的數據被寫入USART_DR(數據寄存器), 然后硬件將數據從USART_DR移動到發(fā)送移位寄存器, 隨著時鐘逐位稱到Tx引腳.
TXE:發(fā)送數據寄存器空 (Transmit data register empty).
當TDR寄存器中的數據被硬件轉移到移位寄存器的時候,該位被硬件置位。如果USART_CR1 寄存器中的TXEIE為1,則產生中斷。對USART_DR的寫操作,將該位清零。
0:數據還沒有被轉移到移位寄存器;
1:數據已經被轉移到移位寄存器。
TC:發(fā)送完成 (Transmission complete)
當包含有數據的一幀發(fā)送完成后, 并且TXE=1時, 由硬件將該位置’1′. 如果USART_CR1中的 TCIE為’1′,則產生中斷. 由軟件序列清除該位(先讀USART_SR,然后寫入USART_DR). TC 位也可以通過寫入’0′來清除,只有在多緩存通訊中才推薦這種清除程序。
0:發(fā)送還未完成;
1:發(fā)送完成。
RXNE:讀數據寄存器非空 (Read data register not empty)
當RDR移位寄存器中的數據被轉移到USART_DR寄存器中,該位被硬件置位。如果 USART_CR1寄存器中的RXNEIE為1,則產生中斷。對USART_DR的讀操作可以將該位清 零。RXNE位也可以通過寫入0來清除,只有在多緩存通訊中才推薦這種清除程序。
0:數據沒有收到;
1:收到數據,可以讀出。
USART的初始化同上一節(jié)一樣, 唯一的區(qū)別就是因為使用了中斷, 要配置NVIC.
有關NVIC的相關知識見 STM32中斷與NVIC概覽
NVIC的配置:
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 2位搶占優(yōu)先級, 兩位響應優(yōu)先級
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 中斷通道
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
// 搶占優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
// 響應優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
// 使能中斷
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return;
}
字符串發(fā)送函數:
BOOL USART1_Puts(const uint8_t *str)
{
// 獲取字符串長度
size_t str_len = strlen((const char *)str);
// FIFO不足以容納字符串, 發(fā)送失敗
if (str_len > FIFO_get_free(&USART1_tx_fifo))
{
return FALSE;
}
while (*str != '