canfestival-rtt icon indicating copy to clipboard operation
canfestival-rtt copied to clipboard

[CANFestival Rtt] 自带例程Master402问题

Open Maxding001 opened this issue 4 years ago • 14 comments

本人刚接触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里的初始化对吗?给的例子程序难道不能使用吗? 请高人指点一下。

Maxding001 avatar Jun 30 '20 07:06 Maxding001

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

gbcwbz avatar Jul 06 '20 06:07 gbcwbz

谢谢你的回复。 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) 这句话发现参数类型不匹配。 这个地方应该怎么修改才可以。请大神赐教一下。

Maxding001 avatar Jul 07 '20 02:07 Maxding001

  • 首先确认一下你开启了RT_CAN_USING_HDR,我以前使用的时候是在 can 接收回调里面调用的 can1ind
  • 回调函数定义rt_err_t (*ind)(rt_device_t dev, void *args , rt_int32_t hdr, rt_size_t size);rt_device_set_rx_indicate中回调函数定义rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size),这两个函数是不匹配的,没有event和hdr信息,如果要强制改的话可以写一个wrap函数,rt_device_set_rx_indicate(candev, wrap),然后在wrap里调用canpara->filter->items[0].ind
  • 不过有时间的话还是研究一下can的filter设置,以及can接收后的回调路径,查出根源

gbcwbz avatar Jul 07 '20 02:07 gbcwbz

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

Maxding001 avatar Jul 07 '20 03:07 Maxding001

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

Maxding001 avatar Jul 07 '20 03:07 Maxding001

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

gbcwbz avatar Jul 07 '20 03:07 gbcwbz

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

hello-jzz avatar Jul 07 '20 12:07 hello-jzz

  • 以前测试的时候是用的 bsp/stm32f10x,其中的bxcan.c中是会自动对hdr赋值的 https://github.com/RT-Thread/rt-thread/blob/76431f6b00cb2558f072acf01c442e756c481ad1/bsp/stm32f10x/drivers/bxcan.c#L869-L876
  • 在新版本的代码中删除了 bsp/stm32f10x ,使用了 HAL 驱动,HAL 驱动中看起来没有对 hdr 进行赋值,而是采用了用户设置的值 https://github.com/RT-Thread/rt-thread/blob/27ed5ee61cf77b67e5bb03a7822e46d723082e54/bsp/stm32/libraries/HAL_Drivers/drv_can.c#L289-L303 我对 HAL 驱动的 can 代码不太熟悉,不知道能不能改,所以现在只能临时手动设置 hdr
  • 在 stm32f10x 中接收中断会调用 rt_hw_can_isr,然后调用 filter 中设置的回调函数 https://github.com/RT-Thread/rt-thread/blob/76431f6b00cb2558f072acf01c442e756c481ad1/components/drivers/can/can.c#L836-L847
  • 在 HAL 驱动中也调用了rt_hw_can_isr https://github.com/RT-Thread/rt-thread/blob/dab737f6375f5cdd86ad3bc6d166657b17364948/bsp/stm32/libraries/HAL_Drivers/drv_can.c#L551-L554 可能因为什么条件没有满足,所以没有调用到 @Maxding001 你可以跟踪一下rt_hw_can_isr看看是什么条件收起的

gbcwbz avatar Jul 07 '20 16:07 gbcwbz

谢谢大神回复 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为什么包含在原始针里,而标准里没有提到?我哪里理解错了?

Maxding001 avatar Jul 08 '20 00:07 Maxding001

  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

gbcwbz avatar Jul 08 '20 06:07 gbcwbz

谢谢 我有点明白了. 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()函数,这是什么原因呢?

Maxding001 avatar Jul 08 '20 06:07 Maxding001

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

gbcwbz avatar Jul 08 '20 07:07 gbcwbz

我在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);

}

Maxding001 avatar Jul 08 '20 08:07 Maxding001

我在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,从而定时器关闭。

Xiao-code123 avatar Mar 17 '21 03:03 Xiao-code123