diff --git a/AUTHORS b/AUTHORS index 3b32d005..a6f1db07 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,6 +15,7 @@ Contributors: Thomas Fischl David Hoerl Michal Ludvig + Darell Tan For minor contributions, please see the ChangeLog files. diff --git a/ChangeLog b/ChangeLog index 20c9de82..617d5054 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2011-08-23 Joerg Wunsch + + Submitted by Darell Tan: + patch #7244: TPI bitbang implementation + * bitbang.c: Add TPI bitbang stuff. + * bitbang.h: (Ditto.) + * avr.c: (Ditto.) + * avr.h: (Ditto.) + * pgm.c: (Ditto.) + * pgm.h: (Ditto.) + * serbb_posix.c: Wire bitbang_cmd_tpi into the struct pgm. + * serbb_win32.c: (Ditto.) + * par.c: (Ditto.) + * doc/avrdude.texi: Document the TPI bitbang support. + 2011-08-17 Joerg Wunsch Submitted by Grygoriy Fuchedzhy: diff --git a/NEWS b/NEWS index d8a28c52..b3a86126 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,8 @@ Current: - ATtiny4313 * New programmers supported: + - TPI programming through bitbang programmers (both, serial + and parallel ones) * Bugfixes diff --git a/avr.c b/avr.c index 6fd8bc7a..d2451def 100644 --- a/avr.c +++ b/avr.c @@ -1,6 +1,7 @@ /* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2000-2004 Brian S. Dean + * Copyright 2011 Darell Tan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,17 +37,61 @@ #include "ppi.h" #include "safemode.h" #include "update.h" +#include "tpi.h" FP_UpdateProgress update_progress; #define DEBUG 0 +/* TPI: returns 1 if NVM controller busy, 0 if free */ +int avr_tpi_poll_nvmbsy(PROGRAMMER *pgm) +{ + unsigned char cmd; + unsigned char res; + int rc = 0; + + cmd = TPI_CMD_SIN | TPI_SIO_ADDR(TPI_IOREG_NVMCSR); + rc = pgm->cmd_tpi(pgm, &cmd, 1, &res, 1); + return (rc & TPI_IOREG_NVMCSR_NVMBSY); +} + +/* TPI: setup NVMCMD register and pointer register (PR) for read/write/erase */ +static int avr_tpi_setup_rw(PROGRAMMER * pgm, AVRMEM * mem, + unsigned long addr, unsigned char nvmcmd) +{ + unsigned char cmd[4]; + int rc; + + /* set NVMCMD register */ + cmd[0] = TPI_CMD_SOUT | TPI_SIO_ADDR(TPI_IOREG_NVMCMD); + cmd[1] = nvmcmd; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + if (rc == -1) + return -1; + + /* set Pointer Register (PR) */ + cmd[0] = TPI_CMD_SSTPR | 0; + cmd[1] = (mem->offset + addr) & 0xFF; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + if (rc == -1) + return -1; + + cmd[0] = TPI_CMD_SSTPR | 1; + cmd[1] = ((mem->offset + addr) >> 8) & 0xFF; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + if (rc == -1) + return -1; + + return 0; +} + int avr_read_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, unsigned long addr, unsigned char * value) { unsigned char cmd[4]; unsigned char res[4]; unsigned char data; + int r; OPCODE * readop, * lext; if (pgm->cmd == NULL) { @@ -60,6 +105,27 @@ int avr_read_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, pgm->pgm_led(pgm, ON); pgm->err_led(pgm, OFF); + if (p->flags & AVRPART_HAS_TPI) { + if (pgm->cmd_tpi == NULL) { + fprintf(stderr, "%s: Error: %s programmer does not support TPI\n", + progname, pgm->type); + return -1; + } + + while (avr_tpi_poll_nvmbsy(pgm)); + + /* setup for read */ + avr_tpi_setup_rw(pgm, mem, addr, TPI_NVMCMD_NO_OPERATION); + + /* load byte */ + cmd[0] = TPI_CMD_SLD; + r = pgm->cmd_tpi(pgm, cmd, 1, value, 1); + if (r == -1) + return -1; + + return 0; + } + /* * figure out what opcode to use */ @@ -151,6 +217,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, unsigned char rbyte; unsigned long i; unsigned char * buf; + unsigned char cmd[4]; AVRMEM * mem; int rc; @@ -171,6 +238,33 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, */ memset(buf, 0xff, size); + /* supports "paged load" thru post-increment */ + if ((p->flags & AVRPART_HAS_TPI) && mem->page_size != 0) { + if (pgm->cmd_tpi == NULL) { + fprintf(stderr, "%s: Error: %s programmer does not support TPI\n", + progname, pgm->type); + return -1; + } + + while (avr_tpi_poll_nvmbsy(pgm)); + + /* setup for read (NOOP) */ + avr_tpi_setup_rw(pgm, mem, 0, TPI_NVMCMD_NO_OPERATION); + + /* load bytes */ + for (i = 0; i < size; i++) { + cmd[0] = TPI_CMD_SLD_PI; + rc = pgm->cmd_tpi(pgm, cmd, 1, &buf[i], 1); + if (rc == -1) { + fprintf(stderr, "avr_read(): error reading address 0x%04lx\n", i); + return -1; + } + + report_progress(i, size, NULL); + } + return avr_mem_hiaddr(mem); + } + if (pgm->paged_load != NULL && mem->page_size != 0) { /* * the programmer supports a paged mode read, perhaps more @@ -303,6 +397,52 @@ int avr_write_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, return -1; } + if (p->flags & AVRPART_HAS_TPI) { + if (pgm->cmd_tpi == NULL) { + fprintf(stderr, "%s: Error: %s programmer does not support TPI\n", + progname, pgm->type); + return -1; + } + + if (strcmp(mem->desc, "flash") == 0) { + fprintf(stderr, "Writing a byte to flash is not supported for %s\n", p->desc); + return -1; + } else if ((mem->offset + addr) & 1) { + fprintf(stderr, "Writing a byte to an odd location is not supported for %s\n", p->desc); + return -1; + } + + while (avr_tpi_poll_nvmbsy(pgm)); + + /* must erase fuse first */ + if (strcmp(mem->desc, "fuse") == 0) { + /* setup for SECTION_ERASE (high byte) */ + avr_tpi_setup_rw(pgm, mem, addr | 1, TPI_NVMCMD_SECTION_ERASE); + + /* write dummy byte */ + cmd[0] = TPI_CMD_SST; + cmd[1] = 0xFF; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + + while (avr_tpi_poll_nvmbsy(pgm)); + } + + /* setup for WORD_WRITE */ + avr_tpi_setup_rw(pgm, mem, addr, TPI_NVMCMD_WORD_WRITE); + + cmd[0] = TPI_CMD_SST_PI; + cmd[1] = data; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + /* dummy high byte to start WORD_WRITE */ + cmd[0] = TPI_CMD_SST_PI; + cmd[1] = data; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + + while (avr_tpi_poll_nvmbsy(pgm)); + + return 0; + } + if (!mem->paged) { /* * check to see if the write is necessary by reading the existing @@ -540,6 +680,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, long i; unsigned char data; int werror; + unsigned char cmd[4]; AVRMEM * m; m = avr_locate_mem(p, memtype); @@ -566,6 +707,40 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, progbuf, wsize); } + + if ((p->flags & AVRPART_HAS_TPI) && m->page_size != 0) { + if (pgm->cmd_tpi == NULL) { + fprintf(stderr, + "%s: Error: %s programmer does not support TPI\n", + progname, pgm->type); + return -1; + } + + while (avr_tpi_poll_nvmbsy(pgm)); + + /* setup for WORD_WRITE */ + avr_tpi_setup_rw(pgm, m, 0, TPI_NVMCMD_WORD_WRITE); + + /* make sure it's aligned to a word boundary */ + if (wsize & 0x1) { + wsize++; + } + + /* write words, low byte first */ + for (i = 0; i < wsize; i++) { + cmd[0] = TPI_CMD_SST_PI; + cmd[1] = m->buf[i]; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + + cmd[1] = m->buf[++i]; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + + while (avr_tpi_poll_nvmbsy(pgm)); + report_progress(i, wsize, NULL); + } + return i; + } + if (pgm->paged_write != NULL && m->page_size != 0) { /* * the programmer supports a paged mode write, perhaps more diff --git a/avr.h b/avr.h index 9be67ce1..477ab9df 100644 --- a/avr.h +++ b/avr.h @@ -37,6 +37,7 @@ extern FP_UpdateProgress update_progress; extern "C" { #endif +int avr_tpi_poll_nvmbsy(PROGRAMMER *pgm); int avr_read_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, unsigned long addr, unsigned char * value); diff --git a/bitbang.c b/bitbang.c index 24b85183..39fbf592 100644 --- a/bitbang.c +++ b/bitbang.c @@ -2,6 +2,7 @@ * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2000, 2001, 2002, 2003 Brian S. Dean * Copyright (C) 2005 Michael Holzt + * Copyright 2011 Darell Tan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,6 +40,7 @@ #include "pgm.h" #include "par.h" #include "serbb.h" +#include "tpi.h" static int delay_decrement; @@ -212,6 +214,93 @@ static unsigned char bitbang_txrx(PROGRAMMER * pgm, unsigned char byte) return rbyte; } +static int bitbang_tpi_clk(PROGRAMMER * pgm) +{ + unsigned char r = 0; + pgm->setpin(pgm, pgm->pinno[PIN_AVR_SCK], 1); + + r = pgm->getpin(pgm, pgm->pinno[PIN_AVR_MISO]); + + pgm->setpin(pgm, pgm->pinno[PIN_AVR_SCK], 0); + + return r; +} + +void bitbang_tpi_tx(PROGRAMMER * pgm, unsigned char byte) +{ + int i; + unsigned char b, parity; + + /* start bit */ + pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 0); + bitbang_tpi_clk(pgm); + + parity = 0; + for (i = 0; i <= 7; i++) { + b = (byte >> i) & 0x01; + parity ^= b; + + /* set the data input line as desired */ + pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], b); + bitbang_tpi_clk(pgm); + } + + /* parity bit */ + pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], parity); + bitbang_tpi_clk(pgm); + + /* 2 stop bits */ + pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 1); + bitbang_tpi_clk(pgm); + bitbang_tpi_clk(pgm); +} + +int bitbang_tpi_rx(PROGRAMMER * pgm) +{ + int i; + unsigned char b, rbyte, parity; + + /* make sure pin is on for "pullup" */ + pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 1); + + /* wait for start bit (up to 10 bits) */ + b = 1; + for (i = 0; i < 10; i++) { + b = bitbang_tpi_clk(pgm); + if (b == 0) + break; + } + if (b != 0) { + fprintf(stderr, "bitbang_tpi_rx: start bit not received correctly\n"); + return -1; + } + + rbyte = 0; + parity = 0; + for (i=0; i<=7; i++) { + b = bitbang_tpi_clk(pgm); + parity ^= b; + + rbyte |= b << i; + } + + /* parity bit */ + if (bitbang_tpi_clk(pgm) != parity) { + fprintf(stderr, "bitbang_tpi_rx: parity bit is wrong\n"); + return -1; + } + + /* 2 stop bits */ + b = 1; + b &= bitbang_tpi_clk(pgm); + b &= bitbang_tpi_clk(pgm); + if (b != 1) { + fprintf(stderr, "bitbang_tpi_rx: stop bits not received correctly\n"); + return -1; + } + + return rbyte; +} int bitbang_rdy_led(PROGRAMMER * pgm, int value) { @@ -267,6 +356,44 @@ int bitbang_cmd(PROGRAMMER * pgm, unsigned char cmd[4], return 0; } +int bitbang_cmd_tpi(PROGRAMMER * pgm, unsigned char cmd[], + int cmd_len, unsigned char res[], int res_len) +{ + int i, r; + + pgm->pgm_led(pgm, ON); + + for (i=0; i= 2) + { + fprintf(stderr, "bitbang_cmd_tpi(): [ "); + for(i = 0; i < cmd_len; i++) + fprintf(stderr, "%02X ", cmd[i]); + fprintf(stderr, "] [ "); + for(i = 0; i < res_len; i++) + { + fprintf(stderr, "%02X ", res[i]); + } + fprintf(stderr, "]\n"); + } + + pgm->pgm_led(pgm, OFF); + if (r == -1) + return -1; + return 0; +} + /* * transmit bytes via SPI and return the results; 'cmd' and * 'res' must point to data buffers @@ -308,6 +435,39 @@ int bitbang_chip_erase(PROGRAMMER * pgm, AVRPART * p) { unsigned char cmd[4]; unsigned char res[4]; + AVRMEM *mem; + + if (p->flags & AVRPART_HAS_TPI) { + pgm->pgm_led(pgm, ON); + + while (avr_tpi_poll_nvmbsy(pgm)); + + /* NVMCMD <- CHIP_ERASE */ + bitbang_tpi_tx(pgm, TPI_CMD_SOUT | TPI_SIO_ADDR(TPI_IOREG_NVMCMD)); + bitbang_tpi_tx(pgm, TPI_NVMCMD_CHIP_ERASE); /* CHIP_ERASE */ + + /* Set Pointer Register */ + mem = avr_locate_mem(p, "flash"); + if (mem == NULL) { + fprintf(stderr, "No flash memory to erase for part %s\n", + p->desc); + return -1; + } + bitbang_tpi_tx(pgm, TPI_CMD_SSTPR | 0); + bitbang_tpi_tx(pgm, (mem->offset & 0xFF) | 1); /* high byte */ + bitbang_tpi_tx(pgm, TPI_CMD_SSTPR | 1); + bitbang_tpi_tx(pgm, (mem->offset >> 8) & 0xFF); + + /* write dummy value to start erase */ + bitbang_tpi_tx(pgm, TPI_CMD_SST); + bitbang_tpi_tx(pgm, 0xFF); + + while (avr_tpi_poll_nvmbsy(pgm)); + + pgm->pgm_led(pgm, OFF); + + return 0; + } if (p->op[AVR_OP_CHIP_ERASE] == NULL) { fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n", @@ -336,6 +496,19 @@ int bitbang_program_enable(PROGRAMMER * pgm, AVRPART * p) { unsigned char cmd[4]; unsigned char res[4]; + int i; + + if (p->flags & AVRPART_HAS_TPI) { + /* enable NVM programming */ + bitbang_tpi_tx(pgm, TPI_CMD_SKEY); + for (i = sizeof(tpi_skey) - 1; i >= 0; i--) + bitbang_tpi_tx(pgm, tpi_skey[i]); + + /* check NVMEN bit */ + bitbang_tpi_tx(pgm, TPI_CMD_SLDCS | TPI_REG_TPISR); + i = bitbang_tpi_rx(pgm); + return (i != -1 && (i & TPI_REG_TPISR_NVMEN)) ? 0 : -2; + } if (p->op[AVR_OP_PGM_ENABLE] == NULL) { fprintf(stderr, "program enable instruction not defined for part \"%s\"\n", @@ -360,17 +533,68 @@ int bitbang_initialize(PROGRAMMER * pgm, AVRPART * p) { int rc; int tries; + int i; bitbang_calibrate_delay(); pgm->powerup(pgm); usleep(20000); + /* TPIDATA is a single line, so MISO & MOSI should be connected */ + if (p->flags & AVRPART_HAS_TPI) { + /* make sure cmd_tpi() is defined */ + if (pgm->cmd_tpi == NULL) { + fprintf(stderr, "%s: Error: %s programmer does not support TPI\n", + progname, pgm->type); + return -1; + } + + /* bring RESET high first */ + pgm->setpin(pgm, pgm->pinno[PIN_AVR_RESET], 1); + usleep(1000); + + if (verbose >= 2) + fprintf(stderr, "doing MOSI-MISO link check\n"); + + pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 0); + if (pgm->getpin(pgm, pgm->pinno[PIN_AVR_MISO]) != 0) { + fprintf(stderr, "MOSI->MISO 0 failed\n"); + return -1; + } + pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 1); + if (pgm->getpin(pgm, pgm->pinno[PIN_AVR_MISO]) != 1) { + fprintf(stderr, "MOSI->MISO 1 failed\n"); + return -1; + } + + if (verbose >= 2) + fprintf(stderr, "MOSI-MISO link present\n"); + } + pgm->setpin(pgm, pgm->pinno[PIN_AVR_SCK], 0); pgm->setpin(pgm, pgm->pinno[PIN_AVR_RESET], 0); usleep(20000); - pgm->highpulsepin(pgm, pgm->pinno[PIN_AVR_RESET]); + if (p->flags & AVRPART_HAS_TPI) { + /* keep TPIDATA high for 16 clock cycles */ + pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 1); + for (i = 0; i < 16; i++) + pgm->highpulsepin(pgm, pgm->pinno[PIN_AVR_SCK]); + + /* remove extra guard timing bits */ + bitbang_tpi_tx(pgm, TPI_CMD_SSTCS | TPI_REG_TPIPCR); + bitbang_tpi_tx(pgm, 0x7); + + /* read TPI ident reg */ + bitbang_tpi_tx(pgm, TPI_CMD_SLDCS | TPI_REG_TPIIR); + rc = bitbang_tpi_rx(pgm); + if (rc != 0x80) { + fprintf(stderr, "TPIIR not correct\n"); + return -1; + } + } else { + pgm->highpulsepin(pgm, pgm->pinno[PIN_AVR_RESET]); + } usleep(20000); /* 20 ms XXX should be a per-chip parameter */ diff --git a/bitbang.h b/bitbang.h index eb959c7d..ae05a89c 100644 --- a/bitbang.h +++ b/bitbang.h @@ -2,6 +2,7 @@ * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2000, 2001, 2002, 2003 Brian S. Dean * Copyright (C) 2005 Michael Holzt + * Copyright 2011 Darell Tan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,6 +40,8 @@ int bitbang_pgm_led (PROGRAMMER * pgm, int value); int bitbang_vfy_led (PROGRAMMER * pgm, int value); int bitbang_cmd (PROGRAMMER * pgm, unsigned char cmd[4], unsigned char res[4]); +int bitbang_cmd_tpi (PROGRAMMER * pgm, unsigned char cmd[], + int cmd_len, unsigned char res[], int res_len); int bitbang_spi (PROGRAMMER * pgm, unsigned char cmd[], unsigned char res[], int count); int bitbang_chip_erase (PROGRAMMER * pgm, AVRPART * p); diff --git a/doc/avrdude.texi b/doc/avrdude.texi index f99a2d53..2933edbd 100644 --- a/doc/avrdude.texi +++ b/doc/avrdude.texi @@ -2436,6 +2436,44 @@ Solution: Use the following pin mapping: @item 6 (GND) @tab GND @tab 2 @end multitable +@item +Problem: I want to program an ATtiny4/5/9/10 device using a serial/parallel +bitbang programmer. How to connect the pins? + +Solution: Since TPI has only 1 pin for bi-directional data transfer, both +@var{MISO} and @var{MOSI} pins should be connected to the @var{TPIDATA} pin +on the ATtiny device. +However, a 1K resistor should be placed between the @var{MOSI} and @var{TPIDATA}. +The @var{MISO} pin connects to @var{TPIDATA} directly. +The @var{SCK} pin is connected to @var{TPICLK}. + +In addition, the @var{Vcc}, @var{/RESET} and @var{GND} pins should +be connected to their respective ports on the ATtiny device. + +@item +Problem: How can I use a FTDI FT232R USB-to-Serial device for bitbang programming? + +Solution: When connecting the FT232 directly to the pins of the target Atmel device, +the polarity of the pins defined in the @code{programmer} definition should be +inverted by prefixing a tilde. For example, the @var{dasa} programmer would +look like this when connected via a FT232R device (notice the tildes in +front of pins 7, 4, 3 and 8): + +@example +programmer + id = "dasa_ftdi"; + desc = "serial port banging, reset=rts sck=dtr mosi=txd miso=cts"; + type = serbb; + reset = ~7; + sck = ~4; + mosi = ~3; + miso = ~8; +; +@end example + +Note that this uses the FT232 device as a normal serial port, not using the +FTDI drivers in the special bitbang mode. + @item Problem: My ATtiny4/5/9/10 reads out fine, but any attempt to program it (through TPI) fails. Instead, the memory retains the old contents. diff --git a/par.c b/par.c index 5a42174d..9399c4dc 100644 --- a/par.c +++ b/par.c @@ -416,6 +416,7 @@ void par_initpgm(PROGRAMMER * pgm) pgm->program_enable = bitbang_program_enable; pgm->chip_erase = bitbang_chip_erase; pgm->cmd = bitbang_cmd; + pgm->cmd_tpi = bitbang_cmd_tpi; pgm->spi = bitbang_spi; pgm->open = par_open; pgm->close = par_close; diff --git a/pgm.c b/pgm.c index 07fa34cf..6c80dac9 100644 --- a/pgm.c +++ b/pgm.c @@ -118,6 +118,7 @@ PROGRAMMER * pgm_new(void) * assigned before they are called */ pgm->cmd = NULL; + pgm->cmd_tpi = NULL; pgm->spi = NULL; pgm->paged_write = NULL; pgm->paged_load = NULL; diff --git a/pgm.h b/pgm.h index 2bd125be..51c6dd4b 100644 --- a/pgm.h +++ b/pgm.h @@ -78,6 +78,8 @@ typedef struct programmer_t { int (*chip_erase) (struct programmer_t * pgm, AVRPART * p); int (*cmd) (struct programmer_t * pgm, unsigned char cmd[4], unsigned char res[4]); + int (*cmd_tpi) (struct programmer_t * pgm, unsigned char cmd[], + int cmd_len, unsigned char res[], int res_len); int (*spi) (struct programmer_t * pgm, unsigned char cmd[], unsigned char res[], int count); int (*open) (struct programmer_t * pgm, char * port); diff --git a/serbb_posix.c b/serbb_posix.c index 5f886c42..668a2079 100644 --- a/serbb_posix.c +++ b/serbb_posix.c @@ -300,6 +300,7 @@ void serbb_initpgm(PROGRAMMER *pgm) pgm->program_enable = bitbang_program_enable; pgm->chip_erase = bitbang_chip_erase; pgm->cmd = bitbang_cmd; + pgm->cmd_tpi = bitbang_cmd_tpi; pgm->open = serbb_open; pgm->close = serbb_close; pgm->setpin = serbb_setpin; diff --git a/serbb_win32.c b/serbb_win32.c index 62c70405..2f5af8ad 100644 --- a/serbb_win32.c +++ b/serbb_win32.c @@ -361,6 +361,7 @@ void serbb_initpgm(PROGRAMMER *pgm) pgm->program_enable = bitbang_program_enable; pgm->chip_erase = bitbang_chip_erase; pgm->cmd = bitbang_cmd; + pgm->cmd_tpi = bitbang_cmd_tpi; pgm->open = serbb_open; pgm->close = serbb_close; pgm->setpin = serbb_setpin;