0

SpokePOV v1.4 windows source code?
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

SpokePOV v1.4 windows source code?

by hiduino on Sat Jun 27, 2015 6:16 pm

I got my daughter a new bicycle and purchased a bunch of the SpokePOV for her. It's been working great. But now I am trying to use bigger EEPROMs for more effects. I have been successfully using 8Kx8 and 16Kx8 eeproms, but the 32Kx8 is not detected correctly by the SpokePOV v1.4 program. I can program the 32Kx8 eeprom via other methods and it works correctly in the SpokePOV boards. So I know the 32Kx8 works. I just can't program them via the Window SpokePOV v1.4 program. I believe it has to do with the detection code in the SpokePOV v1.4 program. I would like to help troubleshoot the issue and would like to know if the Windows source code is available?

FYI, I am using this for the eeprom sources.
http://www.mouser.com/Semiconductors/Memory/EEPROM/_/N-488u2Zscv7?P=1z0z7v3&Keyword=25AA&Ns=Memory+Size%7c0&FS=True

hiduino
 
Posts: 860
Joined: Sat Sep 01, 2012 7:05 pm

Re: SpokePOV v1.4 windows source code?

by adafruit_support_rick on Sun Jun 28, 2015 1:22 pm


adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: SpokePOV v1.4 windows source code?

by hiduino on Sun Jun 28, 2015 6:15 pm

Thanks @adafruit_support_rick!

I took a look at the code and found some issues with the eeprom detection routines and with the tiny2313 firmware. This issue prevents the use of 32Kx8 eeproms and also will cause missing pages with certain bit patterns in the eeprom. I noticed that several users in the past years have complained about loosing 2 pages of eeprom, so that the SpokePOV program only shows 2 pages instead of 4 pages.

Also I have some ideas of how to support upto 64Kx8 eeproms with minimal changes.

The only issue is I don't think I have the environment to re-build the SpokePOV for Windows or Mac. However, I do have the resources to re-build the tiny2313 firmware. I'll be glad to share my findings in a later post.

hiduino
 
Posts: 860
Joined: Sat Sep 01, 2012 7:05 pm

Re: SpokePOV v1.4 windows source code?

by adafruit_support_rick on Mon Jun 29, 2015 9:48 am

Thanks! We'll look forward to your report!

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: SpokePOV v1.4 windows source code?

by hiduino on Tue Jun 30, 2015 11:36 pm

There are a few issues with the current wxSpokePOV program and the t2313 firmware.

1) The EEPROM size detection routine has a bug where certain memory patterns in the eeprom will fool the detection routine to end early and report only 2 pages of eeprom instead of 4 or more depending on the size of the eeprom. This is a rare case but it has happened to a number of users reported here in the forum over the years.

2) 32Kx8 does not get detected properly. This is partly the detection routine and how the tiny2313 firmware chooses between the t2313 internal eeprom vs. the external eeprom to read/write from. (I'll split this to a separate post.)

So first, the original eeprom size detection routine in the SpokePOVFrame::Connect() code is using a method of memory address wrap around to detect the size.
Original code from SpokePOVFrame::Connect():
Code: Select all | TOGGLE FULL SIZE
  unsigned char byte[6];
  /*
#if defined (WIN32_NATIVE)
  for (int delay=1; delay <=5; delay++) {
#else
  for (int delay=4; delay <=6; delay++) {
#endif
  */
  comm->reset(); // reset
  wxMilliSleep(1000);
  comm->delay = comm_delay;
  /*if (comm->readbyte(0, &byte1))
    break;
    wxLogDebug("trying longer delay %d", delay+1);
    }*/
 
  // check up to 32KB
  for (int i = 0; i<6; i++) {
    if (!comm->readbyte((1<<i)*1024, &byte[i]))
      return -1;
  }

  wxLogDebug("%d %d %d %d %d %d", byte[0], byte[1], byte[2], byte[3], byte[4], byte[5] );

  banks = 0;

  // is this a 1x memory?
  for (int j=0; j<6; j++) {
    wxLogDebug("test for %d banks", 1<<j);
    for (int i=j; i<6; i++) {
      if (byte[i] != byte[0])
   break;
    }
    // possibly!
    comm->writebyte(0, ~byte[0]); // invert one byte
    if (!comm->readbyte(1024*(1<<j), &ret)) {
      return -1;
    }
    if (ret == ((unsigned char)(~byte[0]))) {
      banks = 1<<j; // yes!
      break;
    } else {
      wxLogDebug("0x%02x 0x%02x", ret, (unsigned char)~byte[0]);
    }
    if (!comm->writebyte(0, byte[0])) {
      return -1;
    }
  }

  wxLogDebug("found %d banks of memory", banks);
This first section of this code is reading past each of the eeprom sizes boundry, 1K, 2K, 4K, 8K, 16K, and 32K to where it wraps around back to memory location 0 to compare against.
Code: Select all | TOGGLE FULL SIZE
  // check up to 32KB
  for (int i = 0; i<6; i++) {
    if (!comm->readbyte((1<<i)*1024, &byte[i]))
      return -1;
  }
However, in a later part it incorrectly uses byte[0] as memory location 0. This is actually location 0x400 on eeproms 2K and larger. This is where it fails to match the correct location and cause false detection sizes.
Code: Select all | TOGGLE FULL SIZE
    comm->writebyte(0, ~byte[0]); // invert one byte
    if (!comm->readbyte(1024*(1<<j), &ret)) {
      return -1;
    }
    if (ret == ((unsigned char)(~byte[0]))) {
      banks = 1<<j; // yes!
      break;
    } else {
This part appears to be just dead code that really doesn't do anything:
Code: Select all | TOGGLE FULL SIZE
    for (int i=j; i<6; i++) {
      if (byte[i] != byte[0])
   break;
    }

The updated code here fixes the corruption/detection issues and also allows for 64Kx8 eeprom sizes. Also is backwards compatible with old and new attiny2313 firmwares.
Code: Select all | TOGGLE FULL SIZE
 
  comm->reset(); // reset
  wxMilliSleep(1000);
  comm->delay = comm_delay;
 
  unsigned char byte[7],mem4;

  // Save memory 0x04
  if (!comm->readbyte(0x04, &mem4)) {
    return -1;
  }
  // check up to 64KB
  for (int i = 0; i<7; i++) {
    if (!comm->readbyte((1<<i)*1024+0x04, &byte[i]))
      return -1;
  }
  wxLogDebug("%d %d %d %d %d %d %d", byte[0], byte[1], byte[2], byte[3], byte[4], byte[5], byte[6] );

  banks = 0;
  comm->writebyte(0x04, (unsigned char)~mem4); // invert memory 0x04
  // is this a 1x memory?
  for (int j=0; j<7; j++) {
    wxLogDebug("test for %d banks", 1<<j);
    if (byte[j] == mem4) {
      // possibly!
      if (!comm->readbyte((1<<j)*1024+0x04, &ret)) {
        return -1;
      }
      if (ret == (unsigned char)~mem4) {
        banks = 1<<j; // yes!
        break;
      } else {  // nope!
        wxLogDebug("0x%02x 0x%02x", ret, (unsigned char)~byte[0]);
      }
    }
  }
  if (!comm->writebyte(0x04, mem4)) {  // restore memory 0x04
    return -1;
  }

  wxLogDebug("found %d banks of memory", banks);
spokepov.cpp
fixes EEPROM corruption and detection(updated for backwards compatibility)
(34.59 KiB) Downloaded 288 times

Also changing the spokepov.h for 64 pages max.
Code: Select all | TOGGLE FULL SIZE
#define MAX_BANKS 64
spokepov.h
(2.06 KiB) Downloaded 310 times
Last edited by hiduino on Fri Jul 10, 2015 11:43 pm, edited 6 times in total.

hiduino
 
Posts: 860
Joined: Sat Sep 01, 2012 7:05 pm

Re: SpokePOV v1.4 windows source code?

by hiduino on Tue Jun 30, 2015 11:59 pm

Continuing from previous post.

The detection routine relies on the passing memory location 0x8000 to read from for the 32Kx8 eeprom. It expect the eeprom will ignore the high bit and read from memory location 0x0. However, the t2313 firmware is mapping the internal eeprom to those locations 0x8000 - 0x8002 for the rotation offset, mirror, and animation time variables. So the firmware never allows for proper 32Kx8 eeprom detection.
Original tiny2313 firmware code v1.01:
Code: Select all | TOGGLE FULL SIZE
    switch (cmd) {
    case COMP_CMD_RDEEPROM:
    case COMP_CMD_RDEEPROM16:
      if  (cmd == COMP_CMD_RDEEPROM16)
   n = 16;
      else
   n = 1;

      addr = tx_computer_byte(0);
      addr <<= 8;
      addr |= tx_computer_byte(0);
     
      if ((addr & 0x8000) != 0) {
         tx_computer_byte(internal_eeprom_read(addr & 0xFF));
      } else {
   
   spieeprom_read(addr, buff, n);
   for (i=0; i<n; i++) {
     tx_computer_byte(buff[i]);
   }
      }

      tx_computer_byte(COMP_SUCCESS);
      break;

    case COMP_CMD_WREEPROM:
    case COMP_CMD_WREEPROM16:
      if (cmd == COMP_CMD_WREEPROM16)
   n = 16;
      else
   n = 1;

      addr = tx_computer_byte(0);
      addr <<= 8;
      addr |= tx_computer_byte(0);
      set_led((addr/16)%32,FRONT);
      for (i=0; i<n; i++) {
   buff[i] = tx_computer_byte(0);
      }

      tx_computer_byte(COMP_SUCCESS);

      if ((addr & 0x8000) != 0) {
   internal_eeprom_write(addr & 0xFF, buff[0]);
      } else {
   spieeprom_write(addr, buff, n);
      }
      break;
    }

The fix is to test external memory locations outside of the t2313 internal 0x8000 - 0x8002 locations. Also to only do this for single byte read/writes. This will never overlap with the detection routine or with normal external eeprom 16 byte read/writes.
Code: Select all | TOGGLE FULL SIZE
    switch (cmd) {
    case COMP_CMD_RDEEPROM:
    case COMP_CMD_RDEEPROM16:
      if  (cmd == COMP_CMD_RDEEPROM16)
           n = 16;
      else
           n = 1;

      addr = tx_computer_byte(0);
      addr <<= 8;
      addr |= tx_computer_byte(0);
     
      if (((addr & 0xFFFC) == 0x8000) && (n == 1)) {
         tx_computer_byte(internal_eeprom_read(addr & 0xFF));
      } else {
           spieeprom_read(addr, buff, n);
           for (i=0; i<n; i++) {
              tx_computer_byte(buff[i]);
          }
      }

      tx_computer_byte(COMP_SUCCESS);
      break;

    case COMP_CMD_WREEPROM:
    case COMP_CMD_WREEPROM16:
      if (cmd == COMP_CMD_WREEPROM16)
           n = 16;
      else
           n = 1;

      addr = tx_computer_byte(0);
      addr <<= 8;
      addr |= tx_computer_byte(0);
      set_led((addr/16)%32,FRONT);
      for (i=0; i<n; i++) {
          buff[i] = tx_computer_byte(0);
      }

      tx_computer_byte(COMP_SUCCESS);

      if (((addr & 0xFFFC) == 0x8000) && (n == 1)) {
           internal_eeprom_write(addr & 0xFF, buff[0]);
      } else {
          spieeprom_write(addr, buff, n);
      }
      break;
    }
  }
main.c
updated for spokepovfirmware1_01(updated for backwards compatibility)
(10.91 KiB) Downloaded 296 times


I have tested these fixes for the wxSpokePOV software and the tiny2313 firmware with EEPROM from 4Kx8, 8Kx8, 16Kx8, 32Kx8 and 64Kx8 sizes successfully.
(BTW, I was able to build wxSpokePOV under Windows with MinGW/msys and wxWidgets-2.8.1.)
For those who want to try:
spokepov.hex
for the attiny2313
(5.51 KiB) Downloaded 305 times

I would attach the Windows spokepov.exe file but it was a little too large.
Last edited by hiduino on Thu Jul 09, 2015 11:21 pm, edited 6 times in total.

hiduino
 
Posts: 860
Joined: Sat Sep 01, 2012 7:05 pm

Re: SpokePOV v1.4 windows source code?

by hiduino on Thu Jul 09, 2015 9:25 pm

I should note that i have updated wxSpokePOV program above in addition to the eeprom corruption/detection fix and 64Kx8 support, is backwards compatible with the original tiny2313 firmware v1.01. I also updated the attiny2313 firmware so that it is also backward compatible with the older wxSpokePOV program. The firmware update is needed only if you want to use 32Kx8 and 64Kx8 eeproms.

Note: To support the backawards compatibility I reverted back to the original tiny2313 internal EEPROM memory mapped addresses in the global.h file:
Code: Select all | TOGGLE FULL SIZE
#define EEPROM_ROTOFFSET 0x8000
#define EEPROM_MIRROR 0x8001
#define EEPROM_ANIMTIME 0x8002

hiduino
 
Posts: 860
Joined: Sat Sep 01, 2012 7:05 pm

Re: SpokePOV v1.4 windows source code?

by hiduino on Mon Jul 13, 2015 6:34 am

I was having failures with the Verify SpokePOV feature and found another potentially serious bug with wxSpokePOV v1.4 code. It has to do with reading/writing to eeprom banks from/to the wheel panel model[][] array for comparisons.

The model[256][30] array is created with 256 rows by 30 leds. However in the WheelPanel::GetModel() and WheelPanel::SetModel() functions they are trying to access 32 leds, which is out of bounds. This potentially could cause memory corruption on the Computer system.

From wheelplanel.cpp, the "led" index will range from 0 to 31:
Code: Select all | TOGGLE FULL SIZE
bool WheelPanel::GetModel(unsigned char buff[]) {
  int t;
  for (int i=0; i<ROWS_PER_WHEEL; i++) {
    for (int j = 0; j<4; j++) {
      t = 0;
      for (int k=0; k<8; k++) {
        t <<= 1;
        if (! model[i][j*8+k]) {
          t |= 0x1;
        }
      }
      buff[i*4+j] = t;
    }
  }
  return true;
}


bool WheelPanel::SetModel(unsigned char buff[1024]) {
  int row, led;
  for (int i=0; i<1024; i++) {
    row = i/4;
    for (int j = 0; j<8; j++) {
      led = (i%4)*8+j;
      if (buff[i] & (1<<(7-j)))
        model[row][led] = false;
      else
        model[row][led] = true;
    }   
  }
  Refresh();
  return true;
}


I updated the code to do a bounds checking so "led" index should only range from 0 to 29:
Code: Select all | TOGGLE FULL SIZE
bool WheelPanel::GetModel(unsigned char buff[]) {
  int t;
  for (int i=0; i<ROWS_PER_WHEEL; i++) {
    for (int j = 0; j<4; j++) {
      t = 0;
      for (int k=0; k<8; k++) {
        t <<= 1;
        if (j*8+k < num_leds) {  // bounds checking
          if (! model[i][j*8+k]) {
            t |= 0x1;
          }
        } else {
          t |= 0x01;  // default unused bits to off, for verification checks.
        }
      }
      buff[i*4+j] = t;
    }
  }
  return true;
}


bool WheelPanel::SetModel(unsigned char buff[1024]) {
  int row, led;
  for (int i=0; i<1024; i++) {
    row = i/4;
    for (int j = 0; j<8; j++) {
      led = (i%4)*8+j;
      if (led < num_leds) {  // bounds checking
         if (buff[i] & (1<<(7-j)))
            model[row][led] = false;
         else
            model[row][led] = true;
      }
   }
  }
  Refresh();
  return true;
}
wheelpanel.cpp
updated for bounds checking for GetModel() and SetModel()
(22.3 KiB) Downloaded 283 times

hiduino
 
Posts: 860
Joined: Sat Sep 01, 2012 7:05 pm

Please be positive and constructive with your questions and comments.