摄像头采集,framebuffer显示

#include <stdio.h>  
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h>
#include <fcntl.h>                
#include <unistd.h>  
#include <errno.h>  
#include <malloc.h>  
#include <sys/stat.h>  
#include <sys/types.h>  
#include <sys/time.h>  
#include <sys/mman.h>  
#include <sys/ioctl.h>  
#include <asm/types.h>            
#include <linux/videodev2.h>  

#include <linux/fb.h>

#define CLEAR(x) memset (&(x), 0, sizeof (x))  

typedef enum {  

    IO_METHOD_READ, IO_METHOD_MMAP, IO_METHOD_USERPTR,  

} io_method;  

struct buffer {  

    void * start;  

    size_t length;//buffer's length is different from cap_image_size  

};  

static char *fbp; // framebuffer mmap

static char * dev_name = NULL;  

static io_method io = IO_METHOD_MMAP;//IO_METHOD_READ;//IO_METHOD_MMAP;  

static int fd = -1;  

struct buffer * buffers = NULL;  

static unsigned int n_buffers = 0;  

static FILE * outf = 0;  

static unsigned int cap_image_size = 0;//to keep the real image size!!  

//  

static void errno_exit(const char * s) {  

    fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));  

    exit(EXIT_FAILURE);  

}  

static int xioctl(int fd, int request, void * arg) {  

    int r;  

    do  

        r = ioctl(fd, request, arg);  

    while (-1 == r && EINTR == errno);  

    return r;  

}  

static void yuyv2rgb(char *src, int sizeimage)
{
    int Y0,U,V,Y1;
    int R,G,B;
    int Ry,Gu,Bv;
    int i;
//    printf("yuyv2rgb \n");
    for(i = 0; i < sizeimage; i++)
    {
        Y0 = src[i];
        U = src[i+1];
        Y1 = src[i+2];
        V = src[i+3];

//        R = (int)(Y0 + 1.140*V);
//        G = (int)(Y0 - 0.394*U - 0.581*V);
//        B = (int)(Y0 + 2.032*U);

//        Ry = (int)(1159*(V-128));
//        Gu = (int)(0 - 380*(U-128)+ 813*(V-128));
//        Bv = (int)(2018*(U-128));

//        R = (int)(1164*(Y0-16) + Ry)/1000;
//        G = (int)(1164*(Y0-16) - Gu)/1000;
//        B = (int)(1164*(Y0-16) + Bv)/1000;

        R = Y0 + (1.370705 * (V-128));
        G = Y0 - (0.698001 * (V-128)) - (0.337633 * (U-128));
        B = Y0 + (1.732446 * (U-128));

//        src[i] = 0xff;
//        src[i+1] = R;
//        src[i+2] = G;
//        src[i+3] = B;
        if(R < 0) R = 0;
        if(G < 0) G = 0;
        if(B < 0) B = 0;

        src[i+3] = 0xff;
        src[i+2] = R;
        src[i+1] = G;
        src[i+0] = B;

        i+=3;
    }
//    printf("yuyv2rgb\n");
}
static void process_image(void * p, int len)
{
    int i;
    //  static char[115200] Outbuff ;  
//    printf("process_image %d \n", len);

    yuyv2rgb(p, len);

//    printf("process_image  \n");

    for(i = 0; i < len/4; i++)
    {
        ((unsigned long *)fbp)[i*2] = ((unsigned long *)p)[i];
        ((unsigned long *)fbp)[i*2+1] = ((unsigned long *)fbp)[i*2];
    }
}  

static int read_frame(void) {  

    struct v4l2_buffer buf;  

    unsigned int i;  

//    printf("read_frame %d\n", buffers[0].length);

    switch (io) {  

    case IO_METHOD_READ:  

        printf("IO_METHOD_READ \n");
        if (-1 == read(fd, buffers[0].start, buffers[0].length)) {  

            switch (errno) {  

            case EAGAIN:  

                return 0;  

            case EIO:

            default:  

                errno_exit("read");  

            }  

        }  

        //      printf("length = %d\r", buffers[0].length);  

        //      process_image(buffers[0].start, buffers[0].length);  

        printf("image_size = %d,\t IO_METHOD_READ buffer.length=%d\r",  

                cap_image_size, buffers[0].length);  

        process_image(buffers[0].start, cap_image_size);  

        break;  

    case IO_METHOD_MMAP:  
//        printf("IO_METHOD_MMAP \n");

        CLEAR (buf);  

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        buf.memory = V4L2_MEMORY_MMAP;  

        if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {  

            switch (errno) {  

            case EAGAIN:  

                return 0;  

            case EIO:

            default:  

                errno_exit("VIDIOC_DQBUF");  

            }  

        }  

//        printf("IO_METHOD_MMAP \n");
        assert(buf.index < n_buffers);  

        //      printf("length = %d\r", buffers[buf.index].length);  

        //      process_image(buffers[buf.index].start, buffers[buf.index].length);  

//        printf("IO_METHOD_MMAP \n");

//        printf("image_size = %d,\t IO_METHOD_MMAP buffer.length=%d\r",

//                cap_image_size, buffers[0].length);

//        printf("IO_METHOD_MMAP \n");
        process_image(buffers[0].start, cap_image_size);  

//        printf("IO_METHOD_MMAP \n");
        if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))  

            errno_exit("VIDIOC_QBUF");  

        break;  

    case IO_METHOD_USERPTR:  

        printf("IO_METHOD_USERPTR \n");
        CLEAR (buf);  

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        buf.memory = V4L2_MEMORY_USERPTR;  

        if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {  

            switch (errno) {  

            case EAGAIN:  

                return 0;  

            case EIO:  


            default:  

                errno_exit("VIDIOC_DQBUF");  

            }  

        }  

        for (i = 0; i < n_buffers; ++i)  

            if (buf.m.userptr == (unsigned long) buffers[i].start && buf.length  

                    == buffers[i].length)  

                break;  

        assert(i < n_buffers);  

        //      printf("length = %d\r", buffers[i].length);  

        //      process_image((void *) buf.m.userptr, buffers[i].length);  

        printf("image_size = %d,\t IO_METHOD_USERPTR buffer.length=%d\r",  

                cap_image_size, buffers[0].length);  

        process_image(buffers[0].start, cap_image_size);  

        if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))  

            errno_exit("VIDIOC_QBUF");  

        break;  

    }  

    return 1;  

}  

static void mainloop(void) {

    unsigned int count;  

    count = 100;  

    printf("mainloop\n");

    while (count-- > 0) {  

        for (;;) {  

            fd_set fds;  

            struct timeval tv;  

            int r;  

            FD_ZERO(&fds);  

            FD_SET(fd, &fds);  

              

            tv.tv_sec = 2;  

            tv.tv_usec = 0;  

            r = select(fd + 1, &fds, NULL, NULL, &tv);  

            if (-1 == r) {  

                if (EINTR == errno)  

                    continue;  

                errno_exit("select");  

            }  

            if (0 == r) {  

                fprintf(stderr, "select timeout\n");  

                exit(EXIT_FAILURE);  

            }  

            if (read_frame())  

                break;  

              

        }  

    }  

}  

static void stop_capturing(void) {  

    enum v4l2_buf_type type;  

    switch (io) {  

    case IO_METHOD_READ:  

          

        break;  

    case IO_METHOD_MMAP:  

    case IO_METHOD_USERPTR:  

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))  

            errno_exit("VIDIOC_STREAMOFF");  

        break;  

    }  

}  

static void start_capturing(void)
{

    unsigned int i;  

    enum v4l2_buf_type type;  

    printf("start_capturing\n");
    switch (io) {  

    case IO_METHOD_READ:  

          

        break;  

    case IO_METHOD_MMAP:  

        for (i = 0; i < n_buffers; ++i) {  

            struct v4l2_buffer buf;  

            CLEAR (buf);  

            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

            buf.memory = V4L2_MEMORY_MMAP;  

            buf.index = i;  

            if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))  

                errno_exit("VIDIOC_QBUF");  

        }  

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))  

            errno_exit("VIDIOC_STREAMON");  

        break;  

    case IO_METHOD_USERPTR:  

        for (i = 0; i < n_buffers; ++i) {  

            struct v4l2_buffer buf;  

            CLEAR (buf);  

            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

            buf.memory = V4L2_MEMORY_USERPTR;  

            buf.index = i;  

            buf.m.userptr = (unsigned long) buffers[i].start;  

            buf.length = buffers[i].length;  

            if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))  

                errno_exit("VIDIOC_QBUF");  

        }  

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))  

            errno_exit("VIDIOC_STREAMON");  

        break;  

    }  

}  

static void uninit_device(void) {  

    unsigned int i;  

    switch (io) {  

    case IO_METHOD_READ:  

        free(buffers[0].start);  

        break;  

    case IO_METHOD_MMAP:  

        for (i = 0; i < n_buffers; ++i)  

            if (-1 == munmap(buffers[i].start, buffers[i].length))  

                errno_exit("munmap");  

        break;  

    case IO_METHOD_USERPTR:  

        for (i = 0; i < n_buffers; ++i)  

            free(buffers[i].start);  

        break;  

    }  

    free(buffers);  

}  

static void init_read(unsigned int buffer_size)
{
    printf("init_read\n");
    buffers = calloc(1, sizeof(*buffers));  

    if (!buffers) {  

        fprintf(stderr, "Out of memory\n");  

        exit(EXIT_FAILURE);  

    }  

    buffers[0].length = buffer_size;  

    buffers[0].start = malloc(buffer_size);  

    if (!buffers[0].start) {  

        fprintf(stderr, "Out of memory\n");  

        exit(EXIT_FAILURE);  

    }  

}  

static void init_mmap(void)
{

    printf("init_mmap\n");

    struct v4l2_requestbuffers req;  

    CLEAR (req);  

    req.count = 4;  

    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    req.memory = V4L2_MEMORY_MMAP;  

    if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {  

        if (EINVAL == errno) {  

            fprintf(stderr, "%s does not support "  

                "memory mapping\n", dev_name);  

            exit(EXIT_FAILURE);  

        } else {  

            errno_exit("VIDIOC_REQBUFS");  

        }  

    }  

    if (req.count < 2) {  

        fprintf(stderr, "Insufficient buffer memory on %s\n", dev_name);  

        exit(EXIT_FAILURE);  

    }  

    buffers = calloc(req.count, sizeof(*buffers));  

    if (!buffers) {  

        fprintf(stderr, "Out of memory\n");  

        exit(EXIT_FAILURE);  

    }  

    for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {  

        struct v4l2_buffer buf;  

        CLEAR (buf);  

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        buf.memory = V4L2_MEMORY_MMAP;  

        buf.index = n_buffers;  

        if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))  

            errno_exit("VIDIOC_QUERYBUF");  

        buffers[n_buffers].length = buf.length;  

        buffers[n_buffers].start = mmap(NULL , buf.length,  

                PROT_READ | PROT_WRITE ,  

                MAP_SHARED , fd, buf.m.offset);  

        if (MAP_FAILED == buffers[n_buffers].start)  

            errno_exit("mmap");  

    }  
    printf("init_mmap\n");

}  

static void init_userp(unsigned int buffer_size) {  

    struct v4l2_requestbuffers req;  

    unsigned int page_size;  

    page_size = getpagesize();  

    buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);  

    CLEAR (req);  

    req.count = 4;  

    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    req.memory = V4L2_MEMORY_USERPTR;  

    if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {  

        if (EINVAL == errno) {  

            fprintf(stderr, "%s does not support "  

                "user pointer i/o\n", dev_name);  

            exit(EXIT_FAILURE);  

        } else {  

            errno_exit("VIDIOC_REQBUFS");  

        }  

    }  

    buffers = calloc(4, sizeof(*buffers));  

    if (!buffers) {  

        fprintf(stderr, "Out of memory\n");  

        exit(EXIT_FAILURE);  

    }  

    for (n_buffers = 0; n_buffers < 4; ++n_buffers) {  

        buffers[n_buffers].length = buffer_size;  

        buffers[n_buffers].start = memalign(page_size,  

                buffer_size);  

        if (!buffers[n_buffers].start) {  

            fprintf(stderr, "Out of memory\n");  

            exit(EXIT_FAILURE);  

        }  

    }  

}  

static void init_device(struct fb_var_screeninfo *vinfo)
{

    struct v4l2_capability cap;  

    struct v4l2_cropcap cropcap;  

    struct v4l2_crop crop;  

    struct v4l2_format fmt;  

    unsigned int min;  

    if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {  

        if (EINVAL == errno) {  

            fprintf(stderr, "%s is no V4L2 device\n", dev_name);  

            exit(EXIT_FAILURE);  

        } else {  

            errno_exit("VIDIOC_QUERYCAP");  

        }  

    }  

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {  

        fprintf(stderr, "%s is no video capture device\n", dev_name);  

        exit(EXIT_FAILURE);  

    }  

    switch (io) {  

    case IO_METHOD_READ:  

        if (!(cap.capabilities & V4L2_CAP_READWRITE)) {  

            fprintf(stderr, "%s does not support read i/o\n", dev_name);  

            exit(EXIT_FAILURE);  

        }  

        break;  

    case IO_METHOD_MMAP:  

    case IO_METHOD_USERPTR:  

        if (!(cap.capabilities & V4L2_CAP_STREAMING)) {  

            fprintf(stderr, "%s does not support streaming i/o\n", dev_name);  

            exit(EXIT_FAILURE);  

        }  

        break;  

    }  

    //not all capture support crop!!!!!!!  

      

    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

    CLEAR (cropcap);  

    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {  

        crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

#ifndef CROP_BY_JACK  

        crop.c = cropcap.defrect;   

#else  

        crop.c.left = cropcap.defrect.left;  

        crop.c.top = cropcap.defrect.top;  

        crop.c.width = 352;  

        crop.c.height = 288;  
io
#endif  

        printf("----->has ability to crop!!\n");  

        printf("cropcap.defrect = (%d, %d, %d, %d)\n", cropcap.defrect.left,  

                cropcap.defrect.top, cropcap.defrect.width,  

                cropcap.defrect.height);  

        if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {  

            switch (errno) {  

            case EINVAL:  

                  

                break;  

            default:  

                  

                break;  

            }  

            printf("-----!!but crop to (%d, %d, %d, %d) Failed!!\n",  

                    crop.c.left, crop.c.top, crop.c.width, crop.c.height);  

        } else {  

            printf("----->sussess crop to (%d, %d, %d, %d)\n", crop.c.left,  

                    crop.c.top, crop.c.width, crop.c.height);  

        }  

    } else {  

          

        printf("!! has no ability to crop!!\n");  

    }  

    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

    printf("\n");  

    crop finished!  

    //set the format  

    CLEAR (fmt);  

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    fmt.fmt.pix.width = vinfo->xres;

    fmt.fmt.pix.height = vinfo->yres;

    printf("%d  %d \n", vinfo->xres, vinfo->yres);

    //V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUV420 — Planar formats with 1/2 horizontal and vertical chroma resolution, also known as YUV 4:2:0  

    //V4L2_PIX_FMT_YUYV — Packed format with 1/2 horizontal chroma resolution, also known as YUV 4:2:2  

    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUV420;//V4L2_PIX_FMT_YUYV;  

    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;  

    {  

        printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

        printf("=====will set fmt to (%d, %d)--", fmt.fmt.pix.width,  

                fmt.fmt.pix.height);  

        if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {  

            printf("V4L2_PIX_FMT_YUYV\n");  

        } else if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {  

            printf("V4L2_PIX_FMT_YUV420\n");  

        } else if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {  

            printf("V4L2_PIX_FMT_NV12\n");  

        }  

    }  

    if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))  

        errno_exit("VIDIOC_S_FMT");  

    {  

        printf("=====after set fmt\n");  

        printf("    fmt.fmt.pix.width = %d\n", fmt.fmt.pix.width);  

        printf("    fmt.fmt.pix.height = %d\n", fmt.fmt.pix.height);  

        printf("    fmt.fmt.pix.sizeimage = %d\n", fmt.fmt.pix.sizeimage);  

        cap_image_size = fmt.fmt.pix.sizeimage;  

        printf("    fmt.fmt.pix.bytesperline = %d\n", fmt.fmt.pix.bytesperline);  

        printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

        printf("\n");  

    }  

    cap_image_size = fmt.fmt.pix.sizeimage;  

      

    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

      

    min = fmt.fmt.pix.width * 2;  

    if (fmt.fmt.pix.bytesperline < min)  

        fmt.fmt.pix.bytesperline = min;  

    min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;  

    if (fmt.fmt.pix.sizeimage < min)  

        fmt.fmt.pix.sizeimage = min;  

    printf("After Buggy driver paranoia\n");  

    printf("    >>fmt.fmt.pix.sizeimage = %d\n", fmt.fmt.pix.sizeimage);  

    printf("    >>fmt.fmt.pix.bytesperline = %d\n", fmt.fmt.pix.bytesperline);  

    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

    printf("\n");  

    switch (io) {  

    case IO_METHOD_READ:  

        init_read(fmt.fmt.pix.sizeimage);  

        break;  

    case IO_METHOD_MMAP:  

        init_mmap();  

        break;  

    case IO_METHOD_USERPTR:  

        init_userp(fmt.fmt.pix.sizeimage);  

        break;  

    }  
    printf("init_device\n");
}  

static void close_device(void) {  

    if (-1 == close(fd))  

        errno_exit("close");  

    fd = -1;  

}  

static void open_device(void) {  

    struct stat st;  

    if (-1 == stat(dev_name, &st)) {  

        fprintf(stderr, "Cannot identify '%s': %d, %s\n", dev_name, errno,  

                strerror(errno));  

        exit(EXIT_FAILURE);  

    }  

    if (!S_ISCHR(st.st_mode)) {  

        fprintf(stderr, "%s is no device\n", dev_name);  

        exit(EXIT_FAILURE);  

    }  

    fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);  

    if (-1 == fd) {  

        fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_name, errno,  

                strerror(errno));  

        exit(EXIT_FAILURE);  

    }  

}  

static void usage(FILE * fp, int argc, char ** argv) {  

    fprintf(fp, "Usage: %s [options]\n\n"  

        "Options:\n"  

        "-d | --device name   Video device name [/dev/video0]\n"  

        "-h | --help          Print this message\n"  

        "-m | --mmap          Use memory mapped buffers\n"  

        "-r | --read          Use read() calls\n"  

        "-u | --userp         Use application allocated buffers\n"  

        "", argv[0]);  

}  

static const char short_options[] = "d:hmru";  

static const struct option long_options[] = { { "device", required_argument,  

        NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "mmap", no_argument,  

        NULL, 'm' }, { "read", no_argument, NULL, 'r' }, { "userp",  

        no_argument, NULL, 'u' }, { 0, 0, 0, 0 } };  

static void init_fb(struct fb_var_screeninfo *vinfo)
{
    int fbfd = 0;
    unsigned long screensize = 0;

    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (!fbfd) {
        printf("Error: cannot open framebuffer device.\n");
        exit(1);
    }
    printf("The framebuffer device was opened successfully.\n");

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, vinfo)) {
        printf("Error reading variable information.\n");
        exit(1);
    }

    printf("%dx%d, %dbpp\n", vinfo->xres, vinfo->yres, vinfo->bits_per_pixel);
//    if (vinfo.bits_per_pixel != 16) {
//        printf("Error: not supported bits_per_pixel, it only supports 16 bit color\n");
//        exit(1);
//    }

    // Figure out the size of the screen in bytes
    screensize = vinfo->xres * vinfo->yres * 4;
    printf("screensize %d\n", screensize);
    // Map the device to memory
    fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
        fbfd, 0);
    if ((int)fbp == -1) {
        printf("Error: failed to map framebuffer device to memory.\n");
        exit(4);
    }
    printf("The framebuffer device was mapped to memory successfully.\n");
}

int main(int argc, char ** argv)
{
    struct fb_var_screeninfo vinfo;
    dev_name = "/dev/video0";  

    outf = fopen("out.yuv", "wb");  

    for (;;) {  

        int index;  

        int c;  

        c = getopt_long(argc, argv, short_options, long_options, &index);  

        if (-1 == c)  

            break;  

        switch (c) {  

        case 0:   

            break;  

        case 'd':  

            dev_name = optarg;  

            break;  

        case 'h':  

            usage(stdout, argc, argv);  

            exit(EXIT_SUCCESS);  

        case 'm':  

            io = IO_METHOD_MMAP;  

            break;  

        case 'r':  

            io = IO_METHOD_READ;  

            break;  

        case 'u':  

            io = IO_METHOD_USERPTR;  

            break;  

        default:  

            usage(stderr, argc, argv);  

            exit(EXIT_FAILURE);  

        }  

    }  
    io = IO_METHOD_MMAP;

    open_device();  

    init_fb(&vinfo);

    init_device(&vinfo);

    start_capturing();  

    mainloop();  

    printf("\n");  

    stop_capturing();  

    fclose(outf);  

    uninit_device();  

    close_device();  

    exit(EXIT_SUCCESS);  

    return 0;  

}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值