Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Maxding001 opened this issue Jun 30, 2020 · 14 comments
Open

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

Maxding001 opened this issue Jun 30, 2020 · 14 comments

Comments

@Maxding001
Copy link

本人刚接触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
Copy link
Owner

gbcwbz commented Jul 6, 2020

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
Copy link
Author

谢谢你的回复。
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
Copy link
Owner

gbcwbz commented Jul 7, 2020

  • 首先确认一下你开启了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接收后的回调路径,查出根源

@Maxding001
Copy link
Author

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

@Maxding001
Copy link
Author

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

@gbcwbz
Copy link
Owner

gbcwbz commented Jul 7, 2020

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

@hello-jzz
Copy link

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

@gbcwbz
Copy link
Owner

gbcwbz commented Jul 7, 2020

@Maxding001
Copy link
Author

谢谢大神回复
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
Copy link
Owner

gbcwbz commented Jul 8, 2020

  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
Copy link
Author

谢谢 我有点明白了.
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
Copy link
Owner

gbcwbz commented Jul 8, 2020

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

@Maxding001
Copy link
Author

我在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
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants