ISP programming of the (tr)uSDX

I noted some online discussions where some people had troubles using an ISP programmer to program the MCU.

I do not have a (tr)uSDX (trusdx), but inspection of the schematic does hint what those users are doing wrong.

Loading the SCK, MOSI and MISO lines risks problems with operation of the SPI protocol used, but the effect depends to some extent on the driver, length and type of interconnecting cable etc.

Here are some measurements of a USBasp driving an Arduino board with 5V Atmega328P 16MHz chip using about 200mm of ribbon cable… AND the MOSI line is loaded with a 0.01µF capacitor (as in the (tr)uSDX schematic).

As mentioned, ISCP uses an SPI protocol and the capture above uses blue for SCK and cyan for MOSI.

Note that the MOSI rise time is about 1µS, and recall that MOSI is sampled at the rising edge of SCK. The very first rising transition of SCK probably samples MOSI to be logical 1 (since it is more than about 1V)… but in fact it is slowly on its way to logical 0, and has been for 0.5µs. As mentioned, different cable types and lengths, and different programmers are likely to give different results, and some might resolve this bit correctly, others might not.

But that is not what digital systems are about!

Above is the same scenario WITHOUT the 0.1µ capacitor on MOSI. There is no confusion about the value of MOSI around the rising edge of SCK… it works reliably.

The (tr)uSDX shares the MOSI pin with the external PA control jack J9 and filters the line with a 0.1µF capacitor, the work around is to slow the programmer bit rate down so that the slowed MOSI rise and fall do not cause errors, a bit clock rate of 100kHz or less (AVRDUDE -B8 command line option with USBasp) should be reliable… though of course slower to program the chip.

Above is a datastream clocked at 93.75kHz, it can be seen that the rise time is now acceptable for the SCK rate.

The default rate for AVRDUDE and USBasp is 375kHz. It would probably work most of the time in my test setup… but digital systems are about stuff that works ALL the time.

Some software and programmers may not provide a convenient way to slow the clock rate, for example the recommended ArduinoISP may require you adjust source code, the source code default is 167kHz, change to #define SPI_CLOCK (100000). Again, the default probably works MOST of the time, but reducing speed will make it more reliable (given the heavy MOSI filtering discussed above).

My own practice with these type of systems is to use a bat file to invoke AVRDUDE with the correct fuse settings (see WriteOptiBoot.bat), and to erase / unlock / lock the boot region to protect the boot loader from accidental damage. The bat file serves to document to required settings, and reduces the risk of accidental writing of wrong fuse values that is so easy with a GUI such as AVRDUDESS.

Above, the default fuse settings when AVRDUDESS is opened. Hitting the Write button may make changes that are very difficult to recover.

Far and away, the most common cause of unreliability in ISP is wrong / unreliable connections or unstable / insufficient power. Don’t power the target from the programmer unless you are sure there is sufficient capacity.

Learn to read and properly interpret the AVRDUDE log, checking carefully the verify steps for sizes. Look for and check EACH verification, check for both size and success.

The ‘fix’ proposed by some to remove the 0.1µF capacitor might solve the ISP issue, but create other problems, particularly if J9 is used for an external connection.

At the end of the day, this is a user problem, perhaps fueled by a lack of documentation of reliable programming speeds.

Scripts

My own practice would be to perform the bootloader and application programming using a script file (.bat) with all the relevant settings in the file to minimise the risk of programming incorrect fuses or lock bits at a later time.

Here is a bat file:

@echo off
echo Copyright: Owen Duffy 2022, all rights reserved.
echo.
echo Processing
echo.
if not *%2==* (
set SERIAL=%2
echo SERIAL=%SERIAL%
set FL=truSDX_%SERIAL%_Firmware.hex
)
rem set defaults here
if *%PORT%==* set PORT=COM25
if *%FL%==* set FL=truSDX_default-serial_Firmware.hex

if *%1==*BOOT goto boot
if *%1==*APP goto app

:usage
echo %0 BOOT ^| APP [serial]
goto end

:boot
if *%BOOT%==* set BOOT=truSDX-initial_Bootloader.hex
echo BOOT=%BOOT%
set PRG=usbasp
set PRG=avrisp2
set PORTB=usb
set OPTS=-B 8& rem (tr)uSDX has MOSI loaded with 0.1uF
rem set OPTS=
set DEVICE=atmega328p
set AVRDUDE=avrdude
rem AVRDUDE now likes unused fuse bits set to 1
set EFUSE=0xFD& rem 0xFE for 1.8V BOD, 0xFD for 2.7V, CFD enabled (328P)
set HFUSE=0xD6& rem HFUSE=0xD6 (std Optiboot 512B boot sector), HFUSE=0xD4 1kB boot sector
set LFUSE=0xFF
set LOCK=0xCF& rem protect boot sector
echo.
echo Write boot loader (%BOOT%)...
echo.

echo on
@echo Unlock and set default HFUSE so that EEPROM can be erased
%AVRDUDE% %OPTS% -c %PRG% -P %PORTB% -p %DEVICE% -e -Uhfuse:w:0xD9:m
@timeout /nobreak /t 1 >nul
@echo Erase FLASH and EEPROM, set FUSES
%AVRDUDE% %OPTS% -c %PRG% -P %PORTB% -p %DEVICE% -e -Uefuse:w:%EFUSE%:m -Uhfuse:w:%HFUSE%:m -Ulfuse:w:%LFUSE%:m
@timeout /nobreak /t 1 >nul
@echo Write FLASH and LOCK bits
%AVRDUDE% %OPTS% -c %PRG% -P %PORTB% -p %DEVICE% -U flash:w:"%BOOT%":i -Ulock:w:%LOCK%:m
@echo off
set PORTB=
set BOOT=
goto cleanup

:app
echo FL=%FL%
set PRG=arduino
set DEVICE=m328p
set OPTS=-b 115200
rem set OPTS=-b 92160& rem fudge for testing on 16MHz board
set AVRDUDE=avrdude
echo.
echo Write application flash (%FL%)...
echo.

echo on
%AVRDUDE% %OPTS% -c %PRG% -P %PORT% -p %DEVICE% -U flash:w:"%FL%":i
@echo off
goto cleanup

:cleanup
echo.
:end
pause


exit /b

Here is the console log from writing the bootloader using a USBasp:

D:\project\trusdx>prog BOOT
Copyright: Owen Duffy 2022, all rights reserved.

Processing

BOOT=truSDX-initial_Bootloader.hex

Write boot loader (truSDX-initial_Bootloader.hex)...

Unlock and set default HFUSE so that EEPROM can be erased

D:\project\trusdx>avrdude -B 8 -c avrisp2 -P usb -p atmega328p -e -Uhfuse:w:0xD9:m

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: erasing chip
avrdude: reading input file "0xD9"
avrdude: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.01s

avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xD9:

Reading | ################################################## | 100% 0.00s

avrdude: 1 bytes of hfuse verified

avrdude done.  Thank you.

Erase FLASH and EEPROM, set FUSES

D:\project\trusdx>avrdude -B 8 -c avrisp2 -P usb -p atmega328p -e -Uefuse:w:0xFD:m -Uhfuse:w:0xD6:m -Ulfuse:w:0xFF:m

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: erasing chip
avrdude: reading input file "0xFD"
avrdude: writing efuse (1 bytes):

Writing | ################################################## | 100% 0.01s

avrdude: 1 bytes of efuse written
avrdude: verifying efuse memory against 0xFD:

Reading | ################################################## | 100% 0.00s

avrdude: 1 bytes of efuse verified
avrdude: reading input file "0xD6"
avrdude: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.01s

avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xD6:

Reading | ################################################## | 100% 0.00s

avrdude: 1 bytes of hfuse verified
avrdude: reading input file "0xFF"
avrdude: writing lfuse (1 bytes):

Writing | ################################################## | 100% 0.01s

avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xFF:

Reading | ################################################## | 100% 0.00s

avrdude: 1 bytes of lfuse verified

avrdude done.  Thank you.

Write FLASH and LOCK bits

D:\project\trusdx>avrdude -B 8 -c avrisp2 -P usb -p atmega328p -U flash:w:"truSDX-initial_Bootloader.hex":i -Ulock:w:0xCF:m

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "truSDX-initial_Bootloader.hex"
avrdude: writing flash (32768 bytes):

Writing | ################################################## | 100% 9.93s

avrdude: 32768 bytes of flash written
avrdude: verifying flash memory against truSDX-initial_Bootloader.hex:

Reading | ################################################## | 100% 9.42s

avrdude: 32768 bytes of flash verified
avrdude: reading input file "0xCF"
avrdude: writing lock (1 bytes):

Writing | ################################################## | 100% 0.02s

avrdude: 1 bytes of lock written
avrdude: verifying lock memory against 0xCF:

Reading | ################################################## | 100% 0.00s

avrdude: 1 bytes of lock verified

avrdude done.  Thank you.


Press any key to continue . . .

If there is any reason to doubt the bootloader writing process (flash and FUSES), redo it. Experience reported by some users is that if application firmware won’t load or run properly, starting again by writing the bootloader then application firmware has resolved their problem.

A check list of things to increase the probability of success writing the bootloader and FUSES:

  • use and ISP programmer connected to the ISP header with short wires;
  • make sure the connections are correct and reliable;
  • power the target independently of the programmer
  • use a low resistance / short USB cable;
  • be very very very careful specifying FUSES (you can make the chip very difficult to program if you program incompatible FUSES);
  • set the bit clock to less than 100kHz (in source code for the ArduinoISP sketch);
  • make sure you have specified the bootloader hex file;
  • save the AVRDUDE log, and check it very carefully for 4 separate write commands (-U) followed by their verify report, and check the verify is for the data source and data length you expected.

Erasing EEPROM

If there is a need to erase EEPROM, it can become a bit tricky.

Whilst the -e switch erased EEPROM on a new from factory ATmega328P (which already has an erased EEPROM), if the preserve EEPROM FUSE is programmed, then -e does not erase the EEPROM. Be aware that (tr)uSDX programs that FUSE, so if the FUSES have been programmed for (tr)uSDX, you need to change the fuses (with an ISP programmer) to not preserve EEPROM, or simply write the file 1k-FF.bin contained in 1k-FF.7z which is raw binary to the EEPROM (it should damage the existing bootloader and application code in FLASH or the FUSES.

Be very very careful messing with FUSES, you may render the chip very hard to reprogram.

The bat file above takes care of the detail of erasing EEPROM.

Going forward

Good luck, it should be very straightforward, but not everyone finds it so.