Feiyang Wu

Feiyang Wu

Be Brave and be Happy

Hugo Setup

本网站基于HUGO和github.io的建立教程

2024年5月的某一天,我和倪驰明讨论为我们在2024年下半年建立网站方便教学的有关事宜。 一开始我本来想自己手写html,但是倪驰明说,为什么不用hugo呢?

Hugo 是一个流行的静态网站生成器(Static Site Generator,SSG),它允许用户从简单的文本文件(如 Markdown 文件) 生成完整的静态网站,因此我们可以通过编写markdown快速部署网页而无需大量编写html。

从2024年5月12日开始,我致力于使用Hugo开发这个网站。由于我计划将这个网站部署到github.io,因此 我将同时通过这个架构来开发自己的个人主页。

1. 下载安装Hugo

首先进入官网,找到安装页面。根据官网的指示进行安装。 我的开发平台是Windows,其包管理工具没有像Linux和MacOS那么方便。那么我们可以直接找到Prebuilt Binaries,由于我的电脑是AMD芯片,因此我选择hugo_extended_0.125.7_windows-amd64的版本进行 下载,解压,并安装。安装完成之后,将hugo所在的文件夹添加到Path环境变量。终端输入hugo version ,成功打印版本号,证明安装成功。

2. 选择心仪的Hugo主题

我们可以到Hugo主题页面选择合适的主题。我最终挑选FixIt作为我的网站主题。 选择到合适的主题后即可根据Hugo官方教程或FixIt官方教程进行安装。

1
2
3
4
hugo new site mySite
cd mySite
git init
git submodule add https://github.com/hugo-fixit/FixIt.git themes/FixIt

完成之后运行Hugo Server即可在localhost:1313测试网站。

3. Github 网站发布

参考了博客文章

Lecture4

STM32结构和原理

本节有关内容可以详细参见《STM32F4xx中文参考手册》

1. STM32架构

普林斯顿(冯·诺伊曼)架构

/figures/lecture4/Von_Neumann_Architecture.svg.png
普林斯顿(冯·诺伊曼结构)

组成部分:输入设备、输出设备、存储单元、算数逻辑单元、控制单元。

哈佛结构

哈佛结构是一种将程序指令存储和数据存储分开的存储器结构,它的主要特点是将程序和数据存储在不同的存储空间中,即程序存储器和数据存储器是两个独立的存储器,每个存储器独立编址、独立访问,目的是为了减轻程序运行时的访存瓶颈。

重要特点:指令(代码)和数据存储分开

所以一般会有指令I(Instruction)总线和数据D(data)总线的之分。

STM32F4型号架构

/figures/lecture4/stm32_arch.png
STM32F4x架构

2. STM32外设

如何组装一台电脑?

首先我们得先拥有一块主板(包含了我们的CPU)。 然后,我们得有显示屏、键盘、扬声器。我们还得装显卡、声卡、无线网卡……

对于STM32等微处理器,显示屏、键盘、无线上网不是必须的。 未了实现不同的功能,我们的STM32需要完成一些基本功能,例如:

  • 引出一些引脚,我可以控制是高电平还是低电平
  • 添加一个USB串口,可以与电脑通信
  • 一个定时器,在我需要的时候进行计时功能
  • ……

如何制造一个STM32?

采用什么CPU?ARM Cortex系列中央处理器。

配置哪些硬件?由意法半导体为我们完成并封装到STM32中。 外设,类似于组装电脑时的无线网卡、显卡、声卡等硬件。

那么如果我想自定义硬件(外设)怎么办?

使用硬件描述语言生成半定制电路。可以使用FPGA。

如何使用代码来操作外设?

如果使用显卡,那么我需要显卡驱动。

如果使用摄像头,那么我需要摄像头驱动。

如果使用Wifi,那么我需要网卡驱动……

如果使用外设,我需要外设驱动硬件抽象层(Hardware Abstraction Layer,HAL)。 应尽抽象层将硬件方面的不同抽离操作系统的核心,核心模式的代码就不必因为硬件的不同而需要修改。因此硬件抽象层可加大软件的移植性。

硬件抽象层底层逻辑

历史上,总共有两种办法来操作这些设备。 这些硬件外设设备其本质都是为了输入、输出,因此它们都是输入输出设备(IO设备,Input,Ouput)。

第一种方法,是设计专门的汇编命令。如x86汇编就保留了专门用来输入输出的指令。但是随着现代计算机中出现了越来越多不同的新设备,这种设计专门指令的方法也开始落伍。

第二种方法,叫做内存地址映射 IO(Memory Mapped IO)。这种方法也是大部分现代计算机所采用的输入输出的办法。

让我们回到硬件抽象层的概念。

硬件抽象层还是没有接触最底层的原理,因为硬件抽象层毕竟也是用代码写出来的。所以如果我想写硬件抽象层怎么办?

——寄存器操作

把寄存器当成电闸开关。当我想让一个房间的电灯亮起来,只需要拨动开关;类似的,当我要启用某一种功能,比如说让STM32的某一个引脚变成高电平,我只需要将寄存器的某一位设置为1。

或者说我可以用寄存器表示一种模式或功能。假设我的房间的灯有三种颜色,红、绿、蓝。我用00表示红,01表示绿,10表示蓝。

STM32的寄存器主要存在于两种地方: ARM Cortex内核里的寄存器。我们不需要显示地去操作它(除非编写汇编语言)。这些寄存器是和C语言编译完成之后的汇编语言较为相关。 挂接于总线上的寄存器。 假设系统有一块32kB的内存(位于0x0000 ~ 0x8000的位置)。这意味着不能访问0x9000的位置么?不是的。32位机的寻址能力是2^32。

我们可以把寄存器对应到一个空闲的地址上,然后在C语言中使用指针的方式进行读写! 这样,寄存器的表现就仿佛是一块内存,同时,寄存器的具体值又定义了硬件外设的行为。我们可以通过改变寄存器中的值,来控制和访问外设。

这就是内存地址映射IO的方法。

现在假设一个控制LED灯点亮的寄存器,被映射到了0x9000的地址上,寄存器的最低位(第0位)位1的时候,LED灯可以被点亮。

1
2
3
4
#define REG_ADDRESS 0x9000

volatile uint32_t* peripheral_register = REG_ADDRESS; // Volatile 关键字:防止编译器优化
*peripheral_register |= 0x0001;

STM32中外设众多,没有每个外设寄存器与地址的直接对应表,很多时候这些映射地址需要经过复杂的计算才能得出。而且,STM32还有位带操作等概念。

注意
  1. 不建议直接操作寄存器控制外设。请使用现存的库函数或硬件抽象层。
  2. 上述操作寄存器的方式只是一个例子,不代表真实可用的代码。
  3. STM32位带操作可以将寄存器的每个bit膨胀为4字节,但仍是最低位有效。这个映射机制叫做位带操作。

3. STM32时钟树

时钟

时钟信号就是计算机的脉搏,寄存器等时序逻辑器件依赖时钟的上升沿和下降沿运作。 时钟频率越高,计算机的运算能力越强,功耗也越大。为了平衡运算能力与工号,STM32设计了非常复杂的时钟系统。

时钟的作用:同步和时间测量。

同步:时钟信号用于同步计算机中所有组件的操作,包括CPU、内存、输入/输出设备等。通过提供一致的时间基准,时钟确保了不同组件之间的协调和有序运行。

时间测量:时钟提供了时间测量的基础。通过计数时钟周期,计算机可以测量和记录时间,从而支持各种时间相关功能,如时间戳、计时器和计数器等。

STM32时钟源

STM32的时钟源总共有四种,低速外部时钟,低速内部时钟,高速外部时钟,告诉内部时钟。内部时钟一般较频率较低且较不稳定, 外部时钟使用晶体振荡器,会更为稳定。

/figures/lecture4/%E6%97%B6%E9%92%9F%E6%A0%91.png
STM32F4x时钟树

常用概念

分频器(DIV):将时钟频率除以一定的倍数。

PLL倍频锁相环(倍频器):将时钟频率乘以一定的倍数。

通过复杂的时钟配置,最终可以确定各个模块的时钟频率。

4. 通用输入输出GPIO

GPIO(General Purpose Input Ouput, 通用目的输入输出)外设是STM32最基本的外设之一。学习GPIO是学习其他一切外设的基础。 GPIO分为端口(Port)若干,编号为ABCD…… 每个端口分别对应16个引脚(Pin) 例如:GPIOA Pin10, GPIOB Pin 2, GPIOC Pin3 …… 对于每个GPIOx, 都有9个寄存器控制。 分别为:

GPIO 端口模式寄存器GPIO 端口输出类型寄存器GPIO 端口输出速度寄存器GPIO 端口上拉/下拉寄存器GPIO 端口输入数据寄存器GPIO 端口输出数据寄存器GPIO 端口置位/复位寄存器GPIO 复用功能低位寄存器GPIO 复用功能高位寄存器

/figures/lecture4/gpio.png
GPIO原理图
有关寄存器的详细用法和资料,请查阅《STM32F4xx中文参考手册》

Lecture3

STM32基础

1. STM32简介

基础知识

单片机

单片机(Single-chip microcomputer),是把中央处理器、存储器、定时/计数器(timer/counter)、各种输入输出接口等都集成在一块集成电路芯片上的微型计算机。与应用在个人计算机中的通用微处理器相比,它更强调自供应(不用外接硬件)和节约成本,集成程度更高,但因为规格已经包含,所能实现的功能也较专一。它的最大优点是体积小,可放在仪表内部,但存储量小,输入输出接口简单。由于其发展非常迅速,旧的定义已不能满足,所以在很多应用场合被称为范围更广的微控制器(microcontroller)。

STM32

STM32,是由意法半导体基于 ARM Cortex-M 研制和生产的一系列32位单片机。 单片机,即微处理器,微处理器(Microprocessor)通常指称一种可编程特殊集 成电路,其所有组件小型化至一块或数块集成电路内。

其他概念

  • 计算机架构:计算机使用的指令集

    • 计算机常见架构:ARM架构、x86架构
  • 外设:电子计算机中位于机箱之外、能够通电并可以不依赖计算机进行正常的独立或半独立运行的硬件。 例如STM32中的TIM定时器、通用输入输出(GPIO)、中断控制器等。在现代电脑中可以包括键盘鼠标等。

  • 寄存器: 寄存器(Register)是中央处理器内用来暂存指令、数据和地址的存储器。寄存器的存贮容量有限,读写速度非常快。在计算机体系结构里,寄存器存储在已知时间点所作计算的中间结果,通过快速地访问数据来加速计算机程序的执行。

常见单片机

  • 51系列单片机
  • STM32
  • ESP32

STM32组成

  • ARM Cortex CPU
  • 外设

STM32实质上是由ARM提供中央处理器,意法半导体在此基础上设计外设所组成的微处理器。

2. STM32型号与分类

STM32

STM32系列专为要求高性能、低成本、低功耗的嵌入式应用设计的ARM Cortex®-M0,M0+,M3, M4和M7内核  。按内核架构分为不同产品: 主流产品(STM32F0、STM32F1、STM32F3)、超低功耗产品(STM32L0、STM32L1、STM32L4、STM32L4+)、高性能产品(STM32F2、STM32F4、STM32F7、STM32H7)等。

STM32最常见型号:STM32F1系列和STM32F4系列。 RoboMaster A型开发板和C型开发板都属于STM32F4系列。C型开发板具体型号为STM32F407。

3. 常用开发工具

开发软件

  • Keil5
  • STM32CubeMX
  • Clion
  • ARM GNU Toolchain (arm-none-eabi编译器)
  • Eclipse

开发软件库

  • CMSIS
  • STM32标准库
  • STM32标准HAL库
  • 实时操作系统(FreeRTOS, uCOS, ChibiOS)

烧录器

  • ST-Link
  • JTAG

debug、烧录软件

  • OpenOCD

4. STM32开发方法简述

对于裸机编程(不适用实时操作系统进行线程调度),主要使用三种方法编写代码:直接操作寄存器、 标准库函数编程(较老)、标准HAL(Hardware Abstraction Layer,硬件抽象层)库。如果 不采取裸机编程,那么将会使用到实时操作系统(RTOS)。

RTOS具有Linux等操作系统不具有的任务实时性响应机制,具体体现在线程调度机制等方面。

(a) Keil + 标准库

(b) Keil + STM32CubeMX + 标准Hal库

(c) Keil + STM32CubeMX + 标准Hal库 + FreeRTOS

(d) Clion + ChibiOS + ChibiOS HAL

5. Metaz战队工具链

工具链配置教程由Meta战队前项管刘子恺学长倾力打造。 因为这篇Wiki年代久远,其中有些链接内容可能已经发生错误或者失效。 在之前的文章中,我们已经安装过MinGW, git, cmake等工具,在根据教程安装工具链时,不要重复安装。

Meta战队工具链配置教程点击此处

6. 课后作业

  • 继续观看黑马程序员
  • 按照第五部分教程链接完成工具链配置。
  • (可选)自行学习STM32有关知识


Lecture2

C / C++ 编译原理

需要提前掌握的基础知识

  • 比特和字节的关系,8bit = 1B
  • 十六进制数的表达方式以及十六进制数和二进制数的转换
    • 例子:0x2f = 00101111

预习内容

1. 中央处理器(CPU)架构

内存

内存是计算机的存储空间。

对于很多没有接触过计算机的人来说,内存=硬盘。其实这种理解是错误的。内存掉电会丢失内容,而 硬盘在掉电之后信息不会丢失。但是内存的访问速度比硬盘快,因此实际上在电脑启动时操作系统的代码 会从硬盘上搬迁到内存里。平时打开的文件也是如此。

内存地址的基本单位是字节。一个字节=8比特。对于32位计算机,其寻址能力为32位,意思是有2^32个 地址。大部分计算机是字节寻址,即一个地址对应一个字节的数据(ECE120涉及的LC3是16位机,但是 一个地址对应2B两个字节的数据)。

其他

交给ECE120去学吧。

2. 编译流程

常见概念

头文件.h包含了函数声明、宏定义等。在C++当中还会存放类。.c文件中存放源代码。

具体流程

(a)预处理阶段

预处理阶段,其实用朴素的话语来讲就是对宏(Macro)进行文本替换或展开。例如:

1
#include <stdio.h>

在预处理完成之后,这段代码会被直接替换位stdio.h头文件包含的内容。 再例如:

1
#define GPIO_DEVICE_ID 1

代码里所有出现的GPIO_DEVICE_ID将被替换为1。

注意
当使用#include之后,就可以使用头文件里的宏。除非是编译器定义的宏,否则可能会无法被找到。
(b)编译阶段

将所有预处理之后的C/C++代码翻译成汇编语言(汇编文件一般以.s,.asm为后缀,将会在ECE120 后期课程涉及)。

(c)汇编阶段

将汇编语言翻译成计算机可执行的二进制文件(生成若干.o.obj文件)。每个独立的二进制文件 由于不知道相互之间的关系,现在还无法独立运行。

(d)链接阶段

这个阶段是最令人头大阶段。链接过程将多个目标文以及所需的库文件(.so等)链接成最终的可执行文件 (executable file)。该过程较为复杂,希望详细了解可以阅读知乎文章。 通俗易懂的讲法,就是把每个C/C++文件生成的二进制文件,或者一些厂商(例如Windows)提供的 二进制文件(即静态与动态链接库,常见后缀.so.lib.a)组装在一起。

静态链接库:当要使用时,连接器(linker)会找出程序所需的函数,然后将它们拷贝到执行文件, 由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。

动态库链接库:某个程序在运行中要调用某个动态链接库函数的时候,操作系统首先会查看所有正在运行的程序,看在内存里是否已有此库函数的拷贝了。 如果有,则让其共享那一个拷贝;只有没有才链接载入

常见错误

如果使用一个函数但没有定义它(大部分时候有可能是函数单纯打错了):

Failure
link_example.c:(.text+0xe): undefined reference to `foo' collect2.exe: error: ld returned 1 exit status

如果链接器发现了两个不同的二进制文件中有相同的全局变量或函数,连接器会这么报错:

Failure
/tmp/ccIa32rv.o:(.bss+0x0): multiple definition of head'/tmp/ccAvumDO.o:(.bss+0x0): first defined here /tmp/ccuVImET.o:(.bss+0x0): multiple definition of head' /tmp/ccAvumDO.o:(.bss+0x0): first defined here collect2: error: ld returned 1 exit status

3. 编译脚本

脚本(sript)就是把一些命令放在一个文件里,这样在执行的时候不需要一条条手动输入而是一次性 全部执行的一个文件。

编程语言也主要分为两种:编译语言与脚本语言。脚本语言的每一条代码逐条投喂至解释器,并理解 翻译成二进制代码供CPU执行。而编译语言将全部代码全部翻译为二进制代码之后才能供CPU执行。 最常见的脚本语言是Python。最典型的编译语言即为C和C++。

Makefile

Makefile 是一个用于管理项目构建的文件,通常用于编译和链接程序,特别是在大型项目中。 它通过定义一系列规则和目标,自动化了编译和构建过程,从而避免了手动输入复杂的编译命令。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 定义变量
CC = gcc
CFLAGS = -Wall -g

# 定义目标文件
TARGET = myprogram

# 定义依赖关系和规则
$(TARGET): main.o utils.o
    $(CC) $(CFLAGS) -o $(TARGET) main.o utils.o

main.o: main.c
    $(CC) $(CFLAGS) -c main.c

utils.o: utils.c
    $(CC) $(CFLAGS) -c utils.c

# 伪目标,用于清理构建文件
.PHONY: clean
clean:
    rm -f $(TARGET) *.o

找到项目的Makefile文件,打开终端输入:

1
make

make将根据Makefile生成gcc指令。例如上述代码的运行路径为中的文件有:

1
2
3
4
my_project/
├── Makefile
├── main.c
└── utils.c

$(CC)会被翻译成gcc,$(CFLAGS)会被翻译成-Wall -g。-o 我们在之前介绍过,是输出的意思。 所以如果输入make myprogram实际上相当于输入了以下的gcc指令:

1
2
3
gcc -Wall -g -c main.c # 将main.c编译为main.o
gcc -Wall -g -c utils.c
gcc -Wall -g -o myprogram main.o utils.o # 通过.o文件生成myprogram可执行文件

CMake

CMake 是一个跨台的构建系统,它用于管理项目的编译过程,生成特定于平台的构建文件(如 Makefile、Visual Studio 项目文件等)。CMake 通过一种高级脚本语言来描述构建过程, 能够更好地处理复杂的构建需求,并且在不同平台之间保持一致性。在我们的项目中我们一般使用 Cmake来生成Makefile。

假设现在我们的项目文件夹为:

1
2
3
4
my_project/
├── CMakeLists.txt
├── main.cpp
└── utils.cpp

CMakeLists.txt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
cmake_minimum_required(VERSION 3.10)

# 项目信息
project(MyProject)

# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 添加可执行文件
add_executable(MyProject main.cpp utils.cpp)

然后运行:

1
2
3
mkdir build
cd build
cmake ..  # .. 表示上一级目录,这里的意思是Cmakelists.txt在上一级目录

然后我们就能在build文件夹中找到Makefile了。此时我们就能运行:

1
make

我们就能得到想要的二进制可执行文件了。

课后作业

  • 使用Makefile和Cmake成功编译文件。
  • 继续观看黑马程序员教学视频。

Lecture1

欢迎来到Meta战队电控组

1. 预习内容

C 语言:B站黑马程序员(不建议下载使用Visual Studio,我们将不会用到它。如果教程用到了Clion,可以先开始下载。这次不做要求。推荐使用 Vscode,我们前期将使用gcc编译器进行编译C语言代码。Vscode安装参见:https://code.visualstudio.com/

第一讲大家请自行学习P1~P24有关章节的知识。

黑马程序员课程链接点击此处

2. Linux基本操作

(a) 终端

终端(Terminal)是一台电脑或者计算机系统,用来让用户输入数据,及显示其计算结果的机器,简而言之就是人类用户与计算机交互的设备。

Windows 终端(Powershell): 按下Win徽标+R,输入powershell。 Mac: 自行上网查阅如何打开终端。

(b) Linux命令基本用法

熟悉并掌握Linux常用指令的用法。 Linux指令语法结构: [tyang3@localhost Desktop]$ command [-options] [arguments]

command 命令:表示命令的名称,如cd ls mkdir等,实质是一个可执行的二进制程序

options 选项:定义命令的执行特性,中刮号[]并不存在于实际的指令中,而加入选项设定时,通常选项前会带 - 号或–号,有两种长短选项

短选项:用-引导,后面跟单个字符,如 -a、-l、-h等

多个短选项可以组合使用,效果和几个短选项一样,如-a –l -h===-alh

长选项:用–引导,后面跟完整的单词,如—help

arguments 参数:表示命令的作用对象,可以有多个参数,通常情况可以是文件名、目录、或用户名。

来源:https://zhuanlan.zhihu.com/p/33331219

3. 环境变量和MinGW编译器安装

(a)环境变量

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。

如果在终端输入一个命令(command),上面我们所说command实际上是一个二进制的可执行文件。那终端怎么知道这个二进制文件该去如何找到呢?

比如,现在打开windows终端,输入gcc, 按下回车,只会出现报错信息。但是当我将gcc.exe所在的文件夹加入环境变量之后,输入gcc之后终端就能运行gcc这个二进制可执行文件命令。

(b)MinGW编译器安装

(Mac和Linux用户可忽略此步,因为Mac和Linux自带gcc编译器)

接下来是windows MinGW编译器安装方法: 下载:https://sourceforge.net/projects/mingw/files/latest/download

大家可以按照这个教程的MinGW部分进行安装。

如果觉得环境变量部分还不够清楚,可以根据以下方法操作:

安装完成之后,找到安装位置,我们发现里面有一个bin文件夹:

图片

来源:https://baike.baidu.com/item/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F/1730949

在windows上搜索环境变量,打开后,点击环境变量:

环境变量

点击环境变量中的Path:

path

点击新建,把刚才MinGW那个带bin的路径给输入进来:

bin

注意
退出的时候所有窗口都要点击确定!不要直接把窗口叉掉!

bin是binary的缩写,也就是二进制文件。bin文件夹中包含我们将用到的大部分可执行二进制文件,包括gcc。现在这些可执行文件都能被终端找到了。 如果打开bin文件夹,可以找到以下的exe文件,现在这些.exe可执行文件都可以被终端直接执行。

bin

Win+R 输入powershell打开终端,输入gcc得到一下结果:

bin

证明结果正确(fatal error的原因只是因为没有待编译的c文件输入而已)。

(c) gcc编译C语言源文件

要编译单个.c文件,我们可以采取如下方法。 首先,找到.c文件的所在位置,使用终端的cd指令(不知道什么叫cd命令的去网上搜)。假设我想编译prime.c,那么Windows用户将输入: gcc prime.c -o prime.exe Mac\Linux用户请输入: gcc prime.c -o prime prime.c是告诉gcc我想编译哪个文件,-o指的是输出文件名。

bin

之后文件夹中会多出一个叫prime.exe的二进制可执行文件。 Windows运行该可执行文件只需输入: .\prime.exe Mac\Linux请输入: .\prime 可执行文件即可被执行完成。由于prime.c的功能是输出1~100中所有的质数,被打印的质数将会在终端中显示。

bin

4. UIUC VPN安装

由于我们将用github管理仓库,而国内连接github并不稳定,请大家尽快安装UIUC VPN。请根据自己的电脑 配置和操作系统选择合适的链接安装。 VPN下载:https://answers.uillinois.edu/illinois/98773

安装完成之后输入:

/figures/vpn1.png

输入NetID和密码之后,切记选择通道3,否则无法正常访问外网。

5. git和github

对于MacOS用户:

1
brew install git

对于Linux用户:

1
sudo apt install git

对于Windows用户:git安装点击此处。 Windows用户下载文件后,如果没有自动添加环境变量则需手动添加环境变量。

6. C语言基础语法

自行查阅预习内容。应掌握的内容为:P1-P24 应掌握的概念:注释、常量、关键字、变量、字符类型、标识符、数据类型、if语句

0%