From fd8c0b284d4e790b0b2f41b55f80b316707f98a5 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Sat, 19 Jan 2013 18:42:05 +0100 Subject: digital/ucoolib/ucoolib/dev/avrisp: add AVR ISP --- digital/ucoolib/ucoolib/dev/avrisp/avrisp.cc | 452 +++++++++++++++++++++++++++ 1 file changed, 452 insertions(+) create mode 100644 digital/ucoolib/ucoolib/dev/avrisp/avrisp.cc (limited to 'digital/ucoolib/ucoolib/dev/avrisp/avrisp.cc') diff --git a/digital/ucoolib/ucoolib/dev/avrisp/avrisp.cc b/digital/ucoolib/ucoolib/dev/avrisp/avrisp.cc new file mode 100644 index 00000000..10550687 --- /dev/null +++ b/digital/ucoolib/ucoolib/dev/avrisp/avrisp.cc @@ -0,0 +1,452 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2013 Nicolas Schodet +// +// APBTeam: +// Web: http://apbteam.org/ +// Email: team AT apbteam DOT org +// +// 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 +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// }}} +#include "avrisp.hh" + +#include "ucoolib/utils/delay.hh" + +namespace ucoo { + +/// Command used to load extended address. +static const uint8_t avrisp_cmd_load_extended_address = 0x4d; +/// Bit to address high byte. +static const uint8_t avrisp_cmd_high_byte = 1 << 3; +/// Extra EEPROM programming delay. +static const int avrisp_eeprom_delay_ms = 2; +/// Miscellaneous read delay. +static const int avrisp_misc_read_delay_ms = 5; + +AvrIsp::AvrIsp (AvrIspIntf &intf) + : intf_ (intf) +{ +} + +AvrIspResult +AvrIsp::enter_progmode (uint8_t sck_duration_us, uint8_t timeout_ms, + uint8_t stab_delay_ms, uint8_t cmd_exe_delay_ms, + uint8_t synch_loops, uint8_t byte_delay_ms, + uint8_t poll_value, uint8_t poll_index, + const uint8_t cmd[4]) +{ + int i; + uint8_t tmp1, tmp2; + // Reset context. + ext_addr_ = 0; + last_ext_addr_ = 0xff; + larger_than_64k_ = 0; + bytes_left_ = 0; + // Enable SPI. + intf_.enable (sck_duration_us); + delay_ms (stab_delay_ms); + // Limit the number of loops. + if (synch_loops > 48) + synch_loops = 48; + // Minimum byte delay. + if (byte_delay_ms < 1) + byte_delay_ms = 1; + // Synchronisation loops. + for (i = 0; i < synch_loops; i++) + { + delay_ms (cmd_exe_delay_ms); + intf_.send_and_recv (cmd[0]); + delay_ms (byte_delay_ms); + intf_.send_and_recv (cmd[1]); + delay_ms (byte_delay_ms); + tmp1 = intf_.send_and_recv (cmd[2]); + delay_ms (byte_delay_ms); + tmp2 = intf_.send_and_recv (cmd[3]); + // Test success. + if (poll_index == 0 + || (poll_index == 3 && tmp1 == poll_value) + || (poll_index != 3 && tmp2 == poll_value)) + return AVRISP_OK; + // Else, new try. + intf_.sck_pulse (); + delay_ms (20); + } + return AVRISP_FAILED; +} + +void +AvrIsp::leave_progmode (uint8_t pre_delay_ms, uint8_t post_delay_ms) +{ + delay_ms (pre_delay_ms); + intf_.disable (); + delay_ms (post_delay_ms); +} + +void +AvrIsp::load_address (uint32_t addr) +{ + addr_ = addr & 0xffff; + ext_addr_ = (addr >> 16) & 0xff; + last_ext_addr_ = 0xff; + larger_than_64k_ = (addr & 0x80000000) ? 1 : 0; +} + +AvrIspResult +AvrIsp::chip_erase (uint8_t erase_delay_ms, uint8_t poll_method, + const uint8_t cmd[4]) +{ + int tries; + intf_.send_and_recv (cmd[0]); + intf_.send_and_recv (cmd[1]); + intf_.send_and_recv (cmd[2]); + intf_.send_and_recv (cmd[3]); + if (poll_method == 0) + { + // Use delay. + delay_ms (erase_delay_ms); + } + else + { + // Use RDY/BSY command. + tries = 150; + while ((spi_tx_32 (0xf0000000) & 1) && tries) + tries--; + if (tries == 0) + return AVRISP_TIMEOUT; + } + return AVRISP_OK; +} + +AvrIspResult +AvrIsp::program_begin (uint16_t num_bytes, uint8_t mode, uint8_t delay_ms, + uint8_t cmd_write_mem, uint8_t cmd_write_page, + uint8_t cmd_read_mem, const uint8_t poll[2], + uint8_t flash) +{ + // Set delay bounds. + if (delay_ms < 4) + delay_ms = 4; + if (delay_ms > 32) + delay_ms = 32; + // Check data size. + if (flash && (num_bytes & 1)) + return AVRISP_FAILED; + // Store parameters. + start_addr_ = addr_; + bytes_left_ = num_bytes; + mode_ = mode; + delay_ms_ = delay_ms; + cmd_write_mem_ = cmd_write_mem; + cmd_write_page_ = cmd_write_page; + cmd_read_mem_ = cmd_read_mem; + poll_[0] = poll[0]; + poll_[1] = poll[1]; + pollable_ = 0; + flash_ = flash; + return AVRISP_OK; +} + +AvrIspResult +AvrIsp::program_flash_begin (uint16_t num_bytes, uint8_t mode, + uint8_t delay_ms, uint8_t cmd_write_mem, + uint8_t cmd_write_page, uint8_t cmd_read_mem, + const uint8_t poll[2]) +{ + return program_begin (num_bytes, mode, delay_ms, cmd_write_mem, + cmd_write_page, cmd_read_mem, poll, 1); +} + +AvrIspResult +AvrIsp::program_eeprom_begin (uint16_t num_bytes, uint8_t mode, + uint8_t delay_ms, uint8_t cmd_write_mem, + uint8_t cmd_write_page, uint8_t cmd_read_mem, + const uint8_t poll[2]) +{ + return program_begin (num_bytes, mode, delay_ms, cmd_write_mem, + cmd_write_page, cmd_read_mem, poll, 0); +} + +AvrIspResult +AvrIsp::program_continue (const uint8_t *data, uint16_t size) +{ + uint16_t i; + uint8_t read; + int tries; + // Check size. + if (size > bytes_left_ || (flash_ && (size & 1))) + return AVRISP_FAILED; + bytes_left_ -= size; + // Loop on each input bytes. + for (i = 0; i < size; i++) + { + // Check for Load Extended Address. + if (larger_than_64k_ && last_ext_addr_ != ext_addr_) + { + intf_.send_and_recv (avrisp_cmd_load_extended_address); + intf_.send_and_recv (0x00); + intf_.send_and_recv (ext_addr_); + intf_.send_and_recv (0x00); + last_ext_addr_ = ext_addr_; + } + // Write memory/load page, using bit 3 as byte selector. + if (flash_ && (i & 1)) + intf_.send_and_recv (cmd_write_mem_ | avrisp_cmd_high_byte); + else + intf_.send_and_recv (cmd_write_mem_); + spi_tx_16 (addr_); + intf_.send_and_recv (data[i]); + // EEPROM needs more delay. + if (!flash_) + delay_ms (avrisp_eeprom_delay_ms); + // Poll. + if (!(mode_ & AVRISP_MODE_PAGE)) + { + // Poll right now. + tries = 150; + read = poll_[0]; + if ((mode_ & AVRISP_MODE_WORD_VALUE) && data[i] != read) + { + // Poll by reading byte. + while (read != data[i] && tries) + { + if (flash_ && (i & 1)) + intf_.send_and_recv (cmd_read_mem_ + | avrisp_cmd_high_byte); + else + intf_.send_and_recv (cmd_read_mem_); + spi_tx_16 (addr_); + read = intf_.send_and_recv (0x00); + tries--; + } + } + else if (mode_ & AVRISP_MODE_WORD_RDY_BSY) + { + // RDY/BSY polling. + while ((spi_tx_32 (0xf0000000) & 1) && tries) + tries--; + } + else + // Simple delay. + delay_ms (delay_ms_); + if (tries == 0) + return AVRISP_TIMEOUT; + } + else + { + // Check for poll method. + if (!pollable_ && data[i] != poll_[0]) + { + pollable_ = 0x02 | (i & 1); + poll_addr_ = addr_; + } + } + // Increment address. + if (!flash_ || (i & 1)) + { + addr_++; + if (addr_ == 0) + ext_addr_++; + } + } + return AVRISP_OK; +} + +AvrIspResult +AvrIsp::program_end () +{ + uint8_t read; + int tries; + // Check size. + if (bytes_left_ != 0) + return AVRISP_FAILED; + // Write page if requested. + if ((mode_ & (AVRISP_MODE_PAGE | AVRISP_MODE_PAGE_WRITE)) + == (AVRISP_MODE_PAGE | AVRISP_MODE_PAGE_WRITE)) + { + // Write page. + intf_.send_and_recv (cmd_write_page_); + spi_tx_16 (start_addr_); + intf_.send_and_recv (0x00); + // EEPROM needs more delay. + if (!flash_) + delay_ms (avrisp_eeprom_delay_ms); + // Poll. + tries = 150; + read = poll_[0]; + if ((mode_ & AVRISP_MODE_PAGE_VALUE) && pollable_) + { + // Poll by reading byte. + while (read == poll_[0] && tries) + { + if (flash_ && (pollable_ & 1)) + intf_.send_and_recv (cmd_read_mem_ | avrisp_cmd_high_byte); + else + intf_.send_and_recv (cmd_read_mem_); + spi_tx_16 (poll_addr_); + read = intf_.send_and_recv (0x00); + tries--; + } + } + else if (mode_ & AVRISP_MODE_PAGE_RDY_BSY) + { + // RDY/BSY polling. + while ((spi_tx_32 (0xf0000000) & 1) && tries) + tries--; + } + else + // Simple delay. + delay_ms (delay_ms_); + if (tries == 0) + return AVRISP_TIMEOUT; + } + return AVRISP_OK; +} + +AvrIspResult +AvrIsp::read_begin (uint16_t num_bytes, uint8_t cmd_read_mem, uint8_t flash) +{ + // Check data size. + if (flash && (num_bytes & 1)) + return AVRISP_FAILED; + // Store parameters. + bytes_left_ = num_bytes; + cmd_read_mem_ = cmd_read_mem; + flash_ = flash; + return AVRISP_OK; +} + +AvrIspResult +AvrIsp::read_flash_begin (uint16_t num_bytes, uint8_t cmd_read_mem) +{ + return read_begin (num_bytes, cmd_read_mem, 1); +} + +AvrIspResult +AvrIsp::read_eeprom_begin (uint16_t num_bytes, uint8_t cmd_read_mem) +{ + return read_begin (num_bytes, cmd_read_mem, 0); +} + +AvrIspResult +AvrIsp::read_continue (uint8_t *data, uint16_t size) +{ + uint16_t i; + // Check size. + if (size > bytes_left_ || (flash_ && (size & 1))) + return AVRISP_FAILED; + bytes_left_ -= size; + // Loop on each bytes. + for (i = 0; i < size; i++) + { + // Check for Load Extended Address. + if (larger_than_64k_ && last_ext_addr_ != ext_addr_) + { + intf_.send_and_recv (avrisp_cmd_load_extended_address); + intf_.send_and_recv (0x00); + intf_.send_and_recv (ext_addr_); + intf_.send_and_recv (0x00); + last_ext_addr_ = ext_addr_; + } + // Read memory, using bit 3 as byte selector. + if (flash_ && (i & 1)) + intf_.send_and_recv (cmd_read_mem_ | avrisp_cmd_high_byte); + else + intf_.send_and_recv (cmd_read_mem_); + spi_tx_16 (addr_); + data[i] = intf_.send_and_recv (0x00); + // Increment address. + if (!flash_ || (i & 1)) + { + addr_++; + if (addr_ == 0) + ext_addr_++; + } + } + return AVRISP_OK; +} + +AvrIspResult +AvrIsp::read_end () +{ + // Check size. + if (bytes_left_ != 0) + return AVRISP_FAILED; + return AVRISP_OK; +} + +void +AvrIsp::program_misc (const uint8_t cmd[4]) +{ + uint8_t i; + for (i = 0; i < 4; i++) + intf_.send_and_recv (cmd[i]); +} + +uint8_t +AvrIsp::read_misc (uint8_t ret_addr, const uint8_t cmd[4]) +{ + uint8_t i, read = 0, tmp; + for (i = 0; i < 4; i++) + { + tmp = intf_.send_and_recv (cmd[i]); + if (i == ret_addr) + read = tmp; + delay_ms (avrisp_misc_read_delay_ms); + } + return read; +} + +void +AvrIsp::multi (uint8_t num_tx, uint8_t num_rx, uint8_t rx_start, + const uint8_t *dout, uint8_t *din) +{ + uint8_t in, out; + while (num_tx || num_rx) + { + out = 0; + if (num_tx) + { + out = *dout++; + num_tx--; + } + in = intf_.send_and_recv (out); + if (rx_start) + rx_start--; + else if (num_rx) + { + *din++ = in; + num_rx--; + } + } +} + +void +AvrIsp::spi_tx_16 (uint16_t data) +{ + intf_.send_and_recv ((data >> 8) & 0xff); + intf_.send_and_recv ((data >> 0) & 0xff); +} + +uint8_t +AvrIsp::spi_tx_32 (uint32_t data) +{ + intf_.send_and_recv ((data >> 24) & 0xff); + intf_.send_and_recv ((data >> 16) & 0xff); + intf_.send_and_recv ((data >> 8) & 0xff); + return intf_.send_and_recv ((data >> 0) & 0xff); +} + +} // namespace ucoo -- cgit v1.2.3