#stm32 blue pill: bare metal max7219 programming

I have now interfaced to a max7219 7 segment display using SPI and a blue pill. There’s not much for me to say about this. It worked first time! Working with I2C was something of a fight. SPI is significantly easier. Check out the project page for the code.

I hope that others take inspiration from my bare metal library. Getting things up and running can be a real trial, so it is always good to see other people’s solutions. There are sure to be bugs in my code. So if you find them, then let me know. Tell me what the fix is, too.

I am still contemplating the merits of using CMSIS. Configuring it looks to be a bit of a pain.

Posted in Uncategorized | Leave a comment

Switched to Debian Stable

Ubuntu 20.10 is due to hit the interwebs today. It is no coincidence that I chose this precise moment to switch to Debian Stable. Two months ago I was contemplating the desirability of just sticking with a long-term distro rather than upgrading Ubuntu every 6 months.

I hit snags with Ubuntu, I think it was 19.10. I had all sorts of problems with freezing, which manifested itself on a couple of machines of mine. I reverted to an older version of Ubuntu I had: 18.10 I think it was. The problem was that I wanted to install some new packages, but the distro was out of support, so that was not possible.

I decided to give Slackware and a derivative, Absolute Linux, another go. It was “mostly” successful. More about that later.

So when Ubuntu 20.04 came out, I was eager to install it. I had no stability problems, which is what you would expect from an LTS release.

I had my sources of dissatisfaction with Ubuntu, though. They had been caught with dubious business practises in the past. I’m also a detractor of Snap, a “technology” that Ubuntu seems to be increasingly enthusiastic about shoving down my throat. There have been a lot of electrons spilled over the merits and demerits of Snap, so there’s no real need to repeat them here. Suffice it to say that, as far as I’m concerned, it’s a cure that is worse than the disease.

Another thing that always bugged me about Ubuntu is that when I try to install it, it wants to download language packs. It’s not a deal-breaker, but why-oh-why, when I just downloaded a DVD’s worth of stuff, do I have to download more stuff for each machine that I install it on? Madness!

On top of that, there seems to be updates available after a fresh install. How can there be hundreds of megabytes of updates on a distro that isn’t even a day old? (That’s a rhetorical question, please don’t bother to answer).

As time progresses, more updates become available, and Ubuntu wants to let you know about it, too. No! I’ll install updates when I’m ready. My major beef with the direction that Linux is heading is that it is increasingly intent on setting policy. Well, that’s not the Unix way. Remember, the guys who created Unix were genii. You are not. Yeah, I’m looking at you Poettering.

Developers: keep your autism to yourself. Ask your manager if your attention deficit disorder is causing a nuisance to others. FFS

Would I recommend Ubuntu to people new to Linux? Yes! My philosophy is still: if you have to ask what distro to choose, then the answer is Ubuntu. Having said that, I have been hearing a lot of good reviews on Linux Mint, so I might well be tempted to give it a go. I had tried it in the past, many moons ago, but didn’t stick around. I can’t remember the exact reasons. I think it was because, at least at the time, they mixed and matched repo sources, which wasn’t the best for stability.

Slackware has no niche

As much as I have a fondness of Slackware, let’s fact an uncomfortable truth: there’s no point to it anymore.

It has now been over 4 years since Slackware made a release. If that works for you, then fine. The chances are, though, that it doesn’t work for you. Maybe, just maybe, it’s OK as a workhorse server that never needs new software and just serves email/webpages/whatever until the end of time.

You are likely to be better served by a different relatively static distro like Debian Stable, or hell, even Ubuntu server LTS.

For desktop users, Slackware is a bad choice. The lack of package dependency management (God forbid that I should say that Slackware doesn’t have a package manager) is a disadvantage. Slackers: you’re doing things the hard way.

But it gets worse. Dependency cascades can not only be tiresome, they can also be unreconcilable. If you have a GNOME component as a dependency, for example, then you are likely to land yourself in a world of hurt.

You may ask: what about Slackware Current? To which I would respond: yeah, what about Slackware Current? Surely, if you use Current, then you might as well admit that Slackware isn’t the distro you’re looking for, and switch to Arch.

My advice to the Slackware maintainers would be to try to make a release every 2 years. 2 years seems about right to me. You’re not chasing fads, but you’re not mired in the past, either.

And let’s be clear about this: although newer software isn’t necessarily better, it isn’t necessarily worse, either. There can, and often is, bugs in old software which more recent versions fix. If the software is relatively new (for example CDE a couple of years ago), then the improvements are likely to be large.

MATE is an excellent DE

I chose the MATE version of Debian to download. I have a ten-year old machine, so I want my DE to be fairly lightweight.

After the last couple of months of trying out MATE, I have to say that I rather like it. I don’t find it too “old”.

MATE actually strikes a well-considered balance between features and resource-usage. The MATE developers seem to have really thought through what they were trying to achieve. Well done, guys!

There are other lighterweight DEs worthy of mention: IceWm, LXDE, even Fluxbox, and I’m sure a few others whose names escape me right now. They do require some setup, however, and their functionality can sometimes be a bit picky.

MATE, on the other hand, gives me what I want, and then gets out of my way.

GNOME is a memory pig

GNOME goes in cycles with me. First I hate it, then I persist with it, declare it “not as bad as I thought”, and then go back to hating it again.

GNOME, meh, I kinda get it. Boy is it a resource hog, though. Yesterday my CPU fan on my experimental machine started spinning around like a helicopter. It seemed that “tracker” was hogging everything.

Maybe there’s a fix to it, I don’t care. I’ve got MATE on my main machine now, and made a mental note to myself that friends never let friends use GNOME.

Anything based on Java is a memory pig

If there’s a language that has no real reason to exist, it’s Java. It’s just one big bag of bloat, and yet one more slab-o-electrons to have to download and occupy by disk drive.

I do a lot of development work on MCUs (microcontrollers). There are a lot of development environments out there. The best is the Arduino IDE. OK, the IDE itself needs much to be desired. It’s a slow old dog to start, too. But it’s ecosystem is unsurpassed.

I’m going to be unequivocal about this: if you want to explore the world of MCUs, make LEDs blink and soforth, then get yourself an Arduino Uno. Actually, get yourself something like an Elegoo Basic Starter Kit. It costs less than an Uno, and comes with a host of devices, including instructions. I personally prefer the Nano. It has the same processor as the Uno , but plugs directly into a breadboard. That’s way more convenient than having it separate.

No doubt many of the pros out there are going to scoff at my suggestion, pointing out that you can get more powerful MCUs cheaper, etc., but I stand by my statement. The Arduino ecosystem is excellent. The community is widespread. If you want to delve under the hood, there’s nothing to stop you. The simplicity of the AVR makes it good to really get to grips with low-level stuff. Plus, as I said before, community support is excellent, so you’re sure to find plenty of resources and tutorials to help you.

I seem to recall that I installed the Eclipse IDE many years ago, and came to the conclusion that it was a stupendously slow piece of software. Unfortunately, you can get many IDEs that are based on Eclipse.

I’m thinking of IDEs like Atollic, or STM32CubeIDE, for example, which target the STM32 processors. They require colossal downloads, and run glacially. The Cube sometimes froze on me.

I am actually in the process of writing my own STM32 library, specifically for the “blue pill”. Progress is slow, and frustrating, but, well, I’m getting a lot of bases covered. I’ve got SPI, I2C, USART and PWM working. That’s quite useful! I haven’t got ADC or DMA working yet. That’s for later. Still, I can get useful things done even at this stage.

If I were a little smarter, maybe I would have used STM32’s official CMSIS and SPL installed. There seemed to be some fiddly setup involved, so I’ve just stuck with going down the route of starting out with a blank sheet of paper and building what I want from there.

It does raise the question as to how much fancy IDEs, wizards, and hardware abstraction layers help. From what little experimentation I’ve done on the Cube, my answer so far is: not so much. The problem is that if you don’t know what you’re doing before you used the wizard, then you’re going to be at least as baffled as to what’s going on after you’ve used the wizard.

One setup that did actually offer some promise was the combo of Microsoft Visual Studio and PlatformIO. Yes, that’s right, the Great Evil One has produced an IDE that not only works on Linux, but is relatively lightweight, too.

You can add PlatformIO from there, to which you can play with a whole bunch of MCUs and the platforms. Actually, MS Visual Studio seems to be the preferred way of using PlatformIO.

You can choose a board that you want to program, and are given a list of platforms that you can use. The Arduino ecosystem is available from there, as are a number of others such as mbed, Zepher, and loads more.

As you can probably gather, I am a minimalist at heart, so downloading these things tends to run against the grain of the way I usually work. I must say, though, that it seems a good option. Certainly, if you’re fed up with the limitations of the Arduino IDE, then you can install MS VS. This will give you a much better IDE, whilst still allowing you to work with the Arduino libraries.


It’s no wonder that Microsoft dominates in Office software when the best that freeware has to offer is LibreOffice. Why is it so slow?

One of the first things I did when installing Debian on my regular machine was to uninstall LibreOffice. Apt said that it would free up 1G of space. 1G! How does an office suite take up so much space?

In truth, it likely takes up more than 1G. There seemed to be a lot of dependencies that could be removed once LibreOffice was removed. The sheer bloat of this software!

I’m not saying that I’ll never have recourse to using LibreOffice, but I’ll cross that bridge when I come to it.

The internet is broken. Firefox lost the plot. I’m willing to try MS Edge

Like a lot of people, I’m becoming more disillusioned with Firefox. The sackings at Firefox, together with the ever-burgeoning salary of the head honcho don’t help.

But I think it started before then. I installed Firefox on my dad’s Windows 7 machine. It seemed to bug him on a regular basis to upgrade.

When I switched away from Ubuntu last year, I downloaded Firefox from its official download site. What I couldn’t do is figure out how to disable those pesky updates. In previous version of Firefox, I could.

It is still possible, but they sure do try to make it difficult for you to do so. I hadn’t gotten to the bottom of it though.

And here, my friends, is everything wrong with modern software development practises. Software is increasingly dictating policy. Listen up studmuffins, the way I see it, I’m the operator, I dictate policy – as is the Unix way – not anyone else. in my eyes, to pester me to update my system is an act of hostility.

That, and someone wrote on Reddit that he thought Firefox using 4G of RAM was a good idea. My main system doesn’t even have 4G of RAM. What? I should buy a new computer just so that I can use a webrowser?

Spleen venting near completion

You can probably see now why Debian is a better fit for me.

Debian Stable has a reputation for having really out-of-date software. After having used it for a couple of months, I find it does nearly everything I need.

A sensible approach is to have a stable system that covers most bases, and then install a few extra necessities as required.

Perhaps it’s a bit harsh to say that if Debian doesn’t have it then it is not worth having. OTOH, I found from my experimental setup of Debian that the extra stuff is usually not all that useful.

In fact, it has shaped the way I will approach things in future. I can install whatever fancy piece of software I want on my experimental machine, and if it proves useful after a couple of month, migrate it to my main box. Even if the software does prove somewhat useful, I can keep it off my main machine, and just it on my experimental box.

So, in summary, Debian Stable is quite a nifty little distro, and I haven’t found it “too old”. Hope that helps.

Well, that’s me done. I’m sure I’ve left out points that I wanted to cover. Feel free to do your own internal proofreading. I can’t be arsed. Unlike in Timothy Dexter‘s book, the luckiest man alive, I shall not be supplying a selection of commas and full stops at the end to allow the reader to distribute them amongst the prose as he sees fit.

Stay safe.

Posted in Uncategorized | Leave a comment

#stm32 blue pill: playing hard-coded audio using PWM

Keys: IRQ, PWM, audio, clock, timers

In this project, we play a hard-coded voice recording. It builds on
project 12. You should review that project to learn about things like timer prescalers, channels, and preloading of timer resets.

Skim through the source code to get a flavour of what it looks like, then read on for a discussion of how it works.

The recording is in file track.h, which encodes a RAW audio file at a frequency of 8kHz, where each sample (a volume reading) is an unsigned byte. There is a problem, though: an 8kHz frequency is within the human hearing range. If you use PWM at that frequency, then you will be able to hear this “carrier frequency” during playback. It’s very annoying.

The fix is to choose a higher frequency like 16kHz.
Although still technically within the range of human hearing, it is undiscernable.
Maybe the younglings out there will be able to detect it, but not me.

Doubling the frequency to 16kHZ creates some extra problems. One of them is in the interrupt handler. Our audio was sampled at 8kHz, but is played at 16kHz. So we must scale our lookup into the audio track by a factor of a half. So rather than our interrupt handler looking like this:

void TIM3_IRQHandler()
        // increment the PWM counter for when to set low
        static u32 idx=0;
        if(idx >= track_raw_len) idx = 0;
        TIM3->CCR1 = (u32) track_raw[idx];

        TIM3->SR &= ~TIM_SR1_UIF; // reset interrupt flag

it has to look like this:

void TIM3_IRQHandler()
        // increment the PWM counter for when to set low
        static u32 idx=0;
        if(idx >= 2*track_raw_len) idx = 0;
        TIM3->CCR1 = (u32) track_raw[idx/2];

        TIM3->SR &= ~TIM_SR1_UIF; // reset interrupt flag

Now, the track samples are an unsigned byte, so we set:

        TIM3->ARR = 255;

Recall that ARR is the Auto Reset Register. So the sample volume corresponds exactly to the duty cycle. Better quality audio is likely to be had at 10 or 12 bits; but that is not considered here.

Recall from the previous project that it implies that the timer prescaler must be set according to formula:

psc = fclk/ fpwm/(arr+1) -1

Here, fclk = 8MHz is the default system clock, fpwm is 16kHz as we decided above, and arr=255. Plugging these numbers in:

psc = 8000000/16000/256 -1 = 0.953..

I may have been able to get away with setting PSC (prescaler) to either 0 or 1, but what
I choose to do instead was increase the system clock to give me better timing accuracy.

This leads us down the rabbit hole of the Clock Tree, figure 11 on page 125 of the STM32 datasheet. I haven’t quite figured things out yet, but as far as I can determine, the blue pill has two clock sources: one internal to the chip, and an external oscillator 8MHz wired to the board itself.

Follow the traces in diagram below to see how I figured things out:

We want to use PLLMUL to scale up out clock. The clock starts out at the familiar default of 8MHz,
and then is divided by 2. I’m not sure why the MCU does this. If we then set PLLMUL to scale by 8,
then our resulting SYSCLK frequency will be 8/2*8 = 32MHz. We don’t mess with the other prescalers like AHB and APB1, so the final clock frequency of TIM3 will 32MHz.

We set up this clock in main():

void main()
        // change the system core clock from 8MHz to 32MHz
        RCC_CFGR |= 0b0110 << RCC_CFGR_PLLMUL; // PLLMUL 8X
        RCC_CFGR |= 0b10;  //PLL selected as system clock
        while(RCC_CR & RCC_CR_PLLRDY);
        RCC_CR |= RCC_CR_PLLON;
        SystemCoreClock = 32000000UL; // tell everyone about new speed


We adjust RCC_CFGR to set the PLLMUL to 8X (refer to the register so
see what multipliers can be used), and select the PLL.
Then, in RCC_CR we actually switch to PLL.

We need to set SystemCoreClock so that other timed services like
usart know how to compute the correct transfer rates.

Our revised figure to psc is therefore:

psc = 32000000/16000/256/256 -1 = 6.8125

Hence, in setup_timer() we choose:

        TIM3->PSC = 7;

As in the previous project, plug a speaker into pin PA6, and its other lead to ground.
Upload the sketch, and you should hear the words “that does not compute.”

I am happy with the way this project works. I actually prefer it to using a DAC like the MCP4921.
The output quality seems pretty good, and I have no real complaints.
It also means that I don’t have to fiddle with extra components, set up SPI, and soforth.
The output from the MCP4921 is often rather weak, and in need of amplification.
With the current setup all of that can be dispensed with.

Now all that remains for me to do is figure out how to read SD cards, then we can
build ourselves a little music player.

Posted in Uncategorized | Leave a comment

#stm32 blue pill approx. 440Hz wave using PWM

Dissatisfied with the offerings from elsewhere, I have decided to write my own STM32 bare metal library from scratch. It is shaping up nicely. I’ve gotten GPIO, delays, interrupts, PWM, I2C and SPI working, so I’m well on the way of having a truly usable lightweight library which eschews piling on layers of abstractions. I haven’t investigated ADC or DMA yet.

It has not been an easy task. There is no way I could have achieved this much without the many online tutorials, videos, peeks at code and a bit of reverse engineering. I’m getting better at using datasheets, but for me anyway, they are a bad place to start. They can be impenetrable if you don’t have some conceptual hooks on which to hang the details.

I hope that other people find my growing library useful, if for no other reason than to use as a cheatsheet, or come away thinking “ah, so that’s how you do it.” I focus specifically on the stm32f103c8t6 (blue pill). That keeps the layers of abstraction to a minimum. You may well find that my libraries have limited portability outside of it, though.

Anyway, today I wanted to enhance the PWM capabilities of a previous example by producing a 440Hz triangular wave. Here’s the code:

include <gpio.h>
include <timers.h>
include <usart.h>

define FREQ 440
define NSAMPLES 16

char msg[40];

void printi(u32 v)
  itoa(v, msg, 10);

void print_bin(u32 v)
  for(int i=0; i<8; i++) {
    print(i==0 ? " 0b" : "'");
    for(int j=0; j<4; j++) {
      putchar(v & (1<<31) ? '1' : '0');
    v <<= 1;

void TIM3_IRQHandler()
  // increment the PWM counter for when to set low
  static u32 count=0;
  if(count >= NSAMPLES) count = 0;
  TIM3->CCR1 = count++;

  TIM3->SR &= ~TIM_SR1_UIF; // reset interrupt flag

void setup_timer()
  gpio_mode(PA6, 0b1011); // output 50MHz, push-pull, alt function
  TIM3->CCER |= TIM_CCER_CC1E; // enable capture/compare   
  TIM3->CCMR1 |= TIM_CCMR1_OC1PE; // enable preload 
  TIM3->CCMR1 |= 0b110<<4; // output pwm compare mode 1 
  TIM3->CR1 |= TIM_CR1_CEN; // enable counter

void printn(u32 v)

void main()
  puts("12 pwm with interrupt");

  // setup interrupt 
  NVIC_ISER0 |= (1<<29); // position for   TIM3_IRQHandler 
  TIM3->DIER |= TIM_DIER_UIE; // enable interrupt (this wasn't in project 11) 

And here’s some commentary:

Attach a speaker with one terminal to PA6, and one to ground. This project builds on project 11.

As we saw in project 11, it is possible to produce a PWM signal of a given frequency and duty cycle. This project takes things a step further: varying the duty cycle over each cycle. It is therefore possible to produce DAC-like behaviour using PWM. I am excited by this prospect, as it means (potentially) that we can ditch DACs and just use PWMs.

The ingredients added to this project are:

  • interrupts, where we increment the timer’s CCR1 (capture/compare register 1) on each interrupt
  • use "preloading" of CCR1 (aka "buffering" or using the "shadow register") so as to ensure that CCR1 is only updated at the beginning of an ARR (Auto Reload Reset) rather than at some arbitrary time

Near the top of main.c, we have:

#define FREQ 440
#define NSAMPLES 16

indicating that we want to generate a signal of 440Hz, and discretise the output "volumes" over 16 samples. Our timer will therefore need to be of a frequency of 7040Hz (=440*16), which is 16X faster than in project 11.

In setup_timer() we have:


for convenience sakes, and we’re going to get TIM3->CCR1 incrementally on each interrupt.



We’re using an 8MHz clock. Recall that

PSC+1 = f\_clk/f\_pwm/NSAMPLES


f\_pwm = FREQ * NSAMPLES

yielding the code above.

Further down, we dispense with setting TIM3->CCR1, because we are going to do that in the interrupt. We do need to add the line:

TIM3->CCMR1 |= TIM_CCMR1_OC1PE; // enable preload

which will allow us to properly sync our new value of CCR1 with the ARR reset.

We initialise the interrupt:

NVIC_ISER0 |= (1<<29); // position for TIM3_IRQHandler
TIM3->DIER |= TIM_DIER_UIE; // enable interrupt (this wasn't in project 11)

Here is the interrupt:

void TIM3_IRQHandler()
        // increment the PWM counter for when to set low
        static u32 count=0;
        if(count >= NSAMPLES) count = 0;
        TIM3->CCR1 = count++;

        TIM3->SR &= ~TIM_SR1_UIF; // reset interrupt flag

Note that TIM3 is used by pin PA6 on channel 1 for PWM. We could, theoretically, use a different timer to change CCR1, but there is not point.

TIM3_IRQHandler() triggers with a frequency 7040Hz, and we increment CCR1 by 1 each time, cycling when we get to a count of NSAMPLES. Note that this produces a triangular wave rather than a sawtooth one. A pin’s maximum volume occurs when the duty cycle is 50%, so as we increase the duty cycle beyojnd 50%, the volume actually goes down again.

Note that the final line of the interrupt handler reads

TIM3->SR &= ~TIM_SR1_UIF; // reset interrupt flag

which resets the interrupt so that it doesn’t end up being a one-shot.

The full code is available in its project directory on github.

I hope you find it useful. If there’s really strong demand for this kind of material, I may think of switching to a more useful platform. WordPress is OK if you want to produce flowery posts, but it is hard work for technical writing.

Posted in Uncategorized | Leave a comment

Some of my missteps with MCUs (microcontrollers)

Mmm, Pi — Homer Simpson

It all started with a Pi2 (Raspberry Pi 2). They were so cheap that I just had to buy one. I never intended to experiment with electronics on a Pi2 – in fact I never did – I saw it more of a Linux server. I used it as an email server, web server and as a development platform. When I was bored at work I used to log into it remotely via ssh and do some programming work.

Python was painfully slow. Go was better, but was quite laggy. When I pointed this out to Go advocates, I was treated with scorn. Actually, I found Gophers to be like Progressivists: full of contradictions, and always espousing the opposite view to the one I was presenting. “Go is a systems programming language”, “Of course Go is slow, it’s not a system programming language,” and so on. With the benefit of hindsight, we all now know that Go never was, nor never shall be, a systems programming language. Its garbage collection ensures that it won’t be.

Then I got a Pi3 because the Pi2 was really slow. It was still cheap, and I could do lots of programming stuff with it.

I still use the Pi2, where it is performing stalwart work as a git bare master, NAS server, and internal mirror of my github website. People might balk at the idea of using it for any of these purposes, but hey, it works for me. It connects to my network headless using a Wifi dongle. I try not to interfere with it too much, as I want it to work, I don’t want it going down. So don’t bash the little Pi2, it still has its uses.

I had the Pi3 for a long time before I tried my first bit of electronics via a Cam Edukit. I wouldn’t say it got me “hooked” onto electronics at the outset, it was more of a gradual descent into the world of electronics and microcontrollers.

I do not have a Pi4. I put an indefinite moritorium on buying new computers. My main desktop is a 2010 Dell, I have an ASUS Vivo as a test machine (originally purchased as a server), a Pi3, and Pi2. I figured that ought to be enough for anyone, despite the increasing age of the systems.

Numero Uno

Eventually, I decided to play with MCUs. I went for the obvious choice: an Arduino Uno. In fact, I bought the Elegoo Basic Starter Kit. It contains a Uno clone, plus LEDs, resistors, breadboard, switches and other goodies for you to play with. All for the price of an official Uno. I heartily recommend such a kit for anyone looking to get into the world of MCUs. An Uno by itself is of little use. You need other gadgets to control: hence its name: microcontroller.

I personally prefer the Nanos to the Unos. The Nanos plug straight into a breadboard rather than forcing you to have wires trailing from the MCU to the breadboard. The Nano is a no-brainer in that respect.

The AVR micros are comparatively expensive, mind. It is possible to get vastly more powerful MCUs much cheaper than Unos. Arduinos pick up a lot of flak for this and their IDE. True, maybe, but I will say this: there is no better way to begin to explore the world of MCUs than through Arduino.

I have heard some commentators say that it really doesn’t matter which MCU you choose initially. My advice would be more definitive: choose an Arduino! More specifically: an Elegoo kit.

Arduinos are much more beginner-friendly than any other MCU might care to investigate. This is important! As is community support. I usually downplay the importance of support, but for things like Raspberry Pis and microcontrollers it is a significant bonus.

I also want to present counter-arguments to criticisms of Arduino’s IDE, and how it encourages the “perpetual beginner.” Although the Arduino programming environment does present a pleasant layer of abstraction over the hardware, is that really any of a “worse” interface that you get with more “professional” tools? I mean that you don’t see what goes on “under the hood” with the so-called pro tools any more than with the Arduino IDE.

Furthermore, it you really do want to peek under the hood, there’s nothing really stopping you with the Arduino. In fact, I argue that the Unos are a great learning resource if you want to take a deep dive into the workings of MCUs. This is for two reasons:

  • Unos have a comparatively simple architecture, so they are a great way of learning the basics of how MCUs work. The datasheet even has some example C code.
  • Community support, again. You’ll find more tutorials and Youtube videos explaining how to do things than with other MCUs.

Bloat-tastic IDEs

Although the Arduino IDE might be a bit clunky for my, and many other people’s tastes, I find it to be less finnicky than the offerings by other vendors.

I’m old-school. I think that a “development environment” should consist of a set of headers, a library, vim, and a Makefile. There’s a project called avr-libc that seems in-line with this desire. It is available for Debian, and is likely available for All Good Distros. If not, then you can download it and compile it yourself. I haven’t investigated the library in detail yet, but it’s on my list.

In contradistinction with this are offering like CubeMx, Attollic, ESP’s free SDK, and so on. Typically, they’ll provide you with an IDE. That IDE might be based off Eclipse. Eclipse slows my system to a crawl. I also found CubeMx to be too “automagical” to my liking. It’s got its wizards, which makes it hard to determine what’s actually going on.

Or, if not for that, it’s the use of Python. ESP’s SDK seems to use “kproject” and CMakefile. What’s a kproject? There’s configuration files, and Makefiles that link to other Makefiles. It’s all an interweaved mish-mash of dependencies.

And the SDK wants to download and install other subsystems. But I already have a compiler. Why can’t you just use that?

I’m not singling out just ESP, you understand, they’re all like that. Mbed is similar. It seems to depend on incompatible Python modules, though. Fortunately mbed is available online via a web interface, so at least that’s something.

Then there are the free offerings, like PlatformIO. PlatformIO seems to rely on Atom (?). I don’t really know what PlatformIo and Atom are unrelated. As far as I can tell, Atom is a bloated editor. PlatformIO wraps more bacon around the pig, and wants to download the compiler for you particular MCU. At least that’s what I think is going on.

See what I mean? What does all this crap supposed to do? It reminds me of the time I was working at the then-Logica. This was the late-90’s, at the height of the tech boom. I had programmed in C on a Unix system for most of my time there. That meant Emacs and the GCC. I then worked on a small project on Windows using Microsoft Visual C++. My project manager – who thought that anything less than Lisp was doing things the hard way – commented on the vast number of files that Visual C++ automatically generated. “What are all these files ‘for’?” He had my sympathies on that.

Internet of Tripe

Back to more recent history. After playing with a Nano, I heard that ATTiny85’s were a good way to shrinkfy projects. I do like the 85’s, and built a few projects with them. I found that they were much easier to flash using a custom-made board for the Pi than using an Arduino as a programmer. My Arduino programmer always seemed a bit finnicky. The Pi programmer was reliable.

Although I admired the 85’s, their tedious flashing routine and lack of readily-availabe serial port meant they were a fiddle to work with.

The I discovered Espressif, with their ESP8266’s and ESP32’s. Wow, what fantastic MCUs they are. It’s like you went to hire a boat, but were handed the keys to a yacht. Plus they had built in Wifi, all for about 5 quid. Amazing!

When you have Wifi, it seems almost impossible that you can conceive of anything without Wifi. I had a cheap RTC clock, the infamous “Tiny RTC”. I got a pack of 5 of them for a couple of quid. Bargain! Except the Tiny RTC, based on a DS1307, has all the accuracy of an hour glass. The DS1307 is not especially accurate. I’m given to understand that the Tiny RTC is badly designed, meaning that the module is pretty useless. It was out by a minute a day. Pretty useless if you want to build a long-running clock.

I followed the advice of adding fudge-factors to the timing. After a bit of trial-and-error, I managed to come up with decent timings over a longish period.

It occurred to me that, with the ESP, I could sync the clock with a time-server over the interwebs. Thusly, I created a project which I imaginatively called “dev07”:

“dev07” in all its gory

With the benefit of hindsight, I should have allowed for space around the ESP’s antenna. I couldn’t be arsed trying to write my own routine to convert UTC to local time, so I hit upon the idea of getting my Pi2 to act as a server of local time. The ESP then periodically syncs with Pi2.

I eventually replaced the project. I dragged it out today in order to take a piccy of it. The row of lights down the bottom is a “blinkt”, which I use as a bank of status lights. The status of the lights demonstates that the device is properly powered (I had been having problems with brownouts and Watchdog resets), it is talking to the DS1307 clock correctly, but that WiFi and NTP is not working. Well, the NTP not working is easily attributable to the WiFi not working. I suspect that I have long disabled the time server on my Pi in any case.

What did I learn?

Lesson 1: don’t buy crap. The need for WiFi and status lights for a simple project should really have been an obvious indicator that the Tiny RTC was unusable junk, and that I would have been better off with a much more usable module. So I went and bought a DS3231, which has vastly better precision. It cost more, but then again, at least it was fit for purpose.

Lesson 2: the internet is unreliable: really unreliable. Especially WiFi. All sorts of connection issues can occur, and caused all sorts of complications for me. The fact that I felt the need for status lights on such a simple projects should have been an additional “smell” that I should have ditched the DS1307 ages back.

Fortunately, I am only making these projects for hacking purposes for little money. People that have spend beaucoup dineros on fancy devices are hostages to the vagaries of internet availability and the vendor still maintaining its servers. Plenty of companies like Yahoo, Google, etc. get bored with their products and pull the plugs. Smaller companies – the ones you bought your IoT device from – are in an even more precarious position.

Just say “no” to the InternetOfShite. It’s all for vendor-lockin and information harvesting, you know.

With those lessons learned, I built a new device, “0clock”:

Nice woodworking skills and creative use of sellotape. Not.

I still use it to this day. You see that it powered by our old friend, the Arduino Nano. It uses a vastly more accurate DS3231 clock, which should be accurate to within a couple of minutes a year. I suspect it is even more accurate than that, judging by how long I have kept it running. It also adjusts for daylight saving.

You may be asking if all this was worth it. If I’m being logical, then the answer is “no”. However, it was fun to make, I learned a lot. It also survives the frequent power outages we have around our neck of the woods, and doesn’t need pesky time resets when daylight savings starts or stops. And another thing…

Lesson 3: don’t throw away old projects. The board I used there was from a previous attempt at building a clock. There is some pins there where the original DS1307 was. The board was kept in a drawer for a long time, and I figured that I would salvage some of its components one day. In fact what happened was that I decided to resurrect the board. I snipped some wires to accommodate the DS3231 instead of the DS1307, and soldered in some wires for the switches on the segmental display. They don’t have any functionality at the moment, but I figured that whilst I was creating chaos with my soldering iron, I might as well wire up the switches properly.

The lesson of “don’t throw away old projects” has happened with other boards I built for different projects. You shelve them, but from time-to-time you think to yourself “you know, I’m actually going to use that project right now.

Lesson 4: is any project really finished? I find that no project is ever really “finished”. I build something, and then can see ways of improving it, or combining it with other functionality. Sometimes you have to draw a line and say that something is “good enough”.

My recent toys: STM32 and STM8. Baring all

Not so many months ago I discovered the STM32 “blue pill” ARM boards. They’re pretty neat, being a lot cheaper, and more powerful than the Arduinos. Initially I programmed them with the Arduino IDE. At the moment, I’m getting things done using ST’s online mbed programmer.

Being disillusioned with what is available, I am trying to go down the “bare metal” route on the STM32. It is a long slow journey, but I’m making progress. I’ve got blinking, delays, interrupts and USART working to my satisfaction. There’s still SPI, I2C and PWM to do yet, but once I’ve got those down, I figure I’ll have a very usable library.

In tandem, I bought a couple of STM8S’s. Online resources are a bit thin on the ground. But I’ve got USART, blinking, delays and interrupts working, in addition to SPI master and slave. So, some good progress. I’m finding the STM8’s to be easier to program than the STM32’s. The former is a much simpler chip. It’s a cheaper chip. Size-wise, it seems between an ATTiny85 and a Nano. I think there are a lot of possibilities with this chip, and it may end up being one of my favourite chips for projects that don’t need a lot of grunt.

When I have libraries working to my satisfaction, I fully intend to tell the community. The way I see it is they won’t be the best that you can get. But they will at least be functional and act as a springboard and learning exercise for those that want to experiment with the chips. Compilation is simple is speedy; no need for Python tools, submodules, or any of that. Just libraries that can be built in seconds and linked in simply, keeping abstractions to a minimum, with no complex infrastructure.

That’s all for now. I hope you found it interesting.

Posted in Uncategorized | Leave a comment

Did the bell ring?

Six months ago I wrote an article “Has the bell rung at the bottom” in which I wrote:

I am now going to commit an act of utter insanity: by predicting that the market is near the bottom. To quantify that as a specific prediction: the FT100 is within 10% of its 2020 low, and will be higher in 6 months than it is now.

How well did I do? At the time the FT100 was 5091. It is now 6007: increase of 18%. It reached a year low of 4993 on 23 March, just 4 days after my prediction. That is 2% lower than when I made the call, and well within the 10% margin I gave myself.

Needless to say, I am dead chuffed with such an oracular call. What prompted me to stick my neck out was that some scribe from Barron’s wrote:

Fear Grips Markets Again. The Only Safe Place Is Cash.

This is from a news outlet that bills itself as “a leading source of financial news, providing in-depth analysis and commentary.” According to MarketWatch, it has been “delivering market-beating stock picks and investment advice since 1921.”

Hmm, maybe not so much. All of this was a red rag to a bull (no pun intended) to me.

As ever, there is always the possibility of making a cock-up in the bravado department. But sometimes a signal is so loud that the odds weigh heavily in one’s favour.

I did not gain personally out of this, as I was fully invested at the time anyway.

Stay safe out there.

Posted in Uncategorized | Leave a comment

Let’s build an oscilloscope with #raspberrypi and #stm32

It’s possible to build a simple oscilloscope with an Arduino Uno, its standard serial port and the serial plotter. I wanted to see if I could take things to the next level and incorporate (the beginnings) of data capture and analysis, and be faster, too.

For this project you’re going to need a Raspberry Pi and an STM32 microcontroller. The Pi does the logging, and the STM32 acts as an ADC (Analogue to Digital Converter). You could use an off-the-shelf ADC module, but I happened to have an STM32 Blue Pill (STM32F103C8T6). You’ll also need the Arduino IDE with the stm32duino libraries installed.

Another good candidate would be an ESP32-WROOM in place of the STM32, but I have only provided code for the latter chip, not the former. You could also use an Arduino Uno/Nano, or some other flavour, but remember that these operate at 5V, and are therefore incompatible with the Pi unless you use logic level shifters, or maybe voltage dividers.

We use the SPI protocol to communicate, with the Pi acting as master, and the STM32 acting as slave. We can also use the Pi to generate tones to feed into the “capture card” (i.e the STM32).

To get the code, clone my project over on github:

git clone https://github.com/blippy/rpi.git

In directory stm32/adc-interrupts, there is an INO file. Compile it and upload it to the STM32. It’s quite a short program (although it took me long enough to figure out!) and uses interrupts to capture the ADC data.

In a terminal on the Pi, cd to directory rpi/oscope. All the action will happen there. Hookup the Pi to the STM32 pins as follows:

      Pi  STM32
MOSI  19  PA7
MISO  21  PA6
SCK   23  PA5
SS    24  PA4
SAMP  35  PB1

Make sure that the devices are also connected by a common ground. Plug a wire between one of the ground pins on the Pi with a ground pin on the STM32.

As you might expect, MOSI, MISO, SCK and SS are for the SPI communications. You can probably dispense with the MOSI connection.

The row labelled SAMP is the sampling pin on the STM32. That’s where it’s ADC is set up. Any external voltage source that you want to sample should be fed into this pin instead of pin 35 from the Pi.

There’s nothing special about pin 35. However,I have provided the file tone.py that generates a 440Hz squarewave with a 50% duty cycle. So connect pin 35 to PB1 and run

python3 tone.py

Let’s see what it looks like on our oscilloscope, shall we? Type


which both runs capture.py and plot.py. Later, you can experiment with both files. The plotter is actually quite slow, but after a few seconds you should get a plot that looks something like this:

Groovy! This was achieved by setting

spi.max_speed_hz = 500000

in capture.py. Let’s spice things up by creating an RC (resistor-capacitor circuit). An RC circuit looks something like this:

The bottom terminal points in the diagram represents ground, whilst the upper two are positive voltages. Take the lead out of PB1 and put it into “Vin”. Choose values for R and C. I chose 220 ohm for the former, and 2.2uF for the latter. It gives a nice result. Attach “Vout” to PB1. Remember to ground the capacitor.

Run ./oner again, and you’ll get an output that should look like this:

The signal now approaches a triangular wave. Experiment further, and see if you can produce a better triangular wave, or perhaps a sine wave. It will involve having two RC circuits in series with correctly-chosen values.

Further directions for the project would be:

  • to have continuous capture. However, I haven’t found any graphing software that’s fast enough yet.
  • to be able to set trigger points. It would be nice to see switch bouncing, for instance
  • Fourier transforms

Go and explore!


If you want to change the capture frequency, then the best way is likely through changing


in capture.py. Reduce the speed if you want a slower capture, increase it if you want more samples. It is also possible to introduce a delay between fetches. There are some limits on the capture speeds, bounded by:

  • ADC conversion time. I’ve discusses various tradeoffs here. It currently takes the sample rate is set with ADC_SMPR_55_5, which equates with 175kHz. Pretty good
  • Transfer rate between STM32 and Pi.
  • Speed of the python program itself. Capture frequencies maybe adversely affected by the fact that python is a slow interpreter

Experimentation would be required to see how far the data capture rates could be pushed. Suffice it to say that you do not get anywhere near the 175kHz sample rate quoted above. Reimplementing the python program in C++ might be a good first step in upping the rates.

Update 2020-07-23

Choosing the SPI rate

As of today, you can choose the SPI capture rate in python.py. The question is: what SPI rate should you choose for what “real” frequency?

Rate table
       -r    us   Actual     FF
    1,000  4755    210Hz    4.8
    5,000  3596    278Hz   18.0
   10,000  1842    543Hz   18.4
   50,000   389   2570Hz   19.5
  100,000   210   4751Hz   21.0
  200,000   121   8264Hz   24.1
  300,000  90.8    11kHz   27.3
  400,000  81.5  12.2kHz   32.8
  500,000  68.5  14.6kHz   34.2
1,000,000  36.5  27.4kHz   36.5

The above table was compiled from actual samplings on a Pi3. They are indicative, and are probably inapplicable on something like a Pi4 or Pi2.

-r is the SPI speed parameter passed to either oner or capture.py The default speed is 1,000,000 Hz, but you can slow down the speed of SPI by issuing a command like:

oner -r 50000

which, obviously, sets the SPI rate to 50kHz. Slowing down the rate will obviously reduce the number of samples you take per second, which is actually an advantage if you want to sample lower frequencies.

us is the actual sampling time in microoseconds. So, setting the SPI rate higher reduces the time for each sample, as indicated in the table.

Actual is the actual sampling rate, as measured in Hz. It’s just the us converted into an equivalent frequency.

FF is the fudge factor that you need to apply to convert your desired actual frequency to the SPI value.

You will notice that there is not a linear relationship between the actual frequency of measurement and the SPI rate. This is because the python code takes time to process a loop, the Linux kernel may interrupt processes, and so forth.

The table is just a guide. If you want 100 samples at around 11kHz, then you would use a value of -r or 300000. I have given some examples below.

Example 1

You want to capture input at a rate of 8kHz, which may be generated from, say, audio output. A value of -r of 200000 would be suitable. Look at the actual rate column, and cross-reference it with a -r value. Alternatively, you can apply a fudge factor of, let’s say, 25, which will give you the same value. You will end up with a sequence of 100 samples (the default) taken at approx 8kHz.

Example 2

Suppose you have a signal which you estimate has a frequency of 440Hz. You want to take 20 samples over that period. In other words, you want a sampling frequency of 20*440 = 8800Hz. So, here again, choosing a SPI rate of 200000 is about right.

You should be able to capture approx 100/20 = 5 waves for a 100-sample capture. You can increase the -r value for a finer-grained measurement of the wave, which will reduce the number of waves you capture. And vice versa.

I hope that makes sense.

Update 2 2020-07-23: investigating speedups

Dissatisfied with the speed as it stood, I decided to try a test using C++. At the 1MHz speed, I was getting reads in the range 37us-53us. The results were highly variable! It means that the Python code wasn’t likely to be the problem. Who knew Python could be so fast?

It turns out that reading in words (uint16_t) in blocks of 100 instead of a word at a time approximately doubles the transfer rate. I estimate that the time per read is 17us, maybe going down to 16us if one reads in blocks of 200.

This is close to the theoretical maximum of SPI transferring at a rate of 1MHz, which is 1E6/16 (bits) = 62.5kHz. A time per read of a sample 17us implies a sampling rate of 1E6/17 = 59kHz.

So, reading in blocks of 100 effectively maxes out the SPI, and the only way you can increase the sample rate is to increase the SPI rate. I haven’t yet found out if this is feasible.

In the STM32 I have set the ADC sample rate to ADC_SMPR_55_5 (see here), giving an ADC conversion rate of 5.7us, which equates with 175kHz.

So maybe there could be more efficiency gains on top of that. More work would be required.

Posted in Uncategorized | Leave a comment

#rakulang grammar hints about creating a templating system

Raku’s grammar system is very powerful, but often mystifying and frustrating. Some worked examples go a long way. This post isn’t a “how to create a templating system”, it just contains a few hints on getting started.

It turns out that a simple system is easy. I have in mind a way of extending my assembler written in Raku with a very naive substitution system. This will enable me to write assembler at a higher-level. Really, it will land me a third of the way towards BASIC.

Where might I be going with this? Well, instead of specifying a grammar for BASIC as is normally the case, I had an interesting idea of creating a kind of extension mechanism inspired by Forth. So you have a kind of BASIC, but with a plug-in extensible grammar system.

I’ll describe how such a curious feat might be achieved in a later post, assuming I decide to implement it. But I have digressed too far already. so, back to the topic at hand.

What I’m trying to achieve in the first instance is that you may have triggering syntax embedded inside general text, and you want to perform actions on this special text.

The example I chose to work with is wherever I see the word IF, I want to replace it by --GOT IF--, and where I see the word IFX, I replace it with ..GOT IFX.. . All other text should be left alone. So, pretty simple, but not necessarily easy to figure out how to achieve if, like me, you’re new to Raku’s grammars.

It turns out that the implementation (available as a gist) is not difficult at all:

grammar G {
	token TOP { <atom>* }
	token atom { 	IFX { print "\n.. GOT IFX..\n"; } 
			| IF {print "\n--GOT IF--\n"; }  
			| .  { print $/; }

my $str = "heIFllIFXo\n";


# outputs
# he
# --GOT IF--
# ll
# .. GOT IFX..
# o

There we go, simple as that. Some points to note: I have defined inputs as being made of “atoms”. An atom is either one of the keywords, or anything else. You can see token atom selects from a list of alternatives via the | operator.

Note also that the action that we perform is dependent on the actual match that succeeded. This facility may be obvious to some, but I didn’t know about it until a couple of days ago. I had always assumed that the action was based on the whole token. Like I said before, Raku grammars have a lot of stuff available, but it’s not necessarily obvious for the beginner. Trying to wade through a morass of documentation often doesn’t help. It is often tempting to ditch the whole approach and just hand-roll a grammar.

Note that IFX is define before IF, because if they were defined the other way around, then you could only match IF, and not IFX. For the same reason, the . , which matches anything, is defined last.

A final point to note is that $/ represents the “current match”. In the context of the code above, if we don’t match IF or IFX, we match something else, which we just echo out.

Great! Now go and implement the Unix tools m4 and groff in Raku. Whilst you’re at it, be sure to add programmability, a la awk.

That’s all for now. Stay safe. May you all be happy.

Posted in Uncategorized | 1 Comment

#rakulang An assembler for a virtual machine

I have implemented a Forth, a BASIC, and a formula interpreter for a spreadsheet. The idea of a virtual instruction set keeps cropping up from time-to-time with me.

In this post, I shall be looking into my github repo bennett . It contains source code from the book “Introduction to Compiling Techniques: A First Course using ANSI C, LEX and YACC”, by JP Bennett. I have made my own modifications to improve compilation on modern hardware, as well as re-implementing the assembler and disassembler in Raku (formerly Perl6, of course).

The purpose of the book is to implement a Basic-like language which supports recursion. The assembler is therefore a perfectly capable one. The assembly language consists of 15 instructions: HALT (to stop the machine), NOP (does nothing), TRAP (outputs a byte), ADD/SUB/MUL/DIV (arithmetic), STI/LDI/LDA/LDR (storing/retrieving values) and BZE/BNZ/BRA/BAL (branching).

The major limitations is that it does not take in keyboard input, and doesn’t have a facility for incorporating foreign functions. These shouldn’t be too difficult to add, especially the former.

The repo does have a C implementation of the VSL (Very Simple Language, i.e. a form of BASIC, which compiles to assembler), VAS (assembler, which compiles to machine instructions), VAM (a virtual machine executer).

Raku enthusiasts may be more interested in the stuff that I’ve implemented for Raku, though. It is available in the myvm directory . asm.p6 is the assembler, and disasm.p6 is the disassembler. It is satisfying to note that the assembler takes only slightly more than 100 lines to implement using Raku’s grammar facilities. The line count could be reduced further if offsets were stored in little-endian format rather than big-endian. I chose to stick with Bennett’s way of implementing things, though, for compatibility.

If you want to test things out, then compile the top level directory:

autoreconf -iv
cd examples

The examples will compile to VAS and VAM using Bennett’s implementations. You can write your own assembly programs, but you’ll probably want to use the examples in the first instance. If you are in the examples directory, and have already issued a make in accordance with the instructions above, then you can prove that the Raku assembler works by issuing a command like:

../asm.p6 fact.vas

This will create a file called out.vam, which should be identical to fact.vam. You can run the compiled code by issuing the command:

./vc out.vam

The next step might be to implement the virtual machine in Raku, and a little more ambitiously, the parser for VSL.

Some directions I personally might want to take:

  1. see if the assembly language is a good base implementation for a Schorre Meta-II compiler. That would be an interesting project.
  2. see if I can implement co-routines or continuations in an easy fashion.

If there’s significant interest, I’ll post some details on Bennett’s instruction set, and what’s going on under the hood of the compiler. The repo does contain some info in vam.txt for those interested in writing some assembler.

That’ll do for now, though.

Update 2020-06-30

I’ve made a few exciting updates to the assembler and VM. Surprisingly perhaps, Bennett’s VM didn’t have an N flag, to test for negativity. There were no branch instructions based on this. So you could only branch unconditionally, on zero, or not-zero.

Consequently, I have added BLTZ (branch on less than zero) and BGTZ (branch on greater than 0).

I have also added a SYS instruction, in order to make system calls (“interrupts”). The format for it is:


where n is the call number. Currently there is only one system call you can make: 0 (for getchar()). You can extend the VM to make other calls, though.

See examples/echo.vsl , which copies stdin to stdout. Here is its implementation:

\ added by mcarter 2020-06-30
\ test of syscalls
\ this just echos stdin to stdout

        SYS 0           \ getchar into R15
        LDR R15, R15    \ check for EOF
        BLTZ fin        \ if EOF then finished
        BRA loop

You can read more about the VM instructions here.

Posted in Uncategorized | 1 Comment

Father’s Day Card

Just a little watercolour card I made for Father’s Day on 21 June.


Posted in Uncategorized | Leave a comment