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記錄(十二、UART設(shè)備—使用測(cè)試)

矜辰所致 ? 來(lái)源:矜辰所致 ? 作者:矜辰所致 ? 2022-07-02 12:42 ? 次閱讀
UART 設(shè)備開始學(xué)會(huì)使用 RT-Thread I/O 設(shè)備模型 。

目錄

前言
一、UART 設(shè)備操作

1.1 UART 設(shè)備控制塊
1.2 UART 操作函數(shù)
1.2.1 查找 UART 設(shè)備
1.2.2 打開/關(guān)閉 UART 設(shè)備
實(shí)際應(yīng)用中的串口讀寫說明
1.2.3 控制 UART 設(shè)備
1.2.4 發(fā)送數(shù)據(jù)
1.2.5 設(shè)置接收回調(diào)函數(shù)
1.2.6 接收數(shù)據(jù)

二、UART 設(shè)備使用步驟

2.1 RT-Thread setting
2.2 board.h 設(shè)置
2.3 應(yīng)用程序流程

三、UART 示例測(cè)試

3.1 與無(wú)線模塊串口通訊
3.2 示例說明

結(jié)語(yǔ)

前言

通過前面的兩篇文章,我們基本上完全明白了 RT-Thread I/O 設(shè)備模型的基本原理,當(dāng)然我們的最終目的還是應(yīng)用,所以本文開始我們就開始進(jìn)行常用設(shè)備的使用學(xué)習(xí)和測(cè)試,就從 UART 設(shè)備開始。

從本文開始,就開始進(jìn)行常用 I/O 設(shè)備的學(xué)習(xí)測(cè)試。

本 RT-Thread 專欄記錄的開發(fā)環(huán)境:
RT-Thread記錄(一、RT-Thread 版本、RT-Thread Studio開發(fā)環(huán)境 及 配合CubeMX開發(fā)快速上手)
RT-Thread記錄(二、RT-Thread內(nèi)核啟動(dòng)流程 — 啟動(dòng)文件和源碼分析)
RT-Thread 設(shè)備篇系列博文鏈接:
RT-Thread記錄(十、全面認(rèn)識(shí) RT-Thread I/O 設(shè)備模型)
RT-Thread記錄(十一、I/O 設(shè)備模型之UART設(shè)備 — 源碼解析)

一、UART 設(shè)備操作

雖然在上一篇文章中,我們已經(jīng)認(rèn)識(shí)過 RT-Thread UART 的操作函數(shù),但是我們并沒有對(duì)其參數(shù)進(jìn)行說明。
學(xué)習(xí)使用一個(gè)設(shè)備,在 RT-Thread 系統(tǒng)中就是一個(gè)對(duì)象, 還是得按照我們之前的流程進(jìn)行簡(jiǎn)單介紹。

1.1 UART 設(shè)備控制塊

在我們前面許多文章介紹其他內(nèi)核對(duì)象的時(shí)候,我們首先都會(huì)介紹其對(duì)象控制塊,對(duì)于 UART 設(shè)備而言,它也有自己的控制塊。
但是與其他對(duì)象機(jī)制不同的是,UART 屬于 I/O 設(shè)備,對(duì)于上層應(yīng)用程序而言,所有的 I/O 設(shè)備都是屬于 struct rt_device 類。

在我們前面文章《RT-Thread記錄(十、全面認(rèn)識(shí) RT-Thread I/O 設(shè)備模型》初次介紹 I/O 設(shè)備模型的時(shí)候就已經(jīng)說明了這個(gè)統(tǒng)一的控制塊:

poYBAGK_zLyAHSI4AAFJTr2oFO8861.png

上面的控制塊是對(duì)于應(yīng)用程序而言,在我們的 UART 設(shè)備的設(shè)備驅(qū)動(dòng)框架層,是有定義了 UART 設(shè)備自己的控制塊,其繼承了rt_device的內(nèi)容,同時(shí)還增加了 UART 設(shè)備特有的一些配置,操作,回調(diào)函數(shù)之類的內(nèi)容,如下圖:

pYYBAGK_zLyAGs_VAABHg_EJRBM341.png

上面的 UART 設(shè)備控制塊在我們的上一篇文章也有過分析說明。

?? UART 設(shè)備屬于 I/O 設(shè)備大類中的一個(gè)小類,對(duì)于上層應(yīng)用程序而言,UART 設(shè)備控制塊rt_serial_device并不透明,我們用戶操作的還是 I/O 設(shè)備模型的控制塊rt_device_t類型。

1.2 UART 操作函數(shù)

因?yàn)?UART 的操作函數(shù) 與 I/O 設(shè)備的操作函數(shù)基本一致,所以本小結(jié)有點(diǎn)類似《RT-Thread記錄(十、全面認(rèn)識(shí) RT-Thread I/O 設(shè)備模型》中的 2.3 訪問 I/O 設(shè)備相關(guān) API 操作,但是針對(duì) UART 設(shè)備,也有一些獨(dú)有的參數(shù)說明。

老規(guī)矩,函數(shù)介紹部分說明看注釋。

1.2.1 查找 UART 設(shè)備

需要先定義一個(gè) I/O 設(shè)備結(jié)構(gòu)體(rt_device_t類型)的指針變量,接收創(chuàng)建好的句柄。

/*
參數(shù) 	描述
name 	設(shè)備名稱,對(duì)于UART設(shè)備而言,默認(rèn)一般是 uart0,uart1,uart2,uart3 等
返回 	——
設(shè)備句柄 	查找到對(duì)應(yīng)設(shè)備將返回相應(yīng)的設(shè)備句柄
RT_NULL 	沒有找到相應(yīng)的設(shè)備對(duì)象
*/
rt_device_t rt_device_find(const char* name);

1.2.2 打開/關(guān)閉 UART 設(shè)備

先說打開 UART 設(shè)備:

/**
參數(shù) 	描述
dev 	設(shè)備句柄
oflags 	設(shè)備模式標(biāo)志

oflags可選的的值如下:
#define RT_DEVICE_FLAG_STREAM       0x040     流模式     
接收模式參數(shù)
#define RT_DEVICE_FLAG_INT_RX       0x100     中斷接收模式
#define RT_DEVICE_FLAG_DMA_RX       0x200     DMA 接收模式
發(fā)送模式參數(shù)
#define RT_DEVICE_FLAG_INT_TX       0x400     中斷發(fā)送模式
#define RT_DEVICE_FLAG_DMA_TX       0x800     DMA 發(fā)送模式

返回值:
RT_EOK 	設(shè)備打開成功
-RT_EBUSY 	如果設(shè)備注冊(cè)時(shí)指定的參數(shù)中包括 RT_DEVICE_FLAG_STANDALONE 參數(shù),此設(shè)備將不允許重復(fù)打開
其他錯(cuò)誤碼 	設(shè)備打開失敗
 */
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)

打開設(shè)備時(shí),會(huì)檢測(cè)設(shè)備是否已經(jīng)初始化,沒有初始化則會(huì)默認(rèn)調(diào)用初始化接口初始化設(shè)備。

如果 oflags 沒有指定使用中斷模式或者 DMA 模式,則默認(rèn)使用輪詢模式。

這里有個(gè)問題,流模式是什么情況下使用的?485通訊? 暫時(shí)不知道,希望知道的朋友能夠給個(gè)說明。

在官方的文檔中,關(guān)于流模式有如下說明:

poYBAGK_zLyAA2QVAAB8BN8W5nI975.png

實(shí)際應(yīng)用中的串口讀寫說明

串口RX:

在我們正常的項(xiàng)目使用中,一般都是 中斷接收 或者 DMA 接收,基本上不會(huì)使用 輪詢接收的方式(極大的浪費(fèi)資源,反正我是沒用過)。

所以我們打開串口設(shè)備的時(shí)候,基本上都是如下兩種:

rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);

rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX);

在 RT-Thread 系統(tǒng)中,我們常用信號(hào)量或者消息隊(duì)列 來(lái)標(biāo)志是否接收到串口數(shù)據(jù),這樣的好處是當(dāng)沒有數(shù)據(jù)的時(shí)候,會(huì)將數(shù)據(jù)處理線程掛機(jī),讓出CPU資源。

串口TX:

對(duì)于串口 TX 來(lái)說,大部分項(xiàng)目中我自己一直都用的是 輪詢 方式發(fā)送。

對(duì)于串口的中斷發(fā)送方式,在上一篇文章我們分析 UART 源碼,雖然沒有詳細(xì)說明,但是實(shí)際上在設(shè)備驅(qū)動(dòng)層 drv_usart.c 驅(qū)動(dòng)文件里,中斷發(fā)送方式最終還是調(diào)用了該驅(qū)動(dòng)文件里面的stm32_putc函數(shù):

pYYBAGK_zLyAKHpmAACFz153GM8372.png

我感覺還是和輪詢一樣,將數(shù)據(jù)寫入 數(shù)據(jù)寄存器DR,使用while死等發(fā)送完成(雖然時(shí)間很短)。

上面雖然只是 RT-Thread 中的UART設(shè)備驅(qū)動(dòng)文件,也多少能說明一些問題,中斷發(fā)送最終無(wú)非就是發(fā)送完了多一個(gè)中斷通知。

對(duì)于另外一種 DMA 發(fā)送,我記得以前聽老人提到過,DMA發(fā)送使用不得當(dāng),可能導(dǎo)致發(fā)送數(shù)據(jù)異常,簡(jiǎn)單來(lái)說就是 DMA 發(fā)送函數(shù)返回后,數(shù)據(jù)都不一定發(fā)送完成了,如果此時(shí)修改了 DMA 發(fā)送指定的buffer 區(qū)的內(nèi)容,那么后面的數(shù)據(jù)就錯(cuò)誤了。

所以,如果沒有特殊需求,我們項(xiàng)目中的串口發(fā)送使用 輪詢發(fā)送 即可(有些特殊情況的根據(jù)自己的實(shí)際需求而定)。

所以結(jié)合上面所說,我們實(shí)際應(yīng)用中,使用以下兩種方式打開串口設(shè)備能滿足大部分場(chǎng)合需求:

/*輪詢方式發(fā)送,中斷接收*/
rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
/*輪詢方式發(fā)送,DMA接收*/
rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX);

有打開設(shè)備,當(dāng)然也有關(guān)閉設(shè)備:

/**
參數(shù) 	描述
dev 	設(shè)備句柄
返回 	——
RT_EOK 	關(guān)閉設(shè)備成功
-RT_ERROR 	設(shè)備已經(jīng)完全關(guān)閉,不能重復(fù)關(guān)閉設(shè)備
其他錯(cuò)誤碼 	關(guān)閉設(shè)備失敗
 */
rt_err_t rt_device_close(rt_device_t dev)

關(guān)閉設(shè)備接口和打開設(shè)備接口需配對(duì)使用,打開一次設(shè)備對(duì)應(yīng)要關(guān)閉一次設(shè)備,這樣設(shè)備才會(huì)被完全關(guān)閉,否則設(shè)備仍處于未關(guān)閉狀態(tài)。

當(dāng)然在一般的應(yīng)用場(chǎng)合,用到串口通訊的地方設(shè)備都是需要一直開啟的,所以很多情況下都不需要使用 UART 設(shè)備關(guān)閉函數(shù)。

1.2.3 控制 UART 設(shè)備

rt_device_control 一般用在 rt_device_open (打開串口設(shè)備)之前,對(duì)需要使用的串口進(jìn)行必要的配置。

/**
參數(shù) 	描述
dev 	設(shè)備句柄
cmd 	命令控制字,可取值:RT_DEVICE_CTRL_CONFIG
arg 	控制的參數(shù),可取類型: 
struct serial_configure
{
    rt_uint32_t baud_rate;             波特率 
    rt_uint32_t data_bits    :4;       數(shù)據(jù)位 
    rt_uint32_t stop_bits    :2;       停止位 
    rt_uint32_t parity       :2;       奇偶校驗(yàn)位 
    rt_uint32_t bit_order    :1;       高位在前或者低位在前 
    rt_uint32_t invert       :1;       模式 
    rt_uint32_t bufsz        :16;      接收數(shù)據(jù)緩沖區(qū)大小 
    rt_uint32_t reserved     :4;       保留位 
};

波特率可取值:
#define BAUD_RATE_2400                  2400
#define BAUD_RATE_4800                  4800
#define BAUD_RATE_9600                  9600
#define BAUD_RATE_19200                 19200
#define BAUD_RATE_38400                 38400
#define BAUD_RATE_57600                 57600
#define BAUD_RATE_115200                115200
#define BAUD_RATE_230400                230400
#define BAUD_RATE_460800                460800
#define BAUD_RATE_921600                921600
#define BAUD_RATE_2000000               2000000
#define BAUD_RATE_3000000               3000000

數(shù)據(jù)位可取值:
#define DATA_BITS_5                     5
#define DATA_BITS_6                     6
#define DATA_BITS_7                     7
#define DATA_BITS_8                     8
#define DATA_BITS_9                     9

停止位可取值:
#define STOP_BITS_1                     0
#define STOP_BITS_2                     1
#define STOP_BITS_3                     2
#define STOP_BITS_4                     3

極性位可取值:
#define PARITY_NONE                     0
#define PARITY_ODD                      1
#define PARITY_EVEN                     2

高低位順序可取值:
#define BIT_ORDER_LSB                   0
#define BIT_ORDER_MSB                   1

模式可取值:
#define NRZ_NORMAL                      0     
#define NRZ_INVERTED                    1 
    
接收數(shù)據(jù)緩沖區(qū)默認(rèn)大?。?#define RT_SERIAL_RB_BUFSZ              64

返回 	——
RT_EOK 	函數(shù)執(zhí)行成功
-RT_ENOSYS 	執(zhí)行失敗,dev 為空
其他錯(cuò)誤碼 	執(zhí)行失敗
 */
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)

我們已經(jīng)知道,在串口初始化的時(shí)候會(huì)有一個(gè)默認(rèn)配置:

poYBAGK_zL2AVRCOAABcGXgNRJw568.png

所以在我們使用串口的時(shí)候,如果對(duì)應(yīng)的配置與默認(rèn)的配置不一樣,就需要使用此函數(shù)修改配置。

接收緩沖區(qū):

當(dāng)串口使用中斷接收模式打開時(shí),串口驅(qū)動(dòng)框架會(huì)根據(jù) RT_SERIAL_RB_BUFSZ 大小開辟一塊緩沖區(qū)用于保存接收到的數(shù)據(jù),底層驅(qū)動(dòng)接收到一個(gè)數(shù)據(jù),都會(huì)在中斷服務(wù)程序里面將數(shù)據(jù)放入緩沖區(qū)。

在修改緩沖區(qū)大小時(shí)請(qǐng)注意,緩沖區(qū)大小無(wú)法動(dòng)態(tài)改變,只有在 open 設(shè)備之前可以配置。open 設(shè)備之后,緩沖區(qū)大小不可再進(jìn)行更改。但除緩沖區(qū)之外的其他參數(shù),在 open 設(shè)備前 / 后,均可進(jìn)行更改。

串口控制修改使用官方修改示例說明一下:

#define SAMPLE_UART_NAME       "uart2"    /* 串口設(shè)備名稱 */
static rt_device_t serial;                /* 串口設(shè)備句柄 */
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;  /* 初始化配置參數(shù) */

/* step1:查找串口設(shè)備 */
serial = rt_device_find(SAMPLE_UART_NAME);

/* step2:修改串口配置參數(shù) */
config.baud_rate = BAUD_RATE_9600;        //修改波特率為 9600
config.data_bits = DATA_BITS_8;           //數(shù)據(jù)位 8
config.stop_bits = STOP_BITS_1;           //停止位 1
config.bufsz     = 128;                   //修改緩沖區(qū) buff size 為 128
config.parity    = PARITY_NONE;           //無(wú)奇偶校驗(yàn)位

/* step3:控制串口設(shè)備。通過控制接口傳入命令控制字,與控制參數(shù) */
rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);

/* step4:打開串口設(shè)備。以中斷接收及輪詢發(fā)送模式打開串口設(shè)備 */
rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);

1.2.4 發(fā)送數(shù)據(jù)

/**
參數(shù) 	描述
dev 	設(shè)備句柄
pos 	寫入數(shù)據(jù)偏移量,此參數(shù)串口設(shè)備未使用
buffer 	內(nèi)存緩沖區(qū)指針,放置要寫入的數(shù)據(jù)
size 	寫入數(shù)據(jù)的大小
返回 	——
寫入數(shù)據(jù)的實(shí)際大小 	如果是字符設(shè)備,返回大小以字節(jié)為單位;
0 	需要讀取當(dāng)前線程的 errno 來(lái)判斷錯(cuò)誤狀態(tài)
 */
rt_size_t rt_device_write(rt_device_t dev,
                          rt_off_t    pos,
                          const void *buffer,
                          rt_size_t   size)
                          

寫其實(shí)很好理解,除了多一個(gè)設(shè)備句柄參數(shù),和我們裸機(jī)中使用的發(fā)送函數(shù)一樣,看一下一個(gè)普通的裸機(jī)串口發(fā)送函數(shù):

在這里插入圖片描述

這里說明一下,因?yàn)槲覀兩厦娣治鲞^實(shí)際應(yīng)用中的串口讀寫,一般都使用輪詢發(fā)送,所以我這里并不打斷介紹 設(shè)置發(fā)送完成回調(diào)函數(shù) 。

1.2.5 設(shè)置接收回調(diào)函數(shù)

/**
參數(shù) 	描述
dev 	設(shè)備句柄
rx_ind 	回調(diào)函數(shù)指針

回調(diào)函數(shù)參數(shù) 描述
dev 		設(shè)備句柄
size 		緩沖區(qū)數(shù)據(jù)大小
返回 	——
RT_EOK 	設(shè)置成功
 */
rt_err_t
rt_device_set_rx_indicate(rt_device_t dev,
                          rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size))

若串口以中斷接收模式打開:
當(dāng)串口接收到一個(gè)數(shù)據(jù)產(chǎn)生中斷時(shí),就會(huì)調(diào)用回調(diào)函數(shù),并且會(huì)把此時(shí)緩沖區(qū)的數(shù)據(jù)大小放在 size 參數(shù)里,把串口設(shè)備句柄放在 dev 參數(shù)里供調(diào)用者獲取。

若串口以 DMA 接收模式打開:
當(dāng) DMA 完成一批數(shù)據(jù)的接收后會(huì)調(diào)用此回調(diào)函數(shù)。

在使用 RT-Thread 時(shí)候,一般會(huì)用一個(gè)信號(hào)量通知串口數(shù)據(jù)處理線程有數(shù)據(jù)到達(dá)。

在使用 RT-Thread Nano 的時(shí)候,其實(shí)我也是使用信號(hào)量來(lái)處理數(shù)據(jù)的接收:

poYBAGK_zL2AfCgPAACDwyGuchk435.png

具體詳情可查看博文:RT-Thread 應(yīng)用篇 — 在STM32L051上使用 RT-Thread (四、無(wú)線溫濕度傳感器 之 串口通訊)

回調(diào)函數(shù)處理的示例我們使用官方示例說明,與下面的接收數(shù)據(jù)函數(shù)一起展示。

1.2.6 接收數(shù)據(jù)

數(shù)據(jù)接收處理函數(shù),在接收回調(diào)函數(shù)運(yùn)行之后運(yùn)行。

/**
參數(shù) 	描述
dev 	設(shè)備句柄
pos 	讀取數(shù)據(jù)偏移量,此參數(shù)串口設(shè)備未使用
buffer 	緩沖區(qū)指針,讀取的數(shù)據(jù)將會(huì)被保存在緩沖區(qū)中
size 	讀取數(shù)據(jù)的大小
返回 	——
讀到數(shù)據(jù)的實(shí)際大小 	如果是字符設(shè)備,返回大小以字節(jié)為單位
0 					需要讀取當(dāng)前線程的 errno 來(lái)判斷錯(cuò)誤狀態(tài)
 */
rt_size_t rt_device_read(rt_device_t dev,
                         rt_off_t    pos,
                         void       *buffer,
                         rt_size_t   size)

我們與上面的設(shè)置接收回調(diào)函數(shù)一起使用官方示例作為說明:

#define SAMPLE_UART_NAME       "uart2"    /* 串口設(shè)備名稱 */
static rt_device_t serial;                /* 串口設(shè)備句柄 */
static struct rt_semaphore rx_sem;    /* 用于接收消息的信號(hào)量 */

/* 接收數(shù)據(jù)回調(diào)函數(shù) */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    /* 串口接收到數(shù)據(jù)后產(chǎn)生中斷,調(diào)用此回調(diào)函數(shù),然后發(fā)送接收信號(hào)量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

/* 接收數(shù)據(jù)的線程 */
static void serial_thread_entry(void *parameter)
{
    char ch;

    while (1)
    {
        /* 從串口讀取一個(gè)字節(jié)的數(shù)據(jù),沒有讀取到則等待接收信號(hào)量 */
        while (rt_device_read(serial, -1, &ch, 1) != 1)
        {
            /* 阻塞等待接收信號(hào)量,等到信號(hào)量后再次讀取數(shù)據(jù) */
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }
        /* 讀取到的數(shù)據(jù)通過串口錯(cuò)位輸出 */
        ch = ch + 1;
        rt_device_write(serial, 0, &ch, 1);
    }
}

static int uart_sample(int argc, char *argv[])
{
    serial = rt_device_find(SAMPLE_UART_NAME);

    /* 以中斷接收及輪詢發(fā)送模式打開串口設(shè)備 */
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);

    /* 初始化信號(hào)量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);

    /* 設(shè)置接收回調(diào)函數(shù) */
    rt_device_set_rx_indicate(serial, uart_input);
}

示例中使用的是信號(hào)量,收到一個(gè)數(shù)據(jù),便會(huì)喚醒接收數(shù)據(jù)的線程,所以其實(shí)是一個(gè)字節(jié)一個(gè)字節(jié)(一個(gè)字符等于一個(gè)字節(jié))的讀取, 示例處理方式只能使用 RT_DEVICE_FLAG_INT_RX 方式接收。

?? 接收數(shù)據(jù)rt_device_read 函數(shù)的返回值需要注意一下,返回值為讀到的數(shù)據(jù)實(shí)際大小,就是接收到的數(shù)據(jù)長(zhǎng)度。

二、UART 設(shè)備使用步驟

簡(jiǎn)單介紹一下在 RT-Thread Studio 開發(fā)環(huán)境下 UART的使用步驟。

2.1 RT-Thread setting

如果需要使用某個(gè)設(shè)備,是需要在 ENV 工具中配置的,現(xiàn)在有了 RT-Thread Studio ,所以可以直接通過工程目錄下的 RT-Thread setting 進(jìn)行圖形化界面的配置,如下圖:

pYYBAGK_zL2AOO30AAGiQw6GNs0322.png

因?yàn)镾hell 工具需要使用串口,所以默認(rèn)串口這里已經(jīng)是勾選中的,這里說明只是為了讓大家知道,在以后的 I/O 設(shè)備使用的時(shí)候,第一步就是在 RT-Thread setting 中使能設(shè)備。

2.2 board.h 設(shè)置

完成設(shè)備使能,我們還需要使用宏定義進(jìn)行串口的基本設(shè)置,該設(shè)置在board.h文件中進(jìn)行,如下圖:

poYBAGK_zL6AXbZVAAGLwOlBCiI124.png

board.h 中包括了很多外設(shè)的使用說明,除了 UART,還有I2C、SPI、ADC等設(shè)備,我們?cè)诤竺鎸W(xué)習(xí)這些設(shè)備使用的時(shí)候,需要經(jīng)常用到這個(gè)頭文件,一些基本的使能配置都是在這個(gè)文件中用宏定義使能。

2.3 應(yīng)用程序流程

完成上面 2 步的基本配置以后,我們就可以在應(yīng)用程序通過上文介紹的 UART 設(shè)備操作函數(shù)進(jìn)行串口的使用,具體的步驟概括如下:

UART 設(shè)備使用步驟 :
/
#include "rtdevice.h"
/
1、使用rt_device_find查找串口設(shè)備;
/
2、根據(jù)需求使用rt_device_control設(shè)置串口;
/
3、初始化回調(diào)函數(shù)中使用的信號(hào)量(在接收回調(diào)函數(shù)中 發(fā)送信號(hào)量 喚醒數(shù)據(jù)處理線程),如果使用消息隊(duì)列接收初始化消息隊(duì)列;
/
4、使用rt_device_open打開串口設(shè)備(根據(jù)自己的情況判斷使用什么方式接收,發(fā)送前面分析過了,一本應(yīng)用使用輪詢發(fā)送即可);
/
5、使用rt_device_set_rx_indicate設(shè)置串口設(shè)備的接收回調(diào)函數(shù)
/
6、創(chuàng)建數(shù)據(jù)讀取的線程。

按照上面的步驟,我進(jìn)行了如下的示例測(cè)試,不要忘記 #include "rtdevice.h"

pYYBAGK_zL6AT6liAAGDq_PkBNc310.png

上圖其實(shí)是根據(jù)官方示例代碼,使用的 ESP8266 WIFI 模塊做了一個(gè)簡(jiǎn)單的測(cè)試:

poYBAGK_zL-AcCXrAAShKhg1Xhs811.png

三、UART 示例測(cè)試

在上面介紹應(yīng)用程序流程的時(shí)候,其實(shí)已經(jīng)做了一個(gè)簡(jiǎn)單的示例測(cè)試。

同時(shí)在官方已經(jīng)也提供了3種典型的示例程序:

中斷接收及輪詢發(fā)送、DMA 接收及輪詢發(fā)送、串口接收不定長(zhǎng)數(shù)據(jù)

作為以應(yīng)用為目的系列博文,我自己還是根據(jù)自己的工作需求進(jìn)行串口通訊的測(cè)試,使用的是 Enocean 無(wú)線通訊模塊,當(dāng)時(shí)在 RT-Thread 的應(yīng)用篇,RT-Thread Nano 使用記錄的時(shí)候就使用的這個(gè)無(wú)線模塊。

要說明的是,用什么模塊做通訊并不是重點(diǎn),重點(diǎn)在于使用過程中對(duì)串口數(shù)據(jù)的處理方式。

3.1 與無(wú)線模塊串口通訊

雖然換了一個(gè)通訊設(shè)備,但是官方給的例程:中斷接收及輪詢發(fā)送 還是適用的,我們先來(lái)看一看直接使用官方的例程做的測(cè)試:

/* 接收數(shù)據(jù)回調(diào)函數(shù) */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    /* 串口接收到數(shù)據(jù)后產(chǎn)生中斷,調(diào)用此回調(diào)函數(shù),然后發(fā)送接收信號(hào)量 */
    rt_sem_release(&rx_sem);
    return RT_EOK;
}
static void test_thread_entry(void *par){
    uint8_t ch;
    while (1)
    {
        /* 從串口讀取一個(gè)字節(jié)的數(shù)據(jù),沒有讀取到則等待接收信號(hào)量 */
        while (rt_device_read(testuart, -1, &ch, 1) != 1)
        {
            /* 阻塞等待接收信號(hào)量,等到信號(hào)量后再次讀取數(shù)據(jù) */
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }
        rt_kprintf("%x ",ch);
    }
}

其測(cè)試結(jié)果如下:

pYYBAGK_zL-ATCMPAADTmylu_Ro038.png

為了更好的做數(shù)據(jù)解析,我們需要對(duì)原始的程序進(jìn)行修改,使得能夠針對(duì)一幀數(shù)據(jù)一幀數(shù)據(jù)進(jìn)行接收處理:

uint8_t USART_Enocean_BUF[64];
uint8_t Enocean_Data = 0;

/* 接收數(shù)據(jù)回調(diào)函數(shù) */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{

    Enocean_Data = size;
    /* 串口接收到數(shù)據(jù)后產(chǎn)生中斷,調(diào)用此回調(diào)函數(shù),然后發(fā)送接收信號(hào)量 */
    rt_sem_release(&rx_sem);
    return RT_EOK;
}
static void test_thread_entry(void *par){
    uint8_t i = 0;
    while (1)
    {
        if(rt_sem_take(&rx_sem, RT_WAITING_FOREVER) == RT_EOK){
              while(!rt_sem_take(&rx_sem, 7));
              rt_device_read(testuart, -1, USART_Enocean_BUF, Enocean_Data);

        }

        for (i = 0; i < Enocean_Data; ++i) {
            rt_kprintf("%x ",USART_Enocean_BUF[i]);
        }
        rt_kprintf("\r\n");

        rt_memset(&USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF));
        Enocean_Data = 0;
    }
}

測(cè)試結(jié)果如下,實(shí)現(xiàn)了我們所需要的的針對(duì)每一幀數(shù)據(jù)的接收(既然都已經(jīng)可以區(qū)別每一幀數(shù)據(jù)了,所那么后續(xù)的處理也就簡(jiǎn)單了):

poYBAGK_zL-AVwbpAAB6DlwoloM296.png

對(duì)于數(shù)據(jù)的接收處理信號(hào)量只是其中一種。
即便是信號(hào)量,也有多種可實(shí)現(xiàn)行的方式,而上面我測(cè)試使用的方式也只不過其中的一種:收到第一個(gè)數(shù)據(jù)的時(shí)候等待一定的時(shí)間,然后認(rèn)為是一幀數(shù)據(jù)接收完成。
這也只是判斷一幀數(shù)據(jù)接收完成的方法中的一種 = =!

3.2 示例說明

在上面我們用了信號(hào)量作為通知的方式接收串口數(shù)據(jù),官方的示: DMA 接收及輪詢發(fā)送 采用了消息隊(duì)列的方式進(jìn)行處理,表面上看起來(lái)與我們上面那種方式不一樣。

其實(shí)本質(zhì)都是一樣的,都不過是給線程一個(gè)通知,并沒有“真正意義上的傳遞了消息”(比如串口接收到的數(shù)據(jù)):

pYYBAGK_zMCAZ5zKAAEJr-YrcQ8317.png

如果想要使用消息隊(duì)列作為緩存正常的傳輸串口接收的數(shù)據(jù),不使用 I/O 設(shè)備模型的情況下更加適合,究其原因,如下圖分析:

在這里插入圖片描述

如上面表格所說,使用了I/O 設(shè)備模型之后,我們底層串口初始化的時(shí)候已經(jīng)有了一段數(shù)據(jù)接收的buffer了,所以我們直接使用 rt_device_read 函數(shù)從驅(qū)動(dòng)層的 buffer 讀取數(shù)據(jù),用臨時(shí) buffer 來(lái)處理就可以了(不過如果需要對(duì)處理程序,單獨(dú)設(shè)計(jì)函數(shù),也可以用一個(gè)全局 buffer 來(lái)處理),也不過是2個(gè)buffer 的內(nèi)存占用。

所以在官方的示例中,雖然給的是信號(hào)量,和消息隊(duì)列的不同的處理方式,但是究其根本還是一樣的。只是給了一個(gè)通知,這個(gè)其他的 IPC機(jī)制 比如 事件集一樣可以做到,即便不用 IPC 機(jī)制,普通簡(jiǎn)單的應(yīng)用,全局變量也未嘗不可。(對(duì)于消息隊(duì)列傳遞串口接收數(shù)據(jù)的應(yīng)用,以后我還是會(huì)單獨(dú)的說明的,本文在于說明 UART 基于 I/O 設(shè)備模型的使用,所以就不做測(cè)試了 = =?。?/p>

?? 使用了 UART 設(shè)備模型,最終還是需要使用rt_device_read函數(shù),從內(nèi)部緩存讀取串口數(shù)據(jù),IPC只不過是給線程一個(gè)通知。

結(jié)語(yǔ)

一個(gè) UART 設(shè)備畫了兩篇文章,還算是比較值得的,通過上一篇文章加深對(duì) RT-Thread I/O 設(shè)備模型的理解,通過本文實(shí)際體驗(yàn)了一把 UART 設(shè)備。

體驗(yàn)上來(lái)說,還是感覺特別方便簡(jiǎn)單的。但是這個(gè)前提條件時(shí),能夠真正的理解 RT-Thread I/O 設(shè)備模型,理解到位才能用起來(lái)游刃有余,也能夠在以后出問題的時(shí)候更容易的發(fā)現(xiàn)問題,解決問題。


?? 學(xué)會(huì)了使用一個(gè)東西當(dāng)然是一件慶幸的事情,但是能夠理解它才是更加重要的事情! ??

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

    關(guān)注

    22

    文章

    1199

    瀏覽量

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

    關(guān)注

    31

    文章

    1239

    瀏覽量

    39442
收藏 人收藏

    評(píng)論

    相關(guān)推薦

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

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

    RT-Thread記錄(十一、UART設(shè)備—源碼解析)

    一文帶你深入理解 RT-Thread I/O 設(shè)備模型 — UART 設(shè)備源碼分析。
    的頭像 發(fā)表于 07-01 11:24 ?5278次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(十一、<b class='flag-5'>UART</b><b class='flag-5'>設(shè)備</b>—源碼解析)

    RT-Thread記錄(十四、I/O 設(shè)備模型之ADC設(shè)備

    我曾經(jīng)考慮過把 RT-Thread 常用的設(shè)備都寫完,其實(shí)通過前面的《全面認(rèn)識(shí) RT-Thread I/O 設(shè)備模型》文章學(xué)習(xí),以及 UART
    的頭像 發(fā)表于 07-04 12:28 ?4175次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(十四、I/O <b class='flag-5'>設(shè)備</b>模型之ADC<b class='flag-5'>設(shè)備</b>)

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

    在前面我們RT-Thread Studio工程基礎(chǔ)之上講一講RT-Thread內(nèi)核啟動(dòng)流程.
    的頭像 發(fā)表于 06-20 00:30 ?4788次閱讀
    <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記錄(十三、I/O 設(shè)備模型之PIN設(shè)備

    講完UART設(shè)備之后,我們已經(jīng)熟悉RT-Thread I/O 設(shè)備模型了,回頭看看基本的 PIN 設(shè)備
    的頭像 發(fā)表于 07-03 11:28 ?4338次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(十三、I/O <b class='flag-5'>設(shè)備</b>模型之PIN<b class='flag-5'>設(shè)備</b>)

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

    RT-Thread記錄十二UART設(shè)備—使用測(cè)試R
    發(fā)表于 07-26 14:56

    RT-Thread全球技術(shù)大會(huì):RT-Thread上的單元測(cè)試框架與運(yùn)行測(cè)試用例

    RT-Thread全球技術(shù)大會(huì):RT-Thread上的單元測(cè)試框架與運(yùn)行測(cè)試用例 ? ? ? ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 16:21 ?1508次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):<b class='flag-5'>RT-Thread</b>上的單元<b class='flag-5'>測(cè)試</b>框架與運(yùn)行<b class='flag-5'>測(cè)試</b>用例

    RT-Thread全球技術(shù)大會(huì):在RT-Thread上編寫測(cè)試用例

    RT-Thread全球技術(shù)大會(huì):在RT-Thread上編寫測(cè)試用例 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 16:28 ?1367次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):在<b class='flag-5'>RT-Thread</b>上編寫<b class='flag-5'>測(cè)試</b>用例

    RT-Thread全球技術(shù)大會(huì):RT-Thread測(cè)試用例集合案例

    RT-Thread全球技術(shù)大會(huì):RT-Thread測(cè)試用例集合案例 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 16:34 ?1964次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):<b class='flag-5'>RT-Thread</b><b class='flag-5'>測(cè)試</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文檔_UART 設(shè)備

    RT-Thread文檔_UART 設(shè)備
    發(fā)表于 02-22 18:32 ?2次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>UART</b> <b class='flag-5'>設(shè)備</b>

    RT-Thread文檔_UART 設(shè)備 v2版本

    RT-Thread文檔_UART 設(shè)備 v2 版本
    發(fā)表于 02-22 18:32 ?0次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>UART</b> <b class='flag-5'>設(shè)備</b> v2版本

    RT-Thread文檔_Pulse Encoder 設(shè)備

    RT-Thread文檔_Pulse Encoder 設(shè)備
    發(fā)表于 02-22 18:39 ?1次下載
    <b class='flag-5'>RT-Thread</b>文檔_Pulse Encoder <b class='flag-5'>設(shè)備</b>

    RT-Thread文檔_utest 測(cè)試框架

    RT-Thread文檔_utest 測(cè)試框架
    發(fā)表于 02-22 18:43 ?2次下載
    <b class='flag-5'>RT-Thread</b>文檔_utest <b class='flag-5'>測(cè)試</b>框架