Teensy APA102 POV Poi / Pixel Poi Build. Tutorial

Hi guys!! I hope you are healthy and doin good!

I have one question. Can i use teensy 3.2 LC? Thank you!!!

Sure, why not
I did made some version using teensy LC and its fine. using SD card/apa102
It would be restricted to its SD reading speed, but still can use DMA for apa102 drawing
could reach up to 40 pixels and still have some good enough POV effect for flowart
 
Sure, why not
I did made some version using teensy LC and its fine. using SD card/apa102
It would be restricted to its SD reading speed, but still can use DMA for apa102 drawing
could reach up to 40 pixels and still have some good enough POV effect for flowart

In want to use same code as with 3.2 without SD card. Po ting do you have tiktok?
 
Hello
I'm David, I've been following this thread for a long time. I built a pixel poi on apa102 60 leds and teensy 3.5 tape, now I'm playing with pcb 128 leds apa 2020 and teensy 4.1 (I've already burned two teensy).
Teensy 3.5 and 60 led works with the mortonkopf code, unfortunately the code reading images from the sd card does not want to work on these poi. The code reading images from micro sd works on teensy 4.1 but it refreshes poorly, i.e. the image is displayed once per rotation and you can see piste points in the displayed images, there is no such phenomenon when I use the mortonkopf code. You can see that the diodes where there are 60 flash with a higher frequency than when reading the image from the micro sd. The second thing is where to look in the image positioning code? I have to rotate all graphics by 90 degrees :( Thanks in advance for your help.
 
According to the octows2811 page, you can also use the octows2811 to drive 4 streams of APA102's instead of 8 streams of WS2812's:

any more detail on how the Teensy 4.0 with an OctoWS2811 adaptor could be used to drive four APA102 strips?
That would be an advantage over the PropShield which is able to drive a single APA102 strip.
Thank you,
 
any more detail on how the Teensy 4.0 with an OctoWS2811 adaptor could be used to drive four APA102 strips?
That would be an advantage over the PropShield which is able to drive a single APA102 strip.
Thank you,
Wow, blast from the past. Note, while I bought the OctoWS2811 board some time in the past, I've never used it, and I generally only use small numbers of WS2812B leds instead of APA102.

But I have to imagine that if you were just using the board and not the library meant for using the board to display WS2812B strips, that it is just a matter of using a pair of pins for each LED instead of using a single pin for WS2812B (obviously your choice of pins to select from are limited, and you want the clock/data pins to be on the same RJ45 cable).. The page mentions something about switching SPI on Teensy (which the Teensy 4.0 can't do), but the google+ document mentioned is no longer there. Also, you would need to use some sort of parallel APA102 library (Fastleds perhaps).

Alternatively, you could just use something like a 74AHCT125 which provides 4 pins for translation, but you ultimately may need to add things like resistors, etc. like the Octows2811 board has.
 
Wow, blast from the past. Note, while I bought the OctoWS2811 board some time in the past, I've never used it, and I generally only use small numbers of WS2812B leds instead of APA102.

But I have to imagine that if you were just using the board and not the library meant for using the board to display WS2812B strips, that it is just a matter of using a pair of pins for each LED instead of using a single pin for WS2812B (obviously your choice of pins to select from are limited, and you want the clock/data pins to be on the same RJ45 cable).. The page mentions something about switching SPI on Teensy (which the Teensy 4.0 can't do), but the google+ document mentioned is no longer there. Also, you would need to use some sort of parallel APA102 library (Fastleds perhaps).

Alternatively, you could just use something like a 74AHCT125 which provides 4 pins for translation, but you ultimately may need to add things like resistors, etc. like the Octows2811 board has.


Thanks a lot, that helped understanding how the OctoWS2811 could be used with APA102 strips.
I've been able to use a single APA102 strip with an OctoWS2811 adaptor, with the PropShield and with the SmartMatrix v5. Now I'd like to use the SPI1 and SPI2 (the hw SPI ports) of Teensy 4 to drive multiple strips at maximum speed (not sw based bit banging), and I need to find a way to use those pins into the OctoWS2811 which connect to none of them though (except for SPI on pins 7 and 14).
 
Hi again everyone,
It's been a few years since my last post, nice to see this thread is still going.
I've recently started working on a new version and was wondering if anyone has tried MPT with the teensy 4?
https://github.com/KurtE/MTP_Teensy

I've used it with no problems on both the Teensy 4.0 and 4.1. With the Teensy 4.0 you can also use the memory above the program as a filesystem that can be exported with MTP. If you have the audio shield mounted, you can export file systems on the micro SD on the audio adapter, along with the flash memory chip that can be soldered underneath the Teensy.

With the Teensy 4.1, in addition to the program memory, you also can export the filesystems from the 2 chips that can be soldered underneath the teensy (either one psram chip and one flash memory chip or two psram chips -- note the psram chips are volatile and the flash memory chips are preserved across boots. For flash memory, you need to use different constructors based on what flash memory is used. Finally you can also export the file system on the micro SD card.

Here is the example program that I modified to use #ifdef's to enable/disable various filesystems.

Code:
// From /shared/arduino/git/MTP_t4.git/trunk/examples/mtp-basic/mtp-basic.ino

#include "SPI.h"
#include "SD.h"
#include "MTP.h"

#if defined(ARDUINO_TEENSY41)
// The Teensy 4.1 has two sets of solder pads underneath the Teensy that you
// can solder either 2 PSram chips, 1 flash memory chip, or 1 flash memory chip
// and a PSram chip.  The smaller flash chips tend to use NOR flash while the
// larger flash chips tend to use NAND flash.
#define USE_LFS_RAM			1	// T4.1 PSRAM (or RAM)
#define USE_LFS_QSPI_NOR		1	// T4.1 QSPI NOR flash (16MB)
#define USE_LFS_QSPI_NAND		1	// T4.1 QSPI NAND flash (128MB)

#else
#define USE_LFS_RAM			0	// T4.1 PSRAM (or RAM)
#define USE_LFS_QSPI_NOR		0	// T4.1 QSPI NOR flash (16MB)
#define USE_LFS_QSPI_NAND		0	// T4.1 QSPI NAND flash (128MB)
#endif

#if defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
// Teensy 4.0 and 4.1 can use the upper flash memory as a file system via
// LittleFS.  The top bytes (64K for Teensy 4.0 and 256K for Teensy 4.1) of
// this memory is reserved for EEPROM emulation and the LED blink restore
// program.  On the Teensy 4.0 you have 1 megabyte - 64K of flash for a
// filesystem.  On the Teensy 4.1, you have 7 megabytes - 256K of flash for the
// filesystem.
#define USE_LFS_PROGM			1	// T4.0/T4.1 Progam Flash
#else
#define USE_LFS_PROGM			0	// T4.0/T4.1 Progam Flash
#endif

#define USE_SD				1	// Use either the built-in micro SD card reader or the reader on pin 10 (audio adapter)
#define USE_LFS_SPI			1	// SPI Flash (soldered to the audio adapter or the built-in flash on the prop shield)

// If you use the audio adapter revision A through C on a Teensy LC, 3.2, 3.5,
// or 3.6, it needs to remap the MOSI/SCK pins because those pins (11 and 13)
// are also I2S pins used by the audio shield.  But the prop shield wants to
// use the normal definitions.  In general, I've used the prop shield mostly on
// the Teensy 3.2 and LC, while if I use it, I use the audio shield on the
// Teensy 3.5 or Teensy 3.6.
//
// The Teensy 4.0 and 4.1 uses the audio adapter revision D, and it uses the
// standard SPI pins.
#if defined(ARDUINO_TEENSY35) || defined(ARDUINO_TEENSY36)
#define USE_TEENSY3_AUDIO_SHIELD	1	// Whether to use the Audio shield MOSI/SCLK for Teensy 3.x processors
#else
#define USE_TEENSY3_AUDIO_SHIELD	0	// Use standard configuration
#endif

#if USE_EVENTS==1
  extern "C" int usb_init_events(void);
#else
  int usb_init_events(void) {}
#endif

#if USE_LFS_RAM==1 ||  USE_LFS_PROGM==1 || USE_LFS_QSPI_NOR==1 || USE_LFS_QSPI_NAND==1 || USE_LFS_SPI==1
  #include "LittleFS.h"
#endif

#if defined(__IMXRT1062__)
  // following only as long usb_mtp is not included in cores
  #if !__has_include("usb_mtp.h")
    #include "usb1_mtp.h"
  #endif
#else
  #ifndef BUILTIN_SDCARD 
    #define BUILTIN_SDCARD 254
  #endif
  void usb_mtp_configure(void) {}
#endif


/****  Start device specific change area  ****/
// SDClasses 
#if USE_SD==1
  // edit SPI to reflect your configuration
#if USE_TEENSY3_AUDIO_SHIELD
 // Teensy audio adapter for Teensy 3.2, 3.5, and 3.6 (revision A-C) needs to remap the MOSI and SCK pins
  #define SD_MOSI  7
  #define SD_MISO 12
  #define SD_SCK  14
#else
 // Standard Teensy configuration
  #define SD_MOSI 11
  #define SD_MISO 12
  #define SD_SCK  13
#endif

  #define SPI_SPEED SD_SCK_MHZ(33)				// adjust to sd card 

  #if defined (BUILTIN_SDCARD)
    const char *sd_str[]={"BUILTIN-SD", "AUDIO-SD"};		// edit to reflect your configuration
    const int cs[] = {BUILTIN_SDCARD, 10};			// edit to reflect your configuration
  #else
    const char *sd_str[]={"AUDIO-SD"};				// edit to reflect your configuration
    const int cs[] = {10};					// edit to reflect your configuration
  #endif
  const int nsd = sizeof(sd_str)/sizeof(const char *);

SDClass sdx[nsd];
#endif

//LittleFS classes
#if USE_LFS_RAM==1
  const char *lfs_ram_str[]	= {"RAM1","RAM2"};		// edit to reflect your configuration
  const int lfs_ram_size[]	= {2'000'000, 4'000'000};	// edit to reflect your configuration
  const int nfs_ram		= sizeof(lfs_ram_str)/sizeof(const char *);

  LittleFS_RAM ramfs[nfs_ram]; 
#endif

#if USE_LFS_QSPI_NOR==1
  const char *lfs_qspi_nor_str[]	= {"QSPI-NOR"};		// edit to reflect your configuration
  const int nfs_qspi_nor		= sizeof(lfs_qspi_nor_str)/sizeof(const char *);

  LittleFS_QSPIFlash qspifs_nor[nfs_qspi_nor]; 
#endif

#if USE_LFS_QSPI_NAND==1
  const char *lfs_qspi_nand_str[]	= {"QSPI-NAND"};	// edit to reflect your configuration
  const int nfs_qspi_nand		= sizeof(lfs_qspi_nand_str)/sizeof(const char *);

  LittleFS_QPINAND qspifs_nand[nfs_qspi_nand];
#endif

#if USE_LFS_PROGM==1
  const char *lfs_progm_str[]={"PROGRAM"};			// edit to reflect your configuration
  const int lfs_progm_size[] = {1'000'000};			// edit to reflect your configuration
  const int nfs_progm = sizeof(lfs_progm_str)/sizeof(const char *);

  LittleFS_Program progmfs[nfs_progm]; 
#endif

#if USE_LFS_SPI==1
  const char *lfs_spi_str[]={"SPI-FLASH"};			// edit to reflect your configuration
  const int lfs_cs[] = {6};					// edit to reflect your configuration
  const int nfs_spi = sizeof(lfs_spi_str)/sizeof(const char *);

LittleFS_SPIFlash spifs[nfs_spi];
#endif


MTPStorage_SD storage;
MTPD    mtpd(&storage);

void storage_configure()
{
  #if USE_SD==1
    #if defined SD_SCK
      SPI.setMOSI(SD_MOSI);
      SPI.setMISO(SD_MISO);
      SPI.setSCK(SD_SCK);
    #endif

    for(int ii=0; ii<nsd; ii++)
    { 
      #if defined(BUILTIN_SDCARD)
        if(cs[ii] == BUILTIN_SDCARD)
        {
          if(!sdx[ii].sdfs.begin(SdioConfig(FIFO_SDIO))) 
          { Serial.printf("SDIO Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
          }
          else
          {
            storage.addFilesystem(sdx[ii], sd_str[ii]);
            uint64_t totalSize = sdx[ii].totalSize();
            uint64_t usedSize  = sdx[ii].usedSize();
            Serial.printf("SDIO Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
            Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
          }
        }
        else if(cs[ii]<BUILTIN_SDCARD)
      #endif
      {
        pinMode(cs[ii],OUTPUT); digitalWriteFast(cs[ii],HIGH);
        if(!sdx[ii].sdfs.begin(SdSpiConfig(cs[ii], SHARED_SPI, SPI_SPEED))) 
        { Serial.printf("SD Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
        }
        else
        {
          storage.addFilesystem(sdx[ii], sd_str[ii]);
          uint64_t totalSize = sdx[ii].totalSize();
          uint64_t usedSize  = sdx[ii].usedSize();
          Serial.printf("SD Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
          Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
        }
      }
    }
    #endif

    #if USE_LFS_RAM==1
    for(int ii=0; ii<nfs_ram;ii++)
    {
      if(!ramfs[ii].begin(lfs_ram_size[ii])) 
      { Serial.printf("Ram Storage %d %s failed or missing",ii,lfs_ram_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(ramfs[ii], lfs_ram_str[ii]);
        uint64_t totalSize = ramfs[ii].totalSize();
        uint64_t usedSize  = ramfs[ii].usedSize();
        Serial.printf("RAM Storage %d %s ",ii,lfs_ram_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif

    #if USE_LFS_PROGM==1
    for(int ii=0; ii<nfs_progm;ii++)
    {
      if(!progmfs[ii].begin(lfs_progm_size[ii])) 
      { Serial.printf("Program Storage %d %s failed or missing",ii,lfs_progm_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(progmfs[ii], lfs_progm_str[ii]);
        uint64_t totalSize = progmfs[ii].totalSize();
        uint64_t usedSize  = progmfs[ii].usedSize();
        Serial.printf("Program Storage %d %s ",ii,lfs_progm_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif

    #if USE_LFS_QSPI_NOR==1
    for(int ii=0; ii<nfs_qspi_nor;ii++)
    {
      if(!qspifs_nor[ii].begin()) 
      { Serial.printf("QSPI Storage %d %s failed or missing",ii,lfs_qspi_nor_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(qspifs_nor[ii], lfs_qspi_nor_str[ii]);
        uint64_t totalSize = qspifs_nor[ii].totalSize();
        uint64_t usedSize  = qspifs_nor[ii].usedSize();
        Serial.printf("QSPI NOR Storage %d %s ",ii,lfs_qspi_nor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif

    #if USE_LFS_QSPI_NAND==1
    for(int ii=0; ii<nfs_qspi_nand;ii++)
    {
      if(!qspifs_nand[ii].begin()) 
      { Serial.printf("QSPI Storage %d %s failed or missing",ii,lfs_qspi_nand_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(qspifs_nand[ii], lfs_qspi_nand_str[ii]);
        uint64_t totalSize = qspifs_nand[ii].totalSize();
        uint64_t usedSize  = qspifs_nand[ii].usedSize();
        Serial.printf("QSPI NAND Storage %d %s ",ii,lfs_qspi_nand_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif

    #if USE_LFS_SPI==1
    for(int ii=0; ii<nfs_spi;ii++)
    {
      if(!spifs[ii].begin(lfs_cs[ii])) 
      { Serial.printf("SPIFlash Storage %d %d %s failed or missing",ii,lfs_cs[ii],lfs_spi_str[ii]); Serial.println();
      }
      else
      {
        storage.addFilesystem(spifs[ii], lfs_spi_str[ii]);
        uint64_t totalSize = spifs[ii].totalSize();
        uint64_t usedSize  = spifs[ii].usedSize();
        Serial.printf("SPIFlash Storage %d %d %s ",ii,lfs_cs[ii],lfs_spi_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
      }
    }
    #endif
}

void setup()
{ 
  while (!Serial && millis () < 3000)
    ;

  Serial.println("MTP_test");
  Serial.printf ("USE_LFS_RAM              = %d\n", USE_LFS_RAM);
  Serial.printf ("USE_LFS_QSPI_NOR         = %d\n", USE_LFS_QSPI_NOR);
  Serial.printf ("USE_LFS_QSPI_NAND        = %d\n", USE_LFS_QSPI_NAND);
  Serial.printf ("USE_LFS_PROGM            = %d\n", USE_LFS_PROGM);
  Serial.printf ("USE_SD                   = %d\n", USE_SD);
  Serial.printf ("USE_LFS_SPI              = %d\n", USE_LFS_SPI);
  Serial.printf ("USE_TEENSY3_AUDIO_SHIELD = %d\n", USE_TEENSY3_AUDIO_SHIELD);
  Serial.println ("");

  #if USE_EVENTS==1
    usb_init_events();
  #endif

  #if !__has_include("usb_mtp.h")
    usb_mtp_configure();
  #endif
  storage_configure();

}

void loop()
{ 
  mtpd.loop();

#if USE_EVENTS==1
  if(Serial.available())
  {
    char ch=Serial.read();
    Serial.println(ch);
    if(ch=='r') 
    {
      Serial.println("Reset");
      mtpd.send_DeviceResetEvent();
    }
  }
#endif
}
 

It's been a long time and I finally have some time to seek to move my POV project from T3 to T4​

I have to redig into old codes and things and test them on T4😂😂

@mercyful : the refresh speed could be higher if you use DMA with SPI, OctoWS2811 with enough hack could work on parallel apa as if it's shortened to 1/4 or 1/8 length, however the most time probably is used reading the SD, with SDIO or not.​


I have used DMA in T.LC/3.6 with my old code

not sure how much it needed to work out before using on T4 yet..
 
Greetings, not sure how alive this thread still is - I'm usually late to the party. Discovered LED poi this summer when a friend had some at a wedding we were at... and they were the Ignis ones, and cost a fortune. I suspected it could be done a *lot* cheaper and I have some coding / hacking experience so started trying to pull something together with bits I had lying around. These examples use the WS2812b strip, which I have since learnt really don't have the refresh rate required (and also look v. blocky compared to ones I've seen running the AP102 strips. I now have some of the APA102s on order - looking forward to more experimenting. Kudos to @mortonkopf and everyone else here for the work people have put into this already. 👏👏👏
 

Attachments

  • IMG_1096.jpg
    IMG_1096.jpg
    251.6 KB · Views: 22
  • IMG_1105.jpg
    IMG_1105.jpg
    308.8 KB · Views: 21
  • IMG_1118.jpg
    IMG_1118.jpg
    216 KB · Views: 24
  • IMG_1122.jpg
    IMG_1122.jpg
    490.1 KB · Views: 32
Back
Top