lugl4313820 发表于 2022-1-27 23:40

【国民技术N32G457评测】9 RT_Thread Studio 驱动CAN 填坑之苦难历程!

本帖最后由 lugl4313820 于 2022-1-28 01:48 编辑

<p>今天成功的挂载了CAN1设备,抄了官的can_sample进行测试:</p>

<pre>
<code>/*
* 程序清单:这是一个 CAN 设备使用例程
* 例程导出了 can_sample 命令到控制终端
* 命令调用格式:can_sample can1
* 命令解释:命令第二个参数是要使用的 CAN 设备名称,为空则使用默认的 CAN 设备
* 程序功能:通过 CAN 设备发送一帧,并创建一个线程接收数据然后打印输出。
*/

#include &lt;rtthread.h&gt;
#include &quot;rtdevice.h&quot;

#define CAN_DEV_NAME       &quot;can1&quot;      /* CAN 设备名称 */

static struct rt_semaphore rx_sem;   /* 用于接收消息的信号量 */
static rt_device_t can_dev;            /* CAN 设备句柄 */

/* 接收数据回调函数 */
static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
{
    /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&amp;rx_sem);

    return RT_EOK;
}

static void can_rx_thread(void *parameter)
{
    int i;
    rt_err_t res;
    struct rt_can_msg rxmsg = {0};

    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(can_dev, can_rx_call);

#ifdef RT_CAN_USING_HDR
    struct rt_can_filter_item items =
    {
      RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 0, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */
      RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 0, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x300~0x3ff,hdr 为 - 1 */
      RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 0, 0x7ff, RT_NULL, RT_NULL), /* std,match ID:0x211,hdr 为 - 1 */
      RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL),                  /* std,match ID:0x486,hdr 为 - 1 */
      {0x555, 0, 0, 0, 0x7ff, 7,}                                       /* std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表 */
    };
    struct rt_can_filter_config cfg = {5, 1, items}; /* 一共有 5 个过滤表 */
    /* 设置硬件过滤表 */
    res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &amp;cfg);
    RT_ASSERT(res == RT_EOK);
#endif

    while (1)
    {
      /* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */
      rxmsg.hdr = -1;
      /* 阻塞等待接收信号量 */
      rt_sem_take(&amp;rx_sem, RT_WAITING_FOREVER);
      /* 从 CAN 读取一帧数据 */
      rt_device_read(can_dev, 0, &amp;rxmsg, sizeof(rxmsg));
      /* 打印数据 ID 及内容 */
      rt_kprintf(&quot;ID:%x&quot;, rxmsg.id);
      for (i = 0; i &lt; 8; i++)
      {
            rt_kprintf(&quot;%2x&quot;, rxmsg.data<i>);
      }

      rt_kprintf(&quot;\n&quot;);
    }
}

int can_sample(int argc, char *argv[])
{
    struct rt_can_msg msg = {0};
    rt_err_t res;
    rt_size_tsize;
    rt_thread_t thread;
    char can_name;

    if (argc == 2)
    {
      rt_strncpy(can_name, argv, RT_NAME_MAX);
    }
    else
    {
      rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX);
    }
    /* 查找 CAN 设备 */
    can_dev = rt_device_find(can_name);
    if (!can_dev)
    {
      rt_kprintf(&quot;find %s failed!\n&quot;, can_name);
      return RT_ERROR;
    }
    rt_kprintf(&quot;find %s !\n&quot;, can_name);
    /* 初始化 CAN 接收信号量 */
    rt_sem_init(&amp;rx_sem, &quot;rx_sem&quot;, 0, RT_IPC_FLAG_FIFO);

    /* 以中断接收及发送方式打开 CAN 设备 */
    res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
    rt_kprintf(&quot;opne %s !\n&quot;, can_name);
    RT_ASSERT(res == RT_EOK);
    /* 创建数据接收线程 */
    thread = rt_thread_create(&quot;can_rx&quot;, can_rx_thread, RT_NULL, 1024, 25, 10);
    if (thread != RT_NULL)
    {
      rt_thread_startup(thread);
    }
    else
    {
      rt_kprintf(&quot;create can_rx thread failed!\n&quot;);
    }

    msg.id = 0x78;            /* ID 为 0x78 */
    msg.ide = RT_CAN_STDID;   /* 标准格式 */
    msg.rtr = RT_CAN_DTR;       /* 数据帧 */
    msg.len = 8;                /* 数据长度为 8 */
    /* 待发送的 8 字节数据 */
    msg.data = 0x00;
    msg.data = 0x11;
    msg.data = 0x22;
    msg.data = 0x33;
    msg.data = 0x44;
    msg.data = 0x55;
    msg.data = 0x66;
    msg.data = 0x77;
    /* 发送一帧 CAN 数据 */
    size = rt_device_write(can_dev, 0, &amp;msg, sizeof(msg));
    if (size == 0)
    {
      rt_kprintf(&quot;can dev write data failed!\n&quot;);
    }

    return res;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(can_sample, can device sample);</i></code></pre>

<p><i>下载到开发板后,成功的找到设备,但是报:</i></p>

<p><i>find can1 !<br />
can init error2<br />
To initialize device:can1 failed. The error code is -1<br />
(res == RT_EOK) assertion failed at function:can_sample, line number:98</i></p>

<p><i>找到报错的地方:</i></p>

<p><i>&nbsp; 我于是考虑是不是can是不是坏了,为了排除can是否正常,我下载了官方提供的demo:</i></p>

<p><i>用can1&nbsp;CAN_LoopBack_Mode 模块测试是好的。改为CAN_Normal_Mode模式,接上can收发模块,用CAN工具测试也是好的:</i></p>

<pre>
<i>
<code>/* CAN1 transmit message */
    TransmitMailbox = CANTxMessage(CAN1,
                                 &amp;CAN1_TxMessage,
                                 0x0400,
                                 0x00,
                                 CAN_ID_STD,
                                 CAN_RTRQ_DATA,
                                 CAN_TXDLC_8,
                                 0x01,
                                 0x02,
                                 0x03,
                                 0x04,
                                 0x05,
                                 0x06,
                                 0x07,
                                 0x08);</code></i></pre>

<p><i>电脑上收到的数据:</i></p>

<p><i>&nbsp;从上面的测试得出,RT_Thread Studio 生成的 can INIT有问题:</i></p>

<p><i>调试模式追踪进去:</i></p>

<pre>
<i>
<code>static rt_err_t _can_config(struct rt_can_device *can, struct can_configure *cfg)
{
    struct n32g45x_can *drv_can;
    rt_uint32_t baud_index;

    RT_ASSERT(can);
    RT_ASSERT(cfg);
    drv_can = (struct n32g45x_can *)can-&gt;parent.user_data;
    RT_ASSERT(drv_can);

    /* Configure CAN1 and CAN2 */
    if (drv_can-&gt;CANx == CAN1)
    {
      RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_CAN1, ENABLE);
      n32_msp_can_init(CAN1);
    }</code></i></pre>

<p><i><code>这是初始化 can的数据:</code></i></p>

<p> &nbsp;</p>

<p>出错的地方就是在这里:</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp; 我用MDK初始化到这里的这样的:</p>

<p>&nbsp;然后两个对比一下!</p>

<p>&nbsp;查看数据手册:</p>

<p> &nbsp;</p>

<p>都半夜了,想想,还是找找办法,网上找到这篇文章,帮了大忙:</p>

<p><a href="https://club.rt-thread.org/ask/article/3322.html">RT-Thread-[ N32G457 ] rtthread上使用can设备驱动收发数据RT-Thread问答社区 - RT-Thread</a></p>

<p>把他的示例抄下来:</p>

<p>can_sample.c</p>

<pre>
<code>/*
* Change Logs:
* Date         Author       Notes
* 2022-01-15   chenbin
*/
#include "stdint.h"
#include "stdio.h"
#include "string.h"
#include "rtthread.h"
#include "rtdevice.h"

#define DBG_TAG "can1"
#define DBG_LVL DBG_LOG
#include &lt;rtdbg.h&gt;

#define CAN1_DEVICE_NAME            "can1"

static               rt_device_t            can1_dev;   /* CAN 设备句柄 */
static struct rt_semaphore      can1_rx_sem;   /* 用于接收消息的信号量 */

/* 接收数据回调函数 */
static rt_err_t can1_rx_call(rt_device_t dev, rt_size_t size)
{
    /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&amp;can1_rx_sem);

    return RT_EOK;
}


void can1_send_0x123(void)
{
    struct rt_can_msg msg = {0};
    msg.id =0x123;            /* ID 为 0x123 */
    msg.ide = RT_CAN_STDID;   /* 标准格式 */
    msg.rtr = RT_CAN_DTR;       /* 数据帧 */
    msg.len = 8;                /* 数据长度为 8 */
    /* 待发送的 8 字节数据 */
    msg.data = 0x00;
    msg.data = 0x11;
    msg.data = 0x22;
    msg.data = 0x33;
    msg.data = 0x44;
    msg.data = 0x55;
    msg.data = 0x66;
    msg.data = 0x77;
    /* 发送一帧 CAN 数据 */
    int size = rt_device_write(can1_dev, 0, &amp;msg, sizeof(msg));
    if (size &lt; 0)
    {
      rt_kprintf("can1 dev write data failed rc:%d\n",size);
    }
}

void can1_send_0x456_RTR(void)
{
    struct rt_can_msg msg = {0};
    msg.id =0x456;            /* ID 为 0x456 */
    msg.ide = RT_CAN_STDID;   /* 标准格式 */
    msg.rtr = RT_CAN_RTR;       /* 远程帧 */

    int size = rt_device_write(can1_dev, 0, &amp;msg, sizeof(msg));
    if (size &lt; 0)
    {
      rt_kprintf("can1 dev write data failed rc:%d\n",size);
    }
}

static void can1_thread(void *param)
{
    int rc = 0;
    can1_dev = rt_device_find(CAN1_DEVICE_NAME);
    if (!can1_dev)
    {
            rt_kprintf("find %s failed!\n", CAN1_DEVICE_NAME);
            return ;
    }else
    {
      rt_kprintf("find %s ok!\n", CAN1_DEVICE_NAME);
    }
    rt_sem_init(&amp;can1_rx_sem, "can1_rx_sem", 0, RT_IPC_FLAG_FIFO);

    /* 以中断接收及发送方式打开 CAN 设备 */
    rc = rt_device_open(can1_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
    RT_ASSERT(rc == RT_EOK);

    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(can1_dev, can1_rx_call);

    rt_device_control(can1_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN500kBaud);

    rt_device_control(can1_dev, RT_CAN_CMD_SET_MODE, (void *)RT_CAN_MODE_NORMAL);

#ifdef RT_CAN_USING_HDR
    struct rt_can_filter_item items =
    {
      RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */
      RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x300~0x3ff,hdr 为 - 1 */
      RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 1, 0x7ff, RT_NULL, RT_NULL), /* std,match ID:0x211,hdr 为 - 1 */
      RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL),                  /* std,match ID:0x486,hdr 为 - 1 */
      {0x555, 0, 0, 1, 0x7ff, 7,}                                       /* std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表 */
    };
    struct rt_can_filter_config cfg = {5, 1, items}; /* 一共有 5 个过滤表 */
    /* 设置硬件过滤表 */
    res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &amp;cfg);
    RT_ASSERT(res == RT_EOK);
#endif
    struct rt_can_msg rxmsg = {0};
    while(1)
    {
            /* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */
            rxmsg.hdr = -1;
            /* 阻塞等待接收信号量 */
            rt_sem_take(&amp;can1_rx_sem, RT_WAITING_FOREVER);
            /* 从 CAN 读取一帧数据 */
            rt_device_read(can1_dev, 0, &amp;rxmsg, sizeof(rxmsg));

            rt_kprintf("CAN1 RX\n");
            /* 打印数据 ID 及内容 */
            if(rxmsg.rtr == RT_CAN_RTR)//远程帧
            {
                if(rxmsg.ide == RT_CAN_EXTID)//扩展帧
                {
                  rt_kprintf("ID:0x%08X RTR len:%d \n", rxmsg.id, rxmsg.len);
                }else
                {
                  rt_kprintf("ID:0x%04X RTR len:%d \n", rxmsg.id, rxmsg.len);
                }
                if(rxmsg.id == 0x123)
                {
                  can1_send_0x123();
                }
            }else//数据帧
            {
                if(rxmsg.ide == RT_CAN_EXTID)//扩展帧
                {
                  rt_kprintf("ID:0x%08X len:%d ", rxmsg.id, rxmsg.len);
                  for (int i = 0; i &lt; rxmsg.len; i++)
                  {
                            rt_kprintf(" %02X", rxmsg.data);
                  }
                  rt_kprintf("\n");
                }else
                {
                  rt_kprintf("ID:0x%04X len:%d ", rxmsg.id, rxmsg.len);
                  for (int i = 0; i &lt; rxmsg.len; i++)
                  {
                            rt_kprintf(" %02X", rxmsg.data);
                  }
                  rt_kprintf("\n");
                }
            }
    }
}

int can1_test_init(void)
{
    static int can1_init = 0;
    rt_thread_t tid;
    if(can1_init &gt; 0)
    {
      return 0;
    }
    can1_init = 1;

    tid = rt_thread_create("can1",can1_thread, NULL,2048,20, 10);
    if (tid != RT_NULL)
            rt_thread_startup(tid);
    return 0;
}


void can1_test(uint8_t argc, char **argv)
{
    rt_kprintf("can1 test\n");
    can1_send_0x123();
}
MSH_CMD_EXPORT(can1_test, can1 test);

void can1_test1(uint8_t argc, char **argv)
{
    rt_kprintf("can1 test1\n");
    can1_send_0x456_RTR();
}
MSH_CMD_EXPORT(can1_test1, can1 test1);


void can1_debug(uint8_t argc, char **argv)
{
    if(argc &gt; 1)
    {
      int val = atoi(argv);
      rt_kprintf("can1 debug set %d\n",val);
    }
}
MSH_CMD_EXPORT(can1_debug, can1 debug);
</code></pre>

<p>main.c:</p>

<pre>
<code>/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date         Author       Notes
* 2015-07-29   Arda.Fu      first implementation
*/
#include &lt;stdint.h&gt;
#include &lt;rtthread.h&gt;
#include &lt;rtdevice.h&gt;
#include "drv_can.h"



/* defined the LED1 pin: PB5 */
#define LED1_PIN    91
    int can1_test_init(void);

int main(void)
{
    uint32_t Speed = 200;
    /* set LED1 pin mode to output */
    rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
   // rt_hw_can_init();
    can1_test_init();
    while (1)
    {
      rt_pin_write(LED1_PIN, PIN_LOW);
      rt_thread_mdelay(Speed);
      rt_pin_write(LED1_PIN, PIN_HIGH);
      rt_thread_mdelay(Speed);
    }
}

</code></pre>

<p>然后还不对,看到他的文章修改CAN1的脚,因为我用MDK用的是PD0跟PD1,我查一下资料,才知道CAN1的TX RX默认是PA11 PA12,悲伤呀,可能是由于脚不对,初始化不成功。立马把线跳到PA11、PA12.</p>

<p>烧录下果然没有报错了:</p>

<p>msh &gt;can1_te<br />
can1_test<br />
can1_test1<br />
msh &gt;can1_test<br />
can1 test<br />
msh &gt;<br />
&nbsp;\ | /<br />
- RT - &nbsp; &nbsp; Thread Operating System<br />
&nbsp;/ | \ &nbsp; &nbsp; 4.0.5 build Jan 28 2022 01:22:53<br />
&nbsp;2006 - 2021 Copyright by rt-thread team<br />
msh &gt;find can1 ok!<br />
嘿嘿,功夫不负有心人呀,只怪自己太初心了。。。。</p>

<p>&nbsp;经过一天一夜的填坑,终于把CAN驱动好了。。。。</p>

<p>希望大家以后不要走我的老路,悲伤呀!!!</p>

Jacktang 发表于 2022-1-28 08:11

<p>CAN1的TX RX默认是PA11 PA12,这下记住了</p>

<p>谢谢楼主的填坑经验<img height="55" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan11.gif" width="70" /></p>

littleshrimp 发表于 2022-1-28 09:36

<p>为楼主的毅力点赞<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan88.gif" width="59" /></p>

<p>这个引脚定义是官方资料没有提吗?</p>

lugl4313820 发表于 2022-1-28 09:54

littleshrimp 发表于 2022-1-28 09:36
为楼主的毅力点赞

这个引脚定义是官方资料没有提吗?

<p>官方资料手册默认的是:PA11,PA12,但是我看官方提供的DEMO是初始化的PD0与PD1,所以就没有想这么多。</p>

lugl4313820 发表于 2022-3-14 22:15

<p>CAN看来使用的地方比较少呀,不知道以后会不会跟串口一样使用普遍。是成本还是其他的,欢迎大家讨论。</p>

freebsder 发表于 2022-3-14 22:42

lugl4313820 发表于 2022-3-14 22:15
CAN看来使用的地方比较少呀,不知道以后会不会跟串口一样使用普遍。是成本还是其他的,欢迎大家讨论。

<p>使用上还是不方便,尤其是上层协议,要搞清楚还是有些饶</p>
页: [1]
查看完整版本: 【国民技术N32G457评测】9 RT_Thread Studio 驱动CAN 填坑之苦难历程!