国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

stm32 高效串口收發(fā)

darkerXi / 2315人閱讀

摘要:接收緩沖區(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ā)器

    • USART是一個串行通信設(shè)備,可以靈活地與外部設(shè)備進(jìn)行全雙工數(shù)據(jù)交換。
  • UART(universal asynchronous receiver and transmitter): 通用異步收發(fā)器

    • 異步串行通信口(UART)就是我們在嵌入式中常說的串口,它還是一種通用的數(shù)據(jù)通信議。

區(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)。

USART 中斷

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VkffEvL6-1632828582303)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/image-20210815155528305.png)]

USART 中斷事件被連接到相同的中斷向量:

  • 發(fā)送期間:發(fā)送完成、清除以發(fā)送或發(fā)送數(shù)據(jù)寄存器為空中斷。
  • 接收期間:空閑線路檢測、上溢錯誤、接收數(shù)據(jù)寄存器不為空、奇偶校驗錯誤、LIN 斷路 檢測、噪聲標(biāo)志(僅限多緩沖區(qū)通信)和幀錯誤(僅限多緩沖區(qū)通信)

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-IbZhqtpL-1632828582305)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/image-20210811170712630.png)]

串口模式配置

使用 DMA 進(jìn)行連續(xù)通信

USART 能夠使用 DMA 進(jìn)行連續(xù)通信。接收緩沖區(qū)和發(fā)送緩沖區(qū)的 DMA 請求是獨立的。

使用 DMA 進(jìn)行發(fā)送

使用 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 表示通道編號):

  1. DMA 控制寄存器中寫入 USART_DR 寄存器地址,將其配置為傳輸?shù)哪繕?biāo)地址。每次發(fā)生 TXE 事件后,數(shù)據(jù)都會從存儲器移動到此地址。

  2. DMA 控制寄存器中寫入存儲器地址,將其配置為傳輸?shù)脑吹刂?。每次發(fā)生 TXE 事件后,數(shù)據(jù)都會從這個存儲區(qū)域加載到 USART_DR 寄存器中。

  3. DMA 控制寄存器中配置要傳輸?shù)目傋止?jié)數(shù)。

  4. DMA 寄存器中配置通道優(yōu)先級。

  5. 根據(jù)應(yīng)用的需求,在完成一半或全部傳輸后產(chǎn)生 DMA 中斷。

  6. SR 寄存器中的 TC 位寫入 0,將其清零。

  7. 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。

使用 DMA 進(jìn)行接收

USART_CR3 寄存器中的 DMAR 位置 1 可以使能 DMA 模式進(jìn)行接收。接收數(shù)據(jù)字節(jié)時,數(shù)據(jù)會從 USART_DR 寄存器加載到 SRAM 區(qū)域中(通過 DMA 配置,參見 DMA 規(guī)范)。要映射一個 DMA 通道以進(jìn)行 USART 接收,請按以下步驟操作:

  1. DMA 控制寄存器中寫入 USART_DR 寄存器地址,將其配置為傳輸?shù)脑吹刂?。每次發(fā)生 RXNE 事件后,數(shù)據(jù)都會從此地址移動到存儲器。

  2. DMA 控制寄存器中寫入存儲器地址,將其配置為傳輸?shù)哪繕?biāo)地址。每次發(fā)生 RXNE 事件后,數(shù)據(jù)都會從 USART_DR 寄存器加載到此存儲區(qū)。

  3. DMA 控制寄存器中配置要傳輸?shù)目傋止?jié)數(shù)。

  4. DMA 控制寄存器中配置通道優(yōu)先級。

  5. 根據(jù)應(yīng)用的需求,在完成一半或全部傳輸后產(chǎn)生中斷。

  6. 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)生中斷。

編程

接收的方式:

  1. DMA接收中斷接收
  2. DMA+串口+DMA空閑中斷接收
  3. DMA雙緩沖區(qū)+串口+DMA空閑中斷接收
  4. DMA+串口+DMA空閑中斷+環(huán)形隊列接收

發(fā)送的方式:

  1. DMA+串口發(fā)送
  2. 單串口發(fā)送
  3. DMA+串口發(fā)送+環(huán)形隊列(雙緩沖)
  4. 動態(tài)內(nèi)存分配的FIFIO

下面主要用 環(huán)形隊列+DMA+非動態(tài)內(nèi)存分配+IDLE中斷

建議先看最下面的參考文章

接收流程

USART1 + DMA + IDLE中斷 +無鎖隊列

主函數(shù):

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;        }    }}

中斷處理函數(shù):

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;        }    }}

初始化(標(biāo)準(zhǔn)庫)

#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 的冪次方        }    }}

發(fā)送流程

在設(shè)計串口驅(qū)動過程中,要遵循的準(zhǔn)則是:

  1. 盡量的減少程序運行時間
  2. 盡量的減少程序所占的內(nèi)存

開啟串口發(fā)送完成中斷

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;    }}

開啟DMA發(fā)送完成中斷

//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;    }}

DMA發(fā)送函數(shù)

//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

stm32使用dma傳輸串口數(shù)據(jù)時,當(dāng)dma中斷發(fā)送完成

程序1

/*指針是指向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ù)

程序2

/*將數(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ū)。

  • 頭指針:指向有數(shù)據(jù)區(qū)的頂部,每次寫入數(shù)據(jù),都更新頭指針,如果到了緩沖區(qū)的末端,就自動返回到緩沖區(qū)的起始處(StartIndex),直到寫入到尾指針為止,這時緩沖區(qū)已經(jīng)被裝滿,不能再裝入數(shù)據(jù)。
  • 尾指針:指向有數(shù)據(jù)區(qū)的尾部,當(dāng)數(shù)據(jù)發(fā)送完畢后,更新尾指針的位置,如果到了緩沖區(qū)的末端(EndIndex),就自動返回到緩沖區(qū)的起始處(StartIndex),直到遇到頭指針為止,這是證明所有的數(shù)據(jù)已經(jīng)發(fā)送完畢。

這樣就實現(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

相關(guān)文章

  • 基于UCOSII的RS485通信(STM32F107)

    摘要:為了可靠工作,在總線狀態(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):...

    verano 評論0 收藏0
  • STM32】標(biāo)準(zhǔn)庫與HAL庫對照學(xué)習(xí)教程八--串口通信詳解

    摘要:異步通信與同步通信異步通信異步通信是指通信的發(fā)送與接收設(shè)備使用各自的時鐘控制數(shù)據(jù)的發(fā)送和接收過程。同步通信同步通信時要建立發(fā)送方時鐘對接收方時鐘的直接控制,使雙方達(dá)到完全同步。配置串口設(shè)置為異步通信基礎(chǔ)參數(shù)波特率為。 ...

    yck 評論0 收藏0
  • AS608指紋+STM32串口通信錄入或刪除指紋

    摘要:芯片內(nèi)置運算單元,集成了指紋識別算法,能高效快速采集圖像并識別指紋特征。模塊配備了串口通訊接口,用戶無需研究復(fù)雜的圖像處理及指紋識別算法,只需通過簡單的串口按照通訊協(xié)議便可控制模塊。我們的指紋已經(jīng)被成功錄入。 目錄 一、硬件使用分類 1.整體圖展示 ?2.STM32F103RCT6單片機 ...

    kel 評論0 收藏0
  • 關(guān)于STM32 RS485控制I/O口不能正常輸出高低電平的解決方法

    摘要:當(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)串...

    null1145 評論0 收藏0
  • 項目四:串口打印超聲波測距

    摘要:通信引腳與連接方式通信數(shù)據(jù)流動過程需要配置的串口參數(shù)與串口相關(guān)的的庫函數(shù)庫函數(shù)初始化串口步驟代碼二超聲波基礎(chǔ)知識前言是利用超聲波特性檢測距離的傳感器。其帶有兩個超聲波探頭,分別用作發(fā)射和接收超聲波。 注意?。。?!一定要??RCC_APB2PeriphClockCmd(RCC_APB2Peri...

    shusen 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<