[2021]Linux下C语言qrencode二维码生成库的基本使用和ARM开发板移植
Linux下使用qrencode的资料还是蛮少的,总会出现一些奇怪的问题。在解决问题的时候,大多数都是在windows下结合QT来使用的。所以我在Linux下解决使用的问题也废了挺多功夫,本文将详细的描述。我就纳闷了,为什么非得用QT呢,百度出来的文章几乎都是一样的。我就想写一个不一样的。
文章目录
一、前言
因为工作中的产品设备中需要一个根据设备的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
更多推荐
所有评论(0)