One of the more attractive features or the STM32 processors, at least in their more recent revisions, is the presence of a built-in serial bootloader. JTAG is all very well but you need extra hardware and then software to drive it. If you want to do in-circuit debugging, it is ideal. However, for my use, I don’t much want a 20 pin header and I find the debugging feature for a micromouse to be a little redundant. Debugging can be done on a static test rig. But then, if I want to update the mouse software, a serial bootloader is perfect and I can use the same serial connection to communicate with my code when it is running

The STM32 has two pins which can be used to determine where the processor looks for code when it starts up. These are BOOT0 and BOOT1. The following table gives their function: [Corrected 16 May 2009 – PH]

BOOT1 BOOT0 Boot Mode
X 0 Boot from user flash
0 1 Boot from System Memory
1 1 Boot from embedded RAM

If we forego the option to boot from RAM, we can hold BOOT1 low and add a switch or jumper to BOOT0. If BOOT0 is high after a reset, the processor boots from system memory which, as delivered, means the bootloader. Holding the pin low boots from the on-board user flash and executes our code.

You can find a full description of the bootloader in the ST document AN2606

ST provide a flash loader application both as a Windows GUI application and as a command line program. Both versions are really just too much like hard work and you won’t use them much before looking for a better way. And there is one. It is a Python script originally written by Ivan A-R and available from here:

The page is in Russian but the comments and help are in English. The version there seems to have a small bug relating to writing a file containing the contents of the chip. I found a fix for that but can’t remember the location of that page so here is a revised version that I have used to successfully write and read the STM32F103RBT6 devices on my test boards.


My development platform at present is a Mac. That means I get Python already present on the system. If you have a Linux box, it may well have Python installed or it will be easy to do so. It can also be readily installed on Windows. Be sure to get Python 2.6.x as there are syntax differences that will stop the script from running in Python 3. Have a look here:

Before you can use the script, you will also need to get the PySerial library from here:

This is needed so that Python can talk to the serial ports. Once you have the script, run it with the –h switch to get some help: [-hqVewvr] [-l length] [-p port] [-b baud] [-a addr] [file.bin]

    -h          This help
    -q          Quiet
    -V          Verbose
    -e          Erase
    -w          Write
    -v          Verify
    -r          Read
    -l length   Length of read
    -p port     Serial port (default: /dev/tty.usbserial-ftCYPMYJ)
    -b baud     Baud speed (default: 115200)
    -a addr     Target address    ./ -e -w -v example/main.bin

My script defaults to the normal location of my serial port. You will need to change that to suit your circumstances or use the appropriate switches. Note that the loader only understands binary files, not hex files. If you are using another toolchain you will need to get it to create binary files. If you have a hex file, it can be converted to a binary file with the following command:

arm-eabi-objcopy -I ihex -O binary data.hex data.bin

It is also necessary to erase the chip on each write so your typical write command looks like this:

stm32loader -evw data.bin

The script will know how much data to transfer and will default to writing it to location 0x08000000. Reading data requires that you tell the script how much you want and the name of the file you want it stored in.

stm32loader –r –l 0x1000 data.bin

To use the loader

  • set your switch/jumper to make BOOT0 high
  • press the reset button on the board
  • run the script
  • wait until it is done
  • set the switch/jumper to make BOOT0 low
  • reset the board to run your code

Once in loader mode, the chip waits for data from the computer and does an autobaud detection to determine transfer speed. Sometimes I seem to need to do this a couple of times before it works. That may be because I would be better off with a lower speed. Next we need to write something worth putting in the chip. Stay tuned.

This Post Has 16 Comments

  1. poine

    looks like you are mistaking boot0 and boot1 in the last part of your article

    apart from that, thanx alot

  2. peteh


    Thanks for pointing that out.

    The error is actually in the earlier part of the item. The instructions at the end are correct.

    I have updated it all to be consistent.

  3. ekawahyu

    Does the built-in serial bootloader work like TSR? Or we should press reset to call it back?

  4. peteh

    It is always there. Look in the text to see how to use it. You need only to set a pin level on the processor and it starts up on reset, listening to the serial pins.

  5. Geoffrey McRae


    Just thought I would drop a link here to a C based GPL 2.0 licensed flash program I have been developing, I did find your flash program first, but it relies on perl which I prefer not to use on my system.

    If someone wants to write a serial_windows.c driver for it, it will also be cross platform. Supports a few things your perl script doesn’t such as device model detection, Intel HEX parsing, and resume already initialized connection support.

    Still to come are the protection functions, but it is already usable in its current state.

  6. Geoffrey McRae

    Just realized its Python, not Perl, but still, a scripted language which I would prefer to avoid.

  7. peteh

    This is a handy looking utility. Thanks for sharing.

  8. Andrei

    Hi, I have downloaded your but it doesn’t seem to work in my Ubuntu machine. I am getting this errors:


    andrei@Tux:~/Desktop$ ./ -evw ET-STM32F103.bin

    Bootloader version 22 Chip id `[‘0x4’, ‘0x14′]’

    Write 256 bytes at 0x8000000

    Write 256 bytes at 0x8000100

    Write 256 bytes at 0x8000200

    Write 256 bytes at 0x8000300

    Write 256 bytes at 0x8000400

    Write 256 bytes at 0x8000500

    Write 256 bytes at 0x8000600

    Write 256 bytes at 0x8000700

    Write 256 bytes at 0x8000800

    Write 256 bytes at 0x8000900

    Write 256 bytes at 0x8000A00

    Traceback (most recent call last):
      File "./", line 414, in cmd.writeMemory(conf[‘address’], data)
        File "./", line 289, in writeMemory self.cmdWriteMemory(addr, data[offs:offs+256])
          File "./", line 175, in cmdWriteMemory self._wait_for_ask("0x31 address failed")
            File "./", line 73, in _wait_for_ask raise CmdException("NACK "+info)
                NACK 0x31 address failed


  9. peteh

    I am afraid I do not use this script any longer as I have changed my toolset. However, the error you see comes from the script itself rather than the system. It seems to be a communication error with the bootloader code in the target chip. You might try a lower speed and/or a smaller binary image first.

  10. Anonymous

    I found an error in the script. In fact it reads the file in text mode instead of binary mode so, when it reaches a EOF character (0x1a), it ends. To solve this problem the line 408 must be modified as follows:

    data = map(lambda c: ord(c), file(args[0], 'rb').read())

  11. peteh

    I don’t have the means to test this fix but it appears to be correct so thank you very much.


    I have updated the download file with the change you suggested.

  12. ken

    Thnk you for your kindnees~~lovly man~!thnnnnnnnnk you!

  13. amin

    thank you

  14. MrSulon

    i was wondering did any one got STM32loader working on pyton 2.6 or 2.7. if so please help me i am trying to program a bootloader of my own. any help would be appreciated

    thank you

  15. MrSulon

    i have tried running the file but it is not working for me. again any help would be appreciated

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.