天天看点

嵌入式学习④——嵌入式系统调试

嵌入式系统调制

    • 前言
    • 一、代码的GDB调试
      • 1. GDB的使用
      • 2. GDB的调试
        • ① GDB编译
        • ② 段错误调试
      • 3. GDB的常用命令
        • ① list命令
        • ② run命令
        • ③ break命令
        • ④ print命令
    • 二、Opencv的使用
      • 1. opencv4.5.0的安装
      • 2. 使用opencv进行编译
      • 3. opencv打开摄像头
    • 总结
    • 附录
      • 一、GDB常用命令
      • 二、opencv常用方法及功能

前言

OpenCV是Intel开元计算机视觉库,它有一系列开源的C函数和少量C++类构成,实现了图像处理和计算机视觉很多通用的算法。

OpenCV特点:

1.拥有包括300多个C函数的跨平台的中、高层API;

2.跨平台:Windows/Linux;

3.Free:无论是对于商业还是非商业应用;

4.速度快;

5.使用方便。

一、代码的GDB调试

GDB : GNU Debugger,是GNU工程为GNU操作系统开发的调试器,但它的使用不局限于GNU操作系统, GDB可以运行在UNIX、Linux甚至Microsoft Windows。

使用GDB我们可以:

– 设置断点使程序停住

– 监视或修改程序中变量的值

– 跟踪代码执行过程

1. GDB的使用

• 程序要能被调试,必须包含调试信息

• 编译程序时,通过gcc的-g选项为程序添加调试信息,例如:

gcc –g –o helloworld helloworld.c
           
  • GDB的启动

打开终端输入gdb

嵌入式学习④——嵌入式系统调试

启动完成

2. GDB的调试

① GDB编译

下面的代码实现对一个整型数的反转功能,比如输入123,则输出321
#include <stdio.h>
void ShowRevertNum(int iNum)
{
 while (iNum > 10)
 {
printf("%d", iNum % 10);
 iNum = iNum / 10;
 }
 printf("%d\n", iNum);
}
int main(void)
{
 int iNum;
 printf("Please input a number :");
 scanf("%d", &iNum);
 printf("After revert : ");
 ShowRevertNum(iNum);
}
           

使用GDB编译以上代码

gcc -g -o Revert Revert.c
           
嵌入式学习④——嵌入式系统调试

编译成功

发现已生成可执行文件Revert

嵌入式学习④——嵌入式系统调试

使用GDB运行Revert文件

嵌入式学习④——嵌入式系统调试

执行成功

② 段错误调试

但是在有些情况下会出现段错误

示例代码:

#include<stdio.h>
#include<string.h>

#define BUFSIZE 256

static char acBuf[BUFSIZE];
static char *pStr;
int main(void)
{
        printf("请输入一个字符");
        gets(pStr);
        printf("\n你的字符是:%s\n",pStr);
}
           

编译后发现出现段错误

嵌入式学习④——嵌入式系统调试

段错误调试可以参考linux环境下使用gbd调试段错误

3. GDB的常用命令

① list命令

在gdb中运行list命令(缩写l)可以列出代码
嵌入式学习④——嵌入式系统调试

创建一段示例代码

#include<stdio.h>
int add(int a, int b)
 {
  return a + b;
 }
 int main()
 {
         int sum[10] =
          {
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0
          } ;
         int i;
          int array1[10] =
          {
                  48, 56, 77, 33, 33, 11, 226, 544, 78, 90
          };
          int array2[10] =
          {
                  85, 99, 66, 0x199, 393, 11, 1, 2, 3, 4
          };

          for (i = 0; i < 10; i++)
          {
                  sum[i] = add(array1[i], array2[i]);
          }
 }

           

list的具体形式包括:

  • list < linenum > ,显示程序第linenum行周围的源程序,如:
    嵌入式学习④——嵌入式系统调试
  • list < function > ,显示函数名为function的函数的源程序,如:
    嵌入式学习④——嵌入式系统调试

当行数不足时,将会显示最后几行代码

嵌入式学习④——嵌入式系统调试

② run命令

在gdb中,运行程序使用run命令

在程序运行前,我们可以设置如下4方面的工作环境:

  • 程序运行参数

set args 可指定运行时参数,如:set args 10 20 30 40 50;show args 命令可以查看设置好的运行参数。

  • 运行环境

path

可设定程序的运行路径;how paths可查看程序的运行路径;set environment varname [=value]用于设置环境变量,如set env USER=baohua;

show environment [varname]则用于查看环境变量。

嵌入式学习④——嵌入式系统调试
  • 工作目录

cd 相当于shell的cd命令;pwd 显示当前所在的目录。

嵌入式学习④——嵌入式系统调试
  • 程序的输入输出

info terminal 用于显示程序用到的终端的模式;

gdb中也可以使用重定向控制程序输出,如run > outfile;

tty命令可以指定输入输出的终端设备,如:tty /dev/ttyS1。

③ break命令

在gdb中用break命令来设置断点

设置断点的方法包括:

  • break < function >

在进入指定函数时停住,C++中可以使用class::function或function(type, type)格式来指定函数名。

嵌入式学习④——嵌入式系统调试
  • break < linenum >

在指定行号停住。

嵌入式学习④——嵌入式系统调试
  • info breakpoints [n] / info break [n] 可查看断点
    嵌入式学习④——嵌入式系统调试
    运行至断点,使用 whatis 函数查看iNum变量类型
嵌入式学习④——嵌入式系统调试

④ print命令

在调试程序时,当程序被停住时,可以使用print命令(缩写为p),或是同义命令inspect来查看当前程序的运行数据

print命令的格式是:

print <expr>
 print /<f> <expr>
           

当程序停住时,或是单步跟踪时,这些变量会自动显示。 如果要修改变量,如x的值,可使用如下命令:

print x=4
 x=4
           
嵌入式学习④——嵌入式系统调试

二、Opencv的使用

1. opencv4.5.0的安装

opencv安装参考教程如下:

ubuntu16.04安装opencv3.4.1教程

2. 使用opencv进行编译

嵌入式学习④——嵌入式系统调试

打开命令窗口test.cpp

输入代码

#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
	CvPoint center;
    double scale = -3; 

	IplImage* image = cvLoadImage("lena.jpg");
	argc == 2? cvLoadImage(argv[1]) : 0;
	
	cvShowImage("Image", image);
	
	
	if (!image) return -1; 	center = cvPoint(image->width / 2, image->height / 2);
	for (int i = 0;i<image->height;i++)
		for (int j = 0;j<image->width;j++) {
			double dx = (double)(j - center.x) / center.x;
			double dy = (double)(i - center.y) / center.y;
			double weight = exp((dx*dx + dy*dy)*scale);
			uchar* ptr = &CV_IMAGE_ELEM(image, uchar, i, j * 3);
			ptr[0] = cvRound(ptr[0] * weight);
			ptr[1] = cvRound(ptr[1] * weight);
			ptr[2] = cvRound(ptr[2] * weight);
		}

	Mat src;Mat dst;
	src = cvarrToMat(image);
	cv::imwrite("test.png", src);

    cvNamedWindow("test",1);  	imshow("test", src);
	 cvWaitKey();
	 return 0;
}

           

保存并编译

这一步要注意test文件的路径和类型要与代码一致
嵌入式学习④——嵌入式系统调试

运行test.cpp

./test
           
嵌入式学习④——嵌入式系统调试

3. opencv打开摄像头

参考代码:

#include<opencv2/opencv.hpp>
using namespace cv;

int main()
{
	VideoCapture capture(0);
	while(1)
	{	
		Mat frame;
		capture >> frame;
		imshow(“读取视频帧”,frame);
		waitKey(30);
	}
	
	system(“pause”);
	return 0;
}

           
嵌入式学习④——嵌入式系统调试

有几项需要注意

1) 如果要求打开你硬盘上一个视频文件来播放,请问第7行代码如何修改?

  • 如果需要打开硬盘上一个视频文件来播放,第7行代码应改为

    /home/Download/ awsl.avi

2)在第9行的while循环中,Mat是一个什么数据结构? 为什么一定要加一句waitKey延时代码,删除它行不行?
  • Mat是一个类。由两部分数据组成:矩阵头(包括矩阵尺寸、存储方法、存储地址等信息)和一个指向所有像素值的矩阵(根据所选存储方法不同,矩阵可以是不同的维数)的指针。
3)此代码会在while循环中一直运行,你如果试图用鼠标关闭图像显示窗口,会发现始终关不掉。需要用键盘Ctrl+C 强制中断程序,非常不友好。如何改进?
  • waitKey延时越长 fps越小 出现跳帧 摄像头显示变卡,故要有一定的延时,可以加一个break代码,如if(ch == 27) break;

参考代码<打开摄像头采集并保存>

/*********************************************************************
打开电脑摄像头,空格控制视频录制,ESC退出并保存视频RecordVideo.avi
*********************************************************************/
#include<iostream>
#include <opencv2/opencv.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;

void main()
{
	//打开电脑摄像头
	VideoCapture cap(0);
	if (!cap.isOpened())
	{
		cout << "error" << endl;
		waitKey(0);
		return;
	}

	//获得cap的分辨率
	int w = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_WIDTH));
	int h = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_HEIGHT));
	Size videoSize(w, h);
	VideoWriter writer("RecordVideo.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25, videoSize);
	
	Mat frame;
	int key;//记录键盘按键
	char startOrStop = 1;//0  开始录制视频; 1 结束录制视频
	char flag = 0;//正在录制标志 0-不在录制; 1-正在录制

	while (1)
	{
		cap >> frame;
		key = waitKey(100);
		if (key == 32)//按下空格开始录制、暂停录制   可以来回切换
		{
			startOrStop = 1 - startOrStop;
			if (startOrStop == 0)
			{
				flag = 1;
			}
		}
		if (key == 27)//按下ESC退出整个程序,保存视频文件到磁盘
		{
			break;
		}

		if (startOrStop == 0 && flag==1)
		{
			writer << frame;
			cout << "recording" << endl;
		}
		else if (startOrStop == 1)
		{
			flag = 0;
			cout << "end recording" << endl;
			
		}
		imshow("picture", frame);
	}
	cap.release();
	writer.release();
	destroyAllWindows();
}

           

打开摄像头效果如下:

嵌入式学习④——嵌入式系统调试

动图演示:

嵌入式学习④——嵌入式系统调试

总结

这次GDB及Opencv操作遇到了许多错误

解决方法如下:

嵌入式学习④——嵌入式系统调试

使用

(gdb) next

代替

(gdb) s

便可解决

嵌入式学习④——嵌入式系统调试

这是由于gedit命令自动在根目录生成文件,将

gcc test.cpp

改为

g++ /test.cpp

即可解决

附录

一、GDB常用命令

命 令 解 释 示 例
file <文件名>

加载被调试的可执行程序文件。

因为一般都在被调试程序所在目录下执行GDB,因而文本名不需要带路径

(gdb) file

gdb-sample

r

Run的简写,运行被调试的程序.如果此前没有下过断点,则执行完整个程序;

如果有断点,则程序暂停在第一个可用断点处。

(gdb) r
c Continue的简写,继续执行被调试程序,直至下一个断点或程序结束。 (gdb) c
s, n

s: 执行一行源程序代码,如果此行代码中有函数调用,则进入该函数;

n: 执行一行源程序代码,此行代码中的函数调用也一并执行。

(gdb) file

gdb-sample

b <行号>

b <函数名称>

b *<函数名称>

b *<代码地址>

d [编号]

b: Breakpoint的简写,设置断点。两可以使用“行号”“函数名称”“执行地址”等方式指定断点位置。

其中在函数名称前面加“*”符号表示将断点设置在“由编译器生成的prolog代码处”。如果不了解汇编,可以不予理会此用法。

d: Delete breakpoint的简写,删除指定编号的某个断点,或删除所有断点。断点编号从1开始递增。

(gdb) b 8

(gdb) b main

(gdb) b *main

(gdb) b *0x804835c

(gdb) d

二、opencv常用方法及功能

1、cvLoadImage:将图像文件加载至内存;

2、cvNamedWindow:在屏幕上创建一个窗口;

3、cvShowImage:在一个已创建好的窗口中显示图像;

4、cvWaitKey:使程序暂停,等待用户触发一个按键操作;

5、cvReleaseImage:释放图像文件所分配的内存;

6、cvDestroyWindow:销毁显示图像文件的窗口;

7、cvCreateFileCapture:通过参数设置确定要读入的AVI文件;

8、cvQueryFrame:用来将下一帧视频文件载入内存;

9、cvReleaseCapture:释放CvCapture结构开辟的内存空间;

10、cvCreateTrackbar:创建一个滚动条;

11、cvSetCaptureProperty:设置CvCapture对象的各种属性;

12、cvGetCaptureProperty:查询CvCapture对象的各种属性;

整理至Opencv常用方法