全部文档
概述 硬件支持 快速开发指南 内核 驱动 通用组件 专业组件 常见问题

CAN设备用户开发


一、基础BSP配置--以STM32为例

建立工程文件

如果在提供的OneOS源码中已经有适配好的DEMO工程,则可以直接使用;如果没有请参照快速上手中的操作指南,新建一个适合的工程。

使用STM32CUBEMX配置硬件

  1. 打开 oneos\projects\xxxxx(project文件夹)\board\CubeMX_Config 下 的 CUBE 工程文件;
  2. 在 CUBE 工程中进行 CAN 配置,如下图所示,红色框选中的地方依次选择CAN、勾选Master Mode,注意检查此时自动选择的CAN引脚线是否与硬件电路一致,然后修改[3]中的Time Quanta,保证软件不提醒冲突,接着在Clock Configuration检查时钟配置是否正确;然后检查引脚配置和中断配置如[5]和[6],需要注意,如果外接了CAN控制器则可以直接使用默认的引脚配置,否则必须配置为pull-up。 cube_clock_generate cube_can_pin cube_can_int
  3. 最后点击GENARATE CODE 生成代码;

使用 Menuconfig 配置工程选项

  1. 在对应的 oneos\projects\xxxxx(project文件夹) 目录下打开OneOS-Cube工具,在命令行输入 menuconfig 打开可视化配置界面;
  2. 通过[↑]、[↓]按键、空格、enter键或向右方向键选择 Drivers->CAN 下的 Using CAN drivers选项,如下所示:
(Top) → Drivers→ CAN
[*] Using CAN drivers

3.Esc键退出menuconfig,注意保存所修改的设置。

使用 Scons 构建工程

在上一步打开的OneOS-Cube 工具命令行中输入 scons --ide=mdk5重新构建keil工程;

二、工程编译及实现

  1. 打开对应的 oneos\projects\xxxxx(project文件夹) 目录下的 project.uvprojx 工程文件,此时即可在工程中添加相应的应用程序。
  2. 在工程中将原有或自己编写的can_test.c文件加入到application子文件夹中;
  3. 编译并下载工程,运行程序;
  4. 打开串口工具如xshell、SecureCRT等,通信成功后,即可使用can_start命令设置CAN的工作模式并开启一个接受任务,使用can_send命令即可通过串口查看到CAN接收任务打印的接收数据;
  5. 通过修改can_test.c中can的工作模式、发送数据值等,查看CAN的接收数据效果。
  6. 如不再使用CAN设备,可再次使用OneOS-Cube工具取消Drivers->CAN 下的Using CAN drivers的选择,保存设置后重新生成工程即可。

三、CAN设备API介绍


API 列表

接口 说明
os_device_find 查找设备
os_device_open 打开设备
os_device_read_block 读取数据(阻塞)
os_device_read_nonblock 读取数据(非阻塞)
os_device_write_block 写入数据(阻塞)
os_device_write_nonblock 写入数据(非阻塞)
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);
参数 说明
dev CAN设备名称
返回 说明
OS_EOK 设备打开成功
其他错误码 设备打开失败

os_device_read_block

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

os_size_t os_device_read_block(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 错误码

如果设备有数据可读,函数立即返回,数据被读取到 buffer 中,返回值为实际读取的数据量。如果设备没有准备好数据,读线程会睡眠阻塞,直到数据可读才被唤醒、返回。

os_device_read_nonblock

该函数用于非阻塞式接收 CAN 数据,函数原型如下:

os_size_t os_device_read_nonblock(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 错误码

如果设备有数据可读,函数立即返回,数据被读取到 buffer 中,返回值为实际读取的数据量。如果设备没有准备好数据,函数立即返回,返回值为0。

os_device_write_block

该函数用于阻塞式发送 CAN 数据,函数原型如下:

os_size_t os_device_write_block(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 中的数据写入设备,函数立即返回,返回值为实际写入的数据量。如果设备没有空间可写,写线程会睡眠阻塞,直到有空间可写才被唤醒,返回实际写入的数据量。

其中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_write_nonblock

该函数用于非阻塞式发送 CAN 数据,函数原型如下:

os_size_t os_device_write_nonblock(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 中的数据写入设备,函数立即返回,返回值为实际写入的数据量。如果设备没有空间可写,函数立即返回,返回值为0。

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 函数执行成功
OS_ENOSYS 执行失败,设备不支持 control 接口
其他错误码 执行失败

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

#define OS_DEVICE_CTRL_CONFIG     IOC_UNKNOWN(7)    /* configure device */#define OS_DEVICE_CTRL_SET_CB     IOC_UNKNOWN(3)    /* 设置rx/tx回调函数 */#define OS_CAN_CMD_SET_FILTER     IOC_CAN(1) /* 设置硬件过滤表 */#define OS_CAN_CMD_SET_BAUD       IOC_CAN(2) /* 设置波特率 */#define OS_CAN_CMD_SET_MODE       IOC_CAN(3) /* 设置 CAN 工作模式 */#define OS_CAN_CMD_SET_PRIV       IOC_CAN(4) /* 设置发送优先级 */#define OS_CAN_CMD_GET_STATUS     IOC_CAN(5) /* 获取 CAN 设备状态 */#define OS_CAN_CMD_SET_STATUS_IND IOC_CAN(6) /* 设置状态回调函数 */#define OS_CAN_CMD_SET_BUS_HOOK   IOC_CAN(7) /* 设置 CAN 总线钩子函数 */

os_device_close

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

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

四、CAN示例介绍

CAN参数配置示例:

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

/ 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);

/ OS_DEVICE_CTRL_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, OS_DEVICE_CTRL_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, OS_DEVICE_CTRL_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 <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 <driver.h>#include <can/can.h>#define CAN_DATA_DUMP#define CAN_TEST_WRITE_NONBLOCK#define CAN_TEST_READ_NONBLOCKstatic 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;OS_USED 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};#ifdef CAN_TEST_READ_NONBLOCK    struct os_device_cb_info cb_info =     {        .type = OS_DEVICE_CB_TYPE_RX,        .cb   = can_rx_call,    };    os_device_control(can_dev, OS_DEVICE_CTRL_SET_CB, &cb_info);#endif        while (1)    {        rxmsg.hdr = -1;#ifdef CAN_TEST_READ_NONBLOCK        os_sem_wait(&rx_sem, OS_WAIT_FOREVER);        if (os_device_read_nonblock(can_dev, 0, &rxmsg, sizeof(rxmsg)) <= 0)#else        if (os_device_read_block(can_dev, 0, &rxmsg, sizeof(rxmsg)) <= 0)#endif            continue;                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!\r\n", can_name);        return OS_ERROR;    }    os_sem_init(&rx_sem, "rx_sem", 0, OS_SEM_MAX_VALUE);    res = os_device_open(can_dev);    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);    if (can_task != OS_NULL)    {        os_task_startup(can_task);    }    else    {        os_kprintf("create can_rx task failed!\r\n");    }    return res;}SH_CMD_EXPORT(can_start, can_start, "can device sample");OS_USED 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;#ifdef CAN_TEST_WRITE_NONBLOCK    struct os_device_cb_info cb_info =     {        .type = OS_DEVICE_CB_TYPE_TX,        .cb   = can_tx_done,    };    os_device_control(can_dev, OS_DEVICE_CTRL_SET_CB, &cb_info);#endif    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#ifdef CAN_TEST_WRITE_NONBLOCK        size = os_device_write_nonblock(can_dev, 0, &msg, sizeof(msg));#else        size = os_device_write_block(can_dev, 0, &msg, sizeof(msg));#endif        if (size == 0)        {            os_kprintf("can dev write data failed!\r\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 ""