ponyprog/SrcPony/picbus.cpp

580 lines
11 KiB
C++

//=========================================================================//
// //
// PonyProg - Serial Device Programmer //
// //
// Copyright (C) 1997-2019 Claudio Lanconelli //
// //
// http://ponyprog.sourceforge.net //
// //
//-------------------------------------------------------------------------//
// //
// 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 version2 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 (see LICENSE); if not, write to the //
// Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //
// //
//=========================================================================//
#include "types.h"
#include "picbus.h"
#include "errcode.h"
#include <QDebug>
#include <QtCore>
#include "globals.h"
#include "e2profil.h"
#include "e2cmdw.h"
class e2CmdWindow;
PicBus::PicBus(BusInterface *ptr)
: BusIO(ptr),
DataMask(0xff),
ProgMask(0x3fff),
ReadProgCode(0x04), //Read Data from Program Memory
LoadProgCode(0x02), //Load Data for Program Memory
ReadDataCode(0x05), //Read Data from Data Memory
LoadDataCode(0x03), //Load Data for Data Memory
LoadConfigCode(0x00), //Load Configuration
IncAddressCode(0x06), //Increment Address
EraseProgMem(0x09), //Bulk Erase Program Memory
EraseDataMem(0x0b), //Bulk Erase Data Memory
BeginEraseProgCode(0x08), //Begin Erase Programming Cycle
BeginProgOnlyCode(0x18) //Begin Programming Only Cycle
{
}
void PicBus::SetDelay()
{
int val = E2Profile::GetPICSpeed();
int n;
switch (val)
{
case TURBO:
n = 1;
break;
case FAST:
n = 2;
break;
case SLOW:
n = 20;
break;
case VERYSLOW:
n = 150;
break;
case ULTRASLOW:
n = 1000;
break;
default:
n = 6; //Default (< 100KHz)
break;
}
Q_CHECK_PTR(busI);
busI->SetDelay(n);
qDebug() << __PRETTY_FUNCTION__ << "=" << n;
}
int PicBus::SendDataBit(int b)
{
//setCLK(); //set SCK high
//bitDI(b);
//ShotDelay();
//clearCLK(); //device latch data bit now!
//ShotDelay();
int err = OK;
clearCLK();
busI->xferBit(err, b, SPI_MODE_1 | xMODE_WRONLY);
return err;
}
// returns a negative number in case of error, 0 or 1 otherwise
int PicBus::RecDataBit()
{
//setCLK(); //set SCK high (Pic output data now)
//ShotDelay();
//b = getDO(); // sampling data on falling edge
//clearCLK();
//ShotDelay();
int err = OK;
clearCLK();
int rv = busI->xferBit(err, 1, SPI_MODE_1 | xMODE_RDONLY);
if (err == OK)
{
return rv;
}
else
{
return err;
}
}
int PicBus::SendDataWord(long wo, int wlen)
{
int err = OK;
clearCLK();
clearDI();
WaitUsec(busI->GetCmd2CmdDelay());
busI->xferWord(err, wo, SPI_MODE_1 | xMODE_WRONLY, wlen, true);
setDI();
//1 usec from a command to the next
//WaitUsec(shot_delay/4+1);
return err;
}
long PicBus::RecDataWord(int wlen)
{
int err = OK;
clearCLK();
clearDI();
WaitUsec(busI->GetCmd2CmdDelay());
setDI();
ShotDelay();
int rv = busI->xferWord(err, 0xffff, SPI_MODE_1 | xMODE_RDONLY, wlen, true);
//WaitUsec(shot_delay/4+1);
if (err == OK)
{
return rv;
}
else
{
return err;
}
}
int PicBus::SendCmdCode(int opcode)
{
return SendDataWord(opcode, 6);
}
int PicBus::SendProgCode(uint16_t data)
{
//the code is 14 bit data with leading and trailing 0's
data &= ProgMask;
data <<= 1;
return SendDataWord(data);
}
uint16_t PicBus::RecvProgCode()
{
return (uint16_t)(RecDataWord() >> 1) & ProgMask;
}
int PicBus::SendDataCode(uint16_t data)
{
//the code is 8 bit data with leading and trailing 0's
data &= DataMask;
data <<= 1;
return SendDataWord(data);
}
uint16_t PicBus::RecvDataCode()
{
return (uint16_t)(RecDataWord() >> 1) & DataMask;
}
int PicBus::WaitReadyAfterWrite(long timeout)
{
WaitMsec(10);
return OK;
}
int PicBus::Reset(void)
{
SetDelay();
SetMCLR(); //First bogus entry to charge capacitors
WaitMsec(150);
clearDI();
ClearMCLR(); //Now reset the micro
setCLK(); //keep Vdd on
WaitUsec(1000);
clearCLK(); //Prepare for Program mode entry
WaitUsec(1000);
SetMCLR(); //Program mode entry
WaitMsec(10);
return OK;
}
void PicBus::DisableCodeProtect()
{
SendCmdCode(LoadConfigCode);
SendProgCode(0xffff);
//go to location 0x2007
int k;
for (k = 0; k < 7; k++)
{
SendCmdCode(IncAddressCode);
}
SendCmdCode(0x01);
SendCmdCode(0x07);
SendCmdCode(BeginEraseProgCode);
WaitMsec(20);
SendCmdCode(0x01);
SendCmdCode(0x07);
WaitMsec(10);
Reset();
}
long PicBus::ReadConfig(uint16_t *data)
{
Q_CHECK_PTR(data);
if (data == 0) //read only configuration word
{
return BADPARAM;
}
SendCmdCode(LoadConfigCode);
SendProgCode(0xffff);
int k;
for (k = 0; k < 8; k++, data++)
{
uint8_t *bp = (uint8_t *)data;
uint16_t val;
SendCmdCode(ReadProgCode);
val = RecvProgCode();
if (val == ProgMask)
{
val = 0xffff;
}
#ifdef _BIG_ENDIAN_
*bp++ = (uint8_t)(val >> 8);
*bp++ = (uint8_t)(val & 0xFF);
#else
*bp++ = (uint8_t)(val & 0xFF);
*bp++ = (uint8_t)(val >> 8);
#endif
SendCmdCode(IncAddressCode);
}
Reset();
return OK;
}
long PicBus::WriteConfig(uint16_t *data)
{
Q_CHECK_PTR(data);
if (data == 0)
{
return BADPARAM;
}
SendCmdCode(LoadConfigCode);
SendProgCode(data[7]); //Other PIC programmers do this
int k;
for (k = 0; k < 8; k++, data++)
{
if (*data != 0xffff)
{
uint8_t *bp = (uint8_t *)data;
uint16_t val;
//Write Data code
SendCmdCode(LoadProgCode);
#ifdef _BIG_ENDIAN_
val = (uint16_t)(*bp++) << 8;
val |= (uint16_t)(*bp++);
#else
val = (uint16_t)(*bp++);
val |= (uint16_t)(*bp++) << 8;
#endif
SendProgCode(val);
SendCmdCode(BeginEraseProgCode);
if (WaitReadyAfterWrite())
{
break;
}
//Verify while programming (10/11/99)
SendCmdCode(ReadProgCode);
if (CompareSingleWord(val, RecvProgCode(), ProgMask))
{
return E2ERR_WRITEFAILED;
}
}
SendCmdCode(IncAddressCode);
}
WaitMsec(20);
Reset();
return OK;
}
int PicBus::Erase(int type)
{
if (type == ALL_TYPE)
{
DisableCodeProtect();
}
if (type & PROG_TYPE)
{
SendCmdCode(LoadProgCode);
SendProgCode(0xffff);
SendCmdCode(EraseProgMem);
SendCmdCode(BeginEraseProgCode);
WaitMsec(40);
Reset();
}
if (type & DATA_TYPE)
{
SendCmdCode(LoadDataCode);
SendDataCode(0xffff);
SendCmdCode(EraseDataMem);
SendCmdCode(BeginEraseProgCode);
WaitMsec(40);
Reset();
}
return OK;
}
long PicBus::Read(int addr, uint8_t *data, long length, int page_size)
{
long len;
ReadStart();
//Data is 8 bit, while Program is 14 bit (i.e. 16 bit)
if (addr == 0)
{
length >>= 1;
}
for (len = 0; len < length; len++)
{
//Send command opcode
if (addr)
{
//Read DataCode
SendCmdCode(ReadDataCode);
*data++ = (uint8_t)RecvDataCode();
SendCmdCode(IncAddressCode);
}
else
{
//Read Program Code
SendCmdCode(ReadProgCode);
uint16_t val = RecvProgCode();
if (val == ProgMask)
{
val = 0xffff;
}
#ifdef _BIG_ENDIAN_
*data++ = (uint8_t)(val >> 8);
*data++ = (uint8_t)(val & 0xFF);
#else
*data++ = (uint8_t)(val & 0xFF);
*data++ = (uint8_t)(val >> 8);
#endif
SendCmdCode(IncAddressCode);
}
if (ReadProgress(len * 100 / length))
{
break;
}
}
WaitMsec(1); //Flush
ReadEnd();
if (addr == 0)
{
len <<= 1;
}
return len;
}
long PicBus::Write(int addr, uint8_t const *data, long length, int page_size)
{
long len;
WriteStart();
if (addr == 0)
{
length >>= 1;
}
for (len = 0; len < length; len++)
{
uint16_t val;
//Send command opcode
if (addr)
{
//Write Data code
SendCmdCode(LoadDataCode);
val = (uint16_t)(*data++);
SendDataCode(val);
SendCmdCode(BeginEraseProgCode);
if (WaitReadyAfterWrite())
{
break;
}
//Verify while programming (10/11/99)
// SendCmdCode(ReadDataCode);
// if ( CompareSingleWord(val, RecvDataCode(), DataMask) )
// break;
SendCmdCode(IncAddressCode);
}
else
{
//Write Program code
SendCmdCode(LoadProgCode);
#ifdef _BIG_ENDIAN_
val = (uint16_t)(*data++) << 8;
val |= (uint16_t)(*data++);
#else
val = (uint16_t)(*data++);
val |= (uint16_t)(*data++) << 8;
#endif
SendProgCode(val);
SendCmdCode(BeginEraseProgCode);
if (WaitReadyAfterWrite())
{
break;
}
//Verify while programming (10/11/99)
// SendCmdCode(ReadProgCode);
// if ( CompareSingleWord(val, RecvProgCode(), ProgMask) )
// break;
SendCmdCode(IncAddressCode);
}
if (WriteProgress(len * 100 / length))
{
break;
}
}
WaitMsec(1); //Flush
WriteEnd();
if (addr == 0)
{
len <<= 1;
}
return len;
}
int PicBus::CompareSingleWord(uint16_t data1, uint16_t data2, uint16_t mask)
{
return (data1 & mask) != (data2 & mask);
}
int PicBus::CompareMultiWord(uint8_t *data1, uint8_t *data2, long length, int split)
{
int retval = 0;
if (data1 == 0 || data2 == 0 || (length & 1) != 0)
{
return BADPARAM;
}
if (!split)
{
long k;
for (k = 0; k < length; k += 2)
{
uint16_t val1, val2;
#ifdef _BIG_ENDIAN_
val1 = (uint16_t)(*data1++) << 8;
val1 |= (uint16_t)(*data1++);
val2 = (uint16_t)(*data2++) << 8;
val2 |= (uint16_t)(*data2++);
#else
val1 = (uint16_t)(*data1++);
val1 |= (uint16_t)(*data1++) << 8;
val2 = (uint16_t)(*data2++);
val2 |= (uint16_t)(*data2++) << 8;
#endif
if ((retval = CompareSingleWord(val1, val2, ProgMask)))
{
break; //Stop if a difference is detected
}
}
}
else
{
retval = memcmp(data1, data2, length);
}
return retval;
}