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

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

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

RT-Thread SPARK CAN的通信內(nèi)核詳解

冬至子 ? 來源:BURRIEROW ? 作者:BURRIEROW ? 2023-09-20 11:45 ? 次閱讀

CAN發(fā)送

1.rt_device_write
rt_size_t rt_device_write(rt_device_t dev,
rt_off_t pos,
const void buffer,
rt_size_t size)
{
/
parameter check /
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
if (dev->ref_count == 0)
{
rt_set_errno(-RT_ERROR);
return 0;
}
/
call device_write interface /
if (device_write != RT_NULL)
{
// rt_kprintf("device_write:%dn",device_write(dev, pos, buffer, size));
return device_write(dev, pos, buffer, size);
}
/
set error code */
rt_set_errno(-RT_ENOSYS);
return 0;
}

其實挺一目了然的,就是調(diào)用device的device_write接口

2.device_write
#define device_write (dev->write)
dev->write = rt_pipe_write;
還挺能藏

3.rt_pipe_write
static rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buffer, rt_size_t count)
{
uint8_t *pbuf;
rt_size_t write_bytes = 0;
rt_pipe_t *pipe = (rt_pipe_t *)device;
if (device == RT_NULL)
{
rt_set_errno(EINVAL);
return 0;
}
if (count == 0) return 0;
pbuf = (uint8_t *)buffer;
rt_mutex_take(&pipe->lock, -1);
while (write_bytes < count)
{
int len = rt_ringbuffer_put(pipe->fifo, &pbuf[write_bytes], count - write_bytes);
if (len <= 0) break;
write_bytes += len;
}
rt_mutex_release(&pipe->lock);
return write_bytes;
}

函數(shù)接受四個參數(shù) device、pos、buffer 和 count,分別表示設(shè)備指針、寫入位置(未使用)、數(shù)據(jù)緩沖區(qū)指針和要寫入的數(shù)據(jù)字節(jié)數(shù)。

將數(shù)據(jù)緩沖區(qū)指針 buffer 強制轉(zhuǎn)換為 uint8_t 類型的指針,方便按字節(jié)操作數(shù)據(jù)。

使用互斥鎖 pipe->lock 來保護管道操作的原子性,通過調(diào)用 rt_mutex_take 函數(shù)獲取互斥鎖。

進入循環(huán),不斷寫入數(shù)據(jù)到管道,直到寫入的字節(jié)數(shù)達到指定的 count。

調(diào)用 rt_ringbuffer_put 函數(shù)將數(shù)據(jù)寫入管道的環(huán)形緩沖區(qū)(pipe->fifo)。該函數(shù)返回實際寫入的字節(jié)數(shù) len。

如果 len 小于等于 0,說明無法繼續(xù)寫入數(shù)據(jù)到管道,跳出循環(huán)。

釋放互斥鎖,通過調(diào)用 rt_mutex_release 函數(shù)釋放互斥鎖。

返回已寫入的字節(jié)數(shù) write_bytes。

4.rt_ringbuffer_put

rt_ringbuffer是啥?

/* ring buffer */
struct rt_ringbuffer
{
rt_uint8_t buffer_ptr;
/
use the msb of the {read,write}_index as mirror bit. You can see this as

  • if the buffer adds a virtual mirror and the pointers point either to the
  • normal or to the mirrored buffer. If the write_index has the same value
  • with the read_index, but in a different mirror, the buffer is full.
  • While if the write_index and the read_index are the same and within the
  • same mirror, the buffer is empty. The ASCII art of the ringbuffer is:
  • mirror = 0                    mirror = 1
    
  • +---+---+---+---+---+---+---+|+ + + + + + +~~~+
  • | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Full
  • +---+---+---+---+---+---+---+|+ + + + + + +~~~+
  • read_idx-^ write_idx-^
  • +---+---+---+---+---+---+---+|+ + + + + + +~~~+
  • | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Empty
  • +---+---+---+---+---+---+---+|+ + + + + + +~~~+
  • read_idx-^ ^-write_idx
  • The tradeoff is we could only use 32KiB of buffer for 16 bit of index.
  • But it should be enough for most of the cases.
  • Ref: http://en.wikipedia.org/wiki/Circular_buffer#Mirroring /
    rt_uint16_t read_mirror : 1;
    rt_uint16_t read_index : 15;
    rt_uint16_t write_mirror : 1;
    rt_uint16_t write_index : 15;
    /
    as we use msb of index as mirror bit, the size should be signed and
  • could only be positive. */
    rt_int16_t buffer_size;
    };
    enum rt_ringbuffer_state
    {
    RT_RINGBUFFER_EMPTY, //環(huán)形緩沖區(qū)空
    RT_RINGBUFFER_FULL, //環(huán)形緩沖區(qū)滿
    RT_RINGBUFFER_HALFFULL, //環(huán)形緩沖區(qū)半滿
    };
    定義
    環(huán)形緩沖區(qū)是嵌入式系統(tǒng)中十分重要的一種數(shù)據(jù)結(jié)構(gòu),比如在串口處理中,串口中斷接收數(shù)據(jù)直接往環(huán)形緩沖區(qū)丟數(shù)據(jù),而應(yīng)用可以從環(huán)形緩沖區(qū)取數(shù)據(jù)進行處理,這樣數(shù)據(jù)在讀取和寫入的時候都可以在這個緩沖區(qū)里循環(huán)進行,程序員可以根據(jù)自己需要的數(shù)據(jù)大小來決定自己使用的緩沖區(qū)大小。

特點

使用讀取索引和寫入索引的高位作為鏡像位,通過鏡像位的不同來表示緩沖區(qū)的狀態(tài)。

當(dāng)讀取索引和寫入索引的值相同時,但鏡像位不同時,表示緩沖區(qū)已滿。read_index==write_index&&

當(dāng)讀取索引和寫入索引的值相同時,并且鏡像位也相同時,表示緩沖區(qū)為空。

使用環(huán)形緩沖區(qū)的好處是當(dāng)一個元素被消費時,不需要移動其他元素,因為新的元素可以直接寫入到最后一個位置上,實現(xiàn)了一種先進先出(FIFO)的數(shù)據(jù)結(jié)構(gòu)。這在很多應(yīng)用中非常有用。

回到實際代碼

rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb,
const rt_uint8_t ptr,
rt_uint16_t length)
{
rt_uint16_t size;
RT_ASSERT(rb != RT_NULL);
/
whether has enough space /
size = rt_ringbuffer_space_len(rb);
/
no space /
if (size == 0)
return 0;
/
drop some data /
if (size < length)
length = size;
if (rb->buffer_size - rb->write_index > length)
{
/
read_index - write_index = empty space /
rt_memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
/
this should not cause overflow because there is enough space for

length of data in current mirror /
rb->write_index += length;
return length;
}
rt_memcpy(&rb->buffer_ptr[rb->write_index],
&ptr[0],
rb->buffer_size - rb->write_index);
rt_memcpy(&rb->buffer_ptr[0],
&ptr[rb->buffer_size - rb->write_index],
length - (rb->buffer_size - rb->write_index));
/
we are going into the other side of the mirror */
rb->write_mirror = ~rb->write_mirror;
rb->write_index = length - (rb->buffer_size - rb->write_index);
return length;
}

該函數(shù)的目的是向環(huán)形緩沖區(qū)中寫入數(shù)據(jù),根據(jù)當(dāng)前寫指針的位置和可用空間的大小,判斷數(shù)據(jù)是否可以連續(xù)寫入當(dāng)前鏡像,如果不能,則跨越鏡像邊界繼續(xù)寫入。函數(shù)返回實際寫入的數(shù)據(jù)長度

5.存放數(shù)據(jù)的管道結(jié)構(gòu)體
/**

Pipe Device
/
struct rt_pipe_device
{
struct rt_device parent;
rt_bool_t is_named;
/
ring buffer in pipe device */
struct rt_ringbuffer *fifo;
rt_uint16_t bufsz;
rt_uint8_t readers;
rt_uint8_t writers;
rt_wqueue_t reader_queue;
rt_wqueue_t writer_queue;
struct rt_mutex lock;
};
typedef struct rt_pipe_device rt_pipe_t;
rt_pipe_write函數(shù)把我們的device指針強轉(zhuǎn)為管道device

在它的fifo數(shù)據(jù)緩沖區(qū)中存儲數(shù)據(jù)

CAN接收

前面和上面差不多,直接跳到pipe層

rt_pipe_read
static rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt_size_t count)
{
uint8_t *pbuf;
rt_size_t read_bytes = 0;
rt_pipe_t *pipe = (rt_pipe_t *)device;
if (device == RT_NULL)
{
rt_set_errno(EINVAL);
return 0;
}
if (count == 0) return 0;
pbuf = (uint8_t *)buffer;
rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);
while (read_bytes < count)
{
int len = rt_ringbuffer_get(pipe->fifo, &pbuf[read_bytes], count - read_bytes);
if (len <= 0) break;
read_bytes += len;
}
rt_mutex_release(&pipe->lock);
return read_bytes;
}

調(diào)用 rt_ringbuffer_get 函數(shù)從管道的環(huán)形緩沖區(qū)中讀取數(shù)據(jù)到緩沖區(qū)中,返回實際讀取的字節(jié)數(shù)。將讀取的數(shù)據(jù)追加到 pbuf 中,并更新已讀取字節(jié)數(shù)。

rt_ringbuffer_get
/**

@brief Get data from the ring buffer.

@param rb A pointer to the ring buffer.
@param ptr A pointer to the data buffer.
@param length The size of the data we want to read from the ring buffer.

@return Return the data size we read from the ring buffer.
*/
rt_size_t rt_ringbuffer_get(struct rt_ringbuffer *rb,
rt_uint8_t ptr,
rt_uint16_t length)
{
rt_size_t size;
RT_ASSERT(rb != RT_NULL);
/
whether has enough data /
size = rt_ringbuffer_data_len(rb);
/
no data /
if (size == 0)
return 0;
/
less data /
if (size < length)
length = size;
if (rb->buffer_size - rb->read_index > length)
{
/
copy all of data /
rt_memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
/
this should not cause overflow because there is enough space for
length of data in current mirror /
rb->read_index += length;
return length;
}
rt_memcpy(&ptr[0],
&rb->buffer_ptr[rb->read_index],
rb->buffer_size - rb->read_index);
rt_memcpy(&ptr[rb->buffer_size - rb->read_index],
&rb->buffer_ptr[0],
length - (rb->buffer_size - rb->read_index));
/
we are going into the other side of the mirror */
rb->read_mirror = ~rb->read_mirror;
rb->read_index = length - (rb->buffer_size - rb->read_index);
return length;
}

首先通過調(diào)用 rt_ringbuffer_data_len(rb) 函數(shù)獲取當(dāng)前環(huán)形緩沖區(qū)中的數(shù)據(jù)大小。

如果數(shù)據(jù)大小為0,則表示緩沖區(qū)中沒有數(shù)據(jù)可讀,直接返回0。

如果數(shù)據(jù)大小小于要讀取的長度 length,則將要讀取的長度修改為數(shù)據(jù)大小,避免越界訪問。

判斷從當(dāng)前讀取索引位置開始,到緩沖區(qū)末尾的數(shù)據(jù)是否足夠讀取 length 大小的數(shù)據(jù)。如果足夠,則直接將數(shù)據(jù)復(fù)制到指定的數(shù)據(jù)緩沖區(qū) ptr 中,并更新讀取索引。

如果從當(dāng)前讀取索引位置開始,到緩沖區(qū)末尾的數(shù)據(jù)不足以滿足讀取 length 大小的數(shù)據(jù),則先將緩沖區(qū)末尾的數(shù)據(jù)復(fù)制到 ptr 中,然后將剩余長度的數(shù)據(jù)從緩沖區(qū)開頭復(fù)制到 ptr 的剩余空間中。

更新讀取索引,將其設(shè)置為剩余數(shù)據(jù)的起始位置,并且切換讀取索引的鏡像位,以便正確讀取數(shù)據(jù)。

綜上所述

SPARK的can例程看似并不涉及通俗意義的can協(xié)議,只是把一段數(shù)據(jù)層層調(diào)用接口,最終寫入一個can設(shè)備的pipe強轉(zhuǎn)類型的rt_ringbuffer環(huán)形緩沖區(qū)種,然后再由要讀的設(shè)備從那里讀出來。比如說can0想要向can1寫數(shù)據(jù),那么就會直接把數(shù)據(jù)寫在can1的環(huán)形緩存區(qū)中。

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

    關(guān)注

    5

    文章

    93

    瀏覽量

    17755
  • FIFO存儲
    +關(guān)注

    關(guān)注

    0

    文章

    103

    瀏覽量

    5944
  • 串口中斷
    +關(guān)注

    關(guān)注

    0

    文章

    64

    瀏覽量

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

    關(guān)注

    31

    文章

    1239

    瀏覽量

    39429
  • 環(huán)形緩沖
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    1503
收藏 人收藏

    評論

    相關(guān)推薦

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

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

    RT-Thread內(nèi)核對象控制塊詳解

    學(xué)習(xí)RT-Thread,除了基礎(chǔ)的應(yīng)用,應(yīng)該花點時間,研究下底層內(nèi)核的實現(xiàn)方法。
    發(fā)表于 06-02 09:48 ?448次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>內(nèi)核</b>對象控制塊<b class='flag-5'>詳解</b>

    RT-Thread內(nèi)核簡介

    RT-Thread 內(nèi)核簡介前言1、RT-Thread 的架構(gòu)2、RT-Thread內(nèi)核3、RT-T
    發(fā)表于 08-06 07:44

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 理解defunct僵尸線程

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)核對象rt_objectRT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)
    發(fā)表于 01-25 18:19 ?8次下載
    <b class='flag-5'>RT-Thread</b> <b class='flag-5'>內(nèi)核</b>學(xué)習(xí)筆記 - 理解defunct僵尸線程

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 設(shè)備模型rt_device的理解

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)核對象rt_objectRT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)
    發(fā)表于 01-25 18:19 ?8次下載
    <b class='flag-5'>RT-Thread</b> <b class='flag-5'>內(nèi)核</b>學(xué)習(xí)筆記 - 設(shè)備模型<b class='flag-5'>rt</b>_device的理解

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)核對象鏈表結(jié)構(gòu)深入理解

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)核對象rt_objectRT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)
    發(fā)表于 01-25 18:23 ?6次下載
    <b class='flag-5'>RT-Thread</b> <b class='flag-5'>內(nèi)核</b>學(xué)習(xí)筆記 - <b class='flag-5'>內(nèi)核</b>對象鏈表結(jié)構(gòu)深入理解

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)核對象初始化鏈表組織方式

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)核對象rt_objectRT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)
    發(fā)表于 01-25 18:24 ?3次下載
    <b class='flag-5'>RT-Thread</b> <b class='flag-5'>內(nèi)核</b>學(xué)習(xí)筆記 - <b class='flag-5'>內(nèi)核</b>對象初始化鏈表組織方式

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)核對象操作API

    RT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)核對象rt_objectRT-Thread 內(nèi)核學(xué)習(xí)筆記 - 內(nèi)
    發(fā)表于 01-25 18:26 ?7次下載
    <b class='flag-5'>RT-Thread</b> <b class='flag-5'>內(nèi)核</b>學(xué)習(xí)筆記 - <b class='flag-5'>內(nèi)核</b>對象操作API

    大佬帶你理解RT-Thread內(nèi)核并上手實踐

    RT-Thread內(nèi)核的相關(guān)概念和基礎(chǔ)知識,然后了解RT-Thread系統(tǒng)的啟動流程、內(nèi)存分布情況以及內(nèi)核的配置方法。內(nèi)核處于硬件層之上,包
    發(fā)表于 06-30 17:10 ?1158次閱讀

    RT-Thread文檔_內(nèi)核基礎(chǔ)

    RT-Thread文檔_內(nèi)核基礎(chǔ)
    發(fā)表于 02-22 18:28 ?0次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>內(nèi)核</b>基礎(chǔ)

    RT-Thread文檔_線程間通信

    RT-Thread文檔_線程間通信
    發(fā)表于 02-22 18:29 ?0次下載
    <b class='flag-5'>RT-Thread</b>文檔_線程間<b class='flag-5'>通信</b>

    RT-Thread文檔_內(nèi)核移植

    RT-Thread文檔_內(nèi)核移植
    發(fā)表于 02-22 18:31 ?3次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>內(nèi)核</b>移植

    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文檔_CAN 設(shè)備

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

    基于rt-thread的socket通信設(shè)計

    最近再研究 rt-thread通信 ,想設(shè)計出 eps8266(多個) rt-thread(作為中控) 服務(wù)器的通信框架,使用的開發(fā)板是 潘多拉
    的頭像 發(fā)表于 10-13 15:02 ?1059次閱讀
    基于<b class='flag-5'>rt-thread</b>的socket<b class='flag-5'>通信</b>設(shè)計