0%

SPI简介

SPI 的物理线支持 三种类型

  • 片选线(NCS / CS / SS),某些数据手册会将 片选引脚 称为 NSS /NCS / SEL (Select) / CS (Chip Select) / STE (Slaver Transmit Enable)。
  • 时钟线(CLK / SCK)
  • 数据线(DA 或 MOSI 和 MISO )(此处的 DA 特指单根支持半双工的数据线)。

需要注意的是,SPI的通信线有太多的名称,需要自行对照芯片手册进行查询,避免混淆。

SPI 通过对硬件资源的使用修改(禁用片选、禁 / 复用数据线)可以达到 全双工半双工 通信的目的。标准SPI为 四线SPI (CS / CLK / MOSI / MISO);最低支持2根数据引脚(仅保留 CLK / DA,其中DA为分时复用数据线 )来达到半双工通信的目的,可以增加一 / 多根 CS 来达到拓展SPI从设备片选的目的;也可以利用四根独立的半双工数据线,来达到 标准四线SPI 四倍通信能力 的 六线SPI(一般被称为 Quad-IO SPI)。

SPI分类

SPI通信按GPIO 数量可以分类为 2~6线 SPI,包含单工、半双工、全双工三总。

按GPIO数量分类 通信制式 通信线
两线SPI 单工/半双工 CLK / DA 或 CLK / MOSI 或 CLK / MISO
三线SPI 单工/半双工/全双工 CS / CLK / DA 或 CLK / MOSI / MISO
四线SPI 全双工 CS / CLK / MOSI / MISO
六线SPI 全双工 CS / CLK / DA1 / DA2 / DA3 / DA4

例如,据STM32L476RG芯片手册《RM0351》第1450页所示,SPI外设可以被配置为以下三种模式,即 三线全双工两线半双工两线单工

  • Full-duplex synchronous transfers on three lines
  • Half-duplex synchronous transfer on two lines (with bidirectional data line)
  • Simplex synchronous transfers on two lines (with unidirectional data line)

HALF DUPLEX SPI

半双工通信可以是 两线SPI 或 三线SPI,后面着重说明两者异同。

PIN DEFINITION

NCS: SPI控制读写使能信号;使用时需要被拉低,否则SDIO会处在 HIGH-Z 态,而SCLK信号也会被无视。也可以被用于在通信错误发生时重置SPI 通信。两线SPI不需要这根线,但个别芯片可能需要这个引脚接地(不能悬空),具体看芯片手册。

SDIO: SPI的数据读写端口;半双工读写。只有在“从被控设备读出数据”的情况下,SDIO 才会由被控制设备控制。

SCLK: SPI接口时钟;总是由主控制器生成和控制。

TRANSMISSION PROTOCOL

跟其他通信类似,SPI 的 SDIO 需要在 时钟低电平时 进行电平跳变。

半双工SPI读写操作都包含两个字(byte),第一个字包含1 bit的 数据方向(或称 控制位) 和 7 bit的 地址,第二个字包含 数据

关于数据方向/控制位

通常定义是,当需要写入数据时,控制位写 1 。当需要读出数据时,控制位写 0

image-20220118135819247

但不一定所有支持半双工SPI的通信控制位都是在MSB,有可能是在 LSB 。

如,下方右侧 SL8541E 的安卓平台主控,支持半双工SPI,但是其控制位就在地址字符的LSB上。

image-20220419235450360

写操作

主控制器在SCLK下降沿时改变 SDIO的电平,被控设备在SCLK上升沿时读取SDIO的电平。

image-20220118140526017

读取操作

SDIO会在SCLK下降沿时修改,主控制器需要在SCLK的上升沿时读取SDIO的电平信息。

注意:发送完地址字 之后需要延长半个周期的时钟低电平。

image-20220118141033005

DEBUG RECORDS

下面这个时序图错误的坑是真的大,调了好就才折腾明白,这不能是 Hi-Z 态。

时序图错误

内部调试时发现,SDIO在读取数据前后必须是 输出模式,最好 强制拉高,而不是 Hi-Z 态。

image-20220419210753902

读写单个字符

以下这两个函数中的 for() 使用了 MSB 发送数据,最好不要自作聪明将 i 的数据类型改为 uint8_t 或其他非负型数据,以下这种循环写法会让程序陷入死循环的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static void write_byte(uint8_t data)
{
for(int i = 7; i>= 0; i--)
{
if( (data >> i) & 0x01)
{
write_high_level();
}
else
{
write_low_level();
}
}
}

static uint8_t read_byte(void)
{
uint8_t data = 0;
for(int i = 7; i>= 0; i--)
{
SCLK_SET_LOW;
delay_nop(LEVEL_DELAY_SLICES);

SCLK_SET_HIGH;
delay_nop(LEVEL_DELAY_SLICES);

data |= HAL_GPIO_ReadPin(SPI_DATA_GPIO_Port, SPI_DATA_Pin) << i;
}

return data;
}

SOURCE CODE

下方是适用于 STM32 平台的 GPIO-模拟 半双工SPI 代码,利用 空指令进行延时控制,实现半双工(三线/两线)SPI 的通信。

需要适配以下几个功能:

  1. 控制精细度在 \([10^{-9}s:10^{-6}s]\) 之间的 精准延时(如果做不到,通信速率就上不去 1MHz )。
  2. 控制引脚电平的宏定义。
  3. 设置数据接口输出设置数据接口输入 的代码。
  4. 拉高电平拉低电平 的代码。
  5. 对照着 正确的时序图 撸一遍 读单字符写单字符 的代码。
  6. 对照着 正确的时序图 撸一遍 写寄存器读寄存器 的代码。

注意:都是按顺序来完成的,所有后面的功能都需要依托于前面的功能来实现。而且,需要用示波器(有条件)进行测量以校准通信速率,并使用正确的时序图进行编码(如果是错误的,浪费时间,尽快跟厂家确认)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
 /*-----------------------------------------------------------------------------
* HEARDER FILES
*---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>

#include "spi_access.h"
#include "stm32l4xx_hal.h"
#include "log.h"
#include "main.h"

/*-----------------------------------------------------------------------------
* MACRO DEFINITIONS
*---------------------------------------------------------------------------*/
// 通信引脚定义
#define SPI_DATA_Pin GPIO_PIN_5
#define SPI_DATA_GPIO_Port GPIOC

#define SPI_CLK_Pin GPIO_PIN_6
#define SPI_CLK_GPIO_Port GPIOC

#define SPI_CS_Pin GPIO_PIN_8
#define SPI_CS_GPIO_Port GPIOC

// 定义电平
#ifndef HIGH_LEVEL
#define HIGH_LEVEL GPIO_PIN_SET
#endif

#ifndef LOW_LEVEL
#define LOW_LEVEL GPIO_PIN_RESET
#endif

// 定义引脚电平控制
#define SDIO_SET_HIGH {HAL_GPIO_WritePin(SPI_DATA_GPIO_Port,SPI_DATA_Pin,HIGH_LEVEL);}
#define SDIO_SET_LOW {HAL_GPIO_WritePin(SPI_DATA_GPIO_Port,SPI_DATA_Pin,LOW_LEVEL);}

#define SDIO_SET_IN {HAL_GPIO_ReadPin(SPI_DATA_GPIO_Port, SPI_DATA_Pin);}

#define SCLK_SET_HIGH {HAL_GPIO_WritePin(SPI_CLK_GPIO_Port, SPI_CLK_Pin, HIGH_LEVEL);}
#define SCLK_SET_LOW {HAL_GPIO_WritePin(SPI_CLK_GPIO_Port, SPI_CLK_Pin, LOW_LEVEL);}

#define SNCS_SET_HIGH {HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, HIGH_LEVEL);}
#define SNCS_SET_LOW {HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, LOW_LEVEL);}

// 电平延时
#define LEVEL_DELAY_SLICES 80

// 寄存器(最大)尝试写入次数
#define MAX_WRITE_TIMES 10

/*-----------------------------------------------------------------------------
* DATA TYPE DECLARATION
*---------------------------------------------------------------------------*/
// spi句柄
typedef struct
{
int handle;
} spi_dev;

/*-----------------------------------------------------------------------------
* GLOBAL PARAMETERS
*---------------------------------------------------------------------------*/
static bool two_wired_spi; // 两线SPI使能开关

/*-----------------------------------------------------------------------------
* PRIVATE FUNCTIONS DEFINITION
*---------------------------------------------------------------------------*/
/**
* @brief delay for a slice of cpu
* @param {volatile uint32_t} times
* @return {*}
*/
static void delay_nop(volatile uint32_t times)
{
for(uint32_t i = 0; i < times; i++)
{
__NOP();
}
}

/**
* @brief delay for 1us
* @param {volatile uint32_t} times
* @return {*}
*/
static void delay_us(volatile uint32_t times)
{
for(uint32_t i = 0; i < times; i++)
{
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
}
}

static void delay_ms(uint16_t times)
{
for(uint16_t i = 0; i < times; i++)
{
delay_us(1000);
}
}

static void sdio_input_mode(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};

GPIO_InitStruct.Pin = SPI_DATA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(SPI_DATA_GPIO_Port, &GPIO_InitStruct);
}

static void sdio_output_mode(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};

GPIO_InitStruct.Pin = SPI_DATA_Pin ;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SPI_DATA_GPIO_Port, &GPIO_InitStruct);
}

static void write_high_level(void)
{
SCLK_SET_LOW;
SDIO_SET_HIGH;
delay_nop(LEVEL_DELAY_SLICES);
SCLK_SET_HIGH;
delay_nop(LEVEL_DELAY_SLICES);
}

static void write_low_level(void)
{
SCLK_SET_LOW;
SDIO_SET_LOW;
delay_nop(LEVEL_DELAY_SLICES);
SCLK_SET_HIGH;
delay_nop(LEVEL_DELAY_SLICES);
}

static void write_byte(uint8_t data)
{
for(int i = 7; i>= 0; i--)
{
if( (data >> i) & 0x01)
{
write_high_level();
}
else
{
write_low_level();
}
}
}

static uint8_t read_byte(void)
{
uint8_t data = 0;
for(int i = 7; i>= 0; i--)
{
SCLK_SET_LOW;
data |= HAL_GPIO_ReadPin(SPI_DATA_GPIO_Port, SPI_DATA_Pin) << i;
delay_nop(LEVEL_DELAY_SLICES);

SCLK_SET_HIGH;
delay_nop(LEVEL_DELAY_SLICES);
}

return data;
}

/*-----------------------------------------------------------------------------
* PUBLIC FUNCTIONS DEFINITION
*---------------------------------------------------------------------------*/
void *write_sensor_init(bool switch_two_wired_spi)
{
if(switch_two_wired_spi)
{
two_wired_spi = true;
}
else
{
two_wired_spi = false;
}

spi_dev *instance = NULL;
instance = malloc(sizeof(spi_dev));
memset(instance, 0, sizeof(spi_dev));

if(two_wired_spi)
{
SNCS_SET_LOW;
SCLK_SET_HIGH;
delay_ms(5);
SCLK_SET_LOW;
delay_ms(1);
SCLK_SET_HIGH;
delay_ms(5);
}
else
{
sdio_input_mode();
SCLK_SET_HIGH;
delay_nop(LEVEL_DELAY_SLICES);
SCLK_SET_LOW;
delay_nop(LEVEL_DELAY_SLICES);
SCLK_SET_HIGH;
delay_nop(LEVEL_DELAY_SLICES);
}

instance->handle = 1;
return (void *)instance;
}

void write_sensor_deinit(void* handle)
{
if (handle != NULL)
{
free(handle);
}
else
{
LOGE("ERROR: NULL spi handle.");
}
}


void spi_resynchronized()
{
if (two_wired_spi)
{
SCLK_SET_HIGH;
SDIO_SET_HIGH;
delay_ms(2);
SCLK_SET_LOW;
delay_us(1);
SCLK_SET_HIGH;
delay_ms(2);
SDIO_SET_IN;
delay_ms(1);
}
else
{
// 暂时未完成
return;
}
}

int write_register(uint8_t regAddr, uint8_t regData)
{
regAddr |= 0x80;

for(uint8_t write_times = 0; write_times < MAX_WRITE_TIMES; write_times++)
{
sdio_output_mode(); // 设置SDIO为输出模式
SDIO_SET_HIGH; // 拉高SDIO电平
SCLK_SET_HIGH; // 拉高SCLK电平
delay_us(1); // 2-wired spi

if(!two_wired_spi)
{
SNCS_SET_LOW; // 拉低SNCS电平
}
delay_nop(LEVEL_DELAY_SLICES);

write_byte(regAddr);
write_byte(regData);

if(!two_wired_spi)
{
SNCS_SET_HIGH;
sdio_input_mode();
delay_nop(LEVEL_DELAY_SLICES);
}
else
{
delay_us(500);
}

if(read_register(regAddr) == regData) return 0;
}

LOGE("ERROR: Write register failed.");
return -1;
}

uint8_t read_register(uint8_t regAddr)
{
uint8_t data = 0;
regAddr &= 0x7F; // 设置为读取模式

sdio_output_mode(); // 设置SDIO为输出模式
if(two_wired_spi) delay_us(10);
SDIO_SET_HIGH; // 拉高SDIO电平
SCLK_SET_HIGH; // 拉高SCLK电平
if(two_wired_spi) delay_us(100);

if(!two_wired_spi)
{
SNCS_SET_LOW; // 拉低SNCS电平
}
delay_nop(LEVEL_DELAY_SLICES);

write_byte(regAddr);

SCLK_SET_LOW;
sdio_input_mode();
if(two_wired_spi) delay_us(5);
else delay_nop(LEVEL_DELAY_SLICES);

data = read_byte();

SCLK_SET_HIGH;
sdio_output_mode();
delay_nop(LEVEL_DELAY_SLICES);
if(two_wired_spi) delay_us(40);
else SNCS_SET_HIGH;

return data;
}

REFERENCE

  1. [【硬件通信协议】5. 实例解析非标准SPI(三线SPI)_sishuihuahua的博客-CSDN博客_三线spi](https://blog.csdn.net/sishuihuahua/article/details/105047926)

本文主要是将 在KEIL上能够成功编译的 STM32F103VE 芯片项目转移到 VS Code 的 EIDE 插件中,主要是针对 Keil MDK-ARM 中的 Option 进行参数复制,即 EIDE 的配置。

需要以下两个工具:

  • Keil MDK-ARM
  • Keil.STM32F1xx_DFP.2.4.0.pack

其实还是依托于 Keil MDK-ARM ,只是换了 VS Code 的皮。

EIDE INSTALL

首先需要在 VS Code 中安装 Embedded IDE(如下),官方手册:EIDE在线文档

image-20220414214014581

EIDE CONFIGURATION

对 EIDE 的配置主要包括 扩展设置芯片支持包安装构建参数设置项目属性设置

image-20220414211312253

扩展设置

可以在 VS Code 的插件中选中 Embedded IDE ,在齿轮状设置图标菜单里选择 扩展设置,在里面配置好编译工具链的路径还有一些可设置选项。

image-20220414203531929

STM32 可以用 ARM CC 和 ARM GCC 进行编译,具体需要看芯片要求,Keil 的话用的是 ARM CC 5版本或者 ARM CC 6 版本。

可以到 Keil MDK-ARM 的安装路径下进行查看,例如 在 D:\Keil_v5\ARM\ARMCC\bin 中,选择右键 用 Powershell 打开,然后输入 ./armcc.exe 即可查看版本号。

image-20220414183859215

主要配置六个参数即可,需要根据本地的 Keil 安装路径来进行设置(不要盲目复制粘贴)。

image-20220414203709473

image-20220414203744037

芯片支持包安装

Keil 的芯片支持包主要看 Option -> Target 下的 STM32F103xx.svd

image-20220414194711199

通过本地搜索可以看到这个文件需要通过安装 .pack 扩展包得到。

image-20220414190916865

MDK5 Software Packs (keil.com) 中搜索对应芯片的扩展包,如 Keil.STM32F1xx_DFP.2.4.0.pack

image-20220414193428926

点击 芯片支持包 右侧的 加号,即可选择通过 在线安装(From Repo) 或者 离线安装(From Disk)。

image-20220414212238565
image-20220414212220319

安装完扩展包之后 再点击下图中的加号,输入芯片型号进行进一步选择。

image-20220414191124478

即可查看芯片的大致信息:

image-20220414190706840

构建参数设置

构建参数的依据是 Keil 中的这几个参数选项卡:

image-20220414212732414

方法:

  1. 提取 Keil 中的构建参数 (如下 C/C++ 参数设置)
  2. 提取 EIDE 中的构建参数
  3. 对比差异,以 Keil 为准对 EIDE 进行增删

C/C++ 参数设置

下方是 Keil 中对 C/C++ 的构建参数管理页面:

image-20220414200627162

以下是从 Keil MD5 的 Option 中扒下来的编译参数,即最后一行的 Compiler Control String

1
2
3
4
5
6
-c --cpu Cortex-M3 -D__MICROLIB -g -O3 --apcs=interwork --split_sections -I include -I ../ -I ../../../Libraries/CMSIS/CM3/CoreSupport -I ../../../Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x -I ../../../Libraries/STM32F10x_StdPeriph_Driver/inc -I ../../../Utilities/STM32_EVAL -I ../../../Utilities/STM32_EVAL/STM3210B_EVAL -I ../../../Utilities/STM32_EVAL/Common -I ../../STM32F10x_StdPeriph_Template -I ../MDK-ARM
-IF:/Workspace/xiwang_production_test_embedded/Project/STM32F10x_StdPeriph_Template/MDK-ARM/RTE/_STM3210B-EVAL
-ID:/Keil_v5/ARM/PACK/ARM/CMSIS/5.0.0/CMSIS/Include
-ID:/Keil_v5/ARM/PACK/Keil/STM32F1xx_DFP/2.4.0/Device/Include
-D__UVISION_VERSION="522" -D_RTE_ -DSTM32F10X_HD -DUSE_STDPERIPH_DRIVER -DSTM32F10X_HD -DUSE_STM3210B_EVAL
-o .\build\STM3210B-EVAL\*.o --omf_browse .\build\STM3210B-EVAL\*.crf --depend .\build\STM3210B-EVAL\*.d

以下是从 EIDE 中扒下来的编译命令:

1
2
3
4
5
6
7
8
9
10
11
......

C command line (armcc):

-c --apcs=interwork --cpu Cortex-M3 --li --c99 -D__MICROLIB -O0 --split_sections --diag_suppress=1 --diag_suppress=1295 -g -I.\.. -I.\..\..\..\Libraries\CMSIS\CM3\CoreSupport -I.\..\..\..\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x -I.\..\..\..\Libraries\STM32F10x_StdPeriph_Driver\inc -I.\..\..\..\Utilities\STM32_EVAL -I.\..\..\..\Utilities\STM32_EVAL\STM32100E_EVAL -I.\..\..\..\Utilities\STM32_EVAL\Common -I.\.cmsis\dsp_lib -I.\.cmsis\include -I.\RTE\_STM32100E-EVAL -I.\.eide\deps -I.\include -I.\.eide\deps -DUSE_STDPERIPH_DRIVER -DSTM32F10X_HD_VL -DUSE_STM32100E_EVAL -DGD32F10X_HD

CPP command line (armcc):

-c --cpp --apcs=interwork --cpu Cortex-M3 --li -D__MICROLIB -O0 --split_sections --diag_suppress=1 --diag_suppress=1295 -g -I.\.. -I.\..\..\..\Libraries\CMSIS\CM3\CoreSupport -I.\..\..\..\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x -I.\..\..\..\Libraries\STM32F10x_StdPeriph_Driver\inc -I.\..\..\..\Utilities\STM32_EVAL -I.\..\..\..\Utilities\STM32_EVAL\STM32100E_EVAL -I.\..\..\..\Utilities\STM32_EVAL\Common -I.\.cmsis\dsp_lib -I.\.cmsis\include -I.\RTE\_STM32100E-EVAL -I.\.eide\deps -I.\include -I.\.eide\deps -DUSE_STDPERIPH_DRIVER -DSTM32F10X_HD_VL -DUSE_STM32100E_EVAL -DGD32F10X_HD

......

EIDE 中查看编译命令的方法:项目名称右键点击 查看生成的编译器命令行 即可。

image-20220414194229852

互相比对二者之间的编译参数差异,发现以下差异:

  1. Keil 中的代码优化等级为 3 即参数 -O3 ,而 EIDE 中为 0 即参数 -O0
  2. EIDE 比 Kei多了几个参数 --diag_suppress=1 --diag_suppress=1295 ,需要删除。

ASM 和 Linker 参数设置

对比过 Keil 的参数之后,保持如下设置:

image-20220414213239217
image-20220414213257541

项目属性设置

包含路径设置

这个就不多说了。

需要注意的是,RTE路径下的 STM32xxx-EVAL 一定要是正确的,可以多,不要少。

image-20220414213503962

预处理定义

从 EIDE 导入 Keil 项目时,可能回设置很多不需要或者错误的 预处理定义(宏定义),需要跟 Keil 保持一致。

image-20220414213732758

image-20220414195358900

DEBUG RECORDS

EXPECTED A }

提示 少了 } ,该文件是官方文件,应该不会错。经过检查,发现是宏定义过多导致的错误,修改过 预处理定义 中的宏定义设置就解决了。

image-20220414190748098

如下,STM32F10X_HDSTM32F10X_HD_VL 冲突了。

image-20220414194044509

最后,编译结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
[ INFO ] start building at 2022-04-14 20:22:39

[ TOOL ] ARM Compiler 5.06 update 4 (build 422)

[ INFO ] file statistics (rebuild mode)

+---------+-----------+-----------+-----------+--------+
| C Files | Cpp Files | Asm Files | Lib Files | Totals |
+---------+-----------+-----------+-----------+--------+
| 46 | 0 | 1 | 0 | 47 |
+---------+-----------+-----------+-----------+--------+

[ INFO ] start compilation (jobs: 4) ...

>> [ 2%] CC '../../../Libraries/CMSIS/CM3/CoreSupport/core_cm3.c'
>> [ 4%] CC 'main.c'
>> [ 6%] CC '../../../Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_flash.c'
>> [ 8%] CC '../../../Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_usart.c'
......
>> [ 89%] CC 'source/stm32f10x_it.c'
>> [ 91%] CC '../../../Utilities/STM32_EVAL/STM32100E_EVAL/stm32100e_eval_lcd.c'
>> [ 93%] CC 'source/system_stm32f10x.c'
>> [ 95%] CC '../../../Utilities/STM32_EVAL/stm32_eval.c'
>> [ 97%] CC 'source/write.c'
>> [100%] AS '../../../Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/arm/startup_stm32f10x_hd_vl.s'

[ INFO ] start linking ...

Program Size: Code=9028 RO-data=340 RW-data=156 ZI-data=1412

Total RO Size (Code + RO Data) 9368 ( 9.15kB)
Total RW Size (RW Data + ZI Data) 1568 ( 1.53kB)
Total ROM Size (Code + RO Data + RW Data) 9524 ( 9.30kB)

RAM : [ ] 2.4% 1.5KB/64.0KB
FLASH: [ ] 1.8% 9.3KB/512.0KB

[ INFO ] start outputting file ...

>> output hex file [done]

file path: "build/STM32100E-EVAL/FT_Test.hex"

>> output s19 file [done]

file path: "build/STM32100E-EVAL/FT_Test.s19"

>> output bin file [done]

file path: "build/STM32100E-EVAL/FT_Test.bin"

[ DONE ] build successfully !, elapsed time 0:0:9

[ INFO ] run tasks after build ...

>> axf to elf [done]

类似于 gnuplot , matplotlib 也是开源的图形绘制软件。

需要使用到的工具如下:

  • Jupyter:python 分布调试 和 Markdown 笔记记录一体化工具。
  • Python3.10
  • Python3 插件: matplotlib / pandas
  • VS code 所需插件: pylance

其中, matplotlib 是绘图所用的开源工具库,而 pandas 是文档管理的 “瑞士军刀”。

环境安装

JUPYTER 安装

Jupyter可以支持在线和离线使用,在线即以网页形式支持使用,可以在服务器上进行部署。离线即在 Visual Studio Code 中以插件形式提供安装和使用。

在线安装

以 Ubuntu 系统为例,可以用 apt 进行安装,也可以用 pip (或 pip3 )进行安装。

1
sudo apt install jupyter

在线版 Jupyter 还需要安装一个 ipyparallel 的类:

1
2
pip3 install ipyparallel
ipcluster start

远程访问配置项

参考 《ubuntu 安装Juypter并设置远程访问》对 服务器端 Jupyter进行设置和访问测试。

相关防火墙设置 / 证书 / 域名 不再赘述。

自启动配置

另外,Linux 服务器端 Jupyter 用 jupyter notebook 命令启动,无法开机自启,需要参考 《Linux下Jupyter开机启动设置》 进行设置。

设置好后 在线版 Jupyter 编辑器的页面如下:

在线使用界面

离线安装

在 VS code 插件中搜索并安装第一个 Jupyter 即可,插件系统会自动安装下面附带的 Jupyter KeymapJupyter Notebook Renderers 两个插件。

image-20220327163752153

安装完之后,需要新建一个 .ipynb 格式的文件,即可自动打开 Jupyter 插件进行使用。

离线使用界面

PIP 依赖库安装

在线版和离线版都需要使用 terminal 打开并输入命令进行安装下方的依赖库:

1
pip3 install matplotlib pandas

图形生成代码

下方利用 pandas 类对 .csv 文件中的相关列进行数据提取,然后利用 matplotlib 类对 数据进行绘图。

因功能比较简单,不再赘述。

具体需要注意的事项已在代码注释中写明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 导入依赖库
import numpy as np
import pandas as pd
import csv

# 导入工具
from pandas import Series,DataFrame
from matplotlib import pyplot as plt
from matplotlib.font_manager import FontProperties

# 配置csv表格中的行信息
column=["ts","dx","dy","motion","iqc","shutter","frame_avg","status"]

# 设置文件路径
csv_file_path = '../data/'
pic_file_path = '../pic/'

# 输入和输出文件名
file = '20220327_135012'
csv_file_name = file + '.csv'
pic_file_name = file + '.jpg'

# 配置各轴及标题标签
chart_title = 'A4 data collection in stm32l476rg'
x_label = 'time(ms)'
y_label = 'data collected'

# 配置各线段标签
line_1_label = 'dx'
line_2_label = 'dy'

# 配置线段透明度
line_1_alpha = 0.5
line_2_alpha = 0.5

# 配置线段类型
# 可选 scatter 和 line
plot_type = 'scatter'

# 用于正常显示中文标签
plt.rcParams['font.sans-serif']=['YAHEI']
# 用于正常显示正负号
plt.rcParams['axes.unicode_minus']=False

# 定义空列表存放xy轴数据点
x = []
y = []
z = []

'''
# 可以使用open()方法对csv文件进行读取
# 但是暂时没有找到可以避免读取到头部信息的方法,即不能跳过部分数据进行读取
with open( csv_file_path + csv_file_name, 'r', encoding='utf8') as csv_file:
plots = csv.reader(csv_file, delimiter=',')
for row in plots:
x.append(int(row[0]))
y.append(int(row[1]))
z.append(int(row[2]))
'''

# 使用panda的read_csv()方法可以设置跳过部分数据进行处理
csv_file = pd.read_csv(csv_file_path + csv_file_name,
skiprows=21,
delimiter=',',
names=column)

# 将对应列数据放入变量中
x = csv_file['ts']
y = csv_file['dx']
z = csv_file['dy']

# 设置线段标签
if( plot_type=='line'):
plt.plot(x,y, label=line_1_label, alpha=line_1_alpha, lw=1)
plt.plot(x,z, label=line_2_label, alpha=line_2_alpha, lw=1)
elif( plot_type == 'scatter'):
plt.scatter(x,y, label=line_1_label, alpha=line_1_alpha, s=1)
plt.scatter(x,z, label=line_2_label, alpha=line_2_alpha, s=1)

# 设置x轴、y轴和标题标签
plt.xlabel(x_label)
plt.ylabel(y_label)
plt.title(chart_title)

# 展示图像
plt.legend()

# 图像保存
plt.savefig( pic_file_path + pic_file_name , dpi=400)
plt.show()

使用方法

  1. 生成一个文件夹,下面包含三个并行子路径。
1
2
3
├── data
├── pic
└── src

网页版:

网页版文件结构示意
  1. 将代码放到 src/ 中,在将代码第19行中的 file = '20220327_135012' 文件名进行修改,确保其与 data/ 路径下的 .csv 文件名成可以对上。
  2. 执行整段代码,即可在 pic/ 文件夹下找到对应的图像文件。

REFERENCE

  1. vscode写jupyter notebook

  2. Pandas将csv数据拆分成多列并保存

TOOLS DOWNLOAD AND SETUP

在 ST 官网下载了 CubeMX 的 Mac 版软件,然后会发现关闭安全相关的限制也没办法打开该软件包。

image-20220222205346872

通过 tree 命令查看目录可以找到一个叫 SetupSTM32CubeMX-6_3_0 的应用程序,运行该文件即可进行安装。

image-20220222205452868

Mac 系统上可运行的程序都是 .App 结尾的文件夹,直接点击右键的 显示包内容 即可进入查看。

image-20220222205524560

双击 SetupSTM32CubeMX-6_3_0 即可运行安装程序。

image-20220222205545346

NEW PROJECT

在 CubeMX 的 Home 界面,可以 根据芯片型号新建项目,也可以 根据ST板子型号新建项目

image-20220222213510204

只需要在对应需要的 MCU 或者 板子上双击即可。

image-20220222213140557

如果使用ST自家的评估板,新建项目时建议直接选择 ACCESS TO BOARD SELECTOR

image-20220222212802407

它会帮助默认设置一些板子上的外设(它会提示 Initialize all peripherals with their default Mode? ),就不用用户自己去设置,否则选择 ACCESS TO MCU SELECTOR 就会在某些地方上产生默认配置差异,如 GPIO。

image-20220222213201459

下图为根据 ST 板子型号生成项目时默认配置的一些外设引脚。当然,还需要自行设置各个需要使用到的引脚的具体配置,如输入/输出、拉高/拉低等等。

image-20220222213417943

下图为根据 ST 芯片信号生成项目时默认不配置任何引脚:

image-20220222213744404

DEBUG RECORDS

cmake .. ; make 时发现 cmake 无误,而 make 报错,提示 attempt to rename sepc 'link' to already defined spec 'nano_link'

image-20220222224906371

具体报错信息如下:

1
2
3
4
5
fatal error: /Users/liewzheng/bin/gcc-arm-none-eabi-5_4-2016q3/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/nano.specs: attempt to rename spec 'link' to already defined spec 'nano_link'
compilation terminated.
make[2]: *** [led_blink.elf] Error 1
make[1]: *** [CMakeFiles/led_blink.elf.dir/all] Error 2
make: *** [all] Error 2

CMakeLists.txt 文件中只有以下此句设计 specnano 关键字(下方注释是后来加上去的),此配置在 linux 平台上是运行正常的。

1
2
3
4
5
# 如果提示 linker 相关的错误,请删除  `-specs=nosys.specs` 和 `-specs=nano.specs` 这两个标志及所在临时文件夹并再次 `cmake .. ; make` 进行尝试
SET(CPU "-mcpu=cortex-m4")
SET(FPU "-mfpu=fpv4-sp-d16")
SET(FLOAT_ABI "-mfloat-abi=hard")
SET(MCU "${CPU} -mthumb ${FPU} ${FLOAT_ABI}" --specs=nosys.specs -specs=nano.specs)

解决方法

根据 Building Error from imported STM32CubeMX project 的解决方法,删除 -specs=nosys.specs-specs=nano.specs 这两个连接器的标志即可编译通过。

image-20220222230947465

删除该两个标志后,MCU 变量为 SET(MCU "${CPU} -mthumb ${FPU} ${FLOAT_ABI}"

WARNING: CANNOT FIND ENTRY SYMBOL ARCH_PATHS_FIRST

image-20220222235232398

本文只涉及 Jenkins 在 Debian Linux (Kali) 下的相关安装,不涉及使用教程,关于Jenkins的相关科普请自行搜。

Jenkins安装

Jenkins 可以通过两种方式运行:

  • 通过官网下载 http://mirrors.jenkins.io/war-stable/latest/jenkins.war 程序,并确保安装了 openjdk-8 即可。此种方式是直接运行 .war 的软件的,免安装,但基于 ssh 远程连接的用户断开连接之后可能会导致无法运行。即,如果不知道如何正确操作shell以便在后台运行该免安装程序,就不推荐使用。
  • 另一种是安装版本,仍然需要用户提前先安装好 openjdk-8 ,然后通过 官网教程 来添加源和使用 apt 命令进行安装。这种方式可以使用 systemctl 命令确保 Jenkins 开机自启动并在后台运行。

JAVA SETUP

需要注意的是,jre有 oracle 版本和 openjdk 版本,需要安装后者。

1
sudo apt update ; sudo apt upgrade ; sudo apt install openjdk-8-jre

JAVA_HOME 写到你对应的 shell 配置文件中。

1
2
3
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64
export CLASSPATH=$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$JAVA_HOME/bin:$PATH

免安装运行

下面就是使用 java -jar jenkins.war --httpPort=8080 命令来免安装运行的。

image-20220224003818560

安装运行

实际上,直接运行下面的 sudo apt install openjdk-11-jre 可能会导致无法运行,原因不明,还是推荐自行使用 sudo apt install openjdk-8-jre 进行安装。

image-20220224191828341

防火墙配置

这一点就不再赘述了。

  1. 开启系统内端口:用 iptables 参考 《CentOS7安装iptables防火墙》,注意看后面评论内容。
  2. 开启控制台端口:配置为云服务器的要去网页界面的控制台将对应端口打开。

Jenkins配置

安装完毕之后,在浏览器中输入 对应网址的 8080 端口,进入页面后输入管理员密码。

image-20220224003852043

选择新手入门配置安装插件。

image-20220224003716199

安装完毕之后就会让你设置管理员账户。

虽然,java11是推荐安装,可我就是运行失败。

image-20220224004120934

基本上也就只参考了廖雪峰这篇 《 搭建Git服务器 》 的文章。

部署git服务器

确保已安装git

for debian linux:

1
sudo apt install git 

and check the version of it:

1
git --version

创建git账户

Add a account name git in linux :

1
sudo adduser git

设置使用者的SSH配置

Copy the SSH keys of your client computers to save to the file in /home/git/.ssh/authorized_keys .

禁用shell登录

For the safety of your server, you should ban the shell login for git account.

1
sudo vim /etc/passwd

Find something like git:x:1001:1001:,,,:/home/git:/bin/bash , and modify it to:

1
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell

git用户可以正常通过ssh使用git,但无法登录shell,因为已经为git用户指定的git-shell每次一登录就自动退出。

新建仓库

  • Build a directory named srv to store the repositories. (Of course you can name it the way you like.)
  • Initializa a new bare repository name xxx.
  • Change its owner as the user git which is just created before.
1
2
3
cd /srv/
sudo git init --bare /srv/xxxx.git
sudo chown -R git:git /srv/xxxx.git

And you can copy the script below TO CREATE YOUR NEW REPOSITY. Remember to save it as .sh file and use chmod +x command to make it executable.

By default, this script is stored as gitInit.sh in the diretory /srv.

If you change the script execute diretory, remember to modify the script below.

1
2
3
GIT_REPOSITORY=$1
sudo git init --bare $GIT_REPOSITORY.git
sudo chown -R git:git $GIT_REPOSITORY.git

USAGE:

1
./gitInit.sh <newRepository>

使用仓库

到这一步为止,已经可以使用 git pull git@<yourServer>:/srv/xxx.git 进行仓库拉取了。

1
$ git clone git@<yourServer>:/srv/sample.git

And you will see:

1
2
Cloning into 'sample'...
warning: You appear to have cloned an empty repository.

如果需要添加一个固定的远程仓库,需要使用到 url ,即需要开启了 443 端口的服务器。

其他服务器管理

证书颁发

如下所示,下面的子域名已经签审了 SSL 证书了,可以正常使用 443 端口不被拒绝。

image-20220223015715136

防火墙设置

确保腾讯云服务器管理页面下的防火墙设置中已开启 443 端口。

image-20220223015852398

使用 ubuntu 和 centos 系统的可以参考 《CentOS7安装iptables防火墙》 开启 443 端口,直接运行下面命令即可:

1
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

克隆远程仓库

如果本地已有仓库,可能会与远程服务器的 master 分支冲突,需要自行使用 git fetch 管理,然后使用 git push --set-upstream master 进行上传。

image-20220223014741138

Mac 平台自带的 zsh 真的极不好用,同样是 unix-like 平台,跟 kali 平台上的 zsh 体验相差甚远。 一直都是黑白配色,而且修改中断和配置的动手能力确实没有那么强。偶然适用了一下 fish ,发现在 Mac 平台使用效果挺好的,就决定进行更换。切换成黑色背景后更为舒适了,之前一直使用瞎眼的白色。

image-20220223001619177

FISH SETUP

我安装了 MacPorts 包管理工具,直接用 port 命令即可安装 fish

1
sudo port install fish

ADD FISH TO /etc/shells

使用 which fish 可以调出 fish 的安装路径(用 port 安装的话默认路径是 /opt/local/bin/fish ),复制该路径,然后粘贴到 /etc/shells 文件末尾。

1
vim /etc/shells

NOTE:千万不要用 sudo vim /etc/shells 这样会修改到 root 管理员角色的 shell (如果出问题就会很麻烦),用户自己折腾自己的 shell 就好了。

通过 cat /etc/shells 可以查看所有可供使用的 shell 。

CHANGE DEFAULT SHELL

使用 chsh 语句即可将 fish 设置为默认 shell 。

1
chsh -s /opt/local/bin/fish

NOTE

fish 所使用的配置文件并非 ~/.fish 而是 ~/.bash_profile ,在里面修改完内容可以 source 该文件进行立即更新。

网卡驱动安装

Macbook Air 2015年 13英寸版所使用的的网卡是 博通的 BCM94360CS2

可以从 关于本机 --> 系统报告 --> Wi-Fi 中查看到硬件固件版本。

image-20220128154108052

安装完Linux之后,暂时无法联网,需要使用有线网络进行上网,可以使用 iPhone 接 lighting-USBA 的转接线进行上网,然后使用 apt 命令下载安装 bcmwl-kernel-source

如果使用 pkgs.org 官网上下载的 bcmwl-kernel-source.dpkg 则会提示缺少很多依赖,无法直接一步安装到位。

image-20220128154312606

APFS 硬盘内容读取

https://github.com/sgan81/apfs-fuse/

REFERENCE

  1. 在linux访问macos下的分区

本博文并不提供全面的教学指导,详细手册请查阅 Git-Reference开发

Git 是一个开放源代码的版本控制系统,专用于处理分布在多个代码库上的大型项目。

Repo 是 google android 以 Git 为基础构建的代码库管理工具,依赖于 python2 脚本调用 Git,主要用来下载、管理android项目的软件仓库。

Repo 简化了跨多个代码库运行的流程,与 Git 相辅相成。Repo 可以在必要时 整合多个 Git 代码库,将相关内容上传到我们的修订版本控制系统,借助单个 Repo 命令,可以将文件从多个代码库下载到本地工作目录。使用 Repo 执行基本的跨网络操作可大大简化文件管理工作。

GIT

对git的使用会分为 基础概念部分(即相关概念介绍)、指令语法使用场景 三部分。

INTRODUCTION

DAG: Directed Acyclic Graph,有向无环图。

DVCS: Distributed Version Control System,分布式版本控制系统。

DVCS

节点:在DVCS中,每个节点代表项目的一个修订(一个版本),这些对象通常被称为提交。

有向边: 在DVCS中,每条边是基于两个修订之间的关系生成的。箭头是从父修订指向子修订的,代表他们之间的从属关系。 修订DAG中的箭头并不一定会形成闭环。通常修订的DAG是从左向右的结构(根节点在左,叶节点在右)或者自上而下结构(最新的版本在上面)。

BRANCH / TAG / REFS / HASH-CODE

分支(branch)和 标签 (tag)有时候也统称为 引用(refs),它们在修订 DAG 中的含义是样的,都是修订结构图表中的外部引用(指针)。

image-20220226205719300

上图中,包含两个分支,当前的分支 Master 和另一分支 maint 。其中 34ac2 分支有一个 v0.9 的标签,另一个 3fb00 标签是合并提交的。

标签(tag),即 给定版本的符号名称,如 v1.3-rc3其永远指向相同对象,且不会变更

这就是一个允许开发人员快速查询和浏览的信息,且该信息必须对所有人来说都是具有相同意义的。

分支(branch),即 一系列开发工作的符号名称

本地分支的引用信息都存放在 .git/refs/heads/ 路径下,如 master 分支信息存放于 .git/refs/heads/master 中,而 refs/heads/ 为该 master 的命名空间。

image-20220226211442066

目前Git在硬盘中采用了两种截然不同的方式来表示分支:松散格式压缩格式

例如master分支(该分支是Git采用的默认分支名,用户在创建新的版本库时默认的分支名就是它)。

  • 采用 松散格式 时,它是 .git/refs/heads/master 中的一行文本,其中指代分支的 内容是十六进制的SHA-1码
  • 采用 压缩格式 时,它是 .git/packed-refs 中的一行文本,使用 最顶部修订的SHA-1码分支全名一起 表示该分支。

Configuration

可以通过 git config --global user.email ""git config --global user.name "" 进行配置,相关信息存储在 ~/.gitconfig 文件中。

image-20220226203128964

Branch Name

分支 命名 说明
主分支 master 主分支,所有提供给用户使用的正式版本
开发分支 dev 开发分支,永远是功能最新最全的版本
功能分支 feature-* 新功能分支,某个功能点正在开发的分支
发布分支 release-* 发布定期要上线的功能
修复分支 bug-* 修复代码bug的分支

Master

代码库应该有且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。Git主分支的名字,默认称为 MasterMaster 主分支是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。

主分支只用来分布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做 Dev 这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在 Master 分支上,对 Dev 分支进行”合并”(merge)。

git 中的分支 本质上是个指向 commit 对象的可变指针。

而在每一个使用 git 的 .git/ 路径下,还保存着一个名为 HEAD 的特别指针,一般来说,它是指向正在工作中的本地分支的指针。

HEAD 的指向是可以改变的,比如在 commit / checkout / branch / tag / retest / restore 等操作之后。

有的时候 HEAD 会指向一个没有分支名字的修订版本,这种情况叫 detached HEAD

image-20220226203838866

在最底层实现中,Git 历史版本识别是通过一个 SHA-1 哈希码实现的,例如 2b953b4380

Git 支持多种形式的版本查询,其中就包括使用哈希码的精确匹配(最少提供4个字符)。

GIT COMMAND GRAMMER

  1. 使用 git <command> -h 可以获得该命令的所有参数用法帮助(简略的)。然后可以针对性地去搜索该指令和参数的用法。
  2. 下方会列出一些较为常见的(自己会使用得到的)命令及其参数。个别较常见的命令会贴上一张官方的参数用法图片。

git add

<file name>

使用本命令参数可以直接确认某个文件的变动记录。

git add readme.md 确认了 readme.md 文件的修改,git add . 确认了当前路径下包含的所有子路径和子文件的修改。

git blame

<file name>

本参数可以查看文件内部每一行内容的变动,例如新增一行,文件的编辑者等。

image-20220226202721363

git diff 不同的是,本命令是真的打开文件然后一行一行地给你修改和提示信息,包括每一行是谁写的

git branch

image-20220226213748945
1
2
git branch -D origin/xxxxxx
git branch -D xxxxxx

-a

使用本命令标志可以显示 本地 (local) 所有分支远程 (remote) 所有分支。 其中,带 * 的为当前所在分支。

image-20220226200020105

-d <branch name>

使用本命令标志可以删除 某个分支。

image-20220226211322680

-v

使用命令标志可以展示分支的哈希函数和commit信息,-v--verbose ,表示 "冗长的"。

例如: 下方,从 master 主分支刚创建出新分支 SWV 时,两者的哈希函数和commit信息是一样的。

image-20220226214736094

但修改了 SWV 分支的内容并确认提交之后,其哈希函数和commit信息就发生了改变。

image-20220226214912676

git checkout

image-20220226213843563

<branch>

单独使用此参数可以用于切换分支(也有新的实验性语句 switch 可以使用,但是 checkout 更稳定)或恢复工作树文件。使用方式如:

1
2
3
git checkout dev
git checkout origin/dev
git checkout xxxxxx

-b

使用本标志 + <new branch name> 可以创建一个新分支并切换至该分支,等同于 先使用 git branch <new branch name>git checkout <new branch name>

--orphan <branch name>

使用本命令标志可以创建一个与主分支没有联系的孤儿分支。

git commit

-a/-all

使用本标志指 接受被追踪文件的所有变更,可以在暂存区创建一个注释来实现操作隔离。

-m ""

本标志和参数可以支持在 "" 内实现对合并的快速注释。

git clone

使用本命令可以直接从某个库中拷贝某个路径下的 repository,如:

1
git clone git@git.islet.space:srv/led_blink.git

当然,拷贝不公开的repository时,需要确保已经将自己的 SSH-key 加入到该网站的 /home/git/.ssh/authorized_keys 文件中。公开的 repository 可以随便拷贝。

git diff

本命令可以简要地查看距离上次 commit 操作到底修改什么信息。

image-20220226202625977

git log

注意:所有的 git log 命令只会显示如下格式的 log 记录。

image-20220226202114879

-<num>

如输入 git log -2,可以查看当前项目最近两条的提交记录。

--author 或 --committer

本标志支持查看提交者信息,使用方法如:

1
git log --author=Linus

--merges 或 --no-merges

本标志支持查看已合并和提交或非合并提交的信息。

<path / file name>

本参数可以

git merge

默认情况下,Git执行 快进式合并(fast-forward merge),会直接将 Master 分支指向 Dev 分支。 使用 –no–ff 参数后,会执行正常合并,在 Master 分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法。即:

1
git merge -no-ff dev

git pull

执行本命令后,Git系统会将服务器版本库中的变更下载到本机。自动将 remote 版本和 local 版本进行合并,然后把合并后的变更提交到本机 版本库中。

git push

--set-upstream

设置本地(新)分支的远程上游分支,一些在网上可以查找到的信息如下:

  • set upstream branches to work properly
  • closely associated with remote branches
  • define the branch tracked on the remote branches (also called as remote tracking branch)
  • --set-upstream is the same as -u

在本地创建的新分支如果希望在远程版本库中添加对应的上游分支,使用此命令和标志可以使得此分支推送至远程分支的目标更加明确。

1
git push --set-upstream origin better-random

git rebase

rebase是将自己的分支重新基于别人的分支之上,即,在别人的基础上再添加什么东西。

git reset

image-20220226223239807
1
git reset --hard HEAD^

git remote

本命令可以进行如下操作:

  1. 对远程repository进行 添加(add)、删除(remove)、重命名(rename)等操作。
image-20220226223823738

git status

直接使用本命令可以查看当前路径下所有文件的修改记录。

image-20220226223119337

GIT SITUATION

远程git clone一个master分支为空的仓库时

要么整个仓库都为空,要么肯定还有分支,使用 git branch -a 查看远程仓库分支,一般如下:

1
2
3
4
5
* dev
remotes/origin/xxxxxxxxxx
remotes/origin/HEAD -> origin/dev
remotes/origin/dev
remotes/origin/master

而正好 xxxxxxxxxx 可能就是真正存放现有文件的分支,使用 git checkout origin/xxxxxxxxxx 进行分支切换。

conflict

当文件冲突时,Git系统无法自动合并它们,因为可能这些代码块不是独立的。

image-20220225214016189
image-20220225211850394

git pull 也 pull 不下来。

image-20220225212102034

REPO

repo 部分将分为 安装repo命令语法使用场景 三部分。

INSTALL REPO

repo 的安装有 自动手动 两种,类 UNIX 系统都可以通过对应的包管理工具进行安装,但如果没有手动修改源,使用效果可能也不尽如人意。

安装完毕之后还需要进行 版本验证,可能还需要 对python版本进行更改

SETUP AUTOMATICALLY

根据 google android installing repo 指示,可以从 apt 包管理库中下载和安装 repo,也可以使用 google源、清华源或其他源进行手动下载。

Debian 系 Linux 的安装:

1
2
sudo apt-get update
sudo apt-get install repo

Mac 安装:

1
sudo port install repo

1
sudo brew install repo

SETUP MANUALLY

手动安装 repo 的大概思路如下:

  1. 在用户根目录 ~ 下新建 bin 文件夹,即把 $HOME/bin~/bin 当做 repo 的执行文件路径。
  2. 将该路径加入系统变量 PATH 中,可以通过写入 ~/.profile 或对应的 shell 配置文件(如zsh的 ~/.zshrc )中并更新该配置文件来实现。
  3. 然后在该路径下载 repo的执行程序,主要是将某个 url 中(如下方所示为清华源的repo)的信息写入 ~/bin/repo 中。
  4. 为所有用户 a 附加 ~/bin/repo 的可执行 x 权限。
  5. export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo' 加入到配置文件中,使其一直生效(也可以在 terminal 中临时输入此命令,使其在设备重启前生效)。如果未写入 shell 的配置文件,则重启后该变量就会消失。

下方代码以安装了 zsh shell 的 linux 或 debian 系系统为例,可以拷贝粘贴保存至脚本以运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 手动安装repo
mkdir ~/bin
PATH=~/bin:$PATH
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo > ~/bin/repo
chmod a+x ~/bin/repo

# 将源添加到shell配置文件并使其生效
echo "export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'" >> ~/.zshrc
source ~/.zshrc

# 验证信息
repo version
echo $REPO

注意:google android用的是 https://storage.googleapis.com/git-repo-downloads/repo 的链接,但不是很方便下载,所以就更换成了清华源的。

VERIFICATION

安装完毕之后,可以对手动安装的 repo 进行版本验证,输入 repo versionecho $REPO_URL 即可,结果如下:

此处提示的 <repo not installed> 是正常的,因为在 ~ 目录下的确没有安装 repo .

image-20220226193048479

PYTHON NOT FOUND

如果出现以下提示,则说明你的系统中没有为 python 添加 软连接

1
/usr/bin/env: ‘python’: No such file or directory

解决方法

  1. 验证是否已经安装了 python2 :输入命令 python2 --version,如果提示对应的版本信息则跳到 第3步,否则进行 第2步。
  2. 下载 python2:debian 系用户输入命令 sudo apt install python2 进行安装。mac系用户输入 sudo port install python2 进行安装。
  3. /usr/bin/ 目录下为 python2 创建软链:sudo ln -s /usr/bin/python2 /usr/bin/python
  4. 再次运行 repo init 即可正常。

如,在 Kali Linux 绑定 python2python 之后,可以输入 whereis pythonpython --version 进行验证:

image-20220125143434000

REPO COMMAND GRAMMER

  1. 参考 Repo 命令参考资料
  2. 使用 repo <command> -h 可以获取相应命令的帮助。
  3. 在初始化过 repo 的路径下使用 repo help 可以获得以下信息:
image-20220226220906805

repo init

执行下方的命令会在当前目录中创建一个 .repo/ 目录并初始化 Repo 的版本控制,其中包含存放 Repo 源代码,以及从 URL(对应下方 -u 参数) 中同步下来的 Git 代码库 的某个分支 (对应下方 -b 参数)。

1
repo init -u <git@xxxx.com:xxxx/manifest> -b <oneOfTheBranch>
  • -u:即URL,指定从中检索清单代码库的网址。
  • -m:选择代码库中的清单文件。如果未选择清单名称,则默认为 default.xml
  • -b:指定修订版本,即特定的 manifest-branch,可以是 dev 分支,也可以是特定分支,拉下来之后参照 REPO SWITCH BRANCH 进行分支切换。

注意:对于所有剩余的 Repo 命令,当前的工作目录必须是 .repo/ 的父目录或该父目录的子目录。

repo branch

使用本命令可以查看所有的 目前可用的主题分支(currently available topic branches)。

repo sync

manifest 源中往外拉取分支。下方的 -j<number> 是指创建多线程进行数据资源获取,如 -j4 是指创建四线程进行数据资源获取。

1
2
3
repo sync
repo sync -j4
repo sync -j8

但,bitbucket的网络资源极差,很多情况下都拉不下来代码。

repo help

在其他地方如果没有运行过 repo init 而直接输入 repo help ,则会出现以下信息,详见 REPO INIT

image-20220226220753429

REPO SITUATION

REPO SWITCH BRANCH

前文有说过 “借助单个 Repo 命令,可以将文件从多个代码库下载到本地工作目录”,即使用repo拉下来的是多个 git repository,且这个repo是通过 .xml 文件进行管理的,也就是它自身包含了一些版本和分支切换相关的信息。

如果要对repo拉下来的东西进行分支切换,就要参考一下步骤:

  1. .repo/manifest 路径中。
  2. 使用 git branch -a 查看分支信息。
  3. 使用 git checkout <branch name> 进行分支切换。
  4. 再次使用 repo sync 命令进行同步。
image-20220225211308567

下图是通过 repo 拉下来的多个git repository 之一中的隐藏文件,可以看到这个 .git 文件被使用指向上级 .repo/ 路径下的某个 .git 文件,也就是说,需要先通过 repo 去拉取信息,再使用 git 来切换分支。

image-20220225214801501

REFERENCE

  1. Git与Repo简单入门
  2. git rebase 命令
  3. git reference
  4. 《Git高手之路》,Jakub Narebski,人民邮电出版社

本文参考 《Mac 外接显示器色彩问题》和 《为 macOS 10.15 开启 HiDPI,让 2K 显示器更舒适》两篇文章。

刚买来新的 HUAWEI Mateview 无线版,显示器分辨率为 3840*2560 ,该死的 3:2 屏幕啊,我图啥......

外接2015年的Macbook Air的MiniDP口输出准4K分辨率。

但是,但是,但是~~Air只能输出 16:916:10 的分辨率,让我搞了整整一下午来调整这个分辨率,4个小时啊。

算了,总结一下吧。

关闭SIP

Big sur 和之前版本不同,需要的操作如下:

  1. 关机
  2. 长按 Command 键 和 R 键(都不要松开),短按 开机电源键,直到出现进度条才能松开。
  3. 找到终端,然后输入 csrutil authenticated-root disable ,出现 successfully 字样就算是成功了。(之前的版本是 csrutil disable

查询设备号

  1. 先断开显示器,输入如下代码:
1
2
ioreg -l | grep "DisplayVendorID"
ioreg -l | grep "DisplayProductID"
  1. 再插上显示器,再次运行上方代码。
  2. 两次得出来的不同的设备号就可以区分哪个 VendorIDProductID 是外接显示器的。
image-20220123175000229
  1. 记下那串代码,并将外接显示器的两个ID转换成对应的十六进制数, 如 895022f6 , 2819466e2

生成配置参数

  1. 在用户目录 ,即 ~ 下,使用 DisplayVendorID-xxxxxxxx 即对应外接显示器 Vendor 号的16进制 )创建一个文件夹。
  2. 然后在对应文件夹下创建一个 使用 DisplayProductID-yyyy ( yyyy` 即对应外接显示器 Product 号的16进制 )创建一个无后缀名的文件。
  3. Scaled Resolution for your Mac 网站生成配置参数,在红色框中分别填入你外接显示器的对应参数,和你想使你的显示器所呈现的分辨率参数。
image-20220123175609151
  1. 复制右侧的 xml 代码至刚刚的 DisplayProductID-yyyy 文件中,并保存。

导入系统

在Big Sur系统下,根目录 / 是禁止写入的,需要稍微用点技巧(走点弯路)。

  1. 新建一个文件夹,将解除SIP之后的 / 挂载到某个地方,如下方所示:
1
2
mkdir ~/nvme
sudo mount -o nobrowse -t apfs /dev/disk1s5 ~/nvme/

其中,/dev/disk1s5 中的 /disk1s5 要通过 磁盘工具 各自查看自己的 设备 ,最后面的 s1 去掉不写。

image-20220123173550666
  1. 然后就可以 cd 进去查看对应目录下的文件内容,但是所有读写操作都需要加上 sudo 才可以。
1
2
cd ~/nvme/System/Library/Displays/Contents/Resources/Overrides
ls
image-20220123173806535
  1. ~/DisplayVendorID-xxxx 复制到 ~/nvme/System/Library/Displays/Contents/Resources/Overrides 里面:
1
sudo cp -r ~/DisplayVendorID-22f6 ~/nvme/System/Library/Displays/Contents/Resources/Overrides/DisplayVendorID-22f6

复制完毕之后可以再 ls 查看,就存在了。

  1. 但是这种复制只是一种暂时的,重启之后就会被镜像文件所覆盖,所以要重新 snapshot 一下。注意,这一步一定要做,否则重启就失效了。
1
sudo bless --folder ~/nvme/System/Library/Displays/Contents/Resources/Overrides --bootefi --create-snapshot