全部文档
OneOS简介 硬件支持 快速开发指南 编译构造工具 API参考文档 高级语言 用户编程手册 OnePos定位 应用笔记 FAQ

CAN设备


API 列表

接口 说明
os_device_find 查找设备
os_device_open 打开设备
os_device_read 读取数据
os_device_write 写入数据
os_device_control 控制设备
os_device_close 关闭设备

os_device_find

该函数用于查找 CAN 设备,应用程序根据 CAN设备名称通过该函数查找 CAN 设备并获取设备指针,进而可以操作 CAN设备,函数原型如下:

os_device_t *os_device_find(const char *name);
参数 说明
name CAN设备名称
返回 说明
设备指针 查找到CAN设备将返回CAN设备的指针
OS_NULL 没有找到CAN设备

os_device_open

该函数用于开启 CAN 设备,通过CAN设备指针,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口进行初始化,函数原型如下:

os_err_t os_device_open(os_device_t *dev, os_uint16_t oflag);
参数 说明
dev CAN设备名称
oflag 打开设备模式标志
返回 说明
OS_EOK 设备打开成功
OS_EBUSY 如果设备注册时指定的参数中包括 OS_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开
其他错误码 设备打开失败

OneOS当前的 CAN 设备驱动框架支持中断接收和中断发送模式。故oflag 参数支持下列取值 (可以采用或的方式实现多种模式的支持):

#define OS_DEVICE_FLAG_INT_RX           0x100     /**< INT mode on Rx */
#define OS_DEVICE_FLAG_INT_TX           0x400     /**< INT mode on Tx */

os_device_control

该函数用于CAN 设备参数修改,通过命令控制字,应用程序可以对 CAN 设备参数进行配置,函数原型如下:

os_err_t os_device_control(os_device_t *dev, int cmd, void *arg);
参数 说明
dev CAN设备指针
cmd 控制命令
arg 控制参数
返回 说明
OS_EOK 函数执行成功
其他错误码 执行失败

arg(控制参数)根据命令不同而不同,cmd(控制命令)可取以下值:

#define OS_DEVICE_CTRL_RESUME       0x01     /* resume device */
#define OS_DEVICE_CTRL_SUSPEND      0x02     /* suspend device */
#define OS_DEVICE_CTRL_CONFIG       0x03     /* configure device */

#define OS_CAN_CMD_SET_FILTER       0x13     /* 设置硬件过滤表 */
#define OS_CAN_CMD_SET_BAUD         0x14     /* 设置波特率 */
#define OS_CAN_CMD_SET_MODE         0x15     /* 设置 CAN 工作模式 */
#define OS_CAN_CMD_SET_PRIV         0x16     /* 设置发送优先级 */
#define OS_CAN_CMD_GET_STATUS       0x17     /* 获取 CAN 设备状态 */
#define OS_CAN_CMD_SET_STATUS_IND   0x18     /* 设置状态回调函数 */
#define OS_CAN_CMD_SET_BUS_HOOK     0x19     /* 设置 CAN 总线钩子函数 */

#define IOC_SET_CB          (0x12345678)     /* 设置rx或tx回调函数 */

os_device_write

该函数用于发送 CAN 数据,在发送数据前务必正确开启CAN设备,函数原型如下:

os_size_t os_device_write(os_device_t *dev, os_off_t pos, const void *buffer, os_size_t size)
参数 说明
dev CAN设备指针
pos 写入数据偏移量,此参数 CAN 设备默认为0(未使用)
buffer CAN 消息类型的指针
size CAN 消息大小
返回 说明
>0 发送的CAN消息量大小
=0 数据发送失败

其中buffer一般指向CAN消息结构体,其原型如下:

struct os_can_msg
{
    os_uint32_t id  : 29;    /* can_id:为适配扩展长度29位,标准格式时11位 */
    os_uint32_t ide : 1;     /* 标识扩展帧位 */
    os_uint32_t rtr : 1;     /* 标识远程帧位 */
    os_uint32_t rsv : 1;     /* 保留位 */
    os_uint32_t len : 8;     /* 数据段长度 */
    os_uint32_t priv : 8;    /* 报文发送优先级 */
    os_int32_t hdr : 8;      /* 硬件过滤表号 */
    os_uint32_t reserved : 8; /* 保留位 */
    os_uint8_t data[8];       /* can数据 */
};

os_device_read

该函数用于接收 CAN 数据,函数原型如下:

os_size_t os_device_read(os_device_t *dev, os_off_t pos, void *buffer, os_size_t size)
参数 说明
dev CAN设备指针
pos 写入数据偏移量,此参数 CAN 设备默认为0(未使用)
buffer CAN 消息类型的指针
size CAN 消息大小
返回 说明
>0 接收的CAN消息量大小
=0 数据接收失败

os_device_close

该函数用于关闭 CAN 设备,当不再使用CAN设备时,可以关闭 CAN 设备(关闭、打开设备接口需配对使用,以保证设备处于关闭状态),函数原型如下:

os_err_t os_device_close(os_device_t *dev)
参数 说明
dev CAN设备指针
返回 说明
OS_EOK 关闭设备成功
OS_ERROR 设备已经完全关闭,不能重复关闭设备
其他错误码 关闭设备失败

CAN参数配置示例:

参数设置主要通过os_device_control函数实现。各项参数的具体设置如下所示:

/ 确定CAN设备名称,并查找设备是否存在 /

#define CAN_DEV_NAME       "can1"           /* CAN device name */
static os_device_t *can_dev;                /* CAN device pointer */
os_err_t res;
can_dev = os_device_find(CAN_DEV_NAME);     /* Find can devices */

/ 中断方式发送接收打开CAN设备 /

res = os_device_open(can_dev, OS_DEVICE_OFLAG_RDWR);

/ OS_CAN_CMD_SET_BAUD:设置CAN设备的波特率 /

res = os_device_control(can_dev, OS_CAN_CMD_SET_BAUD, (void *)CAN500kBaud);

/ OS_CAN_CMD_SET_MODE:设置CAN的工作模式,本例设置为回环模式 /

res = os_device_control(can_dev, OS_CAN_CMD_SET_MODE, (void *)OS_CAN_MODE_LOOPBACK);

/ OS_CAN_CMD_GET_STATUS:读取CAN设备的状态 /

res = os_device_control(can_dev, OS_CAN_CMD_GET_STATUS, &status);

/ OS_CAN_CMD_SET_FILTER:设置CAN硬件过滤表 /

struct os_can_filter_item item[1] =
{
    OS_CAN_FILTER_ITEM_INIT(0x01, 0, 0, 1, 0x01, OS_NULL, OS_NULL),
};
struct os_can_filter_config cfg = {1, 1, item};
res = os_device_control(can_dev, OS_CAN_CMD_SET_FILTER, &cfg);

/ IOC_SET_CB:设置rx或tx回调函数 /

    struct os_device_cb_info rx_cb_info = 
    {
        .type = OS_DEVICE_CB_TYPE_RX,
        .cb   = can_rx_call,/* 回调函数指针 */
    };

    os_device_control(can_dev, IOC_SET_CB, &rx_cb_info);

    struct os_device_cb_info tx_cb_info = 
    {
        .type = OS_DEVICE_CB_TYPE_TX,
        .cb   = can_tx_done,/* 回调函数指针 */
    };

    os_device_control(can_dev, IOC_SET_CB, &tx_cb_info);

其中CAN的过滤设置主要通过结构体实现:

/* filter结构体 */
struct os_can_filter_item
{
    os_uint32_t id  : 29;   /* CAN message ID */
    os_uint32_t ide : 1;    /* Extended frame flag */
    os_uint32_t rtr : 1;    /* Remote frame flag */
    os_uint32_t mode : 1;   /* can Filter table mode */
    os_uint32_t mask;       /* ID 掩码,0 表示对应的位不关心,1 表示对应的位必须匹配 */
    os_int32_t hdr;         /* -1 表示不指定过滤表号,对应的过滤表控制块也不会被初始化,正数为过滤表号,对应的过滤表控制块会被初始化 */
#ifdef OS_CAN_USING_HDR
    /* 过滤表回调函数 */
    os_err_t (*ind)(os_device_t dev, void *args , os_int32_t hdr, os_size_t size);
    /* 回调函数参数 */
    void *args;
#endif /*OS_CAN_USING_HDR*/
};

/* 为方便初始化过滤表,开发了对应的过滤表宏 */
#define OS_CAN_FILTER_ITEM_INIT(id,ide,rtr,mode,mask,ind,args) \
     {(id), (ide), (rtr), (mode), (mask), -1, (ind), (args)}

/* 过滤表配置前还需指定过滤表配置控制块 */
struct os_can_filter_config
{
    os_uint32_t count;       /* 过滤表数量 */
    os_uint32_t actived;      /* 过滤表激活选项,1 表示初始化过滤表控制块,0 表示不初始化过滤表控制块 */
    struct os_can_filter_item *item;   /* 过滤表指针,可指向一个过滤表数组 */
};

上述有关CAN的设置均可通过os_device_control的返回值判断是否设置成功。

CAN收发数据示例:

#include <os_task.h>
#include <os_device.h>
#include <os_sem.h>
#include <os_errno.h>
#include <os_clock.h>
#include <os_assert.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <shell.h>
#include <os_drivers.h>
#include <can/can.h>

#define CAN_DATA_DUMP

static struct os_semaphore rx_sem;

static os_device_t *can_dev;

static int rx_count = 0;
static int tx_count = 0;

static uint16_t tx_crc = 0;
static uint16_t rx_crc = 0;

static os_err_t can_rx_call(os_device_t *dev, struct os_device_cb_info *info)
{
    os_sem_post(&rx_sem);
    rx_count++;

    return OS_EOK;
}

static void can_rx_task(void *parameter)
{
    struct os_can_msg rxmsg = {0};

    struct os_device_cb_info cb_info = 
    {
        .type = OS_DEVICE_CB_TYPE_RX,
        .cb   = can_rx_call,
    };

    os_device_control(can_dev, IOC_SET_CB, &cb_info);

    while (1)
    {
        rxmsg.hdr = -1;
        os_sem_wait(&rx_sem, OS_IPC_WAITING_FOREVER);
        if (os_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg)) > 0)
        {
            rx_crc = crc16(rx_crc, rxmsg.data, 8);
#ifdef CAN_DATA_DUMP
            os_kprintf("recv(%d) id:%x ", rx_count, rxmsg.id);
            hex_dump(rxmsg.data, 8);
#endif
        }
    }
}

int can_start(int argc, char *argv[])
{
    os_err_t    res;
    os_task_t  *can_task;
    os_uint32_t baud = CAN500kBaud;
    os_uint8_t  mode = OS_CAN_MODE_NORMAL;
    char       *can_name;

    if (argc < 2)
    {
        os_kprintf("usage: can_start <dev> [baud] [mode]\r\n");
        os_kprintf("       can_start can1  [500000] [mode]\r\n");
        os_kprintf("       can_start can1  500000 0(normal)\r\n");
        os_kprintf("       can_start can1  500000 1(listen)\r\n");
        os_kprintf("       can_start can1  500000 2(loopback)\r\n");
        os_kprintf("       can_start can1  500000 3(listen loopback)\r\n");
        return -1;
    }

    can_name = argv[1];

    if (argc > 2)
    {
        baud = strtol(argv[2], OS_NULL, 0);
    }

    if (argc > 3)
    {
        mode = strtol(argv[3], OS_NULL, 0);
    }

    os_kprintf("can:%s, baud:%d.\r\n", can_name, baud);

    can_dev = os_device_find(can_name);
    if (!can_dev)
    {
        os_kprintf("find %s failed!\n", can_name);
        return OS_ERROR;
    }
    os_sem_init(&rx_sem, "rx_sem", 0, OS_IPC_FLAG_FIFO);

    res = os_device_open(can_dev, OS_DEVICE_OFLAG_RDWR);
    OS_ASSERT(res == OS_EOK);

    res = os_device_control(can_dev, OS_CAN_CMD_SET_BAUD, (void *)baud);
    res = os_device_control(can_dev, OS_CAN_CMD_SET_MODE, (void *)mode);


    can_task = os_task_create("can_rx", can_rx_task, OS_NULL, 1024, 25, 10);
    if (can_task != OS_NULL)
    {
        os_task_startup(can_task);
    }
    else
    {
        os_kprintf("create can_rx task failed!\n");
    }
    return res;
}
SH_CMD_EXPORT(can_start, can_start, "can device sample");

static os_err_t can_tx_done(os_device_t *uart, struct os_device_cb_info *info)
{
    //os_kprintf("<%d>tx done\r\n", os_tick_get());
    tx_count++;
    return 0;
}

void can_send(int argc, char *argv[])
{
    struct os_can_msg msg = {0};
    os_size_t size;

    int loops = 1;

    if (argc == 2)
    {
        loops = strtol(argv[1], OS_NULL, 0);
    }

    msg.id      = 0x68;
    msg.ide     = OS_CAN_STDID;
    msg.rtr     = OS_CAN_DTR;
    msg.len     = 8;

    rx_count = 0;
    tx_crc = 0;
    rx_crc = 0;

    tx_count = 0;

    struct os_device_cb_info cb_info = 
    {
        .type = OS_DEVICE_CB_TYPE_TX,
        .cb   = can_tx_done,
    };

    os_device_control(can_dev, IOC_SET_CB, &cb_info);

    while (loops--)
    {
        msg.data[0] = rand();
        msg.data[1] = rand();
        msg.data[2] = rand();
        msg.data[3] = rand();
        msg.data[4] = rand();
        msg.data[5] = rand();
        msg.data[6] = rand();
        msg.data[7] = rand();

        tx_crc = crc16(tx_crc, msg.data, 8);

#ifdef CAN_DATA_DUMP
        os_kprintf("send id:%x ", msg.id);
        hex_dump(msg.data, 8);
#endif
        size = os_device_write(can_dev, 0, &msg, sizeof(msg));
        if (size == 0)
        {
            os_kprintf("can dev write data failed!\n");
        }
    }

    os_task_msleep(3000);
    os_kprintf("rx_count: %d\r\n", rx_count);
    os_kprintf("tx_count: %d\r\n", tx_count);
    os_kprintf("    %s tx_crc:%04x, rx_crc:%04x\r\n", (tx_crc == rx_crc) ? "success" : "failed", tx_crc, rx_crc);
}
SH_CMD_EXPORT(can_send, can_send, "send can data");

例程主要由can_start和send_can_data组成,并将两个函数导入到SH命令行中。其中send_can_data主要发送一组CAN数据,can_start实现的功能如下图所示:

can_start

在串口的命令行中输入"can_start canX"启动CAN设备后,再输入can_send即可发送数据。

results matching ""

    No results matching ""

    返回顶部