芯路恒电子技术论坛

 找回密码
 立即注册
热搜: 合集
查看: 7643|回复: 0

【Zynq】【Liunx】基于Zynq的Linux的摄像头图像采集发送

[复制链接]

该用户从未签到

68

主题

85

帖子

937

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
937
发表于 2022-8-17 17:46:45 | 显示全部楼层 |阅读模式

功能描述

该程序基于ACZ702/ACZ7015开发板的出厂固件环境,将OV5640摄像头采集的图像通过网口或wifi传输到PC端,并使用上位机查看传输的图像。如何恢复出厂固件环境,请查看【如何烧录ACZ702开发板的出厂固件

上位机软件、应用程序源码以及测试脚本见文章末尾的附件。

本例使用OV5640摄像头,购买链接【OV5640高清摄像头

应用程序源码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <dirent.h>
#include <inttypes.h>
#include <sys/time.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include "math.h"
#include <string.h>
#include <linux/fb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <sys/stat.h>
#include <errno.h>
#include <ctype.h>
#include <termios.h>

unsigned char STOP = 0;

void signalFunc(int _signal)
{
        STOP = 1;
}

#define MAP_SIZE 4096UL         //映射的内存区大小
#define MAP_MASK (MAP_SIZE - 1)     //MAP_MASK = 0XFFF

/**
* @brief 从实际物理地址读取数据。
* @details 通过 mmap 映射关系,找到对应的实际物理地址对应的虚拟地址,然后读取数据。
* 读取长度,每次最低4字节。
* @param[in] readAddr, unsigned long, 需要操作的物理地址。
* @param[out] buf,unsigned char *, 读取数据的buf地址。
* @param[in] bufLen,unsigned long , buf 参数的容量,4字节为单位,如 unsigned long buf[100],那么最大能接收100个4字节。
* 用于避免因为buf容量不足,导致素组越界之类的软件崩溃问题。
* @return len,unsigned long, 读取的数据长度,字节为单位。如果读取出错,则返回0,如果正确,则返回对应的长度。
*/
static int Devmem_Read(unsigned long readAddr, unsigned long* buf, unsigned long len)
{
    int i = 0;
    int fd,ret;
    int offset_len = 0;
    void *map_base, *virt_addr; 
    off_t addr = readAddr;
    unsigned long littleEndianLength = 0;

    if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
    {
        fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", __LINE__, __FILE__, errno, strerror(errno));
        return 0;
    }

    //将内核空间映射到用户空间
    map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & ~MAP_MASK);
    if(map_base == (void *) -1)
    {
        fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", __LINE__, __FILE__, errno, strerror(errno));
        close(fd);
        return 0;
    }

    for (i = 0; i < len; i++)
    {
        // 翻页处理
        if(offset_len >= MAP_MASK)
        {
            offset_len = 0;
            if(munmap(map_base, MAP_SIZE) == -1)
            {
                fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", __LINE__, __FILE__, errno, strerror(errno));
                close(fd);
                return 0;
            }
            map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & ~MAP_MASK);
            if(map_base == (void *) -1)
            {
                fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", __LINE__, __FILE__, errno, strerror(errno));
                close(fd);
                return 0;
            }
        }
        virt_addr = map_base + (addr & MAP_MASK);   // 将内核空间映射到用户空间操作
        buf[i] = *((unsigned long *) virt_addr);    // 读取数据
        addr += 4;
        offset_len += 4;
    }

    if(munmap(map_base, MAP_SIZE) == -1)
    {
        fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", __LINE__, __FILE__, errno, strerror(errno));
        close(fd);
        return 0;
    }
    close(fd);
    return i;
}


int main(int argc, char *argv[]) {

        unsigned long readData[192000];
        char ip_addr[50] = {0};

        signal(SIGINT, signalFunc);
        signal(SIGTERM, signalFunc);

        int nX = 640;
        int nY = 480;

        Devmem_Read((unsigned long)0x1800000, readData, 192000);        // 读取数据

        int retval;

    char buf[2048];
    int socket_descriptor; //套接口描述字
        struct sockaddr_in address;//处理网络通信的地址
        int port=6000;
        bzero(&address,sizeof(address));
        address.sin_family=AF_INET;
        sprintf(ip_addr,"192.168.%s",argv[1]);
        address.sin_addr.s_addr=inet_addr(ip_addr);//这里不一样
        address.sin_port=htons(port);
    //unsigned char *p;
        //创建一个 UDP socket

        socket_descriptor=socket(AF_INET,SOCK_DGRAM,0);//IPV4  SOCK_DGRAM 数据报套接字(UDP协议)

        int rbufsz = 5 * 1024 * 1024; //5M
        setsockopt(socket_descriptor, SOL_SOCKET, SO_RCVBUF, &rbufsz, sizeof(rbufsz));
        int wbufsz = 5 * 1024 * 1024; //5M
        setsockopt(socket_descriptor, SOL_SOCKET, SO_SNDBUF, &wbufsz, sizeof(wbufsz));

        int maxfdp;
        fd_set fds;
        fd_set fdsErr;
    struct timeval timeout = {1, 0};//设置select等待3秒,3秒轮询,非阻塞就置0
        int nSendFile = 0;
        long iter = 0;

    connect(socket_descriptor, (struct sockaddr *)&address,sizeof(address));

        while (!STOP)
        {

            FD_ZERO(&fds);//每次循环都要清空集合,否则不能检测描述符变化
                FD_SET(socket_descriptor, &fds);//添加描述符
                //FD_ZERO(&fdsErr);//每次循环都要清空集合,否则不能检测描述符变化
                //FD_SET(socket_descriptor, &fdsErr);//添加描述符
            maxfdp = socket_descriptor + 1;//描述符最大值加1
           // printf("1\n");
            retval = select(maxfdp, NULL, &fds, NULL, &timeout);
                if (retval <= 0)
                {
                        //usleep(1000);
                        continue;
                }
                //p = (unsigned char *)((nSendFile == 0) ? szBmp1 : szBmp2);
                buf[1]=iter >> 8;
                buf[0]=iter & 0xff;
                //memcpy(&buf[2],p,1280);
                memcpy(&buf[2], (unsigned char*)readData + iter * 1600, 1280);

            if(FD_ISSET(socket_descriptor,&fds))//测试skt是否可写,即网络上是否有数据
            {
                retval = write(socket_descriptor, buf, 1282);
                if(retval < 0){
                    perror("sendto error: ");
                    exit(1);
                }
            }

                iter++;

                if (iter > nY - 1)
                {
                        iter = 0;
                        Devmem_Read((unsigned long)0x1800000, readData, 192000);        // 读取数据
                        //nSendFile = (nSendFile + 1) % 2;
                        //usleep(1000);
                }
        }
        printf("app exit\n");

}



使用方法

(1)将该程序文件使用交叉编译器编译,得到可执行文件udp_send_video(附件中已提供)

arm-linux-gnueabihf-gcc udp_send_video.c -o udp_send_video

(2)将可执行文件“udp_send_video”和文章末尾的附件“send_video.sh”拷贝到开发板的Linux系统/home/root/路径下。

(3)查看电脑网口的ip,记住IP地址,例如下图就是192.168.6.176

image.png

(4)查看开发板的网口ip地址,注意开发板和电脑的IP地址必须在同一网段(此处也可使用板载wifi实现无线视频传输,但限于wifi性能,传输会很慢而且距离受限)

ifconfig

image.png

(5)输入以下命令运行脚本,开启视频传输,脚本后的参数为ip地址后两位,由步骤(3)可知我的电脑端ip地址为192.168.6.176,因此脚本后的参数为6.176

./send_video.sh 6.176

image.png

开机后的第一次运行会弹出配置信息,再次运行就不会弹出了。

(6)以"管理员权限"运行附件“小梅哥UDP摄像头V3.2.exe”程序,并按下图所示修改配置。

image.png

(7)修改完成后,点击连接,开始图像传输

image.png

(8)输入以下命令关闭摄像头传输

./send_video.sh 0

image.png

上位机源码

需要上位机源码,可查看该帖子【上位机源码

附件下载

小梅哥UDP摄像头V3.2.exe

3.13 MB, 下载次数: 652

udp_send_video.c

6.01 KB, 下载次数: 315

send_video.sh

317 Bytes, 下载次数: 309

udp_send_video

12.97 KB, 下载次数: 304

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|芯路恒电子技术论坛 |鄂ICP备2021003648号

GMT+8, 2024-11-21 17:18 , Processed in 0.117686 second(s), 35 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc. Template By 【未来科技】【 www.wekei.cn 】

快速回复 返回顶部 返回列表