移植zephyr到AT32_SURF_F437开发板

感谢Artery公司提供开发板,以及Artery各位工程师的技术支持。

背景

今年上半年收到了Artery公司 木一川 先生的邀请,有幸白嫖到了AT32的开发板。开发板搭载的AT32F437ZMT7芯片和其他国内芯片公司的xx32产品类似,芯片内核采用ARM Cortex-M4,外设和STM32F1相似。

题外话: 在写这篇文章的时候,翻阅了以前写的GD32移植时候看到当时刚刚考完日语N2,一年半时间飞逝,N1考试因为疫情连续取消了三回。

zephyr移植

基础知识:

请参考gd32移植

对于传统的嵌入式软件开发,可能会使用IAR、KEIL等IDE,而绝大多数的芯片厂家默认提供的BSP也往往基于这些工具进行开发。 然而对于开源项目来说,这些工具费用高昂,并且对工作流的细节控制很难实现。因此,对于第一次接触zephyr项目的朋友优先理解编译器(和目标平台架构相关,对于at32来说是gcc)和构建系统(cmake)的作用。

zephyr系统利用了大量linux的开发工具:

  • 为了解决c语言大量宏(marco)的依存关系,zephyr使用linux的kconfig工具来管理宏。
  • 为了解决各个平台的硬件区别,zephyr基于使用linux的devicetree框架对硬件设备从Board,SOC,Arch三个层级对硬件平台进行描述。但是zephyr项目的设备树会生成c语言头文件而不是dtb

为了对开发平台进行调试,一般会使用pyocd或者OpenOCD这两个工具来建立JTAG或者SWD连接,配合gdb对芯片进行调试。 一般gdb不会单独运行,在命令行中调试即使配合tui也是十分痛苦的,但是对于一些高手,利用hook,可以打造出更顺手的调试环境。我个人一般会配合vscode或者gdbgui等工具一同使用,这些GUI界面一般也可以使用hook,并且使用gdb的终端。

以上工具,一般在zephyr项目中也不会直接调用,而是使用west命令进行操作。

AT32移植

zephyr项目支持多种架构和大量厂商的芯片,一般情况下,我们只需要制作最外层的Board级别的移植,即可完成。

但是本次Artery公司尚未被zephyr项目支持,但是arm cortex-m4是被zephyr项目支持的架构arch,所以需要SOC级别的移植。

如果你使用的是RenesasRH850或者infineonTriCore系列芯片,那么你将会面对一个arch级别的移植,这会面对很大的工作量,目前我也没有经历过这个级别的适配。

  • 供应商支持
    • zephyr会对供应商进行管理,只有记录在vendor-prefix.txt内的供应商名称,才可以作为vendor出现在设备树中。
  • 板极支持(boardat_surf_F437)
    • 板级设备树
      • 这里一般会描述LED、按键、串口等板级外设的配置。
    • 板级kconfig配置
      • 对一些设定进行配置,例如LOG等级,IP地址等。
    • 调试工具配置
      • openocdpyocd以及各个厂家特有的下载工具等进行配置,以便对目标设备进行调试和烧写。
    • 文档
      • 一般会描述开发板的功能和简单的编译烧写方法。
  • 芯片级支持(SOCAT32F437ZMT7)
    • soc启动代码(soc.c)
      • 对系统启动进行一些初期设定,arm架构一般会调用cmsis接口SystemInit
    • 芯片级dts
      • 对芯片级设备进行描述,例如定时器,flash等。
    • 芯片级KCONFIG
      • 对芯片系列进行定义。
    • 芯片片内外设驱动
      • 串口、定时器等外设驱动。
      • 这部分代码会大量利用c语言宏的一些特性,熟练掌握可以很大程度的提升c语言宏的使用技巧。
  • 供应商硬件抽象层(对于本次移植来说是hal_artery)
    • 在适配驱动的过程中,如果完全重新写一遍外设驱动的话会产生很大的工作量;同时一般芯片厂商会提供一定的代码以便加速开发。但是,这些代码的维护和zephyr之间关系不大,因此作为module存在。
    • 在使用这些代码的时候,我们需要十分小心。因为c语言没有package或者namespace这些方法对符号的作用域进行限缩,所以在引入供应商HAL代码的时候,符号会重名,这种时候需要对HAL名称进行修正。万幸是这些修正项目往往比较规则,即使在版本更新后,一般也只需要再做一遍即可。如果希望更加一劳永逸的办法,建议了解coccinelle
    • HAL库的另外一大工作量是芯片的引脚复用的管理(pinctrl)。一般情况下,我们对引脚的处理仅仅局限在某一个系列的兼容,然而在zephyr项目中,我们需要对整个供应商的引脚IP进行描述。并且,zephyr项目一般不允许手动修改这些描述,因此制作一个脚本程序是必要的,例如:hal_gigadevice: gd32pinctrl.py
    • 作为zephyr模块,根目录应当包含zephyr文件夹和module.yml文件。这些文件的修改可以参考west工具的说明。

应用篇 (zephyr-SUMP

谈论了很久的移植,最后来聊一下产品的开发。本节将简单的说明下zephyr应用开发的方式,并做一个简单的逻辑分析仪。为了简单起见,本次没有采用out-of-tree的开发方式,而是将代码放在了app文件夹内。

SUMP与SIGROK

  • sigrok
    • 这是一个支持大量设备的开源逻辑分析仪程序(也支持示波器、电源等设备)。
  • SUMP
    • SUMP是一个基于串口(UART/USBCDC)的逻辑分析仪传输协议。

基于这两个工具,我们实现逻辑分析仪的技术准备就基本完成了。

开发APP

为了获得更高的采样速度,需要在APP层级重新定义端口驱动,以绕开zephyr的IO驱动,较少操作系统抽象造成的性能损失。

  • sample文件夹下有大量例子,可以简单的复制出来,作为我们开发的基础。
  • APP KCONFIG设置
    • 对于app级别的宏进行描述,这些宏可以根据实际使用的目标平台进行调整。例如:采样频率,采样数据长度等。
  • 板级设备树覆写
    • 对于逻辑分析仪来说,为了更快的采样速度,我们需要更快的GPIO驱动,而zephyr提供的接口由于抽象,会损失些许性能。
  • app层驱动
    • 这里是对于不同芯片的端口驱动。不同芯片根据资源不同,可能会提供不同数量的端口数和采样方法。例如:
      • Zephyr GPIO方法
      • 汇编方法(目前只实现了ARM)
      • Timer + DMA方法(目前没有做)
  • app
    • 启动了一个测试用pwm(绿灯)
    • SUMP协议的处理
  • 文档
    • 一些文档更加利于别人理解这个项目的用途
  • todos
    • 这个app是我头脑一热做的,还有很多不完善的地方,暂时记录在这里

图片

开发板照片

PulseView采样

IAR ARM 9.30 破解

由于突如其来的疫情,被迫在宅工作。

匆忙中从公司拿了IAR的功能安全版本的加密狗回家,但是有的同事拿的是普通版本的IAR,为了确保大家环境都OK,所以只能无奈破解下以便确认工程正常。简单来说,和之前破解IAR CCRH的逻辑类似。

只记录破解方法,不提供破解结果。

使用雷锋破解器激活

这一步直接跳过吧,遍地都能搜索到。

然后新建工程,编译得到错误:The generation feature is not of version 18.

                 
  test - Debug
  Reading project nodes...
 
  main.c
  Fatal Error[LMS001]: License check failed. Use the IAR License Manager to resolve the problem.
  The generation feature is not of version 18.
 
  Total number of errors: 1
  Total number of warnings: 0
 
  Build failed
 ​

使用IDA打开iccarm.exe

  • strings窗口搜索The generation feature is not of version 18
  • 双击进入反汇编
  • X跳转至调用处

修改jgejl

  • 保存exe,重新编译,写个小错误也被成功识别

License窗口和调试

之前在RH850编译器破解时候有修改LicenseInfo.exerh850sim.dll

估计IAR ARM也是一样逻辑,不过这两个模块我暂时用不到,能试着编译成功就行了。

WIFI+USBIP+MCU=无线USB设备

请注意,本文提供方法基于zephyr OS制作,由于目前zephyr项目的USB子系统处于大规模重构状态,所以本方案不受zephyr正式支持。详细请参考:

https://github.com/zephyrproject-rtos/zephyr/pull/46862

背景

目前,我们有许多MCU项目利用USB协议与PC进行通信,绝大多数使用USB从机协议,随着Wifi协议的广泛使用以及速度的提升,我认为绝大多数的USB设备都可以经由网络协议而渐渐变得无线化。

但是,如果完全将协议栈转换为网络,会导致一系列兼容软件或驱动需要再次适配,较大的开发量和不明显的收益使得各大厂商在这方面进展缓慢。

本文介绍一种基于USBIP的USB设备无线化方法,希望能够推进设备线束的无线化进程。

USB with WIFI

灵感来源(废话部分,技术无关,可以跳过😄)

前段时间有幸从artery公司木一川先生处得到一片at32 demo板,正巧促使我简单的学习了一下openocd和cmsisdap。发现cmsis-dap协议很容易就可以在zephyr中引用,以获得大量开发板支持。

但是如果只是制作一个普通的dap仿真器,比较缺少挑战性,因此我决定尝试制作无线的仿真器。

最初的目标是通过TCP转发cmsis-dap协议,修改openocd以支援网络设备,但是后面我意识到这会导致windows的KEIL和IAR等IDE环境难以运用。

因此,我将目标转向USB设备的网络访问,因此了解了USBIP协议,并且偶然发现zephyr OS中对USBIP是有部分实现的,我只需要简单的将他扩展到其他设备即可,因此便有了本文。

方案介绍

再次提示,本方案虽然基于zephyr OS制作,但是由于zephyr OS的USB subsystem处于大规模重构状态,所以不受zephyr主线支持!

实现代码请参考:https://github.com/zephyrproject-rtos/zephyr/pull/46862

实际在下面设备中运行通过:

传统USB程序结构

这里指的传统zephyr USB 设备程序(因为zephyr系统目前没有host设备,这也是大规模重构的原因)。

如果您的程序目前采用其他方式架构(可能大部分程序都是吧),您首先需要将其移植到zephyr OS。(目前USB subsystem处于不稳定状态,商业用户需要谨慎考虑)

zephyr OS USB DEVICE

USBIP 网络USB设备

本方案工作在UDD层,通过注册zephyr USB device驱动,并将其转换为USBIP协议。因此没有虽然没有USB硬件,但是在操作系统看来和有USB驱动是一样的。

示意图

注意:

  • 这不是标准USBIP Server的实现,但是我认为对于绝大多数的USB设备已经足够实现功能

使用

MCU程序

建议创建如下application

  • 启动后的wifi配置:帐号,密码,功耗等(有线网络可忽略)
  • 静态IP或DHCP服务

简单使用教程

  • 使用usbip客户端,确认基本通信
usbip list -r FEILONG-CMSIS-DAP
  • 挂载usbip设备
sudo usbip attach -r FEILONG-CMSIS-DAP -b 1-1
  • 调试可以使用wireshark,内置有USBIP协议。

windows

请参考

https://github.com/cezanne/usbip-win

有些功能可能需要使用zadig安装驱动:

https://zadig.akeo.ie/

linux

linux用户应该不需要多说,参考archlinux wiki即可使用。

https://wiki.archlinux.org/title/USB/IP

注意:

在linux上使用时,如果在使用过程中,没有detach直接复位、断电等,会造成linux内核出现异常,重启后即可解决。(windows没有发现此类问题)

参考链接

IIC 通讯

写这篇文章的原因是本来我觉得IIC,SPI这种级别的协议早已了然于胸,直到今天在github被别人提醒,我才明白以前对着代码和各种中文资料知道的只是能凑合通讯,实际的规范中还有亿点点细节o.o

有点懒,基本协议内容就不再描述了(如果不了解可以看下下面规范原文),下面部分说明直接机翻

规范位置

比较重要的协议一览

  • 除了与高速模式相关的一种特殊情况外,不允许 I2C 设备驱动总线,即通过在总线上施加电压来发送 1。
    • 总线终止于高电平
  • Clock Stretching(不是所有从机都有这个功能,没有的话可以不实现)
    • 任何IIC总线设备(各个从机或主机)都可以要求延长SCL低电平时间甚至暂停通讯。
    • 规范中不包含等待超时(但是驱动中可能会有)
    • 一些从设备在超时一段时间之后会认为总线空闲
    • 这个功能对总线带宽有影响,使用时应当充分评估
    • 仅在 ACK 位之后(并且在下一个字节的第一个位之前)才允许在高速模式下进行时钟延长。位 2-9 之间的拉伸是非法的,因为这些位的边缘通过额外的电流源被提升。
  • i2c level shifting
  • Arbitration(单主机模式可以不实现)
    • 虽然看到协议想起来确实有这么回事,但是一般凑合用的时候就没实现过 😄
    • 能够遵循仲裁逻辑。如果两个设备同时开始通信,则向总线(或速度较慢的设备)写入更多零的设备赢得仲裁,而另一个设备立即停止总线上的任何操作。(这个没用过)
    • 总线繁忙检测。每个设备都必须检测正在进行的总线通信并且不得中断它。这是通过识别总线通信并在开始通信之前等待停止条件出现来实现的。(这个比较常见)
    • 如果使用多主机,那么所有主设备都应支持multimaster功能。
  • I2C标准定义了0.3 Vcc的低电平阈值,0.7 Vcc的高电平阈值。
  • 广播呼叫使用I2C 地址 0寻址总线上的所有设备。
  • 为了兼容淘汰的C-Bus协议,保留地址(’0000001X’)。I2C 设备必须忽略发往该地址的消息。
  • 地址“0000010X”旨在将 I2C 设备与在同一总线上使用不同协议的设备互连。只有能够使用这些协议运行的 I2C 设备才能回复消息。 (这个不太懂)
  • 10-bit-addressing
    • 为了防止地址冲突,由于 7 位地址的范围有限,引入了新的 10 位地址方案。此增强功能可以与 7 位寻址混合使用,并将可用地址范围增加约十倍。在开始条件之后,前导“11110”引入 10 位寻址方案。第一个字节的最后两个地址位与八位连接整个 10 位地址的第二个字节。仅使用 7 位寻址的设备只会忽略带有前导“11110”的消息。
  • auto-increment
    • eeprom什么的地址自动递增是协议的一部分
  • 速度(精度不重要)
    • 100k – 龟速(我起的名字) standard-mode
    • 400k – 快速 Fast Mode
    • 1 M – Fast Mode Plus (大部分都能在此频率工作,不过建议以各个设备datasheet为准)
    • 3.4M – high speed mode
    • 5M 超快速模式 ultra-fast-mode
      • 单向通信,从机不再发送ack
      • 可以多主机,但是没有仲裁机制
      • 推挽驱动
      • 多个设备可以通过共享 I2C UfM 总线进行寻址。112 个节点可用。
      • 超快速模式总线主要集中在 LED 设备。
高速模式
  • PC debug
  • Repeated Start Condition
    • 在发送地址字节(地址和读/写位)后,主机可以发送任意数量的字节,然后是停止条件。除了发送停止条件之外,还允许再次发送另一个开始条件,后跟地址(当然包括读/写位)和更多数据。这是递归定义的,允许发送任意数量的开始条件。这样做的目的是允许在不释放总线的情况下对一个或多个设备进行组合的写/读操作,从而保证操作不会中断。
    • 无论在一次传输期间发送了多少个开始条件,传输都必须恰好由一个停止条件结束。

IAR (RH850) (V2.21.1)破解笔记

很久没有研究逆向了,最近研究编译器的起因也是因为被瑞萨的CS+恶心坏了。然而目标设备还是RH850这种非ARM芯片(内核是瑞萨自主的G3KH),可以换用的编译器选择很少,所以研究下破解IAR。

之前在公司邮箱的时候收到过IAR的邮件,发现RH850这个系列可以使用IAR作为开发环境,但是价格么印象里ARM系列貌似5万多一个加密狗,瑞萨系列的没有询价,估计这么小众的,几乎没有竞争对手的可能还会更贵。

本来目标是找一个现成的破解版,体验一下,但是搜索发现,IAR由于更换了License算法,所以以前的License都不可用了。于是自己动手一下。

破解结果

个人破解能力比较有限,注册机之类的基本不会,所以只能爆破。

通过尝试,发现应该是只需要破解iccrh850.exe即可。其他exe在运行时候都不会检查License。

首先,使用了 http://imyoung.ys168.com/ 提供的IAR破解工具, 虽然备注了新版无法使用,但是还是用了一下,确实不能用。

但是,这个时候编译错误已经出现了变化,说生成器的版本不符。为了让他符合,爆破一下0xCB319BJGE修改为JL,然后就成功了。

上图其他几处修改是想尝试没有License的时候能否破解,但是没成功,后面突然发现了捷径 🙂

CAN232/CANUSB调查

最近研究了一下廉价CAN分析设备的原理,顺便明白了和CAN-OE设备的差距真的是天上地下.不过存在着不少优点还是可以学习的.下面是商品链接:

https://www.can232.com/

从名字上就可以看出这是一个串口到can数据包的转换工具,但是搜索很久都没找到通信协议,插在电脑上也不会主动发送消息,正在我百思不得其解的时候,发现了Linux内核里面有个叫做slcan的驱动模块,里面写着CAN232设备的网址.于是先调查下slcan是个什么东西.

slcan

全名 serial line CAN interface driver ,正好是CAN232设备的支持模块,需要配合slcand来使用(位于can=utils程序中).

初始化slcan很简单,正常初始化即可,但是如果我们把CAN232换成普通串口进行抓包,也是可以初始化的o.o

因此在打开设备时候可以捕获到下面数据(结尾均为0x0d,即’\r’):

C
S6
F
O

根据slcand的代码,可以判断相应字母(区分大小写)的意思,每个命令0x0d结尾

C -> 关闭
S6 -> 速度500k (参考: https://elinux.org/Bringing_CAN_interface_up#SLCAN_based_Interfaces)
F ->  获取状态
O -> 打开设备
// 下面这些也是代码中描述的协议
L -> 只听模式
s -> 设置波特率

//数据报文(大写代表扩展帧,小写代表数据帧)
t 数据帧
r 远程帧

//回复结尾
0x0d 正常
            异常(我忘了o.o,回头再试一下)

z 发送成功

因此,随便一个串口只要按照上面格式发送数据就可以被linux转换为CAN消息.当然这样的协议存在明显的缺点,即抗干扰能力差(不过CAN232采用了FTDI的串口芯片,一般实验室情况下稳定性足够了).

最后补上一张串口助手模拟的CAN.

slcanfd

这个串口模块非常古老,因此不支持CAN-FD的协议,但是内核驱动写的很清晰易懂,配合虚拟串口回环和cangen工具,制作了一个支持CAN-FD的驱动https://github.com/feilongfl/slcanfd.

首先模块名字改成了slcanfd,编译安装并手动加载即可使用slcand进行启动.(注意卸载slcan模块,否则会冲突)

后续可以考虑修改下slcand工具,增加下数据速度设置.在stm32G431上配合usb cdc跟fdcan做个usbcanfd(淘宝好像也有现成的,不过我想玩玩画板子,4层板都快成白菜价了)

发送接收展示:

pc发送不加速canfd
pc发送加速canfd
pc发送普通CAN(兼容原格式)
串口发送(CANFD Only)
串口发送(CANFD+CAN)

顺便wakatime也挺好玩的,能看到调查这堆东西花了多少时间:

FT4232H体验记录

体验背景

对于单片机来讲,串口通信是比较方便的接口了,打印log什么的不需要自己写上位机,网上串口助手一大把,随便找个就行,比较适合嵌入式项目的前期调试使用。

但是,随着嵌入式芯片的速度不断提高,产生的log越来越多,串口也需要比较高的速度,前段时间给mcu的串口终端通信压力测试时候,用国产山寨pl2303把速度设到921600才能给mcu一点压力。

并且新的项目需要更加快速的log速度,于是我开始寻找更加快速的串口。首先想到的是以前玩px4时候用的ft232高贵的三十多块钱的串口,速度好像3m左右来着,当年也没什么特殊需求,也没在速度这边做研究。于是打开FTDI的官网,发现最大速度12m,而且还有更加高贵的版本,70多块的ft4232,相当于4个ft232的版本。更加新一代的产品ft4233在当前(2020/5/1)还没有发售,比ft232支持了typec的pd功能(其实这个更符合我的需求,奈何淘宝没有,之后联络供应商可能会有其他回答)

体验(踩坑)历程

  1. 淘宝卖家发来的是没有焊接引脚的版本,对于我这种手残党,直接翻车。
正面
背面

清晰可见电容扭曲,因为我焊接引脚时候焊锡落在在电容上,两个电容是我后焊上去的,折腾了好久。如果不是测试用,强烈不建议购买这种,做工很差,pcb特别薄,而且明显的拼版,掰开后没打磨,在从usb拔出时候极易割伤手指(我已经被割伤了)。四周尖角,扎人。非要买的话,建议一起买个锉刀,打磨下。

2. 驱动的话,windows 10manjaro linux基本都能自己驱动,不用折腾,连接在PC上4个COM口,linux的话lsusb出现一个设备,/dev/ttyUSB出现4个,在我这边测试都是从小到大对应ABCD,没有出现反人类情况。

3. 新的FTDI芯片可以通过FT PROG对eeprom进行配置。这个软件只能windows运行。

FT Prog

这个软件在点击扫描时候有可能闪退(比如我这),通过上面命令行执行SCAN命令,可以看到错误

对这个错误进行搜索,可以查询到这是一个已知的问题,https://www.ftdicommunity.com/index.php?topic=39.0

简单来说就是和我的鼠标冲突(因为我键盘使用ps2接口,不使用USB HID协议),于是拔下鼠标,按F5然后再把鼠标插上就可以正常工作了,这里根据需要设置即可,不过感觉设置没什么作用,可能是因为我还没有测试D2XX接口。

4 通过串口助手进行发送测试,波特率直接测试12000000,通信没什么问题,回环和逻辑分析仪都没什么问题,但是循环发送速度特别慢,约几kb/s,让人难以接受,在网络上搜索没什么有效的结果,于是上逻辑分析仪进行测试。

overview
两包数据间隔时间

可以看到每次传输都是比较快的,但是在中间等待了很长时间才进行下一次传输,于是猜测串口助手影响,使用golang写(抄)了如下测试代码。

package main

import (
    "flag"
    "github.com/tarm/goserial"
    "github.com/larspensjo/config"
    "os"
    "log"
)

var (
    conFile = flag.String("configfile","/config.ini","config file")
)

var TOPIC = make(map[string]string)

func main() {
    file, _ := os.Getwd()
    cfg, err := config.ReadDefault(file + *conFile)

    id, err := cfg.String("COM","COMID")
    baud, err := cfg.Int("COM","BAUD")
    c := &serial.Config{Name: id, Baud: baud}
    s, err := serial.OpenPort(c)

    if err != nil {
        log.Fatal(err)
    }

	data := []byte("0")

	for i:=0; i< 1000000; i++ {
		_, err := s.Write(data)
		
		if err != nil {
			log.Fatal(err)
		}
	}

}
[COM]
COMID=COM17
BAUD=12000000

依旧间隔很久,肉眼可见的等待时间,怀疑驱动层或者goserial或者windows系统对串口处理有问题(linux尚未测试,c语言串口也需要测试一下),于是对每次传输字节数量进行增加,得到下面波形,可见大数据是可以高速通信的。

传输大byte
放大一点

接收的话12m的波特率没什么问题,可以正常看到数据,没发现丢包情况,不过看串口助手cpu占用率挺高,快占满一个核心了,估计自制串口通讯工具时候也需要注意这一点,把串口接收单独一个线程,防止丢数据。

启动电平时间

这方面应该跟板载电容大小有关,但是作为淘宝货,应该也是直接抄的公版电路,所以对启动电平的测量也能在一定程度上说明一定问题,对今后串口下载电路的实现可以起到一定的借鉴作用.因为这个板子没有usb的电压接口,只能采取测量ldo的3v3输出.

可以看到上电初期时序,做下载电路的话,dtr时间最长.适合作为rst使用.大约40us左右,rts可以作为类似与stm32的boot0一样的存在,用于选择引导位置.

使用vscode作为zephyr调试环境(基于stm32f746g-discovery)

前提条件west debug能正常工作,如果不能的话,请参考官方文档进行配置.

安装插件,大概下面这些.

cortex-debug,C/C++, Native Debug,arm

配置cortex-debug

settings.json里面加上下面两行

{
    "cortex-debug.armToolchainPath": "/opt/zephyr-sdk/arm-zephyr-eabi/bin/",
    "cortex-debug.armToolchainPrefix": "arm-zephyr-eabi"
}

工程配置

下面这些参考了github,各种文档汇总而成.

launch.json

{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "cortex-debug",
            "request": "launch",
            "servertype": "openocd",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceFolder}/build/zephyr/zephyr.elf",
            "name": "stm32f746g-discovery",
            "device": "STM32F746NG",
            "configFiles": [
                "${workspaceFolder}/boards/arm/stm32f746g_disco/support/openocd.cfg"
            ]
        }
    ]
}

c_cpp_properties.json

{
    "env": {
        "myZephyrSourcePath": "/home/feilong/workspace/zephyrproject/zephyr/",
        "myZephyrSDKPath": "/opt/zephyr-sdk"
    },
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${myZephyrSourcePath}/include/",
                "${myZephyrSourcePath}/lib/libc/minimal/include/",
                "${myZephyrSourcePath}/arch/arm/include/",
                "${myZephyrSourcePath}/build/zephyr/include/generated/"
            ],
            "compilerPath": "${myZephyrSDKPath}/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "gcc-x64"
        }
    ],
    "version": 4
}

west build之后f5就可以了.