Alternative Crystal Frequency For Pinguino

After I built my first Pinguino, I decided to do some experiments on using crystals of different frequencies. By default, Pinguino uses a 20 Mhz crystal, since most of the crystals I have run at 16 Mhz I decided to dig a little deeper to see whether I could make Pinguino bootloader run at frequencies other than 20 Mhz.

So I pulled the source code of the latest bootloader V2.12 from HackingLab. But soon I realized that this particular version was missing a few key files (such as in order to be built successfully. I guess that I could have created a Makefile myself for the build, but the process would likely be time consuming as each of the dependencies needed to be figured out one by one. So I reached out to one of Pinguino’s maintainers JP Mandon. After described what I was trying to achieve, he kindly referred me to the source files listed here. As it turned out, the Pinguino bootloader had been incorporated into a much larger project — PIC USB Framework (PUF).

While the bootloader from the PUF project has the same version number as the one I downloaded from the HackingLab website, the files are not all the same. But functionality-wise, these two versions seem to be identical.

After reviewing the code, it seemed that configure.c is where the PIC configuration registers are set. The version of the bootloader on HackingLab is slightly easier to work with as the configurations are set directly (the bootloader from the PUF project is essentially identical to the version on HackingLab except that the constants are re-factored into another header file).

code char __at 0x300000 conf1 = 0x24; 
code char __at 0x300001 conf2 = 0x0e; 
code char __at 0x300002 conf3 = 0x3f; 
code char __at 0x300003 conf4 = 0x1e;
code char __at 0x300005 conf5 = 0x81;
code char __at 0x300006 conf6 = 0x81;
code char __at 0x300008 conf7 = 0x0f; 
code char __at 0x300009 conf8 = 0xc0;
code char __at 0x30000A conf9 = 0x0f; 
code char __at 0x30000B conf10 = 0xa0;
code char __at 0x30000C conf11 = 0x0f; 
code char __at 0x30000D conf12 = 0x40;

The configuration bits at 0x300000 set the crystal frequency used in conjunction with the USB bus speed. For detailed configuration settings, please refer to PIC18F2550’s datasheet. And the bit calculator I created a while ago can be quite handy at computing the configuration bits.

Microchip’s MPLab has a build-in config bits calculator tool and it can be used to generate the configuration settings needed for the program:

MPLab Configuration Bits Tool

MPLab Configuration Bits Tool

For the Pinguino bootloader, the default setting at 0x3000000 is 0x24, which translates to:

USBDIV = 1 (USB clock source comes from 96 MHz PLL / 2)
CPUDIV:CPUDIV0 = 0 (Primary oscillator used directly for system clock, no prescaler)
PLLDIV2:PLLDIV0 = 100 (Divide by 5, 20 MHz oscillator input)

According to the datasheet, the following crystal frequencies can also be used with USB by just changing PLLDIV2:PLLDIV0 without any other code adjustments:

101 (configuration bits = 0x25) — divide by 6 (24 Mhz oscillator input)
100 (configuration bits = 0x24) — divide by 5 (20 Mhz oscillator input)
011 (configuration bits = 0x23) — divide by 4 (16 Mhz oscillator input)
010 (configuration bits = 0x22) — divide by 3 (12 Mhz oscillator input)
001 (configuration bits = 0x21) — divide by 2 (8 Mhz oscillator input)
000 (configuration bits = 0x20) — no prescale (4Mhz oscillator input)

So for a 16 Mhz crystal, all we need to do is just change the configuration register at 0x300000 from 0x24 to 0x23 and recompile the bootloader.

To build the bootloader, extract the PUF tar ball and change the config.c under puf-1.1/bootloader/. If you want to compile the version of bootloader I used you can simply just remove the bootloader directory from puf-1.1, extract the bootloader into the PUF project directory and rename the directory to bootloader. This way, you will be able to use the same Makefile that comes with PUF to compile your bootloader code.

The PUF project comes with the code for Odyssey PIC programmer, which is compiled when the PUF project compiles. Since Odyssey is a very old project, the source file is no longer compatible with the latest gcc (e.g. 4.4.3). I created a patch that adds the necessary includes to compile Odyssey.

After recompiling and re-flashing the bootloader (I used the programmer I built a few weeks ago), everything worked!

I have only tested this change with a 16Mhz crystal, but other frequencies listed above should work as well.

Downloads: Bootloader (16MHz)

Be Sociable, Share!


  1. Rui Jorge says:

    Nice to see more and more people interested in Pinguino!
    Great post!!!

  2. noam says:

    good job!!!
    will this work for changing the code protection fuse also?
    do you know where can i find the source code for pic 32 boot loader?

  3. Andres says:

    Great Job. Very Interesting.
    In my class we all have a pic trainer made by mi teacher in college. I have one, but it has a 16 Mhz crystal. I have followed all your steps, but I have some errors when the program is compiled, and the .hex archive does not appear in any folder. I have Ubuntu 12.04 in Virtual Box, and I have installed g++. As I said before, I followed all your steps, but there is no result. Also, the PUF Vasco Proyect is a little bit old, and does not have much information of bugs. If you please explain a little bit more, step by step, what Linux you have, what packages you have installed before to make a clean compilation, I will be very grateful, and you avoid me the risk of desoldering the crystal and spoil my custom pic trainer. Thanks!!!!!
    (Sorry the english, I am from Ecuador! hehe)

  4. Andres says:

    First I tried to compile the original bootloader, but the hex file was not created

    The steps I followed:

    1. Extract puf-1.1 in desktop
    2. sudo ./configure
    checking for a BSD-compatible install… /usr/bin/install -c
    checking whether build environment is sane… yes
    checking for gawk… no
    checking for mawk… mawk
    checking whether make sets $(MAKE)… yes
    configure: creating ./config.status
    config.status: creating Makefile
    config.status: creating application/Makefile
    config.status: creating archives/Makefile
    config.status: creating include/Makefile
    config.status: creating lib/Makefile
    config.status: creating bootloader/Makefile
    config.status: creating scripts/Makefile
    config.status: creating udev/Makefile

    4. make
    The last lines are:

    make[2]: se ingresa al directorio «/home/uda/Escritorio/puf-1.1/archives/odyssey-0.5»
    Making all in src
    make[3]: se ingresa al directorio «/home/uda/Escritorio/puf-1.1/archives/odyssey-0.5/src»
    make all-am
    make[4]: se ingresa al directorio «/home/uda/Escritorio/puf-1.1/archives/odyssey-0.5/src»
    depbase=`echo ConfigFile.o | sed ‘s|[^/]*$|.deps/&|;s|\.o$||’`; \
    if g++ -DHAVE_CONFIG_H -I. -DDATADIR=”\”/home/uda/Escritorio/puf-1.1/tools/share/odyssey\”” -DSYSCONFDIR=”\”/home/uda/Escritorio/puf-1.1/tools/etc\”” -Wall -Wundef -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Werror -g -O2 -MT ConfigFile.o -MD -MP -MF “$depbase.Tpo” -c -o ConfigFile.o ConfigFile.cxx; \
    then mv -f “$depbase.Tpo” “$depbase.Po”; else rm -f “$depbase.Tpo”; exit 1; fi
    ConfigFile.cxx: En el constructor ‘ConfigFile::ConfigFile(const char*, const char*, const char*)’:
    ConfigFile.cxx:34:23: error: ‘getenv’ no se declaró en este ámbito
    ConfigFile.cxx: En la función miembro ‘bool ConfigFile::get_integer(const string&, const string&, long int*)’:
    ConfigFile.cxx:189:38: error: ‘strtol’ no se declaró en este ámbito
    ConfigFile.cxx: En la función miembro ‘bool ConfigFile::get_integerv(const string&, const string&, long int*, unsigned int*)’:
    ConfigFile.cxx:235:52: error: ‘strtol’ no se declaró en este ámbito
    make[4]: *** [ConfigFile.o] Error 1
    make[4]: se sale del directorio «/home/uda/Escritorio/puf-1.1/archives/odyssey-0.5/src»
    make[3]: *** [all] Error 2
    make[3]: se sale del directorio «/home/uda/Escritorio/puf-1.1/archives/odyssey-0.5/src»
    make[2]: *** [all-recursive] Error 1
    make[2]: se sale del directorio «/home/uda/Escritorio/puf-1.1/archives/odyssey-0.5»
    make[1]: *** [odyssey] Error 2
    make[1]: se sale del directorio «/home/uda/Escritorio/puf-1.1/archives»
    make: *** [all-recursive] Error 1

    5. make check

    Making check in archives
    make[1]: se ingresa al directorio «/home/uda/Escritorio/puf-1.1/archives»
    make[1]: No se hace nada para «check».
    make[1]: se sale del directorio «/home/uda/Escritorio/puf-1.1/archives»
    Making check in include
    make[1]: se ingresa al directorio «/home/uda/Escritorio/puf-1.1/include»
    make[1]: No se hace nada para «check».
    make[1]: se sale del directorio «/home/uda/Escritorio/puf-1.1/include»
    Making check in lib
    make[1]: se ingresa al directorio «/home/uda/Escritorio/puf-1.1/lib»
    /home/uda/Escritorio/puf-1.1/tools/bin/sdcc -mpic16 -p18f4550 –optimize-goto –denable-peeps –obanksel=9 –opt-code-size –optimize-cmp –optimize-df –fstack -I../include -c -c picon.c
    /bin/bash: /home/uda/Escritorio/puf-1.1/tools/bin/sdcc: No existe el archivo o el directorio
    make[1]: *** [picon.o] Error 127
    make[1]: se sale del directorio «/home/uda/Escritorio/puf-1.1/lib»
    make: *** [check-recursive] Error 1

  5. Andres says:

    I didnt install docker-1.2.tar.bz2 nor odyssey-0.6.tar.bz2 because I have pickit2 and i want to develop in windows. I need to install those?

  6. Andres says:

    Sorry, what exaclty I have to compile? I don’t have a 2550 but a 4550, so I can’ use your .hex :(. In any case I write the .hex for 16MHz in my Pic but I can´t install the driver. Anyway, I finally decided to desolder the 16MHz crystal and solder a 20MHz. After I did that everything worked!!! Thanks anyway! (I think I am too probie at Linux).

  7. oren says:

    Hi, great post.
    Can i use the hex file we with 4550?
    Or do you have one that work with it?

  8. DELTA67 says:

    Hi Kerry,
    THANKS very much for sharing your knowledge on this great blog.
    To use an other crystal (than 20MHz) I open the HEX file with ICPROG (choose 18F4550 if there is no 18F2550), make changes as you said, then I save the file and basta. No need to recompile the sources !!
    I’ve done this when building PICKIT2, cause I’ve only 8MHz crystal.

Leave a Reply to kwong