Flash-Resident USB-HID Bootloader with the NXP Kinetis K22 Microcontroller

The tinyK22 board (see “tinyK22 Boards arrived“) gets rolled out at the Lucerne University of Applied Sciences and Arts, so I thought I write-up an article this weekend how to use that board with a Flash Resident Bootloader.

tinyK22 with USB HID Bootloader

tinyK22 with USB HID Bootloader

Outline

This article describes how to program and use the NXP K22 with a flash resident bootloader. I tried to keep this article as much generic as possible, so you can use any other Eclipse based IDE (DIY, Kinetis Design Studio or the NXP MCUXpresso IDE). For the example project and sources used, see the link to GitHub at the end of this article.

💡 If you are interested in another very good open source bootloader, check out the μTasker project page (http://www.utasker.com/kinetis/tinyK22.html). That bootloader offers MSD loading and much more and is much smaller too.

As hardware the NXP Kinetis K22FN512 is used. This ARM Cortex-M4F microcontroller is present on the tinyK22 board as well on the NXP FRDM-K22F board.

Blinky on a FRDM-K22F with SDK V2.0 and Processor Expert

tinyK22 with USB HID Bootloader

tinyK22 with USB HID Bootloader

USB HID Bootloader

A bootloader based on USB HID is a cool way to load applications on a microcontroller. For USB HID no special drivers are needed for a Windows/Mac/Linux host as USB HID drivers are part of the host operating system. The downside is that USB is a rather complex protocol, thus requires USB Hardware and a USB  stack on the target device.

I ported the presented project from the NXP KBOOT and as well using the utilities that come with that package. The implemented bootloader is a flash resident bootloader. This means initially I program the microcontroller with the bootloader and it remains in flash and then is used to load any application I want. With optimization -O1 set, I have a USB HID bootloader which uses around 26 KBytes of FLASH.

Project Structure

The screenshot below shows the bootloader project structure.

bootloader project

bootloader project

  • link: contains the linker file. The bootloader vector table and address is mapped to 0x0000’0000 which is the default vector table for ARM Cortex-M
  • MK22F51212: contains the hardware specific port to the Kinetis K22 microcontroller. The bootloader_config.h file in this folder is used to configure the bootloader.
  • src: contains all the bootloader sources which are pretty much generic

Bootloader Project Defines

The project uses a few global defines to configure the bootloader:

Bootloader Defines

Bootloader Defines

The above defines disable extra debug messages, set the USB stack to be bare-metal (no RTOS used), configures the device (MK22 and ARM Cortex-M4). The last define configures the bootloader to be FLASH resident.

Bootloader Configuration

The header file MK22F51212\bootloader_config.h is used to configure the bootloader, for example which communication protocols are supported. In the setting below I’m configuring it to use USH HID only:

#if !defined(BL_CONFIG_SCUART)
#define BL_CONFIG_SCUART (0)
#endif
#if !defined(BL_CONFIG_I2C)
#define BL_CONFIG_I2C (0)
#endif
#if !defined(BL_CONFIG_DSPI)
#define BL_CONFIG_DSPI (0)
#endif
#if !defined(BL_CONFIG_USB_HID)
#define BL_CONFIG_USB_HID (1)
#endif
#if !defined(BL_CONFIG_USB_MSC)
#define BL_CONFIG_USB_MSC (0)
#endif
#if !defined(BL_TARGET_FLASH)
#define BL_TARGET_FLASH (1)
#endif

The more or the more complex communication protocols are used, the more FLASH memory is occupied by the bootloader. With the USB HID enabled, the size is around 26 KByte of FLASH it fits into the  memory area below 0xb000 (see “text, data and bss: Code and Data Size Explained“)  how to read the output below.

arm-none-eabi-size --format=berkeley "tinyK22_KBOOT_bootloader.elf"
  text data bss dec hex filename
 26204 1856 14000 42060 a44c tinyK22_KBOOT_bootloader.elf

For  example if I would communicate to the bootloader with UART, it would only need 21 KByte of FLASH memory.

The bootloader can use a GPIO pin to enter bootloader mode after a reset (e.g. POR, Power-On-Reset). Which pin is used is configured in MK22F51212\hardware_init_K22F512:

 
#define BOOT_PIN_NUMBER 17
#define BOOT_PIN_PORT PORTB
#define BOOT_PIN_GPIO GPIOB
#define BOOT_PIN_ALT_MODE 1
tinyK22 Pinout

tinyK22 Pinout

With the above code, I’m using the GPIO Pin PTB17. The function is_boot_pin_asserted() is used to check that pin. Typically this pin is pulled HIGH by a pull-up and pulling it to LOW during startup I will enter bootloader mode.

If not using any bootloader pin, I can configure the bootloader timeout specified in bootloader_config.h:

 
#define BL_DEFAULT_PERIPHERAL_DETECT_TIMEOUT 5000

With the above setting, after reset it will wait for 5 seconds for any communication from the host until the bootloader proceeds. If such a timeout is not desirable, then I recommend to configure a dedicated bootloader pin.

Another setting in the bootloader_config.h is where the vector table of the application resides. Basically this defines where my application FLASH memory space starts. Below I have set it to 0xC000 which means that the bootloader can use 0x0000 up to 0xBFFF.

 
// The bootloader will check this address for the application vector table upon startup.
#if !defined(BL_APP_VECTOR_TABLE_ADDRESS)
#define BL_APP_VECTOR_TABLE_ADDRESS 0xc000
#endif

Programming the bootloader

The bootloader needs to be programmed with a SWD/JTAG debug probe (e.g. Segger J-Link, P&E Multilink or CMSIS-DAP) to the microcontroller.

Stopping the bootloader with the debugger shows that it waits for incoming USB packets:

USB HID Bootloader waiting for USB packets

USB HID Bootloader waiting for USB packets

Building an Application to be loaded with the Bootloader

An application to be loaded with the bootloader needs to be linked at the correct address, where the bootloader expects it. As discussed above, I have my application start address (actually its vector table address) at 0x0000’C000.

Linker File Mapping Application at 0xC000

Linker File Mapping Application at 0xC000

With that linker file I create the S19 and/or the binary file of the application which I want to load with the bootloader. See “Binary (and S19) Files for the mbed Bootloader with Eclipse and GNU ARM Eclipse Plugins” and “MCUXpresso IDE: S-Record, Intel Hex and Binary Files” how to create S19 or binary files with GNU tools.

Memory Map with Bootloader and Application

Memory Map with Bootloader and Application (adopted from FTF-DES-F1299.pdf)

Connecting to the bootloader

KBOOT (see links at the end of this article) includes two utilities to communicate with the bootloader:

  • blhost: command line utility
  • KinetisFlashTool: Same as above, but with a GUI

Connect to the USB HID Bootloader using the 0x15A2 for VID and 0x0073 as PID:

Connect to USB HID Bootloader

Connect to USB HID Bootloader

💡 Note that I need to the the bootloader needs to be active for the Connect!

If that succeeds, it will show me the bootloader information:

connected with USB HID Bootloader

connected with USB HID Bootloader

The same way the blhost utility can be used to connect to the device on the command line (the USB VID/PID is optional):

blhost --usb 0x15a2,0x0073 - get-property 1
blhost with USB HID Bootloader

blhost with USB HID Bootloader

Once connected, I can update the binary using the bootloader. As the binary file does not contain any destination address information, I have to specify this in the dialog (0x0000c000, as this is where my application starts with its vector table):

Update with new binary

Update with new binary

The same thing can be accomplished using the blhost command line:

To load a binary file to address 0xc000:

blhost.exe --usb 0x15A2,0x0073 write-memory 0xC000 tinyK22_KBOOT_led_blinky_1Hz_0xC000.bin
loading binary file

loading binary file

For loading S19 files the flash-image command is used:

blhost.exe --usb 0x15A2,0x0073 flash-image tinyK22_KBOOT_led_blinky_1Hz_0xC000.srec
loading S19 file

loading S19 file

reset the board, and the new application is running 🙂

Summary

A flash resident bootloader is a bootloader which I can configure and program into the FLASH memory of the microcontroller. I can configure it for the communication protocol to be used to the outside world (USB HID in this case) and then connect either with a GUI or command line tool to the device and update the application. This is especially useful if I have devices in the field and I have to update them with a new firmware without the need for a debugger.

Happy Bootloading 🙂

Links

Advertisements

30 thoughts on “Flash-Resident USB-HID Bootloader with the NXP Kinetis K22 Microcontroller

  1. Hi Eric

    Please take a look at http://www.utasker.com/kinetis/tinyK22.html where I have added a simulation of your board and made a USB-MSD loader for it, which is usually more convenient that the KBOOT HID one. It allows simple connection (no driver installation) and drag-and-drop on Windows, macOS X and Linux [without needing to install and learn any program on the PC either]. Each time the board starts in the boot loader it displays the loaded program (with its size, date etc.) and allows it to be read back – also drag-and-drop – with optional password protection on the read-back operation.

    As you will see from the file size it is also more memory efficient that the Kboot code – it needs 14.4k (about 55% of the Kboot one) of flash.

    I also have a version there which supports loading either binary, SREC or iHEX formation (it detects and decodes each automatically) which is slightly larger [15.4k] but still less that 60% the Kboot’s memory footprint.

    Regards

    Mark

    Like

    • Hi Mark,
      thanks for that page and port to the tinyK22, I’m sure my students will love that version too! I tried out the uTasker bootloader and demo apps with the tinyK22, and it works great.
      About the size: I did not optimize the KBOOT version because on that device I have plenty of space available, but for sure for more memory restriced devices saving a few KBytes can make a big difference.
      In my view, the MSD bootloader is very handy, but I usually prefer for ‘real’ applications in the field a serial one (which your uTasker) includes too. There is far too much danger and and possibilities what can go wrong with USB MSD interfaces. Maybe I had too many bad experiences in the past. So I prefer a controlled (e.g. UART) way. The KBOOT USB HID is still somewhat problematic as it requries that special tool or command line interface on the host, but has worked fine so far in the field.

      Thanks again,
      Erich

      Like

      • Hi Eric

        I just added a version that does USB-MSD and Kboot-HID as composite USB device, plus UART (SREC/iHex) loading on PTE0 and PTE1 at 115200 Baud [21.4k].
        This means that it should satisfy all tastes.

        Note that I never had any USB loading problems after several years in many commercial products and in my experience USB-MSD tends to be the preferred method (quite rare UART nowadays, SD-card and USB-memory stick possibly second place). I never heard of Kboot being used for commercial products – I use it only for factory programing of things like KL27 but only to install a more flexible solution for later field use.

        I am also happy to hear that the loader and application worked since I don’t have your board so could only simulate it. It took about 2 hours to port and simulate the loader and application for the HW and create a web page with downloads, so I was taking a chance in publishing binaries that hadn’t been used before…. 😉

        Regards

        Mark

        Like

  2. Hi Erich,
    Its good to see this USB HID bootloader in KBOOT working with MCUXpresso , I had tried it a while ago with a Freedom board and KDS but was not convinced by the results I learned that IAR was used to compile it originally. A USB HID bootloader could be very appropriate if your developing a product destined to be used by the masses that could provide firmware updates since the HID driver is native to windows and all you need is a standard USB cable.
    Using serial ports is OK if you know what one is, using virtual COM ports sometimes requires going into device manager to determine if you have the right port and USB MSD requires more user interaction – to drop the firmware update file onto the right drive. In my evaluation of KBOOT I thought it would be great if there was a stripped down version that just supported USB HID.
    Best regards
    Chad

    Like

      • Hi Erich,
        for sure it would require adapting the bflwkdll library thats used by the Kinetis flash tool and integrating it into a custom windows application if that can be done.
        Chad

        Like

    • Chad

      USB-MSD comes up with a disk with the name of the board (or whatever one wants it to come up with) so not a lot can go wrong. I use USB-MSD in many commercial devices (hundreds of thousands or possibly > million such in the field) and there are no know issues.

      I just changed the disk name to show as “TINYK22” rather than “UPLOAD_DISK”.

      I also added a new loader version which performs USB-MSD and Kboot (as composite USB device), plus UART SREC/iHEX on pins PTE0 and PTE1. This means that it should satisfy everyone since the method can simply be chosen as desired (21.4k Flash size).

      Regards

      Mark

      P.S: Builds with CW, KDS, MCUXpresso, Keil uVision, IAR, Standalone GNU make file (which I tend to use for speed and simplicity), Visual Studio, S32 Design Studio, Rowley Crossworks, CooCox, Green Hills and Atollic TrueSTUDIO.

      Liked by 1 person

  3. Pingback: Tutorial: CRC32 Checksum with the KBOOT Bootloader | MCU on Eclipse

  4. Pingback: Converting Binary Files to Intel Hex Format with the SRecord Tool | MCU on Eclipse

  5. Hello Erich, My application is similar to this except that the Kinetis is the host microcontroller which has access to the .bin file that is supposed to go into the application section of the target microcontroller(ATmega88) which has its own bootloader. I would like to transmit the said .bin file through UART to the Atmega88 controller.and test my bootloader also in this process. I am using MKV10Z32xx Kinetis micro as the host and PE to write code. I am also using the SDK. A litlle nudge in the right direction would be helpful.
    Thanks.

    Like

      • I have the hex file also for the application to be coded on the Atmega88. I have the bootloader also on the Atmega88. All the KV has to do is read information from the hex file store it in a buffer and then send that buffer over through UART to be written on the Atmega88.
        I am unsure how the .hex/.bin/srec file needs to be sent through UART? Is there a sample code for me to look at that does this?
        Thanks

        Like

        • With the UART you can send any data or bytes, nothing special needed. You can use any sample code for this which sends something to the UART, so there won’t be any special app note for binary data.

          Like

  6. Hello Erich,
    I tried reading the intel hex file using fopen command in KDS and somehow the code doesnot open the intelhex file. I have the code snippet attached here.

    The functions used here are load_file and parse_hex_line.
    What it does is it opens the hex file that is to flashed on the target and takes the address and data bytes in separate buffers and saves them. This code compiles on an online C compiler and gives me a result that says that so many bytes have been saved in this buffer. What I want to do is to transmit the said buffer to the target microcontroller. When I try debugging this on KDS, the problem is that the code gets stuck on fopen line.

    Is there a way to debug this?

    int main(int argc, char** argv)
    /*lint -restore Enable MISRA rule (6.3) checking. */
    {
    int parse_hex_line(theline, bytes, addr, num, code)
    char *theline;
    int *addr, *num, *code, bytes[];
    {
    int sum, len, cksum;
    char *ptr;

    *num = 0;
    if (theline[0] != ‘:’) return 0;
    if (strlen(theline) < 11) return 0;
    ptr = theline+1;
    if (!sscanf(ptr, "%02x", &len)) return 0;
    ptr += 2;
    if ( strlen(theline) > 8) & 255) + (*addr & 255) + (*code & 255);
    while(*num != len) {
    if (!sscanf(ptr, “%02x”, &bytes[*num])) return 0;
    ptr += 2;
    sum += bytes[*num] & 255;
    (*num)++;
    if (*num >= 256) return 0;
    }
    if (!sscanf(ptr, “%02x”, &cksum)) return 0;
    if ( ((sum & 255) + (cksum & 255)) & 255 ) return 0; /* checksum error */
    return 1;
    }

    void load_file(filename)
    char *filename;
    {
    char line[1000];
    FILE *fin;
    int addr, n, status, bytes[256];
    int i, total=0, lineno=1;
    int minaddr=65536, maxaddr=0;

    fin = fopen(filename, “r”);
    while (!feof(fin) && !ferror(fin)) {
    line[0] = ‘\0’;
    fgets(line, 1000, fin);
    if (line[strlen(line)-1] == ‘\n’) line[strlen(line)-1] = ‘\0’;
    if (line[strlen(line)-1] == ‘\r’) line[strlen(line)-1] = ‘\0’;
    if (parse_hex_line(line, bytes, &addr, &n, &status)) {
    if (status == 0) { /* data */
    for(i=0; i<=(n-1); i++) {
    mem[addr] = bytes[i] & 255;
    total++;
    if (addr maxaddr) maxaddr = addr;
    addr++;
    }
    }
    if (status == 1) { /* end of file */
    fclose(fin);
    printf(” Loaded %d bytes between:”, total);
    printf(” %04X to %04X\n”, minaddr, maxaddr);
    return;
    }
    if (status == 2) ; /* begin of file */
    } else {
    printf(” Error: ‘%s’, line: %d\n”, filename, lineno);
    }
    lineno++;
    }
    }

    load_file(“BlinkTest.hex”);

    Like

    • Hi Leandro,
      you mean the tinyK22? Unfortunately this is not so easy with all the admistration and shipping. We easily can hand them out our students because they are in the university systems, but outside of this is it a rather large effort :-(.
      It will be much easier for you go get the FRDM-K22F board instead?

      Like

  7. Hi Erich,
    I am trying to get a USB bootloader added to my K22 project and am having some troubles.
    First off, I thought I’d look at using the new MCUXpresso 11.0. When I try to build the SDK for my processor ( The K22FX512 which has CAN bus, instead of the K22FN512) the nxp tool builds an SDK 2.3 version, and MCUXpresso 11.0 will not accept that. I can build a K22FN512 2.6 SDK, but I am not sure if the differences are significant enough to cause problems.

    Next I try to build the example frdmk22f_freedom_bootloader , which builds okay but when I execute it (on my K22FX hardware, I haven’t dug up a K22FN to try yet) it hardfaults at the FLASH_init() function. the flash config data structure passed to that function is full of zeros, so that isn’t to surprising it doesn’t work. I don’t know how to make this example work…

    My goal is to be able to have the bootloader read in an Srec image from the USB-CDC connection from a serial port on a laptop. Eventually I’d like to upgrade from a USB memory stick, but because of how I wired the board such a memory stick would need a battery to overcome not supplying USB power from my target.

    Is there an ‘easiest’ solution? I would even use the uTasker system if I can make that work.

    Brynn

    Like

    • Hi Brynn,
      I do have the SDK 2.3.1 for the MK22FX512xx12, and that works in the IDE V11? Maybe you need to do ‘Recreate and reload part info’ first?
      The FN and FX are different in their internal FLASH configuration (the FX has some dedicated FLEXRAM/FLASH), so maybe this makes a difference.
      I have the NXP bootloader running on the FN, but never had a chance to get it running on the FX part, the former FX projects have moved to the FN part.

      Like

    • Hi Mark,
      I’m not sure what I am doing wrong with uTasker. If I can get it to work I’ll buy the commercial license.

      My first attempt was to go from the git-master and follow some video to import into KDS. That one the compile fails because it wants something out of the STM32 directory which implies I did not configure it right for the K22FX512 that I am using. (That is actually my second attempt at KDS, the first one had some different problem).
      I’ve also attempted to build it with MCUXpresso 11.0, and that I fail at even moreso.

      I had a video I was working from the first time, but I can’t find that one again.
      I think part of my problem is the video tells me to copy the .cproject and .project up to the top directory and then import it, and I think I went to far up for the ‘top’ directory, or not far enough.

      I’d love a little direction. the license fee will be no problem because it is less than a half day of work that it should easily save me.

      Brynn

      Like

      • I’m following the uTasker SHORT Getting Started – From GIT to KDS build
        again now, trying to not screw up…

        Like

        • Mark,
          I change the config.h to #define Tiny22 (I assume close enough to my K22FX512) commented out #define K64F, changed the linker script to K_512_128.ld (the FX512 has 128K ram)

          I followed the video exactly this time, and I still got compiler errors in the STM32 directory :

          Building file: ../Hardware/STM32/STM32_ports.cpp
          Invoking: Cross ARM C++ Compiler
          arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -Os -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -Wall -g -std=gnu++11 -fabi-version=0 -MMD -MP -MF”Hardware/STM32/STM32_ports.d” -MT”Hardware/STM32/STM32_ports.o” -c -o “Hardware/STM32/STM32_ports.o” “../Hardware/STM32/STM32_ports.cpp”
          ../Hardware/STM32/STM32_ports.cpp:22:20: fatal error: config.h: No such file or directory
          #include “config.h”
          ^
          compilation terminated.
          Hardware/STM32/subdir.mk:38: recipe for target ‘Hardware/STM32/STM32_ports.o’ failed
          make: *** [Hardware/STM32/STM32_ports.o] Error 1

          So I don’t know where to go from here, because anything I try is not following the video.

          Like

  8. Erich, Sorry if we hijacked your comments…
    Mark, If I single step through your video I see that the build goes from the file ../stack/zero_config.c to the next file ../Hardware/Kinetis/kinetis.c.
    my build proceeds from ../stack/zero_config.c to ../Hardware/STM32/STM32.c

    I don’t think the right makefile is being used or generated….

    Like

    • I also noticed that the import does NOT check the box ‘copy to workspace’
      I always check that box because I don’t like indirect links to my source files.

      I will try to do the import video again without checking that box this time.
      Brynn

      Like

      • starting over and unclicking that box does not help.

        right clicking the GNU_STM32 directory and excluding it from the build, and unexcluding the directory GNU_Kinetis helps. If I do that, then I get the error
        SIZE_OF_RAM not defined. I #define SIZE_OF_RAM 0x20000 then I get SIZE_OF_FLASH not defined.
        I #define SIZE_OF_FLASH 0x80000 and then it gets a little farther –
        it then complains with an #error line that I cannot program flash when in high speed mode (which is correct, there is a limit in the K22 that I cannot have that high speed clock bit set. solution is to just run 80Mhz instead of 120Mhz for the bootloader. OR 64Mhz would work as well. I’m at a loss as to where I would start changing that, and by this point it is clear that there is a bug in the utasker configuration for the K22 that needs to be fixed.

        Brynn

        Like

        • AHA! if I select #define FRDM_K22F instead of tinyK22, that fixes the clock issue and size of ram issue. I now have it up and running.

          Brynn

          Like

        • Just be aware that the FRDM-K22F has a K22FN512 while you are targeting the K22FX512 which is different. But you are already aware I guess…

          Like

        • Hi
          I tested KDS (and assume MCUXpressor is equivalent):
          1. Copied the content of \Applications\uTaskerV1.4\KinetisDesignStudio\Project_Settings to the root directory so that the KDS project is used
          2. Ported this into KDS.
          3. Selected the Serial loader target and set up the Core and Linker script iles to match the HW (Read the compiler guide and see videos for KDS workarounds when it doesn’t display the selected target correctly).
          4. In the serial loaders’ coonfig.h select the “tinyk22” and disable whatever was selected by default
          5. Setup for USB CDC SREC loading with
          #define USB_INTERFACE
          and
          #define USE_USB_CDC
          6. Build (gives a coupel fo errors
          7. Exclude the Hardware/STM32 directory from the target
          8. Build – gives error due to missing SIZE_OF_RAM. Thsi is because the tinyK22 is not configured in the open source project (it does have FRDM_K22F and tinyK20 but is missing the tinyK22). The missing setup in app_hw_kinetis.c can be copied from the uTaskerV1.4 target setup.

          9. To work with a K22 with flex memory add the defines (with the appropriate size)
          #define FLEXFLASH_DATA // use FlexNMV in data mode
          #define SIZE_OF_FLEXFLASH (32 * 1024) // 32 Flex
          and the code will adapt itself for FX compatibility.

          10. To change a crystal from 8M to 16M change
          #define CRYSTAL_FREQUENCY 8000000 -> 16000000
          and
          #define CLOCK_DIV 4 –> 8

          I have just checked in these tinyK22 configuration to the open source version so that the tinyK22 builds without any complications.

          Regards

          Mark

          Like

  9. Mark, I now have it running, but it gets locked in the _LowLevelInit() routine trying to set the clocks up. Unlike the FRDM_K22F board which has I believe an 8Mhz crystal, my K22FX512 board has a 16Mhz crystal. Tried to set CRYSTAL_FREQUENCY (in app_hw_kinetis.h) to 16000000, but that just makes it crash with the GDB error:
    714,438 ~”/home/build/work/GCC-4-8-build/src/gdb/gdb/thread.c:615: internal-error: is_thread_state: \
    Assertion `tp’ failed.\nA problem internal to GDB has been detected,\nfurther debugging may prove un\
    reliable.\n

    Not sure what the proper fix is now…

    Like

    • Hi Brynn

      I have sent you access to the professional version which is possibly better maintained than the open source one. It may be that you need to exclude some STM32 resources in Eclipse based IDEs (since they automatically add a C files that they find).

      You have also been given an account at the uTasker forum in case you would like to continue discussions there. I will also check the open source version and explain what problems you may have had there, including explaining how to set up different clocks and what to do if you have FX Kinetis types (such chips default to an area of program memory and an area of data memory, which, although not concatonated, can also be used as if it were a bigger area of flash – when EEPROM mode is not configured; personally I don’t see much interest in the EEPROM configuration since similar EEPROM emulation can be performed on any chip with greater flexibility and the same level of wear multiplication).

      Regards

      Mark

      P.S. I’ll report back tomorrow after I verify the build you were attempting.

      Like

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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