Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
__pycache__
.idea
venv
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,23 @@ Goal of this project is to bring more flexible support for different MCUs, very
- allow to control breakpoints or watchpoints
- support for more ST-Link devices connected at once
- other file formats (SREC, HEX, ELF, ...)
- pip installer
- ~~pip installer~~
- proxy to GDB
- and maybe GUI
- support for ST-Link/V1 is NOT planed, use ST-Link/V2 or V2-1 instead


## Usage

```python
stlink = StlinkWrapper()
stlink\
.reset()\
.flash_erase(erase=True, verify=True, file='../firmware.bin', addr='0x8000000')\
.dispatch(verbosity=2)
```


## Install

### Requirements
Expand Down
91 changes: 0 additions & 91 deletions lib/dbg.py

This file was deleted.

3 changes: 1 addition & 2 deletions pystlink
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ if [ -L "$exec_file" ] ; then
fi

work_dir=`dirname $exec_file`

python3 $work_dir/pystlink.py $*
cd $work_dir && python3 -m stlink.pystlink $*
58 changes: 58 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import setuptools
import os
import codecs


_ABOUT = {
'APP_NAME': 'pystlink',
'VERSION': '1.0.0',
'DESCRIPTION': 'Python tool for manipulating with STM32 MCUs using ST-Link in-system programmer and debugger.',
'URL': 'https://github.com/pavelrevak/pystlink',
'AUTHOR': 'Pavel Revak',
'AUTHOR_EMAIL': '[email protected]'
}


def get_long_description():
current_dir = os.path.abspath(os.path.dirname(__file__))
readme_file = os.path.join(current_dir, 'README.md')
with codecs.open(readme_file, encoding='utf-8') as readme_file:
long_description = readme_file.read()
return long_description


setuptools.setup(
name=_ABOUT['APP_NAME'],
version=_ABOUT['VERSION'],
description=_ABOUT['DESCRIPTION'],
long_description=get_long_description(),
url=_ABOUT['URL'],
author=_ABOUT['AUTHOR'],
author_email=_ABOUT['AUTHOR_EMAIL'],
license='MIT',
keywords='SWD debugger STM32 STLINK CORTEX-M ARM',

classifiers=[
# https://pypi.python.org/pypi?%3Aaction=list_classifiers
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Topic :: Software Development :: Embedded Systems',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
],

packages=[
'stlink'
'/lib'
],

install_requires=[
'pyusb (>=1.0.2)',
],

entry_points={
'console_scripts': [
'pystlink = stlink.cmd.run_cli'
],
},
)
126 changes: 126 additions & 0 deletions stlink/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import logging
import time

from stlink.pystlink import PyStlink
from typing import Optional, List, Union, Callable
from argparse import Namespace
# from .pystlink import PyStlink

__all__ = [
'PyStlink',
'StlinkWrapper'
]


class StlinkWrapper:
def __init__(self):
self._link = PyStlink()
self._commands: List[List] = []

def _dispatch(self, actions: Optional[List[List]], verbosity: int = 1, serial: str = None, hard: bool = False,
index: int = 0, cpu: List[str] = (), no_unmount=True, no_run=True,
bar_on_update: Callable[[int, str], None] = None):
args = {
'action': [':'.join(filter(None, [] if action is None else action)) for action in actions],
'serial': serial, 'index': index, 'hard': hard, 'verbosity': verbosity, 'cpu': cpu,
'no_run': no_run, 'no_unmount': no_unmount
}
args = Namespace(**args)
self._link.start(args, bargraph_on_update=bar_on_update)
self._commands.clear()

def dispatch(self, verbosity: int = 1, serial: str = None, hard: bool = False,
index: int = 0, cpu: List[str] = (), no_unmount=True, no_run=True,
bar_on_update: Callable[[int, str], None] = None):
self._dispatch(self._commands, verbosity, serial, hard, index, cpu, no_unmount, no_run, bar_on_update)

def _add_command(self, commands: List):
self._commands.append(commands)

def reset(self, halt: bool = False):
self._add_command(['reset', 'halt'] if halt else ['reset'])
return self

def run(self):
self._add_command(['run'])
return self

def sleep(self, duration: float):
self._add_command(['sleep', f'{duration}'])
return self

def step(self):
self._add_command(['step'])
return self

def halt(self):
self._add_command(['halt'])
return self

"""
Verify flash {at address} against binary file ( or against .srec file)
"""
def flash_check(self, file: Optional[str] = None, addr: Optional[int] = None):
self._add_command(['flash', 'check', f'{file}', f'{addr}'])
return self

def flash_erase(self, erase: bool, verify: bool, file: str, addr: Optional[str] = None):
self._add_command(['flash', 'erase' if erase else None, 'verify' if verify else None, addr if addr is not None else None,
file])
return self

"""
Write binary file into memory of sram
:arg addr Use sram to write value to sram
"""
def write(self, file: str, addr: Union[str, int]):
self._add_command(['fill', f'{addr}', file])
return self

"""
Fill memory or sram with pattern
"""
def fill(self, addr: Union[str, int], size: int, pattern: str):
self._add_command(['fill', f'{addr}', None if size is None else size, f'{pattern}'])
return self

"""
Read memory/SRAM/flash into file
"""
def read(self, addr: Union[str, int], size: int, file: str):
self._add_command(['read', addr, f'{size}', file])
return self

"""
Set register or 32 bit memory
"""
def set(self, reg: str, data: str):
self._add_command(['set', reg, data])
return self

"""
Print all address value to the console
:args addr Possible values: "core", "sram", "flash", or integer value representing the address of the core register
"""
def dump(self, addr: Union[str, int], size: Optional[int]):
self._add_command(['dump', f'{addr}', size])
return self

def dump16(self, addr: int):
self._add_command(['dump16', addr])
return self

def dump8(self, addr: int):
self._add_command(['dump8', addr])
return self


if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
logging.StreamHandler.terminator = '' # Ugly trick to suppress '\n', they are manually managed by the DBG class
stlink = StlinkWrapper()
stlink\
.reset()\
.flash_erase(erase=True, verify=True, file='../firmware.bin', addr='0x8000000') \
.reset()\
.dispatch(verbosity=3, hard=True, no_run=False)
7 changes: 7 additions & 0 deletions stlink/cmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from stlink.pystlink import PyStlink


def run_cli():
pystlink = PyStlink()
pystlink.start()

File renamed without changes.
Loading