摘要:接收緩沖區(qū)和發(fā)送緩沖區(qū)的請求是獨立的。此時串口實際上還有個字節(jié)并未發(fā)送完成,數(shù)據(jù)寄存器和移位寄存器中的個字節(jié)還需要發(fā)送,并不能關(guān)閉串口發(fā)送。
串行接口簡稱串口,也稱串行通信接口或串行通訊接口(通常指COM接口),是采用串行通信方式的擴展接口。串行接口(SerialInterface)是指數(shù)據(jù)一位一位地順序傳送,其特點是通信線路簡單,只要一對傳輸線就可以實現(xiàn)雙向通信(可以直接利用電話線作為傳輸線),從而大大降低了成本,特別適用于遠(yuǎn)距離通信,但傳送速度較慢。
USART(universal synchronous asynchronous receiver and transmitte): 通用同步異步收發(fā)器
UART(universal asynchronous receiver and transmitter): 通用異步收發(fā)器
區(qū)別:
USART是指單片機的一個端口模塊,可以根據(jù)需要配置成同步模式(SPI,I2C),也可以將其配置為異步模式,后者就是UART。所以說UART姑且可以稱之為一個與SPI,I2C對等的“協(xié)議”,而USART則不是一個協(xié)議,而是更應(yīng)該理解為一個實體。
相比于同步通訊,UART不需要統(tǒng)一的時鐘線,接線更加方便。但是,為了正常的對信號進(jìn)行解碼,使用UART通訊的雙方必須事先約定好波特率,即單位事件內(nèi)傳輸碼元的個數(shù)。
補充:
在電子通信領(lǐng)域,波特(Baud)即調(diào)制速率,指的是有效數(shù)據(jù)訊號調(diào)制載波的速率,即單位時間內(nèi)載波調(diào)制狀態(tài)變化的次數(shù)。它是對符號傳輸速率的一種度量,1波特即指每秒傳輸1個符號,而透過不同的調(diào)制方式,可以在一個碼元符號上負(fù)載多個bit位信號。[1]“波特”(Baud)本身已是速率,所以不需要寫成 Baud Rate(Rate 是贅字)。單位“波特”本身就已經(jīng)是代表每秒的調(diào)制數(shù),以“波特每秒”(Baud per second)為單位是一種常見的錯誤,但是在一般中文口語化的溝通上還是常以“波特率”來描述“波特”(Baud)。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VkffEvL6-1632828582303)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/image-20210815155528305.png)]
USART 中斷事件被連接到相同的中斷向量:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-IbZhqtpL-1632828582305)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/image-20210811170712630.png)]
USART 能夠使用 DMA 進(jìn)行連續(xù)通信。接收緩沖區(qū)和發(fā)送緩沖區(qū)的 DMA 請求是獨立的。
使用 DMA 進(jìn)行發(fā)送 將 USART_CR3 寄存器中的 DMAT 位置 1 可以使能 DMA 模式進(jìn)行發(fā)送。當(dāng) TXE 位置 1 時,可將數(shù)據(jù)從 SRAM 區(qū)(通過 DMA 配置,參見 DMA 部分)加載到 USART_DR 寄存器。要映射一個 DMA 通道以進(jìn)行 USART 發(fā)送,請按以下步驟操作(x 表示通道編號):
在 DMA 控制寄存器中寫入 USART_DR 寄存器地址,將其配置為傳輸?shù)哪繕?biāo)地址。每次發(fā)生 TXE 事件后,數(shù)據(jù)都會從存儲器移動到此地址。
在 DMA 控制寄存器中寫入存儲器地址,將其配置為傳輸?shù)脑吹刂?。每次發(fā)生 TXE 事件后,數(shù)據(jù)都會從這個存儲區(qū)域加載到 USART_DR 寄存器中。
在 DMA 控制寄存器中配置要傳輸?shù)目傋止?jié)數(shù)。
在 DMA 寄存器中配置通道優(yōu)先級。
根據(jù)應(yīng)用的需求,在完成一半或全部傳輸后產(chǎn)生 DMA 中斷。
向 SR 寄存器中的 TC 位寫入 0,將其清零。
在 DMA 寄存器中激活該通道。
當(dāng)達(dá)到在 DMA 控制器中設(shè)置的數(shù)據(jù)傳輸量時,DMA 控制器會在 DMA 通道的中斷向量上產(chǎn)生一個中斷。
在發(fā)送模式下,DMA 對所有要發(fā)送的數(shù)據(jù)執(zhí)行了寫操作(DMA_ISR 寄存器中的 TCIF 標(biāo)志置 1)后,可以對 TC 標(biāo)志進(jìn)行監(jiān)視,以確保 USART 通信已完成。在禁止 USART 或進(jìn)入停止模式前必須執(zhí)行此步驟,以避免損壞最后一次發(fā)送。軟件必須等待直到 TC=1。TC 標(biāo)志在所有數(shù)據(jù)發(fā)送期間都必須保持清零狀態(tài),然后在最后一幀發(fā)送結(jié)束后由硬件置 1。
將 USART_CR3 寄存器中的 DMAR 位置 1 可以使能 DMA 模式進(jìn)行接收。接收數(shù)據(jù)字節(jié)時,數(shù)據(jù)會從 USART_DR 寄存器加載到 SRAM 區(qū)域中(通過 DMA 配置,參見 DMA 規(guī)范)。要映射一個 DMA 通道以進(jìn)行 USART 接收,請按以下步驟操作:
在 DMA 控制寄存器中寫入 USART_DR 寄存器地址,將其配置為傳輸?shù)脑吹刂?。每次發(fā)生 RXNE 事件后,數(shù)據(jù)都會從此地址移動到存儲器。
在 DMA 控制寄存器中寫入存儲器地址,將其配置為傳輸?shù)哪繕?biāo)地址。每次發(fā)生 RXNE 事件后,數(shù)據(jù)都會從 USART_DR 寄存器加載到此存儲區(qū)。
在 DMA 控制寄存器中配置要傳輸?shù)目傋止?jié)數(shù)。
在 DMA 控制寄存器中配置通道優(yōu)先級。
根據(jù)應(yīng)用的需求,在完成一半或全部傳輸后產(chǎn)生中斷。
在 DMA 控制寄存器中激活該通道。
當(dāng)達(dá)到在 DMA 控制器中設(shè)置的數(shù)據(jù)傳輸量時,DMA 控制器會在 DMA 通道的中斷向量上產(chǎn)生一個中斷。在中斷子程序中,USART_CR3 寄存器中的 DMAR 位應(yīng)由軟件清零。
注意: 如果 DMA 用于接收,則不要使能 RXNEIE 位
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NpkQgBln-1632828582308)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/image-20210811171131932.png)]
多緩沖區(qū)通信中的錯誤標(biāo)志和中斷生成
在多緩沖區(qū)通信中,如果事務(wù)中發(fā)生任何錯誤,都會在當(dāng)前字節(jié)后放置錯誤標(biāo)志。如果中斷使 能置 1,則會產(chǎn)生中斷。在單字節(jié)接收過程中,與 RXNE 一同置位的幀錯誤、上溢錯誤和噪聲 標(biāo)志具有多帶帶的錯誤標(biāo)志中斷使能位(USART_CR3 寄存器中的 EIE 位);如果該位置 1, 則會因其中任何一個錯誤而在當(dāng)前字節(jié)后產(chǎn)生中斷。
接收的方式:
發(fā)送的方式:
下面主要用 環(huán)形隊列+DMA+非動態(tài)內(nèi)存分配+IDLE中斷
建議先看最下面的參考文章
USART1 + DMA + IDLE中斷 +無鎖隊列
int main(void){ uint8_t buff_read[32]; uint32_t length; usart1_init(); while (1) { length = fifo_read_buff(pfifo_x, buff_read, 32); if (length) { printf("lengtt = %d", length); // 實際接收的數(shù)據(jù)長度 //對接收的數(shù)據(jù)進(jìn)行處理 } else { printf("no data rx");// 沒有數(shù)據(jù) } if (pfifo_x->error) { printf("fifo error %d", pfifo_x->error);// 接收錯誤 pfifo_x->error = 0; } }}
void USART1_IRQHandler(void) // 接收數(shù)據(jù)中斷{ __IO uint8_t Len = 0; //發(fā)送完成中斷 /* * DMA中斷時,只表示需要傳送的所有數(shù)據(jù)字節(jié)全部傳送到串口的發(fā)送數(shù)據(jù)寄存器中了。 * 此時串口實際上還有2個字節(jié)并未發(fā)送完成,數(shù)據(jù)寄存器和移位寄存器中的2個字節(jié)還需要發(fā)送,并不能關(guān)閉串口發(fā)送。 * 同理,如果是485切換方向,必須要等到發(fā)送完成,也就是移位寄存器發(fā)送完成-TC標(biāo)志置位。 * * TXE指的是發(fā)送緩沖器DR空,TC指的是SHIFT移位寄存器空。 * DMA完成只是代表把最后一個字節(jié)送到DR寄存器里面了,此時SHIFT移位寄存器有1個字節(jié)正在開始發(fā)送, * DR寄存器里面有一個字節(jié)等待發(fā)送,所以就是2個字節(jié)未發(fā)送完成。 */ if (USART_GetITStatus(USART1, USART_IT_TC) == SET) { USART_ClearITPendingBit(USART1, USART_IT_TC); USART_ITConfig(USART1, USART_IT_TC, DISABLE); DMA2_Stream7_working = 0; } //總線空閑中斷 if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //觸發(fā)中斷標(biāo)志位 { Len = USART1->SR; //清除RXNE標(biāo)志 Len = USART1->DR; //清USART_IT_IDLE標(biāo)志 Len = DMA_GetCurrDataCounter(DMA2_Stream5); //獲取當(dāng)前剩余數(shù)據(jù)量大小的函數(shù) if (pfifo_1 != 0) { // Len為當(dāng)前接收索引 pfifo_1->in += ((pfifo_1->last_cnt - Len) & (pfifo_1->size - 1)); //更新in pfifo_1->last_cnt = Len; if ((pfifo_1->in - pfifo_1->out) > pfifo_1->size) { pfifo_1->out = pfifo_1->in; // 清空緩存,注意賦值順序,pfifo->in = pfifo->out 是錯誤的 pfifo_1->error |= FIFO_DMA_ERROR_RX_FULL; } } else { pfifo_1->error |= FIFO_DMA_ERROR_RX_POINT_NULL; } }}
#define USART1_RX_LEN 32#define USART1_TX_LEN 32uint8_t Usart1_Rx[USART1_RX_LEN] = {0};uint8_t Usart1_Tx[USART1_TX_LEN] = {0};uint8_t Usart1_Tx_Buffer[USART1_TX_LEN] = {0};fifo_rx_def fifo_usart_rx_1;fifo_rx_def *pfifo_1 = &fifo_usart_rx_1;fifo_rx_def fifo_usart_tx_1;uint8_t DMA2_Stream7_working = 0;void usart1_init(void){ /* -------------- Enable Module Clock Source ----------------------------*/ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能GPIOA時鐘 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能USART1時鐘 GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); //GPIOA9復(fù)用為USART1 GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); //GPIOA10復(fù)用為USART1 /* -------------- Configure GPIO ---------------------------------------*/ { GPIO_InitTypeDef GPIO_InitStruct; NVIC_InitTypeDef NVIC_InitStructure; USART_InitTypeDef USART1_InitStruct; //USART1端口配置 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9與GPIOA10 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //復(fù)用功能 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽復(fù)用輸出 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA, &GPIO_InitStruct); //初始化PA9,PA10 //USART1 初始化設(shè)置 USART_DeInit(USART1); USART1_InitStruct.USART_BaudRate = 115200; //波特率設(shè)置 USART1_InitStruct.USART_WordLength = USART_WordLength_8b; //字長為8位數(shù)據(jù)格式 USART1_InitStruct.USART_StopBits = USART_StopBits_1; //一個停止位 USART1_InitStruct.USART_Parity = USART_Parity_No; //無奇偶校驗位 USART1_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //無硬件數(shù)據(jù)流控制 USART1_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發(fā)模式 USART_Init(USART1, &USART1_InitStruct); //初始化串口1 USART_Cmd(USART1, ENABLE); //使能串口1 USART_ClearFlag(USART1, USART_FLAG_TC); //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1中斷通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //搶占優(yōu)先級 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根據(jù)指定的參數(shù)初始化VIC寄存器 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //開啟相關(guān)中斷 USART_ITConfig(USART1, USART_IT_TC, DISABLE); USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); } /* -------------- Configure DMA -----------------------------------------*/ /* 發(fā)送 */ { DMA_InitTypeDef DMA_InitStruct; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //發(fā)送數(shù)據(jù) DMA_Cmd(DMA2_Stream7, DISABLE); //關(guān)閉DMA DMA_DeInit(DMA2_Stream7); //重置為缺省值 DMA_InitStruct.DMA_Channel = DMA_Channel_4; DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR); //源地址 DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(Usart1_Tx); //目的地址 DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral; //數(shù)據(jù)傳輸方向為外設(shè)到內(nèi)存 DMA_InitStruct.DMA_BufferSize = USART1_TX_LEN; //設(shè)置數(shù)據(jù)的緩沖大小 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設(shè)地址不變 DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //內(nèi)存緩沖區(qū)地址自加 DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //8位字節(jié)傳輸 DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //數(shù)據(jù)寬度為8位 DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; //工作在正常模式 DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh; //最高優(yōu)先級 DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream7, &DMA_InitStruct); NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE); //開啟發(fā)送完成中斷 DMA_Cmd(DMA2_Stream7, DISABLE); USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //使能串口的DMA發(fā)送 if (fifo_init(&fifo_usart_tx_1, Usart1_Tx_Buffer, USART1_TX_LEN) == -1) { // 必須 2 的冪次方 } } // 接收數(shù)據(jù) { DMA_InitTypeDef DMA_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); DMA_Cmd(DMA2_Stream5, DISABLE); //關(guān)閉DMA while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) ; DMA_DeInit(DMA2_Stream5); //重置為缺省值 DMA_InitStruct.DMA_Channel = DMA_Channel_4; DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR); //源地址 DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(Usart1_Rx); //目的地址 DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory; //數(shù)據(jù)傳輸方向為外設(shè)到內(nèi)存 DMA_InitStruct.DMA_BufferSize = USART1_RX_LEN; //設(shè)置數(shù)據(jù)的緩沖大小 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設(shè)地址不變 DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //內(nèi)存緩沖區(qū)地址自加 DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //8位字節(jié)傳輸 DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //數(shù)據(jù)寬度為8位 DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //工作在循環(huán)緩存模式 DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh; //最高優(yōu)先級 DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; DMA_InitStruct.DMA_MemoryBurst = DMA_Mode_Normal; //DMA_MemoryBurst_Single;// DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream5, &DMA_InitStruct); DMA_Cmd(DMA2_Stream5, ENABLE); USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); if (fifo_init(pfifo_1, Usart1_Rx, USART1_RX_LEN) == -1) { // 必須 2 的冪次方 } }}
在設(shè)計串口驅(qū)動過程中,要遵循的準(zhǔn)則是:
void USART1_IRQHandler(void) // 接收數(shù)據(jù)中斷{ __IO uint8_t Len = 0; //發(fā)送完成中斷 /* * DMA中斷時,只表示需要傳送的所有數(shù)據(jù)字節(jié)全部傳送到串口的發(fā)送數(shù)據(jù)寄存器中了。 * 此時串口實際上還有2個字節(jié)并未發(fā)送完成,數(shù)據(jù)寄存器和移位寄存器中的2個字節(jié)還需要發(fā)送,并不能關(guān)閉串口發(fā)送。 * 同理,如果是485切換方向,必須要等到發(fā)送完成,也就是移位寄存器發(fā)送完成-TC標(biāo)志置位。 * * TXE指的是發(fā)送緩沖器DR空,TC指的是SHIFT移位寄存器空。 * DMA完成只是代表把最后一個字節(jié)送到DR寄存器里面了,此時SHIFT移位寄存器有1個字節(jié)正在開始發(fā)送, * DR寄存器里面有一個字節(jié)等待發(fā)送,所以就是2個字節(jié)未發(fā)送完成。 */ if (USART_GetITStatus(USART1, USART_IT_TC) == SET) { USART_ClearITPendingBit(USART1, USART_IT_TC); USART_ITConfig(USART1, USART_IT_TC, DISABLE); DMA2_Stream7_working = 0; }}
//uint8_t DMA2_Stream7_working = 0;/** * @brief 發(fā)送完成中斷 * @param[in] void * @retval void *//* * ST官方都有APPNOTE指導(dǎo)的(對于UART沒有RS485功能的單片機型號而言): * 1、啟動DMA前,先關(guān)閉UART發(fā)送完成中斷,并清除發(fā)送完成中斷標(biāo)志; * 2、在DMA傳輸完成中斷函數(shù)中,開啟UART發(fā)送完成中斷; * 3、在UART發(fā)送完成中斷函數(shù)中,切換RS485為接收態(tài);*/void DMA2_Stream7_IRQHandler(void){ if (DMA_GetITStatus(DMA2_Stream7, DMA_IT_TCIF7) != RESET) { DMA_ClearFlag(DMA2_Stream7, DMA_IT_TCIF7); DMA_Cmd(DMA2_Stream7, DISABLE); //不使能傳輸 //while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE); USART_ITConfig(USART1, USART_IT_TC, ENABLE); //DMA2_Stream7_working = 0; }}
//uint8_t DMA2_Stream7_working = 0;/** * @brief 串口一+DMA 發(fā)送 * @param[in] *data: 需要發(fā)送的數(shù)據(jù) * @param[in] len: 數(shù)據(jù)的大小 * @retval void */uint32_t usart1_dma_send(uint8_t *data, uint16_t len){ uint32_t result = fifo_write_buff(&fifo_usart_tx_1, data, len); //將數(shù)據(jù)放入循環(huán)緩沖區(qū) //result != 0 說明放入數(shù)據(jù)成功 DMA2_Stream6_working == 0 說明緩沖區(qū)里面沒有數(shù)據(jù) if (DMA2_Stream7_working == 0 && result != 0) { len = fifo_read_buff(&fifo_usart_tx_1, Usart1_Tx, USART1_TX_LEN); //從循環(huán)緩沖區(qū)獲取數(shù)據(jù) DMA_SetCurrDataCounter(DMA2_Stream7, len); //設(shè)定傳輸長度 DMA_Cmd(DMA2_Stream7, ENABLE); //使能傳輸 DMA2_Stream7_working = 1; } if (result == len) { return len; } else { return result; }}
https://www.amobbs.com/thread-4516795-1-1.html
/*指針是指向ptr,需要發(fā)送count個數(shù)據(jù)*/void USART1WriteDataToBuffer(*ptr,u8 count){ /*判斷數(shù)據(jù)是否發(fā)送完畢*/ while(count--) { /*發(fā)送數(shù)據(jù)*/ USART1SendByte(*ptr++); /*等待這個數(shù)據(jù)發(fā)送完畢,然后進(jìn)入下一個數(shù)據(jù)的發(fā)送過程*/ while(USART_GetFlagStatus(USART1, USART_FLAG_TC); } /*數(shù)據(jù)發(fā)送完畢,返回*/}
這段程序會在實際應(yīng)用中產(chǎn)生災(zāi)難性的后果。首先,但發(fā)送數(shù)據(jù)送到寄存器啟動后,CPU就一直在等待這個數(shù)據(jù)發(fā)送完成,這樣,直到所有要發(fā)送的數(shù)據(jù)完成,CPU才能做其他事情。相對于CPU內(nèi)核運行的速度而言,串口外設(shè)的運行速度是非??斓?,讓一個非常快的設(shè)備去等待相對很慢的設(shè)備,程序的效率是非常底下的。
所以盡量采取中斷的方式去發(fā)送數(shù)據(jù)
/*將數(shù)據(jù)寫入發(fā)送緩沖區(qū)*/void USART1WriteDataToBuffer(*ptr,u8 count){ while (count != "/0") { USART1SendTCB[Index++] = *ptr++; Count = count; } /***判斷溢出等其他代碼省略***/}/***發(fā)送中斷的ISR***/void USART1SendUpdate(void){ /***判斷發(fā)送緩沖區(qū)中的數(shù)據(jù)是否發(fā)送完畢***/ /***將發(fā)送緩沖區(qū)的數(shù)據(jù)發(fā)送出去***/ USART1SendByte( *ptr++ ); /***發(fā)送指針加一,待發(fā)送的字節(jié)數(shù)減一等代碼***/}
這樣,當(dāng)調(diào)用USART1WriteDataToBuffer
這個函數(shù)的時候,我們將要發(fā)送的數(shù)據(jù)寫入發(fā)送緩沖區(qū),CPU就可以執(zhí)行其他任務(wù)了,待一個數(shù)據(jù)發(fā)送完成以后,中斷ISR就會觸發(fā),在中斷服務(wù)程序中將下一個數(shù)據(jù)寫入發(fā)送寄存器,啟動下一次發(fā)送,直到完全發(fā)送完畢。
但是在實際工程應(yīng)用中,經(jīng)常會遇到這種類似的情況,串口顯示屏需要顯示1000個點,通過串口發(fā)送這1000個點的顏色的RGB年度數(shù)值。將這1000個數(shù)據(jù)寫入發(fā)送寄存器以后,啟動發(fā)送。在115200的波特率,一位起始位,一位停止位,在無校驗位的情況下,至少需要(10*1000*2)/115200=0.1736
秒,在這段期以內(nèi),時鐘更新了,需要再發(fā)送一串時間更新數(shù)據(jù),這個數(shù)據(jù)大約有100個,這樣這串?dāng)?shù)據(jù)需要寫入到發(fā)送緩沖區(qū)的發(fā)送字節(jié)后面。
同樣的道理,在這個時候如果有顯示任務(wù)更新的話,將會有其他的數(shù)據(jù)寫入到緩沖區(qū)。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JxeVlfbL-1632828582309)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/ourdev_611415V21OP9.jpg)]
從圖上可以看出,程序2雖然滿足了時間上的要求,卻沒有滿空間上的要求,它的數(shù)據(jù)緩沖區(qū)的單向的,這樣,當(dāng)發(fā)送緩沖區(qū)撐滿了以后才能將發(fā)送發(fā)送緩沖區(qū)的數(shù)據(jù)清空,以便下次的緩沖數(shù)據(jù)。這樣內(nèi)存較小的嵌入式系統(tǒng)來說是不能容忍的。
因此,也可以將發(fā)送緩沖區(qū)建立一個環(huán)形緩沖區(qū),在這個緩沖區(qū)內(nèi),通過頭指針(HostIndex)和尾指針(HostIndex)來定位空白區(qū)和數(shù)據(jù)區(qū)。
這樣就實現(xiàn)了發(fā)送緩沖區(qū)的動態(tài)調(diào)整空白區(qū)和數(shù)據(jù)區(qū),剛剛發(fā)送完畢的數(shù)據(jù),馬上就被開辟出來用于存放下一個數(shù)據(jù),最大可能的節(jié)省了寶貴的發(fā)送緩沖區(qū)的空間,提高了效率。
fifo_buff
代碼fifo_buff.c
/** ****************************************************************************** * @file fifo_buff.c/h * @brief 無鎖隊列 * @note * @history 2021.07.08 * @verbatim ============================================================================== 利用 串口+DMA+IDLE中斷+無鎖隊列,提高串口接收效率 (參考《魚鷹談單片機公眾號》) 1、串口初始化函數(shù)一旦執(zhí)行完成,串
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/121488.html
摘要:為了可靠工作,在總線狀態(tài)切換時需要做適當(dāng)延時,再進(jìn)行數(shù)據(jù)收發(fā)。 一、實現(xiàn)效果 ????????基于ucosii實時操作系統(tǒng)的RS485通信,采用USART + DMA進(jìn)行收發(fā), ?二、開發(fā)環(huán)境 開發(fā)工具:KEIL V5開發(fā)板: STM32f107RC采用方式:USART + DMA使用系統(tǒng):...
摘要:異步通信與同步通信異步通信異步通信是指通信的發(fā)送與接收設(shè)備使用各自的時鐘控制數(shù)據(jù)的發(fā)送和接收過程。同步通信同步通信時要建立發(fā)送方時鐘對接收方時鐘的直接控制,使雙方達(dá)到完全同步。配置串口設(shè)置為異步通信基礎(chǔ)參數(shù)波特率為。 ...
摘要:芯片內(nèi)置運算單元,集成了指紋識別算法,能高效快速采集圖像并識別指紋特征。模塊配備了串口通訊接口,用戶無需研究復(fù)雜的圖像處理及指紋識別算法,只需通過簡單的串口按照通訊協(xié)議便可控制模塊。我們的指紋已經(jīng)被成功錄入。 目錄 一、硬件使用分類 1.整體圖展示 ?2.STM32F103RCT6單片機 ...
摘要:當(dāng)單片機要接收數(shù)據(jù)的時候,控制為低電平,數(shù)據(jù)通過接收回來。檢測通過萬用表測量控制的引腳一直處于高電平,即使函數(shù)就單獨寫將該引腳為低電平,測量出來還是高電平。 一、問題: 問題現(xiàn)象:在進(jìn)行RS485操作時,發(fā)現(xiàn)接收時而進(jìn)時而不進(jìn)中斷: 將485的AB輸出腳直接與串口的TX,RX對接發(fā)現(xiàn)串...
摘要:通信引腳與連接方式通信數(shù)據(jù)流動過程需要配置的串口參數(shù)與串口相關(guān)的的庫函數(shù)庫函數(shù)初始化串口步驟代碼二超聲波基礎(chǔ)知識前言是利用超聲波特性檢測距離的傳感器。其帶有兩個超聲波探頭,分別用作發(fā)射和接收超聲波。 注意?。。?!一定要??RCC_APB2PeriphClockCmd(RCC_APB2Peri...
閱讀 829·2021-11-22 11:59
閱讀 3247·2021-11-17 09:33
閱讀 2316·2021-09-29 09:34
閱讀 1947·2021-09-22 15:25
閱讀 1962·2019-08-30 15:55
閱讀 1326·2019-08-30 15:55
閱讀 536·2019-08-30 15:53
閱讀 3351·2019-08-29 13:55