Monthly Archives: April 2021

Black Pill: STM32F401CC / STM32F411CE

ST

STM32F401CC, High-performance access line, Arm Cortex-M4 core with DSP and FPU, 256 Kbytes of Flash memory, 84 MHz CPU, ART Accelerator
STM32F411CC, High-performance access line, Arm Cortex-M4 core with DSP and FPU, 256 Kbytes of Flash memory, 100 MHz CPU, ART Accelerator
STM32F411CE, High-performance access line, Arm Cortex-M4 core with DSP and FPU, 512 Kbytes of Flash memory, 100 MHz CPU, ART Accelerator

Schematics

WeAct Black Pill V2.0, STM32F411CEU6
WeAct Black Pill V3.0, STM32F401CEU6

Reference

STM32F103C8, Mainstream Performance line, Arm Cortex-M3 MCU with 64 Kbytes of Flash memory, 72 MHz CPU, motor control, USB and CAN

AliExpress

STM32F103C8T6 ARM STM32 Minimum System Development Board STM32F401 STM32F411 STM32F4 + ST-LINK V2 Simulator Download Programmer
STM32F401CCU6 411CEU6 256KB ROM Development Board V1.2 STM32F4 Learning Board For Arduino ST-Link V2 Simulator Download

David Ledger’s uSupply (STM32, C++17)

EEVblog

C++ for the Embedded Programmer
uSupply Firmware Released

Offtopic

STM32 macro for port and pin read and write
Peripheral register access using C Struct’s – part 1

YouTube

C++ for the Embedded Programmer
C/C++ Interrupt Undefined Behavior

GitHub

github.com/SeppyMoose

GitLab

gitlab.com/eevblog/usupply-firmware
gitlab.com/eevblog/usupply-library
gitlab.com/eevblog/usupply-usb

Other Libraries

stm32plus C++ library

Digikey Electronics – Getting Started with STM32 and Nucleo

Getting Started with STM32 and Nucleo

Getting Started with STM32 and Nucleo Part 1: Introduction to STM32CubeIDE and Blinky – Digi-Key
Getting Started With STM32 and Nucleo Part 2: How to Use I2C to Read Temperature Sensor TMP102
Getting Started With STM32 and Nucleo Part 3: FreeRTOS – How To Run Multiple Threads w/ CMSIS-RTOS
Getting Started With STM32 and Nucleo Part 4: Working with ADC and DMA – Maker.io
Getting Started with STM32 and Nucleo Part 5: How to Use SPI | Digi-Key Electronics
Getting Started with STM32 and Nucleo Part 6: Timers and Timer Interrupts | Digi-Key Electronics

Programming the Adafruit Feather STM32F405 Express with STM32CubeIDE – Maker.io

STM32duino UART / Serial

STM32duino III. – How to use Serial (USART)


STM32duino Wiki

Welcome to the stm32duino wiki!
API HardwareSerial


PlatformIO

How to use stm32duino build_opt.h in PlatformIO?
How to use stm32duino (official STM32 core) build_opt.h in PlatformIO
Include header files using command line option?

build_flags = 
    -DENABLE_HWSERIAL3
    -DPIN_SERIAL3_RX=PB11
    -DPIN_SERIAL3_TX=PB10

or

build_flags = 
     -include build_opt.h


Variant

ldscript.ld
PeripheralPins.c
PinNamesVar.h
variant.cpp
variant.h
#define PC5  0
#define PC4  1
#define PA10 2
[...]
HAL_ADC_MODULE_ENABLED
HAL_DAC_MODULE_ENABLED
HAL_I2C_MODULE_ENABLED
HAL_TIM_MODULE_ENABLED
HAL_UART_MODULE_ENABLED
HAL_SPI_MODULE_ENABLED
WEAK const PinMap PinMap_UART_TX[] = {
  [...]
  {PA_5,  USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART3)},
  {PB_2,  USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART3)},
  [...]
                                    ^                 ^
                                    |                 |
                          STM_MODE_INPUT           GPIO_NOPULL
                          STM_MODE_OUTPUT_PP       GPIO_PULLUP
                          STM_MODE_OUTPUT_OD       GPIO_PULLDOWN
                          STM_MODE_ANALOG
                          STM_MODE_AF_OD => Alternate Function, Open-Drain
                          STM_MODE_AF_PP => Alternate Function, Push-Pull
#define STM_PIN_DATA(FUNC_OD, PUPD, AFNUM) \
            STM_PIN_DEFINE(FUNC_OD, PUPD, AFNUM)
#define STM_PIN_DATA_EXT(FUNC_OD, PUPD, AFNUM, CHANNEL, INVERTED) \
            STM_PIN_DEFINE_EXT(FUNC_OD, PUPD, AFNUM, CHANNEL, INVERTED)


Digital

Arduino_Core_STM32—pinMode() implementation analysis

void pinMode(uint32_t ulPin, uint32_t ulMode)
{
  PinName p = digitalPinToPinName(ulPin);

  if (p != NC) {
    [...]
    switch (ulMode) {
      case INPUT: /* INPUT_FLOATING */
        pin_function(p, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
        break;
      case INPUT_PULLUP:
        pin_function(p, STM_PIN_DATA(STM_MODE_INPUT, GPIO_PULLUP, 0));
        break;
      case INPUT_PULLDOWN:
        pin_function(p, STM_PIN_DATA(STM_MODE_INPUT, GPIO_PULLDOWN, 0));
        break;

      [...]
    }
  }
}


PinMap_UART_TX

typedef struct {
  PinName pin;
  void *peripheral;
  int function;
} PinMap;
typedef enum {
  // Not connected
  NC = (int)0xFFFFFFFF,

  // Pin name definition
  PA_0  = (PortA << 4) + 0x00,
  [...]

  // Specific pin name define in the variant
  #include "PinNamesVar.h"

  P_END = NC
} PinName;
extern GPIO_TypeDef *GPIOPort[];

typedef enum {
  FirstPort = 0x00,
  PortA = FirstPort,
  PortB,
#if defined GPIOC_BASE
  PortC,
#endif
  [...]
  PortEND,
  LastPort = PortEND - 1
} PortName;

#define MAX_NB_PORT (LastPort-FirstPort+1)
extern const PinName digitalPin[];
extern const uint32_t analogInputPin[];

[...]

#define digitalPinToPinName(p)      (((uint32_t)p < NUM_DIGITAL_PINS) ? digitalPin[p] : \
            ((uint32_t)p >= NUM_ANALOG_FIRST) && ((uint32_t)p <= NUM_ANALOG_LAST) ? \
            digitalPin[analogInputPin[p-NUM_ANALOG_FIRST]] : NC)
  • STM_PIN_DATA()
  • STM_PIN_DATA_EXT()
  • pin_in_pinmap()
    bool pin_in_pinmap(PinName pin, const PinMap *map)
    Loop
  • pinmap_peripheral()
    void *pinmap_peripheral(PinName pin, const PinMap *map)
  • pinmap_pinout()
    void pinmap_pinout(PinName pin, const PinMap *map)
  • pinmap_pin()
    PinName pinmap_pin(void *peripheral, const PinMap *map)
./cores/arduino/pins_arduino.h:           pin_in_pinmap(digitalPinToPinName(p), PinMap_UART_TX))
./cores/arduino/stm32/PeripheralPins.h:   extern const PinMap PinMap_UART_TX[];
./libraries/SrcWrapper/src/stm32/uart.c:  #define DEBUG_UART          pinmap_peripheral(digitalPinToPinName(PIN_SERIAL_TX), PinMap_UART_TX)
./libraries/SrcWrapper/src/stm32/uart.c:  USART_TypeDef *uart_tx = pinmap_peripheral(obj->pin_tx, PinMap_UART_TX);
./libraries/SrcWrapper/src/stm32/uart.c:  pinmap_pinout(obj->pin_tx, PinMap_UART_TX);
./libraries/SrcWrapper/src/stm32/uart.c:  serial_debug.pin_tx = pinmap_pin(DEBUG_UART, PinMap_UART_TX);


Serial3

  • HAVE_HWSERIAL3
  • ENABLE_HWSERIAL3
  • SERIAL_UART_INSTANCE
[...]
extern HardwareSerial Serial3;
extern HardwareSerial Serial4;
[...]
  [...]
  #if defined(HAVE_HWSERIAL3)
    HardwareSerial Serial3(USART3);
    void serialEvent3() __attribute__((weak));
  #endif

  #if defined(HAVE_HWSERIAL4)
    #if defined(USART4)
      HardwareSerial Serial4(USART4);
    #else
      HardwareSerial Serial4(UART4);
    #endif
    void serialEvent4() __attribute__((weak));
  #endif

  [...]

// Constructors ////////////////////////////////////////////////////////////////
/* ex. PA0 from #define in variant.h */
HardwareSerial::HardwareSerial(uint32_t _rx, uint32_t _tx)
{
  init(digitalPinToPinName(_rx), digitalPinToPinName(_tx));
}

/* ex. PA_0 from enum PinName in PinNames.h*/
HardwareSerial::HardwareSerial(PinName _rx, PinName _tx)
{
  init(_rx, _tx);
}

/* ex. peripheral Serial2 (stm32g071xx.h: USART2 => USART2_BASE => 0x40004400) */
HardwareSerial::HardwareSerial(void *peripheral, HalfDuplexMode_t halfDuplex)
{
  [...]

  /* if pins are defined (variant.h or compiler flag -D), use them, otherwise use from PinMap */

#if defined(Serial) && defined(PIN_SERIAL_TX)
  if ((void *)this == (void *)&Serial) {
#if defined(PIN_SERIAL_RX)
    setRx(PIN_SERIAL_RX);
#endif
    setTx(PIN_SERIAL_TX);
  } else
#endif

  [...]

#if defined(PIN_SERIAL3_TX) && defined(USART3_BASE)
        if (peripheral == USART3) {
#if defined(PIN_SERIAL3_RX)
          setRx(PIN_SERIAL3_RX);
#endif
          setTx(PIN_SERIAL3_TX);
        } else
#endif

  [...]

                          // else get the pins of the first peripheral occurence in PinMap
                        {
                          _serial.pin_rx = pinmap_pin(peripheral, PinMap_UART_RX);
                          _serial.pin_tx = pinmap_pin(peripheral, PinMap_UART_TX);
                        }

  [...]
}

[...]

void HardwareSerial::init(PinName _rx, PinName _tx)
{
  [...]
}
/*!< Peripheral declaration */
#define USART2              ((USART_TypeDef *) USART2_BASE)
#define USART3              ((USART_TypeDef *) USART3_BASE)
#define USART4              ((USART_TypeDef *) USART4_BASE)
[...]
#define LPUART1             ((USART_TypeDef *) LPUART1_BASE)

[...]

/*!< APB peripherals */
#define USART2_BASE           (APBPERIPH_BASE + 0x00004400UL)
#define USART3_BASE           (APBPERIPH_BASE + 0x00004800UL)
#define USART4_BASE           (APBPERIPH_BASE + 0x00004C00UL)
[...]
#define LPUART1_BASE          (APBPERIPH_BASE + 0x00008000UL)
  [...]
  #if defined(ENABLE_HWSERIAL3)
    #if defined(USART3_BASE)
      #define HAVE_HWSERIAL3
    #endif
  #endif
  #if defined(ENABLE_HWSERIAL4)
    #if defined(USART4_BASE) || defined(UART4_BASE)
      #define HAVE_HWSERIAL4
    #endif
  #endif
  [...]
WEAK void serialEventRun(void)
{
  [...]
#if defined(HAVE_HWSERIAL3)
  if (serialEvent3 && Serial3.available()) {
    serialEvent3();
  }
#endif
#if defined(HAVE_HWSERIAL4)
  if (serialEvent4 && Serial4.available()) {
    serialEvent4();
  }
#endif
  [...]
}


Callstack

size_t HardwareSerial::write(uint8_t c)
{
  [...]

  if (!serial_tx_active(&_serial)) {
    uart_attach_tx_callback(&_serial, _tx_complete_irq);
  }

  [...]
}

int HardwareSerial::_tx_complete_irq(serial_t *obj)
{
  // If interrupts are enabled, there must be more data in the output
  // buffer. Send the next byte
  obj->tx_tail = (obj->tx_tail + 1) % SERIAL_TX_BUFFER_SIZE;

  if (obj->tx_head == obj->tx_tail) {
    return -1;
  }

  return 0;
}
typedef struct serial_s serial_t;

struct serial_s {
  /*  The 1st 2 members USART_TypeDef *uart
   *  and UART_HandleTypeDef handle should
   *  be kept as the first members of this struct
   *  to have get_serial_obj() function work as expected
   */
  USART_TypeDef *uart;
  UART_HandleTypeDef handle;
  void (*rx_callback)(serial_t *);
  int (*tx_callback)(serial_t *);
  PinName pin_tx;
  PinName pin_rx;
  IRQn_Type irq;
  uint8_t index;
  uint8_t recv;
  uint8_t *rx_buff;
  uint8_t *tx_buff;
  uint16_t rx_tail;
  uint16_t tx_head;
  volatile uint16_t rx_head;
  volatile uint16_t tx_tail;
};
/**
 * Begin asynchronous TX transfer.
 *
 * @param obj : pointer to serial_t structure
 * @param callback : function call at the end of transmission
 * @retval none
 */
void uart_attach_tx_callback(serial_t *obj, int (*callback)(serial_t *))
{
  if (obj == NULL) {
    return;
  }
  obj->tx_callback = callback;

  /* Must disable interrupt to prevent handle lock contention */
  HAL_NVIC_DisableIRQ(obj->irq);

  /* The following function will enable UART_IT_TXE and error interrupts */
  HAL_UART_Transmit_IT(uart_handlers[obj->index], &obj->tx_buff[obj->tx_tail], 1);

  /* Enable interrupt */
  HAL_NVIC_SetPriority(obj->irq, UART_IRQ_PRIO, UART_IRQ_SUBPRIO);
  HAL_NVIC_EnableIRQ(obj->irq);
}

/**
  * @brief  Tx Transfer completed callback
  * @param  UartHandle pointer on the uart reference
  * @retval None
  */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  serial_t *obj = get_serial_obj(huart);

  if (obj && obj->tx_callback(obj) != -1) {
    if (HAL_UART_Transmit_IT(huart, &obj->tx_buff[obj->tx_tail], 1) != HAL_OK) {
      return;
    }
  }
}

Rust Programming

Developer Survey

Rust language: Stack Overflow Developer Survey, 22. Juni 2022
Stack Overflow Developer Survey 2023
Stack Overflow Developer Survey 2022
10 Most Loved Programming Languages: Rust, TypeScript, and More, May 29, 2020

Documentation

The Rust Reference
The Rust Programming Language

Asynchronous Programming in Rust

Rust by Example
github.com/rust-lang/rust-by-example
Rust By Practice

Blog

Fearless Concurrency with Rust
Abstraction without overhead: traits in Rust

Virtual Structs Part 1: Where Rust’s enum shines
Virtual Structs Part 2: Classes strike back
Virtual Structs Part 3: Bringing Enums and Structs Together

Unsafe

The Rustonomicon

  • the meaning of (un)safety
  • unsafe primitives provided by the language and standard library
  • techniques for creating safe abstractions with those unsafe primitives
  • subtyping and variance
  • exception-safety (panic/unwind-safety)
  • working with uninitialized memory
  • type punning
  • concurrency
  • interoperating with other languages (FFI)
  • optimization tricks
  • how constructs lower to compiler/OS/hardware primitives
  • how to not make the memory model people angry
  • how you’re going to make the memory model people angry


Books

Fullstack Rust – Adding State to Our Web App


Syntax

unit type

Primitive Type unit
What is the purpose of the unit type in Rust?

Option and Result

Module std::option
Module std::result
Understanding Rust Option and Result enums

static

fn spawn<F>(f: F) where F: 'static, ...
fn scoped<'a, F>(f: F) -> JoinGuard<'a> where F: 'a, ...

constraint

'static no borrowed data is permitted in the closure
'a cannot escape the scope of any data borrowed by the closure

trait

trait ClickCallback {
    fn on_click(&self, x: i64, y: i64);
}
struct Button {
    listeners: Vec<Box<ClickCallback>>,
    ...
}

trait object

  • traits are types, but they are “unsized”
  • they are only allowed to show up behind a pointer like Box (which points onto the heap) or & (which can point anywhere)
  • Static and dynamic dispatch
&ClickCallback no borrowed data is permitted in the closure
Box cannot escape the scope of any data borrowed by the closure

Future trait

The Future Trait
Build an Executor


Tutorials

NICLAS ROSSBERGER

Rust-01: Why am I learning Rust?
Rust-02: The beginning
Rust-03: Improving the simple program

Medium

C++ vs Rust: an async Thread-per-Core story

How to Idiomatically Use Global Variables in Rust

Java

ExecutorService – Waiting for Threads to Finish
Guide to java.util.concurrent.Future


YouTube

Andrew Sharp

Learn Rust Part 1: numbers, variables, types and functions
Learn Rust Part 2: Structs
Learn Rust Part 3: References
Learn Rust Part 4: Mutable References
Learn Rust Part 5: Ownership
Learn Rust Part 6: Borrowing
Learn Rust Part 7: Lifetimes
Learn Rust Part 8: Lifetimes with Structs
Learn Rust Part 9: Constant Items and the Static Lifetime
Learn Rust Part 10: Methods and Associated Functions

Dave Halter

Rust für Python Developers (english) | Swiss Python Summit 2022
github.com/davidhalter/rust-for-python-developers

Ghidra Xtensa ISA

Analyzing an esp32 flash dump with ghidra
Enter /home/dragon with Ghidra
github.com/Ebiroll/ghidra-xtensa, Tensilica Xtensa processor module for Ghidra

Implementing a New CPU Architecture for Ghidra (PDF)
Quick Guide to Creating a Processor in Ghidra

C:\Users\andreas\Downloads\ghidra_9.2.2_PUBLIC>support\sleigh.bat -a Ghidra\Processors\Xtensa
INFO  Using log config file: jar:file:/C:/Users/andreas/Downloads/ghidra_9.2.2_PUBLIC/Ghidra/Framework/Generic/lib/Generic.jar!/generic.log4j.xml (LoggingInitialization)
INFO  Using log file: C:\Users\andreas\.ghidra\.ghidra_9.2.2_PUBLIC\application.log (LoggingInitialization)
Compiling Ghidra\Processors\Xtensa\data\languages\xtensa.slaspec:
WARN  2 NOP constructors found (SleighCompile)
WARN  Use -n switch to list each individually (SleighCompile)

1 languages successfully compiled

github.com/pfalcon/ida-xtensa2/, IDAPython plugin for Tensilica Xtensa (as seen in ESP8266), version 2
ithub.com/pfalcon/ida-xtensa2/blob/master/xtensa.py

General Ghidra

What’re you telling me, Ghidra? An introduction to Ghidra’s primary components

General ESP

ESP32 Programmers’ Memory Model