Maker.io main logo

MicroProgrammer

2016-05-25 | By Sergey Sokol

License: None

Overview

At times you want a simple programmer that is small, simple, inexpensive and fills its niche without any bells and whistles. We wanted to create a programmer that can program ATMega and ATTiny microcontrollers via the ISP interface. Our intention is that there will be no debugging or JTAG, this is simply an extremely small flash programmer.

Approach

As our primary goals are to have the programmer be small and cheap, we are going to attempt to make the board small enough to fit within the head of a USB cable itself. This will increase portability, eliminate the need for a custom enclosure, and protect the hardware from the vagaries of bench top life.

The firmware has already been completed for this project by other programmers though the PC application and GUI interface need to be created. This interface will be a straightforward GUI that, at first, will be entirely focused on getting the necessary functionality.

As people familiar with any sort of flasher/programmer for MCUs, there’s often the ability to control the voltage on the board or chip being programmed. We wish to do the same with this to allow the flexibility of programming chips in circuits where there may be more sensitivity or specific requirements for the voltages while being charged.

Challenges and Design Considerations

Size is a challenge. Using smaller parts means that PCB design isn’t as difficult, yet, as the prototypes are done by hand, 0402 is the smallest reasonable size we can hand place. In this we run into a wall in that we can’t get smaller than 0402 without making it impossible to assemble, which puts the pressure on PCB design.

As we’ve done much more firmware programming than PC, and particularly GUI PC, programming, we anticipate this to be a challenge to get it working as we’d like it.

End Result

We were able to achieve our size goals and hide the entirety of the board inside the USB connector head, though there were some minor PCB errors that required some “airborne traces”. Despite these wires, everything still exactly as planned, though the wires did get squished a bit.

The PC application is much as we anticipated - it is rough yet fully functional. The ability to change the voltage based off of PC input was a bit of a challenge as we didn’t want any physical switches to be involved. This required a controllable LDO but it ended up working well and has reasonable accuracy in the output voltage.

The overall project was a success in that it performed its role perfectly. It flashes the target boards quickly, is small and tough, even with the wires. It is also extremely low cost, the entire BOM is less than $3.

Moving Forward

Other than minor changes to the PCB design, which have already been completed, most of the changes will be done to test it more thoroughly and identify if there’s a need to make it more robust or if there is anything that can be simplified to reduce cost. Another goal will be to make the PC application more robust as well. Once the foundation of the application has been more firmly established, a review of the user interface will be performed, to make it a more pleasing experience for the end user.

 
Copy Code
/* Name: main.c
* Project: AVR USB driver for CDC-SPI interface on Low-Speed USB
* for ATtiny2313
* Author: Osamu Tamura
* Tabsize: 4
* Copyright: (c) 2010 by Recursion Co., Ltd.
* License: Proprietary, free under certain conditions. See Documentation.
*
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
//#include <avr/wdt.h>
#include <util/delay.h>

#include "usbdrv.h"
#include "oddebug.h"


#define SPI_DDR DDRB
#define SPI_PORT PORTB
#define SPI_PIN PINB
#define SPI_SS 0 // b1:DTS, b0:Break
#define SPI_SPEED 4 // 1:1MHz, 0:125kHz
#define SPI_MOSI 5
#define SPI_MISO 6
#define SPI_SCK 7

#define HW_CDC_BULK_OUT_SIZE 8
#define HW_CDC_BULK_IN_SIZE 8


enum {
SEND_ENCAPSULATED_COMMAND = 0,
GET_ENCAPSULATED_RESPONSE,
SET_COMM_FEATURE,
GET_COMM_FEATURE,
CLEAR_COMM_FEATURE,
SET_LINE_CODING = 0x20,
GET_LINE_CODING,
SET_CONTROL_LINE_STATE,
SEND_BREAK
};


static PROGMEM char configDescrCDC[] = { /* USB configuration descriptor */
9, /* sizeof(usbDescrConfig): length of descriptor in bytes */
USBDESCR_CONFIG, /* descriptor type */
67,
0, /* total length of data returned (including inlined descriptors) */
2, /* number of interfaces in this configuration */
1, /* index of this configuration */
0, /* configuration name string index */
#if USB_CFG_IS_SELF_POWERED
(1 << 7) | USBATTR_SELFPOWER, /* attributes */
#else
(1 << 7), /* attributes */
#endif
USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */

/* interface descriptor follows inline: */
9, /* sizeof(usbDescrInterface): length of descriptor in bytes */
USBDESCR_INTERFACE, /* descriptor type */
0, /* index of this interface */
0, /* alternate setting for this interface */
USB_CFG_HAVE_INTRIN_ENDPOINT, /* endpoints excl 0: number of endpoint descriptors to follow */
USB_CFG_INTERFACE_CLASS,
USB_CFG_INTERFACE_SUBCLASS,
USB_CFG_INTERFACE_PROTOCOL,
0, /* string index for interface */

/* CDC Class-Specific descriptor */
5, /* sizeof(usbDescrCDC_HeaderFn): length of descriptor in bytes */
0x24, /* descriptor type */
0, /* header functional descriptor */
0x10, 0x01,

4, /* sizeof(usbDescrCDC_AcmFn): length of descriptor in bytes */
0x24, /* descriptor type */
2, /* abstract control management functional descriptor */
0x06, /* SEND_BREAK,SET_LINE_CODING,GET_LINE_CODING,SET_CONTROL_LINE_STATE */

5, /* sizeof(usbDescrCDC_UnionFn): length of descriptor in bytes */
0x24, /* descriptor type */
6, /* union functional descriptor */
0, /* CDC_COMM_INTF_ID */
1, /* CDC_DATA_INTF_ID */

5, /* sizeof(usbDescrCDC_CallMgtFn): length of descriptor in bytes */
0x24, /* descriptor type */
1, /* call management functional descriptor */
3, /* allow management on data interface, handles call management by itself */
1, /* CDC_DATA_INTF_ID */

/* Endpoint Descriptor */
7, /* sizeof(usbDescrEndpoint) */
USBDESCR_ENDPOINT, /* descriptor type = endpoint */
0x83, /* IN endpoint number 3 */
0x03, /* attrib: Interrupt endpoint */
8, 0, /* maximum packet size */
USB_CFG_INTR_POLL_INTERVAL, /* in ms */

/* Interface Descriptor */
9, /* sizeof(usbDescrInterface): length of descriptor in bytes */
USBDESCR_INTERFACE, /* descriptor type */
1, /* index of this interface */
0, /* alternate setting for this interface */
2, /* endpoints excl 0: number of endpoint descriptors to follow */
0x0A, /* Data Interface Class Codes */
0,
0, /* Data Interface Class Protocol Codes */
0, /* string index for interface */

/* Endpoint Descriptor */
7, /* sizeof(usbDescrEndpoint) */
USBDESCR_ENDPOINT, /* descriptor type = endpoint */
0x01, /* OUT endpoint number 1 */
0x02, /* attrib: Bulk endpoint */
HW_CDC_BULK_OUT_SIZE, 0, /* maximum packet size */
0, /* in ms */

/* Endpoint Descriptor */
7, /* sizeof(usbDescrEndpoint) */
USBDESCR_ENDPOINT, /* descriptor type = endpoint */
0x81, /* IN endpoint number 1 */
0x02, /* attrib: Bulk endpoint */
HW_CDC_BULK_IN_SIZE, 0, /* maximum packet size */
0, /* in ms */
};


uchar usbFunctionDescriptor(usbRequest_t *rq)
{

if(rq->wValue.bytes[1] == USBDESCR_DEVICE){
usbMsgPtr = (uchar *)usbDescriptorDevice;
return usbDescriptorDevice[0];
}else{ /* must be config descriptor */
usbMsgPtr = (uchar *)configDescrCDC;
return sizeof(configDescrCDC);
}
}

/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */

static uchar sendEmptyFrame;

uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;

if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
uchar value;

/* GET_LINE_CODING -> usbFunctionRead() */
if( rq->bRequest==GET_LINE_CODING )
return 0xff;

value = rq->wValue.word;
if( rq->bRequest==SEND_BREAK )
{
if (value)
{
SPI_PORT |= (1<<SPI_SS);
SPI_DDR &= ~((1<<SPI_SCK)|(1<<SPI_MISO));
}
else
{
SPI_PORT &= ~(1<<SPI_SS);
SPI_DDR |= (1<<SPI_SCK)|(1<<SPI_MISO);
}
}
}

return 0;
}


/*---------------------------------------------------------------------------*/
/* usbFunctionRead */
/*---------------------------------------------------------------------------*/

uchar usbFunctionRead( uchar *data, uchar len )
{
/* reply USART configuration */
#define BAUD 9600
data[0] = BAUD & 0xff;
data[1] = (uchar)(BAUD>>8);
data[2] = 0;
data[3] = 0;
data[4] = 0;
data[5] = 0;
data[6] = 8;

return 7;
}


/* spi buffer */
static uchar rx_buf[HW_CDC_BULK_IN_SIZE];
static uchar tx_buf[HW_CDC_BULK_OUT_SIZE];
static uchar iwptr, uwptr;
static uchar sck = 0;


void usbFunctionWriteOut( uchar *data, uchar len )
{

/* host -> spi */
while( len-- )
tx_buf[uwptr ] = *data ;

/* postpone receiving next data */
usbDisableAllRequests();
}


static void hardwareInit(void)
{
uchar i;

/* activate pull-ups except on USB lines */
USB_CFG_IOPORT = (uchar)~((1<<USB_CFG_DMINUS_BIT)|(1<<USB_CFG_DPLUS_BIT));
/* all pins input except USB (-> USB reset) */
#ifdef USB_CFG_PULLUP_IOPORT /* use usbDeviceConnect()/usbDeviceDisconnect() if available */
USBDDR = 0; /* we do RESET by deactivating pullup */
usbDeviceDisconnect();
#else
USBDDR = (1<<USB_CFG_DMINUS_BIT)|(1<<USB_CFG_DPLUS_BIT);
#endif

/* keep 300 mS */
for( i=150; --i; ) {
// wdt_reset();
_delay_ms(2.0);
}

#ifdef USB_CFG_PULLUP_IOPORT
usbDeviceConnect();
#else
USBDDR = 0; /* remove USB reset condition */
#endif
}


int main(void)
{
uchar i;
sck = 2;
odDebugInit();
hardwareInit();
usbInit();

/* set SCL, DO, and SS as output */
SPI_DDR |= (1<<SPI_SS);
SPI_PORT = (1<<SPI_SS);

//Set 5V supply voltage
PORTB &= ~_BV(PB1);
DDRB |= _BV(PB1);


/* SPI mode */
USICR = (1<<USIWM0)|(1<<USICS1)|(1<<USICLK);

sei();
for(;;)
{ /* main event loop */
// wdt_reset();
usbPoll();

/* host => device */
if( uwptr!=0 && iwptr==0 )
{
usbEnableAllRequests();

//Set programming speed
if (tx_buf[0] == 0xEE)
{
sck = tx_buf[1];
iwptr = 2;
}
//Set supply voltage
else if (tx_buf[0] == 0xEF)
{
if (tx_buf[1])
DDRB |= _BV(PB1);
else
DDRB &= ~_BV(PB1);
iwptr = 2;
}
//Data exchange
else
while( iwptr<uwptr )
{
USIDR = tx_buf[iwptr];
USISR = (1<<USIOIF);

if (sck)
do
{
for (i = 0; i < sck; i )
_delay_us(1);
USICR |= (1<<USITC);
} while( !(USISR&(1<<USIOIF)) );
else
do
{
USICR |= (1<<USITC);
} while( !(USISR&(1<<USIOIF)) );
rx_buf[iwptr ] = USIDR;
}
uwptr = 0;
}
/* host <= device */
if( usbInterruptIsReady() && (iwptr|sendEmptyFrame) )
{
usbSetInterrupt(rx_buf, iwptr);
sendEmptyFrame = iwptr & HW_CDC_BULK_IN_SIZE;
iwptr = 0;
}
}
return 0;
}
 

MicroProgrammer Photo 1

MicroProgrammer Photo 2

MicroProgrammer Photo 3

MicroProgrammer Photo 4

MicroProgrammer Photo 5

MicroProgrammer Photo 6

MicroProgrammer Photo 7

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.