0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

rt-thread 驅(qū)動(dòng)篇(二) serialX 理論實(shí)現(xiàn)

出出 ? 來(lái)源:出出 ? 作者:出出 ? 2022-06-22 09:03 ? 次閱讀

前言

“serialX” 我起的名字,起個(gè)名字想破頭。
在前一篇文章里,大致提出了我的串口驅(qū)動(dòng)框架理論。里面做了一些對(duì)串口驅(qū)動(dòng)特性的幻想。也在 NUC970 芯片下通過(guò)了中斷模式的實(shí)踐驗(yàn)證。但是,因?yàn)?NUC970 的 uart 自帶 fifo 。用它測(cè)試效果好,并不能真正說(shuō)明驅(qū)動(dòng)框架通過(guò)驗(yàn)證了。

然后,緊接著筆者在 STM32F429 完成了中斷和 DMA 兩種模式。今天,我把一些測(cè)試結(jié)果和移植說(shuō)明發(fā)出來(lái),征求全網(wǎng)公測(cè)。

測(cè)試配置:DMA 二級(jí)緩存 32 個(gè)字節(jié),串口收發(fā)緩存各 512 字節(jié)。

注:本串口驅(qū)動(dòng)工作特性請(qǐng)參閱前一篇文章rt-thread 驅(qū)動(dòng)篇(一) serialX 框架理論

STM32 中斷模式測(cè)試

以下是三組連續(xù)發(fā)收測(cè)試:

1. 定時(shí)間隔20ms,發(fā)送250字節(jié)數(shù)據(jù),持續(xù)發(fā)送2600w,接收發(fā)送數(shù)據(jù)量相等

250-20ms-2600w.png

2. 定時(shí)間隔50ms,發(fā)送250字節(jié)數(shù)據(jù),持續(xù)發(fā)送600w,接收發(fā)送數(shù)據(jù)量相等

250-50ms-600w.png

3. 定時(shí)間隔80ms,發(fā)送1000字節(jié)數(shù)據(jù),持續(xù)發(fā)送600w,接收發(fā)送數(shù)據(jù)量相等

1000-80ms-610w.png

注:剛剛跟我們小伙伴求證了一下,串口調(diào)試助手的定時(shí)間隔是固定周期。如果是這樣的,以上測(cè)試是有意義的,如果不是,那就沒(méi)達(dá)到串口帶寬上限。

STM32 DMA模式測(cè)試

1. 讀寫測(cè)試,串口調(diào)試助手定時(shí) 10ms ,發(fā)送40字節(jié)數(shù)據(jù),持續(xù)發(fā)送129w

dma-40-10ms-129w.png

2. 串口調(diào)試助手定時(shí) 50ms ,發(fā)送500字節(jié)數(shù)據(jù),持續(xù)發(fā)送527w

dma-500-50ms-527w.png

3. 串口調(diào)試助手定時(shí) 40ms ,發(fā)送500字節(jié)數(shù)據(jù),持續(xù)發(fā)送261w

dma-500-40ms-261w.png

4. 串口調(diào)試助手定時(shí) 40ms ,發(fā)送1000字節(jié)數(shù)據(jù),持續(xù)發(fā)送262w

dma-1000-40ms-262w.png

串口調(diào)試助手上發(fā)送和接收數(shù)量不相等,接著我在代碼中添加了個(gè)斷點(diǎn),單獨(dú)發(fā)送了一個(gè)字節(jié) ‘Z’ 。

dma-1000-40ms-262w-rw.png

代碼中接收和發(fā)送數(shù)量相等,都等于串口調(diào)試助手的接收量。這個(gè)缺少的部分是串口調(diào)試助手發(fā)送失敗數(shù)量,還是串口驅(qū)動(dòng)接收丟失了?

接下來(lái),修改成中斷接收發(fā)送模式,其它不做修改進(jìn)行相同的測(cè)試,也是有數(shù)量差。進(jìn)一步檢查串口驅(qū)動(dòng)里,接收緩存有溢出現(xiàn)象。應(yīng)用層沒(méi)來(lái)得及把數(shù)據(jù)取走,就刪掉了最舊的數(shù)據(jù)。

接口詳解及移植說(shuō)明

rtdef.h 添加幾個(gè)宏定義

添加阻塞打開(kāi)相關(guān)標(biāo)志

#define RT_DEVICE_OFLAG_BLOCKING        0x000           /**< blocking io mode */
#define RT_DEVICE_OFLAG_NONBLOCKING     0x004           /**< non-blocking io mode */
...

#define RT_DEVICE_CTRL_BLOCKING         0x05            /**< blocking io */

serialX.h

添加串口驅(qū)動(dòng)緩存和 DMA 二級(jí)緩存大小定義(放棄使用 `RT_SERIAL_RB_BUFSZ`):

#ifndef RT_SERIAL_FIFO_BUFSZ
#define RT_SERIAL_FIFO_BUFSZ            512
#endif
#ifndef RT_SERIAL_DMA_BUFSZ
#define RT_SERIAL_DMA_BUFSZ             32
#endif

串口接收和發(fā)送使用的緩存大小是一樣的,如果想改變串口緩存大小,請(qǐng)修改 `RT_SERIAL_FIFO_BUFSZ` 的值。

如果想改變 DMA 二級(jí)緩存大小,請(qǐng)修改 `RT_SERIAL_DMA_BUFSZ` 的值。

定義一個(gè)收發(fā)通用 fifo:

struct rt_serial_fifo
{
   rt_uint32_t buf_sz;
   /* software fifo */
   rt_uint8_t *buffer;
   rt_uint16_t put_index, get_index;

   rt_bool_t is_full;
};

重新定義 `rt_serial_device` 定義:

struct rt_serial_device
{
   struct rt_device          parent;
   const struct rt_uart_ops *ops;
   struct serial_configure   config;

   void *serial_rx;   // 串口接收緩存
   void *serial_tx;   // 串口發(fā)送緩存

#ifdef RT_SERIAL_USING_DMA       // 串口收發(fā)緩存和 DMA 使用的二級(jí)緩存分開(kāi)
   rt_size_t dma_idx_rx;
   rt_uint8_t serial_dma_rx[RT_SERIAL_DMA_BUFSZ]; // DMA 接收緩存
   rt_uint8_t serial_dma_tx[RT_SERIAL_DMA_BUFSZ]; // DMA 發(fā)送緩存
#endif

   cb_serial_tx _cb_tx;  // 寫過(guò)程回調(diào)函數(shù)指針
   cb_serial_rx _cb_rx;  // 讀過(guò)程回調(diào)函數(shù)指針

   struct rt_completion completion_tx;    // 發(fā)送完成
   struct rt_completion completion_rx;    // 接收到新數(shù)據(jù)
};
typedef struct rt_serial_device rt_serial_t;

串口驅(qū)動(dòng)通用框架和硬件底層接口定義

struct rt_uart_ops
{
// 用于配置外設(shè)寄存器,引腳功能復(fù)用,啟用外設(shè)等等
   rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);
   // 用于使能禁用中斷,初始配置 DMA
   rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);
// 串口外設(shè)寫數(shù)據(jù)寄存器*為空*,把數(shù)據(jù)放入寫數(shù)據(jù)寄存器。*不為空*,死等
   int (*putc)(struct rt_serial_device *serial, char c);
   // 串口外設(shè)讀數(shù)據(jù)寄存器*不為空*,讀出讀數(shù)據(jù)寄存器的值。*為空*,返回 -1
   int (*getc)(struct rt_serial_device *serial);
// 啟動(dòng)發(fā)送,多數(shù)是開(kāi)啟串口外設(shè)發(fā)送寄存器空中斷
   void (*start_tx)(struct rt_serial_device *serial);
// 結(jié)束發(fā)送,多數(shù)是關(guān)閉串口外設(shè)發(fā)送寄存器空中斷
   void (*stop_tx)(struct rt_serial_device *serial);

#ifdef RT_SERIAL_USING_DMA
// 判斷 DMA 是否在發(fā)送過(guò)程中,就像上一篇里筆者多次提示的,必須有效檢測(cè) DMA 是否在發(fā)送數(shù)據(jù)中
   rt_bool_t (*is_dma_txing)(struct rt_serial_device *serial);
   // 啟動(dòng) DMA 發(fā)送
   void (*start_dma_tx)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size);
   // 停止 DMA 發(fā)送
   void (*stop_dma_tx)(struct rt_serial_device *serial);
#endif
// 使能串口外設(shè)中斷
   void (*enable_interrupt)(struct rt_serial_device *serial);
   // 禁用串口外設(shè)中斷
   void (*disable_interrupt)(struct rt_serial_device *serial);
};

移植 serialX 到新芯片上,必須按照 `rt_uart_ops` 的定義實(shí)現(xiàn)上述幾個(gè)接口。函數(shù)功能不能隨意更改。

`rt_hw_serial_isr`

這個(gè)中斷只接收 `RT_SERIAL_EVENT_RX_IND` `RT_SERIAL_EVENT_RX_IND` `RT_SERIAL_EVENT_RX_DMADONE` `RT_SERIAL_EVENT_TX_DMADONE` 四種中斷狀態(tài)。

  • `RT_SERIAL_EVENT_RX_IND` 接收寄存器不空中斷
  • `RT_SERIAL_EVENT_TX_DONE` 發(fā)送寄存器空中斷,為了兼容自帶 fifo 的芯片,event 參數(shù)的高三字節(jié)代表 fifo 容量
  • `RT_SERIAL_EVENT_RX_DMADONE` 串口接收 DMA 中斷。 這個(gè)可以兼容接收半傳輸和全傳輸?shù)榷喾N中斷。event 參數(shù)的高三字節(jié)代表 DMA fifo 接收數(shù)據(jù)數(shù)量(1-RT_SERIAL_DMA_BUFSZ)。
  • `RT_SERIAL_EVENT_TX_DMADONE` 串口發(fā)送 DMA 中斷。這個(gè)應(yīng)該保證 DMA 發(fā)送完本次 DMA 緩存中的所有數(shù)據(jù),也就是對(duì)于 stm32 芯片是 DMA 計(jì)數(shù)達(dá)到 0。

使用注意

  • `RT_SERIAL_FIFO_BUFSZ` `RT_SERIAL_DMA_BUFSZ` 兩個(gè)的定義和實(shí)際是否合適,小數(shù)據(jù)量通信可以定義小點(diǎn)兒,數(shù)據(jù)量大的情況適當(dāng)調(diào)整這兩個(gè)值。
  • `rt_uart_ops` 接口定義,功能實(shí)現(xiàn)必須匹配。
  • 阻塞模式,收發(fā)是一致的。默認(rèn)是阻塞模式。想使用非阻塞模式請(qǐng) open 的時(shí)候添加 `RT_DEVICE_OFLAG_NONBLOCKING` flag。
  • 使用 `RT_DEVICE_FLAG_INT` `RT_DEVICE_FLAG_DMA_RX` `RT_DEVICE_FLAG_INT_TX` `RT_DEVICE_FLAG_DMA_TX` 四個(gè) open flag 指定收發(fā)模式,是用中斷還是 DMA。
  • **特別提醒**,非阻塞模式下,read 可能返回 0。write 返回值可能不是目標(biāo)寫入 size。read/write 還可能返回 `RT_EXXX` 錯(cuò)誤值。
  • **特別提醒**,阻塞模式下,read 返回值可能不是期望數(shù)據(jù)量 size。筆者也曾經(jīng)提供過(guò)可靠處理流數(shù)據(jù)的方案,詳見(jiàn) rt-thread 使用寶典(2022-0516更新)

使用完成量進(jìn)入阻塞漏洞分析

> PS: 謝謝 @HelloBye 的及時(shí)糾正,`rt_completion` 不存在本小節(jié)描述的漏洞。各位看官可以直接跳過(guò)本小節(jié)了

串口驅(qū)動(dòng)里有幾個(gè)阻塞點(diǎn),進(jìn)入阻塞都使用的 `rt_completion` ,如下代碼:

serial->ops->enable_interrupt(serial);
ret = rt_completion_wait(&(serial->completion_rx), RT_WAITING_FOREVER);// 或者 serial->completion_tx

首先開(kāi)中斷,調(diào)用 `rt_completion_wait` 等待完成量進(jìn)入阻塞。這樣是有個(gè)漏洞的,當(dāng)開(kāi)中斷后有個(gè)串口中斷,中斷處理函數(shù)里調(diào)用 `rt_completion_done` 是沒(méi)有任何反應(yīng)的,`rt_completion_done` 直接返回退出。

進(jìn)而回到原線程才執(zhí)行 `rt_completion_wait`。之后,如果有第二次接收(或發(fā)送)中斷發(fā)生時(shí)才會(huì)結(jié)束上一次的阻塞。但是,第二次什么時(shí)候出現(xiàn)也就是個(gè)未知數(shù)了。即便前一次可能已經(jīng)收全了全部想要的數(shù)據(jù),但是會(huì)不定期阻塞下去。

解決方法有兩個(gè):一、不用永久阻塞,換成 10ms 或者幾 ms 等待;二、用二值信號(hào)量替代。

但是?。?!我沒(méi)有用上述方法中的任何一個(gè)進(jìn)行改進(jìn),原因是:

第一種方法無(wú)疑要引入一個(gè)循環(huán),`rt_completion_wait` 超時(shí)返回的時(shí)候循環(huán)繼續(xù)阻塞。還有就是等待時(shí)間沒(méi)有理論支持。最重要的是用循環(huán)方式補(bǔ)漏洞的方式不美觀。

沒(méi)使用二值信號(hào)量的原因是,rt-thread 的信號(hào)量實(shí)現(xiàn)沒(méi)有真正的“二值”,如果中斷已經(jīng)多次 release 了,然后應(yīng)用層才來(lái)一次 take,之后還可能成功 take 多次(雖然應(yīng)該是要阻塞的,但是實(shí)際不阻塞,反而會(huì)循環(huán) take 多次)。

結(jié)束語(yǔ)

現(xiàn)筆者將打碼開(kāi)放出來(lái) gitee 倉(cāng)庫(kù) [serialX]( https://gitee.com/thewon/serialX ),求全論壇公測(cè)。期待各位大佬提出各種測(cè)試方案對(duì)它蹂躪。

有問(wèn)題可以在倉(cāng)庫(kù)里提 [issue]( https://gitee.com/thewon/serialX/issues ) ,或者到 rt-thread 官方論壇上進(jìn)行討論。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • STM32
    +關(guān)注

    關(guān)注

    2258

    文章

    10828

    瀏覽量

    352493
  • 中斷
    +關(guān)注

    關(guān)注

    5

    文章

    889

    瀏覽量

    41220
  • 串口驅(qū)動(dòng)
    +關(guān)注

    關(guān)注

    2

    文章

    81

    瀏覽量

    18503
  • RT-Thread
    +關(guān)注

    關(guān)注

    31

    文章

    1239

    瀏覽量

    39435
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    RT-Thread記錄(一、版本開(kāi)發(fā)環(huán)境及配合CubeMX)

    RT-Thread 學(xué)習(xí)記錄的第一文章,RT-Thread記錄(一、RT-Thread 版本、RT-Thread Studio開(kāi)發(fā)環(huán)境 及
    的頭像 發(fā)表于 06-20 00:28 ?4925次閱讀
    <b class='flag-5'>RT-Thread</b>記錄(一、版本開(kāi)發(fā)環(huán)境及配合CubeMX)

    RT-Thread記錄(RT-Thread內(nèi)核啟動(dòng)流程)

    在前面我們RT-Thread Studio工程基礎(chǔ)之上講一講RT-Thread內(nèi)核啟動(dòng)流程.
    的頭像 發(fā)表于 06-20 00:30 ?4784次閱讀
    <b class='flag-5'>RT-Thread</b>記錄(<b class='flag-5'>二</b>、<b class='flag-5'>RT-Thread</b>內(nèi)核啟動(dòng)流程)

    rt-thread 驅(qū)動(dòng)(六)serialX弊端及解決方法

    serialX 作為一個(gè)非阻塞串口驅(qū)動(dòng)框架,在遇到一些異常時(shí),需要做一些特殊處理,今天,筆者帶大家來(lái)扒一扒 serialX 使用過(guò)程中需要注意哪些問(wèn)題。
    的頭像 發(fā)表于 06-20 11:43 ?3147次閱讀

    RT-Thread NUC97x 移植 LVGL

    不涉及 rt-thread 驅(qū)動(dòng),但是它是 LVGL 和 rt-thread 的接口。LVGL 在 rt-thread 上運(yùn)行的基石。
    發(fā)表于 07-08 09:37 ?1426次閱讀

    基于RT-Thread的SPI通訊

    驅(qū)動(dòng)層的驅(qū)動(dòng)。(rt-thread的設(shè)備 I/O 模型有設(shè)備管理層、設(shè)備驅(qū)動(dòng)框架層、設(shè)備驅(qū)動(dòng)層),我寫過(guò)一
    的頭像 發(fā)表于 08-22 09:28 ?1534次閱讀

    RT-Thread ssd1306驅(qū)動(dòng)

    RT-Thread 驅(qū)動(dòng)ssd1306
    的頭像 發(fā)表于 04-21 10:08 ?26.4w次閱讀
    <b class='flag-5'>RT-Thread</b> ssd1306<b class='flag-5'>驅(qū)動(dòng)</b>

    【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集

    軟件包)NO2 專欄作者 :出出簡(jiǎn)介:rt-thread 研究。1. rt-thread 驅(qū)動(dòng)rt-thread
    發(fā)表于 07-26 14:56

    RT-Thread編程指南

    RT-Thread編程指南——RT-Thread開(kāi)發(fā)組(2015-03-31)。RT-Thread做為國(guó)內(nèi)有較大影響力的開(kāi)源實(shí)時(shí)操作系統(tǒng),本文是RT-Thread實(shí)時(shí)操作系統(tǒng)的編程指南
    發(fā)表于 11-26 16:06 ?0次下載

    RT-Thread Studio驅(qū)動(dòng)SD卡

    RT-Thread Studio驅(qū)動(dòng)SD卡前言一、創(chuàng)建基本工程1、創(chuàng)建Bootloader2、創(chuàng)建項(xiàng)目工程、配置RT-Thread Settings三、代碼分析1.引入庫(kù)2.讀入數(shù)據(jù)
    發(fā)表于 12-27 19:13 ?20次下載
    <b class='flag-5'>RT-Thread</b> Studio<b class='flag-5'>驅(qū)動(dòng)</b>SD卡

    RT-Thread全球技術(shù)大會(huì):RT-Thread對(duì)POSIX的實(shí)現(xiàn)情況介紹

    RT-Thread全球技術(shù)大會(huì):RT-Thread對(duì)POSIX的實(shí)現(xiàn)情況介紹 ? ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 16:52 ?1767次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):<b class='flag-5'>RT-Thread</b>對(duì)POSIX的<b class='flag-5'>實(shí)現(xiàn)</b>情況介紹

    rt-thread 驅(qū)動(dòng)(四)serialX 多架構(gòu)適配

    自筆者提出 serialX 串口驅(qū)動(dòng)到今天近半年了,當(dāng)初只在 STM32F4 NUC970 兩個(gè)系列芯片上做過(guò)理論驗(yàn)證。一個(gè)是 ARM CM4 核心架構(gòu),一個(gè)是 ARM9。這兩款芯片能完美實(shí)現(xiàn)
    的頭像 發(fā)表于 06-10 10:21 ?2861次閱讀

    rt-thread 驅(qū)動(dòng)(五)serialX 小試牛刀

    終于來(lái)到了 serialX 的實(shí)踐,期待很久了。
    的頭像 發(fā)表于 06-16 11:29 ?4279次閱讀
    <b class='flag-5'>rt-thread</b> <b class='flag-5'>驅(qū)動(dòng)</b><b class='flag-5'>篇</b>(五)<b class='flag-5'>serialX</b> 小試牛刀

    RT-Thread文檔_RT-Thread 簡(jiǎn)介

    RT-Thread文檔_RT-Thread 簡(jiǎn)介
    發(fā)表于 02-22 18:22 ?5次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> 簡(jiǎn)介

    RT-Thread文檔_RT-Thread SMP 介紹與移植

    RT-Thread文檔_RT-Thread SMP 介紹與移植
    發(fā)表于 02-22 18:31 ?9次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> SMP 介紹與移植

    RT-Thread設(shè)備驅(qū)動(dòng)開(kāi)發(fā)指南》基礎(chǔ)--以先楫bsp的hwtimer設(shè)備為例

    一、概述(一)RT-Thread設(shè)備驅(qū)動(dòng)RT-Thread設(shè)備驅(qū)動(dòng)開(kāi)發(fā)指南》書籍是RT-thread官方出品撰寫,系統(tǒng)講解
    的頭像 發(fā)表于 02-24 08:16 ?951次閱讀
    《<b class='flag-5'>RT-Thread</b>設(shè)備<b class='flag-5'>驅(qū)動(dòng)</b>開(kāi)發(fā)指南》基礎(chǔ)<b class='flag-5'>篇</b>--以先楫bsp的hwtimer設(shè)備為例