有兄弟使用emacs写一些单片机代码工程文件吗?例如STM32

不懂呀,命令是啥,bear – ,我看里面介绍说的命令是这个,然后your-build-command是啥东西呀,纯小白一个。

这个玩意是不是要一个makefile,然后bear make这样的 :sweat:

你和我当初的情况一样,这确实是要makefile。

makefile是一个工程管理工具,指导程序应该如何一步一步被编译出来,而补全就是根据每一步的编译指令“gcc ……” 来推断每个文件里面可以看到哪些库(注意“能看到”与“实际include”的区别),因此便能够据此提供补全功能。

你如果和我当初一样是刚刚进入Linux世界的话,那我猜测你之前是用keil做单片机开发的。那么你就需要去网上找找你所开发的单片机的对应makefile工程,我记得GitHub上面就有,然后尝试着编写一个gpio的点灯程序试试能否编译烧录。

编译器应该是交叉编译器“gcc-none-arm-eabi”,好像是这个名,太久没有用记不清了,可能顺序不一样,但是none和arm是要有的,none指裸板(即无操作系统的平台),arm指芯片架构。

烧录我不太记得清了,记得是下载一个驱动(pacman和apt仓库好像都有,名字忘记了)然后再下载一个串口通信的软件,就能够烧录了。

最后我觉得Linux做单片机开发对于初学者来说,心智负担实在是太大了,不如还是先使用keil,stm32cubeide(这个好像有Linux版,但是当时折腾了好久也没有烧录上)来做开发比较好。

还有就是,我只懂这么多了,因为我已经放弃在Linux上面折腾单片机开发了。上一次折腾这个已经是快两年,近两年半前了,现在的Linux下面开发工具的最新状况我也不是太了解

卧槽猜对了,兄弟过来人。我大学的时候折腾过几个月linux就跑路回windows。\n 最近想换掉vscode上车emacs。(我实际的需求就是一个编辑器,vscode有时候太蠢,有点制约我看代码) 因为太多什么QT,C#,Can协议,485协议,各种软件要搞,未来还有一个canopen之类的要处理,linux基本不可能,所以大概率我会在windows下面操作emacs,lsp-mode,当成一个编辑器就够。 兄弟为啥放弃在linux折腾。

因为自己当时太傲慢,太蠢了,遇到问题如果谷歌不到就放弃了,完全不懂得上咱们论坛问问,或者去stackoverflow之类的地方用英文问别人应该怎么做,什么都自己摸索。。。然后自己摸索了许久,然后发现总是有问题,就放弃了。

仔细回忆一下,大概是一开始是根本不知道怎么搞,后面通过折腾知道了需要自己弄项目管理的工具还有交叉编译器,Keil 是用 .uproj(大概这名)文件来管理项目的,而Linux上面只能用makefile或者CMake,于是就自己学Makefile和CMake。。。后面发现需要调试串口的时候又卡住了,Linux下面的串口调试怎么也接不到STM32发出来的串口信息(当时我只用Linux),一直卡着调不出来,又不想重装回Windows,负反馈太大,就失去兴趣了。

后面上了大三了专业课压力又大,又要准备考研了,于是就没有整过它了。现在考完研(没考上😢),也快毕业了,搞毕业论文,又要二战,更加没时间弄这个了。

不过当时学的时候还是蛮快乐的,因为当时我是用『寄存器』的视角去学习的(好像当时STM32学习的三层视角:寄存器、官方库函数、HAL库函数),虽然痛苦而且没有什么项目成果,但是却让我理解起计算机底层更加深入了233

人要克已才能成已,古人诚不欺我。

如果你需要经常处理各种奇奇怪怪的格式的文件,那么linux生态比windows强太多了,当然,你需要慢慢摸索你自己的各种工具。

一旦达到一个程度,就再也不想回到windows下了。

给个建议,分析用工具生成的 compile_command.json 文件格式,印象中就是每个源文件路径和对应的编译命令这些,然后自己来生成这个compole_command.json,源文件用文本编辑器打开keil的工程文件可以通过正则表达式去过滤得到,编译命令貌似在keil里就看得到

1 个赞

兄弟,c语言怎么语法高亮呀,现在makefile和.json的问题我解决了。但是嘛,现在还剩两个问题。

1,lsp-mode打开源文件白茫茫的一片,尝试ccls,也有.json,搞了一天终于装好了,但是没效果。

2,打开.c文件,tab默然8个字符,搞得排版全乱。这个怎么处理。

screenshot_1

兄弟,最近遇到一个问题,就是我看别人的工程文件的时候(编译数据库肯定是没,makefile也没),.ccls怎么写才行。

我模仿你的写法,-Ixxxx这个能把需要的头文件都包含进去,比较容易跳。源文件.c怎么包含进去呢。

我在弄的时候,发现每打开一个.c文件,.ccls-cache里面会生成相关.c文件的缓存,所以能跳过去。 所以我在猜测肯定有相关刚开始就能包含所有源文件的写法,但是找不到相关描述

(Project Setup · MaskRay/ccls Wiki · GitHub)

对于没有生成 compile_commands.json 的项目, 可以自己建一个 compile_commands.json 文件
已知可以在 clangd 使用
编写非常简单, 只需编写一个源码文件记录就可以了, 比如 main.c 文件就够了, 不需要全部文件添加
directory: 项目根目录
file: 随便一个根目录文件
command: 编译命令
command解释: -I 表示添加一个头文件目录 -D表示添加一个宏定义
一个简单但全部功能完整的实例:

[
    {
        "directory": "C:\\Users\\Jack\\Desktop\\Modbus-TCP",
        "file": "C:\\Users\\Jack\\Desktop\\Modbus-TCP\\USER\\main.C",
        "command": "\"F:\\App\\Scoop\\apps\\mingw-winlibs-llvm-ucrt\\current\\bin\\gcc.exe\" -D STM32F10X_CL -I./USER  -I./Libraries/CMSIS/CM3/CoreSupport -I./Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x -I./Libraries/STM32F10x_StdPeriph_Driver/inc -o ./USER/main.o -MMD ./USER/main.C"
    }
]

已知问题: 使用函数跳转时如果没有打开源码文件, 默认跳转到头文件定义的, 比如:
*.h 文件定义: extern void RTC_Time(void);
*.c 文件函数源码: void RTC_Time(void){you code}

举个wch ch582m 单片机的例子:

首先wch官方提供的sdk是面向eclipse的,所以需要自己写个makefile来编译项目:

CH58X_SDK ?= ./EVT/EXAM/SRC

SRCS += $(CH58X_SDK)/Startup/startup_CH583.S
#SRCS += $(CH58X_SDK)/../FreeRTOS/Startup/startup_CH583.S

SRCS += $(CH58X_SDK)/RVMSIS/core_riscv.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_adc.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_clk.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_flash.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_gpio.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_i2c.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_spi0.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_spi1.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_sys.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_timer0.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_timer1.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_timer2.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_timer3.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_uart0.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_uart1.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_uart2.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_uart3.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_usbdev.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_pwm.c \
	$(CH58X_SDK)/StdPeriphDriver/CH58x_pwr.c

INCS += -I $(CH58X_SDK)/RVMSIS \
	-I $(CH58X_SDK)/StdPeriphDriver/inc

LIBS += -L $(CH58X_SDK)/StdPeriphDriver \
	-lISP583

#SRCS += $(CH58X_SDK)/StdPeriphDriver/CH58x_usbhostBase.c
#SRCS += $(CH58X_SDK)/StdPeriphDriver/CH58x_usb2hostBase.c
#SRCS += $(CH58X_SDK)/StdPeriphDriver/CH58x_usbhostClass.c
#SRCS += $(CH58X_SDK)/StdPeriphDriver/CH58x_usb2hostClass.c
#SRCS += $(CH58X_SDK)/StdPeriphDriver/CH58x_usb2dev.c

#SRCS += $(CH58X_SDK)/../FreeRTOS/FreeRTOS/croutine.c \
#	$(CH58X_SDK)/../FreeRTOS/FreeRTOS/event_groups.c \
#	$(CH58X_SDK)/../FreeRTOS/FreeRTOS/list.c \
#	$(CH58X_SDK)/../FreeRTOS/FreeRTOS/queue.c \
#	$(CH58X_SDK)/../FreeRTOS/FreeRTOS/stream_buffer.c \
#	$(CH58X_SDK)/../FreeRTOS/FreeRTOS/tasks.c \
#	$(CH58X_SDK)/../FreeRTOS/FreeRTOS/timers.c \
#	$(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/GCC/RISC-V/portASM.S \
#	$(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/GCC/RISC-V/port.c

#SRCS += $(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/MemMang/heap_1.c
#SRCS += $(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/MemMang/heap_2.c
#SRCS += $(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/MemMang/heap_3.c
#SRCS += $(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/MemMang/heap_4.c
#SRCS += $(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/MemMang/heap_5.c

#INCS += -I $(CH58X_SDK)/../FreeRTOS/FreeRTOS \
#	-I $(CH58X_SDK)/../FreeRTOS/FreeRTOS/include \
#	-I $(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/ \
#	-I $(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/GCC/RISC-V/

SRCS += main.c usbdev.c 

FW_NAME ?= ch58x-ec
CROSS_COMPILE ?= riscv32-imac-elf-
CC = $(CROSS_COMPILE)gcc
OD = $(CROSS_COMPILE)objdump
OC = $(CROSS_COMPILE)objcopy
SZ = $(CROSS_COMPILE)size

LINK_SCRIPT ?= $(CH58X_SDK)/Ld/Link.ld
#LINK_SCRIPT ?= $(CH58X_SDK)/../FreeRTOS/Ld/Link.ld

CFLAGS += -Wall -Wextra \
	-flto -fanalyzer \
	-Wno-unused-parameter \
	-march=rv32imac_zicsr -mabi=ilp32 \
	-Os -g3 \
	-fdata-sections -ffunction-sections -Wl,--gc-sections \
	-nostartfiles \
	-T $(LINK_SCRIPT) \
	-DINT_SOFT

all: patch $(FW_NAME).elf $(FW_NAME).bin $(FW_NAME).asm
	$(SZ) $(FW_NAME).elf

flash: all
	wchisp flash $(FW_NAME).bin

clean:
	rm -f *.out $(FW_NAME).asm $(FW_NAME).elf $(FW_NAME).bin

patch:
	sed -i -e 's/__attribute__((interrupt("WCH-Interrupt-fast")))/__INTERRUPT/g' \
		$(CH58X_SDK)/../FreeRTOS/FreeRTOS/portable/GCC/RISC-V/port.c
	sed -i -e 's/void FLASH_ROM_READ(UINT32 StartAddr, PVOID Buffer, UINT32 len);//g' \
		$(CH58X_SDK)/StdPeriphDriver/inc/CH58x_flash.h

format:
	clang-format -i *.c *.h

$(FW_NAME).elf:
	$(CC) $(CFLAGS) $(INCS) $(SRCS) $(LIBS) -o $(FW_NAME).elf

$(FW_NAME).asm: $(FW_NAME).elf
	$(OD) -S -D $(FW_NAME).elf > $(FW_NAME).asm

$(FW_NAME).bin: $(FW_NAME).elf
	$(OC) -O binary $(FW_NAME).elf $(FW_NAME).bin

然后

  1. 下载官方的SDK解压到EVT文件夹中
  2. 下载编译用的工具链,并添加到PATH

然后是emacs的配置:

(add-hook 'c-mode-hook 'eglot-ensure)
(add-hook 'c-mode-hook 'company-mode)

因为clangd对交叉编译的处理有问题,所以在此之前需要在项目目录下添加.clangd:

CompileFlags:
  Add: -Wno-unknown-warning-option
  Remove: [-m*, -f*]

接下来需要使用 bear 或 compiledb 来创建 compile_command.json

bear -- make all

或者

compiledb -- make all

然后使用emacs打开项目里面的文件,输入函数前3个字符就可以得到自动补全了:

至于使用emacs调试,我有点懒,点亮led或串口打日志比这个方便多了

openocd也就bringup阶段用处大一点

3 个赞