Ronan Blog

罗华东的博客 | 向前每多走一步,热爱和勇气就会多一分。

手动搭建 stm32 标准外设库+Makefile工程

2024-09-03 2 min read Docs Ronan

本教程以 stm32f407 为例,其他 stm32 芯片配置方式大同小异。

1.下载官方标准外设库

进入 ST 官网的嵌入式软件板块 ,根据自身板子型号选择,有F0-F4多种型号。

之后根据提示选择下载即可,如果没有意外的话,应该会获得一个类似 en.stsw-stm32065_v1-9-0.zip(这是 F4 的标准库) 的压缩包。

2.创建工程

2.1建立工程结构

1.解压从官网下载的标准外设库 en.stsw-stm32065_v1-9-0.zip ,获得一个 STM32F4xx_DSP_StdPeriph_Lib_V1.9.0 目录

2.新建一个my_project目录,并在该目录下创建四个子目录:driverincludelibsrc

3.将标准固件库目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Libraries/CMSIS/Include文件夹,以及固件库文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Libraries/CMSIS/Device/ST/STM32F4xx/Source/Templates/TrueSTUDIO文件夹,全部拷贝到my_project/cmsis文件夹下

4.将标准固件库文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Libraries/STM32F4xx_StdPeriph_Driver文件夹下的incsrc文件夹全部拷贝移植到lib文件夹下

5.将标准固件库文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Libraries/CMSIS/Device/ST/STM32F4xx/Include文件夹目录下的stm32f4xx.hsystem_stm32f4xx.h文件拷贝到include文件夹下

6.将标准固件文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Project/STM32F4xx_StdPeriph_Templates目录下的main.cstm32f4xx_it.csystem_stm32f4xx.c拷贝移植到src文件夹下,stm32f4xx_conf.hstm32f4xx_it.h拷贝到include文件夹中,其中main.c文件是STM32工程文件的主函数程序,(main.c 也可以不拷贝,自行创建即可)

7.将标准固件库文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Project/STM32F4xx_StdPeriph_Templates/TrueSTUDIO/STM32F40_41xxx文件夹下的STM32F417IG_FLASH.ld拷贝到my_project的工程根目录下,并重命名为stm32_flash.ld

2.2编写 Makefile

到这里我们需要自行手动编写一个 Makefile…

逗你玩呢,我已经写好了,请慢用:


CROSS_COMPILE = arm-none-eabi-

# 将源文件放在这里 (*.c)
SRCDIR=./src
LIBDIR=./lib/src

SRC = ${wildcard ${LIBDIR}/*.c} \
	  ${wildcard ${SRCDIR}/*.c}

# 将以该名称生成二进制文件 (.elf, .bin, .hex)
PROJECT_NAME=out

# 编译器设置。仅编辑 CFLAGS 以包含其他头文件。
CC = $(CROSS_COMPILE)gcc
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)gcc
OBJCOPY = ${CROSS_COMPILE}objcopy

# compiler flags 编译器标志
CFLAGS = -g -O2 -Wall -Tstm32_flash.ld
CFLAGS += -DUSE_STDPERIPH_DRIVER
CFLAGS += -D STM32F40_41xxx
CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -I .
CFLAGS += -specs=nosys.specs

# 包含 STM 库中的文件
CFLAGS += -I./cmsis/Include
CFLAGS += -I./include
CFLAGS += -I./lib/inc

SRC += ./cmsis/TrueSTUDIO/startup_stm32f40_41xxx.s

# 将 SRC 中每个 .c 文件的扩展名替换为 .o,生成对应的目标文件。例如,如果 SRC 中有一个文件 main.c,它会生成 main.o,以便后续编译和链接时使用这些目标文件。
OBJS = $(SRC:.c=.o)

vpath %.c ./lib/src \
vpath %.c ./src \

.PHONY: proj

all:proj

proj:$(PROJECT_NAME).elf

$(PROJECT_NAME).elf: $(SRC)
	$(CC) $(CFLAGS) $^ -o $@
	$(OBJCOPY) -O ihex $(PROJECT_NAME).elf $(PROJECT_NAME).hex
	$(OBJCOPY) -O binary $(PROJECT_NAME).elf $(PROJECT_NAME).bin

clean:
	rm -f *.o $(PROJECT_NAME).elf $(PROJECT_NAME).hex $(PROJECT_NAME).bin

flash:proj
	st-flash write $(PROJECT_NAME).bin Ox8000000
	STM32_Programmer_CLI -c port=SWD -w $(PROJECT_NAME).hex 

将以上内容复制并保存为Makefile,将其放到my_project工程根目录下

Continue reading

GDB调试

2024-09-03 8 min read Docs Ronan

gdb 是由 GNU 软件系统社区提供的调试器,同 gcc 配套组成了一套完整的开发环境,可移植性很好,支持非常多的体系结构并被移植到各种系统中(包括各种类 Unix 系统与 Windows 系统里的 MinGW 和 Cygwin )。此外,除了 C 语言之外,gcc/gdb 还支持包括 C++、Objective-C、Ada 和 Pascal 等各种语言后端的编译和调试。 gcc/gdb 是 Linux 和许多类 Unix 系统中的标准开发环境,Linux 内核也是专门针对 gcc 进行编码的。

gdb 的吉祥物是专门捕杀 bug 的射手鱼,官方有这样一段描述:

For a fish, the archer fish is known to shoot down bugs from low hanging plants by spitting water at them.

作为一种鱼,射手鱼以喷水射下低垂的植物上的虫子而闻名。

GDB 是一套字符界面的程序集,可以使用命令 gdb 加载要调试的程序。 下面为大家介绍一些常用的GDB调试命令。

1.调试准备

项目程序如果是为了进行调试而编译时, 必须要打开调试选项(-g)。另外还有一些可选项,比如: 在尽量不影响程序行为的情况下关掉编译器的优化选项(-O0),-Wall选项打开所有 warning,也可以发现许多问题,避免一些不必要的 bug。

-g选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证gdb能找到源文件。

习惯上如果是c程序就使用gcc编译, 如果是 c++程序就使用g++编译, 编译命令中添加上边提到的参数即可。

Continue reading

「图床」上传脚本,基于 GitHub 仓库

2024-09-03 1 min read Docs Ronan

使用之前需要通过 pip install PyGithub 安装 github 库

35-37 行填入相应信息并将代码保存为 imgs.py:

  • owner:github 用户名
  • repo:仓库名(如 imgs)
  • token:github 私人访问令牌(要给予仓库读写权限)

可以通过以下方法直接运行脚本或者将脚本打包为应用程序

使用方法:usage: python imgs.py [-h] input_file [input_file ...]

from github import Github
import os
import argparse
import base64

class Imgs:
    def __init__(self, owner=None, repo=None, token=None):
        self.owner = owner
        self.repo = repo
        self.token = token

        g = Github(self.token)
        self.repo = g.get_repo(f"{self.owner}/{self.repo}")

    def create_new_file(self, img, content):
        # 第一个参数:要上传到仓库的哪个路径; 第二个参数:commit 信息; 第三个参数:上传文档正文; 第四个参数:上传的分支
        self.repo.create_file(f"blog_imgs/{img}", f"Newfiles: {img} ", content, branch="main")

    def get_img_content(self, img_path):
        with open(img_path, "rb") as image_file:
            img_content = image_file.read()

        return img_content


def main():
    parser = argparse.ArgumentParser(description="基于 echozap/imgs 的图床上传")

    # 传递的图片数量不确定
    parser.add_argument('input_file', type=str, nargs='+', help='输入图片的路径')

    args = parser.parse_args()

    img = Imgs(
        owner = "",
        repo = "",
        token = ""
    )

    for img_path in args.input_file:
        try:
            img_content = img.get_img_content(img_path)
            img_name = os.path.basename(img_path) # 获取带扩展名的文件名

            img.create_new_file(img_name, img_content)

            print(f"{img_name}上传成功")
            print(f"https://img.ronan.us.kg/blog_imgs/{img_name}")
        except Exception as e:
            if '"status": "422"' in str(e):
                print(f"上传 {img_path} 时发生错误: {e}")
                print("图片已存在")
                print(f"https://img.ronan.us.kg/blog_imgs/{img_name}")

if __name__ == "__main__":
    main()
Older posts Newer posts