Данный сайт является зеркалом сайта www.count-zero.ru

Плата STM32F411CE BlackPill + MicroPython. Быстрый старт

разделы: , дата: 24 июля 2020г.

Данная статья появилась случайно. Изначально я собирался написать что-то на тему программирования ARM Cortex®-M4 на связке ассемблера и Си. У меня уже давно валялись парочка плат с микроконтроллерами STM32F411CE и STMF407ZET6 и я уже было предвкушал, как вскоре запущу отладчик, но когда начал изучать и по ходу дела тут же описывать плату WeACT STM32F411CE, то сначала я наткнулся на DFU загрузчик, потом я изучал программу STM32CubeProgrammer, ну и в довершении, я обнаружил, что для платы существует прошивка MicroPython. Я не отбросил прошивку сразу в сторону, прежде всего мне хотелось составить свое мнение о микропитоне, и в конечном итоге я решил посвятить статью полностью этой теме.

Статья не претендует на всеобъемлющее руководство, это скорее краткий мануал для беглого ознакомления с системой MicroPython и навыками работы с платой WeACT STM32F411CE. Я не берусь сказать, будет ли у статьи продолжение. Микропитон мне понравился, но я считаю, что для того чтобы работать с ним серьезно, нужно освоить процесс добавления своих нативных модулей в прошивку. Т.е. драйвер дисплея лучше писать все-таки не на микропитоне, а на Си или ассемблере. В то время как основную логику программы можно писать и на MicroPython.

Полезная документация по теме статьи:

  1. STM32F411xC/E Reference Manual (RM0383)
  2. User Manual "STM32CubeProgrammer software description" (UM2237)
  3. Аккаунт компании WeACT на github'e с документацией на плату MiniF4-STM32F4x1
  4. Документация по MicroPython

Содержание:

I. Обзор платы STM32F411CE (Black Pill V2.0)

  1. Плата WeACT с чипом STM32F411CEU6 ака "Black Pill V2.0"
  2. Использование флешера STM32CubeProgrammer
  3. Прошивка MicroPython
  4. Загрузка программы Blink.py для микропитона, и основы работы с REPL
  5. Установка SPI флешки на плату WeACT STM32F411CEU6
  6. Сборка микропитона из исходников

II. Основы работы c MicroPython

  1. Работа с MicroPython в интерактивной системе REPL
  2. Использование редакторов VS Code и Atom в качестве IDE для MicroPython

1) Плата WeACT с чипом STM32F411CEU6 ака "Black Pill V2.0"

В прошлом году на али появились недорогие платы в форм-факторе "Blue Pill", с чипам STM32F401CC и STM32F411CE. Это чипы с ядрами ARM Cortex®-M4, которые имеют аппаратную поддержку чисел с плавающей запятой одинарной точности и блоком DSP инструкций. На данный момент это самые доступные платы для тех, кто хочет оценить возможности микроконтролеров на ядрах ARM Cortex®-M4. Чип STM32F401CC имеет 256 КБайт флеш-памяти, 64 КБайт ОЗУ и может работать на частотах до 84 МГц включительно. Чип STM32F411CE имеет 512 КБайт флеш-памяти, 128 КБайт ОЗУ и он может работать на частоте до 100 МГц.

Сперва хочется сказать пару слов о чипе STM32F411CE. Если посмотреть на ключевые особенности (key features) данного микроконтроллера, что заявленны на сайте ST https://www.st.com/en/microcontrollers-microprocessors/stm32f411ce.html, то увидим следующее:

Arm® 32-bit Cortex®-M4 CPU with FPU, Adaptive real-time accelerator (ART Accelerator™) allowing 0-wait state execution from Flash memory, frequency up to 100 MHz

Здесь нам обещаются рабочие частоты до 100МГц включительно, некий ART-ускоритель (проприетарная технология ST), который позволит работать с флеш-памятью на частоте CPU, т.е. с "0-wait state" задержкой (читай "без задержки"). Ранее, я уже указывал на то, что флеш-память микроконтроллеров STM работает на меньшей частоте нежели ЦПУ и это существенно сказывается на работу небольших циклов. Если при линейном, т.е. последовательном выполнении программы, данное обстоятельство компенсируется двумя (для i-code bus и d-code bus) 128-битными буферами, то в случае ветвления конвейер сбрасывается, и процессору приходится ожидать флеш-память. В случае 0-wait state задержки ничего бы ждать не приходилось. Однако давайте заглянем в документацию на чип: STM32F411xC/E Reference Manual (RM0383):

Как видно, при частоте ЦПУ в 100 МГц и питании чипа 3.3 Вольт, нам придется работать с 3-wait state задержкой. Формально, вы конечно можете работать с 0-wait state задержкой на частотах порядка 16МГц или даже 24МГц.

Теперь поглядим на схему тактирования микроконтролера:

STM32F411CE имеет три коэффициента PLL: M, N и P. При тактировании от кварца 25МГц, если мы хотим использовать USB, через коэффициенты M, N и P мы можем выставить максимальную частоту ЦПУ не более 96 МГц.

Перейдем непосредственно к платам. Я заказывал свою плату с чипом STM32F411CEU6 пошлой осенью. Выглядит она так:

Вид снизу:

Заметьте, что снизу моей платы стоит номер версии платы - v1.3. Сейчас уже продаются более новые версии плат. На данный момент актуальная ревизия - это V3.0. Наиболее полную информацию об этой плате, так и о многих других, что продаются на ebay и ali, можно посмотреть на вики проекта STM32Duino , в разделе "Generic STM32F4 boards". Там эти платы обозначены как "WeAct Black Pill V2.0", и про них там имеется отдельная страничка WeAct Black Pill V2.0. На фотографиях там версия платы уже V2.0.

У одного из продавцов на али я нашел Changelog версий плат:

Платы версий начиная с 1.3 и выше, имеют три кнопки "BOOT", "FLASH" и "KEY". Один светодиод на питании, и один на выводе PC13. Два кварца, один на 25 MHz для HSE, и часовой кварц на LSE. Разъем для отладчика ST-Link V2 и разъем USB-C. На обратной стороне платы имеются контактные площадки для впайки SPI-флешки.

У компании WeAct имеется аккаунт на github: https://github.com/WeActTC/MiniF4-STM32F4x1, там также выложен Changelog, правда местами с китайскими иероглифами. Еще там есть ссылка на их официальный магазин на али, но если почитать отзывы в том магазине, то народ жалуется, что им присылают подделки. Как бы это не было печально, но факт. Также у них там написано, что платы с чипом STM32F401CC, т.е. что с 256KБ флеш-памяти, признаны устаревшими, и заменены платами с чипом STM32F401CE, т.е. с 512KБ флеш-памяти. Я правда не нашел таких плат на али, но имейте ввиду, если собираетесь использовать на плате микропитон, прошивка с ним может не влезть на чипы с 256KБ флеш-памяти.

В разделе документации, имеется распиновка плат:

Еще:

Там у них есть еще разные картинки, не поленитесь, зайдите и посмотрите.

В целом, я считаю, что преимущества платы с 411-м чипом - это невысокая цена (можно взять сразу десять), миниатюрность (большинство отладочных плат на чипах STM32F4xx выглядят монструозно), огромный объем флеш-памяти и приемлемый объем оперативной памяти (128 килобайт). Кроме того, на плате имеется посадочное место под флешку.

2) Использование флешера STM32CubeProgrammer

Существенным отличием от BluePill, явлется встроенный в чипы STM32F401/STM32F411 DFU-загрузчик. Это означает, что вы сможете прошить свой микроконтролер без программатора, прямо через USB-шнурок. В случае с BluePill, для прошивки платы через USB, сначала нужно было ставить DFU-загрузчик от STM32Duino, а затем наблюдать, как после каждой загрузки вашей программы, USB-порт постоянно переключался c устройства Virtual Serial Port на DFU-загрузчик и обратно. Кроме того, тот загрузчик элементарно затирался, стоило только прошить что-либо через ST-Link. Теперь все эти траблы остались в прошлом. DFU-загрузчик прошит в ПЗУ и останется там при любых условиях. Активируется он только специальной комбинацией нажатия кнопок, а не автоматически при каждом старте как было у Blue Pill.

Через DFU загрузчик STM32F411 можно не только загрузить свою прошивку, но и выставить нужные Option Bytes, прочитать флеш-память микроконтролера и т.д. Т.е. через USB-шнурок вы теперь можете сделать все то, что раньше было доступно только из флешера "STM32 ST-Link Utility", который требовал наличия программатора.

Для работы с DFU-загрузчиком микроконтролера нам понадобится флешер STM32CubeProgrammer. Данный флешер по функционалу примерно соответствует STM32 ST-Link Utility, но при этом работает в разных операционных системах, включая Linux.

На официальном сайте ST имеется книжка с документацией к программе: User Manual "STM32CubeProgrammer software description" (UM2237) . Настоятельно рекомендую полистать ее. Она кратенькая (80 страниц), и довольно доходчивая.

Скачать STM32CubeProgrammer можно также с официального сайта ST - https://www.st.com/en/development-tools/stm32cubeprog.html. После распаковки скачанного архива, перед нами появятся следующие файлы:

SetupSTM32CubeProgrammer-2.4.0.app
SetupSTM32CubeProgrammer-2.4.0.exe
SetupSTM32CubeProgrammer-2.4.0.linux

Здесь перед нами два инсталлятора, для Windows и для Linux. Но если посмотреть на линуксовый инсталлятор:

$ file ./SetupSTM32CubeProgrammer-2.4.0.linux 
./SetupSTM32CubeProgrammer-2.4.0.linux: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linke
d, BuildID[sha1]=367c34dc0ca47b641c12116aaed8e2c741a6bae6, not stripped

то видно, что он является 32-битным приложением и будет работать только в 32-битных или мультибиблиотечных системах. Т.к. у меня теперь чистая 64-битная Slackware, то я по старинке воспользуюсь Windows инсталлятором:

$ java -jar  ./SetupSTM32CubeProgrammer-2.4.0.exe

По умолчанию программа установится в директорию /usr/local/STMicroelectronics.

Программа STM32CubeProgrammer существует в двух ипостасях: в виде графической тулзы, и в виде утилиты с консольным, ака CLI-интерфейсом. Это означает, что теоретически мы можем использовать STM32CubeProgrammer_CLI в качестве флешера для его вызова из Makefile.

Графическая тулза выглядит как-то так:

Интерфейс консольной программы - это довольно длинный список опций, я спрятал его под спойлер:

      -------------------------------------------------------------------
                        STM32CubeProgrammer v2.4.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...)
    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
                          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}
                          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
    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}

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
     [<[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
 -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 32-bits data into device memory
     <address>          : Start address of download
     <16-bit_data>      : 32-bit data to be downloaded
                          values should be separated by space
 -w8                    : Write a 32-bits data into device memory
     <address>          : Start address of download
     <8-bit_data>       : 32-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
 -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
     <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.

Available commands for STM32 MPU:

 -c,     --connect      : Establish connection to the device
     <port=<PortName>   : Interface identifer. ex COM1, /dev/ttyS0, usb1)
    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

 -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

 -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
     <file_path>              : Path of sfi file to be programmed
     <atf-ssp-path>           : Path of atf specific to SSP
     [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)
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

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 recieved 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
  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
      -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.

Хотя список опций CLI интерфейса довольно внушительный, нас касаться будет только та часть, что входит в раздел STM32 MCU.

Теперь подключим плату через USB-C шнурок к компьютеру. На github'е нам предлагается следующие способы для активации DFU (Device Firmware Upgrade) загрузчика:

    How to enter ISP mode
  1. Method 1: When the power is on, press the BOOT0 key and the reset key, then release the reset key, and release the BOOT0 key after 0.5 seconds
  2. Method 2: When the power is off, hold down the BOOT0 key, and release the BOOT0 at 0.5s after the power is on
  3. DFU Mode: Use the data line to connect to the computer. If there is an unrecognized problem, you can heat the chip appropriately (25°C) and then re-enter the ISP mode
  4. Serial Port Mode: Connect PA9 and PA10 of core board with USB serial port
  5. Soft: STM32CubeProg

Нам предлагается два варианта. Второй вариант реализуется через отключение микроконтроллера от компьютера, он нам не подходит, т.к. это неудобно.

Если следовать первому варианту (метод 1), то сначала следует зажать кнопки BOOT0 и RESET, затем сперва отпустить кнопку RESET и через полсекунды так же отпустить кнопку BOOT0. Если все было сделано правильно, то в dmesg появится соответствующий лог:

[16081.551492] usb 4-1: new full-speed USB device number 3 using ohci-pci
[16081.763546] usb 4-1: New USB device found, idVendor=0483, idProduct=df11, bcdDevice=22.00
[16081.763554] usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[16081.763558] usb 4-1: Product: STM32  BOOTLOADER
[16081.763562] usb 4-1: Manufacturer: STMicroelectronics
[16081.763565] usb 4-1: SerialNumber: 389C37823039

У меня в микроконтроллер была залита простая мигалка (заливал сам). Когда я зажимал кнопки BOOT0 и RESET, и входил в режим загрузчика, то светодиод на плате переставал мигать. Чтобы выйти из режима загрузчика, достаточно начать RESET, светодиод снова начнет мигать (если вы не снесете прошивку в процессе, конечно).

Чтобы начать работать с DFU-загрузчиком в Linux, нужно сначала установить udev-правила. В каталоге с программой имеются несколько правил:

nostromo:~: ls -l /usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/Drivers/rules/
итого 28
-rw-r--r-- 1 flanker users 215 апр 11  2019 49-stlinkv1.rules
-rw-r--r-- 1 flanker users 604 авг  7  2019 49-stlinkv2-1.rules
-rw-r--r-- 1 flanker users 480 авг  7  2019 49-stlinkv2.rules
-rw-r--r-- 1 flanker users 862 авг  7  2019 49-stlinkv3.rules
-rw-r--r-- 1 flanker users  97 авг  7  2019 50-usb-conf.rules
-rw-r--r-- 1 flanker users 217 авг  7  2019 Readme.txt
-rw-r--r-- 1 flanker users   5 авг  7  2019 version.txt

Правила для ST-Link у меня уже есть, а вот правило 50-usb-conf.rules потребуется установить. Посмотрим, что там:

$ cat  /usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/Drivers/rules/50-usb-conf.rules 
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", GROUP="users", MODE="0666"

Номера idVendor и idProduct соответствуют аналогичным номерам из нашего лога dmesg, следовательно - это то самое правило, которое нам и нужно. Копируем это udev-правило в /etc/udev/rules.d/ и перезагружаем правила командой из под рута:

# devadm control --reload-rules && udevadm trigger

Подключаем плату вновь (или перезагружаем если уже подключен) к компьютеру и заново активируем режим DFU-загрузчика.

Переключаемся на графическую тулзу. В вертикальной правой панели нужно будет нажать на кнопку обновления:

После этого там должен появиться порт USB1. После этого в выпадающем списке нужно выбрать тип соединения USB, и нажать на расположенную рядом желтую кнопку "Connect". Если все пройдет удачно, то у вас должно рабочее окно программы выглядеть как-то так:

Далее сориентироваться думаю не составит труда. Через программу можно загрузить прошивку в микроконтроллер, изменить Option Bytes (вкладка [OB]), посмотреть содержимое флеш-памяти в режиме дамп-вьювера. Ластик внизу позволяет очистить флеш от прошивки. Вкладка CPU доступна только при подключении через ST-Link. Хочу обратить внимание на переключение "Verbosity Level". Переключая его уровни можно регулировать уровень отладочной информации в окне лога.

Теперь вернемся к версии флешера с консольным интерфейсом. Запустим его с параметром "-l":

$ /usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI -l
      -------------------------------------------------------------------
                        STM32CubeProgrammer v2.4.0                  
      -------------------------------------------------------------------

=====  DFU Interface   =====

Total number of available STM32 device in DFU mode: 1

  Device Index           : USB1
  USB Bus Number         : 004
  USB Address Number     : 001
  Product ID             : STM32  BOOTLOADER
  Serial number          : 389C37823039
  Firmware version       : 0x011a
  Device ID              : 0x0431

===== STLink Interface =====
Error: No ST-Link detected!

=====  UART Interface  =====

Total number of serial ports available: 1

Port: ttyS0
Location: /dev/ttyS0
Description: N/A
Manufacturer: N/A

Нам вывело наш подключенный через DFU микроконтроллер. Попробуем к нему подключиться:

$ /usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI -c port=usb1
      -------------------------------------------------------------------
                        STM32CubeProgrammer v2.4.0                  
      -------------------------------------------------------------------



USB speed   : Full Speed (12MBit/s)
Manuf. ID   : STMicroelectronics
Product ID  : STM32  BOOTLOADER
SN          : 389C37823039
FW version  : 0x011a
Device ID   : 0x0431
Device name : STM32F411xC/E
Flash size  : 512 KBytes (default)
Device type : MCU
Device CPU  : Cortex-M4

Через опцию -vb можно регулировать уровнем отладочной информации. Например, с опцией -vb 2 та же команда выдаст такой лог:

$ /usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI -vb 2 -c port=usb1
      -------------------------------------------------------------------
                        STM32CubeProgrammer v2.4.0                  
      -------------------------------------------------------------------



USB speed   : Full Speed (12MBit/s)
Manuf. ID   : STMicroelectronics
Product ID  : STM32  BOOTLOADER
SN          : 389C37823039
FW version  : 0x011a
Device ID   : 0x0431
 
   AREA NAME          SECT.NBR        ADDRESS         SIZE         TYPE
 
 
   Internal Flash       0000          0x08000000      0016 KB      REW
                        0001          0x08004000      0016 KB      REW
                        0002          0x08008000      0016 KB      REW
                        0003          0x0800c000      0016 KB      REW
                        0004          0x08010000      0064 KB      REW
                        0005          0x08020000      0128 KB      REW
                        0006          0x08040000      0128 KB      REW
                        0007          0x08060000      0128 KB      REW

   Option Bytes         0000          0x1fffc000      0016 B       RW

   OTP Memory           0000          0x1fff7800      0512 B       RW
                        0001          0x1fff7a00      0016 B       RW

   Device Feature       0000          0xffff0000      0004 B       RW

sending an abort request
setting the address pointer to address: 0x08000000
sending an abort request
setting the address pointer to address: 0x08000000
setting the address pointer to address: 0x1fffc008
receiving packet nbr: 0
sending an abort request
UpLoading data
Database: Config 0 is active.
sending an abort request
setting the address pointer to address: 0x08000000
setting the address pointer to address: 0x1fffc008
receiving packet nbr: 0
sending an abort request
UpLoading data
Database: Config 0 is active.
Device name : STM32F411xC/E
Flash size  : 512 KBytes (default)
Device type : MCU
Device CPU  : Cortex-M4

Однако, когда я пытался подключиться к микроконтроллеру через ST-Link, то получил сообщение об ошибке, говорящее, что прошивка моего ST-Link устарела и не совместима с программой STM32CubeProgrammer:

$ /usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI -l
      -------------------------------------------------------------------
                        STM32CubeProgrammer v2.4.0                  
      -------------------------------------------------------------------

=====  DFU Interface   =====

No STM32 device in DFU mode connected

===== STLink Interface =====
Error: Old ST-LINK firmware version. Upgrade ST-LINK firmware

-------- Connected ST-LINK Probes List --------

ST-Link Probe 0 :
   ST-LINK SN  : 56FF6E064966504930072087
   ST-LINK FW  : V2J27S6
-----------------------------------------------

=====  UART Interface  =====

Total number of serial ports available: 1

Port: ttyS0
Location: /dev/ttyS0
Description: N/A
Manufacturer: N/A

В сети можно найти видеоролики о том, как через графическую тулзу прошивка ST-Link обновляется прямо тут же в STM32CubeProgrammer. Но видимо в Linux это не работает так просто. При попытке обновления прошивки из STM32CubeProgrammer я получил еще одно сообщение об ошибке:

Теперь уже мной ST-Link не в DFU-режиме! Опять пришлось все делать по старинке, т.е. скачать c сайта st.com утилиту "ST-Link Firmware Upgrade" и обновить прошивку из виртуалки:

Последний раз я обновлял прошивку ST-Link в 2016 году, тогда это была версия V2.J27.S6. Также как и тогда, обновление прошивки из виртуалки под Linux прошло успешно.

После этого, препятствия для подключения микроконтроллера к флешеру исчезнут:

Если в микроконтроллере имеется прошивка без поддержки отладки, как например в прошивке микропитона, то перед подключением к программе STM32CubeProgrammer (т.е. перед нажатием кнопки "Connect" в графической тулзе или перед нажатием Enter при вводе команды в случае использования консольной утилиты) необходимо будет будет нажать и отпустить кнопку Reset на плате, и примерно через полсекунды после отпускания кнопки подключаться программой. Второй способ заключается опять же в поочередном нажимании кнопок Reset и Boot.

В целом, каких-то преимуществ, по сравнению с подключением через USB-шнурок, в использовании ST-Link я не увидел.

Флеш-память микроконтроллера STM32F411CEU6 разбита на следующие сектора:

Очистка флеш-памяти через CLI-интерфейс осуществляется с помощью ключа "-e". Этот ключ с параметром "all" будет очищать все сектора. Но мы можем выбрать определенные сектора для очистки, в этом случае следует указать их через запятую. Или же мы можем задать диапазон секторов, для этого их номера достаточно будет разделить пробелом. В случае если после "-e" будет стоять одно число, то будет очищен соответствующий сектор:

3) Прошивка MicroPython

Компания WeAct предлагает свои платы как бюджетную платформу для MircoPython. Оригинальная pyboard v1.1 c чипом STM32F405 стоит прилично - 33.9 фунтов + доставка.

Готовую прошивку с микропитоном для микроконтроллера STM32F411CE можно скачать с гитхаба компании WeACT https://github.com/WeActTC/MiniF4-STM32F4x1/tree/master/SDK/STM32F411CEU6/MicroPython:

Там есть прошивки для плат с различными флешками. Внешняя флешка позволит вам размешать ваш код на микропитоне непосредственно на этой самой флешке и быть т.о. по-сути не ограниченным в свободном пространстве. В прошивном случае вам придется довольствоваться 45 килобайтами под собственный код, т.к. остальное место будет занимать микропитон. Т.к. на нашей плате пока никакой флешки не установлено, нам нужна будет прошивка для "internal_rom":

Скачиваем raw-файл с прошивкой:

$ wget https://raw.githubusercontent.com/WeActTC/MiniF4-STM32F4x1/master/SDK/STM32F411CEU6/MicroPython/firmw
are_internal_rom_stm32f411_v1.12-540.hex

Проверяем:

$ arm-none-eabi-size ./firmware_internal_rom_stm32f411_v1.12-540.hex 
   text    data     bss     dec     hex filename
      0  322404       0  322404   4eb64 ./firmware_internal_rom_stm32f411_v1.12-540.hex

Прошивка весит ~320КБ. Прошиваем с помощью команды:

$ STM32_Programmer_CLI -c port=swd -w /tmp/firmware_internal_rom_stm32f411_v1.12-540.hex

Отстыковываем ST-Link, если прошивка проводилась через него, а не через USB-шнурок, и подключаем плату к USB. В dmesg пойдет такой лог:

[32070.940976] usb 4-2: New USB device found, idVendor=f055, idProduct=9800, bcdDevice= 2.00
[32070.940983] usb 4-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[32070.940988] usb 4-2: Product: Pyboard Virtual Comm Port in FS Mode
[32070.940991] usb 4-2: Manufacturer: MicroPython
[32070.940994] usb 4-2: SerialNumber: 389C37823039
[32071.352314] usb-storage 4-2:1.0: USB Mass Storage device detected
[32071.353469] scsi host8: usb-storage 4-2:1.0
[32071.353558] usbcore: registered new interface driver usb-storage
[32071.359817] cdc_acm 4-2:1.1: ttyACM0: USB ACM device
[32071.361341] usbcore: registered new interface driver cdc_acm
[32071.361344] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[32071.368434] usbcore: registered new interface driver uas
[32072.363046] scsi 8:0:0:0: Direct-Access     MicroPy  pyboard Flash    1.00 PQ: 0 ANSI: 2
[32072.374982] sd 8:0:0:0: [sdb] 384 512-byte logical blocks: (197 kB/192 KiB)
[32072.382022] sd 8:0:0:0: [sdb] Write Protect is off
[32072.382029] sd 8:0:0:0: [sdb] Mode Sense: 03 00 00 00
[32072.389035] sd 8:0:0:0: [sdb] No Caching mode page found
[32072.389042] sd 8:0:0:0: [sdb] Assuming drive cache: write through
[32072.443023]  sdb: sdb1
[32072.494018] sd 8:0:0:0: [sdb] Attached SCSI removable disk

Здесь мы видим подключение двух устройств, последовательного порта и флеш-носителя. Монтируем флеш-носитель и смотрим его характеристики и содержимое:

$ df  -h /mnt/tmp
Файловая система Размер Использовано  Дост Использовано% Cмонтировано в
/dev/sdb1           47K         5,0K   42K           11% /mnt/tmp

$ ls -l  /mnt/tmp
итого 5
-rwxr-xr-x 1 root root  528 янв  1  2015 README.txt
-rwxr-xr-x 1 root root  366 янв  1  2015 boot.py
-rwxr-xr-x 1 root root   34 янв  1  2015 main.py
-rwxr-xr-x 1 root root 2999 янв  1  2015 pybcdc.inf

Здесь файл с расширением INF - это драйвер последовательно порта для Windows, его можно сразу стереть, освободится место. README.txt это хелло-файл:

This is a MicroPython board

You can get started right away by writing your Python code in 'main.py'.

For a serial prompt:
 - Windows: you need to go to 'Device manager', right click on the unknown device,
   then update the driver software, using the 'pybcdc.inf' file found on this drive.
   Then use a terminal program like Hyperterminal or putty.
 - Mac OS X: use the command: screen /dev/tty.usbmodem*
 - Linux: use the command: screen /dev/ttyACM0

Please visit http://micropython.org/help/ for further help.

Его тоже можно стереть.

boot.py - это надо думать автозагузочный файл:

# boot.py -- run on boot-up
# can run arbitrary Python, but best to keep it minimal

import machine
import pyb
pyb.country('US') # ISO 3166-1 Alpha-2 code, eg US, GB, DE, AU
#pyb.main('main.py') # main script to run after this one
#pyb.usb_mode('VCP+MSC') # act as a serial and a storage device
#pyb.usb_mode('VCP+HID') # act as a serial device and a mouse

И main.py это пустой файл для вашего кода:

# main.py -- put your code here!

Напоминаю, что для своего кода у вас есть всего 45 килобайта.

4) Загрузка программы Blink.py для микропитона, и основы работы с REPL

На github'е компании WeAct имеется парочка примеров для микропитона: https://github.com/WeActTC/MiniF4-STM32F4x1/tree/master/SDK/MicroPython_Example. Нас будет интересовать содержимое Blink.py:

import pyb, micropython

micropython.alloc_emergency_exception_buf(100)

class Foo(object):
    def __init__(self, timer, led):
        self.led = led
        timer.callback(self.cb)
    def cb(self, tim):
        self.led.toggle()

blue = Foo(pyb.Timer(1, freq=2), pyb.LED(1)) # LED(1) -> PC13

Размещаем эту программу в main.py, размонтируем флешку, жмем на кнопку Reset на плате, и наслаждаемся миганием синего светодиода.

Через последовательный порт у нас также есть доступ к режиму интерпретатора REPL. Там мы вручную сможем помигать светодиодом, но для этого сначала придется отчистить main.py от размещенного там ранее кода, и перезагрузить микроконтроллер, т.к. способа остановить через REPL запущенную из флешки программу, я пока не нашел. Итак входим к REPL с помощью команды:

$ screen /dev/ttyACM0

Нас должно встретить сообщение навроде этого:

MicroPython v1.12-540-gdd65eb920-dirty on 2020-07-05; WeAct_Core with STM32F411CE
Type "help()" for more information.
>>>

Словечко dirty в версии прошивки мне заранее не нравится ;) Вводим команду help() для вывода справки, и получаем следующий вывод:

Welcome to MicroPython!

For online help please visit http://micropython.org/help/.

Quick overview of commands for the board:
  pyb.info()    -- print some general information
  pyb.delay(n)  -- wait for n milliseconds
  pyb.millis()  -- get number of milliseconds since hard reset
  pyb.Switch()  -- create a switch object
                   Switch methods: (), callback(f)
  pyb.LED(n)    -- create an LED object for LED n (n=1,2,3,4)
                   LED methods: on(), off(), toggle(), intensity()
  pyb.Pin(pin)  -- get a pin, eg pyb.Pin('X1')
  pyb.Pin(pin, m, [p]) -- get a pin and configure it for IO mode m, pull mode p
                   Pin methods: init(..), value([v]), high(), low()
  pyb.ExtInt(pin, m, p, callback) -- create an external interrupt object
  pyb.ADC(pin)  -- make an analog object from a pin
                   ADC methods: read(), read_timed(buf, freq)
  pyb.DAC(port) -- make a DAC object
                   DAC methods: triangle(freq), write(n), write_timed(buf, freq)
  pyb.RTC()     -- make an RTC object; methods: datetime([val])
  pyb.rng()     -- get a 30-bit hardware random number
  pyb.Servo(n)  -- create Servo object for servo n (n=1,2,3,4)
                   Servo methods: calibration(..), angle([x, [t]]), speed([x, [t]])
  pyb.Accel()   -- create an Accelerometer object
                   Accelerometer methods: x(), y(), z(), tilt(), filtered_xyz()

Pins are numbered X1-X12, X17-X22, Y1-Y12, or by their MCU name
Pin IO modes are: pyb.Pin.IN, pyb.Pin.OUT_PP, pyb.Pin.OUT_OD
Pin pull modes are: pyb.Pin.PULL_NONE, pyb.Pin.PULL_UP, pyb.Pin.PULL_DOWN
Additional serial bus objects: pyb.I2C(n), pyb.SPI(n), pyb.UART(n)

Control commands:
  CTRL-A        -- on a blank line, enter raw REPL mode
  CTRL-B        -- on a blank line, enter normal REPL mode
  CTRL-C        -- interrupt a running program
  CTRL-D        -- on a blank line, do a soft reset of the board
  CTRL-E        -- on a blank line, enter paste mode

For further help on a specific object, type help(obj)
For a list of available modules, type help('modules')

Вводим команду help('modules') чтобы посмотреть список установленных по-умолчанию модулей:

>>> help('modules')
__main__          math              uasyncio/funcs    umachine
_onewire          micropython       uasyncio/lock     uos
_thread           network           uasyncio/stream   urandom
_uasyncio         onewire           ubinascii         ure
builtins          pyb               ucollections      uselect
cmath             stm               uctypes           usocket
dht               sys               uerrno            ustruct
framebuf          uarray            uhashlib          utime
gc                uasyncio/__init__ uheapq            utimeq
lcd160cr          uasyncio/core     uio               uzlib
lcd160cr_test     uasyncio/event    ujson
Plus any modules on the filesystem

В завершении вывода говорится, что дополнительные модули могут быть установлены на файловый носитель. Фактически это означает, что нам придется устанавливать на плату SPI-флешку.

Вводим команду pyb.info()

>>> pyb.info()
ID=4a006700:19513930:38373538
S=96000000
H=96000000
P1=24000000
P2=48000000
_etext=806b184
_sidata=806b18c
_sdata=20000000
_edata=2000001c
_sbss=2000001c
_ebss=20006494
_sstack=2001bff8
_estack=2001fff8
_ram_start=20000000
_heap_start=20006494
_heap_end=2001bff8
_ram_end=20020000
qstr:
  n_pool=1
  n_qstr=3
  n_str_data_bytes=28
  n_total_bytes=124
GC:
  86848 total
  1536 : 85312
  1=23 2=5 m=40
LFS free: 40960 bytes
THREAD: only main thread

Эта информация на нас пока ничего не значит. Что бы управлять светодиодом, следует вводить команды pyb.LED(1).on(), pyb.LED(1).off(), pyb.LED(1).toggle(). При этом синий светодиод должен включаться и выключаться соответственно:

Также обратите внимание на командные комбинации клавиш. CTRL + D, к примеру, перезагружает микроконтроллер.

5) Установка SPI флешки на плату WeACT STM32F411CEU6

Установка SPI флешки позволит преодолеть досадное ограничение в 45 килобайт под вашу программу и дополнительные модули. Здесь: https://github.com/mcauser/WEACT_F411CEU6 утверждается, что платы успешно тестировались с флешками:

  1. Winbond W25Q32 (4 MByte)
  2. Winbond W25Q64 (8 MByte)
  3. Winbond W25Q128 (16 MByte)

Замечу, что официальной поддержки 16 МБайтной флешки нет, в ее случае придется микропитон собирать из исходников. У меня под рукой была флешка Winbond W25Q64FVSIG. Запаять ее было делом пяти минут:

Кроме флешки, там есть еще посадочное место под конденсатор С15 на 0.1 мкФ:

Посадочное место под SMD конденсатор размера 0603, но у меня под рукой были только 0805. Усилием воли, я поставил тот, что был в наличии, но сел он кривовато. Флешка будет работать и без конденсатора, но его лучше будет все-таки поставить.

После промывки платы от следов пайки и последующей сушки, на микроконтроллере следует обновить прошивку, до версии, которая предназначена для работы с флешками на 8МБ https://github.com/WeActTC/MiniF4-STM32F4x1/tree/master/SDK/STM32F411CEU6/MicroPython:

Загружаем эту прошивку в чип:

Убираем ST-Link (если прошивка производилась через него) и подключаем плату через USB к компьютеру. В dmesg видим следующее:

[17748.129823] cdc_acm 4-2:1.1: ttyACM0: USB ACM device
[17748.131436] usbcore: registered new interface driver cdc_acm
[17748.131437] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[17748.136489] usb-storage 4-2:1.0: USB Mass Storage device detected
[17748.139756] scsi host8: usb-storage 4-2:1.0
[17748.139869] usbcore: registered new interface driver usb-storage
[17748.148136] usbcore: registered new interface driver uas
[17749.216339] scsi 8:0:0:0: Direct-Access     MicroPy  pyboard Flash    1.00 PQ: 0 ANSI: 2
[17749.227319] sd 8:0:0:0: [sdb] 16640 512-byte logical blocks: (8.52 MB/8.13 MiB)
[17749.234348] sd 8:0:0:0: [sdb] Write Protect is off
[17749.234355] sd 8:0:0:0: [sdb] Mode Sense: 03 00 00 00
[17749.241323] sd 8:0:0:0: [sdb] No Caching mode page found
[17749.241325] sd 8:0:0:0: [sdb] Assuming drive cache: write through
[17749.287331]  sdb: sdb1
[17749.316331] sd 8:0:0:0: [sdb] Attached SCSI removable disk

У нас появилась флешка на 8 МБ. Ее даже можно открыть в файловом браузере:

6) Сборка микропитона из исходников

Совсем не обязательно пользоваться готовыми прошивками микропитона, их можно собирать самому. Это может понадобиться, если вы решите поставить флешку на 16 МБ, или вам надо поставить микропитон на какую-то свою плату с микроконтроллером серии STM32F4xx. Или если спустя какое-то время, компания WeACT прекратит существование, и иного способа получить актуальную прошивку не будет.

На github'е https://github.com/WeActTC/MiniF4-STM32F4x1/tree/master/SDK/STM32F411CEU6/MicroPython/WeAct_F411CE выложен порядок сборки микропитона из исходников:

Следуя этим инструкциям, выполняем:

$ git clone https://github.com/micropython/micropython.git
$ cd micropython/
$ git submodule update --init
$ cd mpy-cross
$ make -j4
$ cd ../ports/stm32/boards
$ git clone https://github.com/WeActTC/WeAct_F411CE.git
$ cd ..

Перед сборкой, нам нужно сконфигурировать прошивку на работу с нужной флешкой.

Согласно инструкции, в файле: ./boards/WeAct_F411CE/mpconfigboard.h находим строки:

#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1)
#define MICROPY_HW_SPIFLASH_SIZE_BITS (32 * 1024 * 1024)

и заменяем их на следующие:

#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)
#define MICROPY_HW_SPIFLASH_SIZE_BITS (64 * 1024 * 1024)

Компилируем прошивку:

$ make -j4

После недолгой компиляции, мы получим прошивку в файле ./build-PYBV10/firmware.hex

$ arm-none-eabi-size ./build-PYBV10/firmware.hex 
   text    data     bss     dec     hex filename
      0  356852       0  356852   571f4 ./build-PYBV10/firmware.hex

Прошиваем ее в микроконтроллер, затем подключаем через USB к компьютеру, и заходим в REPL. Самым очевидным признаком того, что мы используем самосборную прошивку, будет изменившееся приветствие. Появится дата нашей сборки, замечаем, что версия микропитона сменилась с 1.12-540 до 1.12-648. Кроме того, из имени сборки исчезло слово "dirty":

Если ввести команду pyb.info(), то в строке "LFS free" появится размер нашей флешки.

7) Работа с MicroPython в интерактивной системе REPL

Прежде всего, как я уже говорил ранее (возможно кто-то читает не все подряд с начала до конца), что бы управлять периферией из REPL (Read-Eval-Print Loop), нам следует очистить от программы файл main.py на флешке и перезапустить микроконтролер. Последнее можно сделать прямо из REPL нажав комбинацию клавиш CTRL+D.

В REPL можно вводить свою программу нажав комбинацию CTRL+E. Допустим только ввод, редактировать текст нельзя, даже затереть последние напечатанные символы не получится. Поэтому если вы вводите большой текст, то пишите его обычном редакторе, а затем используйте "Copy & Paste". Кроме CTRL+C и CTRL+V, мы можем использовать более хардкорное решение.

Т.к. мы уже работаем в screen, который является консольным мультиплексором, то мы можем использовать его встроенные возможности. Все команды в screen посылаются хоткеями "CTRL+Мета+Клавиша_команды". В качестве Мета-клавишы по-умолчанию выступает Alt. Т.о. жмем "CTL+ALT+C" и открываем новую консоль, между которыми можно переключаться через "CTL+ALT+SPACE". В новой консоли, в любом консольном редакторе вводим текст программы и выделяем его через "CTL+ALT+[". После последнего хоткея подводим курсор к началу выделения и нажатием пробела начинаем его выделять. Далее ведем курсор до окончания выделения и еще одним нажатием на пробел, заканчиваем выделение.

Внизу появится сообщение, что столько-то символов было скопировано в буфер. Далее снова переключаемся на REPL, вводим "CTR+E", затем "CTL+ALT+]", и наш текст вставился без малейшей ошибки.

Комбинация REPL "CTL+C" отменяет ввод программы. Комбинация "CTL+D" запускает программу на выполнение. Выполнения программы можно прервать комбинацией "CTL+C".

Теперь давайте напишем что-то полезное. Сделаем мигалку на обычном счетчике, и на нем приблизительно измерим быстродействие нашей MicroPython системы. Вводим в REPL такую программу:

while True:
    for i in range(60000):
        pass
    pyb.LED(1).toggle()

Запускаем ее комбинацией "CTL+D", и судя по частоте мигания синего светодиода, я бы сказал, что быстродействие нашего кода на python где-то на уровне нативного кода Arduino на 16-мегагерцовой меге. Но никто же не ждал чуда? Частоту работу микроконтроллере можно менять через команду: machine.freq(). Введите ее с пустыми скобками, чтобы узнать допустимые значения. Но микроконтроллер по умолчанию работает на частоте 96МГц, т.е. на максимальной производительности.

Можно использовать функцию pyb.delay() для указания задержки к миллисекундах:

import pyb

while True:
    pyb.delay(1000)
    pyb.LED(1).toggle()

Следующий способ помигать светодиодом, на этот раз с помощью таймера, приведен как-раз в документации по этим таймерам - class Timer – control internal timers:

import pyb

tim = pyb.Timer(1)              # create a timer object using timer 1
tim.init(freq=1)                # trigger at 1Hz
tim.callback(lambda t:pyb.LED(1).toggle())

Здесь ключевое слово lambda несет то же значение, что в C++, т.е. указывает на безымянную функцию. Если то же самое сделать через именованную функцию, то получится немного длиннее:

import pyb

def blink(timer):               # we will receive the timer object when being called
    pyb.LED(1).toggle()         # make blink
tim = pyb.Timer(1, freq=2)      # create a timer object using timer 1, trigger at 2Hz
tim.callback(blink)             # set the callback to our blink function

В качестве еще одного примера работы с таймерами можно рассмотреть реализацию бенчмарка, который я описывал в 2018 году: Простой бенчмарк на операции деления:

import pyb

def blink(timer):               # we will receive the timer object when being called
    global ms
    if (ms>0):
        ms = ms-1
tim = pyb.Timer(1, freq=1000)   # create a timer object using timer 1, trigger at 1000Hz
ms=50
tim.callback(blink)             # set the callback to our blink function
div_count=0
while (ms>0):
    result=ms/int(314)
    div_count=div_count+1
tim.callback(None)              # stop timer
print('div_count= ',div_count)

И ожидаемо, что результат его работы покажет производительность даже меньшую чем в Arduino:

Программу можно записать и другим способом. Например:

import pyb

class check(object):
    def __init__(self,ms=50):
        self.ms=ms
        self.timer=pyb.Timer(1,freq=1000)
    def cb(self, tim):
        if (self.ms>0):
            self.ms-=1
    def __call__(self):
        self.div_count=0
        self.timer.callback(self.cb)
        while (self.ms>0):
            self.div_count+=1
        self.timer.callback(None)
        return self.div_count

r = check()
print(r())

Любопытно, что такая, казалось бы, более замороченная программа, работает почти в два раза быстрее предыдущей:

Кстати, раз уж мы используем прерывания, а метод def cb(self, tim) в данном примере является обработчиком прерывания, в документации к микропитону есть рекомендация вставлять следующие строки в начало программы, для возможности генерации сообщения об ошибке, если они возникнут в обработчике прерывания:

import micropython

micropython.alloc_emergency_exception_buf(100)

Если говорить конкретно об алгоритме, то его проще всего было бы реализовать без использования дополнительного таймера, через обычную функцию:

import pyb

def check(ms):
    div_count=0
    start=pyb.millis()
    while (pyb.elapsed_millis(start) < ms):
        div_count+=1
    return div_count

print(check(50))

И результат выполнения будет сопоставим с предыдущим случаем:

Теперь, допустим на нужно помигать светодиодом на произвольном пине. Для этого нужно обратиться к пину по его номеру, а не через LED(1).

Сначала зададим режим работы порта как PUSH PULL:

>>> blu=machine.Pin(pyb.Pin.cpu.C13,machine.Pin.OUT)

Теперь включаем светодиод:

>>> blu.off()

нет, это не ошибка, именно off(). Или выключаем:

>>> blu.on()

А можно и так. Включаем:

>>> blu.value(0)

Выключаем:

>>> blu.value(1)

Или меняем состояние:

>>> blu.value(not blu.value())

А сейчас самое время вспомнить, что на плате имеется кнопка "Key" висящая на PA0. Мы можем читать ее состояние, и использовать светодиод для индикации состояния кнопки. Для начала сделаем это самым простым способом - методом опроса состояния кнопки.

import pyb, machine

led=machine.Pin(pyb.Pin.cpu.C13,machine.Pin.OUT)
btn=machine.Pin(pyb.Pin.cpu.A0,machine.Pin.IN)

while True:
    led.value(btn.value())
    pyb.delay(100)

Теперь, когда мы будем нажимать кнопку "Key", синий светодиод будет загораться.

Переделать программу на внешнем прерывании не составляет большого труда. В данном случае у меня она получилось такой:

from machine import Pin
import pyb, micropython

micropython.alloc_emergency_exception_buf(100)

btn=Pin(pyb.Pin.cpu.A0,Pin.IN)

def btn_irq(pin):
    pyb.LED(1).toggle()
    global interrupt_pin
    interrupt_pin=pin
    print("press: ",interrupt_pin)

btn.irq(trigger=Pin.IRQ_RISING,handler=btn_irq)

while True:
    pyb.delay(100)

При каждом нажатии на кнопку "KEY" синий светодиод будет зажигаться или гаснуть, при этом в консоль пойдет идентификатор пина с кнопкой:

Замечу, что при нажатии на кнопку отсутствовал дребезг контакта, поэтому ставить какие-то задержки, для подавления дребезга, в обработчик внешнего прерывания я не стал.

8) Использование редакторов VS Code и Atom в качестве IDE для MicroPython

Для редакторов кода VS Code и Atom возможна установка плагина Pymakr, который превращает работу в них, в приятную альтернативу использованию программы screen. Полагаю, что наиболее актуально это будет для пользователей Windows, у которых программы screen под рукой как правило нет, да и работать в консоли они не любят. Плагин Pymakr предоставляет вам консоль REPL, и позволяет автоматизировать оправку вашей программы в REPL на исполнение. Но Pymakr написан был для работы с платой Pyboard, и "из коробки" с нашей платой WeACT работать не будет. Его нужно будет настраивать. В принципе, это займет всего пару минут.

Проше всего, как мне показалось, настроить VS Code. Для начала нужно будет установить этот самый плагин Pymakr:

Затем надо будет заглянуть в лог dmesg и посмотреть, как там записан производитель (Manufacturer) последовательного порта.

[ 1709.050627] usb 4-2: New USB device found, idVendor=f055, idProduct=9800, bcdDevice= 2.00
[ 1709.050634] usb 4-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 1709.050639] usb 4-2: Product: Pyboard Virtual Comm Port in FS Mode
[ 1709.050642] usb 4-2: Manufacturer: MicroPython
[ 1709.050645] usb 4-2: SerialNumber: 389C37823039
[ 1709.466521] usb-storage 4-2:1.0: USB Mass Storage device detected
[ 1709.466639] scsi host8: usb-storage 4-2:1.0
[ 1709.466747] usbcore: registered new interface driver usb-storage
[ 1709.474206] cdc_acm 4-2:1.1: ttyACM0: USB ACM device
[ 1709.475847] usbcore: registered new interface driver cdc_acm
[ 1709.475850] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[ 1709.500918] usbcore: registered new interface driver uas
[ 1710.529689] scsi 8:0:0:0: Direct-Access     MicroPy  pyboard Flash    1.00 PQ: 0 ANSI: 2
[ 1710.541684] sd 8:0:0:0: [sdb] 16640 512-byte logical blocks: (8.52 MB/8.13 MiB)
[ 1710.548668] sd 8:0:0:0: [sdb] Write Protect is off
[ 1710.548676] sd 8:0:0:0: [sdb] Mode Sense: 03 00 00 00
[ 1710.555673] sd 8:0:0:0: [sdb] No Caching mode page found
[ 1710.555680] sd 8:0:0:0: [sdb] Assuming drive cache: write through
[ 1710.607667]  sdb: sdb1
[ 1710.668669] sd 8:0:0:0: [sdb] Attached SCSI removable disk

Т.к. последовательный порт виртуальный, в качестве производителя можно в исходниках вписать что угодно, так что эта запись может измениться в будущих прошивках. В случае Windows, производителя нужно искать, я думаю, в диспечере устройств, в свойствах устройства.

После установки Pymakr сразу же откроется файл с настройками плагина - pymakr.json. Но даже если он не откроется автоматически, его можно найти в ~/.config/Code/User/pymakr.json. В этом файле настроек, нам нужно будет поменять адрес устройства с 192.168.4.1 на наш порт: "/dev/ttyACM0". Кроме этого добавить нашего производителя в список поддерживаемых для автосоединения. При этом автосоединения лучше будет сбросить в false, это только мешает.

После этого достаточно будет подключить плату к компьютеру и щелкнуть внизу по "Pymakr Console". В окне терминала появиться консоль REPL. Щелчок по "Run" запустит вашу программу в REPL.

Настройка Atom'а аналогична. Для начала нужно будет установить плагин Pymakr, после чего сразу зайти в настройки плагина:

В настройках нужно будет также изменить адрес устройства на свой порт, сбросить флажок с автосоединения, и добавить своего производителя в общий список:

После этого сверху, на окне Pymakr, нужно щелкнуть по "Connect Device", и из выпадающего списка выбрать свой порт. В окне появиться консоль REPL:

Клик по треугольнику слева загрузит вашу программу на выполнение в REPL.