一、前言

因为工作中的产品设备中需要一个根据设备的IP地址,动态生成二维码的功能。用户扫描这个二维码之后就可以访问设备中运行的web服务,然后就可以用手机配置相关的信息了。

因为我们的嵌入式设备使用的是C语言,所以如何不想自己从头造轮子的话,就需要找一个比较合适的C语言的二维码生成的库。

网上有很多库,最终确定了一位日本的大神所写的qrencode,库是libqrencode。

qrencode官网:https://fukuchi.org/works/qrencode/

qrencode开源github地址:https://github.com/fukuchi/libqrencode

可以看到关注qrencode的人还是挺多的。
在这里插入图片描述

也从网上搜了一下,有不少人对其有很好的评价。所以决定使用qrencode作为我们的二维码生成库。

Linux下使用qrencode的资料还是蛮少的,总会出现一些奇怪的问题。

在解决问题的时候,大多数都是在windows下结合QT来使用的。所以我在Linux下解决使用的问题也废了挺多功夫,本文将详细的描述。

我就纳闷了,为什么非得用QT呢,百度出来的文章几乎都是一样的。我就想写一个不一样的。

二、准备所用到的环境以及版本信息

1、Ubuntu和内核版本

我也在ubuntu20的版本上做过实验,没问题

zh@zh:~$ uname -a
Linux zh 4.18.0-15-generic #16~18.04.1-Ubuntu SMP Thu Feb 7 14:06:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
zh@zh:~$

2、gcc和g++版本

我在Ubuntu20上做过实验,上面gcc和g++都是9.x完全没问题。

zh@zh:~$ gcc --version
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

zh@zh:~$
zh@zh:~$
zh@zh:~$ g++ --version
g++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

zh@zh:~$

3、交叉编译gcc和g++版本

怎么安装就不介绍了,看到本篇文章应该都是老手了吧。
因为我自己的开发板是arm架构的,所以我安装了arm架构的交叉编译工具。

zh@zh:~$ arm
arm2hpdl                        arm-linux-gnueabihf-gcc-6.2.1   arm-linux-gnueabihf-ld.gold
arm-linux-gnueabihf-addr2line   arm-linux-gnueabihf-gcc-ar      arm-linux-gnueabihf-nm
arm-linux-gnueabihf-ar          arm-linux-gnueabihf-gcc-nm      arm-linux-gnueabihf-objcopy
arm-linux-gnueabihf-as          arm-linux-gnueabihf-gcc-ranlib  arm-linux-gnueabihf-objdump
arm-linux-gnueabihf-c++         arm-linux-gnueabihf-gcov        arm-linux-gnueabihf-ranlib
arm-linux-gnueabihf-c++filt     arm-linux-gnueabihf-gcov-tool   arm-linux-gnueabihf-readelf
arm-linux-gnueabihf-cpp         arm-linux-gnueabihf-gdb         arm-linux-gnueabihf-size
arm-linux-gnueabihf-dwp         arm-linux-gnueabihf-gfortran    arm-linux-gnueabihf-strings
arm-linux-gnueabihf-elfedit     arm-linux-gnueabihf-gprof       arm-linux-gnueabihf-strip
arm-linux-gnueabihf-g++         arm-linux-gnueabihf-ld
arm-linux-gnueabihf-gcc         arm-linux-gnueabihf-ld.bfd
zh@zh:~$
zh@zh:~$
zh@zh:~$ arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc (Linaro GCC 6.2-2016.11) 6.2.1 20161016
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

zh@zh:~$
zh@zh:~$ arm-linux-gnueabihf-g++ --version
arm-linux-gnueabihf-g++ (Linaro GCC 6.2-2016.11) 6.2.1 20161016
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

zh@zh:~$
zh@zh:~$

4、开发板信息

是前段时间在淘宝韦东山老师的淘宝店里购买的100ask_imx6ull pro开发板。

就长这个样子:是arm架构的。

在这里插入图片描述

开发板内核版本如下:

[root@100ask:~]# uname -a
Linux 100ask 4.9.88 #1 SMP PREEMPT Sat Jul 24 07:41:11 EDT 2021 armv7l GNU/Linux
[root@100ask:~]#

三、开发环境编译&安装qrencode

1、下载qrencode源代码

打开:https://fukuchi.org/works/qrencode/

现在最新的就是4.1.1版本:
在这里插入图片描述
下载后:
在这里插入图片描述

2、上传至Linux中

可以使用FileZilla工具,以及任何能上传到Linux中的工具都可以。

上传后:

zh@zh:~$ ls -al qrencode-4.1.1.tar.gz
-rw-rw-r-- 1 zh zh 543245 Aug  7 10:36 qrencode-4.1.1.tar.gz
zh@zh:~$

解压出来:

zh@zh:~$ tar zxvf qrencode-4.1.1.tar.gz

3、编译和安装

因为我们现在是编译和安装在开发的环境中,所以可以忽略一些配置信息,直接默认即可。

配置:

zh@zh:~/qrencode-4.1.1$ ./configure

在这里插入图片描述

编译:

zh@zh:~/qrencode-4.1.1$ make

在这里插入图片描述

安装:

zh@zh:~/qrencode-4.1.1$ sudo make install

在这里插入图片描述

可以看到安装到的一些目录:

----------------------------------------------------------------------
 /bin/mkdir -p '/usr/local/bin'
  /bin/bash ./libtool   --mode=install /usr/bin/install -c qrencode '/usr/local/bin'
libtool: install: /usr/bin/install -c .libs/qrencode /usr/local/bin/qrencode
 /bin/mkdir -p '/usr/local/include'
 /usr/bin/install -c -m 644 qrencode.h '/usr/local/include'
 /bin/mkdir -p '/usr/local/share/man/man1'
 /usr/bin/install -c -m 644 qrencode.1 '/usr/local/share/man/man1'
 /bin/mkdir -p '/usr/local/lib/pkgconfig'
 /usr/bin/install -c -m 644 libqrencode.pc '/usr/local/lib/pkgconfig'
make[2]: Leaving directory '/home/zh/qrencode-4.1.1'
make[1]: Leaving directory '/home/zh/qrencode-4.1.1'
zh@zh:~/qrencode-4.1.1$

安装好,就可以看到了:

zh@zh:~/qrencode-4.1.1$
zh@zh:~/qrencode-4.1.1$ ls /usr/local/include/
qrencode.h
zh@zh:~/qrencode-4.1.1$
zh@zh:~/qrencode-4.1.1$
zh@zh:~/qrencode-4.1.1$ ls /usr/local/bin/
qrencode
zh@zh:~/qrencode-4.1.1$
zh@zh:~/qrencode-4.1.1$ ls /usr/local/lib/
libqrencode.la  libqrencode.so  libqrencode.so.4  libqrencode.so.4.1.1  pkgconfig  python2.7  python3.6
zh@zh:~/qrencode-4.1.1$

我分别在Ubuntu18(gcc v7.x)和Ubuntu20(gcc v9.x)上编译安装过,没遇到过任何问题。有问题,也可以一起讨论。

4、编写qrencode测试程序

下面这是一个经过我测试,非常完整的一个测试程序。
核心代码:只有这一个QRcode_encodeString方法

//main.cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <qrencode.h>

using namespace std;

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;

//位图文件头定义;
typedef struct tagBITMAPFILEHEADER
{
    //WORD bfType;//单独读取,结构体中就不定义了
    DWORD bfSize;     //文件大小
    WORD bfReserved1; //保留字
    WORD bfReserved2; //保留字
    DWORD bfOffBits;  //从文件头到实际位图数据的偏移字节数
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER
{
    DWORD biSize;          //信息头大小
    DWORD biWidth;         //图像宽度
    DWORD biHeight;        //图像高度
    WORD biPlanes;         //位平面数,必须为1
    WORD biBitCount;       //每像素位数
    DWORD biCompression;   //压缩类型
    DWORD biSizeImage;     //压缩图像大小字节数
    DWORD biXPelsPerMeter; //水平分辨率
    DWORD biYPelsPerMeter; //垂直分辨率
    DWORD biClrUsed;       //位图实际用到的色彩数
    DWORD biClrImportant;  //本位图中重要的色彩数
} BITMAPINFOHEADER;        //位图信息头定义

//像素信息
typedef struct tagIMAGEDATA
{
    BYTE blue;
    BYTE green;
    BYTE red;
} DATA;




int main()
{
	//可以替换成自己想设置的内容
    //const char *QRTEXT = "哈哈,";

	//也可以放置链接,进行跳转
    const char *QRTEXT = "https://blog.csdn.net/qq_17623363";
	
    QRcode *qrCode;
    int version = 5; //设置版本号,这里设为5,对应尺寸:37 * 37
    QRecLevel level = QR_ECLEVEL_H;
    QRencodeMode hint = QR_MODE_8;
    int casesensitive = 1;

    qrCode = QRcode_encodeString(QRTEXT, version, level, hint, casesensitive);
    if (NULL == qrCode)
    {
        printf("QRcode create fail\n");
        return -1;
    }

    //将要生成的二维码保存为BMP真彩色图片文件
    FILE *pf = fopen("qrcode.bmp", "wb");
    if (NULL == pf)
    {
        printf("file open fail.\n");
        fclose(pf);
        return -1;
    }
    int width = qrCode->width;
    int height = qrCode->width;
    int biCount = 24;                                 //真彩色
    int lineByte = (width * biCount / 8 + 3) / 4 * 4; //每line字节数必须为4的倍数
    //位图文件头
    BITMAPFILEHEADER bitMapFileHeader;
    //bitMapFileHeader.bfType = 0x4D42;
    bitMapFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lineByte * height;
    bitMapFileHeader.bfReserved1 = 0;
    bitMapFileHeader.bfReserved2 = 0;
    bitMapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    //位图信息头
    BITMAPINFOHEADER bitMapInfoHeader;
    bitMapInfoHeader.biBitCount = biCount;
    bitMapInfoHeader.biClrImportant = 0;
    bitMapInfoHeader.biClrUsed = 0;
    bitMapInfoHeader.biCompression = 0;
    bitMapInfoHeader.biHeight = height;
    bitMapInfoHeader.biPlanes = 1;
    bitMapInfoHeader.biSize = 40;
    bitMapInfoHeader.biSizeImage = lineByte * (height);
    bitMapInfoHeader.biWidth = width;
    bitMapInfoHeader.biXPelsPerMeter = 0;
    bitMapInfoHeader.biYPelsPerMeter = 0;

    WORD bfType = 0x4D42;

    //BM类型
    fwrite(&bfType, sizeof(WORD), 1, pf);
    //写文件头进文件
    fwrite(&bitMapFileHeader, sizeof(BITMAPFILEHEADER), 1, pf);
    //写位图信息头进文件
    fwrite(&bitMapInfoHeader, sizeof(BITMAPINFOHEADER), 1, pf);
    unsigned char *pBMPData = new unsigned char[lineByte * height];
    memset(pBMPData, 255, lineByte * height);

    unsigned char *qrData = qrCode->data;
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < lineByte / 3; j++)
        {
            if (*(qrData)&1)
            {
                //设置rgb颜色,可自定义设置,这里设为黑色。
                *(pBMPData + lineByte * i + 3 * j) = 0;
                *(pBMPData + lineByte * i + 3 * j + 1) = 0;
                *(pBMPData + lineByte * i + 3 * j + 2) = 0;
            }
            qrData++;
        }
    }

    //写数据进文件
    fwrite(pBMPData, sizeof(unsigned char), lineByte * height, pf);
    fclose(pf);
    delete[] pBMPData;
    pBMPData = NULL;

    QRcode_free(qrCode);
    return 0;
}

5、把该c++文件上传到Ubuntu中

上传后的:

zh@zh:~/qrcode$ ls
main.cpp
zh@zh:~/qrcode$

编译:

zh@zh:~/qrcode$ g++ main.cpp -o QRTest -I/usr/local/include -L/usr/local/lib -lqrencode
zh@zh:~/qrcode$
zh@zh:~/qrcode$ ll
-rw-rw-r--  1 zh zh  4254 Aug  8 03:22 main.cpp
-rwxrwxr-x  1 zh zh 13168 Aug  8 03:27 QRTest*
zh@zh:~/qrcode$

执行可执行程序,生成二维码:

zh@zh:~/qrcode$ ./QRTest
zh@zh:~/qrcode$
zh@zh:~/qrcode$ ll
-rw-rw-r--  1 zh zh  4254 Aug  8 03:22 main.cpp
-rw-rw-r--  1 zh zh  4198 Aug  8 03:27 qrcode.bmp
-rwxrwxr-x  1 zh zh 13168 Aug  8 03:27 QRTest*
zh@zh:~/qrcode$

qrcode.bmp就是我们生成的二维码图片,我们可以将这个图片下载到windows上,然后进行查看;如果你是ubuntu的图形界面的话,也可以直接查看。

下面这个就是我们想要的二维码图片:
在这里插入图片描述

可以扫描试试哦。

这样开发环境就配置成功了!

四、ARM开发板移植和测试qrencode

在编译的时候,我们可以选择编译成静态库或者动态库。
关于静态库和动态库的基本知识,不在本篇文章的讨论访问之内,所以需要您自己去了解。

关于我交叉编译工具的版本信息,前面已经说了,这里就不再赘述了。

1、编译成动态库


#创建一个目录存放动态库安装的路径,方便我们查找和移植
mkdir /home/zh/libqrencode-arm-so
 
#解压源代码
tar xvzf qrencode-4.1.1.tar.gz

# 进入解压后的qrencode-4.1.1目录
cd qrencode-4.1.1

# 配置一下prefix路径和交叉编译工具链的前缀
./configure --prefix=/home/zh/libqrencode-arm-so --host= arm-linux-gnueabihf --without-tools
 
#编译
make
#安装
make install

如果不出错的话,就成功了。

如果我们移植的话,就需要下面这些东西

zh@zh:~$ ls /home/zh/libqrencode-arm-so/
include  lib
zh@zh:~$
zh@zh:~$
zh@zh:~$ tree /home/zh/libqrencode-arm-so
/home/zh/libqrencode-arm-so
├── include
│   └── qrencode.h
└── lib
    ├── libqrencode.la
    ├── libqrencode.so -> libqrencode.so.4.1.1
    ├── libqrencode.so.4 -> libqrencode.so.4.1.1
    ├── libqrencode.so.4.1.1
    └── pkgconfig
        └── libqrencode.pc

3 directories, 6 files
zh@zh:~$
zh@zh:~$

2、编译成静态库


#创建一个目录存放动态库安装的路径,方便我们查找和移植
mkdir /home/zh/libqrencode-arm-static 
 
#解压源代码
tar xvzf qrencode-4.1.1.tar.gz

# 进入解压后的qrencode-4.1.1目录
cd qrencode-4.1.1

# 配置一下prefix路径和交叉编译工具链的前缀
./configure --prefix=/home/zh/libqrencode-arm-static --host=arm-linux-gnueabihf --without-tools --enable-static --disable-shared

#编译
make
#安装
make install

安装成功后:

zh@zh:~$ ls /home/zh/libqrencode-arm-static/
include  lib
zh@zh:~$
zh@zh:~$ tree /home/zh/libqrencode-arm-static/
/home/zh/libqrencode-arm-static/
├── include
│   └── qrencode.h
└── lib
    ├── libqrencode.a
    ├── libqrencode.la
    └── pkgconfig
        └── libqrencode.pc

3 directories, 4 files
zh@zh:~$

3、编译安装Linux x86

#配置,可以自行配置的
./configure
#编译
make
#安装
make install

4、交叉编译测试程序

为了简单好理解,没有使用Makefile的形式

zh@zh:~/qrcode$ arm-linux-gnueabihf-g++  -I/home/zh/libqrencode-arm-so/include   -c -o main.o main.cpp
zh@zh:~/qrcode$
zh@zh:~/qrcode$
zh@zh:~/qrcode$ arm-linux-gnueabihf-g++ -o Qrcode -g main.o -I/home/zh/libqrencode-arm-so/include  -L/home/zh/libqrencode-arm-so/lib -lqrencode

交叉编译之后我们在Ubuntu开发环境上是无法正常执行的:

zh@zh:~/qrcode$ ls
main.cpp   Qrcode  qrcode.bmp  QRTest
zh@zh:~/qrcode$

#交叉编译的不可以执行
zh@zh:~/qrcode$ ./Qrcode
-bash: ./Qrcode: cannot execute binary file: Exec format error
zh@zh:~/qrcode$

#原始的可以执行
zh@zh:~/qrcode$ ./QRTest 
zh@zh:~/qrcode$

可以查看一下版本信息:

可以看到QRTest是x86-64平台的;
而Qrcode是ARM平台的。

zh@zh:~/qrcode$ file QRTest
QRTest: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=65e6c48fca725e09171b5cb79924671810e5e7ad, not stripped
zh@zh:~/qrcode$
zh@zh:~/qrcode$
zh@zh:~/qrcode$ file Qrcode
Qrcode: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=942712d2411951cb747b0dcfed3302993acc8de8, with debug_info, not stripped
zh@zh:~/qrcode$
zh@zh:~/qrcode$

4、以动态库为例移植

(1)把测试代码和动态库的所有文件拷贝到开发板中

我是挂载的一个nfs,这样就方便调试了。

[root@100ask:/mnt]# df
Filesystem                        1K-blocks     Used Available Use% Mounted on
/dev/root                           1497056   605844    809008  43% /
devtmpfs                              87344        0     87344   0% /dev
tmpfs                                251696        0    251696   0% /dev/shm
tmpfs                                251696      304    251392   1% /tmp
tmpfs                                251696      512    251184   1% /run
192.168.1.200:/home/zh/nfs_rootfs 277709056 23534592 239997952   9% /mnt
[root@100ask:/mnt]#
[root@100ask:/mnt]#
[root@100ask:/mnt]# pwd
/mnt
[root@100ask:/mnt]#

把必要的文件拷贝到nfs共享的目录中:

zh@zh:~$ cp -arf /home/zh/libqrencode-arm-so /home/zh/nfs_rootfs/
zh@zh:~$
zh@zh:~$ cp -arf /home/zh/qrcode /home/zh/nfs_rootfs/
zh@zh:~$

那么我们就可以在开发板上看到了:

[root@100ask:/mnt/qrcode]# ls
QRTest  Qrcode  main.cpp  qrcode.bmp
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]# ls /mnt/libqrencode-arm-so/
include  lib

为了更好的测试是否成功,我们把原先生成的二维码图片删除

[root@100ask:/mnt/qrcode]# rm qrcode.bmp
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]# ls
Makefile  Makefile2  QRTest  Qrcode  main.cpp
[root@100ask:/mnt/qrcode]#

测试是否成功:

可以看到是不能成功的,显示缺少libqrencode.so.4文件

[root@100ask:/mnt/qrcode]# ./Qrcode
./Qrcode: error while loading shared libraries: libqrencode.so.4: cannot open shared object file: No such file or directory
[root@100ask:/mnt/qrcode]#

我们把交叉编译后的动态库文件拷贝到arm开发板上的/usr/lib目录下:


[root@100ask:/mnt/qrcode]# cp -p /mnt/libqrencode-arm-so/
include/ lib/
[root@100ask:/mnt/qrcode]# cp -p /mnt/libqrencode-arm-so/lib/
libqrencode.la        libqrencode.so.4      pkgconfig/
libqrencode.so        libqrencode.so.4.1.1
[root@100ask:/mnt/qrcode]# cp -p /mnt/libqrencode-arm-so/lib/libqrencode.so.4 /usr/lib/
[root@100ask:/mnt/qrcode]#

再次执行:

[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]# ./Qrcode
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]# ls
Makefile  Makefile2  QRTest  Qrcode  main.cpp  qrcode.bmp
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]#

把开发板中的图片拷贝出来,看下是否成功:

在这里插入图片描述

下载成功
在这里插入图片描述

可以看到是可以成功的:
在这里插入图片描述

在这里插入图片描述

五、存在的问题

生成的图片太小,还没时间找解决的办法。

据了解可以使用ffmpeg解决图像大小的问题。

六、参考资料

安装测试相关:
https://blog.csdn.net/zb774095236/article/details/107938644/

移植相关:
https://www.cnblogs.com/kuang17/p/7825323.html

编译相关:
https://blog.csdn.net/u014213012/article/details/51833299

Logo

更多推荐