Close

stm32g0, openocd, and mecrisp stellaris - part 2

andrew-clappAndrew Clapp wrote 11/14/2021 at 17:19 • 6 min read • Like

Let's get to some more real-world examples.  First, we can look at a color-pill.  Pick a color.  If you search for "blue pill" or "green pill" or "red pill" and tac "stm32" on to the search, they all bring up useful information.  Chip availability being what it is, I'd say get yourself something and work with that.  The essentials are the same.  Any STM32 (or MSP430) that is supported by mecrisp will do.  Then all you need to do is mount your chip to a  breakout board with pins, which you can even do with a bit of luck and a soldering iron.  I am partial to the well-made blue boards from Adafruit, with a good solder mask.  They are far easier to work with than the cheap-o green ones.  Or you can pick up one of the many dev boards available... or get a pre-made "blue pill".  For DIY, you can use the suggested caps and pull-up resistor, but for experimenting purposes, I have found the mostly work fine without extra parts, at least in the way I'm using them.  The CH340 and Programmer usb sticks all have 3.3V and 5.0V outputs.  There are 3.3V reg chips on many of them, so for short wire runs, the power is pretty clean, depending on how much heavy lifting you're going to do.  I can run a 4 digit 7 segment display with no limiting resistors just fine.  You need a programmer, and a serial interface, a breadboard will make connections easier, and an LED is nice, maybe a 10K pot if you want to work with the ADC.  I really like having the 7 seg or LCD display to show alpha-numeric stuff on.

For a programming environment, you can use almost any PC or mac or *nix box.  I found that a Pi 3 or Pi 4 is also capable of doing the trick, and linux is a plus as far as I'm counting.  All the tools you need can be installed easily, or built from source by following the build instructions.  I made a lot of good progress using an ST-Link-V2 (SWIM and SWD both work) and a CH340 serial connector.  There are also JTAG specific usb sticks like this one if your mcu bends that way.

I'm using linux on a Pi 4, and I need some software tools to get the work done.  I used stlink-org's st-flash for programming for a very long time.  I am now very recently an openocd convert.  It is a 200 page manual and worth the read.  I'm not saying it will change your life, but it changed mine.  Get yourself a cup of coffee and the better part of a day and read the openocd documentation and if you don't learn at least one thing you did not know before, I would be very surprised.  You will have to write a config file for openocd for your purpose.  I will share my example, but it may not help you.

You can use e4thcom-0.8.2 to communicate with your chip via serial.  I have a simple wrapper that passes the arguments needed to connect and configure it for mecrisp stellaris.

e4thcom-0.8.2/e4thcom -t mecrisp-st -d ttyUSB0 -b B115200

One thing that is very useful indeed when understanding your mcu, or talking to your microcontroller, with a serial terminal, or programming it, or resetting it, or debugging it, would be the file that describes the chip's guts in detail.  These are the System View Description (SVD) files.  You can read more about that in the CMSIS-SVD page of the mecrisp stellaris documentation.  But basically, you want to find that file for your chip.  Then you can use svd2forth to make everything much more readable.  I did not have space for this, which was a bummer, but it is super helpful if you're lost in your micro.  For example, in the mecrisp source's common/ folder, mecrisp-stellaris-2.5.9a/common/svd2forth-v3/STM32G031.svd, and if you run it through the svd2forth process, you'll get something that is much more 'readable'... and it's basically a map of addresses for all the peripherals and such in the chip.  You might see them named memmap.fs and it is literally forth code you can use or include to assign nice names to things.  

This is what it does for you, like using GPIOA:

$50000000 constant GPIOA ( General-purpose I/Os )
GPIOA $0 + constant GPIOA-MODER ( GPIO port mode register )
GPIOA $4 + constant GPIOA-OTYPER ( GPIO port output type register )
GPIOA $8 + constant GPIOA-OSPEEDR ( GPIO port output speed  register )
GPIOA $C + constant GPIOA-PUPDR ( GPIO port pull-up/pull-down  register )
GPIOA $10 + constant GPIOA-IDR ( GPIO port input data register )
GPIOA $14 + constant GPIOA-ODR ( GPIO port output data register )
GPIOA $18 + constant GPIOA-BSRR ( GPIO port bit set/reset  register )
GPIOA $1C + constant GPIOA-LCKR ( GPIO port configuration lock  register )
GPIOA $20 + constant GPIOA-AFRL ( GPIO alternate function low  register )
GPIOA $24 + constant GPIOA-AFRH ( GPIO alternate function high  register )
GPIOA $28 + constant GPIOA-BRR ( port bit reset register )
: GPIOA-MODER. ." GPIOA-MODER (read-write) $" GPIOA-MODER @ hex. GPIOA-MODER 1b. ;
: GPIOA-OTYPER. ." GPIOA-OTYPER (read-write) $" GPIOA-OTYPER @ hex. GPIOA-OTYPER 1b. ;
: GPIOA-OSPEEDR. ." GPIOA-OSPEEDR (read-write) $" GPIOA-OSPEEDR @ hex. GPIOA-OSPEEDR 1b. ;
: GPIOA-PUPDR. ." GPIOA-PUPDR (read-write) $" GPIOA-PUPDR @ hex. GPIOA-PUPDR 1b. ;
: GPIOA-IDR. ." GPIOA-IDR (read-only) $" GPIOA-IDR @ hex. GPIOA-IDR 1b. ;
: GPIOA-ODR. ." GPIOA-ODR (read-write) $" GPIOA-ODR @ hex. GPIOA-ODR 1b. ;
: GPIOA-BSRR. ." GPIOA-BSRR (write-only) $" GPIOA-BSRR @ hex. GPIOA-BSRR 1b. ;
: GPIOA-LCKR. ." GPIOA-LCKR (read-write) $" GPIOA-LCKR @ hex. GPIOA-LCKR 1b. ;
: GPIOA-AFRL. ." GPIOA-AFRL (read-write) $" GPIOA-AFRL @ hex. GPIOA-AFRL 1b. ;
: GPIOA-AFRH. ." GPIOA-AFRH (read-write) $" GPIOA-AFRH @ hex. GPIOA-AFRH 1b. ;
: GPIOA-BRR. ." GPIOA-BRR (write-only) $" GPIOA-BRR @ hex. GPIOA-BRR 1b. ;
: GPIOA.
GPIOA-MODER.
GPIOA-OTYPER.
GPIOA-OSPEEDR.
GPIOA-PUPDR.
GPIOA-IDR.
GPIOA-ODR.
GPIOA-BSRR.
GPIOA-LCKR.
GPIOA-AFRL.
GPIOA-AFRH.
GPIOA-BRR.
;

Now you can write words in forth using names for things like GPIOA-ODR instead of having to write out $50000014 every time.  This can make the code a bit more friendly.

1 constant LEDA         \ PA1
%1 LEDA lshift GPIOA.ODR bis!

If you don't have the space (available memory) then you may have to resort to using the name only in a comment.  Here's where I clear some GPIO pins I've got wired to LEDs.  They are actually the cathodes of 7-segment displays, so off is a logic high or 1.

%1100011110010 $50000014 ( GPIOA.ODR ) bis! \ clear all by turning segment pins to 1

 As another example, here's the section regarding the ADC.

$40012400 constant ADC ( Analog to Digital Converter instance  1 )
ADC $0 + constant ADC-ISR ( ADC interrupt and status  register )
ADC $4 + constant ADC-IER ( ADC interrupt enable register )
ADC $8 + constant ADC-CR ( ADC control register )
ADC $C + constant ADC-CFGR1 ( ADC configuration register 1 )
ADC $10 + constant ADC-CFGR2 ( ADC configuration register 2 )
ADC $14 + constant ADC-SMPR ( ADC sampling time register )
ADC $20 + constant ADC-AWD1TR ( watchdog threshold register )
ADC $24 + constant ADC-AWD2TR ( watchdog threshold register )
ADC $28 + constant ADC-CHSELR ( channel selection register )
ADC $28 + constant ADC-CHSELR_1 ( channel selection register CHSELRMOD = 1 in  ADC_CFGR1 )
ADC $2C + constant ADC-AWD3TR ( watchdog threshold register )
ADC $40 + constant ADC-DR ( ADC group regular conversion data  register )
ADC $A0 + constant ADC-AWD2CR ( ADC analog watchdog 2 configuration  register )
ADC $A4 + constant ADC-AWD3CR ( ADC analog watchdog 3 configuration  register )
ADC $B4 + constant ADC-CALFACT ( ADC calibration factors  register )
ADC $308 + constant ADC-CCR ( ADC common control register )

: ADC-ISR. ." ADC-ISR (read-write) $" ADC-ISR @ hex. ADC-ISR 1b. ;
: ADC-IER. ." ADC-IER (read-write) $" ADC-IER @ hex. ADC-IER 1b. ;
: ADC-CR. ." ADC-CR (read-write) $" ADC-CR @ hex. ADC-CR 1b. ;
: ADC-CFGR1. ." ADC-CFGR1 (read-write) $" ADC-CFGR1 @ hex. ADC-CFGR1 1b. ;
: ADC-CFGR2. ." ADC-CFGR2 (read-write) $" ADC-CFGR2 @ hex. ADC-CFGR2 1b. ;
: ADC-SMPR. ." ADC-SMPR (read-write) $" ADC-SMPR @ hex. ADC-SMPR 1b. ;
: ADC-AWD1TR. ." ADC-AWD1TR (read-write) $" ADC-AWD1TR @ hex. ADC-AWD1TR 1b. ;
: ADC-AWD2TR. ." ADC-AWD2TR (read-write) $" ADC-AWD2TR @ hex. ADC-AWD2TR 1b. ;
: ADC-CHSELR. ." ADC-CHSELR (read-write) $" ADC-CHSELR @ hex. ADC-CHSELR 1b. ;
: ADC-CHSELR_1. ." ADC-CHSELR_1 (read-write) $" ADC-CHSELR_1 @ hex. ADC-CHSELR_1 1b. ;
: ADC-AWD3TR. ." ADC-AWD3TR (read-write) $" ADC-AWD3TR @ hex. ADC-AWD3TR 1b. ;
: ADC-DR. ." ADC-DR (read-only) $" ADC-DR @ hex. ADC-DR 1b. ;
: ADC-AWD2CR. ." ADC-AWD2CR (read-write) $" ADC-AWD2CR @ hex. ADC-AWD2CR 1b. ;
: ADC-AWD3CR. ." ADC-AWD3CR (read-write) $" ADC-AWD3CR @ hex. ADC-AWD3CR 1b. ;
: ADC-CALFACT. ." ADC-CALFACT (read-write) $" ADC-CALFACT @ hex. ADC-CALFACT 1b. ;
: ADC-CCR. ." ADC-CCR (read-write) $" ADC-CCR @ hex. ADC-CCR 1b. ;
: ADC.

ADC-ISR.
ADC-IER.
ADC-CR.
ADC-CFGR1.
ADC-CFGR2.
ADC-SMPR.
ADC-AWD1TR.
ADC-AWD2TR.
ADC-CHSELR.
ADC-CHSELR_1.
ADC-AWD3TR.
ADC-DR.
ADC-AWD2CR.
ADC-AWD3CR.
ADC-CALFACT.
ADC-CCR.
;

...

Like

Discussions