0%

项目文件初始化

C项目初始化

1
curl -s https://storage.islet.space/08_Script/project/c_init.sh | bash -s --
  1. 生成文件夹:build / src / inc / lib / doc
  2. 生成空文档:README.mdsrc/main.cCMakeLists.txt
  3. 下载代码格式化模板:.clang-format
  4. 下载git忽略文件模板:.gitignore
image-20220627212514361

环境初始化

嵌入式工程师开发环境初始化

1
curl -s https://storage.islet.space/08_Script/debian/init.sh | bash -s --
  1. 安装 zshohmyzsh ,并设置 zsh 为默认shell
  2. 安装常用工具:vim / nodejs
  3. 安装编译软件: gcc / lcov / valgrind / cmake / make / git / repo
  4. 安装交叉编译器: android-ndk-r20b / gcc-arm-none-eabi-5.4 / gcc-arm-none-eabi-10.3

python 开发环境初始化

1
curl -s https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh | bash -s --

环境部署

  1. 直接使用绝对路径,如 "D:\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\STM32_Programmer_CLI.exe",这种方法可以在 PowerShell 或 cmd 直接使用。
  2. "D:\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin 添加到 系统环境变量 中,这种方法只有 PowerShell 可以使用,cmd 还是不可以。
image-20220615134109619

使用说明

STM32_Programmer_CLI 的指令参数有多种分类,包括适用于 STM32 MCUSTM32 MPUMCU安全烧录STM32WBxx 三个部分。

下面是这个软件的官方说明:

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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
      -------------------------------------------------------------------
STM32CubeProgrammer v2.9.0
-------------------------------------------------------------------


Usage :
STM32_Programmer_CLI.exe [command_1] [Arguments_1][[command_2] [Arguments_2]...]

Generic commands:

-?, -h, --help : Show this help
-c, --connect : Establish connection to the device
<port=<PortName> : Interface identifier. ex COM1, /dev/ttyS0, usb1,
JTAG, SWD...)
USB port optional parameters:
[sn=<serialNumber>] : Serial number of the usb dfu
[PID=<Product ID>] : Product ID. ex: 0xA38F, etc, default 0xDF11
[VID=<Vendor ID>] : Vendor ID. ex: 0x0389, etc, default x0483
UART port optional parameters:
[br=<baudrate>] : Baudrate. ex: 115200, 9600, etc, default 115200
[P=<parity>] : Parity bit, value in {NONE,ODD,EVEN}, default EVEN
[db=<data_bits>] : Data bit, value in {6, 7, 8} ..., default 8
[sb=<stop_bits>] : Stop bit, value in {1, 1.5, 2} ..., default 1
[fc=<flowControl>] : Flow control
Value in {OFF,Hardware,Software} ..., default OFF
rts=<status> : low or high
dtr=<status> : low or high
Not supported for STM32MP
[noinit=noinit_bit]: Set No Init bits, value in {0,1} ..., default 0
[console] : Enter UART console mode
JTAG/SWD debug port optional parameters:
[freq=<frequency>] : Frequency in KHz. Default frequencies:
4000 SWD 9000 JTAG with STLINKv2
24000 SWD 21333 with STLINKv3
[index=<index>] : Index of the debug probe. default index 0
[sn=<serialNumber>]: Serial Number of the debug probe
[ap=<accessPort>] : Access Port index to connect to. default ap 0
[mode=<mode>] : Connection mode. Value in {UR/HOTPLUG/NORMAL/POWERDOWN}
default mode: NORMAL
[reset=<mode>] : Reset modes: SWrst/HWrst/Crst. Default mode: SWrst
Reset mode with UR connection mode is HWrst
[shared] : Enable shared mode allowing connection of two or more
instances of STM32CubeProgrammer or other debugger
to the same ST-LINK probe.
[tcpport=<Port>] : Port used for running ST-Link Server, default 7184
[LPM] : Enable debug in Low Power mode(default mode)
[dLPM] : Disable debug in Low Power mode
[getAuthID] : Get device identification (Option only for STM32U5 series)
[speed=<Reliable/fast>]: Choose flashing Reliable/fast (Option only for STM32U5 series)
SPI port optional parameters:
[br=<baudrate>] : Baudrate.
[cpha=<cpha_val>] : 1Edge or 2Edge. default 1Edge
[cpol=<cpol_val>] : low or high
[crc=<crc_val>] : enable or disable (0/1).
[crcpol=<crc_pol>] : crc polynom value.
[datasize=<size>] : 8bit/16bit
[direction=<val>] : Direction: 2LFullDuplex/2LRxOnly/1LRx/1LTx
[firstbit=<val>] : First Bit: MSB/LSB
[frameformat=<val>]: Frame Format: Motorola/TI
[mode=<val>] : Mode: master/slave
[nss=<val>] : NSS: soft/hard
[nsspulse=<val>] : NSS pulse: Pulse/NoPulse
[delay=<val>] : Delay: Delay/NoDelay, delay of few microseconds
[noinit=noinit_bit]: Set No Init bits, value in {0,1} ..., default 0
CAN port optional parameters:
[br=<rbaudrate>] : Baudrate : 125, 250, 500, 1000 Kbps, default 125
[mode=<canmode>] : CAN Mode : NORMAL, LOOPBACK..., default NORMAL
[ide=<type>] : CAN Type : STANDARD or EXTENDED, default STANDARD
[rtr=<format>] : Frame Format: DATA or REMOTE, default DATA
[fifo=<afifo>] : Msg Receive : FIFO0 or FIFO1, default FIFO0
[fm=<fmode] : Filter Mode : MASK or LIST, default MASK
[fs=<fscale>] : Filter Scale: 16 or 32, default 32
[fe=<fenable>] : Filter Activation : ENABLE or DISABLE, default ENABLE
[fbn=<fbanknb>] : Filter Bank Number : 0 to 13, default 0
[noinit=noinit_bit]: Set No Init bits, value in {0,1} ..., default 0
I2C port optional parameters:
[add=<ownadd>] : Slave address : address in hex format
[br=<sbaudrate>] : Baudrate : 100 or 400 Kbps, default 400
[sm=<smode>] : Speed Mode : STANDARD or FAST, default FAST
[am=<addmode>] : Address Mode : 7 or 10 bits, default 7
[af=<afilter>] : Analog filter : ENABLE or DISABLE, default ENABLE
[df=<dfilter>] : Digital filter : ENABLE or DISABLE, default DISABLE
[dnf=<dnfilter>] : Digital noise filter : 0 to 15, default 0
[rt=<rtime>] : Rise time : 0-1000(STANDARD), 0-300(FAST), default 0
[ft=<ftime>] : Fall time : 0-300 (STANDARD), 0-300(FAST), default 0
[noinit=noinit_bit]: Set No Init bits, value in {0,1} ..., default 0
-version, --version : Displays the tool's version
-l, --list : List all available communication interfaces
<uart> : UART interface
<usb> : USB interface
<st-link> : st-link interface
-q, --quietMode : Enable quiet mode. No progress bar displayed
-log, --log : Store the detailed output in log file
[<file_Path.log>] : Path of the log file,
default path = $HOME/.STM32Programmer/trace.log
-vb, --verbosity : Specify verbosity level
<Level> : Verbosity level, value in {1, 2, 3}
-y, --yes : Ignore confirmation prompt message

Available commands for STM32 MCU:

--skipErase : Skip sector erase before programming
-sl, --safelib : Add a segment into a firmware file (elf,bin
hex,srec) containing computed CRC values
To use only with the safety lib component
<file_path> : File path to be modified
<start_address> : Flash memory start address
<end_address> : Flash memory end address
<slice_size> : Size of data per CRC value
-ms, --mergesbsfu : Add a binary header and a sbsfu segment to an elf file
<elf_file_path> : File path to be modified
<header_file_path> : Header file path
<sbsfu_file_path> : SBSFU file path
-e, --erase : Erase memory pages/sectors devices:
Not supported for STM32MP
[all] : Erase all sectors
[<sectorsCodes>] : Erase the specified sectors identified by sectors
codes. ex: 0, 1, 2 to erase sectors 0, 1 and 2
for EEPROM : ed1 & ed2
[<[start end]>] : Erase the specified sectors starting from
start code to end code, ex: -e [5 10]
-w, --write
-d, --download : Download the content of a file into device memory
<file_path> : File path name to be downloaded: (bin, hex, srec,
elf, stm32 or tsv file)
[<address>] : Start address of download
-w64 : Write a 64-bits data into device memory
<address> : Start address of download
<64-bit_data> : 64-bit data to be downloaded
values should not be separated by space
-w32 : Write a 32-bits data into device memory
<address> : Start address of download
<32-bit_data> : 32-bit data to be downloaded
values should be separated by space
-w16 : Write a 16-bits data into device memory
<address> : Start address of download
<16-bit_data> : 16-bit data to be downloaded
values should be separated by space
-w8 : Write a 8-bits data into device memory
<address> : Start address of download
<8-bit_data> : 8-bit data to be downloaded
values should be separated by space
-v, --verify : Verify if the programming operation is achieved
successfully
-r32 : Read a 32-bit data from device memory
<address> : Read start address
<size> : Size of data
-r16 : Read a 16-bit data from device memory
<address> : Read start address
<size> : Size of data
-r8 : Read a 8-bit data from device memory
<address> : Read start address
<size> : Size of data
-rst : Reset system
-hardRst : Hardware reset
Available only with JTAG/SWD debug port
-halt : Halt core
-run : Run core
-step : Step core
Available only with JTAG/SWD debug port
-score : Get core status
Available only with JTAG/SWD debug port
-coreReg : Read/Write core registers
[<core_register>] R0/../R15/PC/LR/PSP/MSP/XPSR/APSR/IPSR/EPSR/
PRIMASK/BASEPRI/FAULTMASK/CONTROL
[core_reg=<value>] value in case of write opration
Note: multiple registers can be handled at once
Available only with JTAG/SWD debug port
-bkpt : Set a breakpoint
<address> : Breakpoint address
-clrbkpt : Clear a breakpoint
<address> : Breakpoint address
-r, --read
-u, --upload : Upload the device memory content to a .bin/.hex/.srec file
<address> : Start address of read and upload
<size> : Size of memory content to be read
<file_path> : file path with .bin/.hex/.srec extension

-el, --extload : Select a custom external memory-loader for JTAG/SWD
<file_path> : External memory-loader file path
-elbl, --extloadbl : Select a custom external memory-loader for Bootloader interface (SFIx only)
<file_path> : External memory-loader file path
-s, --start
-g, --go : Run the code at the specified address.
[<address>] : Start address
-rdu, --readunprotect: Remove memory's Read Protection by shifting the RDP
level from level 1 to level 0.

-tzenreg, --tzenregression: Remove TrustZone Protection by disabling the TZEN
from 1 to 0.

-ob, --optionbytes : This command allows the user to manipulate the device
's OptionBytes by displaying or modifying them.
[displ] : This option allows the user to display the whole set
of Option Bytes.
[OptByte=<value>] : This option allows the user to program the given
Option Byte.

-lockRDP1 : Lock RDP level 1
<first_half> : First 32-bit of password
<second_half> : Second 32-bit of password
-unlockRDP1 : Unlock RDP level 1
<first_half> : First 32-bit of password
<second_half> : Second 32-bit of password
-lockRDP2 : Lock RDP level 2
<first_half> : First 32-bit of password
<second_half> : Second 32-bit of password
-unlockRDP2 : Unlock RDP level 2
<first_half> : First 32-bit of password
<second_half> : Second 32-bit of password
-ssigfoxc : Save the chip Certificate,
supported for STM32WL devices
<file_path> : Certificate file path

-wsigfoxc : Write the Sigfox credential,
supported for STM32WL devices
<file_path> : Certificate file path (binary, header)
<address> : start address for write

-fillmemory : Fill memory with the given pattern from the chosen address.
<start_address> : Start address for write. The address 0x08000000 is used by default
[size=<value>] : Size of the data to write
[pattern=<value>] : The pattern value to write.
[dataWidth=8|16|32]: The filling data size:
8 bits is selected by default.
-blankcheck : Verifies that the STM32 Flash memory is blank.
If the Flash memory is not blank, the first address with data is highlighted in a message.
Beta commands:

-regdump : Read and dump Core and MCU registers
<file_path.log> : Log file path
[choice=<number>] : Device number from the list of compatible devices (optional), this list
is displayed if the command is performed without this optional argument
-hf : Hard fault analyzer
Helps to identify system faults that occur when the CPU
is driven into a fault condition by the application code.

Available commands for STM32 MPU:

-c, --connect : Establish connection to the device
<port=<PortName> : Interface identifer. ex COM1, /dev/ttyS0, usb1)
USB port optional parameters:
[sn=<serialNum>] : Serial number of the usb dfu
[serial] : Activate USB serial Gadget for MPU devices
UART port optional parameters:
[br=<baudrate>] : Baudrate. ex: 115200, 9600, etc, default 115200
[P=<parity>] : Parity bit, value in {NONE,ODD,EVEN}, default NONE
[db=<data_bits>] : Data bit, value in {6, 7, 8} ..., default 8
[sb=<stop_bits>] : Stop bit, value in {1, 1.5, 2} ..., default 1
[fc=<flowControl>] : Flow control (Not yet supported for STM32MP)
Value in {OFF,Hardware,Software} ..., default OFF
[noinit=noinit_bit]: Set No Init bits, value in {0,1} ..., default 0
-s, --start
-g, --go : Run the code at the specified partition ID.
[<partitionID>] : Partition ID
If not specified, last loaded partition
will be started

[<startAdress>] : Start address
If not specified, last loaded segment address
[<noack>] : No acknowledgment required
If not specified, acknowledgment will be required

-detach : Send detach command to DFU

-wb : Write blob

<blob_file_path> : Blob file path
-pmic : Program PMIC NVM

<PMIC file_path> : PMIC file_path
-gc, --getcertificate : Get the chip Certificate,
supported for devices with security features
<file_path> : Certificate file path into which the chip
certificate will be uploaded

-p, --phaseID : Display the current partition ID to be loaded

-w, --write
-d, --download : Download the content of a file into device memory
<file_path> : File path name to be downloaded: (bin, stm32 file
<partition_id> : Partition ID to be downloaded
-rp, --readPart : Upload a memory partion ID content to a .bin file
<partionID> : Partion to be read
[<offset address>] : Offset address of read and upload
<size> : Size of partion content to be read
<file_path> : Binary file path

-ssp, --ssp : Program an SSP file
<ssp_file_path> : SSP file path to be programmed, bin or ssp extension
<ssp-fw-path> : SSP signed firmware path
[hsm=0|1] : Set user option for HSM use
value in {0 (do not use HSM), 1 (use HSM)}
Default value : hsm=0
<lic_path|slot=slotID> : Path to the license file (if hsm=0)
or reader slot ID if HSM is used (hsm=1)
-tm : Force timeout
<value> : Number of milliseconds
-rst : Reset USB device

OTP structure v1 commands (for STM32MP15xx):
-otp program : This command allows the user to program SAFMEM
memory by modifying the OTP words
[wordID=<value>] : This field contains the OTP word number
[value=<value>] : Loads value into the chosen OTP word
[sha_rsl=<value>] : Loads value into the corresponding shadow read
sticky lock bit
[sha_wsl=<value>] : Loads value into the corresponding shadow write
sticky lock bit
[sl=<value>] : Loads value into the corresponding programming sticky
lock bit
[pl=<value>] : Loads value into the corresponding programming perma-
nent lock bit

-otp displ : This command allows the user to display all or parts
of the OTP structure
[upper] : Displays the loaded upper OTP shadow registers
values and status
[lower] : Displays the loaded lower OTP shadow registers
values and status
[ctrl] : Displays the loaded BSEC control registers

OTP structure v2 commands (except STM32MP15xx):
-otp displ : This command allows the user to display all or parts
of the OTP structure
[word=<id>] : {Optional} display a specific OTP registers {values and status}
Up to 96 OTP words [0 to 95], id value in hex/dec format

-otp write : This command allows to fuse or update OTP words
Up to 96 OTP words [0 to 95] at the same command line
[lock] : {Optional} indicate the operation type, update or permanent lock
[word=<id>] : This field contains the OTP word number in hex/dec format
[value=<value>] : Value to be written in hex format

-otp lock : This command allows to fuse permanently OTP words
Up to 96 OTP words [0 to 95] at the same command line
[word=<id>] : This field contains the OTP word number in hex/dec format

-otp fwrite : This command allows to program a binary file
[lock] : {Optional} indicate the operation type, update or permanent lock
<bin_path> : Binary file path, 32-bits aligned
[word=<id>] : OTP word number in hex/dec format, start word of program

MCU Secure programming specific commands:

-sfi, --sfi : Program an sfi file
[<protocol=Ptype>] : Protocol type to be used : static/live
Only static protocol is supported so far
Default value static
<file_path> : Path of sfi file to be programmed
[hsm=0|1] : Set user option for HSM use
value in {0 (do not use HSM), 1 (use HSM)}
Default value : hsm=0
<lic_path|slot=slotID> : Path to the SFI license file (if hsm=0)
or reader slot ID if HSM is used (hsm=1)
[<licMod_path>|slot=slotID]: list of the integrated SMI license files paths
if HSM is not used (hsm=0)
Or readers slot IDs list if HSM is used (hsm=1)
Used only in combined case
the list order should correspond to
modules integration order within the SFI file

-smi, --smi : Program an smi file
<protocol=Ptype> : Protocol type to be used : static/live
Only static protocol is supported so far
Default value static
<file_path> : Path of smi file to be programmed
[hsm=0|1] : Set user option for HSM use
value in {0 (do not use HSM), 1 (use HSM)}
Default value : hsm=0
[<address>] : Destination address of the smi module
needed only for relocatable SMI
<lic_path|slot=slotID> : Path to the SMI license file (if hsm=0)
or reader slot ID if HSM is used (hsm=1)

-rsse, --rsse : Set the RSSe file path,
supported for devices with security extension
<file_path> : RSSe file path

-a, --abort : This command allows the user
to clean a not properly finished process.
The currently ongoing operation will stop
and the system will return to idle state

-dsecurity : Disable the security for STM32WL

-setdefaultob : Set default Option Bytes for STM32WL

-rssgetversion, --rssgetversion : get the version of RSS
HSM related commands:
-hsmgetinfo : Read the HSM available information
[slot=<SlotID>] : Slot ID of the Smart Card Reader
Default value: slot=1 (the PC integrated SC reader)
-hsmgetcounter : Read the current value of the license counter
[slot=<SlotID>] : Slot ID of the Smart Card Reader
Default value: slot=1 (the PC integrated SC reader)
-hsmgetfwid : Read the Firmware/Module Identifier
[slot=<SlotID>] : Slot ID of the Smart Card Reader
Default value: slot=1 (the PC integrated SC reader)
-hsmgetstatus : Read the current card life-cycle state
[slot=<SlotID>] : Slot ID of the Smart Card Reader
Default value: slot=1 (the PC integrated SC reader)
-hsmgetlicense : Get a license for the current chip
if counter is not null
<file_path> : File path into which the received license
will be stored
[slot=<SlotID>] : Slot ID of the Smart Card Reader
Default value: slot=1 (the PC integrated SC reader)
[protocol=<Ptype>] : Protocol type to be used : static/live
Only static protocol is supported so far
Default value static

-hsmgetlicensefromcertifbin, -hsmglfcb : Get a license for the input certificate
if counter is not null
<certif_file_path.bin> : Input certificate file path
<license_file_path.bin> : File path into which the received license
will be stored
[slot=<SlotID>] : Slot ID of the Smart Card Reader
Default value: slot=1 (the PC integrated SC reader)
[protocol=<Ptype>] : Protocol type to be used : static/live
Only static protocol is supported so far
Default value static

STM32WBxx specific commands:

-getuid64 : Read the device UID
-fusgetstate : Read the FUS state
-fusopgetversion : Read the FUS Operator version
-antirollback : Perform the antirollback operation (Only on Bootloader interface)
-startfus : Perform the startfus operation
Firmware Upgrade commands:
-fwdelete : Delete the BLE stack firmware
-fwupgrade : Upgrade the BLE stack firmware or the FUS firmware
<file_path> : New firmware image file path
<address> : Start address of download
[firstinstall=0|1] : 1 if it is a first install otherwise 0
optional, Default value: firstinstall=0
[startstack=0|1] : 1 to start the stack after the upgrade otherwise 0
optional, Default value: startstack=1
-autoupgrade : Automatically recognize the stack address and Upgrade the BLE stack firmware
<file_path> : New firmware image file path
-v : Verify if the download operation is achieved
successfully before starting upgrade
-startwirelessstack : Start the wireless stack
Key management commands:
-authkeyupdate : Authentication key update
<file_path> : New authentication key file path.
: This is the public key generated by
: STM32TrustedPackageCreator using -sign command.
-authkeylock : Authentication key lock
: Once locked, it's no longuer possible to change it
: using authkeyupdate command
-wusrkey : Write user key
<file_path> : User key file path
<keytype=1|2|3> : User key type, values : 1, 2 or 3.
: 1 = simple key, 2 = master key, 3 = encrypted key.
Serial Wire Viewer specific commands:

-swv : Printf via SWO
<freq=<frequency>> : System Clock in MHz
<portnumber=0-31|all> : ITM port number, values : 0-31, or all for All ports
[<file_Path.log>] : Path of the SWV log file (optional),
: default path = $USER_HOME/STMicroelectronics/STM32Programmer/SWV_Log/swv.log
-RA : Start the reception of swv automatically
-startswv : Printf via SWO & Start the reception of swv automatically
Script Manager command:

-script : Start the execution of Script File
<file_Path.prg> : Path of the script file (.prg)

查看设备列表

下面语句可以查看所有连接至PC端的设备。

1
STM32_Programmer_CLI -l
设备列表

详见:

1
2
3
4
-l,     --list         : List all available communication interfaces
<uart> : UART interface
<usb> : USB interface
<st-link> : st-link interface

设备连接

可以使用多种连接方式,将ST的从设备与PC上位机进行连接,包括 USB / UART / JTAG / SWD / SPI / CAN / I2C 。

使用指令 STM32_Programmer_CLI -c port=SWD 即可自动连接至设备。

设备自动连接

内容读取

使用指令 STM32_Programmer_CLI -c port=SWD -r32 0x08000000 0x400 可以从 地址 0x08000000 起,以 32bit 格式(-r32 )读取 0x400 byte 数据。

以32bit格式读取数据

-r32 可以更换成 -r16-r8

以16bit格式读取数据
以8bit格式读取数据

内容擦除

大面积擦除(Mass erase)所有内容,一般来说烧录的时候不会去擦除全部内容,这部分应该看情况。

1
STM32_Programmer_CLI -c port=SWD -e all
擦除全部内容

详见:

1
2
3
4
5
6
7
8
-e,     --erase        : Erase memory pages/sectors devices:
Not supported for STM32MP
[all] : Erase all sectors
[<sectorsCodes>] : Erase the specified sectors identified by sectors
codes. ex: 0, 1, 2 to erase sectors 0, 1 and 2
for EEPROM : ed1 & ed2
[<[start end]>] : Erase the specified sectors starting from
start code to end code, ex: -e [5 10]

下面两张图是烧录完某 .bin 文件后,flash内部结束尾部临界处的数据。交替烧录两个大小不易的 .bin 文件,可以观察到结尾处都是正常的,并没有出现异常。

临界处数据
image-20220615130543343

程序烧录

1
STM32_Programmer_CLI -c port=SWD -d "\\wsl.localhost\kali-linux\home\liewzheng\Workspace\stm32\20220511-galileo-manifest\out\mixosense_apps\data_collection_irq\data_collection_irq.bin" 0x00 -v
  • -d 是指 文件下载,后方接 文件路径 以及 起始地址 0x00 。这个起始地址应该是 0x08000000 还是 0x00 我也还没搞清楚。

  • -v 是指 代码验证。

image-20220615123114639

详见:

1
2
3
4
-d,     --download     : Download the content of a file into device memory
<file_path> : File path name to be downloaded: (bin, hex, srec,
elf, stm32 or tsv file)
[<address>] : Start address of download

设备复位和运行

1
2
STM32_Programmer_CLI -c port=SWD -hardRst
STM32_Programmer_CLI -c port=SWD -s
image-20220615124508137

报错

端口占用报错

image-20220615121045036

下面所有的脚本都会类比 Linux Shell Script 进行。

Windows有两个命令行 shell:Command shell 和 PowerShell。 每个 shell 都是一个软件程序,它提供你与操作系统或应用程序之间的直接通信,提供一个环境来自动执行 IT 操作。

Command shell 是内置于 Windows中,用于自动执行常规任务(例如用户帐户管理或夜间备份)和批处理 (.bat) 文件。 使用Windows脚本主机,可以在命令外壳中运行更复杂的脚本。 有关详细信息,请参阅 cscript 或 wscript。 通过使用脚本可以比使用用户界面更有效地执行操作。 脚本接受命令行中提供的所有命令。

PowerShell 旨在扩展 Command shell 的功能,以运行名为 cmdlet 的 PowerShell 命令。 Cmdlet 类似于 Windows命令,但提供了更具可扩展性的脚本语言。 可以在 PowerShell 中Windows命令和 PowerShell cmdlet,但命令外壳只能运行 Windows 命令,不能运行 PowerShell cmdlet。

若要实现最可靠、最新的自动化Windows,建议使用 PowerShell 而不是 Windows 命令或 Windows 脚本主机Windows自动化。

Generic Script

注释

windows bat 批处理和 PowerShell 都可以使用 :: 作为注释头部信息。与 Linux Shell Script 和 Python 的 # 不一致(反人类罪)。

1
:: 注释

DEL& RD

del 删除命令只能删除文件,rd 可以删除文件夹

/Q 是静默删除,不提示文字。/S 除目录本身外,还将删除指定目录下的所有子目录和文件。用于删除目录树。

1
2
3
4
5
6
:: 静默删除所有文件和文件夹
del /Q %BUILD_PATH%\*.o
rd /S /Q %BUILD_PATH%\bin
rd /S /Q %BUILD_PATH%\log
rd /S /Q %BUILD_PATH%\lib
rd /S /Q %BUILD_PATH%\map

常见命令对照表

Windows Bat Windows PowerShell Linux Shell Others
help Get-Help man
cd /D Set-Location cd
cd Get-Location pwd
type Get-Content cat
rd /S
rmdir /S
Remove-Item rm -r
del
erase
Remove-Item rm
dir /b ls
dir Get-ChildItem ll
cls Clear-Host clear
doskey Set-Alias alias
md
mkdir
New-Item mkdir
move Move-Item mv
ren
rename
Rename-Item mv
copy Copy-Item cp
echo Write-Output echo

条件语句

bat 的条件语句教程:《if

PowerShell 的条件语句教程:《about_If

REFERENCE

  1. 什么是 PowerShell?
  2. Microsoft.PowerShell.Management
  3. Windows 命令

因为基本上没什么人直接使用 armcc 作为自动化编译、持续集成和交付的工具,更多是使用 UV4 软件读取 .uvprojx 文件来获取编译信息,进而驱动 armcc 进行编译过程。

通过自行编辑 Makefile 文件,使用 make 和 armcc 工具可以对代码文件进行编译,可以进行静态库的生成,也可以生成可烧录文件,但是就不要想着复杂的 make test 等测试功能了。

DIFF BETWEEN THEM

下方详细描述一下使用 arm-none-eabi-gccmakecmakearmccmake 的不同。

  1. 使用 arm-none-eabi-gccmake 编译的 SDK 过程属于 Unix Makefile 编译过程(详见 CMake 工具及官网对 CMakefiles Generators 说明)。暂时不清楚使用 armcccmake 是如何生成 Makefile 的,因为尚不了解toolchain的绑定过程,导致一致检测到的是 windows平台本地的 mingw 工具。
  2. 启动文件不同。详见 ABOUT STARTUP FILE
  3. 工具链指定方式不同。详见 TOOLCHAIN

KEIL MDK ENV

MDK SOFTPACK

MDK5 Software Packs

TOOLCHAIN DOWNLOADS

首先需要安装版本较新的 Keil MDK-ARM 开发软件。最好是在Windows上安装,方便破解。

可以从 About uVision 中查看版本信息。也可以直接在 PowerShell 中直接调用查看。

MDK 的版本为 v5.22 确实是比较老旧的,建议下载 v5.27 及以上版本。

下载链接:MDKv5.28MDKv5.37keygen

官网下载:ARM Compiler 5 Downloads

image-20220608100300944
image-20220608100748964

TOOLCHAIN

下面的一些参数可以从 ARM Compiler v5.06 for uVision armcc User Guide 获取帮助。

FROMELF

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
Options:
--help display this help screen
--vsn display version information
--output file the output file. (defaults to stdout for -text format)
--nodebug do not put debug areas in the output image
--nolinkview do not put sections in the output image

Binary Output Formats:
--bin Plain Binary
--m32 Motorola 32 bit Hex
--i32 Intel 32 bit Hex
--vhx Byte Oriented Hex format

--base addr Optionally set base address for m32,i32

Output Formats Requiring Debug Information
--fieldoffsets Assembly Language Description of Structures/Classes
--expandarrays Arrays inside and outside structures are expanded

Other Output Formats:
--elf ELF
--text Text Information

Flags for Text Information
-v verbose
-a print data addresses (For images built with debug)
-c disassemble code
-d print contents of data section
-e print exception tables
-g print debug tables
-r print relocation information
-s print symbol table
-t print string table
-y print dynamic segment contents
-z print code and data size information
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
Usage: armlink option-list input-file-list
where
option-list is a list of case-insensitive options.
input-file-list is a list of input object and library files.

General options (abbreviations shown capitalised):
--help Print this summary.
--output file Specify the name of the output file.
--via file Read further arguments from file.

Options for specifying memory map information:
--partial Generate a partially linked object.
--scatter file Create the memory map as described in file.
--ro-base n Set exec addr of region containing RO sections.
--rw-base n Set exec addr of region containing RW/ZI sections.

Options for controlling image contents:
--bestdebug Add debug information giving best debug view to image.
--datacompressor off
Do not compress RW data sections.
--no_debug Do not add debug information to image.
--entry Specify entry sections and entry point.
--libpath Specify path to find system libraries from.
--userlibpath Specify path to find user libraries from.
--no_locals Do not add local symbols to image symbol table.
--no_remove Do not remove unused sections from image.

Options for controlling image related information:
--callgraph Create a static callgraph of functions.
--feedback file Generate feedback that can be used by the compiler in file.
--info topic List misc. information about image.
Available topics: (separate multiple topics with comma)
common List common sections eliminated from the image.
debug List eliminated input debug sections.
sizes List code and data sizes for objects in image.
totals List total sizes of all objects in image.
veneers List veneers that have been generated.
unused List sections eliminated from the image.
--map Display memory map of image.
--symbols List symbols in image.
--xref List all cross-references between input sections.

--summary_stderr

ARMCC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Usage:         armcc [options] file1 file2 ... filen
Main options:

--arm Generate ARM code
--thumb Generate Thumb code
--c90 Switch to C mode (default for .c files)
--cpp Switch to C++ mode (default for .cpp files)
-O0 Minimum optimization
-O1 Restricted optimization for debugging
-O2 High optimization
-O3 Maximum optimization
-Ospace Optimize for codesize
-Otime Optimize for maximum performance
--cpu <cpu> Select CPU to generate code for
--cpu list Output a list of all the selectable CPUs
-o <file> Name the final output file of the compilation
-c Compile only, do not link
--asm Output assembly code as well as object code
-S Output assembly code instead of object code
--interleave Interleave source with disassembly (use with --asm or -S)
-E Preprocess the C source code only
-D<symbol> Define <symbol> on entry to the compiler
-g Generate tables for high-level debugging
-I<directory> Include <directory> on the #include search pat

MAKEFILE

make 可以在任一路径下执行,只需要加入 -C <path> 即可,即告诉make工具在 <path> 路径下执行编译。方便了在 windows 命令行环境下的执行。

编写 Makefile 的时候,某些变量可以通过外部传递,而且方法也很简单,甚至内部甚至不需要声明,只需要在引用处写明变量名称即可。然后外部调用时以 VARIABLE="value" 的形式进行声明即可。如下:

1
make libmxs -C 20220607 MANIFEST_PATH="F:\Workspace\20220511-galileo-manifest" TOOLCHAIN_PATH="D:\Keil_v528\ARM\ARMCC\bin"

COMPILING CONTROL STRINGS

下面这些编译参数是复制自 KEIL IDE 的,对应的平台是 STM32L4R9。

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
--c99
-c
--cpu Cortex-M4.fp
-D__MICROLIB -g -O3 --apcs=interwork --split_sections

-I ../app
-I ../M_BSP
-I ../M_BSP/BMM150
-I ../M_BSP/Sensor_R1
-I ../M_BSP/lps33k
-I ../M_BSP/lcd
-I ../M_BSP/PSRAM
-I ../M_BSP/OSPI_NOR
-I ../Driver
-I ../../../Drivers/CMSIS/Include
-I ../../../Drivers/CMSIS/Device/ST/STM32L4xx/Include
-I ../../../Drivers/STM32L4xx_HAL_Driver/Inc
-I ../../../Drivers/BSP/STM32L4R9I_EVAL
-I ../../../Drivers/BSP/Components/Common

-I./RTE/_Watch_EVAL
-IC:/Users/mixo/AppData/Local/Arm/Packs/ARM/CMSIS/5.5.1/CMSIS/Core/Include
-IDrivers/CMSIS/Device/ST/STM32L4xx/Include
-D__UVISION_VERSION="528" -D_RTE_ -DSTM32L4R9xx -DUSE_HAL_DRIVER -DUSE_GVO_390x390
-o .\Watch\*.o --omf_browse .\Watch\*.crf --depend .\Watch\*.d

TOOLCHAIN

在 Makefile 文件中,往往为了互相兼容,默认编译器的名称就是 gcc ,只是前缀不同而已,而 armcc 编译器在使用时则需清晰指定名称。

下方为 armcc 工具链的指定方式:

1
2
3
4
5
6
# 工具链
CC=$(TOOLCHAIN_PATH)\"armcc.exe"
AS=$(TOOLCHAIN_PATH)\"armasm.exe"
AR=$(TOOLCHAIN_PATH)\"armar.exe"
LINK=$(TOOLCHAIN_PATH)\"armlink.exe"
FROM_ELF=$(TOOLCHAIN_PATH)\"fromelf.exe"

下方为 arm-none-eabi-gcc 工具链的指定(由 stm32 CubeMX 生成)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PREFIX = arm-none-eabi-
# The gcc compiler bin path can be either defined in make command via GCC_PATH variable (> make GCC_PATH=xxx)
# either it can be added to the PATH environment variable.
ifdef GCC_PATH
CC = $(GCC_PATH)/$(PREFIX)gcc
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
CP = $(GCC_PATH)/$(PREFIX)objcopy
SZ = $(GCC_PATH)/$(PREFIX)size
else
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
endif
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S

COMPILING PARAMETERS

总结出常见的编译参数如下:

MACRO DEFINITIONS

宏定义参数:-DUSE_HAL_DRIVER / -DSTM32L4R9xx / -D__MICROLIB

其中,前两个还是要依具体芯片的硬件参数来看的

CPU

CPU 参数:--cpu Cortex-M4.fp / --cpu Cortex-M4 等。

使用armcc --cpu list 命令可以查看所有可接受的芯片,如下:

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
--cpu=4
--cpu=4T
--cpu=5T
--cpu=5TE
--cpu=5TEJ
--cpu=6-M
--cpu=6S-M
--cpu=7-A
--cpu=7-R
--cpu=7-M
--cpu=7E-M
--cpu=7-A.security
--cpu=ARM7EJ-S
--cpu=ARM7TDMI
--cpu=ARM720T
--cpu=ARM7TDMI-S
--cpu=ARM9TDMI
--cpu=ARM920T
--cpu=ARM922T
--cpu=ARM9E-S
--cpu=ARM926EJ-S
--cpu=ARM946E-S
--cpu=ARM966E-S
--cpu=Cortex-M0
--cpu=Cortex-M0plus
--cpu=SC000
--cpu=Cortex-M1
--cpu=Cortex-M1.os_extension
--cpu=Cortex-M1.no_os_extension
--cpu=Cortex-M3
--cpu=Cortex-M3-rev0
--cpu=SC300
--cpu=Cortex-M4
--cpu=Cortex-M4.fp.sp
--cpu=Cortex-M7
--cpu=Cortex-M7.fp.sp
--cpu=Cortex-M7.fp.dp
--cpu=Cortex-R4
--cpu=Cortex-R4F
--cpu=Cortex-A5
--cpu=Cortex-A5.vfp
--cpu=Cortex-A5.neon
--cpu=Cortex-A7
--cpu=Cortex-A7.no_neon
--cpu=Cortex-A7.no_neon.no_vfp
--cpu=Cortex-A8
--cpu=Cortex-A8.no_neon
--cpu=Cortex-A8NoNEON
--cpu=Cortex-A9
--cpu=Cortex-A9.no_neon
--cpu=Cortex-A9.no_neon.no_vfp
--cpu=Cortex-A12
--cpu=Cortex-A12.no_neon.no_vfp
--cpu=Cortex-A15
--cpu=Cortex-A15.no_neon
--cpu=Cortex-A15.no_neon.no_vfp
--cpu=Cortex-A17
--cpu=Cortex-A17.no_neon.no_vfp

ABOUT STARTUP FILE

CubeMX 针对不同的编译方式和平台生成对应的 startup file 。而使用 Unix Makefile 编译的 startup_stm32l476xx.s 和 使用 Keil MDK-ARM 的是不一样的。

startup_stm32xxxxxx.s 是使用汇编语言写好的基本程序,当STM32 芯片上电启动的时候,会执行此处的汇编程序,从而建立 C 语言的运行环境,所以称之为启动文件。

由于编译器不一致,所以 .s 的启动文件也不能互相兼容,需要使用 CubeMX重新生成。

image-20220609110616631

DEBUG

ERROR

ERROR #35: PLEASE SELECT FIRST THE TARGET STM32L4XX DEVICE USED IN YOUR APPLICATION

image-20220608105439447

没有添加宏定义,添加 -DSTM32L476xx-DUSE_HAL_DRIVER 即可。

ERROR L6050U: THE CODE SIZE OF THE IMAGE EXCEEDS THE MAXIMUM

image-20220608110734097

版权问题,当你在使用keil时,出现 error: L6050U: The code size of this image (45178 bytes) exceeds the maximum allowed for this version of the linker. 的时候,那就是 keil 没有被破解,软件本身的字节数有局限性,需要下载个MDK注册机进行破解!

Error: A1854E: Unknown opcode 'BLX', maybe wrong target CPU?

image-20220608151900214

WARNING

Warning: #1295-D: Deprecated declaration two_wired_resync - give arg types

image-20220608135309057

在传递参数为空的函数中,必须写 void ,armcc要求比较严格,arm-none-eabi 的要求比较宽松。

REFERENCE

  1. keil编译链接过程以及ARMCC、ARMASM、FROMELF、ARMLINK、ARMAR的使用
  2. ARM Compiler v5.06 for uVision armcc User Guide
  3. ARM® Compiler v5.06 for μVision®

Common Tools

软件启动:utools / alfred

小插件合集:utools

文件搜索:everything(windows)

MD:Typora(插件Pandoc)

图床:picgo

自由:V2Ray-Core / ClashX

网站视频下载:Downie4

截图:Snipaste

解压:Bandizip

思维导图:Xmind

翻译:deepl /

Engineer

BASH:zsh(Oh-my-zsh)

终端插件:fig

zsh皮肤:powerlevel10k

Python

代码编辑器:Jupyter Lab(比Notebook更好用)

Python环境管理工具:Miniconda

Python常用库:

1
pip install pandas xlrd openpyxl xlsxwriter requests lxml html5lib BeautifulSoup4 matplotlib seaborn plotly bokeh scipy statsmodels python-frontmatter opencv-python

Embedded

VS code 插件

git痕迹查看:GitLens

代码注释:Doxygen

Keil开发代替IDE:EIDE

Compiling

交叉编译:cmake / make

单元测试:valgrind lcov

Jenkins

environment Injector

Publish Over FTP

CMake

xUnit

Linux

  • rsync :同步文件。
  • tree :目录树

奇淫巧技

文本替换

object 类型文本替换

object 目前还没有找到替换方法,可以将 object 类型转换成 string 数据类型,再用 str.replace() 方法进行文本替换。

  1. .astype('string') 方法将 object 类型转换成 string 类型。
  2. 使用 .str.replace(<old-string>,<new-string>) 方法对文本进行替换。并使用 tmp 变量进行保存。
  3. tmp 变量重新赋值给原来的变量。
1
2
3
pdata['timestamp'] = pdata.timestamp.astype('string')  # 将 object 类型转换成 string类型
tmp = pdata['timestamp'].str.replace('BIN3-','')
pdata['timestamp'] = tmp

DEBUG RECORD

nonetype object is not subscriptable

大概意思就是,这个 timestampobject 数据类型,替换了之后这个不能显示了估计。

image-20220522012451872

本文是介绍嵌入式工程师的CLI编译工具部署方法,所用编译器是 ARM GCC,即 gcc-arm-none-eabi ,案例也是使用 STM32 CubeMX 生成的文件。中间涉及到一些 单元测试 相关的插件安装可以视情况选择或弃用。

下方的操作需要提前安装好 Windows子系统Linux(WSL,Windows Subsystem Linux)或者 虚拟机,且确保使用的是 shell 是 zsh,否则下方的一些配置文件(如 ~/.zshrc )需要手动改成对应 shell 的配置文件。

安装软件

image-20220507181341683

安装git

仅实例安装,配置 用户名 和 邮箱 那些的操作请自行搜索。

1
sudo apt install git

安装工具链/编译器

  1. 官网 去下载自己需要的编译器。
  2. 然后进行解压到与 repo 相同位置的 ~/bin (放其他位置也可以)。
  3. 设置系统变量

下方以 gcc-arm-none-eabi-5_4-2016q3 为例:

1
2
3
4
cd ~/Downloads
wget https://storage.islet.space/02_Softwares/06_Embedded/01_CrossCompiler/gcc-arm-none-eabi/gcc-arm-none-eabi-5_4-2016q3.tar.gz
tar -zxvf gcc-arm-none-eabi-5_4-2016q3.tar.gz
cp -r ~/Downloads/gcc-arm-none-eabi-5_4-2016q3 ~/bin/

安装编译工具

下方这些操作默认安装不指定版本的cmake和make。

1
sudo apt install cmake make

安装单元测试工具

1
sudo apt install lcov valgrind

安装repo

gitpython2repo 的依赖,记得先安装。

  1. 创建 ~/bin 路径。
  2. 选择国内源安装repo。
  3. 赋予 ~/bin/repo 执行权限。
  4. 使 REPO_URL 变成系统变量。
  5. ~/bin 放到 PATH 环境变量中。
  6. 使配置生效。。

这一步简化成下面这些操作,对照着执行即可。(千万不要直接用 apt install repo 进行安装,国内大部分情况用不了)

1
2
3
4
5
6
mkdir ~/bin
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo > ~/bin/repo
chmod a+x ~/bin/repo
echo "export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'" >> ~/.zshrc
echo "export PATH=~/bin:$PATH" >> ~/.zshrc
source ~/.zshrc

拉代码

下方仅实例 repo 工具的代码拉取指令

1
2
repo init -u git@github.com:xxx/xxxxxx.git -b dev
repo sync -j8

拉下来之后会有下面这些文件,其中 outtmp 是之后才生成的。

image-20220511154414265

编译命令

使用编写的脚本进行单元测试、编译和其他操作:

1
./sdk/build/build.sh --board stm32l476rg --compiler $ARM_GCC_PATH --debug

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数据拆分成多列并保存