From af98161db27898ad02100b0c8e424f1cbb1523a6 Mon Sep 17 00:00:00 2001 From: Rob Tillaart Date: Sun, 18 Jun 2023 10:51:50 +0200 Subject: [PATCH] 0.1.0 TLC5947 --- libraries/TLC5947/.arduino-ci.yml | 28 ++++ libraries/TLC5947/.github/FUNDING.yml | 4 + .../.github/workflows/arduino-lint.yml | 13 ++ .../.github/workflows/arduino_test_runner.yml | 17 +++ .../TLC5947/.github/workflows/jsoncheck.yml | 18 +++ libraries/TLC5947/CHANGELOG.md | 12 ++ libraries/TLC5947/LICENSE | 21 +++ libraries/TLC5947/README.md | 127 ++++++++++++++++++ libraries/TLC5947/TLC5947.cpp | 103 ++++++++++++++ libraries/TLC5947/TLC5947.h | 51 +++++++ .../examples/TLC5947_demo/TLC5947_demo.ino | 48 +++++++ .../TLC5947_performance.ino | 65 +++++++++ libraries/TLC5947/keywords.txt | 25 ++++ libraries/TLC5947/library.json | 23 ++++ libraries/TLC5947/library.properties | 11 ++ libraries/TLC5947/test/unit_test_001.cpp | 90 +++++++++++++ 16 files changed, 656 insertions(+) create mode 100644 libraries/TLC5947/.arduino-ci.yml create mode 100644 libraries/TLC5947/.github/FUNDING.yml create mode 100644 libraries/TLC5947/.github/workflows/arduino-lint.yml create mode 100644 libraries/TLC5947/.github/workflows/arduino_test_runner.yml create mode 100644 libraries/TLC5947/.github/workflows/jsoncheck.yml create mode 100644 libraries/TLC5947/CHANGELOG.md create mode 100644 libraries/TLC5947/LICENSE create mode 100644 libraries/TLC5947/README.md create mode 100644 libraries/TLC5947/TLC5947.cpp create mode 100644 libraries/TLC5947/TLC5947.h create mode 100644 libraries/TLC5947/examples/TLC5947_demo/TLC5947_demo.ino create mode 100644 libraries/TLC5947/examples/TLC5947_performance/TLC5947_performance.ino create mode 100644 libraries/TLC5947/keywords.txt create mode 100644 libraries/TLC5947/library.json create mode 100644 libraries/TLC5947/library.properties create mode 100644 libraries/TLC5947/test/unit_test_001.cpp diff --git a/libraries/TLC5947/.arduino-ci.yml b/libraries/TLC5947/.arduino-ci.yml new file mode 100644 index 00000000..77a333f9 --- /dev/null +++ b/libraries/TLC5947/.arduino-ci.yml @@ -0,0 +1,28 @@ +platforms: + rpipico: + board: rp2040:rp2040:rpipico + package: rp2040:rp2040 + gcc: + features: + defines: + - ARDUINO_ARCH_RP2040 + warnings: + flags: + +packages: + rp2040:rp2040: + url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json + +compile: + # Choosing to run compilation tests on 2 different Arduino platforms + platforms: + - uno + # - due + # - zero + # - leonardo + - m4 + - esp32 + - esp8266 + # - mega2560 + - rpipico + diff --git a/libraries/TLC5947/.github/FUNDING.yml b/libraries/TLC5947/.github/FUNDING.yml new file mode 100644 index 00000000..90d9ab4c --- /dev/null +++ b/libraries/TLC5947/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: RobTillaart + diff --git a/libraries/TLC5947/.github/workflows/arduino-lint.yml b/libraries/TLC5947/.github/workflows/arduino-lint.yml new file mode 100644 index 00000000..8a26f14a --- /dev/null +++ b/libraries/TLC5947/.github/workflows/arduino-lint.yml @@ -0,0 +1,13 @@ + +name: Arduino-lint + +on: [push, pull_request] +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: arduino/arduino-lint-action@v1 + with: + library-manager: update + compliance: strict diff --git a/libraries/TLC5947/.github/workflows/arduino_test_runner.yml b/libraries/TLC5947/.github/workflows/arduino_test_runner.yml new file mode 100644 index 00000000..fadfa904 --- /dev/null +++ b/libraries/TLC5947/.github/workflows/arduino_test_runner.yml @@ -0,0 +1,17 @@ +--- +name: Arduino CI + +on: [push, pull_request] + +jobs: + runTest: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - run: | + gem install arduino_ci + arduino_ci.rb diff --git a/libraries/TLC5947/.github/workflows/jsoncheck.yml b/libraries/TLC5947/.github/workflows/jsoncheck.yml new file mode 100644 index 00000000..37a11298 --- /dev/null +++ b/libraries/TLC5947/.github/workflows/jsoncheck.yml @@ -0,0 +1,18 @@ +name: JSON check + +on: + push: + paths: + - '**.json' + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: json-syntax-check + uses: limitusus/json-syntax-check@v1 + with: + pattern: "\\.json$" + diff --git a/libraries/TLC5947/CHANGELOG.md b/libraries/TLC5947/CHANGELOG.md new file mode 100644 index 00000000..ecf75d2a --- /dev/null +++ b/libraries/TLC5947/CHANGELOG.md @@ -0,0 +1,12 @@ +# Change Log TLC5947 + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + + +## [0.1.0] - 2023-06-17 +- initial version + + diff --git a/libraries/TLC5947/LICENSE b/libraries/TLC5947/LICENSE new file mode 100644 index 00000000..16ef1551 --- /dev/null +++ b/libraries/TLC5947/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023-2023 Rob Tillaart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libraries/TLC5947/README.md b/libraries/TLC5947/README.md new file mode 100644 index 00000000..87311918 --- /dev/null +++ b/libraries/TLC5947/README.md @@ -0,0 +1,127 @@ + +[![Arduino CI](https://github.com/RobTillaart/TLC5947/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) +[![Arduino-lint](https://github.com/RobTillaart/TLC5947/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/TLC5947/actions/workflows/arduino-lint.yml) +[![JSON check](https://github.com/RobTillaart/TLC5947/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/TLC5947/actions/workflows/jsoncheck.yml) +[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/TLC5947/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/release/RobTillaart/TLC5947.svg?maxAge=3600)](https://github.com/RobTillaart/TLC5947/releases) + + +# TLC5947 + +TLC5947 is an Arduino library for TLC5947 24 channel 12 bit PWM module. + + +## Description + +This EXPERIMENTAL library allows easily control over a TLC5947 module. +To communicate it uses three (bit banging) serial lines. +This module provides in total 24 channels of 12 bit PWM. +So it allows 4096 greyscales or levels to be set, making the output pretty well tunable. +Main purpose is to drive LED's, see datasheet. + +The library is EXPERIMENTAL and needs more testing. +(changes of the interface are definitely possible). + + +#### Daisy chaining + +This library does **NOT** support daisy chaining yet. +The current version can control only 1 module. +To control multiple modules by giving them their own CLOCK line. +The data and latch can be shared (to be tested). + + +#### Links + +- https://www.adafruit.com/product/1429 +- https://github.com/RobTillaart/TLC5947 +- https://github.com/RobTillaart/PCA9634 (I2C) +- https://github.com/RobTillaart/PCA9635 (I2C) +- https://github.com/RobTillaart/PCA9685 (I2C) + + +## Interface + +```cpp +#include TLC5947.h +``` + +- **TLC5947(uint8_t clock, uint8_t data, uint8_t latch, uint8_t blank)** constructor. +Defines the pins used for uploading / writing the PWM data to the module. +The blank pin is explained in more detail below. +- **~TLC5947()** destructor +- **bool begin()** set the pinModes of the pins and their initial values. +- **void setPWM(uint8_t channel, uint16_t PWM)**. Writes a PWM value to the buffer to +be written later. +channel = 0..23, PWM = 0..4095 +- **void setAll(uint16_t PWM)** writes the same PWM value for all channels to the buffer. +- **uint16_t getPWM(uint8_t channel)** get PWM value from the buffer, +Note this value might differ from device when a new value is set after the last **write()**. +- **void write()** writes the buffer (24 x 12 bit) to the device. + + +#### Blank line + +The blank pin (line) is used to set all channels on or off. +This allows to "preload" the registers with values and enable them all at once. + +- **void enable()** all channels reflect last updated PWM value. +- **void disable()** all channels are off / 0. + + +## Performance + +Writing 24 x 12 bit takes time, however is still pretty fast. +On a 16 MHz UNO writing all 24 channels takes less than 4 milliseconds. + +Note that all channels must be written. + +| platform (MHz) | version | command | time (us) | notes | +|:----------------:|:---------:|:----------|:------------|:-------------| +| AVR/UNO (16) | 0.1.0 | setPWM() | 16 | 24 channels | +| AVR/UNO (16) | 0.1.0 | write() | 3808 | 24 channels | +| ESP32 (240) | 0.1.0 | setPWM() | 6 | 24 channels | +| ESP32 (240) | 0.1.0 | write() | 128 | 24 channels | + + +Measured with **TLC5947_performance.ino**. + + +## Future + +#### Must + +- update documentation + - links etc. +- buy hardware + - test test test + + +#### Should + +- add examples + - extend performance sketch +- test if partial write (e.g. first N channels) works. +- test "preloading" when module is disabled. +- AVR optimized bit banging, see **fastShiftOut** + - factor 2 - 4 could be achievable +- "dirty" flag for **bool writePending()**? + - set by **setPWM()** if value changes. + - would speed up unneeded **write()** too. + + +#### Could + +- add unit-tests +- add **void setPercentage(float perc)** and **float getPercentage()** wrappers. +- investigate how to reduce memory usage (now 48 bytes) + - could be 36 (12 bits / channel) or even 24 (8 bits/channel) + - derived class? +- add **setRGB(LED, R, G, B)** wrapper (channel 0..7) + 24 channels == 3 x 8 RGB LEDs +- return value for **setPWM()** ? + + +#### Won't + + diff --git a/libraries/TLC5947/TLC5947.cpp b/libraries/TLC5947/TLC5947.cpp new file mode 100644 index 00000000..12dd2c67 --- /dev/null +++ b/libraries/TLC5947/TLC5947.cpp @@ -0,0 +1,103 @@ +// +// FILE: TLC5947.cpp +// AUTHOR: Rob Tillaart +// VERSION: 0.1.0 +// DATE: 2023-06-17 +// PURPOSE: Arduino library for the TLC5947 24 channel PWM device +// URL: https://github.com/RobTillaart/TLC5947 + + +#include "TLC5947.h" + + +TLC5947::TLC5947(uint8_t clock, uint8_t data, uint8_t latch, uint8_t blank) +{ + _clock = clock; + _data = data; + _latch = latch; + _blank = blank; + _buffer = (uint16_t *) calloc(24, 2); +} + + +TLC5947::~TLC5947() +{ + if(_buffer) free(_buffer); +} + + +void TLC5947::setPWM(uint8_t channel, uint16_t PWM) +{ + _buffer[channel] = PWM > 4095 ? 4095 : PWM; +} + + +bool TLC5947::begin() +{ + pinMode(_clock, OUTPUT); + pinMode(_data, OUTPUT); + pinMode(_latch, OUTPUT); + pinMode(_blank, OUTPUT); + digitalWrite(_clock, LOW); + digitalWrite(_data, LOW); + digitalWrite(_latch, LOW); + digitalWrite(_blank, HIGH); // enable PWM mode + return true; +} + + +uint16_t TLC5947::getPWM(uint8_t channel) +{ + if (channel >= 24) return 0xFFFF; + return _buffer[channel] & 0x0FFF; +} + + +void TLC5947::setAll(uint16_t PWM) +{ + uint16_t pwm = PWM > 4095 ? 4095 : PWM; + for (int chan = 0; chan < 24; chan++) + { + _buffer[chan] = pwm; + } + write(); +} + + +void TLC5947::write() +{ + // also write when blank == LOW + // to "preload the registers" + // local vars to maximize speed. + uint8_t _clk = _clock; + uint8_t _dat = _data; + + for (int chan = 23; chan >= 0; chan--) + { + for (int mask = 0x1000; mask; mask >>= 1) + { + digitalWrite(_clk, LOW); + digitalWrite(_dat, _buffer[chan] & mask ? HIGH : LOW); + digitalWrite(_clk, HIGH); + } + } + digitalWrite(_clk, LOW); + // enable + digitalWrite(_latch, HIGH); + digitalWrite(_latch, LOW); +} + +void TLC5947::enable() +{ + digitalWrite(_blank, HIGH); +} + + +void TLC5947::disable() +{ + digitalWrite(_blank, LOW); +} + + +// -- END OF FILE -- + diff --git a/libraries/TLC5947/TLC5947.h b/libraries/TLC5947/TLC5947.h new file mode 100644 index 00000000..11be75e7 --- /dev/null +++ b/libraries/TLC5947/TLC5947.h @@ -0,0 +1,51 @@ +#pragma once +// +// FILE: TLC5947.h +// AUTHOR: Rob Tillaart +// VERSION: 0.1.0 +// DATE: 2023-06-17 +// PURPOSE: Arduino library for the TLC5947 24 channel PWM device +// URL: https://github.com/RobTillaart/TLC5947 + + +#define TLC5947_LIB_VERSION (F("0.1.0")) + + +#include "Arduino.h" + + +#define TLC5947_MAX_CHANNELS 24 + + +class TLC5947 +{ +public: + TLC5947(uint8_t clock, uint8_t data, uint8_t latch, uint8_t blank); + ~TLC5947(); + + bool begin(); + + // fill the buffer + void setPWM(uint8_t channel, uint16_t PWM); + void setAll(uint16_t PWM); + // get from the buffer, might differ from device! + uint16_t getPWM(uint8_t channel); + + // write the buffer to the device + void write(); + + // control the blank line. + void enable(); + void disable(); + +private: + uint16_t *_buffer; + uint8_t _clock; + uint8_t _data; + uint8_t _latch; + uint8_t _blank; +}; + + +// -- END OF FILE -- + diff --git a/libraries/TLC5947/examples/TLC5947_demo/TLC5947_demo.ino b/libraries/TLC5947/examples/TLC5947_demo/TLC5947_demo.ino new file mode 100644 index 00000000..4728ff81 --- /dev/null +++ b/libraries/TLC5947/examples/TLC5947_demo/TLC5947_demo.ino @@ -0,0 +1,48 @@ +// +// FILE: TLC5947_demo.ino +// AUTHOR: Rob Tillaart +// PURPOSE: demo +// URL: https://github.com/RobTillaart/TLC5947 + + +#include "TLC5947.h" + + +TLC5947 tlc(13, 12, 11, 10); + + +void setup() +{ + Serial.begin(115200); + Serial.println(__FILE__); + Serial.print("TLC5947_LIB_VERSION: \t"); + Serial.println(TLC5947_LIB_VERSION); + + if (tlc.begin() == false) + { + Serial.println("error"); + while(1); + } + + for (int ch = 0; ch < 24; ch++) + { + tlc.setPWM(ch, random(4096)); + } + tlc.write(); + + delay(1000); + tlc.disable(); + delay(1000); + tlc.enable(); + +} + + +void loop() +{ + +} + + +// -- END OF FILE -- + diff --git a/libraries/TLC5947/examples/TLC5947_performance/TLC5947_performance.ino b/libraries/TLC5947/examples/TLC5947_performance/TLC5947_performance.ino new file mode 100644 index 00000000..de52f764 --- /dev/null +++ b/libraries/TLC5947/examples/TLC5947_performance/TLC5947_performance.ino @@ -0,0 +1,65 @@ +// +// FILE: TLC5947_performance.ino +// AUTHOR: Rob Tillaart +// PURPOSE: demo +// URL: https://github.com/RobTillaart/TLC5947 + + +#include "TLC5947.h" + + +TLC5947 tlc(13, 12, 11, 10); + +uint32_t start, stop; + +void setup() +{ + Serial.begin(115200); + Serial.println(__FILE__); + Serial.print("TLC5947_LIB_VERSION: \t"); + Serial.println(TLC5947_LIB_VERSION); + + if (tlc.begin() == false) + { + Serial.println("error"); + while (1); + } + + testSetPWM(); + testWrite(); + + Serial.println("\nDone..."); +} + + +void loop() +{ +} + + +void testSetPWM() +{ + delay(100); + start = micros(); + for (int ch = 0; ch < 24; ch++) + { + tlc.setPWM(ch, 42); + } + stop = micros(); + Serial.print("SETPWM\t"); + Serial.println(stop - start); +} + + +void testWrite() +{ + delay(100); + start = micros(); + tlc.write(); + stop = micros(); + Serial.print("WRITE\t"); + Serial.println(stop - start); +} + + +// -- END OF FILE -- diff --git a/libraries/TLC5947/keywords.txt b/libraries/TLC5947/keywords.txt new file mode 100644 index 00000000..900ddec0 --- /dev/null +++ b/libraries/TLC5947/keywords.txt @@ -0,0 +1,25 @@ +# Syntax Colouring Map For TLC5947 + + +# Data types (KEYWORD1) +TLC5947 KEYWORD1 + + +# Methods and Functions (KEYWORD2) +begin KEYWORD2 + +setPWM KEYWORD2 +getPWM KEYWORD2 +setAll KEYWORD2 + +enable KEYWORD2 +disable KEYWORD2 + + +# Instances (KEYWORD2) + + +# Constants (LITERAL1) +TLC5947_LIB_VERSION LITERAL1 + + diff --git a/libraries/TLC5947/library.json b/libraries/TLC5947/library.json new file mode 100644 index 00000000..9ad8c3b8 --- /dev/null +++ b/libraries/TLC5947/library.json @@ -0,0 +1,23 @@ +{ + "name": "TLC5947", + "keywords": "", + "description": "Arduino library for TLC5947 24 channel 12 bit PWM.", + "authors": + [ + { + "name": "Rob Tillaart", + "email": "Rob.Tillaart@gmail.com", + "maintainer": true + } + ], + "repository": + { + "type": "git", + "url": "https://github.com/RobTillaart/TLC5947.git" + }, + "version": "0.1.0", + "license": "MIT", + "frameworks": "arduino", + "platforms": "*", + "headers": "TLC5947.h" +} diff --git a/libraries/TLC5947/library.properties b/libraries/TLC5947/library.properties new file mode 100644 index 00000000..dcbe87ac --- /dev/null +++ b/libraries/TLC5947/library.properties @@ -0,0 +1,11 @@ +name=TLC5947 +version=0.1.0 +author=Rob Tillaart +maintainer=Rob Tillaart +sentence=Arduino library for TLC5947 24 channel 12 bit PWM. +paragraph= +category=Sensors +url=https://github.com/RobTillaart/TLC5947 +architectures=* +includes=TLC5947.h +depends= diff --git a/libraries/TLC5947/test/unit_test_001.cpp b/libraries/TLC5947/test/unit_test_001.cpp new file mode 100644 index 00000000..bb523be9 --- /dev/null +++ b/libraries/TLC5947/test/unit_test_001.cpp @@ -0,0 +1,90 @@ +// +// FILE: unit_test_001.cpp +// AUTHOR: Rob Tillaart +// DATE: 2023-06-17 +// PURPOSE: unit tests for the TLC5947 library +// https://github.com/RobTillaart/TLC5947 +// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md +// + +// supported assertions +// ---------------------------- +// assertEqual(expected, actual); // a == b +// assertNotEqual(unwanted, actual); // a != b +// assertComparativeEquivalent(expected, actual); // abs(a - b) == 0 or (!(a > b) && !(a < b)) +// assertComparativeNotEquivalent(unwanted, actual); // abs(a - b) > 0 or ((a > b) || (a < b)) +// assertLess(upperBound, actual); // a < b +// assertMore(lowerBound, actual); // a > b +// assertLessOrEqual(upperBound, actual); // a <= b +// assertMoreOrEqual(lowerBound, actual); // a >= b +// assertTrue(actual); +// assertFalse(actual); +// assertNull(actual); + +// // special cases for floats +// assertEqualFloat(expected, actual, epsilon); // fabs(a - b) <= epsilon +// assertNotEqualFloat(unwanted, actual, epsilon); // fabs(a - b) >= epsilon +// assertInfinity(actual); // isinf(a) +// assertNotInfinity(actual); // !isinf(a) +// assertNAN(arg); // isnan(a) +// assertNotNAN(arg); // !isnan(a) + + +#include + + +#include "Arduino.h" +#include "TLC5947.h" + + + +unittest_setup() +{ + fprintf(stderr, "TLC5947_LIB_VERSION: %s\n", (char *) TLC5947_LIB_VERSION); +} + + +unittest_teardown() +{ +} + + +unittest(test_begin) +{ + TLC5947 tlc(13, 12, 11, 10); + + assertTrue(tlc.begin()); +} + + +unittest(test_setPWM_I) +{ + TLC5947 tlc(13, 12, 11, 10); + tlc.begin(); + + for (uint8_t chan = 0; chan < 24; chan++) + { + tlc.setPWM(chan, chan * 170); + assertEqual(tlc.getPWM(chan), chan * 170); + } +} + + +unittest(test_setPWM_II) +{ + TLC5947 tlc(13, 12, 11, 10); + tlc.begin(); + + tlc.setAll(421); + for (uint8_t chan = 0; chan < 24; chan++) + { + assertEqual(tlc.getPWM(chan), 421); + } +} + + +unittest_main() + + +// -- END OF FILE -- +