Android usb学习笔记:Android AOA协议设备端 流程总结

背景

前段时间的项目开发中,由于wifi稳定性的限制,项目采用了Android手机与嵌入式设备通过usb直接连接的方式进行通信。其中Android的usb层使用了Android自身的AOA模式,嵌入式端借助libusb库与Android端通信。在应用层简单实现了一个tcp连接,最终可以抽象为双方socket端口与端口间的通信过程。探索的过程比较曲折,其间受到两位同事也是前辈的帮助指导,收获颇多。

实现

AOA协议实现流程简述

下面是我对AOA通信过程的一些理解

1.Android连接设备,设备可以通过轮询或者注册热插拔事件的方式,检测当前插入的usb设备,查询这个设备是否处在AOA模式,如果不是则开启AOA模式。也就是设备向Android设备写入相应的usb控制信息,写入成功后,我们的Android设备就开启了AOA模式,这时嵌入式端就拿到了Android设备usb的读写描述符,可以对其进行数据传输。

2.Android端接收到嵌入式端写入的信息后,就把自己设置为accessory模式,这时Android会发送一条系统广播,在广播接受者中查询当前是否有accessory连接。如果可以拿到这个accessory,那么就能获取到相应的读写流,也就可以通过这两个流对设备进行读写。

这里设备端底层使用了libusb库,下面是项目地址,可以直接在mac或者linux下编译。在开发Android端代码时网上的参考代码有不少bug,这里给出一份相对比较稳定的Android端例程。

libusb项目链接 https://github.com/libusb/libusb.git
设备aoa模式参考项目链接 https://github.com/timotto/AOA-Proxy.git
本例程设备端项目链接 https://git.oschina.net/vonchenchen/aoa_proxy.git
本例程Android端项目链接 https://git.oschina.net/vonchenchen/aoa_android.git

例程的编译与使用

在编译设备端项目之前需要先编译并安装libusb库。安装完成libusb库后,运行本例程设备端项目中的configure文件,参考日志信息安装其他依赖库,然后执行make,编译完成后将会生成aoaproxy文件,也就是我们能最后生成的可执行文件。

Android端例程可以直接在Android Studio打开运行,直接装入手机即可。

在设备端执行

sudo ./aoaproxy

如果看到如下日志,则说明程序已经正常启动

start_service
start connect device
prepare to connect device

这时,插入android设备,应用会自动启动,设备控制台开始每隔一秒打印如下信息

Start send
recived len 12
usb recive len 12
receive hello

这样就说明例子程序运行起来了。这里每隔1秒设备将向Android端发送一个Hello字符串,Android端收到数据后会原样返回这些数据,这时设备端收到数据后会将这些信息打印在控制台上。

代码分析

设备端代码主要分为一下几个文件,文件对应功能如下

aoaproxy.c —– 主程序

accessory.c —— AOA底层开启

a2spipe.c ——usb与本地server交互管道

tcp.c ——-tcp连接

local_service.c ——- 本地 server 用于数据接收和发送,可以在单独进程中开启

下面我们将分布对这几个文件进行介绍。

main函数
int main(int argc, char** argv) {
    int r;
    int opt;
    int xcount=0;

    while ((opt = getopt(argc, argv, "dfh:p:x:")) != -1) {
       //参数
       ....
    }

    ctx = NULL;
    connectedDevices = NULL;

    //开启本地socketserver
    create_start_service();

    if (do_fork)
        do_fork_foo();

    //注册信号
    initSigHandler();

    // it is important to init usb after the fork!
    if (0 > initUsb()) {
        logError("Failed to initialize USB\n");
        return 1;
    }
    //定时任务轮询
    if(autoscan) {
        struct itimerval timer;
        timer.it_value.tv_sec = 1;
        timer.it_value.tv_usec = 0;
        timer.it_interval.tv_sec = 1;
        timer.it_interval.tv_usec = 0;
        setitimer (ITIMER_REAL, &timer, NULL);
    }

    //usb设备列表
    libusb_device **devs = NULL;
    while(!do_exit) {

        if (doUpdateUsbInventory == 1) {
            doUpdateUsbInventory = 0;
            //清空设备
            cleanupDeadDevices();
            //尝试链接设备
            updateUsbInventory(devs);
        }
        //阻塞等待usb事件
        r = libusb_handle_events(ctx);
        if (r) {
            if (r == LIBUSB_ERROR_INTERRUPTED) {
                // ignore
            } else {
                if(!do_exit)
                    logDebug("libusb_handle_events_timeout: %d\n", r);

                break;
            }
        }
    }

    if (devs != NULL)
        libusb_free_device_list(devs, 1);

    if(autoscan) {
        struct itimerval timer;
        memset (&timer, 0, sizeof(timer));
        setitimer (ITIMER_REAL, &timer, NULL);
    }
    shutdownEverything();
    return EXIT_SUCCESS;
}

这里比较重要的是updateUsbInventory函数。

updateUsbInventory

下面看一下updateUsbInventory方法做什么的

static int updateUsbInventory(libusb_device **devs) {
    static ssize_t cnt = 0;
    static ssize_t lastCnt = 0;
//  static libusb_device **devs;
    static libusb_device **lastDevs = NULL;
    //获取usb设备列表
    cnt = libusb_get_device_list(ctx, &devs);
    if(cnt < 0) {
        logError("Failed to list devices\n");
        return -1;
    }

    ssize_t i, j;
    int foundBefore;
    for(i = 0; i < cnt; i++) {
        foundBefore = 0;
        if ( lastDevs != NULL) {
            for(j=0;j < lastCnt; j++) {
                if (devs[i] == lastDevs[j]) {
                    foundBefore = 1;
                    break;
                }
            }
        }
        if (!foundBefore) {
            logDebug("start connect device\n");
            //连接设备 连接本地服务端
            if(connectDevice(devs[i]) >= 0)
                libusb_ref_device(devs[i]);
        }
    }

    if (lastDevs != NULL) {
//      if (cnt != lastCnt)
//          fprintf(LOG_DEB, "number of USB devices changed from %d to %d\n", lastCnt, cnt);

        for (i=0;i<lastCnt;i++) {
            foundBefore = 0;
            for(j=0;j<cnt;j++) {
                if (devs[j] == lastDevs[i]) {
                    foundBefore = 1;
                    break;
                }
            }
            if(!foundBefore) {
                struct listentry *hit = connectedDevices;

                while(hit != NULL) {
                    if ( hit->usbDevice == lastDevs[i]) {
                        disconnectDevice(lastDevs[i]);
                        libusb_unref_device(lastDevs[i]);
                        break;
                    }
                    hit = hit->next;
                }
            }
        }
        libusb_free_device_list(lastDevs, 1);
    }
    lastDevs = devs;
    lastCnt = cnt;

    return 0;
}
connectDevice

这个函数比较关键,开启了android的accessory,同时也和本地服务器进行连接,这样就打通了usb和本地server的通道。

static int connectDevice(libusb_device *device) {

    logDebug("prepare to connect device \n");

    struct libusb_device_descriptor desc;
    //获取usb设备描述信息
    int r = libusb_get_device_descriptor(device, &desc);
    if (r < 0) {
        logError("failed to get device descriptor: %d", r);
        return -1;
    }

    switch(desc.bDeviceClass) {
    case 0x09:
        logDebug("device 0x%04X:%04X has wrong deviceClass: 0x%02x",
                desc.idVendor, desc.idProduct,
                desc.bDeviceClass);
        return -1;
    }

    struct t_excludeList *e = exclude;
    while(e != NULL) {
        logDebug("comparing device [%04x:%04x] and [%04x:%04x]",
                desc.idVendor, desc.idProduct, e->vid, e->pid);
        if(e->vid == desc.idVendor && e->pid == desc.idProduct) {
            logDebug("device is on exclude list", desc.idVendor, desc.idProduct);
            return -1;
        }
        e = e->next;
    }

    //检查当前设备是否处于accessory模式
    if(!isDroidInAcc(device)) {
        logDebug("attempting AOA on device 0x%04X:%04X\n",
                desc.idVendor, desc.idProduct);
        //写入要启动的应用的信息 开启android的accessory模式 
        switchDroidToAcc(device, 1, haveAudio);
        return -1;
    }

    //entry管理socket与usb
    struct listentry *entry = malloc(sizeof(struct listentry));
    if (entry == NULL) {
        logError("Not enough RAM");
        return -2;
    }
    bzero(entry, sizeof(struct listentry));

    //entry拿到usb句柄device
    entry->usbDevice = device;

    //entry拿到socket句柄
#ifdef SOCKET_RETRY
    //连接本地socketserver, 返回socket客户端的描述符
    while((r = connectTcpSocket(hostname, portno)) <= 0) {
        logError("failed to setup socket: %d, retrying\n", r);
        sleep(1);
    }
    //记录本地soket链接的描述符
    entry->sockfd = r;
    entry->socketDead = 0;
#else
    r = connectTcpSocket(hostname, portno);
    if (r < 0) {
        fprintf(LOG_ERR, "failed to setup socket: %d\n", r);
        free(entry);
        return -4;
    }
    entry->sockfd = r;
    entry->socketDead = 0;
#endif
    //如果android设备已经是aoa模式,打开usb
    logDebug("start setup droid \n");
    //找到accessory接口并用接口信息初始化entry->droid
    r = setupDroid(device, &entry->droid);
    if (r < 0) {
        logError("failed to setup droid: %d\n", r);
        free(entry);
        return -3;
    }

    //将entry加入链表
    entry->next = NULL;
    if (connectedDevices == NULL) {
        entry->prev = NULL;
        connectedDevices = entry;
    } else {
        struct listentry *last = connectedDevices;
        while(last->next != NULL)
            last = last->next;
        entry->prev = last;
        last->next = entry;
    }

    //建立usb与socket互相通信的任务
    r = startUSBPipe(entry);
    if (r < 0) {
        logError("failed to start pipe: %d", r);
        disconnectDevice(device);
        return -5;
    }

    if (haveAudio && entry->droid.audioendp) {
        startAudio(entry);
    }

    logDebug("new Android connected");
    return 0;
}

上述代码中首先检测当前接口是否为accessory模式,如果不是则将其设置为accessory模式,但是此处将所有设备都设置为accessory,可能有些设备并非android设备。同时connectTcpSocket方法开启了tcp连接。这里用entry记录socket和usb信息,并将其放入全局链表connectedDevices维护。
这里不经会让我们产生疑问,到底usb收发数据是在哪里,又是在什么地方与tcp server进行交互,我们继续往下看。
entry中维护了usb状态,同时也有socket,entry被放入startUSBPipe函数,下面着重看一下startUSBPipe的实现。

startUSBPipe
static int startUSBPipe(struct listentry *device) {
    int r;
    if(initUsbXferThread(&device->usbRxThread) < 0) {
        logError("failed to allocate usb rx transfer\n");
        return -1;
    }
    if(initUsbXferThread(&device->socketRxThread) < 0) {
        logError("failed to allocate usb tx transfer\n");
        destroyUsbXferThread(&device->usbRxThread);
        return -1;
    }

    //写入到usb任务
    r = pthread_create(&device->usbRxThread.thread, NULL, (void*)&a2s_usbRxThread, (void*)device);
    if (r < 0) {
        logError("failed to start usb rx thread\n");
        return -1;
    }

    //读出到socket任务
    r = pthread_create(&device->socketRxThread.thread, NULL, (void*)&a2s_socketRxThread, (void*)device);
    if (r < 0) {
        // other thread is stopped in disconnectDevice method
        logError("failed to start socket rx thread\n");
        return -1;
    }

    return 0;
}

这里开启了两个线程,分别是usb数据写入server任务和server写入usb任务。下面分别看一下这两个任务。

a2s_usbRxThread
//usb写入socket任务
void *a2s_usbRxThread( void *d ) {
    logDebug("a2s_usbRxThread started\n");

    struct listentry *device = (struct listentry*)d;

    unsigned char buffer[device->droid.inpacketsize];
    int rxBytes = 0;
    int txBytes;
    int sent;
    int r;

    //初始化usbRxThread.xfr ,关联数据buffer   传输完毕后回调a2s_usbrx_cb  解锁device->usbRxThread.condition
    libusb_fill_bulk_transfer(device->usbRxThread.xfr, device->droid.usbHandle, device->droid.inendp,
            buffer, sizeof(buffer),
            (libusb_transfer_cb_fn)&a2s_usbrx_cb, (void*)&device->usbRxThread, 0);

    while(!device->usbRxThread.stop && !device->usbDead && !device->socketDead) {

        pthread_mutex_lock( &device->usbRxThread.mutex );
        device->usbRxThread.usbActive = 1;

//      logDebug("a2s_usbRxThread reading...\n");
        //请求数据
        r = libusb_submit_transfer(device->usbRxThread.xfr);
        if (r < 0) {
            logError("a2s usbrx submit transfer failed\n");
            device->usbDead = 1;
            device->usbRxThread.usbActive = 0;
            pthread_mutex_unlock( &device->usbRxThread.mutex );
            break;
        }

//      waitUsbXferThread(&device->usbRxThread);

//      logDebug("a2s_usbRxThread waiting...\n");
        //等待接收数据
        pthread_cond_wait( &device->usbRxThread.condition, &device->usbRxThread.mutex);
//      logDebug("a2s_usbRxThread wait over\n");
        if (device->usbRxThread.usbActive) {
            logError("wait, unlock but usbActive!\n");
        }
        pthread_mutex_unlock( &device->usbRxThread.mutex );

        if (device->usbRxThread.stop || device->usbDead || device->socketDead)
            break;

        //查看usb接收数据的状态
        switch(device->usbRxThread.xfr->status) {
        case LIBUSB_TRANSFER_COMPLETED:
//          logDebug("a2s_usbRxThread writing...\n");
            rxBytes = device->usbRxThread.xfr->actual_length;

            logDebug("usb recive len %d \n", rxBytes);

            sent = 0;
            txBytes = 0;
            while(sent < rxBytes && !device->usbRxThread.stop) {
                //将usb接收到的数据全部写入到socket
                txBytes = write(device->sockfd, buffer + sent, rxBytes - sent);
                if (txBytes <= 0) {
                    logError("a2s usbrx socket tx failed\n");
                    device->socketDead = 1;
                    device->usbRxThread.stop = 1;
                } else {
                    sent += txBytes;
                }
            }
            break;
        case LIBUSB_TRANSFER_NO_DEVICE:
            device->usbDead = 1;
            device->usbRxThread.stop = 1;
            break;
        default:
//          logDebug("a2s_usbRxThread usb error %d, ignoring\n", device->usbRxThread.xfr->status);
            break;
        }
    }

    device->usbRxThread.stopped = 1;
    logDebug("a2s_usbRxThread finished\n");
    pthread_exit(0);
    return NULL;
}
libusb_fill_bulk_transfer

这个方法使用entry中的数据,开启usb通路,如果接收到了usb数据,就会回调a2s_usbrx_cb。

之后调用

libusb_submit_transfer

请求接收数据。请求完数据线程被锁,如果a2s_usbrx_cb被回调,则会发送一个信号量,线程锁打开。此时usb数据已经传入到了缓冲区,entry中存储的socket的描述符,直接将buffer写入这个描述符,server就会收到usb信息。数据发送任务也是同理。到此,usb和server通道就已经打通。

另外,AOA模式可以直接打开应用,下面看一下AOA模式是如何打开的。

isDroidInAcc

这个函数用于设备检测是否处于AOA模式

int isDroidInAcc(libusb_device *dev) {
    struct libusb_device_descriptor desc;
    int r = libusb_get_device_descriptor(dev, &desc);
    if (r < 0) {
        logError("failed to get device descriptor\n");
//      fprintf(LOG_ERR, ERR_USB_DEVDESC);
        return 0;
    }

    if (desc.idVendor == VID_GOOGLE) {
        switch(desc.idProduct) {
        case PID_AOA_ACC:
        case PID_AOA_ACC_ADB:
        case PID_AOA_ACC_AU:
        case PID_AOA_ACC_AU_ADB:
            return 1;
        case PID_AOA_AU:
        case PID_AOA_AU_ADB:
            logDebug("device is audio-only\n");
//          logDebug( "device is audio-only\n");
            break;
        default:
            break;
        }
    }

    return 0;
}

通过libusb_device获取当前usb设备的状态信息,用来判断usb的厂商和模式等信息。

switchDroidToAcc

这个函数是将设备设置为Accessory模式,这里可以配置我们的设备连接手机后启动哪个android设备。

void switchDroidToAcc(libusb_device *dev, int force, int audio) {
    struct libusb_device_handle* handle;
    unsigned char ioBuffer[2];
    int r;
    int deviceProtocol;

    //打开设备
    if(0 > libusb_open(dev, &handle)){
        logError("Failed to connect to device\n");
        return;
    }

    //写入控制信息
    if(libusb_kernel_driver_active(handle, 0) > 0) {
        if(!force) {
            logError("kernel driver active, ignoring device");
            libusb_close(handle);
            return;
        }
        if(libusb_detach_kernel_driver(handle, 0)!=0) {
            logError("failed to detach kernel driver, ignoring device");
            libusb_close(handle);
            return;
        }
    }
    if(0> (r = libusb_control_transfer(handle,
            0xC0, //bmRequestType
            51, //Get Protocol
            0,
            0,
            ioBuffer,
            2,
            2000))) {
        logError("get protocol call failed %d \n", r);
        libusb_close(handle);
        return;
    }

    deviceProtocol = ioBuffer[1] << 8 | ioBuffer[0];
    if (deviceProtocol < AOA_PROTOCOL_MIN || deviceProtocol > AOA_PROTOCOL_MAX) {
//      logDebug("Unsupported AOA protocol %d\n", deviceProtocol);
        logDebug( "Unsupported AOA protocol %d\n", deviceProtocol);
        libusb_close(handle);
        return;
    }

    //这些量用于指定启动app,我们在app中也会写入同样的信息
    const char *setupStrings[6];
    setupStrings[0] = vendor;
    setupStrings[1] = model;
    setupStrings[2] = description;
    setupStrings[3] = version;
    setupStrings[4] = uri;
    setupStrings[5] = serial;

    int i;
    for(i=0;i<6;i++) {
        if(0 > (r = libusb_control_transfer(handle,
                0x40,
                52,
                0,
                (uint16_t)i,
                (unsigned char*)setupStrings[i],
                strlen(setupStrings[i]),2000))) {
            logDebug( "send string %d call failed\n", i);
            libusb_close(handle);
            return;
        }
    }

    if (deviceProtocol >= 2) {
        if(0 > (r = libusb_control_transfer(handle,
                0x40, //厂商的请求
                58,
#ifdef USE_AUDIO
                audio, // 0=no audio, 1=2ch,16bit PCM, 44.1kHz
#else
                0,
#endif
                0,
                NULL,
                0,
                2000))) {
            logDebug( "set audio call failed\n");
            libusb_close(handle);
            return;
        }
    }

    if(0 > (r = libusb_control_transfer(handle,0x40,53,0,0,NULL,0,2000))) {
        logDebug( "start accessory call failed\n");
        libusb_close(handle);
        return;
    }

    libusb_close(handle);
}
server例程

下面是我们自己实现的socket server,用于接收和发送数据,这里我们会向usb发送“hello“字符串,然后打印收到的数据。当然,在使用时可以把上面的例子作为单独进程开启,作为单独模块,而数据发送和接收在我们自己的应用的进程中。

//数据接收线程
void *recvThread(void *arg){

    int length;

    struct thread_param *param = (struct thread_param *)arg;
    int socket = param->socket_id;

    printf("recvThread %d\n", socket);

    char buffer[BUFFER_SIZE];
    bzero(buffer, BUFFER_SIZE);

    while(loop_flag){

        length = recv(socket,buffer,BUFFER_SIZE,0 );
        if (length < 0)
        {
            printf("Server Recieve Data Failed!\n");
            loop_flag = 0;
            break;
        }
        //打印接收到的数据内容
        printf("receive %s\n", buffer);
    }
}

//数据发送线程 每隔一秒发送
void *sendThread(void *arg){

    struct thread_param *param = (struct thread_param *)arg;
    int socket = param->socket_id;

    printf("sendThread %d\n", socket);

    char buffer[BUFFER_SIZE];
    bzero(buffer, BUFFER_SIZE);

    while(loop_flag){

        printf("\nStart send\n");

        int length = 12;
        buffer[0] = 'h';
        buffer[1] = 'e';
        buffer[2] = 'l';
        buffer[3] = 'l';
        buffer[4] = 'o';
        buffer[5] = 0;
        //发送buffer中的字符串到new_server_socket,实际是给客户端
        if(send(socket,buffer,length,0)<0)
        {
            printf("Send faield\n");
            loop_flag = 0;
            break;
        }
        sleep(1);
    }
}

//开启自定义服务 
int start_service(int argc, char **argv)
{
    printf("start_service\n");
    //设置一个socket地址结构server_addr,代表服务器internet地址, 端口
    struct sockaddr_in server_addr;
    bzero(&server_addr,sizeof(server_addr)); //把一段内存区的内容全部设置为0
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);

    //创建用于internet的流协议(TCP)socket,用server_socket代表服务器socket
    int server_socket = socket(PF_INET,SOCK_STREAM,0);
    if( server_socket < 0)
    {
        printf("Create Socket Failed!");
        exit(1);
    }
    { 
    int opt =1;
    setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    }

    //把socketsocket地址结构联系起来
    if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))
    {
        printf("Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT); 
        exit(1);
    }

    //server_socket用于监听
    if ( listen(server_socket, LENGTH_OF_LISTEN_QUEUE) )
    {
        printf("Server Listen Failed!"); 
        exit(1);
    }
    while (1) 
    {
        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);

        int new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length);
        if ( new_server_socket < 0)
        {
            printf("Server Accept Failed!\n");
            break;
        }

        loop_flag = 1;

        threadParam.socket_id = new_server_socket;
        pthread_create(&recvThreadId, NULL, recvThread, &threadParam);
        pthread_create(&sendThreadId, NULL, sendThread, &threadParam);

        char buffer[BUFFER_SIZE];
        bzero(buffer, BUFFER_SIZE);

        while(1){
            sleep(1);
            if(loop_flag == 0){
                break;
            }
        }

        printf("close server socket \n");
        //关闭与客户端的连接
        close(new_server_socket);
    }
    //关闭监听用的socket
    close(server_socket);
    return 0;
}

void create_start_service(){

    pthread_create(&mainThreadId, NULL, start_service, &mainThreadParam);
}

总结

上文介绍了Android AOA协议设备端实现的基本流程,首先介绍了项目的使用方法,之后梳理了本地server和usb通信的流程,最后介绍了设备端开启app的方法,下一篇文章我们将分析android端代码如何对接设备端。

  • 10
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Android Open Accessory (AOA) 是 Android 3.1 及以上版本中引入的一种协议,用于让 Android 设备USB 外设通信。其中,AOA 分为两种模式:Accessory 模式和 Host 模式。 在 Accessory 模式下,Android 设备作为一个 USB 主机,外接一个 USB 外设,如 Arduino 或者 PIC 单片机等,通过 USB 接口进行通信。在 Host 模式下,Android 设备作为一个 USB设备,连接到 PC 上,与 PC 进行通信。 对于 51 单片机的 AOA 开发,需要实现 USB 设备的相关硬件电路,并编写相应的固件程序。具体步骤如下: 1. 首先,需要了解 Android 设备与外设的通信协议,了解 AOA 协议的数据格式、命令以及响应的数据包格式。 2. 在单片机中实现 USB 设备的硬件电路,包括 USB 转串口、USB 控制器等。 3. 在单片机中编写 AOA 协议的固件程序,根据协议格式,实现设备的握手、命令响应、数据传输等功能。 4. 在 Android 应用程序中,使用 USB Host API 进行设备的检测和数据传输,根据协议格式,封装命令和数据,发送给单片机。 下面是一个简单的 Android 应用程序,演示如何使用 USB Host API 实现与 51 单片机的通信: ``` public class MainActivity extends Activity implements UsbManager.OnUsbDeviceConnectionListener { private UsbManager mUsbManager; private PendingIntent mPermissionIntent; private UsbDevice mDevice; private UsbEndpoint mEndpoint; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取 UsbManager 实例 mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); // 注册 USB 设备权限 mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(mUsbReceiver, filter); } @Override public void onResume() { super.onResume(); // 枚举所有连接的 USB 设备 for (UsbDevice device : mUsbManager.getDeviceList().values()) { // 判断是否为目标设备 if (device.getVendorId() == VENDOR_ID && device.getProductId() == PRODUCT_ID) { // 请求设备权限 mUsbManager.requestPermission(device, mPermissionIntent); break; } } } private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { mDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { // 获取 USB 设备接口 UsbInterface intf = mDevice.getInterface(0); // 获取 USB 设备点 mEndpoint = intf.getEndpoint(0); // 打开 USB 设备连接 UsbDeviceConnection connection = mUsbManager.openDevice(mDevice); if (connection != null) { connection.claimInterface(intf, true); // 发送命令给单片机 byte[] command = new byte[]{0x01, 0x02, 0x03}; connection.bulkTransfer(mEndpoint, command, command.length, TIMEOUT); } } else { Log.d(TAG, "permission denied for device " + mDevice); } } } } }; @Override public void onUsbDeviceConnection(UsbDevice device, UsbDeviceConnection connection) { // USB 设备连接成功回调 } @Override public void onUsbDeviceDisconnection(UsbDevice device, UsbDeviceConnection connection) { // USB 设备断开连接回调 } } ``` 51 单片机的固件程序实现比较复杂,需要涉及到 USB 设备的控制、数据传输、命令响应等方面,需要根据 AOA 协议的具体要求实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值