Tasmota/lib/lib_div/C2Programmer-1.0.0/src/c2.cpp

670 lines
17 KiB
C++

#include "c2.h"
/////////////////////////////////////////////
// Nothing should need change on this file //
/////////////////////////////////////////////
// Times in microseconds
#define T_RD (20+5)
#define T_SD ( 2+5)
// Layer 0: Bit shifter
static bool c2_bit(bool b) {
C2D(b);
// C2_DELAY_US(1);
C2CK(0);
// C2_DELAY_US(1);
b = C2D();
C2CK(1);
return b;
}
// Layer 1: C2D Register read/write
void c2_address_write(uint8_t address) {
#ifdef C2_DEBUG
Serial.print("AW");
Serial.println(address, HEX);
#endif
// start
c2_bit(true);
C2D_enable(true);
// instruction
c2_bit(1);
c2_bit(1);
// Address
for (int i = 0; i < 8; ++i) {
c2_bit(address & 1);
address >>= 1;
}
// Stop
C2D_enable(false);
c2_bit(1);
}
uint8_t c2_address_read() {
// start
c2_bit(true);
C2D_enable(true);
// instruction
c2_bit(0);
c2_bit(1);
// Change C2D direction
C2D_enable(false);
c2_bit(0);
// Address
uint8_t a = 0, m = 1;
for (int i = 0; i < 8; ++i) {
if (c2_bit(a & 1)) {
a |= m;
}
m <<= 1;
}
// Stop is implied
#ifdef C2_DEBUG
Serial.print("AR");
Serial.println(a, HEX);
#endif
return a;
}
uint8_t c2_data_write(uint32_t d, uint8_t bytes) {
#ifdef C2_DEBUG
Serial.print("DW");
Serial.println(d, HEX);
#endif
// start
c2_bit(true);
C2D_enable(true);
// instruction
c2_bit(1);
c2_bit(0);
// Length
bytes--;
c2_bit(bytes & 1);
c2_bit(bytes & 2);
bytes++;
// Data
for (int i = 0; i < 8 * bytes; ++i) {
c2_bit(d & 1);
d >>= 1;
}
// Reverse C2D direction
C2D_enable(false);
c2_bit(0);
// Wait
uint8_t to = 128;
while (!c2_bit(0)) if (!--to) return C2_SHIFT_TIMEOUT;
// Stop
//c2_bit(0); implied
return C2_SUCCESS;
}
uint8_t c2_data_read(uint32_t &d, uint8_t bytes) {
// start
c2_bit(true);
C2D_enable(true);
// instruction
c2_bit(0);
c2_bit(0);
// Length
bytes--;
c2_bit(bytes & 1);
c2_bit(bytes & 2);
bytes++;
// Reverse C2D direction
C2D_enable(false);
c2_bit(0);
// Wait
uint8_t to = 128;
while (!c2_bit(0)) if (!--to) return C2_SHIFT_TIMEOUT;
// Data
d = 0;
uint32_t m = 1;
for (int i = 0; i < 8 * bytes; ++i) {
if (c2_bit(d & 1)) {
d |= m;
}
m <<= 1;
}
// Stop is implied
#ifdef C2D_DEBUG
Serial.print("DR");
Serial.println(d, HEX);
#endif
return C2_SUCCESS;
}
// Layer 2: Operations
#define C2_POLL_INBUSY() { \
uint16_t to = 1000; \
uint8_t a; \
while (1) { \
a = c2_address_read(); \
if (a == 0xFF) return C2_BROKEN_LINK; \
if (~a & C2_INBUSY) break; \
if (--to == 0) return C2_POLL_TIMEOUT; \
C2_DELAY_MS(1); \
}; \
}
#define C2_POLL_OUTREADY() { \
uint16_t to = 10000; \
uint8_t a; \
while (1) { \
a = c2_address_read(); \
if (a == 0xFF) return C2_BROKEN_LINK; \
if (a & C2_OUTREADY) break; \
if (--to == 0) return C2_POLL_TIMEOUT; \
C2_DELAY_MS(1); \
}; \
}
#define C2_DATA_WRITE_AND_CHECK(v, s) { \
uint8_t r = c2_data_write(v, s); \
if (r != C2_SUCCESS) return r; \
}
#define C2_DATA_READ_AND_CHECK(v, s) { \
uint8_t r = c2_data_read(v, s); \
if (r != C2_SUCCESS) return r; \
}
#define C2_EXPECT_DATA(value) { \
uint8_t d; \
C2_DATA_READ_AND_CHECK(d, 1); \
if (d != (value)) return C2_CMD_ERROR; \
}
uint8_t c2_reset() {
C2CK(false);
C2_DELAY_US(T_RD);
C2CK(true);
C2_DELAY_US(T_SD);
return C2_SUCCESS;
}
uint8_t c2_programming_init(uint8_t devid) {
c2_reset();
c2_address_write(C2FPCTL);
C2_DATA_WRITE_AND_CHECK(C2FPCTL_ENABLE0, 1);
C2_DATA_WRITE_AND_CHECK(C2FPCTL_CORE_HALT, 1);
C2_DATA_WRITE_AND_CHECK(C2FPCTL_ENABLE1, 1)
C2_DELAY_MS(21);
// device specific initialization, see https://www.silabs.com/documents/public/application-notes/AN127.pdf
switch (devid) {
case C2_DEVID_UNKNOWN:
break;
case C2_DEVID_EFM8BB1:
case C2_DEVID_EFM8BB2:
case C2_DEVID_EFM8BB3: // C2_DEVID_EFM8LB1 is the same
c2_address_write(0xFF);
C2_DATA_WRITE_AND_CHECK(0x80, 1);
C2_DELAY_US(5);
c2_address_write(0xEF);
C2_DATA_WRITE_AND_CHECK(0x02, 1);
c2_address_write(0xA9);
C2_DATA_WRITE_AND_CHECK(0x00, 1);
break;
default:
return C2_BROKEN_LINK;
}
return C2_SUCCESS;
}
uint8_t c2_block_write(uint32_t address, uint8_t *data, uint8_t len) {
// 1. Perform an Address Write with a value of FPDAT
c2_address_write(C2FPDAT);
// 2. Perform a Data Write with the Block Write command.
C2_DATA_WRITE_AND_CHECK(C2FPDAT_BLOCK_WRITE, 1);
// 3. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 4. Poll on OutReady using Address Read until the bit set.
C2_POLL_OUTREADY();
// 5. Perform a Data Read instruction. A value of 0x0D is okay.
C2_EXPECT_DATA(0x0D);
// 6. Perform a Data Write with the high byte of the address.
C2_DATA_WRITE_AND_CHECK(address >> 8, 1);
// 7. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 8. Perform a Data Write with the low byte of the address.
C2_DATA_WRITE_AND_CHECK(address & 255, 1);
// 9. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 10. Perform a Data Write with the length.
C2_DATA_WRITE_AND_CHECK(len, 1);
// 12a. Repeat steps 11 and 12 for each byte specified by the length field.
uint8_t i = 0;
do {
// 11. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 12. Perform a Data Write with the data. This will write the data to the flash.
C2_DATA_WRITE_AND_CHECK(data[i], 1);
} while (++i != len);
// 13. Poll on OutReady using Address Read until the bit set.
C2_POLL_OUTREADY();
// 14. Perform a Data Read instruction. A value of 0x0D is okay. write to an EPROM block:
C2_EXPECT_DATA(0x0D);
return C2_SUCCESS;
}
uint8_t c2_eeprom_write(uint32_t address, uint8_t *data, uint8_t len) {
// 1. Write 0x04 to the FPCTL register.
c2_address_write(C2FPCTL);
C2_DATA_WRITE_AND_CHECK(0x04, 1);
// 2. Write 0x40 to EPCTL.
c2_address_write(C2EPCTL);
C2_DATA_WRITE_AND_CHECK(0x40, 1);
// 3. Write 0x58 to EPCTL.
C2_DATA_WRITE_AND_CHECK(0x58, 1);
// 4. Write the high byte of the address to EPADDRH.
c2_address_write(C2EPADDRH);
C2_DATA_WRITE_AND_CHECK(address >> 8, 1);
// 5. Write the low byte of the address to address EPADDRL.
c2_address_write(C2EPADDRL);
C2_DATA_WRITE_AND_CHECK(address, 1);
// 6. Perform an Address Write with a value of EPDAT.
c2_address_write(C2EPDAT);
// 7. Turn on VPP.
// 8. Wait for the VPP settling time.
// 10a. Repeat steps 9 and 10 until all bytes are written.
uint8_t i = 0;
do {
// 9. Write the data to the device using a Data Write.
C2_DATA_WRITE_AND_CHECK(data[i], 1);
// 10. Perform Address Read instructions until the value returned is not 0x80 and the EPROM is no longer busy.
C2_POLL_INBUSY();
} while (++i != len);
// 12. Turn off VPP. Note that VPP can only be applied for a maximum lifetime amount, and this value is specified in the device data sheet.
// 13. Write 0x40 to EPCTL.
c2_address_write(C2EPCTL);
C2_DATA_WRITE_AND_CHECK(0x40, 1);
// 14. Write 0x00 to EPCTL.
C2_DATA_WRITE_AND_CHECK(0x00, 1);
// 15. Write 0x02 to FPCTL.
c2_address_write(C2FPCTL);
C2_DATA_WRITE_AND_CHECK(0x02, 1);
// 16. Write 0x04 to FPCTL.
C2_DATA_WRITE_AND_CHECK(0x04, 1);
// 17. Write 0x01 to FPCTL.
C2_DATA_WRITE_AND_CHECK(0x01, 1);
return C2_SUCCESS;
}
uint8_t c2_block_read(uint32_t address, uint8_t *data, uint8_t len) {
// 1. Perform an Address Write with a value of FPDAT.
c2_address_write(C2FPDAT);
// 2. Perform a Data Write with the Block Read command.
C2_DATA_WRITE_AND_CHECK(C2FPDAT_BLOCK_READ, 1);
// 3. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 4. Poll on OutReady using Address Read until the bit set.
C2_POLL_OUTREADY();
// 5. Perform a Data Read instruction. A value of 0x0D is okay.
C2_EXPECT_DATA(0x0D);
// 6. Perform a Data Write with the high byte of the address.
C2_DATA_WRITE_AND_CHECK(address >> 8, 1);
// 7. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 8. Perform a Data Write with the low byte of the address.
C2_DATA_WRITE_AND_CHECK(address, 1);
// 9. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 10. Perform a Data Write with the length.
C2_DATA_WRITE_AND_CHECK(len, 1);
// 11. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 12. Poll on OutReady using Address Read until the bit set.
C2_POLL_OUTREADY();
// 13. Read FPI Command Status. Abort if Status != 0x0D.
C2_EXPECT_DATA(0x0D);
// 15a. Repeat step 14 and 15 for each byte specified by the length field.
uint8_t i = 0;
do {
// 14. Poll on OutReady using Address Read until the bit set.
C2_POLL_OUTREADY();
// 15. Perform a Data Read instruction. This will read the data from the flash.
C2_DATA_READ_AND_CHECK(data[i], 1);
} while (++i != len);
return C2_SUCCESS;
}
uint8_t c2_eeprom_read(uint32_t address, uint8_t *data, uint8_t len) {
// 1. Write 0x04 to the FPCTL register.
c2_address_write(C2FPCTL);
C2_DATA_WRITE_AND_CHECK(0x04, 1);
// 2. Write 0x00 to EPCTL.
c2_address_write(C2EPCTL);
C2_DATA_WRITE_AND_CHECK(0x00, 1);
// 3. Write 0x58 to EPCTL.
C2_DATA_WRITE_AND_CHECK(0x58, 1);
// 4. Write the high byte of the address to EPADDRH.
c2_address_write(C2EPADDRH);
C2_DATA_WRITE_AND_CHECK(address >> 8, 1);
// 5. Write the low byte of the address to address EPADDRL.
c2_address_write(C2EPADDRL);
C2_DATA_WRITE_AND_CHECK(address, 1);
// 6. Perform an Address Write with a value of EPDAT.
c2_address_write(C2EPDAT);
// 9. Repeat steps 7 and 8 until all bytes are read.
uint8_t i = 0;
do {
// 7.1. Perform an Address Write operation with a value of EPSTAT.
c2_address_write(C2EPSTAT);
// 7.2. Perform a Data Read operation and check the bits of the EPSTAT register.
uint8_t err;
C2_DATA_READ_AND_CHECK(err, 1);
if (err & C2EPSTAT_ERROR) return C2_CMD_ERROR;
// 7.3. Perform an Address Write operation with a value of EPDAT.
c2_address_write(C2EPDAT);
// 7. Perform Address Read instructions until the value returned is not 0x80 and the EPROM is no longer busy.
C2_POLL_INBUSY();
// 8.1. Perform an Address Write operation with a value of EPSTAT.
c2_address_write(C2EPSTAT);
// 8.2. Perform a Data Read operation and check the ERROR bit in the EPSTAT register.
C2_DATA_READ_AND_CHECK(err, 1);
if (err & C2EPSTAT_ERROR) return C2_CMD_ERROR;
// 8.3. Perform an Address Write operation with a value of EPDAT.
C2_DATA_WRITE_AND_CHECK(C2EPDAT, 1);
// 8. Read the byte using the Data Read instruction.
C2_DATA_READ_AND_CHECK(data[i], 1);
} while (++i != len);
// 10. Write 0x40 to EPCTL.
c2_address_write(C2EPCTL);
C2_DATA_WRITE_AND_CHECK(0x40, 1);
// 11. Write 0x00 to EPCTL.
C2_DATA_WRITE_AND_CHECK(0x00, 1);
// 12. Write 0x02 to FPCTL.
c2_address_write(C2FPCTL);
C2_DATA_WRITE_AND_CHECK(0x02, 1);
// 13. Write 0x04 to FPCTL.
C2_DATA_WRITE_AND_CHECK(0x04, 1);
// 14. Write 0x01 to FPCTL.
C2_DATA_WRITE_AND_CHECK(0x01, 1);
return C2_SUCCESS;
}
uint8_t c2_page_erase(uint8_t page) {
// 1. Perform an Address Write with a value of FPDAT.
c2_address_write(C2FPDAT);
// 2. Perform a Data Write with the Page Erase command.
c2_data_write(C2FPDAT_FLASH_PAGE_ERASE, 1);
// 3. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 4. Poll on OutReady using Address Read until the bit set.
C2_POLL_OUTREADY();
// 5. Perform a Data Read instruction. A value of 0x0D is okay.
C2_EXPECT_DATA(0x0D);
// 6. Perform a Data Write with the page number.
c2_data_write(page, 1);
// 7. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 8. Poll on OutReady using Address Read until the bit clears.
C2_POLL_OUTREADY();
// 9. Perform a Data Read instruction. A value of 0x0D is okay.
C2_EXPECT_DATA(0x0D);
// 10. Perform a Data Write with the a value of 0x00.
c2_data_write(0x00, 1);
// 11. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 12. Poll on OutReady using Address Read until the bit set.
C2_POLL_OUTREADY();
// 13. Perform a Data Read instruction. A value of 0x0D is okay.
C2_EXPECT_DATA(0x0D);
return C2_SUCCESS;
}
uint8_t c2_device_erase() {
// 1. Perform an Address Write with a value of FPDAT.
c2_address_write(C2FPDAT);
// 2. Perform a Data Write with the Device Erase command.
C2_DATA_WRITE_AND_CHECK(C2FPDAT_DEVICE_ERASE, 1);
// 3. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 4. Poll on OutReady using Address Read until the bit set.
C2_POLL_OUTREADY();
// 5. Perform a Data Read instruction. A value of 0x0D is okay.
C2_EXPECT_DATA(0x0D);
// 6. Perform a Data Write with a value of 0xDE.
C2_DATA_WRITE_AND_CHECK(0xDE, 1);
// 7. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 8. Perform a Data Write with a value of 0xAD.
C2_DATA_WRITE_AND_CHECK(0xAD, 1);
// 9. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 10. Perform a Data Write with a value of 0xA5.
C2_DATA_WRITE_AND_CHECK(0xA5, 1);
// 11. Poll on InBusy using Address Read until the bit clears.
C2_POLL_INBUSY();
// 12. Poll on OutReady using Address Read until the bit set.
C2_POLL_OUTREADY();
// 13. Perform a Data Read instruction. A value of 0x0D is okay.
C2_EXPECT_DATA(0x0D);
return C2_SUCCESS;
}
uint8_t c2_sfr_write_non_paged(uint8_t address, uint8_t data) {
// 1. Write the SFR address to the device using the Address Write instruction.
c2_address_write(address);
// 2. Write the SFR value to the device using the Data Write instruction.
C2_DATA_WRITE_AND_CHECK(data, 1);
return C2_SUCCESS;
}
uint8_t c2_sfr_write_paged(uint8_t address, uint8_t data) {
// 1. Perform an Address Write with a value of FPDAT.
c2_address_write(C2FPDAT);
// 2. Write the Direct Write command (0x0A) using a Data Write
C2_DATA_WRITE_AND_CHECK(C2FPDAT_DIRECT_WRITE, 1);
// 3. Poll InBusy until the data is processed by the PI.
C2_POLL_INBUSY();
// 4. Poll OutReady it sets to 1.
C2_POLL_OUTREADY();
// 5. Perform a Data Read to ensure a return value of 0x0D (no errors).
C2_EXPECT_DATA(0x0D);
// 6. Perform a Data Write with a value of the SFR address.
C2_DATA_WRITE_AND_CHECK(address, 1);
// 7. Poll InBusy until the data is processed by the PI.
C2_POLL_INBUSY();
// 8. Perform a Data Write with a value of 0x01.
C2_DATA_WRITE_AND_CHECK(0x01, 1);
// 9. Poll InBusy until the data is processed by the PI.
C2_POLL_INBUSY();
// 10. Perform a Data Write with the new SFR value.
C2_DATA_WRITE_AND_CHECK(data, 1);
// 11. Poll InBusy until the data is processed by the PI.
C2_POLL_INBUSY();
return C2_SUCCESS;
}
// 4.6. Reading from an SFR
// To read from an SFR on a device that does not have SFR paging:
uint8_t c2_sfr_read_non_paged(uint8_t address, uint8_t &v) {
// 1. Write the SFR address to the device using the Address Write instruction.
c2_address_write(address);
// 2. Read the SFR value from the device using the Data Read instruction.
C2_DATA_READ_AND_CHECK(v, 1);
return C2_SUCCESS;
}
// For devices with SFR paging, direct reads through the PI using the Direct Read command are recommended to ensure the SFR Page is managed properly.
// To read an SFR from a device with SFR paging:
uint8_t c2_sfr_read_paged(uint8_t address, uint8_t &v) {
// 1. Perform an Address Write with a value of FPDAT.
c2_address_write(C2FPDAT);
// 2. Write the Direct Read command (0x09) using a Data Write.
C2_DATA_WRITE_AND_CHECK(C2FPDAT_DIRECT_READ, 1);
// 3. Poll InBusy until the data is processed by the PI.
C2_POLL_INBUSY();
// 4. Poll OutReady until it sets to 1.
C2_POLL_OUTREADY();
// 5. Perform a Data Read to ensure a return value of 0x0D (no errors).
C2_EXPECT_DATA(0x0D);
// 6. Perform a Data Write with a value of the SFR address.
C2_DATA_WRITE_AND_CHECK(address, 1);
// 7. Poll InBusy until the data is processed by the PI.
C2_POLL_INBUSY();
// 8. Perform a Data Write with a value of 0x01.
C2_DATA_WRITE_AND_CHECK(0x01, 1);
// 9. Poll InBusy until the data is processed by the PI.
C2_POLL_INBUSY();
// 10. Poll OutReady until it sets to 0.
C2_POLL_OUTREADY();
// 11. Read the SFR value from the device using the Data Read instruction.
C2_DATA_READ_AND_CHECK(v, 1);
return C2_SUCCESS;
}
const char *c2_print_status_by_name(uint8_t ch) {
switch (ch) {
case C2_SUCCESS: return "Success";
case C2_SHIFT_TIMEOUT: return "Shift wait timeout error";
case C2_POLL_TIMEOUT: return "Register poll timeout error";
case C2_CMD_ERROR: return "In-command error";
case C2_BROKEN_LINK: return "Broken link, address read failed";
default: return "unknownl error";
}
}
// This is to enforce arduino-like formatting in kate
// kate: space-indent on; indent-width 2; mixed-indent off; indent-mode cstyle;