基于STM32F407的CanOpen(CIA402)主机移植记录

本文主要讲解对象字典配置与使用关系

一、移植前准备

        按照网上CanFestival的移植将底层移植好即可。

二、对象字典生成工具使用

      

按其它教程安装好objdictedit, 打开objdictedit.py ,新建工程。

 主要看几个地方

0x1000-0x1029主要是通信参数,配置同步、心跳、节点守护等。

0x1200-0x12FF配置的是主机/从机的SDO参数,我们使用的是主机,因此要配置从机(客户端)的SDO,如下图所示,我配置了7个客户端,每一个配置有TSDO的COB ID、RSDO的COB ID和节点号,例如SDO1号配置TSDO的COB ID为0X601,RSDO的COB ID为0X581,节点号为1,那么SDO2号配置TSDO的COB ID为0X602,RSDO的COB ID为0X582,节点号为2,因此在使用SDO进行通信的时候,CAN主机发送的ID为0x60x,从机返回的ID为0x58x,x代表了几号从设备。

 0x1400-0x15FF为RPDO的参数配置,下图为我配置好的参数,RPDO1号的COB ID为0x181,同步类型为非同步,inhibit time抑制时间为0,Compatibity Entry兼容性条目为0,Event Timer 事件时间为0,SYNC start value 同步开始值为0,以上为0均为非使能,具体参数意义大家自己查阅,因为我也一知半懂。。。。。

其余的RPDO按如此配置,COB ID为0x18x即可。

 0x1600-0X17FF是RPDO的参数映射,我当前控制的是电机设备,遵循CIA402的协议。

当前映射了状态字、实际速度、错误码三个值,状态字占用16位,实际速度占用32位,错误码占用16位,总共64位数据,根据CAN2.0的协议,数据位不超过64位即8个字节,因此我们最多配置8个字节在这个映射上,如果要配置多个参数,暂时还未找到解决方法。

配置的值例如status_word的地址是0x2004,这个0x2004是自己添加的一个参数,比如状态字的原始词典(CIA402)是0x6041,在代码中对0X6041通过SDO写入到从机的TXPDO上,后续有说明,这里只是定义了主机接收到0X181这个ID的数据代表的含义,这边配置的是8个字节,前两个字节代表状态字,中间四个字节代表了实际速度,最后两个字节代表的是错误码。

其它的RPDO也如此配置。

 0X1800-0X19FF配置TPDO的参数,和0X1400差不多,COB ID配置0x201,同步类型配置为1,循环同步,这块建议配置为循环同步,循环同步的话会自动循环发送该帧,非同步则会在数值发生改变的时候发生该帧一次,若掉包则会接收不到。

其余TPDO也如此配置,0x20x。

 0X1A00-0X1BFF为TPDO的参数映射,和0X1600类似,这边配置映射值都是控制相关的,例如电机模式,控制字,速度值等。

 0X2000-0X5FFF代表用户自定义参数

根据0X1600和0X1A00我们配置的值都是从这里配置后选择的,理论上这个软件配置在配置完0x1000-0x1029之后就先配置0x2000的参数,这样后续才能选择映射的参数。

如下图,我配置了7个电机的参数,其中参数有ctrl_word、celocity1、motor_mode、velocity_act1、status_word、err_word,这些都是第一个电机的参数,所有其它电机都以此为例创建了各自参数。

参数如何建立,点击左下方的添加(地图变量),弹窗后指标代表的是映射地址,从0x2000开始,0x5FFF结束,类型默认VAR,名字按照自己需要填写,例如ctrl_word,确定之后就创立了一个ctrl_word的映射变量,点击该变量,选择类型,UNSIGNED16代表是无符号16位,INTEGER16代表的是有符号16位,根据自己的参数配置好就行。值可改变,改变后生成的代码会初始化该值,其它参数以此类推。

 至此,对象词典就配置完成,点击左上角文件、建立词典,生成的C文件和H文件就可以直接拿来用。

三、代码的调用

        1.生成的代码部分展示

                

         通过对象词典创建的0x2000-0x5FFF变量,自动生成出来的值,如果改变了变量初始值,则会在定义的时候自动生成,例如motor_mode = 0x3,是自动生成的。

在0x1280的配置中,我将UNS8 master_obj1280_Node_ID_of_the_SDO_Server = 0x1;改为了const格式,因为在使用中,master_obj1280_Node_ID_of_the_SDO_Server会变成0(未知原因),因此我将它配置为const类型。

        2.CANOPEN初始化

        2.1CANOPEN的发送的问题

        对于CANOPEN底层配置就不多赘述,关于CAN发送这块,由于同步发送的原因,导致了CAN在发送数据时较快,部分同步数据发送失败(可能是软件底层的原因),因此我在CAN发送函数后添加了一段延时,保证每次数据都发送完毕,实测通过,未造成阻塞。

unsigned char canSend(CAN_PORT notused, Message *m)
{
  uint8_t i;
  static CanTxMsg TxMsg;
	TxMsg.StdId = m->cob_id;

  if(m->rtr)
      TxMsg.RTR = CAN_RTR_REMOTE;
  else
      TxMsg.RTR = CAN_RTR_DATA;

  TxMsg.IDE = CAN_ID_STD;
  TxMsg.DLC = m->len;

  for(i=0; i<m->len; i++)
       TxMsg.Data[i] = m->data[i];
  CAN_Transmit(CAN1, &TxMsg) ;
  DrvTimer_DelayUs(60);		// 延迟等待发送完毕,不然会掉包,延迟时间与PDO数量有关,延迟不要太长

  
  return 0;
}

         2.2 初始化配置

unsigned char nodeID = 0x00;                   //节点ID
	int i = 0;
	
	// 初始自身化节点
	setNodeId(&master_Data, nodeID);
	setState(&master_Data, Initialisation);
	for(i = 1; i < NMT_MAX_NODE_ID;i++)
		master_Data.NMTable[i] = Unknown_state;
	setState(&master_Data, Operational); 
	while(master_Data.nodeState != Operational)
	{
		vTaskDelay(50);
	}
	vTaskDelay(1000);
	stopSYNC(&master_Data);
	for( i = 1; i < 5; i++)
	{
		if( master_Data.NMTable[i] != Unknown_state)
			slave_num++;
	}
	CANOPEN_DEBUG("slave count %d\r\n",slave_num);
	for(i = 1; i < 5;i++)
	{
		// PDO映射初始化
		DevCANOpen_SDO_Init(i);   
	}
	vTaskDelay(1000);	
	startSYNC(&master_Data);   
	for(i = 1; i < 5;i++)
	{
		masterSendNMTstateChange(&master_Data, i, NMT_Reset_Node );
		masterSendNMTstateChange(&master_Data, i, NMT_Start_Node );  
	}

 这里初始化了1-4号电机,ID从1开始而不是从0开始。

 主要的配置为SDO_Init的初始化,以下代码是SDO的初始化,这些也是参考的网上进行修改的,第一个SDO写值为恢复默认参数。

第二到第五则是用SDO配置电机内部参数,0x600+id是SDO写ID,0x23为数据长度是4,0x83 0x60为0x6083的寄存器(CIA402),0X00代表0x6083的内部索引是0,写的值0x0f4240=1000000。

写映射参数,从第五个开始,{0x600+id,0x23,0x00,0x16,0x01,0x08,0x00,0x60,0x60},0x600+id代表COB ID,0x23代表数据长度为4,0x00,0x16为0x1600,配置从机的RPDO,0x01代表第一个索引,0x08,0x00,代表0x0008,表示8位,0x0010代表16位,0x60,0x60代表0x6060寄存器,表示控制模式。

依次到第七行写入了三个参数,控制模式、控制字、控制速度,第八行{0x600+id,0x2F,0x00,0x16,0x00,0x03,0x00,0x00,0x00},

0x600+id代表COB ID,0x2F代表数据长度为1,0x00,0x16为0x1600,配置从机的RPDO,0x00表示索引0,0x03表示写入3个映射参数,即第五行、第六行、第七行三个参数。

后续面有注释,RPDO以此类推。

void DevCANOpen_SDO_Init(INT8U id)
{
	 uint16_t Init_sdo[][9]= // 主站(单片机),恢复从站的初始值 
    {   
        {0x600+id,0x23,0x11,0x10,0x01,0x6C,0x6F,0x61,0x64},  //  【写SDO 1011.01,64616F6C】恢复默认值的写入暗号 
        {0x600+id,0x23,0x81,0x60,0x00,0x20,0xA1,0x07,0x00},   // 写协议速度 	6081 500000 0x07a120
        {0x600+id,0x23,0x83,0x60,0x00,0x40,0x42,0x0f,0x00},   // 写最大加速度 	6083 1000000 0x0f4240
        {0x600+id,0x23,0x84,0x60,0x00,0x40,0x42,0x0f,0x00},   // 写最大减速度 	6084 1000000 0x0f4240
        {0x600+id,0x2B,0x17,0x10,0x00,0xf4,0x01,0x00,0x00},   // 写最大减速度 	6084 1000000 0x0f4240
    };
	
    for (int i=0;i<5;i++)
    {
        DevCANOpen_send_sdo(Init_sdo[i]);
        vTaskDelay(20); 
    }
	 uint16_t message_sdo[][9]=
    {
			// 写参数映射
			{0x600+id,0x23,0x00,0x14,0x01,id,0x02,0x00,0x80},  // 失能pdo: 发送sdo  600+id,22 00 14 01 01 02 00 80
			{0x600+id,0x2F,0x00,0x14,0x02,0x01,0x00,0x00,0x00},    //②Hex1400_02,循环同步,同步方式参数2
			{0x600+id,0x2B,0x00,0x14,0x03,0x20,0x00,0x00,0x00},    //③Hex1400_03,Inhibit time是0;   
		
			{0x600+id,0x2F,0x00,0x16,0x00,0x00,0x00,0x00,0x00},  // 消去个数: 发送sdo 600+id,22 00 16 00 00 00 00 00
			{0x600+id,0x23,0x00,0x16,0x01,0x08,0x00,0x60,0x60},  // 写入参数: 发送sdo 600+id,22 00 16 01 20 00 ff 60
			{0x600+id,0x23,0x00,0x16,0x02,0x10,0x00,0x40,0x60},
			{0x600+id,0x23,0x00,0x16,0x03,0x20,0x00,0xFF,0x60},
			{0x600+id,0x2F,0x00,0x16,0x00,0x03,0x00,0x00,0x00},    //⑥Hex1600_0,启用【映射参数】 【实际映射多少组】 
			{0x600+id,0x23,0x00,0x14,0x01,id,0x02,0x00,0x00},  // 使能pdo:  发送sdo 600+id,22 00 14 01 01 02 00 00
			// 读参数映射
			{0x600+id,0x23,0x00,0x18,0x01,0x80+id,0x01,0x00,0x80},  // 失能Tpdo: 发送sdo  600+id,22 00 14 01 01 02 00 80
			{0x600+id,0x2F,0x00,0x18,0x02,0x02,0x00,0x00,0x00},    //②Hex1800_02,循环同步,同步方式参数2
			{0x600+id,0x2B,0x00,0x18,0x03,0x00,0x00,0x00,0x00},    //③Hex1800_03,Inhibit time是0;   
			
			{0x600+id,0x2F,0x00,0x1A,0x00,0x00,0x00,0x00,0x00},  // 消去个数: 发送sdo 600+id,22 00 16 00 00 00 00 00
			{0x600+id,0x23,0x00,0x1A,0x01,0x10,0x00,0x41,0x60},  // 写入参数: 发送sdo 600+id,22 00 16 01 20 00 41 60	// 状态字
			{0x600+id,0x23,0x00,0x1A,0x02,0x20,0x00,0x6C,0x60},  // 写入参数: 发送sdo 600+id,22 00 16 01 20 00 6C 60	//速度
			{0x600+id,0x23,0x00,0x1A,0x03,0x10,0x00,0x3F,0x60},  // 写入参数: 发送sdo 600+id,22 00 16 01 20 00 6C 60	//速度
			{0x600+id,0x2F,0x00,0x1A,0x00,0x03,0x00,0x00,0x00},    //⑥Hex1A00_0,启用【映射参数】 【实际映射多少组】 
			{0x600+id,0x23,0x00,0x18,0x01,0x80+id,0x01,0x00,0x00},  // 使能pdo:  发送sdo 600+id,22 00 14 01 01 02 00 00
			// 蕾赛电机写看门狗时间,厂商自定义词典5004.06
			{0x600+id,0x2B,0x04,0x50,0x06,0xF4,0x01,0x00,0x00},  // 蕾赛电机写看门狗时间,【写SDO 5004.06,03e8】写看门狗1000ms

		};

		for (int i=0;i<19;i++)
		{
			DevCANOpen_send_sdo(message_sdo[i]);
		    vTaskDelay(10);
		} 
}

至此所有的参数都配置完毕,主函数调用ctrl_word这个变量即可对电机的状态进行控制,其它的变量也可以进行控制。 

  • 2
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
STM32F407是一款高性能的微控制器芯片,具有丰富的外设资源和强大的处理能力。CANopen是一种基于CAN总线的通信协议,通常用于在工业领域中实现分布式控制系统。 将STM32F407配置为CANopen从站可以实现与其他CANopen节点之间的通信。在将STM32F407配置为CANopen从站之前,我们需要先了解CANopen的协议和通信规则。 配置STM32F407为CANopen从站主要包括以下步骤: 1. 初始化CAN总线:配置CAN模块的工作模式、波特率等参数,使其能够正常工作。 2. 配置从站节点ID:每个CANopen节点都有一个唯一的节点ID,该ID用于在CAN总线上进行节点之间的识别和通信。 3. 实现从站对象字典:从站对象字典是CANopen协议中的核心,它用于存储和访问从站的输入和输出数据。开发者需要根据应用需求,在STM32F407的Flash或RAM中实现对象字典。 4. 实现从站状态机:从站状态机用于处理来自主站的请求和数据,并根据协议规定的状态转换规则执行相应的操作。 5. 实现PDO通信:PDO是CANopen协议中用于实现实时数据传输的一种机制。开发者需要根据应用需求,配置和实现PDO通信。 6. 实现SDO通信:SDO是CANopen协议中用于配置和管理从站对象字典的一种机制。开发者需要根据应用需求,配置和实现SDO通信。 配置完成后,STM32F407作为CANopen从站就可以与其他CANopen节点进行通信。可以通过主站发送PDO或SDO消息来读取和写入从站对象字典中的数据,也可以根据应用需求,自行发送PDO消息给其他节点。 总的来说,将STM32F407配置为CANopen从站需要进行硬件和软件上的配置,并实现从站对象字典、状态机以及PDO和SDO通信等功能,以实现与其他CANopen节点的通信。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值