【零基础教程】STM32通过CAN通信驱动Maxon电机 第四章:CAN通信收发与信号过滤

零基础 STM32通过CAN通信驱动Maxon电机

第四章 STM32的CAN通信收发与信号过滤


文章目录

  • 零基础 STM32通过CAN通信驱动Maxon电机
  • 一、Maxon电机CAN通信收发信号
  • 1.回顾Maxon的CAN通信指令
  • 2.接收EPOS返回信号并分析
  • 二、配置CAN通信过滤器
  • 三、封装函数及收发
  • 1.STM32 CAN通信接收报文
  • 2.封装及调用
  • 3.STM32的进制转换问题
  • 4.运行
  • 总结

  • 一、Maxon电机CAN通信收发信号

    1.回顾Maxon的CAN通信指令

    在Maxon的CAN通信指令中所包含的内容如下图所示。


    CAN-ID为601为发送指令,CAN-ID为581则为接收指令。Byte0使用0x22或0x23都可以,代码中出现的0x2B等可以替换为0x22或0x23(请咨询代理商)。Byte1-3位控制类,参照技术手册。Byte4-7位为16进制发送数据值。

    在编写指令时请注意,CAN通信指令是倒着输入的。例如控制类0x60FF-00,输入时Byte1=FF , Byte2=60 , Byte3=00。再比如输入数据值为10000,对应十六进制数为00002710,则Byte4-7 分别为 10 27 00 00;输入数值为100000,对应十六进制数为000186a0,则Byte4-7 分别为 a0 86 01 00。

    本项目中定义发送数据指令为can_send_msg,具体定义请参考can.c。can_send_msg第一项为CAN-ID,第二项指令,第三项指令长度。

    // 发送指令 601 2F 60 60 00 01
    uint8_t canbuf1[]={0x2F,0x60,0x60,0x00,0x01};
    res = can_send_msg(0X601, canbuf1, 5);
    

    2.接收EPOS返回信号并分析

    在当前实验中,EPOS在成功接收所有CAN-ID为0x601的指令时,实际上都会返回一条(一些)指令,如下图所示。


    STM32f103的CAN外设共有2个接收FIFO,每个FIFO都可以存放3个完整的报文,FIFO存储器遵循先入先出的规则。所以若在发送指令后不进行收指令操作,那么如果后续有需要接收指令,则指令将会遵守先入先出的规则接收。这会导致接收的指令不是当前发送指令对应的返回指令。

    以上图为例,86行发送指令0x601 0x40 0x64 0x60 0x00代表的含义是查询当前位置,87行返回指令八位中的后四位代表返回的位置数据(遵循1.1当中的规则)。实际操作中,若之前并未接受指令且未清空寄存器,那么接收到的数据将会是83行返回的指令,而非87行代表返回位置数据的返回的指令。

    同时,EPOS返回的指令并非全部都是CAN-ID为0x581代表的可用数据,还存在其他CAN-ID的数据,这些数据对控制电机没有意义,需要对其进行过滤。

    综上所述,接收EPOS返回信号需要进行1.发送一条消息便收一条消息(或清空FIFO或覆盖指令)2.过滤返回信号。

    二、配置CAN通信过滤器

    有关于CAN通信过滤器的知识,可以到大佬的帖子仔细学习,本文不再赘述。

    STM32 CAN过滤器 http://t.csdnimg.cn/3boRx

    本文仅针对EPOS返回信号进行CAN通信过滤器的设计。

    目的:仅接收标准标识符为0x581的报文(无扩展标识符),其余过滤。

      /*配置CAN过滤器*/
      sFilterConfig.FilterBank = 0;                   
      sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST;    
      sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;  
      sFilterConfig.FilterIdHigh = 0x0000;                 
      sFilterConfig.FilterIdLow = 0x581<<5;
      sFilterConfig.FilterMaskIdHigh = 0x0000;              
      sFilterConfig.FilterMaskIdLow = 0xFFFF;
      sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;   
      sFilterConfig.FilterActivation = ENABLE;     
      sFilterConfig.SlaveStartFilterBank = 14;
    

    以上配置在can.c当中完成,通过以上配置,STM32的FIFO0存储器仅接收标准标识符为0x581的报文。

    三、封装函数及收发

    1.STM32 CAN通信接收报文

    在STM32中,通过can.c中的can_receive_msg实现接收报文。

    uint16_t can_receive_msg(uint32_t id, uint8_t *buf)
    {
      if (HAL_CAN_GetRxFifoFillLevel(&g_canx_handler, CAN_RX_FIFO0) == 0)  
      {
    		printf("no recv");
    		return 0;
      }
    
      if (HAL_CAN_GetRxMessage(&g_canx_handler, CAN_RX_FIFO0, &g_canx_rxheader, buf) != HAL_OK) 
      {
    		printf("read recv err");
    		return 0;
      }
      
      if (g_canx_rxheader.StdId!= id || g_canx_rxheader.IDE != CAN_ID_STD || g_canx_rxheader.RTR != CAN_RTR_DATA)       
      {
    		printf("data err");
    		return 0;  		
      }
    
      return g_canx_rxheader.StdId;
    }
    

    值得注意的是,在调试过程中需要返回接收报文的标准标识符,由于EPOS的有效返回标准标识符为0x581,所以需要将can_receive_msg的类型修改为uint16_t。

    调用can_receive_msg输入的参数包括CAN-ID和返回报文存储位置(指针)。

    2.封装及调用

    在本项目中,在can.c中将EPOS指令进行封装方便调用。
    已有的函数包括:

    void can_run(void); //激活操作模式
    uint8_t can_run_outline_speed(void);//激活轮廓速度模式
    uint8_t can_set_outline_speed(int speed);//设置轮廓速度为speed
    uint8_t can_exit(void);//去使能
    uint8_t can_enable(void);//使能
    uint8_t can_set_position(int position);//设置目标位置为position
    uint8_t can_run_imm(void);//运行(相对立即)
    uint8_t can_read_pos(void);//查询当前位置(请求)
    int can_recv_pos(void);//当前位置的值(返回)
    

    需要注意的是,在同一个封装函数当中同时调用can_send_msg和can_receive_msg可能会出现:接收不到本次发送报文的返回报文(可在函数外接收)的问题。

    3.STM32的进制转换问题

    网上有很多16进制转换十进制的解决方案,可能并不都适合STM32的uint数据类型。通过按位与移位操作,可以实现十六进制与十进制之间的转换。

    //通过移位和按位与操作实现将speed转换为十六进制数,并按规则写入canbufotlspd的后四位
    uint8_t can_set_outline_speed(int speed)
    {
    	uint16_t res=1;
    	uint8_t canbufotlspd[]={0x23,0x81,0x60,0x00,0x00,0x00,0x00,0x00};
    	canbufotlspd[7] = (uint8_t)((speed >> 24) & 0xFF); 
    	canbufotlspd[6] = (uint8_t)((speed >> 16) & 0xFF);  
    	canbufotlspd[5] = (uint8_t)((speed >> 8) & 0xFF);  
    	canbufotlspd[4] = (uint8_t)(speed & 0xFF);
    	res=can_send_msg(0X601, canbufotlspd, 8);
    	if (res==0)
    	{
    		printf("can set otl spd\r\n");
    		return 1;
    	}
    	return 0;
    }
    
    //通过移位和按位与操作实现将接收位置报文后四位转换为position十进制数
    int can_recv_pos(void)
    {
    	uint16_t res=1;
    	int position=0;
    	uint8_t read_position[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    	res=can_receive_msg(0X581, read_position);
    	if (res==0X581 && read_position[0]==0x43 && read_position[1]==0x64 && read_position[2]==0x60 && read_position[3]==0x00)
    	{
    		position=(int)(read_position[4] & 0xFF)+(int)(read_position[5] & 0xFF)*256+(int)(read_position[6] & 0xFF)*256*256+(int)(read_position[6] & 0xFF)*256*256*256;
    		printf("can read_recv_pos\r\n");
    		return position;
    	}
    	return 0;
    }
    

    4.运行

    按照下图的步骤发送报文,并逐步接收报文,验证封装函数正确性,实验完成。

    总结

    本章主要讲STM32实现CAN通信收发相关的内容,目前能够实现正确控制电机并读取电机位置。其他类型函数的封装类似。目前暂未封装位置回零功能(电机不动),后续可封装home函数实现软件回零。

    本章项目链接:https://pan.baidu.com/s/12Ip0d-lvgkK8P_3YmvSV_g
    提取码:1234

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【零基础教程】STM32通过CAN通信驱动Maxon电机 第四章:CAN通信收发与信号过滤

    发表评论