打印
[应用相关]

CANopen 函数移植说明

[复制链接]
2221|21
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
CANopen 函数移植PS:本移植步骤适用于所有 ARM 非操作系统下移植(以 STM32 为例)我们移植需要用到的源文件在 CanFestival-3-10\src 目录下,头文件在CanFestival-3-10\include 目录下。说明
沙发
EDA设计爱好者|  楼主 | 2018-8-28 12:17 | 只看该作者
CanFestival-3-10\src 下的文件如下图所示:

1.png (103.97 KB )

1.png

使用特权

评论回复
板凳
EDA设计爱好者|  楼主 | 2018-8-28 12:17 | 只看该作者
CanFestival-3-10\include 下的文件如下图所示:

小Q截图-20180828121607.png (223.56 KB )

小Q截图-20180828121607.png

使用特权

评论回复
地板
EDA设计爱好者|  楼主 | 2018-8-28 12:19 | 只看该作者
接下来开始移植:
步骤一:
在新建好的工程目录下新建文件夹 CanFestival,再在 CanFestival 下新建文
件夹 driver、inc 和 src,再在 inc 文件夹下面新建 stm32 文件夹(我这里主要以
移植到 stm32 为例说明,如果是移植到 VC 或其他平台下,这里也可以命名为其
他名字,如 vc)。

使用特权

评论回复
5
EDA设计爱好者|  楼主 | 2018-8-28 12:19 | 只看该作者
步骤二:
将 CanFestival-3-10\src 目录下的 dcf.c、emcy.c、lifegrd.c、lss.c、nmtMaster.c、
nmtSlave.c、objacces.c、pdo.c、sdo.c、states.c、sync.c、timer.c 共 12 个文件拷贝
到 CanFestival\src 目录下;将 CanFestival-3-10\include 目录下的所有.h 文件共 19
个 文 件 全 部 拷 贝 到 CanFestival\inc 目 录 下 , 再 把
CanFestival-3-10\examples\AVR\Slave 目录下的 ObjDict.h 文件拷贝过来,一共 20
个;将 CanFestival-3-10\include\AVR 目录下的 applicfg.h、canfestival.h、config.h、
timerscfg.h 共 4 个头文件拷贝到 canfestival\inc\stm32 目录下;将
CanFestival-3-10\examples\TestMasterSlave 目录下的 TestSlave.c、TestSlave.h、
TestMaster.h、TestMaster.c 拷贝到 canfestival\driver 目录下,并在该目录下新建
stm32_canfestival.c 文件。

使用特权

评论回复
6
EDA设计爱好者|  楼主 | 2018-8-28 12:19 | 只看该作者
步骤三:
将 CanFestival\src 目录下的所有.c 文件添加到工程;将 canfestival\driver 目
录下的 stm32_canfestival.c 文件添加到工程;如果实现的是从设备,再将
canfestival\driver 目录下的 TestSlave.c 文件添加到工程,如果实现的是主设备,
则将 TestMaster.c 文件添加到工程;

使用特权

评论回复
7
EDA设计爱好者|  楼主 | 2018-8-28 12:20 | 只看该作者
步骤四:
将文件目录 canfestival\inc、canfestival\inc\stm32、canfestival\driver 等路径添
加到工程包含路径。

使用特权

评论回复
8
EDA设计爱好者|  楼主 | 2018-8-28 12:20 | 只看该作者
步骤五:
在 stm32_canfestival.c 中包含头文件#include "canfestival.h",并定义如下函
数:
void setTimer(TIMEVAL value)
{
}
TIMEVAL getElapsedTime(void)
{
return 1;
}
unsigned char canSend(CAN_PORT notused, Message *m)
{
return 1;
}
可以先定义一个空函数,等到编译都通过了之后,再往里面添加内容,这几个函
数都是定义来供 canfestival 源码调用的,如果找不到这几个函数编译就会报错。

使用特权

评论回复
9
EDA设计爱好者|  楼主 | 2018-8-28 12:20 | 只看该作者
步骤六:
通过以上几步,所有的文件都弄齐了,但是编译一定会出现报错,注释或删
除掉 config.h 文件中的如下几行就能编译通过:
#include <inttypes.h>
#include <avr\io.h>
#include <avr\interrupt.h>
#include <avr/pgmspace.h>
#include <avr\sleep.h>
#include <avr\wdt.h>
如果还有其他报错,那有可能是因为不同源码版本、不同平台、不同人遇到
的错误也会不相同,这里的过程只能做一定的参考,不一定完全相同,解决这
些错误需要有一定的调试功底,需要根据编译出错提示来进行修改对应地方,
一般都是有些函数没声明或者某个头文件没有包含或者包含了一些不必要的头
文件而该文件不存在或者是一些变量类型不符合需定义之类的,如果能够摆平
所有的编译出错,那么移植就算成功了,如果你被编译出错摆平了,那么游戏
就结束,没得玩了。

使用特权

评论回复
10
EDA设计爱好者|  楼主 | 2018-8-28 12:21 | 只看该作者
步骤七:
解决了所有的编译错误后,接下来实现刚才定义的 3 个空函数,函数 void
setTimer(TIMEVAL value)主要被源码用来定时的,时间到了就需要调用一下函
数 TimeDispatch(),函数 TIMEVAL getElapsedTime(void)主要被源码用来查询距
离下一个定时触发还有多少时间,unsigned char canSend(CAN_PORT notused,
Message *m)函数主要被源码用来发一个 CAN 包的,需要调用驱动来将一个 CAN
包发出去。

使用特权

评论回复
11
EDA设计爱好者|  楼主 | 2018-8-28 12:21 | 只看该作者
我们在 stm32_canfestival.c 文件里定义几个变量如下:
unsigned int TimeCNT=0;//时间计数
unsigned int NextTime=0;//下一次触发时间计数
unsigned int TIMER_MAX_COUNT=70000;//最大时间计数
static TIMEVAL last_time_set = TIMEVAL_MAX;//上一次的时间计数
setTimer 和 getElapsedTime 函数实现如下:
//Set the next alarm //
void setTimer(TIMEVAL value)
{
NextTime=(TimeCNT+value)%TIMER_MAX_COUNT;
}
// Get the elapsed time since the last occured alarm //
TIMEVAL getElapsedTime(void)
{
int ret=0;
ret = TimeCNT> last_time_set ? TimeCNT - last_time_set : TimeCNT +
TIMER_MAX_COUNT - last_time_set;
last_time_set = TimeCNT;
return ret;
}

使用特权

评论回复
12
EDA设计爱好者|  楼主 | 2018-8-28 12:22 | 只看该作者
另外还要开一个 1 毫秒的定时器,每 1 毫秒调用一下下面这个函数。
void timerForCan(void)
{
TimeCNT++;
if (TimeCNT>=TIMER_MAX_COUNT)
{
TimeCNT=0;
}
if (TimeCNT==NextTime)
{
TimeDispatch();
}
}

使用特权

评论回复
13
EDA设计爱好者|  楼主 | 2018-8-28 12:22 | 只看该作者
can 发包函数 canSend 跟 CAN 驱动有关,CAN 通道可以使用真实的 CAN 总线,
也可以使用虚拟的 CAN 通道(如文件接口、网络通道等等)。

使用特权

评论回复
14
EDA设计爱好者|  楼主 | 2018-8-28 12:23 | 只看该作者
CANopen 移植程序熟悉性测试
启动时初始化:
在初始化的文件里(比如 main.c)添加以下几行代码
#include "TestSlave.h"
unsigned char nodeID=0x21;
extern CO_Data TestSlave_Data;
在调用函数(比如 main 函数)里调用以下代码初始化
setNodeId(&TestSlave_Data, nodeID);
setState(&TestSlave_Data, Initialisation); // Init the state
其中 TestSlave_Data 在 TestSlave.c 中定义
然后开启调用 TimerForCan()的 1 毫秒定时器,在接收 CAN 数据那里调用一下源
码函数 canDispatch(&TestSlave_Data, &m);
canfestival 源码就可以跑了,如果需要跟主设备联调,还要实现 canSend 函数,
这个与平台的 Can 驱动相关。

使用特权

评论回复
15
EDA设计爱好者|  楼主 | 2018-8-28 12:23 | 只看该作者
Stm32 平台下的驱动实现:
开启一个 1 毫秒定时器,可参考如下代码,调用一下函数 TIM4_start();即可:
/* TIM4 configure */
static void TIM4_Configuration(void)
{
/* 时钟及分频设置 */
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* Time Base configuration */
/* 72M / 72 = 1us */
// 这个就是预分频系数,当由于为 0 时表示不分频所以要减 1
TIM_TimeBaseStructure.TIM_Prescaler =72-1; //72000 - 1;
//计数模式:向上计数
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
//这个就是自动装载的计数值,由于计数是从 0 开始的
//TIM_TimeBaseStructure.TIM_Period =0xffff;//
TIM_TimeBaseStructure.TIM_Period =0x03e8;//1ms
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//重新计数的起始值
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
// TIM IT enable
TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE); //打开溢出中断
// TIM enable counter
TIM_Cmd(TIM4, ENABLE);//计数器使能,开始工作
}

使用特权

评论回复
16
EDA设计爱好者|  楼主 | 2018-8-28 12:24 | 只看该作者
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
/* Enable the TIM4 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

使用特权

评论回复
17
EDA设计爱好者|  楼主 | 2018-8-28 12:24 | 只看该作者
static void RCC_Configuration(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/* TIM4 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/* clock enable */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA ,ENABLE);
}

使用特权

评论回复
18
EDA设计爱好者|  楼主 | 2018-8-28 12:24 | 只看该作者
void TIM4_start(void)
{
RCC_Configuration();
/* configure TIM4 for remote and encoder */
NVIC_Configuration();
TIM4_Configuration();
}

使用特权

评论回复
19
EDA设计爱好者|  楼主 | 2018-8-28 12:25 | 只看该作者
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_CC1) != RESET)
{
//printf("enter tim4");
TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);
}
TimerForCan();
}

使用特权

评论回复
20
EDA设计爱好者|  楼主 | 2018-8-28 12:25 | 只看该作者
canSend 函数实现如下:
unsigned char canSend(CAN_PORT notused, Message *m)
{
uint32_t i;
CanTxMsg *ptx_msg=&TxMessage;
ptx_msg->StdId = m->cob_id;
if(m->rtr)
ptx_msg->RTR = CAN_RTR_REMOTE;
else
ptx_msg->RTR = CAN_RTR_DATA;
ptx_msg->IDE = CAN_ID_STD;
ptx_msg->DLC = m->len;
for(i = 0; i < m->len; i++)
ptx_msg->Data = m->data;
if( CAN_Transmit( CAN1, ptx_msg )==CAN_NO_MB)
{
return 0xff;
}
else
{
return 0x00;
}
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

245

帖子

1

粉丝