当前位置: 首页 > news >正文

移植 Zephyr 到 Art-Pi

背景

​ 最近工作中接触到了 Zephyr,不由觉得 Zephyr 是个很强大、全面、优秀的实时操作系统,但同时是有一定的上手难度的,其复杂的构建系统让小编倒吸一口凉气。为了深入研究并完全掌控 Zephyr,小编决定把它移植到手头的开发板上,为后续探究 Zephyr 源码仓库的构建原理、系统启动原理、多种调度机制 打下基础。

基础知识

  • 搭建 Zephyr 环境
  • 能够使用基本的 west 命令编译、烧录、调试 bsp

可通过 Zephyr 源码调试 章节验证基础知识的掌握程度。具体 west 工具的细节以及整个 elf 文件链接过程暂时不要深究,这是个很深的坑。

移植

​ Zephyr 仓库下面有很多开发板的基础 bsp,我们可以寻找一个与我们开发板上芯片型号类似的 bsp 作为参考。Art-Pi 主控是 Stm32H750XBH6,主频高达 480Mhz,片上资源非常丰富。

我们可以参考 zephyr/boards/st 目录下的 stm32h750b_dk ,该目录树结构如下:

stm32h750b_dk
├── Kconfig.stm32h750b_dk
├── arduino_r3_connector.dtsi
├── board.cmake
├── board.yml
├── doc
│   ├── img
│   │   └── stm32h750b_dk.png
│   └── index.rst
├── stm32h750b_dk.dts
├── stm32h750b_dk.yaml
├── stm32h750b_dk_defconfig
└── support└── openocd.cfg

各文件描述如下:

  • Kconfig.stm32h750b_dk : 板级 Kconfig 定义,用于控制板级资源,如使能 HAL 层的各个驱动
  • arduino_r3_connector.dtsi : arduino 扩展引脚设备树定义,我们的板子不需要该文件,直接删除
  • board.cmake :板级 CMake 配置,一般用于配置板子所使用的调试器参数,比如使用 openocd 作为调试器 Server
  • board.yml : 板级描述文件,暂时不清楚有什么用,但没有这个文件,构建系统会报错
  • doc:板子介绍
  • stm32h750b_dk.dts : 板子设备树定义
  • stm32h750b_dk.yaml : 板级测试相关(Zephyr 自动化单元测试系统使用)
  • stm32h750b_dk_defconfig : 板级 Kconfig 默认值,如果在此处配置了默认值,就无法通过 menuconfig 或者 App 目录下的 pri.conf 文件中进行修改
  • openocd.cfg : openocd 配置文件,如果 board.cmake 中使用 openocd 作为调试器的 server,该文件将会被传递给 openocd 使用

主要是板子设备树文件比较重要,该文件涉及芯片时钟、外设、内存、flash 配置。其余文件中的内容全部改为自己的开发板,Art-Pi 设备树文件最终配置如下:

/** Copyright (c) 2023 STMicroelectronics** SPDX-License-Identifier: Apache-2.0*//dts-v1/;
#include <st/h7/stm32h750Xb.dtsi>
#include <st/h7/stm32h750xbhx-pinctrl.dtsi>
#include <zephyr/dt-bindings/input/input-event-codes.h>/ {model = "STM32H750XBH6 ART PI";compatible = "st,stm32h750xb-art-pi";chosen {zephyr,console = &uart4;  // uart4 作为控制台输出zephyr,shell-uart = &uart4; // uart4 作为控制台输入zephyr,sram = &sram0; // 使用 sram0 作为片内 sram,sram0 的定义在 dtsi 文件中zephyr,flash = &flash0; // 使用 flash0 作为片内 flash,这个并不决定链接时各符号的加载地址,而是由 CONFIG_FLASH_BASE_ADDRESS 这个宏决定,zephyr 构建系统的链接脚本有点复杂,暂时不深究};leds {compatible = "gpio-leds";blue_led: led_1 {gpios = <&gpioi 8 GPIO_ACTIVE_LOW>; // 定义板子蓝灯引脚label = "USER2 LD7";};red_led: led_2 {gpios = <&gpioc 15 GPIO_ACTIVE_LOW>; // 定义板子红灯引脚label = "USER2 LD7";};};gpio_keys {compatible = "gpio-keys"; // 定义板子按键引脚user_button: button {label = "User";gpios = <&gpioh 4 GPIO_ACTIVE_LOW>;zephyr,code = <INPUT_KEY_0>;};};sdram1: sdram@c0000000 { // 定义板子片外 sdram 地址,使用 fmc 驱动compatible = "zephyr,memory-region", "mmio-sram";device_type = "memory";reg = <0xc0000000 DT_SIZE_M(32)>; // 定义 sdram 起始地址、尺寸zephyr,memory-region = "SDRAM1";zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_RAM) )>;};aliases {led0 = &blue_led;led1 = &red_led;sw0 = &user_button;};
};&clk_hse { // 外部 hse 定义clock-frequency = <DT_FREQ_M(25)>;status = "okay";
};&pll { // pll 配置,h750 系列的时钟树比较复杂,可通过 cubemx 简化配置div-m = <5>;mul-n = <192>;div-p = <2>;div-q = <4>;div-r = <4>;clocks = <&clk_hse>;status = "okay";
};&rcc { // 系统各时钟域配置clocks = <&pll>;clock-frequency = <DT_FREQ_M(480)>;d1cpre = <1>;hpre = <2>;d1ppre = <2>;d2ppre1 = <2>;d2ppre2 = <2>;d3ppre = <2>;
};&uart4 { // uart4 作为 zephyr 控制台pinctrl-0 = <&uart4_tx_pa0 &uart4_rx_pi9>;pinctrl-names = "default";current-speed = <115200>;status = "okay";
};&fmc { // fmc 配置,用于驱动 sdram 芯片status = "okay";pinctrl-0 = <&fmc_nbl0_pe0 &fmc_nbl1_pe1&fmc_sdclk_pg8 &fmc_sdnwe_ph5 &fmc_sdcke0_pc3&fmc_sdne0_pc2 &fmc_sdnras_pf11 &fmc_sdncas_pg15&fmc_a0_pf0 &fmc_a1_pf1 &fmc_a2_pf2 &fmc_a3_pf3 &fmc_a4_pf4&fmc_a5_pf5 &fmc_a6_pf12 &fmc_a7_pf13 &fmc_a8_pf14&fmc_a9_pf15 &fmc_a10_pg0 &fmc_a11_pg1 &fmc_a12_pg2&fmc_a14_pg4 &fmc_a15_pg5 &fmc_d0_pd14 &fmc_d1_pd15&fmc_d2_pd0 &fmc_d3_pd1 &fmc_d4_pe7 &fmc_d5_pe8 &fmc_d6_pe9&fmc_d7_pe10 &fmc_d8_pe11 &fmc_d9_pe12 &fmc_d10_pe13&fmc_d11_pe14 &fmc_d12_pe15 &fmc_d13_pd8 &fmc_d14_pd9&fmc_d15_pd10>;pinctrl-names = "default";sdram {status = "okay";power-up-delay = <100>;num-auto-refresh = <8>;mode-register = <0x221>;refresh-rate = <0x02A5>;bank@0 {reg = <0>;st,sdram-control = <STM32_FMC_SDRAM_NC_9STM32_FMC_SDRAM_NR_13STM32_FMC_SDRAM_MWID_16STM32_FMC_SDRAM_NB_4STM32_FMC_SDRAM_CAS_2STM32_FMC_SDRAM_SDCLK_PERIOD_2STM32_FMC_SDRAM_RBURST_ENABLESTM32_FMC_SDRAM_RPIPE_0>;st,sdram-timing = <2 8 6 6 2 2 2>;};};
};

仅通过如上配置就能够编译并链接通过,其背后是 Zephyr 对 stm32 系列芯片 hal 库以及外设驱动的完美适配,以至于用户仅通过简单的设备树描述就能让外部设备正常工作起来。最终 Art-Pi 目录结构如下:

stm32h750_art_pi
├── Kconfig.defconfig
├── board.cmake
├── board.yml
├── stm32h750_art_pi.dts
├── stm32h750_art_pi_defconfig
└── support└── openocd.cfg // openocd 默认会掉用 openocd.cfg 文件,该文件用于描述 openocd 控制的硬件调试器,如 stlink,jlink 

板级调试

​ 以上步骤全部通过后会面临一个问题:如何将固件搞到芯片里面?由于我是使用的 stm32 系列,同时拥有 stlink 调试器,可通过 STM32CubeProgrammer 工具擦除并将固件下载到 flash。但会遇到一个问题,固件并没有按照预期执行,甚至没跑到 main 线程,此时连串口都不能打印日志,此时如果拥有调试器,那便是如虎添翼。

​ Zephyr 的构建系统是支持下载以及调试能力的,这时候也不需要使用 STM32CubeProgrammer 了。一般调试嵌入式设备需要硬件调试器(板子不足以运行 gdb server,但 Zephyr 貌似支持 gdb stub,这样的话可以不需要硬件调试器)、硬件调试器配套的 gdb server(stlink server、jlink server、openocd)、gdb。我们选择使用 openocd 作为 gdb server。可通过使用 west flash 命令验证能否下载固件,如图:请添加图片描述
​ 这一命令的背后到底发生了什么?其本质是调用 openocd 来将固件下入芯片内,但这背后是如何调用起 openocd,暂时不深追,这个坑很深,也正是 Zephyr 构建系统的复杂且神秘之处,给人一种知其然不知其所以然的感觉,小编决定后续几章来深究 Zephyr 构建系统原理。

​ 此时一般会发现固件跑不起来,这时候,可以使用 west debugserver 命令来起一个 gdb server,这个 gdb server 就是 openocd,它在某个端口上起一个 tcp 服务,通过 tcp 与 gdb client 交互,双方通信是明文传输,如图:请添加图片描述
​ Openocd 说它监听 3333 端口,此时我们便可以通过 gdb 来连接 3333 端口进行调试了,如图:

请添加图片描述

​ 使用命令行的方式虽然能进行调试,但是很麻烦,效率不高,这时候可参考 Zephyr 源码调试章节 进行图形化界面调试,这样子能大大提高解决问题的效率,终极调试界面如图:

请添加图片描述
我的开发环境是 wsl ubuntu,使用 wsl 连接 usb 设备需要简单的配置一下,可网上搜索 wsl usb 关键字查看相关教程。

应用配置

​ 移植完毕后,可通过 Zephyr 仓库 zephyr/samples/basic/blinky 下的应用来验证,比如可在 main 函数中翻转 led 灯来验证。但此时,Zephyr 的 shell 还不能使用,因为 shell 作为 Zephyr 的一个子系统此时还未使能,这也是 Zephyr 构建系统的一大优势,以搭积木的形式构建系统,缺少某一模块,仍然能够编译链接通过,仅仅是该能力缺失而已。可在 zephyr/samples/basic/blinky 下的 prj.conf 文件中添加如下配置:

CONFIG_GPIO=y
CONFIG_UART_CONSOLE=y //使能 uart console
CONFIG_SHELL=y //使能 shell 模块
CONFIG_MEMC=y
CONFIG_MEMC_STM32=y
CONFIG_MEMC_STM32_SDRAM=y

​ 最终就能丝滑的感受 Zephyr 了,这才有点操作系统的味道。
请添加图片描述
最终附上 项目地址 ,欢迎 Star~~~

相关文章:

  • C语言 数组指针 指针数组
  • Python 全栈系列236 rabbit_agent搭建
  • 微服务(基础篇-003-Nacos集群搭建)
  • 黑帽子学Python
  • GDC期间LayaAir启动全球化战略
  • Flink中流式的各种聚合
  • Http 超文本传输协议基本概念学习摘录
  • Spark spark-submit 提交应用程序
  • 信号处理--使用EEGNet进行BCI脑电信号的分类
  • Apache HTTP服务器(Linux离线编译安装)
  • 6.3 BP神经网络
  • STM32微控制器的中断优先级设置中,抢占优先级和子优先级如何影响中断响应?
  • 基于SpringBoot的会员制医疗预约服务管理信息系统
  • OSPF邻居震荡抑制
  • [iOS]GCD(一)
  • 「译」Node.js Streams 基础
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • CSS魔法堂:Absolute Positioning就这个样
  • C语言笔记(第一章:C语言编程)
  • docker-consul
  • ES6系列(二)变量的解构赋值
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • learning koa2.x
  • miaov-React 最佳入门
  • Object.assign方法不能实现深复制
  • PAT A1017 优先队列
  • Python - 闭包Closure
  • socket.io+express实现聊天室的思考(三)
  • vue.js框架原理浅析
  • 漂亮刷新控件-iOS
  • 设计模式 开闭原则
  • 深度学习中的信息论知识详解
  • 数组大概知多少
  • 双管齐下,VMware的容器新战略
  • 主流的CSS水平和垂直居中技术大全
  • 树莓派用上kodexplorer也能玩成私有网盘
  • ​力扣解法汇总946-验证栈序列
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (算法)N皇后问题
  • (一)认识微服务
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .NET 8.0 中有哪些新的变化?
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .Net MVC + EF搭建学生管理系统
  • .NET 中 GetProcess 相关方法的性能
  • @ConditionalOnProperty注解使用说明