第一篇 利用VSCode+cmake+GDB+gdbserver 实现I.MX6ULL ARM开发板的gdb在线调试
1.1 在Ubuntu下通过VSCode创建工程和工作区;
1.2 编写各级目录下的 CMakeLists.txt 文件;
1.3 编写 arm-linux-setup.cmake 配置文件,并在其中添加支持GDB调试的配置语句;
1.4 使用cmake工具构建生成Makefile;
1.5 使用make命令编译工程生成带调试信息的可执行文件,并将可执行文件拷贝到开发板中;
1.6 修改VSCode工程下面的launch.json文件,该文件需要根据实际情况配置;
目录结构:
最终的目录结构就如下所示:
├── build
│ ├── bin
│ │ └── gdb_gpioAPP
│ └── lib
│ └── libgdb_gpioAPP.a
├── CMakeLists.txt
├── libbsp
│ ├── CMakeLists.txt
│ ├── gpio_output.c
│ └── gpio_output.h
└── src
│ ├── CMakeLists.txt
│ └── main.c
└── arm-linux-setup.cmake
main.c
#include
#include
#include
#include
#include
#include
#include
#include "gpio_output.h"
int main(int argc, char *argv[])
{
int ret;
char file_path[100] = {0};
if((argc != 2) && ((argv[0] != Y_GPIO_OUTPUT_APP) || (argv[0] != Y_GDB_GPIO_OUTPUT_APP)))
{
perror("GPIO APP format error!!!\r\n");
exit(-1);
}
strcpy(file_path, Y_GPIO_PATH);
strcat(file_path, "/gpio");
strcat(file_path, argv[1]);
ret = access(file_path, F_OK); //F_OK -> Test for existence. 0表示成功,成功即意味文件存在;-1表示失败,失败即意味文件不存在
if(ret) //If the access function returns -1, the file does not exist
{
int export_file_fd, len;
char export_file_path[100] = {0};
strcpy(export_file_path, Y_GPIO_PATH);
strcat(export_file_path, "/");
strcat(export_file_path, Y_GPIO_OPERATIONS[GPIO_EXPORT]);
export_file_fd = open(export_file_path, O_WRONLY);
if(0 > export_file_fd)
{
perror("export_file open error!!!\r\n");
exit(-1);
}
len = strlen(argv[1]);
if(len != write(export_file_fd, argv[1], len))
{
perror("export_file write error!!!\r\n");
close(export_file_fd);
exit(-1);
}
close(export_file_fd);
}
y_gpio_config(file_path, Y_GPIO_CFG[GPIO_CFG_DIRECTION], Y_GPIO_DIRECTION_MODE[GPIO_DIRECTION_OUT]);
y_gpio_config(file_path, Y_GPIO_CFG[GPIO_CFG_ACTIVE_LOW], Y_GPIO_ACTIVE_LOW_CTRL[GPIO_ACTIVE_LOW_ZERO_MEAN_LOW]);
int seconds = 0;
while(seconds < 60)
{
y_gpio_config(file_path, Y_GPIO_CFG[GPIO_CFG_VALUE], Y_GPIO_VALUE_TYPE[GPIO_VALUE_LOW]);
sleep(1);
y_gpio_config(file_path, Y_GPIO_CFG[GPIO_CFG_VALUE], Y_GPIO_VALUE_TYPE[GPIO_VALUE_HIGH]);
sleep(1);
printf("The %d seconds.\r\n", seconds += 2);
}
exit(0);
}
gpio_output.c
#include
#include
#include
#include
#include
#include
#include
#include "gpio_output.h"
/* variables */
const char *const Y_GPIO_OPERATIONS[OPERATION_COUNT] = {"export","unexport"};
const char *const Y_GPIO_CFG[CFG_COUNT] = {"active_low","direction","edge","uevent","value"};
const char *const Y_GPIO_ACTIVE_LOW_CTRL[ACTIVE_LOW_COUNT] = {"0","1"};
const char *const Y_GPIO_DIRECTION_MODE[DRIECTION_COUNT] = {"out","in"};
const char *const Y_GPIO_EDGE_MODE[EDGE_COUNT] = {"none","rising","falling","both"};
const char *const Y_GPIO_VALUE_TYPE[VALUE_COUNT] = {"0","1"};
const char *const Y_GPIO_PATH = "/sys/class/gpio";
const char *const Y_GPIO_OUTPUT_APP = "./gpioAPP";
const char *const Y_GDB_GPIO_OUTPUT_APP = "./gdb_gpioAPP";
/* functions */
void y_gpio_config(const char *dir, const char *cfg, const char *val)
{
int cfg_fd, cfg_len;
char file_cfg[100] = {0};
strcpy(file_cfg, dir);
strcat(file_cfg, "/");
strcat(file_cfg, cfg);
cfg_fd = open(file_cfg, O_WRONLY);
if(0 > cfg_fd)
{
printf("gpio %s file open error!!!\r\n", cfg);
exit(-1);
}
cfg_len = strlen(val);
if(cfg_len != write(cfg_fd, val, cfg_len))
{
printf("gpio %s %s file write error!!!\r\n", cfg, val);
close(cfg_fd);
exit(-1);
}
close(cfg_fd);
}
gpio_output.h
#ifndef __GPIO_OUTPUT_
#define __GPIO_OUTPUT_
/* enum */
typedef enum{
GPIO_EXPORT = 0,
GPIO_UNEXPORT = 1,
OPERATION_COUNT,
}GPIO_OPERATIONS;
typedef enum{
GPIO_CFG_ACTIVE_LOW = 0,
GPIO_CFG_DIRECTION = 1,
GPIO_CFG_EDGE = 2,
GPIO_CFG_UEVENT = 3,
GPIO_CFG_VALUE = 4,
CFG_COUNT,
}GPIO_CONFIG;
typedef enum{
GPIO_ACTIVE_LOW_ZERO_MEAN_LOW = 0,
GPIO_ACTIVE_LOW_ZERO_MEAN_HIGH = 1,
ACTIVE_LOW_COUNT,
}GPIO_ACTIVE_LOW_CTRL;
typedef enum{
GPIO_DIRECTION_OUT = 0,
GPIO_DIRECTION_IN = 1,
DRIECTION_COUNT,
}GPIO_DIRECTION_MODE;
typedef enum{
GPIO_EDGE_NONE = 0,
GPIO_EDGE_RISING = 1,
GPIO_EDGE_FALLING = 2,
GPIO_EDGE_BOTH = 3,
EDGE_COUNT,
}GPIO_EDGE_MODE;
typedef enum{
GPIO_VALUE_LOW = 0,
GPIO_VALUE_HIGH = 1,
VALUE_COUNT,
}GPIO_VALUE_TYPE;
/* variables extern */
extern const char *const Y_GPIO_OPERATIONS[OPERATION_COUNT];
extern const char *const Y_GPIO_CFG[CFG_COUNT];
extern const char *const Y_GPIO_ACTIVE_LOW_CTRL[ACTIVE_LOW_COUNT];
extern const char *const Y_GPIO_DIRECTION_MODE[DRIECTION_COUNT];
extern const char *const Y_GPIO_EDGE_MODE[EDGE_COUNT];
extern const char *const Y_GPIO_VALUE_TYPE[VALUE_COUNT];
extern const char *const Y_GPIO_PATH;
extern const char *const Y_GPIO_OUTPUT_APP;
extern const char *const Y_GDB_GPIO_OUTPUT_APP;
/* functions extern */
extern void y_gpio_config(const char *dir, const char *cfg, const char *val);
#endif
从我们的目录结构可以看出,我们需要编写三份CMakeLists.txt文件。
分别是:
1.工程目录16_gpio下的顶层CMakeLists.txt,
2.16_gpio/libbsp下的CMakeLists.txt,
3.16_gpio/src下的CMakeLists.txt,
三份CMakeLists.txt内容如下
工程目录16_gpio下的顶层CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(gdb_gpioAPP)
add_subdirectory(libbsp)
add_subdirectory(src)
16_gpio/libbsp下的CMakeLists.txt
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
add_library(libbsp gpio_output.c)
set_target_properties(libbsp PROPERTIES OUTPUT_NAME "gdb_gpioAPP")
16_gpio/src下的CMakeLists.txt
include_directories(${PROJECT_SOURCE_DIR}/libbsp)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(gdb_gpioAPP main.c)
target_link_libraries(gdb_gpioAPP libbsp)
上面我们准备好了各级目录下的CMakeLists.txt。这里还需要准备一份配置文件——> arm-linux-setup.cmake;
因为默认情况下cmake是使用Ubuntu系统的编译器来编译我们的工程,得到的可执行文件只能在Ubuntu系统上运行,所以我们需要利用配置文件来设置交叉编译以及设置支持gdb+gdbserver调试,这样可执行文件才可以在ARM开发板上运行。
配置文件放在工程根目录下,内容如下
arm-linux-setup.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(TOOLCHAIN_DIR /opt/fsl-imx-x11/4.1.15-2.1.0/sysroots)
set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/cortexa7hf-neon-poky-linux-gnueabi)
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++)
set(CMAKE_C_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_CXX_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
##########################################################################################
#if you want to support the GDB Debug, you need to configure the following settings.
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -o0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -o3 -Wall")
###########################################################################################
可以看到配置文件最底下有一段添加注释说明的代码,这几个配置项用于cmake支持GDB调试;
如果不需要调试的话注释掉即可,非常方便
下面执行cmake命令时,指定了配置文件给cmake,让它能够配置交叉编译环境和GDB调试,并生成一份Makefile文件;
~/linux/tool/cmake-3.16.0-Linux-x86_64/bin/cmake -DCMAKE_TOOLCHAIN_FILE=../arm-linux-setup.cmake ..
-DCMAKE_TOOLCHAIN_FILE 选项用于指定配置文件,“=”号后面的内容是它的值,也就是它指定的配置文件
我们得到了Makefile文件后,用make命令编译工程,编译完毕后输入下面命令拷贝可执行文件到I.MX6ULL ARM 开发板上
cp bin/gdb_gpioAPP /home/yxm/linux/nfs/rootfs/bin
使用VSCode+gdbserver图形化调试嵌入式Linux C应用程序之前,需要根据实际情况配置launch.json文件;
我的launch.json配置内容如下:
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "gdb_gpioAPP",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/gdb_gpioAPP",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/bin",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"miDebuggerPath": "/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gdb",
"miDebuggerServerAddress": "192.168.0.25:2001"
}
]
}
编译完成工程之后,如果再次执行cmake,会提示一个CMake Warning
CMake Warning:
Manually-specified variables were not used by the project:
CMAKE_TOOLCHAIN_FILE
这是因为缓存没变化,再次配置的时候出现错误;这时候把先前编译生成的文件缓存清理干净,重新cmake之后就正常了