利用VSCode+cmake+GDB+gdbserver调试IMX6ULL的Linux C应用程序

Linux C应用开发系列

第一篇 利用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文件,该文件需要根据实际情况配置;

二、详细步骤

2.1 在VSCode里创建工程

目录结构
在这里插入图片描述
最终的目录结构就如下所示
├── 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#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

2.2 编写各级目录下的CMakeLists.txt文件

从我们的目录结构可以看出,我们需要编写三份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)

2.3 编写arm-linux-setup.cmake配置文件

上面我们准备好了各级目录下的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调试;
如果不需要调试的话注释掉即可,非常方便

2.4 用camke工具构建生成Makefile

下面执行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 选项用于指定配置文件,“=”号后面的内容是它的值,也就是它指定的配置文件

2.5 make编译工程+拷贝可执行文件

我们得到了Makefile文件后,用make命令编译工程,编译完毕后输入下面命令拷贝可执行文件到I.MX6ULL ARM 开发板上
cp bin/gdb_gpioAPP /home/yxm/linux/nfs/rootfs/bin

2.6 VSCode+GDB调试配置

使用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"
        }
    ]
}

三、实验测试

3.1 运行 cmake + make 编译生成可执行文件

在这里插入图片描述
编译完成工程之后,如果再次执行cmake,会提示一个CMake Warning

CMake Warning:
	Manually-specified variables were not used by the project:
		CMAKE_TOOLCHAIN_FILE

这是因为缓存没变化,再次配置的时候出现错误;这时候把先前编译生成的文件缓存清理干净,重新cmake之后就正常了
在这里插入图片描述

3.2 运行实验

如图所示,程序运行到main.c的断点
在这里插入图片描述
如图所示,程序运行到gpio_output.c的断点
在这里插入图片描述
程序运行结束
在这里插入图片描述

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当使用VSCodeCMake创建工程时,可以按照以下步骤进行操作: 1. 安装VSCode:首先,你需要下载并安装Visual Studio Code(简称VSCode),它是一个轻量级的代码编辑器,支持多种编程语言和插件扩展。 2. 安装CMake插件:在VSCode中,你可以通过安装CMake插件来支持CMake项目的创建和构建。打开VSCode,点击左侧的扩展图标(或按下Ctrl+Shift+X),搜索并安装"CMake Tools"插件。 3. 创建工程文件夹:在你的工作目录中创建一个新的文件夹,用于存放你的CMake工程文件。 4. 创建CMakeLists.txt文件:在工程文件夹中创建一个名为"CMakeLists.txt"的文件,该文件是CMake项目的核心配置文件。在该文件中,你需要指定项目的名称、源文件、编译选项等信息。 5. 配置CMake插件:打开VSCode的设置(按下Ctrl+逗号),搜索"CMake"相关的设置项,并根据你的需求进行配置。例如,你可以设置CMake的路径、构建目录、生成器等。 6. 构建项目:在VSCode中打开工程文件夹,点击左侧的CMake图标(或按下Ctrl+Shift+P,输入"CMake: Build"),选择合适的构建目标进行项目构建。CMake插件会自动执行CMake命令,生成构建系统所需的Makefile或其他构建文件。 7. 运行和调试:完成项目构建后,你可以使用VSCode调试功能来运行和调试你的程序。配置调试器(如GDB)并设置断点,然后点击调试按钮即可开始调试

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值