喜迎
元旦

安卓can开发


Android CAN 应用开发

在工作时发现我们很多客户对于Android can应用开发没有一点头绪,下面我通过一个简单项目来实现一个can通信demo。

开发准备

确保开发板硬件上有can节点存在,并且确保通信正常 验证方法如下:

一 、使用cantool验证

1.1初始化并设置波特率

ifconfig can0 down

canconfig can0 bitrate 500000 ctrlmode triple-sampling on //默认使用这个初始化

#ip link set can0 type can bitrate 250000 sample-point 0.8 dbitrate 250000 dsample-point 0.75 fd on //有些厂商用的这个初始化

ifconfig can0 up
1.2 发送数据and接收数据
cansend can0 -e 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88
candump can0

确保上面步骤可以正常进行通信

二、实现Android 的jni方法

首先新建一个工程 根据自己工程默认即可 语言选择java

2.1 添加C++模块

工程创建完毕选择app右键选择 Add C++ to Module 创建路径和文件名称自主修改即可

图片

创建完毕会出现一个cpp文件夹 里面会有一个cmakelists和刚刚创建的文件

图片

2.2 添加java方法

接下来我们创建两个java类 一个名字为 CanFrame 一个名字为 CanControl

图片

CanFrame.java内容为:

public class CanFrame {
    public  int can_id;
    public  char can_dlc;
    public byte[] data;
}

CanControl.java内容为:public class CanControl {

    static {
        System.loadLibrary("myapplication"); //注意这里的库名字为你刚刚创建的C++名称
    }


    public int fd;

    public native static  int OpenCan(String canx);
    public native static  int CanWrite(int fd,int canId, byte[] data);
    public native static CanFrame CanRead(CanFrame mcanFrame, int time);
    public native static  int CloseCan(int fd);

}
2.3 接下来我们来实现CanControl里面的方法

点击CanControl.java里面的方法 比如 OpenCan;

鼠标移动过去会有一个Create JNl function for CanRead的提示 点击即可 其他的也是一样

图片

都创建完了 我们来到cpp文件 可以看到编译器给我们添加了几个方法

图片

接着我们自己来实现一下 我这里已经实现了 不想写的直接复制粘贴即可

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_CanControl_OpenCan(JNIEnv *env, jclass clazz, jstring canx) {
        int fd;
    struct ifreq ifr;
    struct sockaddr_can addr;

    /* open socket */
    if ((fd = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
        return -1;
    }

    const char *str = env->GetStringUTFChars(canx, 0);
    strcpy(ifr.ifr_name, str);
    ioctl(fd, SIOCGIFINDEX, &ifr);

    memset(&addr, 0, sizeof(addr));
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        return -2;
    }

    return fd;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_CanControl_CanWrite(JNIEnv *env, jclass clazz, jint fd,
                                                   jint can_id, jbyteArray data) {
    // 获取 JNI 数据
    jbyte *pdata = env->GetByteArrayElements(data, NULL);
    jsize data_len = env->GetArrayLength(data);

    // 确保数据长度不超过 CAN 最大数据长度
    if (data_len > 8) {
        data_len = 8; // CAN 数据长度最大为 8 字节
    }

    // 创建 CAN 帧
    struct can_frame frame;
    frame.can_id = can_id;
    frame.can_dlc = data_len;

    // 将数据复制到 CAN 帧中
    for (jsize i = 0; i < data_len; i++) {
        frame.data[i] = pdata[i] & 0xFF;
    }
    // 将剩余的数据部分置为 0(如果数据长度小于 8)
    for (jsize i = data_len; i < 8; i++) {
        frame.data[i] = 0;
    }

    // 释放 JNI 数据
    env->ReleaseByteArrayElements(data, pdata, 0);

    // 写入 CAN 总线
    int ret = write(fd, &frame, sizeof(struct can_frame));
    return ret;
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_myapplication_CanControl_CanRead(JNIEnv *env, jclass clazz,
                                                  jobject mcanFrame, jint time) {
    struct can_frame frame;
    read(time, &frame, sizeof(struct can_frame));

    jclass canFrameClass = env->GetObjectClass(mcanFrame);

    jfieldID canIdField = env->GetFieldID(canFrameClass, "can_id", "I");
    jfieldID canDlcField = env->GetFieldID(canFrameClass, "can_dlc", "C");
    jfieldID dataField = env->GetFieldID(canFrameClass, "data", "[B");

    env->SetIntField(mcanFrame, canIdField, static_cast<jint>(frame.can_id));
    env->SetCharField(mcanFrame, canDlcField, frame.can_dlc);

    jbyteArray dataArray = env->NewByteArray(frame.can_dlc);
    env->SetByteArrayRegion(dataArray, 0, frame.can_dlc, (jbyte *)frame.data);
    env->SetObjectField(mcanFrame, dataField, dataArray);

    return mcanFrame;
}

extern "C"
JNIEXPORT int JNICALL
Java_com_example_myapplication_CanControl_CloseCan(JNIEnv *env, jclass clazz, jint fd) {
    return close(fd);
}

三、测试demo

修改MainActivity.java文件

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    CanControl can0;
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv = findViewById(R.id.textview0);
        tv.setMovementMethod(ScrollingMovementMethod.getInstance());

        can0 = new CanControl(this);
        can0.InitCan(500000);
        can0.fd = can0.OpenCan("can0");

        //send
        new Thread() {
            byte[] data = new byte[] {(byte)0xA0, (byte)0xA1, (byte)0xA2, (byte)0xA3, (byte)0xA4, (byte)0xA5, (byte)0xA6, (byte)0xA7};

            @Override
            public void run() {
                while (true) {
                    try {
                        sleep(1000); // 每秒发送一次数据
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    // 更新数据
                    data[0] = (byte) ((data[0] + 1) % 0xFF);

                    // 调用 CanWrite 方法发送数据
                    int result = CanControl.CanWrite(can0.fd,0x123, data);
                    if (result < 0) {
                        // 发送失败,处理错误
                        Log.e("CAN", "发送数据失败");
                    } else {
                        // 发送成功
                        Log.i("CAN", "数据发送成功");
                    }
                }
            }
        }.start();


        // Initialize CanFrame
        final CanFrame[] canFrame = {new CanFrame()};

        //receive
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    // Call the CanRead method
                    canFrame[0] = CanControl.CanRead(canFrame[0], can0.fd);

                    // Extract data from CanFrame
                    int can0id = canFrame[0].can_id;
                    int can0eff = (can0id & 0x80000000) != 0 ? 1 : 0; // Extended frame
                    int can0rtr = (can0id & 0x40000000) != 0 ? 1 : 0; // Remote transmission request
                    int can0len = canFrame[0].can_dlc;
                    byte[] can0data = canFrame[0].data;

                    runOnUiThread(() -> {
                        // Prepare the string to display
                        String str = "can0  RX ";
                        str += (can0eff == 0) ? "S " : "E ";
                        str += (can0rtr == 0) ? "-  " : "R  ";
                        String strid = Integer.toHexString(can0id & 0x1FFFFFFF); // Mask to get ID
                        if (can0eff == 0) {
                            for (int i = 0; i < 3 - strid.length(); i++) {
                                strid = '0' + strid;
                            }
                        } else {
                            for (int i = 0; i < 8 - strid.length(); i++) {
                                strid = '0' + strid;
                            }
                        }
                        str = str + strid + "   [" + can0len + "]  ";
                        for (int i = 0; i < can0len; i++) {
                            String hex = Integer.toHexString(can0data[i] & 0xFF);
                            hex = (hex.length() == 1) ? ('0' + hex) : hex;
                            str = str + ' ' + hex;
                        }
                        str = str.toUpperCase();
                        str += '\n';

                        // Update the TextView
                        if (tv.getLineCount() > 1000) {
                            tv.setText("");
                        }
                        tv.append(str);
                        int offset = tv.getLineCount() * tv.getLineHeight();
                        if (offset > tv.getHeight()) {
                            tv.scrollTo(0, offset - tv.getHeight());
                        }
                    });
                }
            }
        }.start();
    }
}

修改activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textview0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

四、验证


文章作者: Mai
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Mai !
 本篇
安卓can开发 安卓can开发
Android CAN 应用开发在工作时发现我们很多客户对于Android can应用开发没有一点头绪,下面我通过一个简单项目来实现一个can通信demo。 开发准备确保开发板硬件上有can节点存在,并且确保通信正常 验证方法如下: 一 、
2024-08-14 Mai
下一篇 
QT环境配置
QT环境配置
ARM环境配置
2024-08-01
  目录