gbcwbz / canfestival-rtt

A CanFestival port to RT-Thread operating system
GNU Lesser General Public License v2.1
53 stars 31 forks source link

[CANFestival Rtt] 自带例程Master402问题 #4

Open Maxding001 opened 4 years ago

Maxding001 commented 4 years ago

本人刚接触RT-Thread,最近有个项目需要控制一台变频器,通信方式是CANOpen CIA402的方式。 硬件是正点原子的 阿波罗stm32F767开发版

1,首先我在ENV里只激活了CAN 并且用周立功的CAN测试仪器 测试了can的通讯口,可以正常收发。 2,然后再在ENV里激活了CANFentival的组件

想测试一下系统自带的例子CIA402。 3,程序编译通过 下载后运行发现FINSH不能正常工作。 4,监控程序后发现程序在can_rtthread.c里面死循环 void canopen_recv_thread_entry(void parameter) { struct can_app_struct canpara = (struct can_app_struct *) parameter; struct rt_can_msg msg; rt_uint32_t e; Message co_msg;

/* set LED0 pin mode to output */
rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);   

candev = rt_device_find(canpara->name);
RT_ASSERT(candev);
rt_event_init(&canpara->event, canpara->name, RT_IPC_FLAG_FIFO);
rt_device_open(candev, (RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX));
rt_device_control(candev, RT_CAN_CMD_SET_FILTER, canpara->filter);

while (1)
{
    if (rt_event_recv(&canpara->event,
                      ( 1 <<canpara->filter->items[0].hdr),//(1 << canpara->filter->items[0].hdr),
                      canpara->eventopt,
                      RT_WAITING_FOREVER, &e) != RT_EOK)
    {

        rt_pin_write(LED1_PIN, PIN_HIGH);    //在这里死循环
        rt_thread_mdelay(1000);
        rt_pin_write(LED1_PIN, PIN_LOW);
        rt_thread_mdelay(1000);
        continue;
    }

    if (e & (1 << canpara->filter->items[0].hdr))
    {
        msg.hdr = canpara->filter->items[0].hdr;
        while (rt_device_read(candev, 0, &msg, sizeof(msg)) == sizeof(msg))
        {
            co_msg.cob_id = msg.id;
            co_msg.len = msg.len;
            co_msg.rtr = msg.rtr;
            memcpy(co_msg.data, msg.data, msg.len);
            EnterMutex();
            canDispatch(OD_Data, &co_msg);
            LeaveMutex();
        }
    }
}   

}

5,分析原因发现在can.h里

define RT_CAN_FILTER_ITEM_INIT(id,ide,rtr,mode,mask,ind,args) \

 {(id), (ide), (rtr), (mode), (mask), -1, (ind), (args)}

把HDR定义为-1 导致 上面的 rt_event_recv 返回报错。

6,首先问题是,can.h里的初始化对吗?给的例子程序难道不能使用吗? 请高人指点一下。

gbcwbz commented 4 years ago

Sorry, 最近没上 github 回复晚了 我记得以前是不用指定 hdr 的,hdr 是下面自动确定的,以前确实是这么用的。如果现在要自己指定,你可以手动生成一下 filter 结构体。 好久没玩 can 了,一时想不起是哪里确定的 hdr,你可以跟踪一下 rt_device_control(candev, RT_CAN_CMD_SET_FILTER, canpara->filter); 如果指定了rt_event_recv参数RT_WAITING_FOREVER,这个函数应该是不会返回的,表现应该是永远收不到数据

Maxding001 commented 4 years ago

谢谢你的回复。 1,我更改了 can.h里的

define RT_CAN_FILTER_ITEM_INIT(id,ide,rtr,mode,mask,ind,args) \

 {(id), (ide), (rtr), (mode), (mask), **1**, (ind), (args)}

把原来的-1改为了1,程序就不会死循环了。 2,在can_rtthread.c里我发现 static rt_err_t can1ind(rt_device_t dev, void args, rt_int32_t hdr, rt_size_t size) { rt_event_t pevent = (rt_event_t)args; rt_event_send(pevent, 1 << (hdr)); return RT_EOK; } 这个can的接收回调函数没有地方调用 我自己手动增加了 rt_device_set_rx_indicate(candev, canpara->filter->items[0].ind);//后来添加的 void canopen_recv_thread_entry(void parameter) { struct can_app_struct canpara = (struct can_app_struct ) parameter; struct rt_can_msg msg; rt_uint32_t e; Message co_msg; rt_err_t err;

/* set LED0 pin mode to output */
rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); 

candev = rt_device_find(canpara->name);
RT_ASSERT(candev);
rt_event_init(&canpara->event, canpara->name, RT_IPC_FLAG_FIFO);
rt_device_open(candev, (RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX));
rt_device_control(candev, RT_CAN_CMD_SET_FILTER, canpara->filter);

**rt_device_set_rx_indicate(candev, canpara->filter->items[0].ind);**//后来添加的 

while (1)
{
    err = rt_event_recv(&canpara->event,
                      ( 1 <<canpara->filter->items[0].hdr),//(1 << canpara->filter->items[0].hdr),
                      canpara->eventopt,
                      RT_WAITING_FOREVER, &e) ;
    if (err!= RT_EOK)
    {            
        continue;
    }

    if (e & (1 << canpara->filter->items[0].hdr))
    {
        msg.hdr = canpara->filter->items[0].hdr;
        while (rt_device_read(candev, 0, &msg, sizeof(msg)) == sizeof(msg))
        {
            co_msg.cob_id = msg.id;
            co_msg.len = msg.len;
            co_msg.rtr = msg.rtr;
            memcpy(co_msg.data, msg.data, msg.len);
            EnterMutex();
            canDispatch(OD_Data, &co_msg);
            LeaveMutex();
        }
    }
}    

}

rt_device_set_rx_indicate(candev, canpara->filter->items[0].ind) 这句话发现参数类型不匹配。 这个地方应该怎么修改才可以。请大神赐教一下。

gbcwbz commented 4 years ago
Maxding001 commented 4 years ago

1,can的filter设置我大概有个概念,对原始数据针的过滤 这应该是硬件层面的参数设置。 2,can的回调函数在rtthread里都需要调用rt_device_set_rx_indicate来指定,但是为什么例子的程序里没有呢? 3, 我在用其他rtthread的sample基本不用改代码就可以直接使用,很冒昧的问一下,我总感觉这个例子还不是很完善,对于像我这样的小白来讲不能拿来直接用。 4,如果需要修改地方 应该怎么改合理,需要改那些地方。我用的是正点原子 stm32f767的开发板。

Maxding001 commented 4 years ago

RT_CAN_USING_HDR我已经打开了,如果不打开编译错误会有很多。

gbcwbz commented 4 years ago
  1. can.c里面已经处理了
  2. 以前也是开箱即用的,可能 rt-thread 底层更改了,例子好久没有更新了所以运行不起来
  3. 怎么修改你可以先研究一下,有好的更改方法欢迎提 pull request,我有时间会看一下是什么问题
hello-jzz commented 4 years ago

期待本问题的根本解决方案~

gbcwbz commented 4 years ago
Maxding001 commented 4 years ago

谢谢大神回复 1.1,因为默认的hdr设置在can.h里的

define RT_CAN_FILTER_ITEM_INIT(id,ide,rtr,mode,mask,ind,args) \

 {(id), (ide), (rtr), (mode), (mask), -1, (ind), (args)}

其中的-1 就是把can_data.filter->items[0].hdr 变成-1. 1.2,在can.c里有 291行 for (int i = 0; i < filter_cfg->count; i++) { drv_can->FilterConfig.FilterBank = filter_cfg->items[i].hdr; drv_can->FilterConfig.FilterIdHigh = (filter_cfg->items[i].id >> 13) & 0xFFFF; drv_can->FilterConfig.FilterIdLow = ((filter_cfg->items[i].id << 3) | (filter_cfg->items[i].ide << 2) | (filter_cfg->items[i].rtr << 1)) & 0xFFFF; drv_can->FilterConfig.FilterMaskIdHigh = (filter_cfg->items[i].mask >> 16) & 0xFFFF; drv_can->FilterConfig.FilterMaskIdLow = filter_cfg->items[i].mask & 0xFFFF; drv_can->FilterConfig.FilterMode = filter_cfg->items[i].mode; / Filter conf / HAL_CAN_ConfigFilter(&drv_can->CanHandle, &drv_can->FilterConfig); } 其中的HAL_CAN_ConfigFilter对硬件filter进行了设置但是我发现FilterBank 的说明是 uint32_t FilterBank; /!< Specifies the filter bank which will be initialized. For single CAN instance(14 dedicated filter banks), this parameter must be a number between Min_Data = 0 and Max_Data = 13. For dual CAN instances(28 filter banks shared), this parameter must be a number between Min_Data = 0 and Max_Data = 27. / 所以hdr的能设置的参数只能是0到27,为什么在官方文件里can.h要把hdr默认初始化为-1? 1.3,在can_rtthread.c里面的 err = rt_event_recv(&canpara->event, ( 1 <filter->items[0].hdr),//(1 << canpara->filter->items[0].hdr), canpara->eventopt, RT_WAITING_FOREVER, &e) ; if (err!= RT_EOK) {
continue; } 因为rt_event_recv的set参数不能是1<<-1 因为初始化时把items[0].hdr变成了-1 所以程序一直死循环,我的感觉是在 struct rt_can_filter_item filter1item[1] = { RT_CAN_FILTER_ITEM_INIT(0x181, 0, 0, 1, 0, can1ind, &can_data.event)//不能用

{0x181, 0, 0, 1, 0,**1**, can1ind, &can_data.event} //用于替代

}; 第一行不能用,用第二行的初始化来代替。这样理解对吗?

2.1我追踪了一下程序 用PC的can测试设备发送了一针内容为 ID是0x180 EID:0 rtr:0 DLC:2 DATA1:1 DATA2:1 就是标准针 数据针 目标1号的PDO发送 数据带2个字节分别是1和2 我的程序能进入到rt_hw_can_isr,并且收到了以上的数据针。 2.2 数据从链表listmsg = rt_list_entry(rx_fifo->freelist.next, struct rt_can_msg_list, list);里得到 2.3但是 hdr = tmpmsg.hdr;的赋值一直是0;所以程序一直运行不下去,无法调用回调ind if (can->hdr != RT_NULL && can->hdr[hdr].connected && can->hdr[hdr].filter.ind) //can->hdr =RT_NULL { rt_size_t rx_length; RT_ASSERT(hdr < can->config.maxhdr && hdr >= 0);

        level = rt_hw_interrupt_disable();
        rx_length = can->hdr[hdr].msgs * sizeof(struct rt_can_msg);
        rt_hw_interrupt_enable(level);
        if (rx_length)
        {
            can->hdr[hdr].filter.ind(&can->parent, can->hdr[hdr].filter.args, hdr, rx_length);
        }
    }

2.4我发现在drv_can.c 的static int _can_recvmsg(struct rt_can_device can, void buf, rt_uint32_t fifo)里面

status = HAL_CAN_GetRxMessage(hcan, fifo, &rxheader, pmsg->data); if (HAL_OK != status) return -RT_ERROR; / get id / if (CAN_ID_STD == rxheader.IDE) { pmsg->ide = RT_CAN_STDID; pmsg->id = rxheader.StdId; } else { pmsg->ide = RT_CAN_EXTID; pmsg->id = rxheader.ExtId; } / get type / if (CAN_RTR_DATA == rxheader.RTR) { pmsg->rtr = RT_CAN_DTR; } else { pmsg->rtr = RT_CAN_RTR; } / get len / pmsg->len = rxheader.DLC; / get hdr / if (hcan->Instance == CAN1) { pmsg->hdr = (rxheader.FilterMatchIndex + 1) >> 1; } 在can的301标准格式里面数据针不带pmsg->hdr = (rxheader.FilterMatchIndex + 1) >> 1

我不理解的地方在hdr为什么包含在原始针里,而标准里没有提到?我哪里理解错了?

gbcwbz commented 4 years ago
  1. 这个我已经回答过了。-1 表示由底层驱动自动分配,底层驱动会寻找空余的过滤器,并把编号赋给 hdr。can框架的作者最初是用stm32标准库写的,驱动里面有自动赋值(代码我在上个回复里贴了)。你看到的代码是 HAL 驱动的,后来改成 HAL 驱动的人没有遵循 can 框架作者的意图,没有实现,-1时自动分配功能,所以才出现了现在不能用的情况。最好的办法是把用HAL库的驱动改成支持 -1时自动赋值的方式,这样上层不用担心底层还有那些过滤器没有使用。
  2. 如果你 filter1item 里面设置的 hdr 是1,那么
    pmsg->hdr = (rxheader.FilterMatchIndex + 1) >> 1;
    pmsg->hdr = (1+1)>>1 = 4

    除非你设置的就-1

Maxding001 commented 4 years ago

谢谢 我有点明白了. 1,目前我的做法是关闭了RT_CAN_USING_HDR然后用rt_device_set_rx_indicate(candev,can_rx_call);重新制定了一个回调,现在可以正常处理canDispatch(OD_Data, &co_msg);了。 2,在master402_canopen.c里面 int canopen_init(void) { OD_Data->heartbeatError = master402_heartbeatError; OD_Data->initialisation = master402_initialisation; OD_Data->preOperational = master402_preOperational; OD_Data->operational = master402_operational; OD_Data->stopped = master402_stopped; OD_Data->post_sync = master402_post_sync; OD_Data->post_TPDO = master402_post_TPDO; OD_Data->storeODSubIndex = (storeODSubIndex_t)master402_storeODSubIndex; OD_Data->post_emcy = (post_emcy_t)master402_post_emcy;

canOpen(&agv_board, OD_Data);
initTimer();

// Start timer thread
StartTimerLoop(&InitNodes);

return 0;

} INIT_APP_EXPORT(canopen_init);

void InitNodes(CO_Data* d, UNS32 id) { setNodeId(OD_Data, 0x01); setState(OD_Data, Initialisation); } 程序一直不会执行InitNodes()函数,这是什么原因呢?

gbcwbz commented 4 years ago

InitNodes 是在定时器里回调的,要检查一下你的 hwtimer 配置了

Maxding001 commented 4 years ago

我在rtconfig.h里设定了

define CANFESTIVAL_TIMER_DEVICE_NAME "timer13"

define BSP_USING_TIM

define BSP_USING_TIM13

define BSP_USING_TIM11

然后我测试了 perpheral_samples里面的 hwtimer_sample 没有问题

可是用timer_rtthred.c里面的initTimer一直不能调用到timer_timeout_cb 好奇怪? void initTimer(void) { rt_thread_t tid; rt_err_t err; rt_hwtimer_mode_t mode; rt_hwtimerval_t timeout_s;
int freq = 1000000;

canfstvl_mutex = rt_mutex_create("canfstvl",RT_IPC_FLAG_FIFO);
canfstvl_timer_sem = rt_sem_create("canfstvl", 0, RT_IPC_FLAG_FIFO);

canfstvl_timer_dev = rt_device_find(CANFESTIVAL_TIMER_DEVICE_NAME);
RT_ASSERT(canfstvl_timer_dev != RT_NULL);
err = rt_device_open(canfstvl_timer_dev, RT_DEVICE_OFLAG_RDWR);
if (err != RT_EOK)
{
    rt_kprintf("CanFestival open timer Failed! err=%d\n", err);
    return;
}
rt_device_set_rx_indicate(canfstvl_timer_dev, timer_timeout_cb);
err = rt_device_control(canfstvl_timer_dev, HWTIMER_CTRL_FREQ_SET, &freq);
if (err != RT_EOK)
{
    rt_kprintf("Set Freq=%dhz Failed\n", freq);
        return;
}

mode = HWTIMER_MODE_PERIOD;//HWTIMER_MODE_PERIOD HWTIMER_MODE_ONESHOT
err = rt_device_control(canfstvl_timer_dev, HWTIMER_CTRL_MODE_SET, &mode);
    if (err != RT_EOK)
    {
        rt_kprintf("set mode failed! ret is :%d\n", err);
        return ;
    }

    timeout_s.sec = 1;      
timeout_s.usec = 0;    

if (rt_device_write(canfstvl_timer_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
{
    rt_kprintf("set timeout value failed\n");
        return;
}   
rt_device_read(canfstvl_timer_dev, 0, &last_timer_val, sizeof(last_timer_val));

tid = rt_thread_create("cf_timer",
                       canopen_timer_thread_entry, RT_NULL,
                       1024, CANFESTIVAL_TIMER_THREAD_PRIO, 20);
if (tid != RT_NULL) rt_thread_startup(tid);

}

Xiao-code123 commented 3 years ago

我在rtconfig.h里设定了

define CANFESTIVAL_TIMER_DEVICE_NAME "timer13"

define BSP_USING_TIM

define BSP_USING_TIM13

define BSP_USING_TIM11

然后我测试了 perpheral_samples里面的 hwtimer_sample 没有问题

可是用timer_rtthred.c里面的initTimer一直不能调用到timer_timeout_cb 好奇怪? void initTimer(void) { rt_thread_t tid; rt_err_t err; rt_hwtimer_mode_t mode; rt_hwtimerval_t timeout_s; int freq = 1000000;

canfstvl_mutex = rt_mutex_create("canfstvl",RT_IPC_FLAG_FIFO);
canfstvl_timer_sem = rt_sem_create("canfstvl", 0, RT_IPC_FLAG_FIFO);

canfstvl_timer_dev = rt_device_find(CANFESTIVAL_TIMER_DEVICE_NAME);
RT_ASSERT(canfstvl_timer_dev != RT_NULL);
err = rt_device_open(canfstvl_timer_dev, RT_DEVICE_OFLAG_RDWR);
if (err != RT_EOK)
{
    rt_kprintf("CanFestival open timer Failed! err=%d\n", err);
    return;
}
rt_device_set_rx_indicate(canfstvl_timer_dev, timer_timeout_cb);
err = rt_device_control(canfstvl_timer_dev, HWTIMER_CTRL_FREQ_SET, &freq);
if (err != RT_EOK)
{
    rt_kprintf("Set Freq=%dhz Failed\n", freq);
      return;
}

mode = HWTIMER_MODE_PERIOD;//HWTIMER_MODE_PERIOD HWTIMER_MODE_ONESHOT
err = rt_device_control(canfstvl_timer_dev, HWTIMER_CTRL_MODE_SET, &mode);
  if (err != RT_EOK)
  {
      rt_kprintf("set mode failed! ret is :%d\n", err);
      return ;
  }

  timeout_s.sec = 1;      
timeout_s.usec = 0;    

if (rt_device_write(canfstvl_timer_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
{
    rt_kprintf("set timeout value failed\n");
      return;
} 
rt_device_read(canfstvl_timer_dev, 0, &last_timer_val, sizeof(last_timer_val));

tid = rt_thread_create("cf_timer",
                       canopen_timer_thread_entry, RT_NULL,
                       1024, CANFESTIVAL_TIMER_THREAD_PRIO, 20);
if (tid != RT_NULL) rt_thread_startup(tid);

}

没有开启定时器,需要自己开启,在StartTimerLoop(&InitNodes)语句执行完成之后开启,因为该函数会调用SetAlarm(NULL, 0, init_callback, 0, 0),该函数又会调用setTimer(real_timer_value),real_timer_value=value=0,从而定时器关闭。