NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » MicroView Program Load

November 02, 2014
by scootergarrett
scootergarrett's Avatar

I was hoping for a success story today with bootloading the MicroView which uses a 328P, but instead I have questions. So I got a microview and thankfully (for nerdkit users) sparkfun sent out some without a bootload so there is a tutorial on how to break it open and add a bootloader. I used (Noter's v3.0 AVR109 (AVRBOOT) Bootloader) and changed the program pin to PC0:

#define START_SWITCH        C,0                 /// New Program pin

Because PB0 is not broken out. I tested the bootload on a blank 328P DIP chip and it worked

but now when I set the MicroView up to be programed just like nerdkit: CD I keep getting this error:

Connecting to programmer: .
avrdude: butterfly_recv(): programmer is not responding

is there a problem that the MicroView uses a 16MHz crystal?

When/if I get this working correctly I will do a big write up on how I did it

November 03, 2014
by JimFrederickson
JimFrederickson's Avatar

Normally the Serial Port Bit Clock is based off of the MCU System Clock so the appropriate/proper prescaler needs to be set.

It has been customary for sections of code that are "clock dependent" use the "F_CPU" value to automatically adjust the code ot match the System Clock.

    #define F_CPU 14745600

(This is the original Nerdkit Clock value.)

Whether the code you are using for the bootloader is using this value, or using it appropriately, to setup of the prescaler for the UART would be the first thing I would look at.

Nerdkits chose their System Clock Spead based on provoding Baurdrates with the least amount of error for the most common speeds.

For me, I have found that doesn't realy seem to be an issue.

I, normally, run at 20mhz and the only real Serial Errors I encounter are associated with the USB-Serial Device I am using.

November 03, 2014
by scootergarrett
scootergarrett's Avatar

So you can load program on the MCU with a 20MHz crystal using the USB to serial cable that comes with a nerdkit? I have a 20Mhz crystal and I can't seem to load a program while using it.

November 06, 2014
by JimFrederickson
JimFrederickson's Avatar

I don't "specifically use the USB to Serial Cable that comes with a Nerdkit" to program
my MCU at 20mhz.

I do use my 328mega's at 16mhz and 20mhz, on a constant basis.

I transfer data VIA Serial between the MCU's as well as between the MCU's and
Windows/Ubuntu Computers. In nearly all cases I am using the same USB-Serial Cable
that came with my Nerdkits. (They work, and I hadn't seen a need to change them out.)

There was a thread on the Nerdkit site regarding "Serial Error Rates" and matching the
System Clock to the Standard Serial Rates was a concern.

I haven't noticed any issues with that.

I did post a thread A LONG TIME AGO, regarding Serial Error Rates.

At that time I was experiencing Serial Data Errors between my Computers and the MCU's.
(Of course MCU's with the same Crystal that would introduce the same Errors in the Standard Serial Rates wouldn't notice those errors, but I do transfer extensive data to my Computers.)

At first I was concerned it may be the Crystal I was using. (Well the speed of it.)

As it turned out, which I think is in those posts as well, most of the problem was the USB Port on a couple laptops. (It seems the hardware was going bad on both at a
similar time.) The rest of the issue was with my Java Serial Code.

I have since switched exclusively to Python and have no problems/errors transfering
millions of bytes of data accurately.

The minor error rates introduced by "dividing the System Clock down to the appropriate
standard Serial Baud Rates" do not create any issues/errors/problems. (Nearly all Serial Controllers will over sample the incoming bits, and that seems to be enough to catch any erorrs in the Serial Baud Rates.)

I haven't tried programming any of my projects using he Serial Interface at the clock
speeds I use currently.

I program my MCU's with an AVR ISP Programmer.

November 08, 2014
by scootergarrett
scootergarrett's Avatar

SUCCESS sort of, I can now load a program on the MicroView using Noters ISP. Unfortunately the MicroView has to be opened to program it. But for now its in a good position to develop the code to control the screen. Anyways I used the sparkfun walk through on opening the MicroView to access the serial pins of the 328p.

I used a knife to open the MicroView to get at the labeled vias inside. I plugged the MicroView into a bread board then plugged 3 wires in and brought the free end to the vias, then they where in place and I coun't wiggle them off by accedent. I wired it to the ISP where the nodes on the side are the internal numbered vias.

Now I'm going to work on actually using the screen.

For completeness here is Noters ISP code I used:

//
// AVR_ISP.c - Noter's ISP programmer for the NerdKit
//
// v3.0 - Turn the NerdKit into an AVR910 (AVR ISP) ISP programmer
//
//  Conversion from ATmel asm source code with nerdkit enhancement.
//
//  References -
//      avr109 ATmel application note.
//      avr910 ATmel application note.
//      avr911 ATmel application note.
//
//                                      +-------------+------------+------+
//  Commands                            | Host writes | Host reads |      |
//  --------                            +-----+-------+------+-----+      |
//                                      | ID  | data  | data |     | Note |
// +------------------------------------+-----+-------+------+-----+------+
// | Enter programming mode             | 'P' |       |      | 13d |   1  |
// | Report autoincrement address       | 'a' |       |      | 'Y' |      |
// | Set address                        | 'A' | ah al |      | 13d |   2  |
// | Write program memory, low byte     | 'c' |    dd |      | 13d |   3  |
// | Write program memory, high byte    | 'C' |    dd |      | 13d |   3  |
// | Issue Page Write                   | 'm' |       |      | 13d |      |
// | Read program memory                | 'R' |       |dd(dd)|     |   4  |
// | Write data memory                  | 'D' |    dd |      | 13d |      |
// | Read data memory                   | 'd' |       |   dd |     |      |
// | Chip erase                         | 'e' |       |      | 13d |      |
// | check block support                | 'b' |       |dd dd |  Y  |  14  |
// | flash or eeprom block read         | 'g' |       |dd(dd)|     |  14  |
// | flash or eeprom block write        | 'B' |dd(dd) |      | 13d |      |
// | Write fuse bits                    | 'f' |    dd |      | 13d |  11  |
// | Read fuse bits                     | 'F' |       |   dd |     |  11  |
// | Write high fuse bits               | 'n' |    dd |      | 13d |  14  |
// | Read high fuse bits                | 'N' |       |   dd |     |  14  |
// | Write extended fuse bits           | 'q' |    dd |      | 13d |  14  |
// | Read extended fuse bits            | 'Q' |       |   dd |     |  14  |
// | Write lock bits                    | 'l' |    dd |      | 13d |  14  |
// | Read lock bits                     | 'r' |       |   dd |     |  14  |
// | Leave programming mode             | 'L' |       |      | 13d |   5  |
// | Select device type                 | 'T' |    dd |      | 13d |   6  |
// | Read signature bytes               | 's' |       | 3*dd |     |      |
// | Return supported device codes      | 't' |       | n*dd | 00d |   7  |
// | Return software identifier         | 'S' |       | s[7] |     |   8  |
// | Return sofware version             | 'V' |       |dd dd |     |   9  |
// | Return hardware version            | 'v' |       |dd dd |     |   9  |
// | Return programmer type             | 'p' |       |   dd |     |  10  |
// | Universial command                 | ':' |  3*dd |   dd | 13d |      |
// | New universal command              | '.' |  4*dd |   dd | 13d |      |
// | Exit bootloader                    | 'E' |       |      | 13d |  15  |
// | Select target avr                  | 'Z' |   dd  |      | 13d |      |
// | ESC - sync char                    | x1B |   dd  |      |     |      |
// +------------------------------------+-----+-------+------+-----+------+
/*
;* NOTE 1
;*  The 'P' Enter programming mode command MUST be sent one time prior to
;*  the other commands, with the exception of the 't', 'S', 'V', 'v', 'T',
;*  and 'Z' commands. The 'T' command must be sent before the 'P'
;*  (see note 6).
;*
;*  For programmers supporting both parallel and serial programming
;*  mode this command enters parallel programming mode. For programmers
;*  supporting only serial programming mode, this command enters serial
;*  programming mode.
;*
;* NOTE 2
;*  The ah and al are the high and low order bytes of the address. For
;*  parallel programmers this command issues the Load Address Low/High
;*  Byte command. For serial programmers the address byte is stored for
;*  use by the Read/Write commands.
;*
;* NOTE 3
;*  For parallel programmers this command issues the Program Flash
;*  command. For serial programmers this command iussues the Write
;*  Program Memory Command. For devices with byte-wide program memories
;*  only the low byte command should be used.
;*
;* NOTE 4
;*  The contents of the program memory at the address given by the 'A'
;*  command are written to the serial port in binary form. For byte
;*  wide memories one byte is written. For 16 bit memories two bytes
;*  are written,MSB first.
;*
;* NOTE 5
;*  This command must be executed after the programming is finished.
;*
;* NOTE 6
;*  The select device type command must be sent before the enter
;*  programming command
;*
;* NOTE 7
;*  The supported device codes are returned in binary form terminated
;*  by 0x00.
;*
;* NOTE 8
;*  This returns a 7 character ASCII string identifying the programmer.
;*  For the development board it is "AVR DEV", for the parallel
;*  programmer it is "AVR PPR" and for the in-curcuit programmer it is
;*  "AVR ICP". For the nerdkit it's "Nerdkit".
;*
;* NOTE 9
;*  The software/hardware version are returned as two ASCII numbers.
;*
;* NOTE 10
;*  This command should be used to identify the programmer type. The
;*  return value is 'S' for serial (or SPI) programmers or 'P' for
;*  parallel programmers.
;*
;* NOTE 11 - (obsolete info? - noter)
;*  The write fuse bits command are available only on parallel
;*  programmers and only for AVR devices (device code < 0x80). The host
;*  should use the return programmer type command to determine the
;*  programmer type, do not use the  "AVR PPR" idenifier because other
;*  programmers may be available in the future.
;*
;* NOTE 12
;*  Currently only the AVR development board has LEDs. The other boards
;*  must implement this commands as NOPs.
;*
;* NOTE 13
;*      Devices using Page Mode Programming write one page of flash memory
;*      before issuing a Page Mode Write Pulse.
;*
;* NOTE 14 (noter)
;*      Support for newer devices include block read/write, high fuse,
;*      extended fuse, and lock bits. These are added in the nerdkits v3.0 code.
;*      Tested only with Avrdude and ATmega168, Atmega328, ATtiny85
;*      devices.
;*
;* NOTE 15 (noter)
;*      Exit bootloader is meaningless but is included to satisify
;*      avrdude thus prevent an error message on quit terminal mode.
;*
;* HISTORY
;*  V3.0    11.04.10 (noter)        Convert to C, use libnerdkits uart and LCD.
;*                                      High fuse, extended fuse, lock bits read/write implemented.
;*                                      Uart rx ring buffer implemented for overlapping IO - faster.
;*                                      Implement multi-port supporting 4 targets via 'Z' command.
;*  V2.3    00.03.10 (pkastnes)     Added support for multiple new devices
;*  V2.1    98.10.26 (mlund)        New date marking.
;*                                      Removed support for AT90S1200C.
;*                                      Added support for AT90S4433A.
;*  V2.0    98.01.06 (mlund)        ATmega103 support
;*  V1.7    97.11.06 (mlund)        Universial command (':') implemented.
;*                                      Releases all pins when not in
;*                                      programming mode.
;*  V1.6e   97.11.04 (mlund)        mega103 rev D support
;*  V1.6c   97.10.30 (mlund)        Auto incrementing / SPI sync
;*                                      also works for mega103.
;*  V1.6    97.09.09 (hskar)        Created Page Mode Version (mega103)
;*  V1.5    97.08.21 (mlund)        Modified / Bugfix / Major cleanup
;*  ... ...                         (no records)
;*  V?.?    97.03.15 (OS)           Created
*/

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

#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// preprocessor macros for port/pin manulipulation
//
#define INPUT2(port,pin) DDR ## port &= ~_BV(pin)
#define OUTPUT2(port,pin) DDR ## port |= _BV(pin)
#define CLEAR2(port,pin) PORT ## port &= ~_BV(pin)
#define SET2(port,pin) PORT ## port |= _BV(pin)
#define TOGGLE2(port,pin) PORT ## port ^= _BV(pin)
#define READ2(port,pin) ((PIN ## port & _BV(pin))?1:0)
//
#define INPUT(x) INPUT2(x)
#define OUTPUT(x) OUTPUT2(x)
#define CLEAR(x) CLEAR2(x)
#define SET(x) SET2(x)
#define TOGGLE(x) TOGGLE2(x)
#define READ(x) READ2(x)
#define PULLUP_ON(x) INPUT2(x); SET2(x)
#define PULLUP_OFF(x) INPUT2(x); CLEAR2(x)

// define ports, pins
//
#define RESET_0     B,2

#define MOSI        B,3
#define MSIO        B,4
#define SCK         B,5

// local function prototypes
//
void spi_init(void);
uint8_t spi_char(uint8_t send);
void spi_cmd(uint8_t *send, uint8_t *reply, int length);
void show_device_name(void);
char get_signature(void);
void uart_write_PSTR(char *x);
uint8_t uart_read_q(void);
void wait_AVR_Busy();
uint8_t enter_programming_mode();
void leave_programming_mode(void);
void uart_writeX(char x);

// SPI/AVR variables/values/macros
//
#define HIGH(x) ( (uint8_t) (x >> 8) )
#define LOW(x)  ( (uint8_t) x )
#define SPI_OK 0
#define SPI_TIMEOUT 1
#define SPI_ERROR   2
#define CMD_LEN 4
#define SIG_LEN 3
#define MEMORY_TYPE_FLASH 'F'
#define MEMORY_TYPE_EEPROM 'E'
uint8_t SPI_Cmd[CMD_LEN];
uint8_t SPI_Reply[CMD_LEN];
uint8_t AVR_Signature[SIG_LEN];
uint8_t SPCR_enable_mask;
uint16_t address;
uint16_t page_address;
uint16_t byte_count;
uint8_t memory_type;
bool in_programming_mode;
uint8_t target_port;
uint16_t flash_byte_count;
uint16_t eeprom_byte_count;

// UART specific - ring buffer
//
#define Q_SIZE 140
volatile uint8_t q_buff[Q_SIZE];
volatile uint8_t qin;
volatile uint8_t qout;

// programmer info
//
#define PROGRAMMER_ID       "AVR ISP"
#define HARDWARE_VERSION    "01"
#define SOFTWARE_VERSION    "30"

/* Supported Devices
* +-------+----------+------+-------+------+------+------+-------+
* |Device |Signature | Code | Flash |EEProm| Lock | Fuse | PMode |
* +-------+----------+------+-------+------+------+------+-------+
* |tiny85 | 1E 93 0B | 0x01 |  R/W  | R/W  | R/W  | R/W  | Page  |
* |mega168| 1E 94 06 | 0x02 |  R/W  | R/W  | R/W  | R/W  | Page  |
* |mega328| 1E 95 0F | 0x03 |  R/W  | R/W  | R/W  | R/W  | Page  |
* +-------+----------+------+-------+------+------+------+-------+
*/
typedef struct {
    char        name[10];
    uint8_t     signature[SIG_LEN];
    uint8_t     device_code;
    uint16_t    page_size;
    uint16_t    page_count;
}   AVR_DEVICE;
AVR_DEVICE target_avr;
#define DEVICE_COUNT 3
const AVR_DEVICE  avr_info[DEVICE_COUNT] PROGMEM =    {
                                                    {   "ATtiny85",
                                                        "\x1E\x93\x0B",
                                                        0x01,
                                                        64,
                                                        128
                                                    },
                                                    {   "ATmega168",
                                                        "\x1E\x94\x06",
                                                        0x02,
                                                        128,
                                                        128

                                                    },
                                                    {   "ATmega328",
                                                        "\x1E\x95\x0F",
                                                        0x03,
                                                        128,
                                                        256
                                                    }
                                                };

// LCD stream file - enable printf in functions outside of main()
FILE lcd_stream;
#define BLANK_LINE "                  "

// --------------------------------------------------------------------------------------------------------
int main() {
    // initialize LCD display
    lcd_init();
    fdev_setup_stream(&lcd_stream, lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_clear_and_home();
    fprintf_P(&lcd_stream, PSTR("AVR ISP v3.0"));

    // initialize uart, set baudrate, and enable rx interrupts
    uart_init();
    UCSR0A = 0;
    UBRR0L = (F_CPU/(16*BAUD))-1;
    UCSR0B |= _BV(RXCIE0);
    sei();

    // initialize SPI interface
    spi_init();

    // process commands from host
    int i;
    char command;
    while(true){
                                //  Commands                            | Host writes | Host reads |      |
                                //  --------                            +-----+-------+------+-----+      |
                                //                                      | ID  | data  | data |     | Note |
        command=uart_read_q();
        switch(command){
            case 'P':           // | Enter programming mode             | 'P' |       |      | 13d |   1  |
                if(!enter_programming_mode())
                    uart_writeX('\r');
                break;

            case 'L':           // | Leave programming mode             | 'L' |       |      | 13d |   5  |
                leave_programming_mode();
                uart_writeX('\r');
                break;

            case 'a':           // | Report autoincrement address       | 'a' |       |      | 'Y' |      |
                uart_writeX('Y');
                break;

            case 'A':           // | Set address                        | 'A' | ah al |      | 13d |   2  |
                // word address for flash, byte address for eeprom
                address=(uart_read_q()<<8)|uart_read_q();
                uart_writeX('\r');
                break;

            case 'c':           // | Write program memory, low byte     | 'c' |    dd |      | 13d |   3  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\x40\x00\x00\x00"),CMD_LEN);
                SPI_Cmd[2]=LOW(address);
                SPI_Cmd[3]=uart_read_q();
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                flash_byte_count++;
                uart_writeX('\r');
                break;

            case 'C':           // | Write program memory, high byte    | 'C' |    dd |      | 13d |   3  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\x48\x00\x00\x00"),CMD_LEN);
                SPI_Cmd[2]=LOW(address);
                SPI_Cmd[3]=uart_read_q();
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                address++; // auto-increment on high byte only
                flash_byte_count++;
                uart_writeX('\r');
                break;

            case 'm':           // | Issue Page Write                   | 'm' |       |      | 13d |      |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\x4C\x00\x00\x00"),CMD_LEN);
                SPI_Cmd[1]=HIGH(address);
                SPI_Cmd[2]=LOW(address);
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX('\r');
                break;

            case 'R':           // | Read program memory                | 'R' |       |dd(dd)|     |   4  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\x28\x00\x00\x00"),CMD_LEN); // High byte 1st
                SPI_Cmd[1]=HIGH(address);
                SPI_Cmd[2]=LOW(address);
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX(SPI_Reply[3]);
                memcpy_P(SPI_Cmd,PSTR("\x20\x00\x00\x00"),CMD_LEN); // Low byte 2nd
                SPI_Cmd[1]=HIGH(address);
                SPI_Cmd[2]=LOW(address);
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX(SPI_Reply[3]);
                address++;  // auto-increment
                break;

            case 'D':           // | Write data memory                  | 'D' |    dd |      | 13d |      |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\xC0\x00\x00\x00"),CMD_LEN);
                SPI_Cmd[1]=HIGH(address);
                SPI_Cmd[2]=LOW(address);
                SPI_Cmd[3]=uart_read_q();
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                eeprom_byte_count++;
                uart_writeX('\r');
                break;

            case 'd':           // | Read data memory                   | 'd' |       |   dd |     |      |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\xA0\x00\x00\x00"),CMD_LEN);
                SPI_Cmd[1]=HIGH(address);
                SPI_Cmd[2]=LOW(address);
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX(SPI_Reply[3]);
                break;

            case 'e':           // | Chip erase                         | 'e' |       |      | 13d |      |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\xAC\x80\x00\x00"),CMD_LEN);
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX('\r');
                break;

            case 'b':           // | check block support                | 'b' |       |dd dd |  Y  |      |
                uart_writeX('Y');
                if(!enter_programming_mode()) // query target avr and get info
                    if(!get_signature()){
                        uart_writeX(target_avr.page_size>>8);
                        uart_writeX(target_avr.page_size&0xFF);
                    }
                break;

            case 'B':           // | flash or eeprom block write        | 'B' |dd(dd) |      | 13d |      |
                byte_count=(uart_read_q()<<8)|uart_read_q();
                memory_type=uart_read_q();
                wait_AVR_Busy();
                if(memory_type==MEMORY_TYPE_FLASH){
                    page_address=address;
                    for(i=0;i<byte_count;i+=2){
                        memcpy_P(SPI_Cmd,PSTR("\x40\x00\x00\x00"),CMD_LEN);
                        SPI_Cmd[2]=LOW(address);
                        SPI_Cmd[3]=uart_read_q();
                        spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                        memcpy_P(SPI_Cmd,PSTR("\x48\x00\x00\x00"),CMD_LEN);
                        SPI_Cmd[2]=LOW(address);
                        SPI_Cmd[3]=uart_read_q();
                        spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                        address++; // auto-increment
                        flash_byte_count+=2;
                    }
                    memcpy_P(SPI_Cmd,PSTR("\x4C\x00\x00\x00"),CMD_LEN);
                    SPI_Cmd[1]=HIGH(page_address);
                    SPI_Cmd[2]=LOW(page_address);
                    spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                    uart_writeX('\r');
                } else
                    if(memory_type==MEMORY_TYPE_EEPROM){
                        // eeprom - one byte at a time ...
                        for(i=0;i<byte_count;i++){
                            wait_AVR_Busy(); // eeprom is not so fast
                            memcpy_P(SPI_Cmd,PSTR("\xC0\x00\x00\x00"),CMD_LEN);
                            SPI_Cmd[1]=HIGH(address);
                            SPI_Cmd[2]=LOW(address);
                            SPI_Cmd[3]=uart_read_q();
                            spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                            address++;  // auto-increment
                            eeprom_byte_count++;
                        }
                        uart_writeX('\r');
                    }
                break;

            case 'g':           // | flash or eeprom block read         | 'g' |       |dd(dd)|     |      |
                wait_AVR_Busy();
                byte_count=(uart_read_q()<<8)|uart_read_q();
                memory_type=uart_read_q();
                if(memory_type==MEMORY_TYPE_FLASH){
                    for(i=0;i<byte_count;i+=2){
                        memcpy_P(SPI_Cmd,PSTR("\x20\x00\x00\x00"),CMD_LEN);
                        SPI_Cmd[1]=HIGH(address);
                        SPI_Cmd[2]=LOW(address);
                        spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                        uart_writeX(SPI_Reply[3]);
                        memcpy_P(SPI_Cmd,PSTR("\x28\x00\x00\x00"),CMD_LEN);
                        SPI_Cmd[1]=HIGH(address);
                        SPI_Cmd[2]=LOW(address);
                        spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                        uart_writeX(SPI_Reply[3]);
                        address ++;  // auto-increment
                    }
                }else
                    if(memory_type==MEMORY_TYPE_EEPROM)
                        // 8 bit memory
                        for(i=0;i<byte_count;i+=1){
                            memcpy_P(SPI_Cmd,PSTR("\xA0\x00\x00\x00"),CMD_LEN);
                            SPI_Cmd[1]=HIGH(address);
                            SPI_Cmd[2]=LOW(address);
                            spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                            uart_writeX(SPI_Reply[3]);
                            address ++;  // auto-increment
                        }
                break;

            case 'f':           // | Write lfuse bits                   | 'f' |    dd |      | 13d |  11  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\xAC\xA0\x00\x00"),CMD_LEN);
                SPI_Cmd[3]=uart_read_q();
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX('\r');
                break;

            case 'F':           // | Read lfuse bits                    | 'F' |       |   dd |     |  11  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\x50\x00\x00\x00"),CMD_LEN);
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX(SPI_Reply[3]);
                break;

            case 'n':           // | Write high fuse bits               | 'n' |    dd |      | 13d |  14  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\xAC\xA8\x00\x00"),CMD_LEN);
                SPI_Cmd[3]=uart_read_q();
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX('\r');
                break;

            case 'N':           // | Read high fuse bits                | 'N' |       |   dd |     |  14  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\x58\x08\x00\x00"),CMD_LEN);
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX(SPI_Reply[3]);
                break;

            case 'q':           // | Write efuse bits                   | 'q' |    dd |      | 13d |  14  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\xAC\xA4\x00\x00"),CMD_LEN);
                SPI_Cmd[3]=uart_read_q();
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX('\r');
                break;

            case 'Q':           // | Read efuse bits                    | 'Q' |       |   dd |     |  14  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\x50\x08\x00\x00"),CMD_LEN);
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX(SPI_Reply[3]);
                break;

            case 'l':           // | Write lock bits                    | 'l' |    dd |      | 13d |  14  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\xAC\xE0\x00\x00"),CMD_LEN);
                SPI_Cmd[3]=uart_read_q();
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX('\r');
                break;

            case 'r':           // | Read lock bits                     | 'r' |       |   dd |     |  14  |
                wait_AVR_Busy();
                memcpy_P(SPI_Cmd,PSTR("\x58\x00\x00\x00"),CMD_LEN);
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX(SPI_Reply[3]);
                break;

            case 'T':           // | Select device type                 | 'T' |    dd |      | 13d |   6  |
                // read and ignore since we already know our
                // device type.
                uart_read_q();
                uart_writeX('\r');
                break;

            case 's':           // | Read signature bytes               | 's' |       | 3*dd |     |      |
                if(!enter_programming_mode())
                    if(!get_signature())
                        for(i=SIG_LEN-1;i>=0;i--)
                            uart_writeX(AVR_Signature[i]);
                show_device_name();
                break;

            case 't':           // | Return supported device codes      | 't' |       | n*dd | 00d |   7  |
                // only give the one we are going to program because
                // avrdude avr910 code is a little screwed up and will
                // always pick the 1st device we would send. So, make
                // it easy for avrdude, only send the one.
                if(!enter_programming_mode())
                    if(!get_signature())
                        uart_writeX(target_avr.device_code);
                uart_writeX(0x00);
                break;

            case 'S':           // | Return software identifier         | 'S' |       | s[7] |     |   8  |
                uart_write_PSTR(PSTR(PROGRAMMER_ID));
                break;

            case 'V':           // | Return sofware version             | 'V' |       |dd dd |     |   9  |
                uart_write_PSTR(PSTR(SOFTWARE_VERSION));
                break;

            case 'v':           // | Return hardware version            | 'v' |       |dd dd |     |   9  |
                uart_write_PSTR(PSTR(HARDWARE_VERSION));
                break;

            case'p':            // | Return programmer type             | 'p' |       |   dd |     |  10  |
                uart_writeX('S');
                break;

            case ':':           // | Universal command                  | ':' |  3*dd |   dd | 13d |      |
                // I don't think is ever used but left it in
                // just in case.
                wait_AVR_Busy();
                SPI_Cmd[0]=uart_read_q();
                SPI_Cmd[1]=uart_read_q();
                SPI_Cmd[2]=uart_read_q();
                SPI_Cmd[3]=0x00;
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX(SPI_Reply[3]);
                uart_writeX('\r');
                break;

            case '.':           // | New universal command              | '.' |  4*dd |   dd | 13d |      |
                // This one is used although it's hard to figure
                // exactly when and why - more avrdude wierdness.
                wait_AVR_Busy();
                SPI_Cmd[0]=uart_read_q();
                SPI_Cmd[1]=uart_read_q();
                SPI_Cmd[2]=uart_read_q();
                SPI_Cmd[3]=uart_read_q();
                SPI_Reply[3]=0;
                spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
                uart_writeX(SPI_Reply[3]);
                uart_writeX('\r');
                break;

            case 'E':           // | Exit bootloader                    | 'E' |       |      | 13d |      |
                // avrdude gives an error message if we don't do this
                uart_writeX('\r');
                break;

            case 'Z':           // | Select target avr                  | 'Z' |   dd  |      | 13d |      |
                // not supported by avrdude so set the target
                // before you run avrdude. Using echo in makefile
                // just ahead of avrdude is a good method.
                i=uart_read_q();
                if(i>=48&&i<=57)
                    target_port=i-48; // convert from ascii to binary
                lcd_goto_position(1,0);
                fprintf_P(&lcd_stream, PSTR(BLANK_LINE));
                lcd_goto_position(1,0);
                fprintf_P(&lcd_stream, PSTR(">>port %d"),target_port);
                uart_writeX('\r');
                break;

            case 0x1B:          // | ESC - sync char                    | x1B |   dd  |      |     |      |
                // consume, no reply
                break;

            default:            // | Unknown command                    |     |       |      |  ?  |      |
                uart_writeX('?');
                break;
        }
    }
}
// --------------------------------------------------------------------------------------------------------
// The target avr cannot run spi rates greater than 1/4 it's clock.
// Keep that in mind if you change this. div_16 works for targets
// running 8mhz or higher with this programmer running 20mhz.
//
void spi_init(void){
# define SPR_bitrate_div_4      0
# define SPR_bitrate_div_16     1
# define SPR_bitrate_div_64     2
# define SPR_bitrate_div_128    3
    SPCR = 0;
    INPUT(SCK);
    INPUT(MOSI);
    INPUT(MSIO);
    PULLUP_ON(RESET_0);
    // set up the mask and save for use when enabling program mode.
    // with my clock at 18.423MHZ had to change the scaler to 128
    // for factory ATmega328 TQFP32 which has 1MHZ clock.
//  SPCR_enable_mask = _BV(SPE)|_BV(MSTR)|SPR_bitrate_div_16;
    SPCR_enable_mask = _BV(SPE)|_BV(MSTR)|SPR_bitrate_div_128;
    // get the timeout counter going
    sei();
}

// get the signature from the target device and load related info for the target.
// signature is 3 bytes read 1 at a time. The use to lookup dev info in table.
char get_signature(void){
    wait_AVR_Busy();
    memcpy_P(SPI_Cmd,PSTR("\x30\x00\x00\x00"),CMD_LEN);
    spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
    AVR_Signature[0]=SPI_Reply[3];
    memcpy_P(SPI_Cmd,PSTR("\x30\x00\x01\x00"),CMD_LEN);
    spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
    AVR_Signature[1]=SPI_Reply[3];
    memcpy_P(SPI_Cmd,PSTR("\x30\x00\x02\x00"),CMD_LEN);
    spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
    AVR_Signature[2]=SPI_Reply[3];
    int i;
    for(i=0;i<DEVICE_COUNT;i++){
        if(!memcmp_P(AVR_Signature,avr_info[i].signature,SIG_LEN)){
            int j;
            for(j=0;j<sizeof(AVR_DEVICE);j++)
                ((char*)&target_avr)[j] = pgm_read_byte_near(&((char*)&avr_info[i])[j]);
            return(SPI_OK);
        }
    }
    return(SPI_ERROR);
}

// show what is connected on the LCD
void show_device_name(void){
    lcd_goto_position(2,0);
    fprintf_P(&lcd_stream, PSTR(BLANK_LINE));
    lcd_goto_position(2,0);
    fprintf_P(&lcd_stream, PSTR(">>"));
    if(!get_signature())
        fprintf(&lcd_stream, target_avr.name);
    else
        fprintf_P(&lcd_stream, PSTR("error"));
}

// wait on previous spi command to complete
void wait_AVR_Busy(void){
    SPI_Reply[3]=0x01;
    while(SPI_Reply[3]) {
        memcpy_P(SPI_Cmd,PSTR("\xF0\x00\x00\x00"),CMD_LEN);
        spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);{
            SPI_Reply[3]&=0x01;
        }
    }
}

// send/recv a single character over SPI
uint8_t spi_char(uint8_t send){
    SPDR = send;
    while(!(SPSR & _BV(SPIF)));
    return(SPDR);
}

// send/recv a command (4 bytes) over SPI
void spi_cmd(uint8_t *send, uint8_t *reply, int length){
    int i;
    for(i=0;i<length;i++)
        reply[i]=spi_char(send[i]);
}

// enter programming mode requires the target reset to be taken LOW
// and then sending the enter programming mode spi command. Also turn
// on the appropriate port indicator.
uint8_t enter_programming_mode(void){
    if(!in_programming_mode){
        in_programming_mode=true;
        flash_byte_count=0;
        eeprom_byte_count=0;
        lcd_goto_position(3,0);
        fprintf_P(&lcd_stream, PSTR(BLANK_LINE));
        OUTPUT(SCK);
        OUTPUT(MOSI);
        INPUT(MSIO);
        OUTPUT(RESET_0);
        CLEAR(RESET_0);
        SPCR = SPCR_enable_mask;
        _delay_ms(21);
        memcpy_P(SPI_Cmd,PSTR("\xAC\x53\x00\x00"),CMD_LEN);
        spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
        if(SPI_Reply[2]==0x53)
            return(SPI_OK);
        // get out of programming mode if something went wrong.
        leave_programming_mode();
        return(SPI_ERROR);
    }
    return(SPI_OK);
}

// turn off leds, disable spi, spi pins to tri-state,
// and reset pins with pullups.
void leave_programming_mode(void){
    if(in_programming_mode){
        in_programming_mode=false;
        SPCR = 0;
        PULLUP_OFF(SCK);
        PULLUP_OFF(MOSI);
        PULLUP_OFF(MSIO);
        PULLUP_ON(RESET_0);
        lcd_goto_position(3,0);
        fprintf_P(&lcd_stream, PSTR(">>F:%4d E:%4d"), flash_byte_count, eeprom_byte_count);
    }
}

// simply send a flash const string to avrdude
void uart_write_PSTR(char *data){
    int i;
    for(i=0;pgm_read_byte_near((char*)&data[i]);i++)
        uart_writeX(pgm_read_byte_near((char*)&data[i]));
}

// the ring buffer is filled by the usart interrupt routine and
// whenever the index's don't match there is data in the buffer.
// Othewise hang and wait for data. No flow control exists so
// the buffer must be large enough to handle incoming data or
// it will wrap and overwrite. For us this is page size plus
// a few bytes.
uint8_t uart_read_q(void){
    uint8_t data;
    while(qout==qin);
    data=q_buff[qout++];
    if(qout==Q_SIZE) qout=0;
    return(data);
}

// get the incoming character and put it in the buffer
//SIGNAL(USART_RX_vect){
ISR(USART_RX_vect, ISR_BLOCK){
    q_buff[qin++]=UDR0;
    if(qin==Q_SIZE) qin=0;
}

void uart_writeX(char x) {
  // wait for empty receive buffer
  while ((UCSR0A & (1<<UDRE0))==0);
  // send
  UDR0 = x;
}

and the make file:

#
# AVRBOOT Noter's Nerdkit ISP Programmer
PROGRAM=AVR_ISP

#
# settings for the programmer that will load the ISP Programmer
#   (almost always the bootloader)
#
PROGRAMMER="AVRBOOT"
#PROGRAMMER="AVR_ISP"
#PGMR_COM_PORT=COM1
PGMR_COM_PORT=/dev/ttyUSB0
PGMR_COM_BAUD=115200

#
# settings for the target chip
#   (almost always the same as the bootloader)
#
MCU=atmega328p
#MCU=atmega168

#F_CPU=20000000UL
#F_CPU=18432000UL
F_CPU=14745600UL
#F_CPU=8000000UL

#BAUD=9600UL
#BAUD=19200UL
#BAUD=38400UL
#BAUD=57600UL
BAUD=115200UL
#BAUD=250000UL

all: $(PROGRAM).hex

program: $(PROGRAM).pgm

.SECONDARY:

# do not delete secondary files
.SECONDARY:

%.o: %.c Makefile
    avr-gcc -g -O0 -fno-jump-tables -Wall -mmcu=$(MCU) -mcall-prologues \
        -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm \
        $< -o $@ \
        ../libnerdkits/lcd.o ../libnerdkits/uart.o ../libnerdkits/delay.o \
        -DF_CPU=$(F_CPU) -DBAUD=$(BAUD)

%.ass: %.o
    avr-objdump -S -d $< > $@

%.hex: %.o %.ass
    avr-objcopy -j .text -O ihex $< $@
    avr-size -A $(*F).hex

%.pgm: %.hex
    avrdude -F -C ../libnerdkits/avrdude.rs -c $(PROGRAMMER) -p $(MCU) -b $(PGMR_COM_BAUD) -P $(PGMR_COM_PORT) \
        -U flash:w:$<:a

    rm $(PROGRAM).o $(PROGRAM).hex $(PROGRAM).ass
November 10, 2014
by scootergarrett
scootergarrett's Avatar

I nerdkited the Microview library

November 11, 2014
by BobaMosfet
BobaMosfet's Avatar

Nice job!!!

BM

Post a Reply

Please log in to post a reply.

Did you know that NerdKits has been featured on Slashdot, Hack A Day, Hacked Gadgets, the MAKE blog, and other DIY-oriented websites? Learn more...