CAN發送
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;
}
其實挺一目了然的,就是調用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;
}
函數接受四個參數 device、pos、buffer 和 count,分別表示設備指針、寫入位置(未使用)、數據緩沖區指針和要寫入的數據字節數。
將數據緩沖區指針 buffer 強制轉換為 uint8_t 類型的指針,方便按字節操作數據。
使用互斥鎖 pipe->lock 來保護管道操作的原子性,通過調用 rt_mutex_take 函數獲取互斥鎖。
進入循環,不斷寫入數據到管道,直到寫入的字節數達到指定的 count。
調用 rt_ringbuffer_put 函數將數據寫入管道的環形緩沖區(pipe->fifo)。該函數返回實際寫入的字節數 len。
如果 len 小于等于 0,說明無法繼續寫入數據到管道,跳出循環。
釋放互斥鎖,通過調用 rt_mutex_release 函數釋放互斥鎖。
返回已寫入的字節數 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, //環形緩沖區空
RT_RINGBUFFER_FULL, //環形緩沖區滿
RT_RINGBUFFER_HALFFULL, //環形緩沖區半滿
};
定義
環形緩沖區是嵌入式系統中十分重要的一種數據結構,比如在串口處理中,串口中斷接收數據直接往環形緩沖區丟數據,而應用可以從環形緩沖區取數據進行處理,這樣數據在讀取和寫入的時候都可以在這個緩沖區里循環進行,程序員可以根據自己需要的數據大小來決定自己使用的緩沖區大小。
特點
使用讀取索引和寫入索引的高位作為鏡像位,通過鏡像位的不同來表示緩沖區的狀態。
當讀取索引和寫入索引的值相同時,但鏡像位不同時,表示緩沖區已滿。read_index==write_index&&
當讀取索引和寫入索引的值相同時,并且鏡像位也相同時,表示緩沖區為空。
使用環形緩沖區的好處是當一個元素被消費時,不需要移動其他元素,因為新的元素可以直接寫入到最后一個位置上,實現了一種先進先出(FIFO)的數據結構。這在很多應用中非常有用。
回到實際代碼
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;
}
該函數的目的是向環形緩沖區中寫入數據,根據當前寫指針的位置和可用空間的大小,判斷數據是否可以連續寫入當前鏡像,如果不能,則跨越鏡像邊界繼續寫入。函數返回實際寫入的數據長度
5.存放數據的管道結構體
/**
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函數把我們的device指針強轉為管道device
在它的fifo數據緩沖區中存儲數據
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;
}
調用 rt_ringbuffer_get 函數從管道的環形緩沖區中讀取數據到緩沖區中,返回實際讀取的字節數。將讀取的數據追加到 pbuf 中,并更新已讀取字節數。
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;
}
首先通過調用 rt_ringbuffer_data_len(rb) 函數獲取當前環形緩沖區中的數據大小。
如果數據大小為0,則表示緩沖區中沒有數據可讀,直接返回0。
如果數據大小小于要讀取的長度 length,則將要讀取的長度修改為數據大小,避免越界訪問。
判斷從當前讀取索引位置開始,到緩沖區末尾的數據是否足夠讀取 length 大小的數據。如果足夠,則直接將數據復制到指定的數據緩沖區 ptr 中,并更新讀取索引。
如果從當前讀取索引位置開始,到緩沖區末尾的數據不足以滿足讀取 length 大小的數據,則先將緩沖區末尾的數據復制到 ptr 中,然后將剩余長度的數據從緩沖區開頭復制到 ptr 的剩余空間中。
更新讀取索引,將其設置為剩余數據的起始位置,并且切換讀取索引的鏡像位,以便正確讀取數據。
綜上所述
SPARK的can例程看似并不涉及通俗意義的can協議,只是把一段數據層層調用接口,最終寫入一個can設備的pipe強轉類型的rt_ringbuffer環形緩沖區種,然后再由要讀的設備從那里讀出來。比如說can0想要向can1寫數據,那么就會直接把數據寫在can1的環形緩存區中。
-
CAN通信
+關注
關注
5文章
94瀏覽量
17930 -
FIFO存儲
+關注
關注
0文章
103瀏覽量
6026 -
串口中斷
+關注
關注
0文章
67瀏覽量
13968 -
RT-Thread
+關注
關注
31文章
1305瀏覽量
40310 -
環形緩沖
+關注
關注
0文章
3瀏覽量
1529
發布評論請先 登錄
相關推薦
評論