Ubuntu22.04下c++和python分别实现can通讯

本文详细描述了如何在RaspberryPi4B上使用Ubuntu22.04系统,配合MCP2515模块实现CAN通讯,包括C++代码示例以及Python实现,展示了如何通过CANopen协议控制Faulhaber电机。
摘要由CSDN通过智能技术生成

1. 引言

硬件:raspberry 4B 外挂 mcp2515模块

系统:ubuntu22.04

软件: vscode

语言:c++ 和 python

前面已经修改系统配置文件,成功加载mcp2515模块并通过命令行测试can回环模式loopback成功树莓派(Ubuntu22.04)+MCP2515实现can通讯_树莓派 can-CSDN博客

2. c++实现can通讯控制电机

我用的电机是faulhaber,通讯协议canopen,用的pcan_view分析仪

用c++实现can通讯,需要include对应的can.h头文件进行can收发函数的调用

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <iostream>

using namespace std;

int main()
{
    int s,nbytes;
	struct sockaddr_can addr;
	struct ifreq ifr;
	struct can_frame frame[6]={{0}};
	/*struct can_frame {
			canid_t can_id; //can标识符
		    __u8 can_dlc;  //数据场的长度
			__u8 data[8];  //数据	
	}*/
	//创建socketCAN套接字
	s=socket(PF_CAN, SOCK_RAW, CAN_RAW); 
	//指定can0设备
	strcpy(ifr.ifr_name,"can0");    
	ioctl(s,SIOCGIFINDEX,&ifr);
	addr.can_family = AF_CAN;
	addr.can_ifindex = ifr.ifr_ifindex;
	bind(s,(struct sockaddr*)&addr, sizeof(addr));//将套接字与can0绑定
	 //禁用过滤规则,本进程不接收报文,只负责发送
	setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
	
	
	//NMT上电
	frame[0].can_id = 0x000;   //如果为扩展帧,那么frame.can_id = CAN_EFF_FLAG | 0x11;
	frame[0].can_dlc = 1 ;     //数据长度  
	frame[0].data[0]= 0x01;  //数据内容
	nbytes = write(s, &frame[0], sizeof(frame[0]));  //发送frame[0],发送数据使用write函数实现
		printf("nbytes=%d\n",nbytes);
		if(nbytes != sizeof(frame[0]))  //如果nbytes不等于帧长度,就说明发送失败
		{
			printf("Send Error frame[0]\n!");
		}
	sleep(0.2);  //延时0.2s=200ms
   
    //sdo_controlword_0x06---shut down
	frame[1].can_id = 0x601;
	frame[1].can_dlc = 0x08;
	frame[1].data[0]= 0x2F;
	frame[1].data[1]= 0x60;
	frame[1].data[2]= 0x60;
	frame[1].data[3]= 0x00;
	frame[1].data[4]= 0x01;
	frame[1].data[5]= 0x00;
	frame[1].data[6]= 0x00;
	frame[1].data[7]= 0x00;
	nbytes = write(s, &frame[1], sizeof(frame[1]));  //发送frame[1]
		if(nbytes != sizeof(frame[1]))
		{
			printf("Send Error frame[1]\n!");
		}
	sleep(0.2);

	//sdo_controlword_0x07---switch on
	frame[2].can_id = 0x601;
	frame[2].can_dlc = 0x08;
	frame[2].data[0]= 0x23;
	frame[2].data[1]= 0x7A;
	frame[2].data[2]= 0x60;
	frame[2].data[3]= 0x00;
	frame[2].data[4]= 0xE8;
	frame[2].data[5]= 0x03;
	frame[2].data[6]= 0x00;
	frame[2].data[7]= 0x00;
	nbytes = write(s, &frame[2], sizeof(frame[2]));  //发送frame[1]
		if(nbytes != sizeof(frame[2]))
		{
			printf("Send Error frame[2]\n!");
		}
	sleep(0.2);

	//sdo_OperationMode_0x08---modes of operation----csp
	frame[3].can_id = 0x601;
	frame[3].can_dlc = 0x08;
	frame[3].data[0]= 0x2B;
	frame[3].data[1]= 0x40;
	frame[3].data[2]= 0x60;
	frame[3].data[3]= 0x00;
	frame[3].data[4]= 0x0F;
	frame[3].data[5]= 0x00;
	frame[3].data[6]= 0x00;
	frame[3].data[7]= 0x00;
	nbytes = write(s, &frame[3], sizeof(frame[3]));  //发送frame[1]
		if(nbytes != sizeof(frame[3]))
		{
			printf("Send Error frame[3]\n!");
		}
	sleep(0.2);

	//sdo_controlword_0x0F电机上电初始化
	frame[4].can_id = 0x601;
	frame[4].can_dlc = 0x08;
	frame[4].data[0]= 0x2b;
	frame[4].data[1]= 0x40;
	frame[4].data[2]= 0x60;
	frame[4].data[3]= 0x00;
	frame[4].data[4]= 0x7f;
	frame[4].data[5]= 0x00;
	frame[4].data[6]= 0x00;
	frame[4].data[7]= 0x00;
	nbytes = write(s, &frame[4], sizeof(frame[4]));  //发送frame[1]
		if(nbytes != sizeof(frame[4]))
		{
			printf("Send Error frame[4]\n!");
		}
	sleep(0.2);

	
	close(s);
	return 0;

}

3. python实现can通讯控制电机

过程中也遇到不少问题,参考的一些连接放在这里:

教程1:8. CAN总线通讯 — [野火]Python应用开发实战指南——基于LubanCat-i.MX6ULL-MP157开发板 文档 (embedfire.com)

教程2:使用Python玩转CAN通讯_python实现can通信-CSDN博客

用python实现can通讯,需要import can库才能进行can收发函数的调用

提示:python的标准库默认已经在“自家仓库”里了,直接 import 到项目就可以调用

           python的三方库需要先用命令 pip install 从互联网“搬运到自家仓库”,再 import 到项目才可以调用

python can库为第三方库,因此需要两步:

pip install python-can
""" python can 测试 """
import sys
import time
import threading
import can
import numpy
import canlib
import keyword

'''
def msg_recv(device_x):
    "接收消息功能"
    print("success: msg_recv Thread is running!")
    # 将can_mask转换为二进制形式,can_mask中为1的位,用于过滤接收到的帧
    # 举例                     id: 0 0
    #                        mask: 1 0 则接收到消息的ID中,mask为1对应id中的位,必须与id一致,为0
    # 如接收到了四个id的消息  id1: 0 0 此条消息被放行
    #                         id2: 0 1 此条消息被放行
    #                         id3: 1 0 此条消息被过滤
    #                         id4: 1 1 此条消息被过滤
    # 过滤器配置示例如下。第一条规则,接收所有标准帧,第二条规则,接收拓展帧中id为0x300的消息。
    can_filters = [
        {"can_id": 1, "can_mask": 0x0, "extended": False},
        {"can_id": 0x300, "can_mask": 0x1FFFFFFF, "extended": True},
    ]
    # 应用过滤器配置
    device_x.set_filters(can_filters)
    # 查询退出线程是否退出,如果为真,则说明用户期望程序退出,退出本线程循环,线程结束
    while tasks_quitThread.is_alive():
        try:
            # 接收can消息
            msg = device_x.recv(1)
            if msg is not None:
                print("success: ", msg)
        except can.CanError:
            print("error: 接收消息时出错,请检查设备是否启用及状态正常")
'''


def msg_send(device_x):
    "发送消息功能"
    print("success: msg_send Thread is running!")
    # 构造发送的CAN消息结构,ID为0xC0FFEE,数据内容包含在data中,is_extended_id为拓展ID标识
    msg1 = can.Message(arbitration_id=0x000, data=[0x01], is_extended_id=False)
    msg2 = can.Message(arbitration_id=0x601, data=[0x2F, 0x60, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00], is_extended_id=False)
    msg3 = can.Message(arbitration_id=0x601, data=[0x23, 0x7A, 0x60, 0x00, 0xE8, 0x03, 0x00, 0x00], is_extended_id=False)
    msg4 = can.Message(arbitration_id=0x601, data=[0x2B, 0x40, 0x60, 0x00, 0x0F, 0x00, 0x00, 0x00], is_extended_id=False)
    msg5 = can.Message(arbitration_id=0x601, data=[0x2B, 0x40, 0x60, 0x00, 0x7F, 0x00, 0x00, 0x00], is_extended_id=False)
    # 查询退出线程是否退出,如果为真,则说明用户期望程序退出,退出本线程循环,线程结束
    while tasks_quitThread.is_alive():
        try:
            # 发送构造的CAN消息
            device_x.send(msg1)
            time.sleep(0.2)
            device_x.send(msg2)
            time.sleep(0.2)
            device_x.send(msg3)
            time.sleep(0.2)
            device_x.send(msg4)
            time.sleep(0.2)
            device_x.send(msg5)
            time.sleep(0.2)
            # 打印发送提示
            print(f"success: 消息已发送至 {device_x.channel_info}")
        except can.CanError:
            print("error: 消息发送出错,请检查设备是否启用及状态正常!")
        # 两秒后再次发送,sleep函数的单位为s
        time.sleep(1)


def tasks_quit():
    "程序退出功能"
    print("success: tasks_quit Thread is running!")
    exitright = "e"
    while exitright not in ["q", "Q"]:
        # 获取用户输入,如果为q则退出程序
        exitright = input(
            """
***********************************
**输入字母q后, 按下回车以退出程序**
***********************************
"""
        )
        # 线程退出


# 打印运行程序前提示信息
print(
    "information: 执行本程序前, 请先启用can设备。命令如下:\
    \nsudo ip link set can0 type can bitrate 500000\nsudo ip link set can0 up"
)
# 打开CAN设备,CAN设备类型为socketcan,channel为can0,可使用ifconfig -a命令查看。
with can.interface.Bus(bustype="socketcan", channel="can0", bitrate=500000) as device_can0:
    # 创建线程:监听程序退出线程、发送can消息线程、接收can消息线程
    try:
        # 创建线程
        print("information: 开始创建 tasks_quitThread 线程!")
        tasks_quitThread = threading.Thread(target=tasks_quit, daemon=True)
        print("information: 开始创建 msg_sendThread 线程!")
        msg_sendThread = threading.Thread(target=msg_send, daemon=True, args=(device_can0,))
        # print("information: 开始创建 msg_recvThread 线程!")
        # msg_recvThread = threading.Thread(target=msg_recv, daemon=True, args=(device_can0,))
        # 开启线程
        print("information: 开始启动 tasks_quitThread 线程!")
        tasks_quitThread.start()
        print("information: 开始启动 msg_sendThread 线程!")
        msg_sendThread.start()
        # print("information: 开始启动 msg_recvThread 线程!")
        # msg_recvThread.start()
    except:
        print("error: 创建或启动线程中出错!")
        sys.exit()

    # 等待线程结束
    tasks_quitThread.join()
    print("information: tasks_quitThread结束")
    msg_sendThread.join()
    print("information: msg_sendThread结束")
    # msg_recvThread.join()
    # print("information: msg_recvThread结束")
    # 所有正常线程结束,退出程序
    sys.exit()

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值