AVRライタ USBasp 互換機のファームウェアを修正する

※ 2017年に書きかけて、下書きのまま放置されていた記事があるので投稿しておきます。

# はじめに
前回、[AVRライタ USB-ASP2 のファームウェアをアップデートする]
(http://qiita.com/odaki/items/dbd7bfc389bf350ba9bd)で、ファームを最新化することができ、ATtiny10 へのTPI書き込みが成功するようになりました。
ただ、修正版のファームウェアについては、生成済みのHEXをそのまま使わせていただいきましたので、何が問題でどう直すと良いのか、あまりわかっていませんでした。

そこで、あらためて、問題は何なのか、どう直すと良いのか、を調べた上で、自前で本家のファームウェアソース(*)を修正し、修正版のHEXファイルを生成・書き込みまでやり直しました。これはその記録です。

他の問題の対処や修正、機能拡張時の参考になれば。

*本家のファームウェアソース
[USBasp - USB programmer for Atmel AVR controllers](https://www.fischl.de/usbasp/)
by Thomas Fischl
https://www.fischl.de/usbasp/usbasp.2011-05-28.tar.gz

# usbasp.2011-05-28.tar.gz で未修正の問題
## 1. TPI 書き込みが失敗する
### 現象
次のように、ターゲットが応答しない、というエラーで初期化に失敗する。
```avrdude: error: program enable: target doesn't answer. 1```

```text:USB-ASP2をそのまま使った場合
avrdude: Version 6.3, compiled on Jan 17 2017 at 12:01:35
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

(略)

         Programmer Type : usbasp
         Description     : USBasp, http://www.fischl.de/usbasp/

avrdude: auto set sck period (because given equals null)
avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: error: program enable: target doesn't answer. 1 
avrdude: initialization failed, rc=-1
         Double check connections and try again, or use -F to override
         this check.


avrdude done.  Thank you.
```
### 原因
ポートの入出力設定が不適切。
本家本元では問題ないと思われるが、USB-ASP2のような互換品でUSB-にPD2ピン以外の信号が接続されている場合、そのピンが出力として初期化されることによって信号が競合して問題となる。

### 対策
main.c のPORTD入出力設定(DDRD値)を修正する。

[8-bit AVR Microcontroller ATmega8A DATASHEET COMPLETE]
(http://www.atmel.com/images/atmel-8159-8-bit-avr-microcontroller-atmega8a_datasheet.pdf)「18.3.3. Alternate Functions of Port D」によれば、以下のようになっています。

```text:
• AIN1 – Port D, Bit 7
AIN1, Analog Comparator Negative Input. Configure the port pin as input with the internal pull-up switched off to avoid the digital port function from interfering with the function of the Analog Comparator.
• AIN0 – Port D, Bit 6
AIN0, Analog Comparator Positive Input. Configure the port pin as input with the internal pull-up switched off to avoid the digital port function from interfering with the function of the Analog Comparator.
• T1 – Port D, Bit 5
T1, Timer/Counter1 counter source.
• XCK/T0 – Port D, Bit 4
XCK, USART external clock.
T0, Timer/Counter0 counter source.
• INT1 – Port D, Bit 3
INT1, External Interrupt source 1: The PD3 pin can serve as an external interrupt source.
• INT0 – Port D, Bit 2
INT0, External Interrupt source 0: The PD2 pin can serve as an external interrupt source.
• TXD – Port D, Bit 1
TXD, Transmit Data (Data output pin for the USART). When the USART Transmitter is enabled, this pin is configured as an output regardless of the value of DDD1.
• RXD – Port D, Bit 0
RXD, Receive Data (Data input pin for the USART). When the USART Receiver is enabled this pin is configured as an input regardless of the value of DDD0. When the USART forces this pin to be an input, the pull-up can still be controlled by the PORTD0 bit.
```

Bit0, Bit1は、USARTとして使うときに、適切に設定される（inputでも良い）。
Bit2(INT0)は、USB+ とつなぐので input（元々の設定で良い）。
Bit3(INT1)が問題で、USB-ASP2のように、USB- とつながっている場合にはこれも input にしなければならない。
Bit4, Bit5 は、外部クロックの input。
Bit6, Bit7 は、使わない場合は input (かつPull-upもoff)。
ということで、全て input、B00000000、つまり 0 に設定します。

```text:修正前
DDRD = ~(1 << 2);
※ これは、B11111101、つまり、0xFD
```

```diff:ソース差分
diff -Narup usbasp.2011-05-28/firmware/main.c usbasp.2011-05-28-usb-asp2/firmware/main.c
--- usbasp.2011-05-28/firmware/main.c   2011-05-28 16:57:49.000000000 +0900
+++ usbasp.2011-05-28-usb-asp2/firmware/main.c  2017-09-16 14:00:47.000000000 +0900
@@ -306,8 +306,8 @@ int main(void) {
        /* no pullups on USB and ISP pins */
        PORTD = 0;
        PORTB = 0;
-       /* all outputs except PD2 = INT0 */
-       DDRD = ~(1 << 2);
+       /* all PDx inputs */
+       DDRD = 0;
 
        /* output SE0 for USB reset */
        DDRB = ~0;
```

参考１：[Hacking an AVR programmer]
(https://www.sciencetronics.com/greenphotons/?p=938)
参考２：[[TUT][HARD][SOFT] USBasp Internals. Learning & sharing]
(http://www.avrfreaks.net/forum/tuthardsoft-usbasp-internals-learning-sharing)
## 2. 低速書き込み時の動作が不安定
### 現象
低速書き込みモードを使った場合
- シグニチャーが認識されないことがある
- ベリファイに失敗することがある
- その他
### 発生条件
slow SCK ジャンパーをショートさせた場合など、書き込み速度がクロック12MHzの1/128(96.75KHz)未満の場合。
（USBasp がソフトウェアでデータ送信タイミングをとる動作になり、問題の処理が動作することになります）
### 原因
低速書き込みモード時に動作するソフトウェアによるSPIエミュレーション処理において、ビット送信のウェイトを入れるタイミングが間違っている。

```text:修正前
 (1) MOSIに送信するビット値をセット
 (2) SCK立ち上げ
 (3) wait
 (4) SCK立ち下げ
 (5) wait
```
```text:修正後
 (1) MOSIに送信するビット値をセット
*(2) wait
 (3) SCK立ち上げ
 (4) wait
 (5) SCK立ち下げ
```

```diff:ソース差分
diff -Narup usbasp.2011-05-28/firmware/isp.c usbasp.2011-05-28-usb-asp2/firmware/isp.c
--- usbasp.2011-05-28/firmware/isp.c    2011-01-20 03:18:00.000000000 +0900
+++ usbasp.2011-05-28-usb-asp2/firmware/isp.c   2017-09-12 13:16:08.000000000 +0900
@@ -162,10 +162,10 @@ uchar ispTransmit_sw(uchar send_byte) {
                }
 
                /* pulse SCK */
+               ispDelay(); /* slow CLK option */
                ISP_OUT |= (1 << ISP_SCK); /* SCK high */
                ispDelay();
                ISP_OUT &= ~(1 << ISP_SCK); /* SCK low */
-               ispDelay();
        }
 
        return rec_byte;
```

参考１：[Patch to Lcsoft USBasp V2.0]
(http://www.avrfreaks.net/forum/patch-lcsoft-usbasp-v20)
参考２：[[TUT][HARD][SOFT] USBasp Internals. Learning & sharing]
(http://www.avrfreaks.net/forum/tuthardsoft-usbasp-internals-learning-sharing)

## おまけ
USBASP_FUNC_SETISPSCK 機能で設定できる書き込み速度一覧。
デフォルトは AUTOで、AUTO の場合には、375 kHzになります。
(書き込み速度は avrdude の [-B オプション](http://www.nongnu.org/avrdude/user-manual/avrdude_4.html#Option-Descriptions)で-B1khzのように指定します)

```c:usbasp.h
/* ISP SCK speed identifiers */
#define USBASP_ISP_SCK_AUTO   0
#define USBASP_ISP_SCK_0_5    1   /* 500 Hz */
#define USBASP_ISP_SCK_1      2   /*   1 kHz */
#define USBASP_ISP_SCK_2      3   /*   2 kHz */
#define USBASP_ISP_SCK_4      4   /*   4 kHz */
#define USBASP_ISP_SCK_8      5   /*   8 kHz */
#define USBASP_ISP_SCK_16     6   /*  16 kHz */
#define USBASP_ISP_SCK_32     7   /*  32 kHz */
#define USBASP_ISP_SCK_93_75  8   /*  93.75 kHz */
#define USBASP_ISP_SCK_187_5  9   /* 187.5  kHz */
#define USBASP_ISP_SCK_375    10  /* 375 kHz   */
#define USBASP_ISP_SCK_750    11  /* 750 kHz   */
#define USBASP_ISP_SCK_1500   12  /* 1.5 MHz   */
```

おまけその２

パッチを当ててコンパイルしたファーム(HEXファイル)をあげておきます。
自分でハマったので。

[GIST: https://gist.github.com/odaki/982ee76cc60666d2f1460affb31cf297
](https://gist.github.com/odaki/982ee76cc60666d2f1460affb31cf297)