Board Support Package for the Nucleo STM32WB Board
Intro
The board support package for the STM32WB Nucleo Board is restricted to the Arduino UNO R3 pin header and the onboard LEDs and switches (buttons). The STM32 has much more capabilities then 14 digital I/O pins, 6 analog input pins, UART, SPI, and I2C interfaces. But if you want to use the more advanced features you can use the CubeMX to create source code for the internal peripherals. This project wants to show how to use the Cube Ecosystem for a Forth system (or vice versa) and can't implement all features and possibilities the STM32WB has. It is a good starting point for your project.

This page is no longer updated regularly, the current documentation can be found at GitHub.

Board Support Words

Defaults: Digital port pins D0 to D7 are push pull outputs, D8 to D15 are inputs with pull-up resistors.

led1!        ( ? -- )    set LED1 (blue)
led2!        ( ? -- )    set LED2 (green)
led3!        ( ? -- )    set LED3 (red)
led1@        ( -- ? )    get LED1 (blue)
led2@        ( -- ? )    get LED2 (green)
led3@        ( -- ? )    get LED3 (red)

switch1?     ( -- ? )    get switch1, closed=TRUE
switch2?     ( -- ? )    get switch2, closed=TRUE
switch3?     ( -- ? )    get switch3, closed=TRUE

dport!       ( n -- )    set the digital output port (D0=bit0 .. D15=bit15).
dport@       ( -- n )    get the digital input/output port (D0=bit0 .. D15=bit15).
dpin!        ( n a -- )  set the digital output port pin (D0=0 .. D15=15)
dpin@        ( a -- n )  get the digital input/output port pin 
dmod         ( u a -- )  set the pin mode: 0 in, 1 in pull-up, 2 in pull-down, 3 out push pull, 4 out open drain, 
                                            5 out push pull PWM, 6 input capture, 7 output compare, 8 I2C

EXTImod      ( u a -- )  set for pin a (D2, D4, D7, D10) the EXTI mode u: 0 rising, 1 falling, 2 both edges, 3 none
EXTIwait     ( u a -- )  wait for EXTI interrupt on pin a (D2, D4, D7, D10), timeout u in [ms]

pwmpin!      ( u a -- )  set the digital output port pin a (D3=3, D6=6, D9=9) to a PWM value u (0..1000). Default frequency is 1 kHz, TIMER1
pwmprescale  ( u --  )   Set the PWM prescale for TIMER1. 32 kHz / prescale, default 32 -> PWM frequency 1 kHz

ICOCprescale ( u -- )    set the input capture / output compare prescale for TIMER2. default 32 -> 32 MHz / 32 = 1 MHz, timer resolution 1 us
ICOCperiod!  ( u -- )    set the input capture / output compare (TIMER2) period. default $FFFFFFFF (4'294'967'295). 
                         When the up counter reaches the period, the counter is set to 0. 
                         For prescale 32 the maximum time is about 1 h 11 m
ICOCcount!   ( -- u )    set the input capture / output compare counter for TIMER2
ICOCcount@   ( u -- )    get the input capture / output compare counter for TIMER2
ICOCstart    ( -- )      start the ICOC period
ICOCstop     ( -- )      stop the ICOC period
OCmod        ( u a -- )  set for pin a (D0, D1, D5) the Output Compare mode u: 0 frozen, 1 active level on match, 2 inactive level on match, 
                            3 toggle on match, 4 forced active, 5 forced inactive
    
OCstart      ( u a -- )  start the output compare mode for pin a with pulse u
OCstop       ( a -- )    stop output compare for pin a
ICstart      ( u -- )    start input capture u: 0 rising edge, 1 falling edge, 2 both edges
ICstop       ( -- )      stop input capture

waitperiod   ( -- )      wait for the end of the TIMER2 period
OCwait       ( a -- )    wait for the end of output capture on pin a
ICwait       ( u -- u )  wait for the end of input capture with timeout u, returns counter u

apin@        ( a -- u )  get the analog input port pin (A0 .. A5). Returns a 12 bit value (0..4095) 
vref@        ( -- u )    get the Vref voltage in mV (rather the VDDA)
vbat@        ( -- u )    get the Vbat voltage in mV
CPUtemp@     ( -- u )    get CPU temperature in degree Celsius

I2Cput       ( a # u -- )     put a message with length u (count in bytes) from buffer at a to the I2C slave device u
I2Cget       ( a # u -- )     get a message with length u from I2C slave device to buffer at a
I2Cputget    ( a #1 #2 u -- ) put a message with length #1 from buffer at a to the I2C slave device u
                              and get a message with length #2 from device to buffer at a

SPIget       ( a # -- )       get a message with length # from SPI slave device to buffer at a
SPIput       ( a # -- )       put a message with length # from buffer at a to the SPI slave device 
SPIputget    ( a #1 #2 -- )   put a message with length #1 from buffer at a to the SPI slave device 
                              and get a message with length #2 from device to buffer at a
SPImutex     ( -- a )         get the SPI mutex address

Using the Digital Port Pins (Input and Output)

This example is very similar to the McForth#Knight_Rider program. dport! and dport@ set and get all 16 digital pins (D0 to D15) at once. You have to press the SW1 push button til D0 is set to cancel the operation.

3 0 dmod   \ set D0 to Output
3 1 dmod   \ set D1 to Output
3 2 dmod   \ set D2 to Output
3 3 dmod   \ set D3 to Output
3 4 dmod   \ set D4 to Output
3 5 dmod   \ set D5 to Output
3 6 dmod   \ set D6 to Output
3 7 dmod   \ set D7 to Output

: left ( -- ) 
  7 0 do  
    dport@ shl dport!  
    100 osDelay drop  
  loop 
;
: right ( -- )
  7 0 do  
    dport@ shr dport!
    100 osDelay drop  
  loop 
;
: knightrider ( -- )
  1 dport! 
  begin 
    left right 
    switch1? \ or key?
  until 
  0 dport!
;

Single port pin variant (no side effects on port pins D8 to D15):

: left ( -- ) 
  7 0 do
    1 i dpin! 
    100 osDelay drop  
    0 i dpin!
  loop 
;
: right ( -- )
  8 1 do  
    1 8 i - dpin! 
    100 osDelay drop  
    0 8 i - dpin!
  loop 
;
: knigthrider ( -- )
  begin 
    left right 
    switch1? 
  until 
  0 0 dpin!
;

Using the ADC (Analog Input Pins)

apin@ ( a -- u ) returns the ADC value (12 bit, 0 .. 4095) from one of the analog pins A0 to A5 (0 .. 5). Here I use the A0 to control the delay.

: left ( -- ) 
  7 0 do
    1 i dpin! 
    0 apin@ 10 / osDelay drop  \ delay depends on A0
    0 i dpin!
  loop 
;
: right ( -- )
  8 1 do  
    1 8 i - dpin! 
    0 apin@ 10 / osDelay drop  \ delay depends on A0
    0 8 i - dpin!
  loop 
;

To get an idea how fast the ADC, RTOS, and the Forth program are. The left or right word takes about 125 us, the knightrider loop about 50 us (no osDelay). Pretty fast for my opinion.

CH1 yellow: D0 pin
CH2 blue: D1 pin
TEK0012.png

Using the PWM (Analog Output Pins)

Only three port pins are supported so far. The 16 bit TIMER1 is used for the timebase, time resolution is 1 us (32 MHz SysClk divided by 32). The PWM scale is from 0 (0 % duty cycle) to 1000 (100 % duty cycle), this results in a PWM frequency of 1 kHz. If you need higher PWM frequencies, decrease the divider and/or the scale.

PWM port pins: D6 (TIM1CH1), D9 (TIM1CH2), D3 (TIM1CH3)

Simple test program to set brightness of a LED on pin D3 with a potentiometer on A0. Default PWM frequency is 1 kHz (prescaler set to 32). You can set the prescale with the word pwmprescale from 32 kHz (value 1) down to 0.5 Hz (64000).

5 3 dmod   \ set D3 to PWM

: pwm ( -- )
  begin 
    0 apin@  4 /  3 pwmpin!
    10 osDelay drop
    switch1? 
  until 
;

Using Input Capture and Output Compare

Time Base

Default timer resolution is 1 us. The 32 bit TIMER2 is used as time base for Input Capture / Output Compare. For a 5 s period 5'000'000 cycles are needed. All channels (input capture / output compare) use the same time base.

: period ( -- )
  5000000 ICOCperiod! \ 5 s period
  ICOCstart
  begin
     waitperiod
     cr .time
  key? until
  key drop 
;

Output Compare

: oc-toggle ( -- )
  5000000 ICOCperiod! \ 5 s period
  ICOCstart
  3 0 OCmod  1000000 0 OCstart \ toggle D0 after 1 s
  3 1 OCmod  2000000 1 OCstart \ toggle D1 after 2 s
  3 5 OCmod  3000000 5 OCstart \ toggle D5 after 3 s 
  begin
     waitperiod
     cr .time
  key? until
  key drop 
;

When you abort (hit any key) the program, the timer still runs and controls the port pins. To stop the port pins:

0 OCstop  1 OCstop  5 OCstop

Or change the prescale to make it faster or slower:

1 ICOCprescale

Input Capture

This sample program measures the time between the edges on port A2. if no event occurs within 2 seconds, "timeout" is issued. Hit any key to abort program.

: ic-test ( -- )
  6 2 dmod \ input capture on A2
  ICOCstart
  2 ICstart  \ both edges
  ICOCcount@ ( -- count )
  begin
    2000 \ 2 s timeout
    ICwait ( -- old-capture capture ) 
    cr
    dup 0= if
      ." timeout" drop
    else 
      dup rot ( -- capture capture old-capture )
      - 1000 / . ." ms"
    then
  key? until
  key drop
  drop
  ICstop
;

Using EXTI line

: exti-test ( -- )
  2 2 EXTImod \ both edges on D2
  begin
    2000 2 EXTIwait \ wait for edge on D2 with 2 s timeout
    cr
    0= if
      2 dpin@ if
        ." rising edge"
      else
        ." falling edge"
      then 
    else
      ." timeout"
    then
  key? until
  key drop
;

Pinouts

STM32WB Nucleo Board

stm32wb-nucleo.jpg

Arduino Pinout

nucleo wb55rg arduino left.png
Arduino left
nucleo wb55rg arduino right.png
Arduino right

Morpho Pinout

nucleo wb55rg morpho left.png
Morpho left
nucleo wb55rg morpho right2.png
Morpho right

Push Buttons

Signal name STM32WB55 pin Comment
SWITCH1 PC4 (PC13) (WKUP2)
SWITCH2 PD0  
SWITCH3 PD1  

LEDs

Signal name STM32WB55 pin Comment
LD1 PB5  
LD2 PB0  
LD3 PB1  
Neopixel PC12 D8

UART VCP ST-LINK

Signal name STM32WB55 pin Comment
UART_TX PB6 USART1_TX
UART_RX PB7 USART1_RX

Quad SPI for Flash

Signal name STM32WB55 pin Comment
FLASH_NCS PD3 QUADSPI_BK1_NCS
FLASH_IO0 PB9 QUADSPI_BK1_IO0
FLASH_IO1 PD5 QUADSPI_BK1_IO1
FLASH_IO2 PD6 QUADSPI_BK1_IO2
FLASH_IO3 PD7 QUADSPI_BK1_IO3
FLASH_SCLK PA3 QUADSPI_BK1_SCLK

STM32WB Nucleo Dongle

Push Button

Signal name STM32WB55 pin Comment
SWITCH1 PC12 WKUP3

LEDs

Signal name STM32WB55 pin Comment
LD1 PB5  
LD2 PB0  
LD3 PA4  
Neopixel PC12 D6

Nucleo Dongle - Feather Adaptor

Remove the USB Type A plug from the dongle and add a Adafruit Micro B breakout board. It is convenient to have a Micro-SD breakout board (level shifter is not needed) and JTAG connector (I prefer the 14 pin STM variant to have a serial interface by the ST-LINK). Everything mounted on headers on the backside of FeatherWing Tripler Mini Kit.

Description Dongle Function Feather Micro-SD JTAG 14pin
GND CN1.1 GND JP1.13 GND GND 5, 7, 11
NRST CN1.2 RES JP1.16 RST   12
PA13 CN1.3 SWDIO -   4
PA14 CN1.4 SWDCLK -   6
PB3 CN1.5 SWO A4?   8
3V3 CN1.6 3V3 JP1.14/15 3V3 3V 5V 3
PB2 CN1.7 SPI_CS - CS  
PA5 CN1.8 D13 SCK JP1.6 SCK CLK  
PA6 CN1.9 D12 MISO JP1.4 MISO DO  
PA7 CN1.10 D11 MOSI JP1.5 MOSI DI  
PB8 CN2.1 D15 SCL JP3.11 SCL    
PB9 CN2.2 D14 SDA JP3.12 SDA    
PA0 CN2.3 A3 JP1.9 A3    
PA2 CN2.4 D1 JP1.2 D1    
PA3 CN2.5 D0 JP1.3 D0    
PB6 CN2.6 UARTRX     13
PA9 CN2.7 D9 JP3.8 D9    
PB7 CN2.7 UARTTX     14
PA8 CN2.8 D6 Neopixel JP3.9 D6    
GND CN2.9 GND GND    
PA1 CN2.10 A2 JP1.10 A2    
USB5V   5V JP3.3 USB    
BOOT0   BOOT0 JP1.1 B0    
PB0 AT2 ?     JP1.12 A0    
PB1 AT2 ?     JP1.11 A1    
PB3 ? CN1.5 SWO JP1.8 A4?   8
      JP1.7 A5    
      JP3.1 VBAT    
      JP3.2 EN    

-- Peter Schmid - 2020-04-11

Creative Commons License
This work by Peter Schmid is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Topic attachments
I Attachment History Action Size Date Who Comment
PNGpng TEK0012.png r1 manage 3.6 K 2020-04-16 - 14:22 PeterSchmid  
Edit | Attach | Watch | Print version | History: r37 < r36 < r35 < r34 < r33 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r37 - 2024-02-17 - PeterSchmid
 
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback