全球最实用的IT互联网信息网站!

AI人工智能P2P分享&下载搜索网页发布信息网站地图

当前位置:诺佳网 > 电子/半导体 > 嵌入式技术 >

RT-Thread SPARK CAN的通信内核详解

时间:2023-09-20 11:45

人气:

作者:admin

标签:   SPARK  CAN  通信  RT-Thread   

导读:其实挺一目了然的,就是调用device的device_write接口...

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的环形缓存区中。

温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信