简单直接canfestival CANopen状态切换

本文分析了canfestival中的state.c源码,详细解释了CANopen协议状态切换逻辑,涉及通信对象的启动与停止,以及如何通过s_state_communication结构体管理和控制这些对象。
摘要由CSDN通过智能技术生成

前言

最近一直在做canfestival移植到树莓派pico上的工作。期间,阅读canfestival源码时总能看到一些精彩的代码。本人是大龄嵌入式工作者,记性不太好,而且由于移植工作一直没有完成,一直没有新的文章发出来,所以想着开个专栏,把路过的优秀代码记录下来,毕竟优雅永不过时。


一、canfestival简介

canfestival是一个免费而且开源的CANopen协议栈,工作中需要进阶了解CANopen协议,可以深入阅读CanFestival源码。

二、canfestival状态切换state.c源码

satetes.c实现了CANopen协议状态切换逻辑,以及在不同状态下调用对应的CANopen通信对象功能。CiA DS 301中对CANopen状态切换如下图所示:

canopen装填切换
在不同状态下,需要实现的CANopen通信对象如下图所示:

通信对象

2.1 state.c源码

代码如下:

/*
This file is part of CanFestival, a library implementing CanOpen Stack. 

Copyright (C): Edouard TISSERANT and Francis DUPIN

See COPYING file for copyrights details.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*!
** @file   states.c
** @author Edouard TISSERANT and Francis DUPIN
** @date   Tue Jun  5 09:32:32 2007
**
** @brief
**
**
*/

#include "data.h"
#include "sysdep.h"

/** Prototypes for internals functions */
/*!                                                                                                
**                                                                                                 
**                                                                                                 
** @param d                                                                                        
** @param newCommunicationState                                                                    
**/     
void switchCommunicationState(CO_Data* d, 
	s_state_communication *newCommunicationState);

/*!                                                                                                
**                                                                                                 
**                                                                                                 
** @param d                                                                                        
** @param m                                                                                        
**/  

#define StartOrStop(CommType, FuncStart, FuncStop) \
	if(newCommunicationState->CommType && d->CurrentCommunicationState.CommType == 0){\
		MSG_WAR(0x9999,#FuncStart, 9999);\
		d->CurrentCommunicationState.CommType = 1;\
		FuncStart;\
	}else if(!newCommunicationState->CommType && d->CurrentCommunicationState.CommType == 1){\
		MSG_WAR(0x9999,#FuncStop, 9999);\
		d->CurrentCommunicationState.CommType = 0;\
		FuncStop;\
	}
#define None

/*!                                                                                                
**                                                                                                 
**                                                                                                 
** @param d                                                                                        
** @param newCommunicationState                                                                    
**/  	
void switchCommunicationState(CO_Data* d, s_state_communication *newCommunicationState)
{
#ifdef CO_ENABLE_LSS
	StartOrStop(csLSS,	startLSS(d),	stopLSS(d))
#endif
	StartOrStop(csSDO,	None,		resetSDO(d))
	StartOrStop(csSYNC,	startSYNC(d),		stopSYNC(d))
	StartOrStop(csLifeGuard,	lifeGuardInit(d),	lifeGuardStop(d))
	StartOrStop(csEmergency,	emergencyInit(d),	emergencyStop(d)) 
	StartOrStop(csPDO,	PDOInit(d),	PDOStop(d))
	StartOrStop(csBoot_Up,	None,	slaveSendBootUp(d))
}

/*!                                                                                                
**                                                                                                 
**                                                                                                 
** @param d                                                                                        
** @param newState                                                                                 
**                                                                                                 
** @return                                                                                         
**/  
UNS8 setState(CO_Data* d, e_nodeState newState)
{
	if(newState != d->nodeState){
		switch( newState ){
			case Initialisation:
			{
				s_state_communication newCommunicationState = {1, 0, 0, 0, 0, 0, 0};
				d->nodeState = Initialisation;
				switchCommunicationState(d, &newCommunicationState);
				/* call user app init callback now. */
				/* d->initialisation MUST NOT CALL SetState */
				(*d->initialisation)(d);				
			}

			/* Automatic transition - No break statement ! */
			/* Transition from Initialisation to Pre_operational */
			/* is automatic as defined in DS301. */
			/* App don't have to call SetState(d, Pre_operational) */
								
			case Pre_operational:
			{
				
				s_state_communication newCommunicationState = {0, 1, 1, 1, 1, 0, 1};
				d->nodeState = Pre_operational;
				switchCommunicationState(d, &newCommunicationState);
                (*d->preOperational)(d);
			}
			break;
								
			case Operational:
			if(d->nodeState == Initialisation) return 0xFF;
			{
				s_state_communication newCommunicationState = {0, 1, 1, 1, 1, 1, 0};
				d->nodeState = Operational;
				newState = Operational;
				switchCommunicationState(d, &newCommunicationState);
				(*d->operational)(d);
			}
			break;
						
			case Stopped:
			if(d->nodeState == Initialisation) return 0xFF;
			{
				s_state_communication newCommunicationState = {0, 0, 0, 0, 1, 0, 1};
				d->nodeState = Stopped;
				newState = Stopped;
				switchCommunicationState(d, &newCommunicationState);
				(*d->stopped)(d);
			}
			break;
			default:
				return 0xFF;

		}/* end switch case */
	
	}
	/* d->nodeState contains the final state */
	/* may not be the requested state */
    return d->nodeState;  
}

2.1 state.c源码解析:

文章中截出的代码段实现了CANopen状态切设置函数,通过函数 UNS8 setState (CO_Data d, e_nodeState newState)*实现切换到任意状态,以及启动相应通信对象功能。函数原型和参数说明如下:

/** 
 * @ingroup statemachine
 * @brief Change the state of the node 
 * @param *d Pointer on a CAN object data structure
 * @param newState The state to assign
 * @return 
 */
UNS8 setState (CO_Data* d, e_nodeState newState);
  1. UNS8 setState (CO_Data d, e_nodeState newState)*函数有两个参数:
    参数 *d 是指向CAN数据结构体;
    参数newState 需要进入状态的枚举类型。

这段代码值得欣赏的部分有以下部分:

1. s_state_communication 结构体

2. #define StartOrStop(CommType, FuncStart, FuncStop) 宏

3.void switchCommunicationState(CO_Data* d, s_state_communication *newCommunicationState)函数

接下来详细说明以上几段代码为什么值得认真欣赏。

  • s_state_communication 结构体 详细介绍
typedef struct
{
  INTEGER8 csBoot_Up;
  INTEGER8 csSDO;
  INTEGER8 csEmergency;
  INTEGER8 csSYNC;
  INTEGER8 csLifeGuard;
  INTEGER8 csPDO;
  INTEGER8 csLSS;
} s_state_communication;

结构体种每个元素代表一种Canopen 通信对象开关功能,值1代表通信对象start功能,值0代表通信对象stop功能。

  • #define StartOrStop(CommType, FuncStart, FuncStop) 宏
#define StartOrStop(CommType, FuncStart, FuncStop) \
	if(newCommunicationState->CommType && d->CurrentCommunicationState.CommType == 0){\
		MSG_WAR(0x9999,#FuncStart, 9999);\
		d->CurrentCommunicationState.CommType = 1;\
		FuncStart;\
	}else if(!newCommunicationState->CommType && d->CurrentCommunicationState.CommType == 1){\
		MSG_WAR(0x9999,#FuncStop, 9999);\
		d->CurrentCommunicationState.CommType = 0;\
		FuncStop;\
	}
#define None

StartOrStop宏有三个参数,第一个参数是s_state_communication结构体元素,第二个参数为通信对象start功能,第三个参数为通信对象stop功能。

  • void switchCommunicationState(CO_Data* d, s_state_communication *newCommunicationState)函数
void switchCommunicationState(CO_Data* d, s_state_communication *newCommunicationState)
{
#ifdef CO_ENABLE_LSS
   StartOrStop(csLSS,	startLSS(d),	stopLSS(d))
#endif
   StartOrStop(csSDO,	None,		resetSDO(d))
   StartOrStop(csSYNC,	startSYNC(d),		stopSYNC(d))
   StartOrStop(csLifeGuard,	lifeGuardInit(d),	lifeGuardStop(d))
   StartOrStop(csEmergency,	emergencyInit(d),	emergencyStop(d)) 
   StartOrStop(csPDO,	PDOInit(d),	PDOStop(d))
   StartOrStop(csBoot_Up,	None,	slaveSendBootUp(d))
}

这个函数功能是根据newCommunicationState,实现通信对象的stop和start功能,函数调用了StartOrStop宏7次,代表下图中的7种通信对象:
在这里插入图片描述
以上部分的调用:


//声明s_state_communication结构体的变量newCommunicationState,
//并且初始{1, 0, 0, 0, 0, 0, 0},代表csBoot_Up通信对象执行start功能,剩余执行stop功能
   s_state_communication newCommunicationState = {1, 0, 0, 0, 0, 0, 0};

// 调用switchCommunicationState函数,传入声明的newCommunicationState
   switchCommunicationState(d, &newCommunicationState);
   
//switchCommunicationState函数中调用StartOrStop宏
   StartOrStop(csSDO,	None,		resetSDO(d))
   StartOrStop(csSYNC,	startSYNC(d),		stopSYNC(d))
   StartOrStop(csLifeGuard,	lifeGuardInit(d),	lifeGuardStop(d))
   StartOrStop(csEmergency,	emergencyInit(d),	emergencyStop(d)) 
   StartOrStop(csPDO,	PDOInit(d),	PDOStop(d))
   StartOrStop(csBoot_Up,	None,	slaveSendBootUp(d))
  • UNS8 setState(CO_Data* d, e_nodeState newState)函数
    通过传入枚举状态,然后在不同状态初始化不同的*newCommunicationState *,最终实现在不同状态控制相应的通信对象功能。

2.总结

这段代码区别QEP架构的另外一种状态机切换形式,一种可以手动编写状态机的代码架构。

后记

第一次写这种类型文章,准备不够充分,有些不正确的地方欢迎评论,如果认为有价值的地方欢迎关注、点赞、收藏三连。

关于优秀代码记录的系列,后面会一直做下去,坚持就是胜利,量变到质变。

  • 47
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于CAN Festival的CANopen主站开发,包含CiA402,可以实现对CANopen网络中的设备进行控制和通信。 首先,CAN Festival是一个用于CANopen协议开发的开源软件库,提供了一系列的API和功能模块,方便开发者进行CANopen相关的应用开发。CANopen协议是一种用于工业控制系统中的通信协议,通过CAN总线实现设备之间的通信和控制。 在开发基于CAN Festival的CANopen主站时,需要完成以下几个主要步骤: 1. 初始化CAN总线:使用CAN Festival提供的API,初始化CAN总线的参数,包括波特率、帧格式等。这样CANopen主站就能够与CAN总线进行通信。 2. 扫描CANopen网络中的设备:使用CAN Festival的扫描功能,主动发送查询报文,获取CANopen网络中的所有设备信息,如设备ID、设备类型等。这样主站就能够识别出网络中的设备,并建立与其的通信连接。 3. 实现SDO和PDO通信:使用CAN Festival提供的SDO和PDO功能模块,通过CAN总线实现主站与设备之间的数据传输。SDO(服务数据对象)用于对设备进行参数配置和数据读写操作,而PDO(过程数据对象)用于实时数据交换。 4. 实现CiA402功能:根据CiA402设备配置文件,配置主站与CiA402设备之间的通信和控制参数。CiA402是CANopen标准中用于控制伺服驱动器和电机的设备配置文件,主站通过SDO和PDO通信实现对驱动器参数的读写和控制。 5. 开发应用逻辑:根据具体需求,开发主站的应用逻辑。例如,通过CANopen网络控制伺服电机的转速、位置等。 总之,基于CAN Festival的CANopen主站开发涉及CAN总线的初始化、设备的扫描、SDO和PDO通信,以及CiA402的功能实现。通过这些步骤,可以实现对CANopen网络中的设备进行控制和通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值