From 243b1500591477199ba042ab9d7642a2b89ebbd6 Mon Sep 17 00:00:00 2001 From: s-light Date: Sat, 6 Jan 2018 16:17:55 +0100 Subject: [PATCH 1/5] added TLC5971 test cases --- plugins/spi/SPIOutputTest.cpp | 300 ++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) diff --git a/plugins/spi/SPIOutputTest.cpp b/plugins/spi/SPIOutputTest.cpp index 20753b0b2a..e56a89f631 100644 --- a/plugins/spi/SPIOutputTest.cpp +++ b/plugins/spi/SPIOutputTest.cpp @@ -47,6 +47,8 @@ class SPIOutputTest: public CppUnit::TestFixture { CPPUNIT_TEST(testCombinedP9813Control); CPPUNIT_TEST(testIndividualAPA102Control); CPPUNIT_TEST(testCombinedAPA102Control); + CPPUNIT_TEST(testIndividualAPA102ControlPixelBrightness); + CPPUNIT_TEST(testIndividualTLC5971Control); CPPUNIT_TEST_SUITE_END(); public: @@ -969,3 +971,301 @@ void SPIOutputTest::testIndividualAPA102ControlPixelBrightness() { OLA_ASSERT_DATA_EQUALS(EXPECTED10, arraysize(EXPECTED10), data, length); OLA_ASSERT_EQ(7u, backend.Writes(0)); } + + +/** + * Test DMX writes in the individual TLC5971 mode. + */ +void SPIOutputTest::testIndividualTLC5971Control() { + // personality 9= Individual TLC5971 + const uint16_t this_test_personality = 9; + // setup Backend + FakeSPIBackend backend(2); + SPIOutput::Options options(0, "Test SPI Device"); + // setup pixel_count to 2 (enough to test all cases) + options.pixel_count = 3; + // setup SPIOutput + SPIOutput output(m_uid, &backend, options); + // set personality + output.SetPersonality(this_test_personality); + + // simulate incoming DMX data with this buffer + DmxBuffer buffer; + // setup a pointer to the returned data (the fake SPI data stream) + unsigned int length = 0; + const uint8_t *data = NULL; + + // test1 + // basic channel to output mapping (one devices) + // setup some 'DMX' data + buffer.SetFromString("0, 1, 0, 10, 0, 100," + "1, 1, 1, 10, 1, 100," + "2, 1, 2, 10, 2, 100," + "3, 1, 3, 10, 3, 100"); + // simulate incoming data + output.WriteDMX(buffer); + // get fake SPI data stream + data = backend.GetData(0, &length); + // this is the expected spi data stream: + const uint8_t EXPECTED1[] = { 0x94, 0x5F, 0xFF, 0xFF, // header + 0x00, 0x01, 0x00, 0x0A, 0x00, 0x64, // OUT0 + 0x01, 0x01, 0x01, 0x0A, 0x01, 0x64, // OUT1 + 0x02, 0x01, 0x02, 0x0A, 0x02, 0x64, // OUT2 + 0x03, 0x01, 0x03, 0x0A, 0x03, 0x64, // OUT3 + // device2 + 0x00, 0x00, 0x00, 0x00, // header + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT3 + // device3 + 0x00, 0x00, 0x00, 0x00, // header + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT3 + }; + // check for Equality + OLA_ASSERT_DATA_EQUALS(EXPECTED1, arraysize(EXPECTED1), data, length); + // check if the output writes are 1 + OLA_ASSERT_EQ(1u, backend.Writes(0)); + + // test2 + // basic channel to output mapping (two devices) + buffer.SetFromString("0, 1, 0, 10, 0, 100," + "1, 1, 1, 10, 1, 100," + "2, 1, 2, 10, 2, 100," + "3, 1, 3, 10, 3, 100," + "160, 1, 160, 10, 160, 100," + "161, 1, 161, 10, 161, 100," + "162, 1, 162, 10, 162, 100," + "163, 1, 163, 10, 163, 100"); + output.WriteDMX(buffer); + data = backend.GetData(0, &length); + const uint8_t EXPECTED2[] = { 0x94, 0x5F, 0xFF, 0xFF, // header + 0x00, 0x01, 0x00, 0x0A, 0x00, 0x64, // OUT0 + 0x01, 0x01, 0x01, 0x0A, 0x01, 0x64, // OUT1 + 0x02, 0x01, 0x02, 0x0A, 0x02, 0x64, // OUT2 + 0x03, 0x01, 0x03, 0x0A, 0x03, 0x64, // OUT3 + // device2 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0xA0, 0x01, 0xA0, 0x0A, 0xA0, 0x64, // OUT0 + 0xA1, 0x01, 0xA1, 0x0A, 0xA1, 0x64, // OUT1 + 0xA2, 0x01, 0xA2, 0x0A, 0xA2, 0x64, // OUT2 + 0xA3, 0x01, 0xA3, 0x0A, 0xA3, 0x64, // OUT3 + // device3 + 0x00, 0x00, 0x00, 0x00, // header + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT3 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED2, arraysize(EXPECTED2), data, length); + OLA_ASSERT_EQ(2u, backend.Writes(0)); + + // test3 + // test what happens when only new data for the first device is available. + // later data should be not modified so for device2 data set in test2 is valid + buffer.SetFromString("0, 1, 0, 10, 0, 240," + "1, 1, 1, 10, 1, 240," + "2, 1, 2, 10, 2, 240," + "3, 1, 3, 10, 3, 240"); + output.WriteDMX(buffer); + data = backend.GetData(0, &length); + const uint8_t EXPECTED3[] = { 0x94, 0x5F, 0xFF, 0xFF, // header + 0x00, 0x01, 0x00, 0x0A, 0x00, 0xF0, // OUT0 + 0x01, 0x01, 0x01, 0x0A, 0x01, 0xF0, // OUT1 + 0x02, 0x01, 0x02, 0x0A, 0x02, 0xF0, // OUT2 + 0x03, 0x01, 0x03, 0x0A, 0x03, 0xF0, // OUT3 + // device2 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0xA0, 0x01, 0xA0, 0x0A, 0xA0, 0x64, // OUT0 + 0xA1, 0x01, 0xA1, 0x0A, 0xA1, 0x64, // OUT1 + 0xA2, 0x01, 0xA2, 0x0A, 0xA2, 0x64, // OUT2 + 0xA3, 0x01, 0xA3, 0x0A, 0xA3, 0x64, // OUT3 + // device3 + 0x00, 0x00, 0x00, 0x00, // header + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT3 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED3, arraysize(EXPECTED3), data, length); + OLA_ASSERT_EQ(3u, backend.Writes(0)); + + // test4 + // tests what happens if fewer then needed color information are received + buffer.SetFromString("33, 99, 33, 99, 33"); + output.WriteDMX(buffer); + data = backend.GetData(0, &length); + // check that the returns are the same as test3 (nothing changed) + OLA_ASSERT_DATA_EQUALS(EXPECTED3, arraysize(EXPECTED3), data, length); + OLA_ASSERT_EQ(3u, backend.Writes(0)); + + // test5 + // test with changed StartAddress + // set StartAddress + output.SetStartAddress(10); + // values 1 & 2 should not be visible in SPI data stream + buffer.SetFromString("1, 2, 3, 4, 5, 6, 7, 8, 9," + "160, 10, 160, 11, 160, 12," + "161, 10, 161, 11, 161, 12," + "162, 10, 162, 11, 162, 12," + "163, 10, 163, 11, 163, 12," + "240, 10, 240, 11, 240, 12," + "241, 10, 241, 11, 241, 12," + "242, 10, 242, 11, 242, 12," + "243, 10, 243, 11, 243, 12"); + output.WriteDMX(buffer); + data = backend.GetData(0, &length); + const uint8_t EXPECTED5[] = { 0x94, 0x5F, 0xFF, 0xFF, // header + 0xA0, 0x0A, 0xA0, 0x0B, 0xA0, 0x0C, // OUT0 + 0xA1, 0x0A, 0xA1, 0x0B, 0xA1, 0x0C, // OUT1 + 0xA2, 0x0A, 0xA2, 0x0B, 0xA2, 0x0C, // OUT2 + 0xA3, 0x0A, 0xA3, 0x0B, 0xA3, 0x0C, // OUT3 + // device2 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0xF0, 0x0A, 0xF0, 0x0B, 0xF0, 0x0C, // OUT0 + 0xF1, 0x0A, 0xF1, 0x0B, 0xF1, 0x0C, // OUT1 + 0xF2, 0x0A, 0xF2, 0x0B, 0xF2, 0x0C, // OUT2 + 0xF3, 0x0A, 0xF3, 0x0B, 0xF3, 0x0C, // OUT3 + // device3 + 0x00, 0x00, 0x00, 0x00, // header + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OUT3 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED5, arraysize(EXPECTED5), data, length); + OLA_ASSERT_EQ(4u, backend.Writes(0)); + // change StartAddress back to default + output.SetStartAddress(1); + + // test6 + // Check nothing changed on the other output. + OLA_ASSERT_EQ(reinterpret_cast(NULL), + backend.GetData(1, &length)); + OLA_ASSERT_EQ(0u, backend.Writes(1)); + + // test7 + // test for multiple ports + // StartFrame is only allowed on first port. + SPIOutput::Options options1(1, "second SPI Device"); + // setup pixel_count to 2 (enough to test all cases) + options1.pixel_count = 2; + // setup SPIOutput + SPIOutput output1(m_uid, &backend, options1); + // set personality + output1.SetPersonality(this_test_personality); + // setup some 'DMX' data + buffer.SetFromString("160, 10, 160, 11, 160, 12," + "161, 10, 161, 11, 161, 12," + "162, 10, 162, 11, 162, 12," + "163, 10, 163, 11, 163, 12," + "240, 10, 240, 11, 240, 12," + "241, 10, 241, 11, 241, 12," + "242, 10, 242, 11, 242, 12," + "243, 10, 243, 11, 243, 12"); + // simulate incoming data + output1.WriteDMX(buffer); + // get fake SPI data stream + data = backend.GetData(1, &length); + // this is the expected spi data stream: + // StartFrame is missing --> port is >0 ! + const uint8_t EXPECTED7[] = { 0x94, 0x5F, 0xFF, 0xFF, // header + 0xA0, 0x0A, 0xA0, 0x0B, 0xA0, 0x0C, // OUT0 + 0xA1, 0x0A, 0xA1, 0x0B, 0xA1, 0x0C, // OUT1 + 0xA2, 0x0A, 0xA2, 0x0B, 0xA2, 0x0C, // OUT2 + 0xA3, 0x0A, 0xA3, 0x0B, 0xA3, 0x0C, // OUT3 + // device2 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0xF0, 0x0A, 0xF0, 0x0B, 0xF0, 0x0C, // OUT0 + 0xF1, 0x0A, 0xF1, 0x0B, 0xF1, 0x0C, // OUT1 + 0xF2, 0x0A, 0xF2, 0x0B, 0xF2, 0x0C, // OUT2 + 0xF3, 0x0A, 0xF3, 0x0B, 0xF3, 0x0C, // OUT3 + }; + // check for Equality + OLA_ASSERT_DATA_EQUALS(EXPECTED7, arraysize(EXPECTED7), data, length); + // check if the output writes are 1 + OLA_ASSERT_EQ(1u, backend.Writes(1)); + + // test8 + // create new output with pixel_count=16 and check data length + // setup pixel_count to 16 + options.pixel_count = 6; + // setup SPIOutput + SPIOutput output2(m_uid, &backend, options); + // set personality + output2.SetPersonality(this_test_personality); + buffer.SetFromString( // device 1 + " 0, 10, 0, 11, 0, 12," + " 1, 10, 1, 11, 1, 12," + " 2, 10, 2, 11, 2, 12," + " 3, 10, 3, 11, 3, 12," + // device 2 + "160, 10, 160, 11, 160, 12," + "161, 10, 161, 11, 161, 12," + "162, 10, 162, 11, 162, 12," + "163, 10, 163, 11, 163, 12," + // device 3 + "240, 10, 240, 11, 240, 12," + "241, 10, 241, 11, 241, 12," + "242, 10, 242, 11, 242, 12," + "243, 10, 243, 11, 243, 12," + // device 4 + " 0, 10, 0, 11, 0, 12," + " 1, 10, 1, 11, 1, 12," + " 2, 10, 2, 11, 2, 12," + " 3, 10, 3, 11, 3, 12," + // device 5 + "160, 10, 160, 11, 160, 12," + "161, 10, 161, 11, 161, 12," + "162, 10, 162, 11, 162, 12," + "163, 10, 163, 11, 163, 12," + // device 6 + "240, 10, 240, 11, 240, 12," + "241, 10, 241, 11, 241, 12," + "242, 10, 242, 11, 242, 12," + "243, 10, 243, 11, 243, 12"); + output2.WriteDMX(buffer); + data = backend.GetData(0, &length); + const uint8_t EXPECTED8[] = { // device 1 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, // OUT0 + 0x01, 0x0A, 0x01, 0x0B, 0x01, 0x0C, // OUT1 + 0x02, 0x0A, 0x02, 0x0B, 0x02, 0x0C, // OUT2 + 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, // OUT3 + // device 2 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0xA0, 0x0A, 0xA0, 0x0B, 0xA0, 0x0C, // OUT0 + 0xA1, 0x0A, 0xA1, 0x0B, 0xA1, 0x0C, // OUT1 + 0xA2, 0x0A, 0xA2, 0x0B, 0xA2, 0x0C, // OUT2 + 0xA3, 0x0A, 0xA3, 0x0B, 0xA3, 0x0C, // OUT3 + // device 3 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0xF0, 0x0A, 0xF0, 0x0B, 0xF0, 0x0C, // OUT0 + 0xF1, 0x0A, 0xF1, 0x0B, 0xF1, 0x0C, // OUT1 + 0xF2, 0x0A, 0xF2, 0x0B, 0xF2, 0x0C, // OUT2 + 0xF3, 0x0A, 0xF3, 0x0B, 0xF3, 0x0C, // OUT3 + // device 4 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, // OUT0 + 0x01, 0x0A, 0x01, 0x0B, 0x01, 0x0C, // OUT1 + 0x02, 0x0A, 0x02, 0x0B, 0x02, 0x0C, // OUT2 + 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, // OUT3 + // device 5 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0xA0, 0x0A, 0xA0, 0x0B, 0xA0, 0x0C, // OUT0 + 0xA1, 0x0A, 0xA1, 0x0B, 0xA1, 0x0C, // OUT1 + 0xA2, 0x0A, 0xA2, 0x0B, 0xA2, 0x0C, // OUT2 + 0xA3, 0x0A, 0xA3, 0x0B, 0xA3, 0x0C, // OUT3 + // device 6 + 0x94, 0x5F, 0xFF, 0xFF, // header + 0xF0, 0x0A, 0xF0, 0x0B, 0xF0, 0x0C, // OUT0 + 0xF1, 0x0A, 0xF1, 0x0B, 0xF1, 0x0C, // OUT1 + 0xF2, 0x0A, 0xF2, 0x0B, 0xF2, 0x0C, // OUT2 + 0xF3, 0x0A, 0xF3, 0x0B, 0xF3, 0x0C, // OUT3 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED8, arraysize(EXPECTED8), data, length); + OLA_ASSERT_EQ(5u, backend.Writes(0)); +} From 017fce5eea8b06eb559c1e93036f068f2790f31c Mon Sep 17 00:00:00 2001 From: s-light Date: Sat, 13 Jan 2018 23:27:22 +0100 Subject: [PATCH 2/5] readded TLC5971 code --- plugins/spi/SPIOutput.cpp | 275 ++++++++++++++++++++++++++++++++++ plugins/spi/SPIOutput.h | 92 +++++++++++- plugins/spi/SPIOutputTest.cpp | 8 +- 3 files changed, 369 insertions(+), 6 deletions(-) diff --git a/plugins/spi/SPIOutput.cpp b/plugins/spi/SPIOutput.cpp index d6c74ea07f..b3fb4671b1 100644 --- a/plugins/spi/SPIOutput.cpp +++ b/plugins/spi/SPIOutput.cpp @@ -89,7 +89,10 @@ const uint16_t SPIOutput::WS2801_SLOTS_PER_PIXEL = 3; const uint16_t SPIOutput::LPD8806_SLOTS_PER_PIXEL = 3; const uint16_t SPIOutput::P9813_SLOTS_PER_PIXEL = 3; const uint16_t SPIOutput::APA102_SLOTS_PER_PIXEL = 3; +// 3 ch color + 1 pixel brightness const uint16_t SPIOutput::APA102PB_SLOTS_PER_PIXEL = 4; +// 12 channels @ 16bit = 24 dmx channels +const uint16_t SPIOutput::TLC5971_SLOTS_PER_DEVICE = 24; // Number of bytes that each pixel uses on the SPI wires // (if it differs from 1:1 with colors) @@ -99,6 +102,39 @@ const uint16_t SPIOutput::APA102_SPI_BYTES_PER_PIXEL = 4; const uint16_t SPIOutput::APA102_START_FRAME_BYTES = 4; const uint8_t SPIOutput::APA102_LEDFRAME_START_MARK = 0xE0; + +const uint16_t SPIOutput::TLC5971_SPI_BYTES_PER_DEVICE = 28; +// struct TLC5971_PACKET_CONFIG_MASKS { +// // Write Command (6Bit) +// const uint8_t WRCMD = 0b00111111; +// // Function Control Data (5 x 1Bit = 5Bit) +// const uint8_t OUTTMG = 0b00000001; +// const uint8_t EXTGCK = 0b00000001; +// const uint8_t TMGRST = 0b00000001; +// const uint8_t DSPRPT = 0b00000001; +// const uint8_t BLANK = 0b00000001; +// // BC-Data (3 x 7Bits = 21Bit) +// const uint8_t BCB = 0b01111111; +// const uint8_t BCG = 0b01111111; +// const uint8_t BCR = 0b01111111; +// }; +// +// const struct TLC5971_PACKET_CONFIG_LSHIFT { +// // Write Command (6Bit) +// const uint8_t WRCMD = 0 + 7 + 7 + 7 + 1 + 1 + 1 + 1 + 6; +// // Function Control Data (5 x 1Bit = 5Bit) +// const uint8_t OUTTMG = 0 + 7 + 7 + 7 + 1 + 1 + 1 + 1; +// const uint8_t EXTGCK = 0 + 7 + 7 + 7 + 1 + 1 + 1; +// const uint8_t TMGRST = 0 + 7 + 7 + 7 + 1 + 1; +// const uint8_t DSPRPT = 0 + 7 + 7 + 7 + 1; +// const uint8_t BLANK = 0 + 7 + 7 + 7; +// // BC-Data (3 x 7Bits = 21Bit) +// const uint8_t BCB = 0 + 7 + 7; +// const uint8_t BCG = 0 + 7; +// const uint8_t BCR = 0; +// }; + + SPIOutput::RDMOps *SPIOutput::RDMOps::instance = NULL; const ola::rdm::ResponderOps::ParamHandler @@ -214,6 +250,9 @@ SPIOutput::SPIOutput(const UID &uid, SPIBackendInterface *backend, personalities.insert(personalities.begin() + PERS_APA102PB_COMBINED - 1, Personality(m_pixel_count * APA102PB_SLOTS_PER_PIXEL, "APA102 with pixel brightness Combined Control")); + personalities.insert(personalities.begin() + PERS_TLC5971_INDIVIDUAL - 1, + Personality(m_pixel_count * TLC5971_SLOTS_PER_DEVICE, + "TLC5971 Individual Control (16bit per channel)")); m_personality_collection.reset(new PersonalityCollection(personalities)); m_personality_manager.reset(new PersonalityManager( @@ -341,6 +380,9 @@ bool SPIOutput::InternalWriteDMX(const DmxBuffer &buffer) { case PERS_APA102PB_COMBINED: CombinedAPA102ControlPixelBrightness(buffer); break; + case PERS_TLC5971_INDIVIDUAL: + IndividualTLC5971Control(buffer); + break; default: break; } @@ -830,6 +872,239 @@ uint8_t SPIOutput::CalculateAPA102PixelBrightness(uint8_t brightness) { +void SPIOutput::IndividualTLC5971Control(const DmxBuffer &buffer) { + // some detailed information on the protocol: + // http://www.ti.com/lit/ds/symlink/tlc5971.pdf + // 8.5.4 Register and Data Latch Configuration (page23) + // 9.2.2.3 How to Control the TLC5971 (page27) + // How to send: + // the first data we send are received by the last device in chain. + // Device Nth (244Bit = 28Byte) + // Write Command (6Bit) + // WRCMD (fixed: 25h) + // Function Control Data (5 x 1Bit = 5Bit) + // OUTTMG 1bit; GS clock edge select + // 1=rising edge, 0= falling edge + // EXTGCK 1bit; GS reference clock select + // 1=SCKI clock, 0=internal oscillator + // TMGRST 1bit; display timing reset mode + // 1=OUT forced of on latchpulse, 0=no forced reset + // DSPRPT 1bit; display repeat mode + // 1=auto repeate + // 0=Out only turned on after Blank or internal latchpulse + // BLANK 1bit; + // 1=blank (outputs off) + // 0=Out on - controlled by GS-Data + // ic power on sets this to 1 + // BC-Data (3 x 7Bits = 21Bit) + // BCB 7bit; + // BCG 7bit; + // BCR 7bit; + // GS-Data (12 x 16Bits = 192Bit) + // GSB3 16bit; + // GSG3 16bit; + // GSR3 16bit; + // GSB2 16bit; + // GSG2 16bit; + // GSR2 16bit; + // GSB1 16bit; + // GSG1 16bit; + // GSR1 16bit; + // GSB0 16bit; + // GSG0 16bit; + // GSR0 16bit; + // Device Nth-1 (244Bit = 28Byte) + // Device .. + // Device 2 + // Device 1 + // short brake of 8x period of clock (666ns .. 2.74ms) to generate latchpulse + // + 1.34uS + // than next update. + + // OLA_WARN << "******************************************"; + + // calculate DMX-start-address + const unsigned int first_slot = m_start_address - 1; // 0 offset + + // calculate how much channels for full devices are available in dmx_buffer + uint16_t devices_in_buffer = + (buffer.Size() - first_slot) / TLC5971_SLOTS_PER_DEVICE; + // OLA_WARN << " devices_in_buffer:" + // << static_cast(devices_in_buffer); + + // only do something if at least 1 device can be updated.. + if (devices_in_buffer == 0) { + OLA_INFO << "Insufficient DMX data, required " << TLC5971_SLOTS_PER_DEVICE + << ", got " << buffer.Size() - first_slot; + return; + } + + // rename m_pxiel_count for easier understanding. + const unsigned int device_count = m_pixel_count; + + // We always check out the entire string length, even if we only have data + // for part of it + uint16_t output_length = (device_count * TLC5971_SPI_BYTES_PER_DEVICE); + + uint8_t *output = m_backend->Checkout( + m_output_number, + output_length); + + // only update SPI data if possible + if (!output) { + return; + } + + for ( + uint16_t device_index = 0; + device_index < devices_in_buffer; + device_index++ + ) { + // OLA_WARN << " ~~~~~"; + uint16_t dmx_offset = + first_slot + (device_index * TLC5971_SLOTS_PER_DEVICE); + + uint16_t spi_offset = (device_index * TLC5971_SPI_BYTES_PER_DEVICE); + + // OLA_WARN << " device_index:" + // << static_cast(device_index); + // OLA_WARN << " dmx_offset:" + // << static_cast(dmx_offset); + // OLA_WARN << " spi_offset:" + // << static_cast(spi_offset); + + // setup configuration for this device. + TLC5971_packet_t device_data; + // this configuration values are currently hard coded.. + // following values are equal for all devices. + // device_data.fields.config.fields.WRCMD = 0x25; + // device_data.fields.config.fields.OUTTMG = 0; // falling edge + // device_data.fields.config.fields.EXTGCK = 0; // internal + // device_data.fields.config.fields.TMGRST = 0; // no forced reset + // device_data.fields.config.fields.DSPRPT = 1; // auto repeate + // device_data.fields.config.fields.BLANK = 0; // output enabled + // device_data.fields.config.fields.BCB = 0x7F; // full + // // device_data.fields.config.fields.BCG = 0x7F; // full + // device_data.fields.config.fields.BCG = 0x00; // 0 for test + // device_data.fields.config.fields.BCR = 0x7F; // full + + // OLA_WARN << "TLC5971_packet_config_t size:" + // << sizeof(TLC5971_packet_config_t); + // should return 4 + + // OLA_WARN << "FC + BC data:"; + // for (uint16_t i = 0; i < 4; i++) { + // OLA_WARN << "[" << static_cast(i) << "] " + // << std::bitset<8>(device_data.fields.config.bytes[i]); + // } + + // // reset + // device_data.fields.config[0] = 0; + // device_data.fields.config[0] = 0; + // device_data.fields.config[0] = 0; + // device_data.fields.config[0] = 0; + // // fill byte 0 + // device_data.fields.config[0] |= + // static_cast(0x25) << TLC5971_PACKET_CONFIG_LSHIFT_WRCMD; + // device_data.fields.config[0] |= + // (0 & TLC5971_PACKET_CONFIG_MASKS_OUTTMG) // falling edge + // << TLC5971_PACKET_CONFIG_LSHIFT_OUTTMG; + // device_data.fields.config[0] |= + // (0 & TLC5971_PACKET_CONFIG_MASKS_EXTGCK) // internal + // << TLC5971_PACKET_CONFIG_LSHIFT_EXTGCK; + // // byte border ------------------------------------------ + // // fill byte 1 + // device_data.fields.config[1] |= + // (0 & TLC5971_PACKET_CONFIG_MASKS_TMGRST) // no forced reset + // << TLC5971_PACKET_CONFIG_LSHIFT_TMGRST; + // device_data.fields.config[1] |= + // (1 & TLC5971_PACKET_CONFIG_MASKS_DSPRPT) // auto repeate + // << TLC5971_PACKET_CONFIG_LSHIFT_DSPRPT; + // device_data.fields.config[1] |= + // (0 & TLC5971_PACKET_CONFIG_MASKS_BLANK) // output enabled + // << TLC5971_PACKET_CONFIG_LSHIFT_BLANK; + // // BC data could be device dependent to calibrate led colors + // uint8_t temp_BCB = 0x7F; // full + // uint8_t temp_BCG = 0x7F; // full + // uint8_t temp_BCR = 0x7F; // full + // device_data.fields.config[1] |= + // (temp_BCB & TLC5971_PACKET_CONFIG_MASKS_BCB) + // >> TLC5971_PACKET_CONFIG_LSHIFT_BCB_RS; + // // byte border ------------------------------------------ + // device_data.fields.config[2] |= + // (temp_BCB & TLC5971_PACKET_CONFIG_MASKS_BCB) + // << TLC5971_PACKET_CONFIG_LSHIFT_BCB_LS; + // device_data.fields.config[2] |= + // (temp_BCG & TLC5971_PACKET_CONFIG_MASKS_BCG) + // >> TLC5971_PACKET_CONFIG_LSHIFT_BCG_RS; + // // byte border ------------------------------------------ + // device_data.fields.config[3] |= + // (temp_BCG & TLC5971_PACKET_CONFIG_MASKS_BCG) + // << TLC5971_PACKET_CONFIG_LSHIFT_BCG_LS; + // device_data.fields.config[3] |= + // (temp_BCR & TLC5971_PACKET_CONFIG_MASKS_BCR) + // << TLC5971_PACKET_CONFIG_LSHIFT_BCR; + + // // fixed values for testing other things: + device_data.fields.config.bytes[0] = 0b10010100; // 0x94 + device_data.fields.config.bytes[1] = 0b01011111; // 0x5F + device_data.fields.config.bytes[2] = 0b11111111; // 0xFF + device_data.fields.config.bytes[3] = 0b11111111; // 0xFF + + + // OLA_WARN << "FC + BC data:"; + // for (uint16_t i = 0; i < 4; i++) { + // OLA_WARN << "[" << static_cast(i) << "] " + // << std::bitset<8>(device_data.fields.config.bytes[i]); + // } + + // fill gs data + // possible with + // device_data.gsdata.gs_fields.GSB3 = 65000 + // or + for ( + uint8_t gs_index = 0; + gs_index < TLC5971_SLOTS_PER_DEVICE; + gs_index++ + ) { + // OLA_WARN << " gs_index:" + // << static_cast(gs_index); + // OLA_WARN << " dmx_offset + gs_index:" + // << static_cast(dmx_offset + gs_index); + device_data.fields.gsdata.bytes[gs_index] = + buffer.Get(dmx_offset + gs_index); + } + + // OLA_WARN << "GS data:"; + // for (uint16_t i = 0; i < 24; i++) { + // OLA_WARN << "[" << static_cast(i) << "] " + // << std::bitset<8>(device_data.fields.gsdata.bytes[i]); + // } + + // OLA_WARN << " TLC5971_packet_t size:" + // << sizeof(TLC5971_packet_t); + // OLA_WARN << " device_data size:" + // << sizeof(device_data); + // should return 28byte = 224bit + + // copy data to output buffer + // memcpy(output + spi_offset, device_data.bytes, sizeof(TLC5971_packet_t)); + for ( + uint8_t data_index = 0; + data_index < sizeof(TLC5971_packet_t); + data_index++ + ) { + output[spi_offset + data_index] = device_data.bytes[data_index]; + } + } // for devices_in_buffer end + + // write output back + m_backend->Commit(m_output_number); +} + + + + RDMResponse *SPIOutput::GetDeviceInfo(const RDMRequest *request) { return ResponderHelper::GetDeviceInfo( request, ola::rdm::OLA_SPI_DEVICE_MODEL, diff --git a/plugins/spi/SPIOutput.h b/plugins/spi/SPIOutput.h index eb78e45f0d..a4fa85c326 100644 --- a/plugins/spi/SPIOutput.h +++ b/plugins/spi/SPIOutput.h @@ -51,7 +51,8 @@ class SPIOutput: public ola::rdm::DiscoverableRDMControllerInterface { PERS_APA102_INDIVIDUAL = 7, PERS_APA102_COMBINED = 8, PERS_APA102PB_INDIVIDUAL = 9, - PERS_APA102PB_COMBINED = 10 + PERS_APA102PB_COMBINED = 10, + PERS_TLC5971_INDIVIDUAL = 11, }; struct Options { @@ -131,6 +132,8 @@ class SPIOutput: public ola::rdm::DiscoverableRDMControllerInterface { void CombinedAPA102Control(const DmxBuffer &buffer); void IndividualAPA102ControlPixelBrightness(const DmxBuffer &buffer); void CombinedAPA102ControlPixelBrightness(const DmxBuffer &buffer); + void IndividualTLC5971Control(const DmxBuffer &buffer); + unsigned int LPD8806BufferSize() const; void WriteSPIData(const uint8_t *data, unsigned int length); @@ -206,7 +209,92 @@ class SPIOutput: public ola::rdm::DiscoverableRDMControllerInterface { static const uint16_t APA102PB_SLOTS_PER_PIXEL; static const uint16_t APA102_SPI_BYTES_PER_PIXEL; static const uint16_t APA102_START_FRAME_BYTES; - static const uint8_t APA102_LEDFRAME_START_MARK; + static const uint8_t APA102_LEDFRAME_START_MARK; + static const uint16_t TLC5971_SLOTS_PER_DEVICE; + static const uint16_t TLC5971_SPI_BYTES_PER_DEVICE; + + // TLC5971 data structure + PACK( + struct TLC5971_packet_config_fields_t{ + // Write Command (6Bit) + uint8_t WRCMD : 6; + // Function Control Data (5 x 1Bit = 5Bit) + uint8_t OUTTMG : 1; + uint8_t EXTGCK : 1; + uint8_t TMGRST : 1; + uint8_t DSPRPT : 1; + uint8_t BLANK : 1; + // BC-Data (3 x 7Bits = 21Bit) + uint8_t BCB : 7; + uint8_t BCG : 7; + uint8_t BCR : 7; + }); + + union TLC5971_packet_config_t { + uint8_t bytes[4]; + // 6 + 5 + 21 = 4byte + TLC5971_packet_config_fields_t fields; + }; + + // // Write Command (6Bit) + // static const uint8_t TLC5971_PACKET_CONFIG_MASKS_WRCMD = 0b00111111; + // // Function Control Data (5 x 1Bit = 5Bit) + // static const uint8_t TLC5971_PACKET_CONFIG_MASKS_OUTTMG = 0b00000001; + // static const uint8_t TLC5971_PACKET_CONFIG_MASKS_EXTGCK = 0b00000001; + // static const uint8_t TLC5971_PACKET_CONFIG_MASKS_TMGRST = 0b00000001; + // static const uint8_t TLC5971_PACKET_CONFIG_MASKS_DSPRPT = 0b00000001; + // static const uint8_t TLC5971_PACKET_CONFIG_MASKS_BLANK = 0b00000001; + // // BC-Data (3 x 7Bits = 21Bit) + // static const uint8_t TLC5971_PACKET_CONFIG_MASKS_BCB = 0b01111111; + // static const uint8_t TLC5971_PACKET_CONFIG_MASKS_BCG = 0b01111111; + // static const uint8_t TLC5971_PACKET_CONFIG_MASKS_BCR = 0b01111111; + // + // // Write Command (6Bit) + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_WRCMD = 2; + // // Function Control Data (5 x 1Bit = 5Bit) + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_OUTTMG = 1; + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_EXTGCK = 0; + // // byte border ------------------------------------------ + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_TMGRST = 7; + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_DSPRPT = 6; + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_BLANK = 5; + // // BC-Data (3 x 7Bits = 21Bit) + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_BCB_RS = 2; + // // byte border ------------------------------------------ + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_BCB_LS = 6; + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_BCG_RS = 1; + // // byte border ------------------------------------------ + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_BCG_LS = 7; + // static const uint8_t TLC5971_PACKET_CONFIG_LSHIFT_BCR = 0; + + union TLC5971_packet_gsdata_t { + uint8_t bytes[24]; + // the uint16_t will not work everywhere because of endianess problems.. + // 12ch @16bit = 24byte + struct { + uint16_t GSB3; + uint16_t GSG3; + uint16_t GSR3; + uint16_t GSB2; + uint16_t GSG2; + uint16_t GSR2; + uint16_t GSB1; + uint16_t GSG1; + uint16_t GSR1; + uint16_t GSB0; + uint16_t GSG0; + uint16_t GSR0; + } fields; + }; + + union TLC5971_packet_t { + uint8_t bytes[28]; + struct { + TLC5971_packet_config_t config; + // uint8_t config[4]; + TLC5971_packet_gsdata_t gsdata; + } fields; + }; static const ola::rdm::ResponderOps::ParamHandler PARAM_HANDLERS[]; diff --git a/plugins/spi/SPIOutputTest.cpp b/plugins/spi/SPIOutputTest.cpp index e5a6499cfe..b2020dbee6 100644 --- a/plugins/spi/SPIOutputTest.cpp +++ b/plugins/spi/SPIOutputTest.cpp @@ -990,7 +990,7 @@ void SPIOutputTest::testCombinedAPA102ControlPixelBrightness() { FakeSPIBackend backend(2); SPIOutput::Options options(0, "Test SPI Device"); // setup pixel_count to 2 (enough to test all cases) - options.pixel_count = 3; + options.pixel_count = 2; // setup SPIOutput SPIOutput output(m_uid, &backend, options); // set personality @@ -1144,11 +1144,11 @@ void SPIOutputTest::testCombinedAPA102ControlPixelBrightness() { */ void SPIOutputTest::testIndividualTLC5971Control() { // personality 9= Individual TLC5971 - const uint16_t this_test_personality = 9; + const uint16_t this_test_personality = SPIOutput::PERS_TLC5971_INDIVIDUAL; // setup Backend FakeSPIBackend backend(2); SPIOutput::Options options(0, "Test SPI Device"); - // setup pixel_count to 2 (enough to test all cases) + // setup pixel_count to 3 (enough to test all cases) options.pixel_count = 3; // setup SPIOutput SPIOutput output(m_uid, &backend, options); @@ -1357,7 +1357,7 @@ void SPIOutputTest::testIndividualTLC5971Control() { // test8 // create new output with pixel_count=16 and check data length - // setup pixel_count to 16 + // setup pixel_count to 6 options.pixel_count = 6; // setup SPIOutput SPIOutput output2(m_uid, &backend, options); From 3ead26f9e1357cca393b676cc4a8222d1f123285 Mon Sep 17 00:00:00 2001 From: s-light Date: Mon, 22 Jan 2018 18:13:26 +0100 Subject: [PATCH 3/5] fixed mergin bug --- plugins/spi/SPIOutput.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/spi/SPIOutput.cpp b/plugins/spi/SPIOutput.cpp index 82839e84b5..fcb86a2a22 100644 --- a/plugins/spi/SPIOutput.cpp +++ b/plugins/spi/SPIOutput.cpp @@ -88,7 +88,7 @@ const uint16_t SPIOutput::LPD8806_SLOTS_PER_PIXEL = 3; const uint16_t SPIOutput::P9813_SLOTS_PER_PIXEL = 3; const uint16_t SPIOutput::APA102_SLOTS_PER_PIXEL = 3; // 3 ch color + 1 pixel brightness -const uint16_t SPIOutput::APA102PB_SLOTS_PER_PIXEL = 4; +const uint16_t SPIOutput::APA102_PB_SLOTS_PER_PIXEL = 4; // 12 channels @ 16bit = 24 dmx channels const uint16_t SPIOutput::TLC5971_SLOTS_PER_DEVICE = 24; From 3607990c39a13c8582d8563d79345d7f00e5bdd3 Mon Sep 17 00:00:00 2001 From: s-light Date: Wed, 11 Apr 2018 18:43:33 +0200 Subject: [PATCH 4/5] fixed APA change --- plugins/spi/SPIOutput.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/spi/SPIOutput.h b/plugins/spi/SPIOutput.h index ca3bbee8b6..71221073cf 100644 --- a/plugins/spi/SPIOutput.h +++ b/plugins/spi/SPIOutput.h @@ -216,7 +216,7 @@ class SPIOutput: public ola::rdm::DiscoverableRDMControllerInterface { static const uint16_t APA102_PB_SLOTS_PER_PIXEL; static const uint16_t APA102_SPI_BYTES_PER_PIXEL; static const uint16_t APA102_START_FRAME_BYTES; - static const uint8_t APA102_LEDFRAME_START_MARK; + static const uint8_t APA102_LEDFRAME_START_MARK; static const uint16_t TLC5971_SLOTS_PER_DEVICE; static const uint16_t TLC5971_SPI_BYTES_PER_DEVICE; From 04da39ee1db6785f00c488f9c18863719b5a1e00 Mon Sep 17 00:00:00 2001 From: s-light Date: Thu, 12 Apr 2018 16:04:05 +0200 Subject: [PATCH 5/5] fix SPaG --- plugins/spi/SPIOutput.cpp | 6 +++--- plugins/spi/SPIOutput.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/spi/SPIOutput.cpp b/plugins/spi/SPIOutput.cpp index 99ea16a175..910ead3604 100644 --- a/plugins/spi/SPIOutput.cpp +++ b/plugins/spi/SPIOutput.cpp @@ -962,7 +962,7 @@ void SPIOutput::IndividualTLC5971Control(const DmxBuffer &buffer) { // Device .. // Device 2 // Device 1 - // short brake of 8x period of clock (666ns .. 2.74ms) to generate latchpulse + // short break of 8x period of clock (666ns .. 2.74ms) to generate latchpulse // + 1.34uS // than next update. @@ -971,7 +971,7 @@ void SPIOutput::IndividualTLC5971Control(const DmxBuffer &buffer) { // calculate DMX-start-address const unsigned int first_slot = m_start_address - 1; // 0 offset - // calculate how much channels for full devices are available in dmx_buffer + // calculate how many channels for full devices are available in dmx_buffer uint16_t devices_in_buffer = (buffer.Size() - first_slot) / TLC5971_SLOTS_PER_DEVICE; // OLA_WARN << " devices_in_buffer:" @@ -984,7 +984,7 @@ void SPIOutput::IndividualTLC5971Control(const DmxBuffer &buffer) { return; } - // rename m_pxiel_count for easier understanding. + // rename m_pixel_count for easier understanding. const unsigned int device_count = m_pixel_count; // We always check out the entire string length, even if we only have data diff --git a/plugins/spi/SPIOutput.h b/plugins/spi/SPIOutput.h index 71221073cf..a925d59e8e 100644 --- a/plugins/spi/SPIOutput.h +++ b/plugins/spi/SPIOutput.h @@ -276,7 +276,7 @@ class SPIOutput: public ola::rdm::DiscoverableRDMControllerInterface { union TLC5971_packet_gsdata_t { uint8_t bytes[24]; - // the uint16_t will not work everywhere because of endianess problems.. + // the uint16_t will not work everywhere because of endianness problems.. // 12ch @16bit = 24byte struct { uint16_t GSB3;