1 背景
前段時(shí)間學(xué)習(xí)了一下如何使用pyocd配合APM32F411VCTINY板在命令行下給它進(jìn)行各種騷操作,在使用一段時(shí)間后就想著:pyocd是基于python的,那是不是也可以使用python腳本+pyocd使用起來呢?
完成我們的一些重復(fù)的操作的自動化(因?yàn)槲冶容^懶),嘿嘿。想到就去做。
2 pyocd 的python api
之前有介紹pyocd的時(shí)候發(fā)現(xiàn)遺漏了pyocd的api沒有看,它還給了利用python+pyocd的一些例子(https://pyocd.io/docs/api_examples.html)。
比如下載bin文件的例子。
本文檔就對近段時(shí)間我學(xué)習(xí)到的pyocd+python,基于APM32F411TINY板的一些收獲。由于我也是初學(xué)python,里面的一些不科學(xué)的操作,也請大家指出斧正。此致感謝!
2.1 連接
首先是連接的API:
session_with_chosen_probe()
這個(gè)api主要是控制我們選擇哪個(gè)link去連接目標(biāo)芯片,可以使用link的UID去指定,比如說我這里的link UID是:00350054500000144e5448590258(注:可以在CMD命令行用:pyocd list命令查看)。
那我這里設(shè)置指定使用我的 Geehy CMSIS-DAP WinUSB的設(shè)置就是:
ConnectHelper.session_with_chosen_probe(unique_id='00350054500000144e5448590258')
2.2 程序控制
讓程序停下
target.halt()
讓程序繼續(xù)運(yùn)行
target.resume()
2.3 數(shù)據(jù)讀取
數(shù)據(jù)的讀取指令可以使用:
target.read32(address)
這個(gè)可以讀取我們MCU的flash、ram、外設(shè)寄存器等內(nèi)容。
我們也可以使用指令:
target.read_core_register("pc")
讀取我們程序的運(yùn)行到的地方。
2.4 數(shù)據(jù)寫入
數(shù)據(jù)的寫入,我們可以使用:
target.write32(address,data)
這個(gè)可以對我們MCU的fram、外設(shè)寄存器等可以直接寫入內(nèi)容的地址進(jìn)行操作。
Q:為什么不能直接對Flash進(jìn)行直接寫入?
A:因?yàn)閒lash的寫入其實(shí)是flash控制器(解鎖、控制、狀態(tài)等寄存器),去進(jìn)行的。我們通過swd的指令只能通過操作flash的控制器,從而才能對Flash進(jìn)行寫入。
3 程序設(shè)計(jì)
我這里設(shè)計(jì)了兩個(gè)程序,對學(xué)習(xí)到的知識進(jìn)行驗(yàn)證。
3.1 讀取PE5/6的狀態(tài)
這個(gè)程序我設(shè)想的是,我的APM32F411VCTINY板已經(jīng)下載了一個(gè)LED閃爍的程序,我要知道LED當(dāng)前的一個(gè)狀態(tài)。這個(gè)其實(shí)可以類比于一個(gè)黑盒子(芯片端),我們在不開盒子的情況下去獲取我們想知道的寄存器信息。
程序的基本設(shè)計(jì)流程:
1. 連接APM32F411VC,
2. 讀取GPIOE的ODATA寄存器,用于判斷PE5/PE6的高低電平。
3. 輸出寄存器內(nèi)容,PE5/PE6的狀態(tài),以及相應(yīng)的PC的內(nèi)容。
程序如下:
import time
from pyocd.core.helpers import ConnectHelper
# Replace the following string with your target device serial number
TARGET_DEVICE_SERIAL_NUMBER = '00350054500000144e5448590258'
# APM32F411 GPIOE base address and ODATA offset
GPIOE_BASE = 0x40021000
GPIOE_ODATA_OFFSET = 0x14
# Connect to the target device with the specified serial number
with ConnectHelper.session_with_chosen_probe(unique_id=TARGET_DEVICE_SERIAL_NUMBER) as session:
# Get the target object
board = session.board
target = board.target
# ensure the target device in the running state
target.resume()
# Compute the address of the ODATA register
gpioe_odata_address = GPIOE_BASE + GPIOE_ODATA_OFFSET
# Monitor PE5 and PE6 pin status
# Monitor 10 times
for i in range(10):
target.halt()
odata = target.read32(gpioe_odata_address)
pc = target.read_core_register("pc")
target.resume()
pe5 = (odata >> 5) & 0x1
pe6 = (odata >> 6) & 0x1
# Print the contents of the odata read
print("odata: %s " % bin(odata))
print(f'PE5: {"High" if pe5 else "Low"}, PE6: {"High" if pe6 else "Low"}')
# Read some registers.
print("pc: 0x%X" % pc)
print("")
# Wait 0.5 seconds
time.sleep(0.5)
程序運(yùn)行(vscode)起來得到的結(jié)果如下:
發(fā)現(xiàn)可以讀取回來PE5/PE6的狀態(tài),且可以明確知道此時(shí)PC的內(nèi)容。
3.2 解除/上鎖APM32F411的讀保護(hù)
由于我們的程序燒錄進(jìn)APM32F411后一般會對它進(jìn)行讀保護(hù)的操作,從而使得我們的程序不會被“有心人”讀取**。
通過查閱APM32F411的手冊,我們知道對其進(jìn)行上讀保護(hù)的操作的流程有:
1. 解鎖選項(xiàng)字節(jié)編程區(qū)域;
2. 對讀保護(hù)進(jìn)行操作;
3. 重載選項(xiàng)字節(jié)。(PS:重載選項(xiàng)字節(jié)會引起復(fù)位,此時(shí)我們需要重新連接SWD,才能重新讀取內(nèi)容)
下面就根據(jù)這個(gè)流程對python腳本進(jìn)行設(shè)計(jì)。
import time
from pyocd.core.helpers import ConnectHelper
# Replace the following string with your target device serial number
TARGET_DEVICE_SERIAL_NUMBER = '00350054500000144e5448590258'
# APM32F411 Option Bytes related register addresses and key values
FMC_OPTKEY = 0x40023C08
FMC_OPTCTRL = 0x40023C14
OPTCTRL_BYTE1_ADDRESS = FMC_OPTCTRL + 1 # Points to the second byte of OPTCTRL
FMC_OPT_KEY1 = 0x08192A3B
FMC_OPT_KEY2 = 0x4C5D6E7F
OB_RDP_LEVEL_1 = 0x55 # Level 1 read protection
OB_RDP_LEVEL_0 = 0xAA # No read protection
# Connect to the target MCU
with ConnectHelper.session_with_chosen_probe(unique_id=TARGET_DEVICE_SERIAL_NUMBER) as session:
target = session.board.target
# Read the current value of OPTCTRL
optctrl_value = target.read32(FMC_OPTCTRL)
rdp_level = target.read8(OPTCTRL_BYTE1_ADDRESS) # Read the second byte directly
if rdp_level != OB_RDP_LEVEL_0:
print("Target MCU is already read protected")
else:
print("Target MCU is not read protected, proceeding with read protect operation...")
# Unlock Option Bytes programming
if optctrl_value & (1 << 0):? # Check the OPTLOCK bit
target.write32(FMC_OPTKEY, FMC_OPT_KEY1)
target.write32(FMC_OPTKEY, FMC_OPT_KEY2)
optctrl_value = target.read32(FMC_OPTCTRL)
print("optctrl_value: 0x%X" % optctrl_value)
# Set the read protection level (only modify the second byte)
target.write8(OPTCTRL_BYTE1_ADDRESS, OB_RDP_LEVEL_1)
# Start the Option Bytes programming
optctrl_value = target.read32(FMC_OPTCTRL)
optctrl_value |= (1 << 1)? # Set the OPTSTRT bit
print("optctrl_value: 0x%X" % optctrl_value)
target.write32(FMC_OPTCTRL, optctrl_value)
# Wait for programming to complete and trigger a reset
while True:
optctrl_value = target.read32(FMC_OPTCTRL)
if not (optctrl_value & (1 << 1)):? # Wait for the OPTSTRT bit to be cleared
break
# Perform a hardware reset of the target MCU
session.probe.reset()
# Wait 0.5 seconds
time.sleep(0.5)
# Re-establish the connection after the reset
with ConnectHelper.session_with_chosen_probe(unique_id=TARGET_DEVICE_SERIAL_NUMBER) as new_session:
new_target = new_session.board.target
# Verify if read protection has been successfully set
new_optctrl_value = new_target.read32(FMC_OPTCTRL)
new_rdp_level = (new_optctrl_value >> 8) & 0xFF
if new_rdp_level == OB_RDP_LEVEL_1:
print("Read protect operation successful")
else:
print("Read protect operation failed")
腳本運(yùn)行結(jié)果如下:
當(dāng)然我也給大家準(zhǔn)備了解鎖的操作的腳本,腳本運(yùn)行結(jié)果如下:
-
python
+關(guān)注
關(guān)注
55文章
4766瀏覽量
84363 -
命令行
+關(guān)注
關(guān)注
0文章
77瀏覽量
10371 -
腳本
+關(guān)注
關(guān)注
1文章
385瀏覽量
14810
原文標(biāo)題:APM32芯得 EP.34 | 還可以這樣玩?APM32F411與pyocd的火花
文章出處:【微信號:geehysemi,微信公眾號:Geehy極海半導(dǎo)體】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論