0%

TL;DR

支持STM32H7芯片的st-link已经修改好,放到我的github 上了(st-link已经禁止外人提交PR了)。这个是在1.6.1的版本基础是进行的修改,供大家参考,欢迎拍砖批评。当然代码中还有些问题,比如只使用了一个Bank,超过1M的bin文件会出问题,但目前至少能使用,其他后续再改。

背景

因为看上STM32H7的高性价比,所以买来一块正点原子的核心板学习一下。ST官方文档中宣称STM32H743的JPEG(640*480)压缩能力能达到16fps,在考虑用H743准备做一个低成本的Camera。

然而调试却不是那么顺利。我平时烧录和调试都用的是st-link,但是st-link不支持STM32H7系列的芯片!

搜到st-link的相关issue:STM32H7xx support,发现这个问题从2018年2月就有人提,到现在2年多还没解决。st-link官方推荐去使用openocd去烧录h7系列??给竞对送用户,我看他们是不想干了吧。

无奈,只好下载openocd去烧录,我对openocd不熟,而且他也很复杂,烧录的程序貌似有问题,而且又烧录几次后,板子不亮了,没有反应了,提示openocd halt Handler HardFault。我以为板子被烧坏了。

Read more »

STM32中有10几个定时器,工作原理很好理解。就是Enable某个定时器,然后对他ARR(auto-reload register)赋值,定时就会根据CLK信号,对计数寄存器做加法,如果加到了ARR中指定的值,则发出中断,CPU就是中断响应处理。

这个定个闹钟,早上闹钟响了,我就开始起床刷牙洗脸,原理上差不多。

但是STM32的timer的计时不是人类的时间,而是对应的CLK时钟信号。大多数是对应APB1的clock时钟,具体需要到数据手册中查看。

如果单单是定时,其实就没必要设置这么多个Timer了。STM32利用GPIO端口结合定时器,可以实现输入捕获,和输出比较。输出比较就是产生PWM脉冲。

PWM可以实现DAC转换,可以驱动电机。电动机可以利用PWM来控制转速。

舵机会指定一个高电平的时间范围,对应着舵机的转动角度,这里通过简单计算就可以把角度和Timer的CCR寄存器的值对应起来。这样改变CCR,就能改变舵机角度。

这里需要制定Timer的channel,就是占用哪一个GPIO口,把这个口接出来就变成了舵机的信号线。

STM32还有很多定时器模式,参数可选,在CubeMX中配置一下即可,使用还是很方便的。

NVIC

中断就是一种监听方式,响应外部事件

10个内核中断,剩下的是外部中断

抢占优先级,相应优先级

HAL_NVIC_SetPriorityGrouping

IP

IO外部中断

16根外部中断线

EXTI寄存器配置IO口,最多还是16个外部中断,

7个中断服务函数,有公用的

好弱的中断回调处理方式

通过设置触发方式,来决定是普通IO,还是中断响应口

为什么有时钟?

时钟发出时序信号,数据和指令才能才CPU内部流动执行。

任何复杂的东西都是由简单的东西组成的,所以不必害怕,逐一拆解

时钟树

STM32五大时钟源:HSI,HSE,LSI,LSE,PLL

任何一种外设使用前,必先使能其时钟。每种外设在数据通信时都需要时钟,这些时钟都是通过5个主要的源,通过各种分频,倍频,最后得到一个能用的时钟信号。

Systick

内部精准永不停歇的计时器,用于OS。

Systick = 25MHz * N/ (M*P)=25MHz * 432 / ( 25 * 2 ) = 216MHz

频率越高,耗电越高,所以尽可能的降低频率。

外设时钟

外设时钟从Sysclock分频得来,54M,或者108M,然后再根据计数值,产生各种中断。

两种总线

AHB=Advanced High Performance Bus,译作高级高性能总线。如同USB(Universal Serial Bus)一样,也是一种总线接口。AHB主要用于高性能模块(如CPU、DMA和DSP等)之间的连接

APB(Advanced Peripheral Bus)外围总线。APB主要用于低带宽的周边外设之间的连接,例如UART、1284等,它的总线架构不像AHB支持多个主模块,在APB里面唯一的主模块就是APB 桥

遗漏问题

分频是什么?

PLL是什么?锁相环怎么倍频的?

晶振振荡电路原理?

调压系数

Over-drive

基本原理

GPIOF->BSRR = 0x00000001;

通用输入输出端口,就相当于一大堆导线,只不过可以通过代码对这些导线做到开关控制。每根导线相当于一条导线。对于输入也是看每条导线上的电压,电压高就是1,电压低就是0,这样就可以表示任何可以数字化的信息。

Read more »

STM32芯片介绍

STM32是ST公司以ARM cortex-M为内核的单片机,有很多外设(peripheral)。这里的外设就是定时器,串口,GPIO,SPI等等。ST公司是意大利和法国的两个公司合并而来的。

STM32是个一些列MCU的总称,有多种选项可选,根据命名规则标识芯片规格。

相关学习资料的主要来源包括:ARM公司,ST公司,开发板公司

MCU工作原理

从图中我们可以看出一个MCU一般由CPU(中央处理单元)、地址空间、片上外设三个部分组成。CPU是处理器的核心,它控制着整个系统的运作。片上外设以及芯片内部的各种存储设备都会被映射到地址空间中,使得我们在编写程序的时候只需要对地址空间中的某一段进行读写就可以操控外设了。 片上外设实际上是一个集合,指的是MCU芯片内部为实现某一特殊功能而专门设计的模块,比如说串口、GPIO等等,它们都会映射到地址空间中。

一个芯片上有很多引脚,它们有片上外设控制,是连接芯片内部与用户设备之间的桥梁。 每个引脚的功能基本都是确定的,不过为了节省资源同一个引脚往往对应几个不同的功能。所以具体一个引脚是用作串口还是输出PWM信号,除了由其物理属性决定之外, 还需要在程序中通过软件进行配置。通过正确的连接和配置引脚的功能,合理的操控片上外设,我们就可以控制LED,驱动SD卡,进而实现期望的功能。

MCU工作原理

注:以上部分转自网上一篇文章STM32的启动过程

最小系统

用STM32组成的最小系统需要:供电、复位、时钟、Boot模式、下载、后备电池几个部分。

如果自行设计一个PCB,至少需要考虑这几个部分,然后才是其他功能。

  • 供电
    • 要考虑模拟电源、数字电源分开。AMS1117是种输出电压为3.3V的正向低压降稳压器
  • 复位:就是一个按钮开关,接一下地就ok了
  • 时钟:高速,低速精准
  • Boot:两个引脚决定启动模式。就是从SRAM启动,还是从Flash启动
  • 下载:SWD、JTAG、串口
  • 后备电池:1n4148是小型的高速开关二极管

MAC上STM32开发环境搭建

  1. gcc-arm-embedded:arm cortex平台的编译器,开源,免费

brew cask install gcc-arm-embedded

  1. st-link : st公司开发的upload工具

brew install stlink

  1. PlatformIO IDE(VSCode 插件)

在vscode中安装PlatformIO,这个会自动下载MCU所需要的HAL库,能够自动编译,上传,还是比较方便。

这样就有IDE + Toolchain + Loader,而且都是开源免费的

对嵌入式开发的肤浅理解

MCU内核就是一个CPU,现在普遍用ARM,我想主要是因为生态好。STM32能够广泛被应用,也是因为生态好。其实如果认真看看MIT6.004,都能设计一个CPU出来,但是非主流的指令集没人用。而且小众CPU的成本肯定高。

MCU的外设就是一个电路,他通过寄存器的电压信号,再加上各种电路,比如晶体管,电阻,或者与非门,选择器,移位器等等,来实现数据传递。如果传数据,那就是按照时序,一位位的把信息传递出去。每一位在物理层面传递的只是高电压和低电压,通过人为约定变成0或1。如果传控制信号,可能就是用PWM信号传递。

我原来理解的外设就是鼠标,键盘,显示器这些叫外设。但是在MCU的世界里面,一小块接口电路就叫外设,其实他就是一个外设接口,仅仅是信息通路。

学习MCU就是学习他的各种机制,或者简单说就是规则。然后从外部接数据,和给外部发数据,来实现某种应用。这些应用更多的就是为了让人们省时省力省钱。MCU的软件开发其实就是调用厂家提供的API实现功能,难度和JS开发差不多,不会有什么核心技术。当然一整套完整的嵌入式开发还是牵扯的面比较广,器件选型,接口电路设计,PCB制版,焊装,代码开发,调试运行。内容也不少。但这些毕竟是应用技术,就当是开开眼界吧。

很想做一个全栈开发,但是不太可能。这些知识太多,而且如果只是流于表面,那也只是停留在科普水平。完全深入不太可能,记住所有细节也不可能,了解所有细节也不可能。

只能靠随机碰运气,似乎也是很郁闷。

那该怎么办?

随缘。如果自己感兴趣,就去记录,何必在意结果呢。我想用比较简单的文字,记录在当下这一刻,我所知道和理解的一点点知识。也许写的是错的,也许很多遗漏,而且肯定过两天就忘了。但是那又能怎样呢?无所谓!心态!

软件

  • Program Language
  • Network
  • DB
  • DS / Algorithm
  • Compiler
  • OS
    • 驱动开发

电子

  • 单片机
    • STM32
    • STM32开发准备
  • 数电
  • 模电
  • 电路

机械

基础

  • 物理
    • 理论力学
  • 数学
    • 线性代数
    • 微积分

机器人

  • 入门
    • 斯坦福大学公开课——机器人学,Oussama Khatib
    • 教程:John Craig,Introduction to Robotics: Mechanics and Control
  • 实践
    • 比赛:RoboMaster、飞思卡尔智能车大赛、电子设计大赛
    • Penn’s Robotics Specialization(Coursera)
      • Aerial Robotics
      • Computational Motion Planning
      • Mobility
      • Perception
      • Estimation and Learning
    • ROS
      • 官网的教程ROS Tutorials(Robot Operating System, ROS)
      • 硬件平台:TurtleBot、Baxter、Universal Robot
  • 进阶
    • 补数学
      • 数值计算方法:《Numerical Methods for Engineers》
      • 凸优化:Stanford 的公开课《Convex Optimization》
    • Modern Robotics (Coursera)
    • 李群李代数《Notes on Differential Geometry and Lie Groups, I & II》
    • 控制:
      • youtube Brian Douglas
      • Khalil 的教材《Modeling, identification and control of robots》
    • 运动规划
      • 教材《Planning Algorithms》
      • 教材《Principles of Robot Motion Theory, Algorithms, and Implementations》
    • 机器学习
    • 强化学习
      • Sutton 的《Introduction to reinforcement learning》
  • 论文:RSS、ICRA、IROS 等相关会议,了解机器人领域的最新进展

虚拟这两个字太讨厌了。虚拟文件系统一点都不虚拟,做了很多实在的工作。

文件系统
  • 对于要长期保存的数据,独立保存起来,这块数据叫做一个文件。文件和目录形成文件系统。

  • 裸机情况下,硬盘只能根据地址定位一段数据的读写,没法用。需要os进行统一分配和管理

  • 文件描述符,“描述符”,类似还有全局描述符,页目录描述符,其实就是一个struct,里面有各种属性信息。文件被打开后,os会在内核记录被打开文件的属性,还包括状态和信息

    • 文件指针,最近一次读写位置,每个进程维护自己
    • 打开次数
    • 文件磁盘位置,缓存
  • os:文件就是数据块;用户:文件是数据结构

  • 文件读写必须以块为单位,内存是以字节为单位:

    • 读:读出一块,返回对应部分;写:读出一块,修改,写回一块
  • 目录实现

    • 列表、hash表
    • 根目录在磁盘中位置固定
    • PWD,当前工作路径
文件系统分层

类unix文件系统,把标准输入,输出,磁盘文件,网络socket都看成文件。这样就会给程序员,有统一的读写接口。最基础的接口无非就是open, read, write, close。整体分层如下:

  • libc的接口(如:open,read)
  • syscall接口(sys_read, sys_write)
  • vfs接口 (vfs_open, vfs_read)
    • 为什么有这层?主要是为了同一个不同io的接口。统一都使用inode表示一个文件。inode是个索引节点,通过inode,可以找到文件具体在磁盘上的位置。
  • 具体文件系统接口
    • 每个文件系统都有自己的数据结构,主要是如何把数据放到磁盘上
    • 一般来说,都是前面有个superblock,记录整体信息
    • 然后是根目录的inode。对于目录的inode就是记录了每一项的inode位置。
    • 通过目录inode,就可以找到文件的inode。
    • 文件的inode记录每块数据的存储位置。
  • 设备接口(driver)
    • 具体向io发起指令的部分,跟外设相关
文件系统功能

分配磁盘空间

管理文件块

空闲空间管理

分配算法

文件操作
  • 打开:

    • 内核会记录所有打开的文件
  • 读文件

    • 获取字节所在数据块
  • 写文件

    • 先读一块,修改,然后写会
基本数据结构

文件系统基本数据结构,这些全部是在硬盘上的

  • 文件卷控制块(superblock)

    • 对应文件系统,空余块
    • 在磁盘上位置是固定的,他指向目录项
    • 文件系统mount的时候,就是读取这部分数据
  • 文件控制块

    • 每个文件一个
    • 数据块位置
  • 目录项

    • 指向文件数据块,父目录,子目录

文件描述符

  • 对应打开的文件
  • 文件状态信息
  • 系统有系统级全局的打开文件表,每个进程有自己的打开文件表
  • 打开文件,目录项和文件都需要被打开
加载内存时间
  • 卷控制块:文件系统挂载时,载入内存
  • 文件控制块:当文件被访问的时候
  • 目录节点:在遍历文件路径
文件分配
  • 三种分配方式:连续、链式、索引
  • 索引分配:在文件头里指向索引块,索引块保存指向每个数据块指针
  • 对于大文件,用链式索引块,或多级索引块
  • UFS多级索引分配,文件头包含13个指针
文件打开操作
  • 先找到对应设备
  • 从对应设备的mount的fs,进行lookup
  • 找到文件inode,读磁盘加载
  • 保存对应文件inode,到file列表
  • 返回fd给应用进程

内核线程

当bios把os的内核代码从磁盘上加载到内存, os完成内存初始化后,就可以开始执行各种函数进行工作了,单片机就是到这一步,后续执行自身的代码逻辑。而os是给通用计算用的,他需要加载用户程序,所以还不能这么简单的处理。单片机的模式是传感器->cpu/软件逻辑->执行机构,计算机的模式是输入->cpu/软件->人。计算机的输出,驱动的是人,而不是步进马达。

os在加载用户进程前,还要先创建一些必要的内核线程。这些线程也会和用户进程一起参与调度。为了能让多个线程,并发执行,os需要设计一套机制,能够让多个线程、进程共存和切换。这套机制中会有很多复杂的数据结构,会利用栈,会利用很多寄存器。

内核之所以能够切换这些线程或进程,是通过切换相关寄存器来实现的,比如切换eip,代码执行位置就变了,切换esp,ebp,进程的栈就变了。所以,切换前要保护现场,就是要找块内存记录下这些值。这些值是PCB进程控制块的一个成员叫context,context保存了8个寄存器。

内核把当前执行线,封装为主线程,叫idle_proc,实际上他只是做调度。

idle_proc创建了init_main这个内核线程,init_main创建了user_main,启动用户shell。然后init_main就进入wait状态,然后也是反复执行schedule()。

内核线程会通过调用schedule(),主动放弃cpu,换句话说,就是用户进程没法通过时钟中断抢到cpu。而用户进程时间片一到就会被os强制放弃cpu。

用户进程

加载用户进程