From 205ccbce1df07edf589d2a811cfd8739a33e14f1 Mon Sep 17 00:00:00 2001 From: Skylten Date: Sun, 14 Jun 2020 20:40:31 +0200 Subject: [PATCH] Actual code --- DEBIAN/control | 9 + DEBIAN/postinst | 45 + DEBIAN/postrm | 45 + DEBIAN/prerm | 44 + LICENSE | 24 + SConscript | 95 ++ SConstruct | 76 ++ clk.h | 65 ++ dma.c | 78 ++ dma.h | 126 +++ golang/README.md | 28 + golang/examples/basic.go | 60 ++ golang/ws2811/ws2811.go | 91 ++ golang/ws2811/ws2811.go.h | 64 ++ gpio.h | 108 +++ linux.py | 84 ++ mailbox.c | 292 ++++++ mailbox.h | 49 + main.c | 419 +++++++++ pcm.c | 127 +++ pcm.h | 158 ++++ pwm.c | 104 +++ pwm.h | 122 +++ python/.gitignore | 9 + python/README.md | 49 + python/aniamte_blink.py | 75 ++ python/aniamte_christmastree.py | 221 +++++ python/aniamte_starrynight.py | 160 ++++ python/aniamte_starrynight.py~ | 160 ++++ python/animate | 3 + python/animate.py | 82 ++ python/animinate-xmas.py | 1 + python/examples/SK6812_lowlevel.py | 104 +++ python/examples/SK6812_strandtest.py | 107 +++ python/examples/SK6812_white_test.py | 52 ++ python/examples/lightpaint.py | 126 +++ python/examples/lowlevel.py | 98 ++ python/examples/multistrandtest.py | 78 ++ python/examples/neopixelclock.py | 72 ++ python/examples/strandtest.py | 115 +++ python/ez_setup.py | 332 +++++++ python/leds.txt | 494 ++++++++++ python/neopixel.py | 151 +++ python/nohup.out | 0 python/overclock.txt | 26 + python/rpi_ws281x.i | 48 + python/setup.py | 18 + rpihw.c | 466 ++++++++++ rpihw.h | 51 + version | 1 + version.py | 71 ++ ws2811.c | 1284 ++++++++++++++++++++++++++ ws2811.h | 130 +++ 53 files changed, 6897 insertions(+) create mode 100644 DEBIAN/control create mode 100644 DEBIAN/postinst create mode 100644 DEBIAN/postrm create mode 100644 DEBIAN/prerm create mode 100644 LICENSE create mode 100644 SConscript create mode 100644 SConstruct create mode 100644 clk.h create mode 100644 dma.c create mode 100644 dma.h create mode 100644 golang/README.md create mode 100644 golang/examples/basic.go create mode 100644 golang/ws2811/ws2811.go create mode 100644 golang/ws2811/ws2811.go.h create mode 100644 gpio.h create mode 100644 linux.py create mode 100644 mailbox.c create mode 100644 mailbox.h create mode 100644 main.c create mode 100644 pcm.c create mode 100644 pcm.h create mode 100644 pwm.c create mode 100644 pwm.h create mode 100644 python/.gitignore create mode 100644 python/README.md create mode 100644 python/aniamte_blink.py create mode 100644 python/aniamte_christmastree.py create mode 100644 python/aniamte_starrynight.py create mode 100644 python/aniamte_starrynight.py~ create mode 100755 python/animate create mode 100644 python/animate.py create mode 120000 python/animinate-xmas.py create mode 100644 python/examples/SK6812_lowlevel.py create mode 100644 python/examples/SK6812_strandtest.py create mode 100644 python/examples/SK6812_white_test.py create mode 100644 python/examples/lightpaint.py create mode 100644 python/examples/lowlevel.py create mode 100644 python/examples/multistrandtest.py create mode 100644 python/examples/neopixelclock.py create mode 100644 python/examples/strandtest.py create mode 100644 python/ez_setup.py create mode 100644 python/leds.txt create mode 100644 python/neopixel.py create mode 100644 python/nohup.out create mode 100644 python/overclock.txt create mode 100644 python/rpi_ws281x.i create mode 100644 python/setup.py create mode 100644 rpihw.c create mode 100644 rpihw.h create mode 100644 version create mode 100644 version.py create mode 100644 ws2811.c create mode 100644 ws2811.h diff --git a/DEBIAN/control b/DEBIAN/control new file mode 100644 index 0000000..ab02891 --- /dev/null +++ b/DEBIAN/control @@ -0,0 +1,9 @@ +package: libws2811 +Version: 1.1.0-1 +Section: base +Priority: optional +Architecture: armhf +Depends: +Maintainer: Jeremy Garff (jer@jers.net) +Description: Raspberry Pi WS281X Library + LED Control Library for the Raspberry Pi. diff --git a/DEBIAN/postinst b/DEBIAN/postinst new file mode 100644 index 0000000..70ad839 --- /dev/null +++ b/DEBIAN/postinst @@ -0,0 +1,45 @@ +#!/bin/sh + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +# source debconf library +. /usr/share/debconf/confmodule + +# Source dbconfig-common functions +if [ -f /usr/share/dbconfig-common/dpkg/postinst.pgsql ]; then + . /usr/share/dbconfig-common/dpkg/postinst.pgsql +fi + +case "$1" in + + configure) + ldconfig + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + exit 0 + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; + +esac + +db_stop + +exit 0 +~ diff --git a/DEBIAN/postrm b/DEBIAN/postrm new file mode 100644 index 0000000..689222c --- /dev/null +++ b/DEBIAN/postrm @@ -0,0 +1,45 @@ +#!/bin/sh + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +# source debconf library +. /usr/share/debconf/confmodule + +# Source dbconfig-common functions +if [ -f /usr/share/dbconfig-common/dpkg/postinst.pgsql ]; then + . /usr/share/dbconfig-common/dpkg/postinst.pgsql +fi + +case "$1" in + + remove) + ldconfig + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + exit 0 + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; + +esac + +db_stop + +exit 0 +~ diff --git a/DEBIAN/prerm b/DEBIAN/prerm new file mode 100644 index 0000000..b90c5b0 --- /dev/null +++ b/DEBIAN/prerm @@ -0,0 +1,44 @@ +#!/bin/sh + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +# source debconf library +. /usr/share/debconf/confmodule + +# Source dbconfig-common functions +if [ -f /usr/share/dbconfig-common/dpkg/postinst.pgsql ]; then + . /usr/share/dbconfig-common/dpkg/postinst.pgsql +fi + +case "$1" in + + remove|upgrade|deconfigure) + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + exit 0 + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; + +esac + +db_stop + +exit 0 + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2d1979b --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2014, jgarff +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/SConscript b/SConscript new file mode 100644 index 0000000..509b5a1 --- /dev/null +++ b/SConscript @@ -0,0 +1,95 @@ +# +# SConscript +# +# Copyright (c) 2014 Jeremy Garff +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted +# provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other materials +# provided with the distribution. +# 3. Neither the name of the owner nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +Import(['clean_envs']) + +tools_env = clean_envs['userspace'].Clone() + + +# Build Library +lib_srcs = Split(''' + mailbox.c + ws2811.c + pwm.c + pcm.c + dma.c + rpihw.c +''') + +version_hdr = tools_env.Version('version') +ws2811_lib = tools_env.Library('libws2811', lib_srcs) +tools_env['LIBS'].append(ws2811_lib) + +# Shared library (if required) +ws2811_slib = tools_env.SharedLibrary('libws2811', lib_srcs) + +# Test Program +srcs = Split(''' + main.c +''') + +objs = [] +for src in srcs: + objs.append(tools_env.Object(src)) + +test = tools_env.Program('test', objs + tools_env['LIBS']) + +Default([test, ws2811_lib]) + +package_version = "1.1.0-1" +package_name = 'libws2811_%s' % package_version + +debian_files = [ + 'DEBIAN/control', + 'DEBIAN/postinst', + 'DEBIAN/prerm', + 'DEBIAN/postrm', +] + +package_files_desc = [ + [ '/usr/lib', ws2811_slib ], +] + +package_files = [] +for target in package_files_desc: + package_files.append(tools_env.Install(package_name + target[0], target[1])) + +for deb_file in debian_files: + package_files.append( + tools_env.Command('%s/%s' % (package_name, deb_file), deb_file, [ + Copy("$TARGET", "$SOURCE"), + Chmod("$TARGET", 0755) + ]) + ) + +package = tools_env.Command('%s.deb' % package_name, package_files, + 'cd %s; dpkg-deb --build %s' % (Dir('.').abspath, package_name)); + +Alias("deb", package) + diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..64c14c7 --- /dev/null +++ b/SConstruct @@ -0,0 +1,76 @@ +# +# SConstruct +# +# Copyright (c) 2014 Jeremy Garff +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted +# provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other materials +# provided with the distribution. +# 3. Neither the name of the owner nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +import os + + +opts = Variables() +opts.Add(BoolVariable('V', + 'Verbose build', + False)) + +opts.Add('TOOLCHAIN', + 'Set toolchain for cross compilation (e.g. arm-linux-gnueabihf)', + '') + +platforms = [ + [ + 'userspace', # Target Name + [ 'linux', 'version' ], # Scons tool (linux, avr, etc.) + { # Special environment setup + 'CPPPATH' : [ + ], + 'LINKFLAGS' : [ + "-lrt", + ], + }, + ], +] + +clean_envs = {} +for platform, tool, flags in platforms: + env = Environment( + options = opts, + tools = tool, + toolpath = ['.'], + ENV = {'PATH' : os.environ['PATH']}, + LIBS = [], + ) + env.MergeFlags(flags) + clean_envs[platform] = env + +Help(opts.GenerateHelpText(clean_envs)) + +if env['TOOLCHAIN'] != '': + env['CC'] = env['TOOLCHAIN'] + '-gcc' + env['AR'] = env['TOOLCHAIN'] + '-ar' + +Export(['clean_envs']) +SConscript('SConscript'); + diff --git a/clk.h b/clk.h new file mode 100644 index 0000000..7344a7a --- /dev/null +++ b/clk.h @@ -0,0 +1,65 @@ +/* + * clk.h + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __CLK_H__ +#define __CLK_H__ + + +typedef struct { + uint32_t ctl; +#define CM_CLK_CTL_PASSWD (0x5a << 24) +#define CM_CLK_CTL_MASH(val) ((val & 0x3) << 9) +#define CM_CLK_CTL_FLIP (1 << 8) +#define CM_CLK_CTL_BUSY (1 << 7) +#define CM_CLK_CTL_KILL (1 << 5) +#define CM_CLK_CTL_ENAB (1 << 4) +#define CM_CLK_CTL_SRC_GND (0 << 0) +#define CM_CLK_CTL_SRC_OSC (1 << 0) +#define CM_CLK_CTL_SRC_TSTDBG0 (2 << 0) +#define CM_CLK_CTL_SRC_TSTDBG1 (3 << 0) +#define CM_CLK_CTL_SRC_PLLA (4 << 0) +#define CM_CLK_CTL_SRC_PLLC (5 << 0) +#define CM_CLK_CTL_SRC_PLLD (6 << 0) +#define CM_CLK_CTL_SRC_HDMIAUX (7 << 0) + uint32_t div; +#define CM_CLK_DIV_PASSWD (0x5a << 24) +#define CM_CLK_DIV_DIVI(val) ((val & 0xfff) << 12) +#define CM_CLK_DIV_DIVF(val) ((val & 0xfff) << 0) +} __attribute__((packed, aligned(4))) cm_clk_t; + + +/* + * PWM and PCM clock offsets from https://www.scribd.com/doc/127599939/BCM2835-Audio-clocks + * + */ +#define CM_PCM_OFFSET (0x00101098) +#define CM_PWM_OFFSET (0x001010a0) + + +#endif /* __CLK_H__ */ diff --git a/dma.c b/dma.c new file mode 100644 index 0000000..1926bc3 --- /dev/null +++ b/dma.c @@ -0,0 +1,78 @@ +/* + * dma.c + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "dma.h" + + +// DMA address mapping by DMA number index +static const uint32_t dma_offset[] = +{ + DMA0_OFFSET, + DMA1_OFFSET, + DMA2_OFFSET, + DMA3_OFFSET, + DMA4_OFFSET, + DMA5_OFFSET, + DMA6_OFFSET, + DMA7_OFFSET, + DMA8_OFFSET, + DMA9_OFFSET, + DMA10_OFFSET, + DMA11_OFFSET, + DMA12_OFFSET, + DMA13_OFFSET, + DMA14_OFFSET, + DMA15_OFFSET, +}; + + +uint32_t dmanum_to_offset(int dmanum) +{ + int array_size = sizeof(dma_offset) / sizeof(dma_offset[0]); + + if (dmanum >= array_size) + { + return 0; + } + + return dma_offset[dmanum]; +} + + diff --git a/dma.h b/dma.h new file mode 100644 index 0000000..6783dfd --- /dev/null +++ b/dma.h @@ -0,0 +1,126 @@ +/* + * dma.h + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __DMA_H__ +#define __DMA_H__ + + +/* + * DMA Control Block in Main Memory + * + * Note: Must start at a 256 byte aligned address. + * Use corresponding register field definitions. + */ +typedef struct +{ + uint32_t ti; + uint32_t source_ad; + uint32_t dest_ad; + uint32_t txfr_len; + uint32_t stride; + uint32_t nextconbk; + uint32_t resvd_0x18[2]; +} __attribute__((packed, aligned(4))) dma_cb_t; + +/* + * DMA register set + */ +typedef struct +{ + uint32_t cs; +#define RPI_DMA_CS_RESET (1 << 31) +#define RPI_DMA_CS_ABORT (1 << 30) +#define RPI_DMA_CS_DISDEBUG (1 << 29) +#define RPI_DMA_CS_WAIT_OUTSTANDING_WRITES (1 << 28) +#define RPI_DMA_CS_PANIC_PRIORITY(val) ((val & 0xf) << 20) +#define RPI_DMA_CS_PRIORITY(val) ((val & 0xf) << 16) +#define RPI_DMA_CS_ERROR (1 << 8) +#define RPI_DMA_CS_WAITING_OUTSTANDING_WRITES (1 << 6) +#define RPI_DMA_CS_DREQ_STOPS_DMA (1 << 5) +#define RPI_DMA_CS_PAUSED (1 << 4) +#define RPI_DMA_CS_DREQ (1 << 3) +#define RPI_DMA_CS_INT (1 << 2) +#define RPI_DMA_CS_END (1 << 1) +#define RPI_DMA_CS_ACTIVE (1 << 0) + uint32_t conblk_ad; + uint32_t ti; +#define RPI_DMA_TI_NO_WIDE_BURSTS (1 << 26) +#define RPI_DMA_TI_WAITS(val) ((val & 0x1f) << 21) +#define RPI_DMA_TI_PERMAP(val) ((val & 0x1f) << 16) +#define RPI_DMA_TI_BURST_LENGTH(val) ((val & 0xf) << 12) +#define RPI_DMA_TI_SRC_IGNORE (1 << 11) +#define RPI_DMA_TI_SRC_DREQ (1 << 10) +#define RPI_DMA_TI_SRC_WIDTH (1 << 9) +#define RPI_DMA_TI_SRC_INC (1 << 8) +#define RPI_DMA_TI_DEST_IGNORE (1 << 7) +#define RPI_DMA_TI_DEST_DREQ (1 << 6) +#define RPI_DMA_TI_DEST_WIDTH (1 << 5) +#define RPI_DMA_TI_DEST_INC (1 << 4) +#define RPI_DMA_TI_WAIT_RESP (1 << 3) +#define RPI_DMA_TI_TDMODE (1 << 1) +#define RPI_DMA_TI_INTEN (1 << 0) + uint32_t source_ad; + uint32_t dest_ad; + uint32_t txfr_len; +#define RPI_DMA_TXFR_LEN_YLENGTH(val) ((val & 0xffff) << 16) +#define RPI_DMA_TXFR_LEN_XLENGTH(val) ((val & 0xffff) << 0) + uint32_t stride; +#define RPI_DMA_STRIDE_D_STRIDE(val) ((val & 0xffff) << 16) +#define RPI_DMA_STRIDE_S_STRIDE(val) ((val & 0xffff) << 0) + uint32_t nextconbk; + uint32_t debug; +} __attribute__((packed, aligned(4))) dma_t; + + +#define DMA0_OFFSET (0x00007000) +#define DMA1_OFFSET (0x00007100) +#define DMA2_OFFSET (0x00007200) +#define DMA3_OFFSET (0x00007300) +#define DMA4_OFFSET (0x00007400) +#define DMA5_OFFSET (0x00007500) +#define DMA6_OFFSET (0x00007600) +#define DMA7_OFFSET (0x00007700) +#define DMA8_OFFSET (0x00007800) +#define DMA9_OFFSET (0x00007900) +#define DMA10_OFFSET (0x00007a00) +#define DMA11_OFFSET (0x00007b00) +#define DMA12_OFFSET (0x00007c00) +#define DMA13_OFFSET (0x00007d00) +#define DMA14_OFFSET (0x00007e00) +#define DMA15_OFFSET (0x00e05000) + + +#define PAGE_SIZE (1 << 12) +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#define PAGE_OFFSET(page) (page & (PAGE_SIZE - 1)) + + +uint32_t dmanum_to_offset(int dmanum); + +#endif /* __DMA_H__ */ diff --git a/golang/README.md b/golang/README.md new file mode 100644 index 0000000..b1e1a77 --- /dev/null +++ b/golang/README.md @@ -0,0 +1,28 @@ +## Deprecated + +This Go code is being phased out and replaced with https://github.com/rpi-ws281x/rpi-ws281x-go + +For issues and bugs with (or contributions to) the Go library, please see: https://github.com/rpi-ws281x/rpi-ws281x-go/issues + +---- + +## Run a demo + +As this is just a Go wrapper for the library you must clone this into your `$GOPATH` as you would any other Go program. +Your path to the project should be: +``` + $GOPATH/src/github.com/jgraff/rpi_ws281x +``` + + +As listed in the `ws2811.go` file ensure to copy `ws2811.h`, `rpihw.h`, and `pwm.h` in a GCC include path (e.g. `/usr/local/include`) and +`libws2811.a` in a GCC library path (e.g. `/usr/local/lib`). + +To run the basic example run the following commands: +``` + cd golang/examples + go build basic.go + sudo ./basic +``` + +If everything worked you should see a basic color wipe for the first 16 LEDs on your strip. diff --git a/golang/examples/basic.go b/golang/examples/basic.go new file mode 100644 index 0000000..b40e574 --- /dev/null +++ b/golang/examples/basic.go @@ -0,0 +1,60 @@ +package main + +import ( + "time" + "os" + "fmt" + + "github.com/jgarff/rpi_ws281x/golang/ws2811" +) + +const ( + pin = 18 + count = 16 + brightness = 255 +) + +func main() { + defer ws2811.Fini() + err := ws2811.Init(pin, count, brightness) + if err != nil { + fmt.Println(err) + } else { + fmt.Println("Press Ctr-C to quit.") + fmt.Println("Creating blue color wipe") + err = colorWipe(uint32(0x000020)) + if err != nil { + fmt.Println("Error during wipe " + err.Error()) + os.Exit(-1) + } + + fmt.Println("Creating red color wipe") + err = colorWipe(uint32(0x002000)) + if err != nil { + fmt.Println("Error during wipe " + err.Error()) + os.Exit(-1) + } + + fmt.Println("Creating green color wipe") + err = colorWipe(uint32(0x200000)) + if err != nil { + fmt.Println("Error during wipe " + err.Error()) + os.Exit(-1) + } + } +} + +func colorWipe(color uint32) error { + for i := 0; i < count; i++ { + ws2811.SetLed(i, color) + err := ws2811.Render() + if err != nil { + ws2811.Clear() + return err + } + + time.Sleep(50 * time.Millisecond) + } + + return nil +} \ No newline at end of file diff --git a/golang/ws2811/ws2811.go b/golang/ws2811/ws2811.go new file mode 100644 index 0000000..972b84d --- /dev/null +++ b/golang/ws2811/ws2811.go @@ -0,0 +1,91 @@ +// Copyright (c) 2015, Jacques Supcik, HEIA-FR +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* +Interface to ws2811 chip (neopixel driver). Make sure that you have +ws2811.h, rpihw.h, and pwm.h in a GCC include path (e.g. /usr/local/include) and +libws2811.a in a GCC library path (e.g. /usr/local/lib). +See https://github.com/jgarff/rpi_ws281x for instructions +*/ + +package ws2811 + +/* +#cgo CFLAGS: -std=c99 +#cgo LDFLAGS: -lws2811 +#include "ws2811.go.h" +*/ +import "C" +import ( + "errors" + "fmt" + "unsafe" +) + +func Init(gpioPin int, ledCount int, brightness int) error { + C.ledstring.channel[0].gpionum = C.int(gpioPin) + C.ledstring.channel[0].count = C.int(ledCount) + C.ledstring.channel[0].brightness = C.uint8_t(brightness) + res := int(C.ws2811_init(&C.ledstring)) + if res == 0 { + return nil + } else { + return errors.New(fmt.Sprintf("Error ws2811.init.%d", res)) + } +} + +func Fini() { + C.ws2811_fini(&C.ledstring) +} + +func Render() error { + res := int(C.ws2811_render(&C.ledstring)) + if res == 0 { + return nil + } else { + return errors.New(fmt.Sprintf("Error ws2811.render.%d", res)) + } +} + +func Wait() error { + res := int(C.ws2811_wait(&C.ledstring)) + if res == 0 { + return nil + } else { + return errors.New(fmt.Sprintf("Error ws2811.wait.%d", res)) + } +} + +func SetLed(index int, value uint32) { + C.ws2811_set_led(&C.ledstring, C.int(index), C.uint32_t(value)) +} + +func Clear() { + C.ws2811_clear(&C.ledstring) +} + +func SetBitmap(a []uint32) { + C.ws2811_set_bitmap(&C.ledstring, unsafe.Pointer(&a[0]), C.int(len(a)*4)) +} diff --git a/golang/ws2811/ws2811.go.h b/golang/ws2811/ws2811.go.h new file mode 100644 index 0000000..5e078f7 --- /dev/null +++ b/golang/ws2811/ws2811.go.h @@ -0,0 +1,64 @@ +/*********************************************************************************** + * Copyright (c) 2015, Jacques Supcik, HEIA-FR + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***********************************************************************************/ + +#include +#include +#include + +ws2811_t ledstring = { + .freq = 800000, + .dmanum = 10, + .channel = { + [0] = { + .gpionum = 18, + .count = 256, + .invert = 0, + .brightness = 32, + }, + [1] = { + .gpionum = 0, + .count = 0, + .invert = 0, + .brightness = 0, + }, + }, +}; + +void ws2811_set_led(ws2811_t *ws2811, int index, uint32_t value) { + ws2811->channel[0].leds[index] = value; +} + +void ws2811_clear(ws2811_t *ws2811) { + for (int chan = 0; chan < RPI_PWM_CHANNELS; chan++) { + ws2811_channel_t *channel = &ws2811->channel[chan]; + memset(channel->leds, 0, sizeof(ws2811_led_t) * channel->count); + } +} + +void ws2811_set_bitmap(ws2811_t *ws2811, void* a, int len) { + memcpy(ws2811->channel[0].leds, a, len); +} diff --git a/gpio.h b/gpio.h new file mode 100644 index 0000000..01f3a17 --- /dev/null +++ b/gpio.h @@ -0,0 +1,108 @@ +/* + * gpio.h + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GPIO_H__ +#define __GPIO_H__ + + +typedef struct +{ + uint32_t fsel[6]; // GPIO Function Select + uint32_t resvd_0x18; + uint32_t set[2]; // GPIO Pin Output Set + uint32_t resvd_0x24; + uint32_t clr[2]; // GPIO Pin Output Clear + uint32_t resvd_0x30; + uint32_t lev[2]; // GPIO Pin Level + uint32_t resvd_0x3c; + uint32_t eds[2]; // GPIO Pin Event Detect Status + uint32_t resvd_0x48; + uint32_t ren[2]; // GPIO Pin Rising Edge Detect Enable + uint32_t resvd_0x54; + uint32_t fen[2]; // GPIO Pin Falling Edge Detect Enable + uint32_t resvd_0x60; + uint32_t hen[2]; // GPIO Pin High Detect Enable + uint32_t resvd_0x6c; + uint32_t len[2]; // GPIO Pin Low Detect Enable + uint32_t resvd_0x78; + uint32_t aren[2]; // GPIO Pin Async Rising Edge Detect + uint32_t resvd_0x84; + uint32_t afen[2]; // GPIO Pin Async Falling Edge Detect + uint32_t resvd_0x90; + uint32_t pud; // GPIO Pin Pull up/down Enable + uint32_t pudclk[2]; // GPIO Pin Pull up/down Enable Clock + uint32_t resvd_0xa0[4]; + uint32_t test; +} __attribute__((packed, aligned(4))) gpio_t; + + +#define GPIO_OFFSET (0x00200000) + + +static inline void gpio_function_set(volatile gpio_t *gpio, uint8_t pin, uint8_t function) +{ + int regnum = pin / 10; + int offset = (pin % 10) * 3; + uint8_t funcmap[] = { 4, 5, 6, 7, 3, 2 }; // See datasheet for mapping + + if (function > 5) + { + return; + } + + gpio->fsel[regnum] &= ~(0x7 << offset); + gpio->fsel[regnum] |= ((funcmap[function]) << offset); +} + +static inline void gpio_level_set(volatile gpio_t *gpio, uint8_t pin, uint8_t level) +{ + int regnum = pin >> 5; + int offset = (pin & 0x1f); + + if (level) + { + gpio->set[regnum] = (1 << offset); + } + else + { + gpio->clr[regnum] = (1 << offset); + } +} + +static inline void gpio_output_set(volatile gpio_t *gpio, uint8_t pin, uint8_t output) +{ + int regnum = pin / 10; + int offset = (pin % 10) * 3; + uint8_t function = output ? 1 : 0; // See datasheet for mapping + + gpio->fsel[regnum] &= ~(0x7 << offset); + gpio->fsel[regnum] |= ((function & 0x7) << offset); +} + +#endif /* __GPIO_H__ */ diff --git a/linux.py b/linux.py new file mode 100644 index 0000000..ee4c148 --- /dev/null +++ b/linux.py @@ -0,0 +1,84 @@ +# +# linux.py +# +# Copyright (c) 2014 Jeremy Garff +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted +# provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other materials +# provided with the distribution. +# 3. Neither the name of the owner nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +import SCons +import string +import array +import os + + +tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas'] + + +def linux_tools(env): + for tool in tools: + env.Tool(tool) + + if not env['V']: + env['ARCOMSTR'] = 'AR ${TARGET}' + env['ASCOMSTR'] = 'AS ${TARGET}' + env['CCCOMSTR'] = 'CC ${TARGET}' + env['CXXCOMSTR'] = 'C++ ${TARGET}' + env['LINKCOMSTR'] = 'LINK ${TARGET}' + env['RANLIBCOMSTR'] = 'RANLIB ${TARGET}' + +def linux_flags(env): + env.MergeFlags({ + 'CPPFLAGS' : ''' + -fPIC + -g + -O2 + -Wall + -Wextra + -Werror + '''.split(), + }), + env.MergeFlags({ + 'LINKFLAGS' : ''' + '''.split() + }) + + +def linux_builders(env): + env.Append(BUILDERS = { + 'Program' : SCons.Builder.Builder( + action = SCons.Action.Action('${LINK} -o ${TARGET} ${SOURCES} ${LINKFLAGS}', + '${LINKCOMSTR}'), + ), + }) + return 1 + + +# The following are required functions by SCons when incorporating through tools +def exists(env): + return 1 + +def generate(env, **kwargs): + [f(env) for f in (linux_tools, linux_flags, linux_builders)] + diff --git a/mailbox.c b/mailbox.c new file mode 100644 index 0000000..a9bf802 --- /dev/null +++ b/mailbox.c @@ -0,0 +1,292 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd. +Copyright (c) 2016, Jeremy Garff +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mailbox.h" + + +void *mapmem(uint32_t base, uint32_t size, const char *mem_dev) { + uint32_t pagemask = ~0UL ^ (getpagesize() - 1); + uint32_t offsetmask = getpagesize() - 1; + int mem_fd; + void *mem; + + mem_fd = open(mem_dev, O_RDWR | O_SYNC); + if (mem_fd < 0) { + perror("Can't open /dev/mem"); + return NULL; + } + + mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, base & pagemask); + if (mem == MAP_FAILED) { + perror("mmap error\n"); + return NULL; + } + + close(mem_fd); + + return (char *)mem + (base & offsetmask); +} + +void *unmapmem(void *addr, uint32_t size) { + uint32_t pagemask = ~0UL ^ (getpagesize() - 1); + uintptr_t baseaddr = (uintptr_t)addr & pagemask; + int s; + + s = munmap((void *)baseaddr, size); + if (s != 0) { + perror("munmap error\n"); + } + + return NULL; +} + +/* + * use ioctl to send mbox property message + */ + +static int mbox_property(int file_desc, void *buf) { + int fd = file_desc; + int ret_val = -1; + + if (fd < 0) { + fd = mbox_open(); + } + + if (fd >= 0) { + ret_val = ioctl(fd, IOCTL_MBOX_PROPERTY, buf); + + if (ret_val < 0) { + perror("ioctl_set_msg failed\n"); + } + } + + if (file_desc < 0) { + mbox_close(fd); + } + + return ret_val; +} + +uint32_t mem_alloc(int file_desc, uint32_t size, uint32_t align, uint32_t flags) { + int i=0; + uint32_t p[32]; + + p[i++] = 0; // size + p[i++] = 0x00000000; // process request + + p[i++] = 0x3000c; // (the tag id) + p[i++] = 12; // (size of the buffer) + p[i++] = 12; // (size of the data) + p[i++] = size; // (num bytes? or pages?) + p[i++] = align; // (alignment) + p[i++] = flags; // (MEM_FLAG_L1_NONALLOCATING) + + p[i++] = 0x00000000; // end tag + p[0] = i*sizeof *p; // actual size + + if (mbox_property(file_desc, p) < 0) + return 0; + else + return p[5]; +} + +uint32_t mem_free(int file_desc, uint32_t handle) { + int i=0; + uint32_t p[32]; + + p[i++] = 0; // size + p[i++] = 0x00000000; // process request + + p[i++] = 0x3000f; // (the tag id) + p[i++] = 4; // (size of the buffer) + p[i++] = 4; // (size of the data) + p[i++] = handle; + + p[i++] = 0x00000000; // end tag + p[0] = i*sizeof *p; // actual size + + mbox_property(file_desc, p); + + return p[5]; +} + +uint32_t mem_lock(int file_desc, uint32_t handle) { + int i=0; + uint32_t p[32]; + + p[i++] = 0; // size + p[i++] = 0x00000000; // process request + + p[i++] = 0x3000d; // (the tag id) + p[i++] = 4; // (size of the buffer) + p[i++] = 4; // (size of the data) + p[i++] = handle; + + p[i++] = 0x00000000; // end tag + p[0] = i*sizeof *p; // actual size + + if (mbox_property(file_desc, p) < 0) + return ~0; + else + return p[5]; +} + +uint32_t mem_unlock(int file_desc, uint32_t handle) { + int i=0; + uint32_t p[32]; + + p[i++] = 0; // size + p[i++] = 0x00000000; // process request + + p[i++] = 0x3000e; // (the tag id) + p[i++] = 4; // (size of the buffer) + p[i++] = 4; // (size of the data) + p[i++] = handle; + + p[i++] = 0x00000000; // end tag + p[0] = i * sizeof(*p); // actual size + + mbox_property(file_desc, p); + + return p[5]; +} + +uint32_t execute_code(int file_desc, uint32_t code, uint32_t r0, uint32_t r1, + uint32_t r2, uint32_t r3, uint32_t r4, uint32_t r5) { + int i=0; + uint32_t p[32]; + + p[i++] = 0; // size + p[i++] = 0x00000000; // process request + + p[i++] = 0x30010; // (the tag id) + p[i++] = 28; // (size of the buffer) + p[i++] = 28; // (size of the data) + p[i++] = code; + p[i++] = r0; + p[i++] = r1; + p[i++] = r2; + p[i++] = r3; + p[i++] = r4; + p[i++] = r5; + + p[i++] = 0x00000000; // end tag + p[0] = i * sizeof(*p); // actual size + + mbox_property(file_desc, p); + + return p[5]; +} + +uint32_t qpu_enable(int file_desc, uint32_t enable) { + int i=0; + uint32_t p[32]; + + p[i++] = 0; // size + p[i++] = 0x00000000; // process request + + p[i++] = 0x30012; // (the tag id) + p[i++] = 4; // (size of the buffer) + p[i++] = 4; // (size of the data) + p[i++] = enable; + + p[i++] = 0x00000000; // end tag + p[0] = i * sizeof(*p); // actual size + + mbox_property(file_desc, p); + + return p[5]; +} + +uint32_t execute_qpu(int file_desc, uint32_t num_qpus, uint32_t control, + uint32_t noflush, uint32_t timeout) { + int i = 0; + uint32_t p[32]; + + p[i++] = 0; // size + p[i++] = 0x00000000; // process request + p[i++] = 0x30011; // (the tag id) + p[i++] = 16; // (size of the buffer) + p[i++] = 16; // (size of the data) + p[i++] = num_qpus; + p[i++] = control; + p[i++] = noflush; + p[i++] = timeout; // ms + + p[i++] = 0x00000000; // end tag + p[0] = i * sizeof(*p); // actual size + + mbox_property(file_desc, p); + + return p[5]; +} + +int mbox_open(void) { + int file_desc; + char filename[64]; + + file_desc = open("/dev/vcio", 0); + if (file_desc >= 0) { + return file_desc; + } + + // open a char device file used for communicating with kernel mbox driver + sprintf(filename, "/tmp/mailbox-%d", getpid()); + unlink(filename); + if (mknod(filename, S_IFCHR|0600, makedev(100, 0)) < 0) { + perror("Failed to create mailbox device\n"); + return -1; + } + file_desc = open(filename, 0); + if (file_desc < 0) { + perror("Can't open device file\n"); + unlink(filename); + return -1; + } + unlink(filename); + + return file_desc; +} + +void mbox_close(int file_desc) { + close(file_desc); +} diff --git a/mailbox.h b/mailbox.h new file mode 100644 index 0000000..e5b5563 --- /dev/null +++ b/mailbox.h @@ -0,0 +1,49 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#define MAJOR_NUM 100 +#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) + +#define DEV_MEM "/dev/mem" +#define DEV_GPIOMEM "/dev/gpiomem" + +int mbox_open(void); +void mbox_close(int file_desc); + +unsigned get_version(int file_desc); +unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags); +unsigned mem_free(int file_desc, unsigned handle); +unsigned mem_lock(int file_desc, unsigned handle); +unsigned mem_unlock(int file_desc, unsigned handle); +void *mapmem(unsigned base, unsigned size, const char *mem_dev); +void *unmapmem(void *addr, unsigned size); + +unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5); +unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout); +unsigned qpu_enable(int file_desc, unsigned enable); diff --git a/main.c b/main.c new file mode 100644 index 0000000..f88414a --- /dev/null +++ b/main.c @@ -0,0 +1,419 @@ +/* + * newtest.c + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +static char VERSION[] = "XX.YY.ZZ"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "clk.h" +#include "gpio.h" +#include "dma.h" +#include "pwm.h" +#include "version.h" + +#include "ws2811.h" + + +#define ARRAY_SIZE(stuff) (sizeof(stuff) / sizeof(stuff[0])) + +// defaults for cmdline options +#define TARGET_FREQ WS2811_TARGET_FREQ +#define GPIO_PIN 18 +#define DMA 10 +//#define STRIP_TYPE WS2811_STRIP_RGB // WS2812/SK6812RGB integrated chip+leds +#define STRIP_TYPE WS2811_STRIP_GBR // WS2812/SK6812RGB integrated chip+leds +//#define STRIP_TYPE SK6812_STRIP_RGBW // SK6812RGBW (NOT SK6812RGB) + +#define WIDTH 8 +#define HEIGHT 8 +#define LED_COUNT (WIDTH * HEIGHT) + +int width = WIDTH; +int height = HEIGHT; +int led_count = LED_COUNT; + +int clear_on_exit = 0; + +ws2811_t ledstring = +{ + .freq = TARGET_FREQ, + .dmanum = DMA, + .channel = + { + [0] = + { + .gpionum = GPIO_PIN, + .count = LED_COUNT, + .invert = 0, + .brightness = 255, + .strip_type = STRIP_TYPE, + }, + [1] = + { + .gpionum = 0, + .count = 0, + .invert = 0, + .brightness = 0, + }, + }, +}; + +ws2811_led_t *matrix; + +static uint8_t running = 1; + +void matrix_render(void) +{ + int x, y; + + for (x = 0; x < width; x++) + { + for (y = 0; y < height; y++) + { + ledstring.channel[0].leds[(y * width) + x] = matrix[y * width + x]; + } + } +} + +void matrix_raise(void) +{ + int x, y; + + for (y = 0; y < (height - 1); y++) + { + for (x = 0; x < width; x++) + { + // This is for the 8x8 Pimoroni Unicorn-HAT where the LEDS in subsequent + // rows are arranged in opposite directions + matrix[y * width + x] = matrix[(y + 1)*width + width - x - 1]; + } + } +} + +void matrix_clear(void) +{ + int x, y; + + for (y = 0; y < (height ); y++) + { + for (x = 0; x < width; x++) + { + matrix[y * width + x] = 0; + } + } +} + +int dotspos[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; +ws2811_led_t dotcolors[] = +{ + 0x00200000, // red + 0x00201000, // orange + 0x00202000, // yellow + 0x00002000, // green + 0x00002020, // lightblue + 0x00000020, // blue + 0x00100010, // purple + 0x00200010, // pink +}; + +ws2811_led_t dotcolors_rgbw[] = +{ + 0x00200000, // red + 0x10200000, // red + W + 0x00002000, // green + 0x10002000, // green + W + 0x00000020, // blue + 0x10000020, // blue + W + 0x00101010, // white + 0x10101010, // white + W + +}; + +void matrix_bottom(void) +{ + int i; + + for (i = 0; i < (int)(ARRAY_SIZE(dotspos)); i++) + { + dotspos[i]++; + if (dotspos[i] > (width - 1)) + { + dotspos[i] = 0; + } + + if (ledstring.channel[0].strip_type == SK6812_STRIP_RGBW) { + matrix[dotspos[i] + (height - 1) * width] = dotcolors_rgbw[i]; + } else { + matrix[dotspos[i] + (height - 1) * width] = dotcolors[i]; + } + } +} + +static void ctrl_c_handler(int signum) +{ + (void)(signum); + running = 0; +} + +static void setup_handlers(void) +{ + struct sigaction sa = + { + .sa_handler = ctrl_c_handler, + }; + + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); +} + + +void parseargs(int argc, char **argv, ws2811_t *ws2811) +{ + int index; + int c; + + static struct option longopts[] = + { + {"help", no_argument, 0, 'h'}, + {"dma", required_argument, 0, 'd'}, + {"gpio", required_argument, 0, 'g'}, + {"invert", no_argument, 0, 'i'}, + {"clear", no_argument, 0, 'c'}, + {"strip", required_argument, 0, 's'}, + {"height", required_argument, 0, 'y'}, + {"width", required_argument, 0, 'x'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + + while (1) + { + + index = 0; + c = getopt_long(argc, argv, "cd:g:his:vx:y:", longopts, &index); + + if (c == -1) + break; + + switch (c) + { + case 0: + /* handle flag options (array's 3rd field non-0) */ + break; + + case 'h': + fprintf(stderr, "%s version %s\n", argv[0], VERSION); + fprintf(stderr, "Usage: %s \n" + "-h (--help) - this information\n" + "-s (--strip) - strip type - rgb, grb, gbr, rgbw\n" + "-x (--width) - matrix width (default 8)\n" + "-y (--height) - matrix height (default 8)\n" + "-d (--dma) - dma channel to use (default 10)\n" + "-g (--gpio) - GPIO to use\n" + " If omitted, default is 18 (PWM0)\n" + "-i (--invert) - invert pin output (pulse LOW)\n" + "-c (--clear) - clear matrix on exit.\n" + "-v (--version) - version information\n" + , argv[0]); + exit(-1); + + case 'D': + break; + + case 'g': + if (optarg) { + int gpio = atoi(optarg); +/* + PWM0, which can be set to use GPIOs 12, 18, 40, and 52. + Only 12 (pin 32) and 18 (pin 12) are available on the B+/2B/3B + PWM1 which can be set to use GPIOs 13, 19, 41, 45 and 53. + Only 13 is available on the B+/2B/PiZero/3B, on pin 33 + PCM_DOUT, which can be set to use GPIOs 21 and 31. + Only 21 is available on the B+/2B/PiZero/3B, on pin 40. + SPI0-MOSI is available on GPIOs 10 and 38. + Only GPIO 10 is available on all models. + + The library checks if the specified gpio is available + on the specific model (from model B rev 1 till 3B) + +*/ + ws2811->channel[0].gpionum = gpio; + } + break; + + case 'i': + ws2811->channel[0].invert=1; + break; + + case 'c': + clear_on_exit=1; + break; + + case 'd': + if (optarg) { + int dma = atoi(optarg); + if (dma < 14) { + ws2811->dmanum = dma; + } else { + printf ("invalid dma %d\n", dma); + exit (-1); + } + } + break; + + case 'y': + if (optarg) { + height = atoi(optarg); + if (height > 0) { + ws2811->channel[0].count = height * width; + } else { + printf ("invalid height %d\n", height); + exit (-1); + } + } + break; + + case 'x': + if (optarg) { + width = atoi(optarg); + if (width > 0) { + ws2811->channel[0].count = height * width; + } else { + printf ("invalid width %d\n", width); + exit (-1); + } + } + break; + + case 's': + if (optarg) { + if (!strncasecmp("rgb", optarg, 4)) { + ws2811->channel[0].strip_type = WS2811_STRIP_RGB; + } + else if (!strncasecmp("rbg", optarg, 4)) { + ws2811->channel[0].strip_type = WS2811_STRIP_RBG; + } + else if (!strncasecmp("grb", optarg, 4)) { + ws2811->channel[0].strip_type = WS2811_STRIP_GRB; + } + else if (!strncasecmp("gbr", optarg, 4)) { + ws2811->channel[0].strip_type = WS2811_STRIP_GBR; + } + else if (!strncasecmp("brg", optarg, 4)) { + ws2811->channel[0].strip_type = WS2811_STRIP_BRG; + } + else if (!strncasecmp("bgr", optarg, 4)) { + ws2811->channel[0].strip_type = WS2811_STRIP_BGR; + } + else if (!strncasecmp("rgbw", optarg, 4)) { + ws2811->channel[0].strip_type = SK6812_STRIP_RGBW; + } + else if (!strncasecmp("grbw", optarg, 4)) { + ws2811->channel[0].strip_type = SK6812_STRIP_GRBW; + } + else { + printf ("invalid strip %s\n", optarg); + exit (-1); + } + } + break; + + case 'v': + fprintf(stderr, "%s version %s\n", argv[0], VERSION); + exit(-1); + + case '?': + /* getopt_long already reported error? */ + exit(-1); + + default: + exit(-1); + } + } +} + + +int main(int argc, char *argv[]) +{ + ws2811_return_t ret; + + sprintf(VERSION, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); + + parseargs(argc, argv, &ledstring); + + matrix = malloc(sizeof(ws2811_led_t) * width * height); + + setup_handlers(); + + if ((ret = ws2811_init(&ledstring)) != WS2811_SUCCESS) + { + fprintf(stderr, "ws2811_init failed: %s\n", ws2811_get_return_t_str(ret)); + return ret; + } + + while (running) + { + matrix_raise(); + matrix_bottom(); + matrix_render(); + + if ((ret = ws2811_render(&ledstring)) != WS2811_SUCCESS) + { + fprintf(stderr, "ws2811_render failed: %s\n", ws2811_get_return_t_str(ret)); + break; + } + + // 15 frames /sec + usleep(1000000 / 15); + } + + if (clear_on_exit) { + matrix_clear(); + matrix_render(); + ws2811_render(&ledstring); + } + + ws2811_fini(&ledstring); + + printf ("\n"); + return ret; +} diff --git a/pcm.c b/pcm.c new file mode 100644 index 0000000..51dac35 --- /dev/null +++ b/pcm.c @@ -0,0 +1,127 @@ +/* + * pcm.c + * + * Copyright (c) 2014 Jeremy Garff + * PCM version Copyright (c) 2016 Ton van Overbeek + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include "pcm.h" + + +// Mapping of Pin to alternate function for PCM_CLK +const pcm_pin_table_t pcm_pin_clk[] = +{ + { + .pinnum = 18, + .altnum = 0, + }, + { + .pinnum = 28, + .altnum = 2, + }, +}; + +// Mapping of Pin to alternate function for PCM_FS +const pcm_pin_table_t pcm_pin_fs[] = +{ + { + .pinnum = 19, + .altnum = 0, + }, + { + .pinnum = 29, + .altnum = 2, + }, +}; + +// Mapping of Pin to alternate function for PCM_DIN +const pcm_pin_table_t pcm_pin_din[] = +{ + { + .pinnum = 20, + .altnum = 0, + }, + { + .pinnum = 30, + .altnum = 2, + }, +}; + +// Mapping of Pin to alternate function for PCM_DOUT +const pcm_pin_table_t pcm_pin_dout[] = +{ + { + .pinnum = 21, + .altnum = 0, + }, + { + .pinnum = 31, + .altnum = 2, + }, +}; + +const pcm_pin_tables_t pcm_pin_tables[NUM_PCMFUNS] = +{ + { + .pins = pcm_pin_clk, + .count = sizeof(pcm_pin_clk) / sizeof(pcm_pin_clk[0]), + }, + { + .pins = pcm_pin_fs, + .count = sizeof(pcm_pin_fs) / sizeof(pcm_pin_fs[0]), + }, + { + .pins = pcm_pin_din, + .count = sizeof(pcm_pin_din) / sizeof(pcm_pin_din[0]), + }, + { + .pins = pcm_pin_dout, + .count = sizeof(pcm_pin_dout) / sizeof(pcm_pin_dout[0]), + }, +}; + + +int pcm_pin_alt(int pcmfun, int pinnum) +{ + if (pcmfun < 0 || pcmfun > 3) { + return -1; + } + const pcm_pin_tables_t *pintable = &pcm_pin_tables[pcmfun]; + int i; + + for (i = 0; i < pintable->count; i++) + { + if (pintable->pins[i].pinnum == pinnum) + { + return pintable->pins[i].altnum; + } + } + + return -1; +} + diff --git a/pcm.h b/pcm.h new file mode 100644 index 0000000..353c6e5 --- /dev/null +++ b/pcm.h @@ -0,0 +1,158 @@ +/* + * pcm.h + * + * Copyright (c) 2014 Jeremy Garff + * PCM version Copyright (c) Ton van Overbeek + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __PCM_H__ +#define __PCM_H__ + + +/* + * + * Pin mapping of alternate pin configuration for PCM + * + * GPIO ALT PCM_CLK ALT PCM-FS ALT PCM_DIN ALT PCM_DOUT + * + * 18 0 + * 19 0 + * 20 0 + * 21 0 + * 28 2 + * 29 2 + * 30 2 + * 31 2 + * + */ + + +typedef struct +{ + uint32_t cs; +#define RPI_PCM_CS_STBY (1 << 25) +#define RPI_PCM_CS_SYNC (1 << 24) +#define RPI_PCM_CS_RXSEX (1 << 23) +#define RPI_PCM_CS_RXF (1 << 22) +#define RPI_PCM_CS_TXE (1 << 21) +#define RPI_PCM_CS_RXD (1 << 20) +#define RPI_PCM_CS_TXD (1 << 19) +#define RPI_PCM_CS_RXR (1 << 18) +#define RPI_PCM_CS_TXW (1 << 17) +#define RPI_PCM_CS_RXERR (1 << 16) +#define RPI_PCM_CS_TXERR (1 << 15) +#define RPI_PCM_CS_RXSYNC (1 << 14) +#define RPI_PCM_CS_TXSYNC (1 << 13) +#define RPI_PCM_CS_DMAEN (1 << 9) +#define RPI_PCM_CS_RXTHR(val) ((val & 0x03) << 7) +#define RPI_PCM_CS_TXTHR(val) ((val & 0x03) << 5) +#define RPI_PCM_CS_RXCLR (1 << 4) +#define RPI_PCM_CS_TXCLR (1 << 3) +#define RPI_PCM_CS_TXON (1 << 2) +#define RPI_PCM_CS_RXON (1 << 1) +#define RPI_PCM_CS_EN (1 << 0) + uint32_t fifo; + uint32_t mode; +#define RPI_PCM_MODE_CLK_DIS (1 << 28) +#define RPI_PCM_MODE_PDMN (1 << 27) +#define RPI_PCM_MODE_PDME (1 << 26) +#define RPI_PCM_MODE_FRXP (1 << 25) +#define RPI_PCM_MODE_FTXP (1 << 24) +#define RPI_PCM_MODE_CLKM (1 << 23) +#define RPI_PCM_MODE_CLKI (1 << 22) +#define RPI_PCM_MODE_FSM (1 << 21) +#define RPI_PCM_MODE_FSI (1 << 20) +#define RPI_PCM_MODE_FLEN(val) ((val & 0x3ff) << 10) +#define RPI_PCM_MODE_FSLEN(val) ((val & 0x3ff) << 0) + uint32_t rxc; +#define RPI_PCM_RXC_CH1WEX (1 << 31) +#define RPI_PCM_RXC_CH1EN (1 << 30) +#define RPI_PCM_RXC_CH1POS(val) ((val & 0x3ff) << 20) +#define RPI_PCM_RXC_CH1WID(val) ((val & 0x0f) << 16) +#define RPI_PCM_RXC_CH2WEX (1 << 15) +#define RPI_PCM_RXC_CH2EN (1 << 14) +#define RPI_PCM_RXC_CH2POS(val) ((val & 0x3ff) << 4) +#define RPI_PCM_RXC_CH2WID(val) ((val & 0x0f) << 0) + uint32_t txc; +#define RPI_PCM_TXC_CH1WEX (1 << 31) +#define RPI_PCM_TXC_CH1EN (1 << 30) +#define RPI_PCM_TXC_CH1POS(val) ((val & 0x3ff) << 20) +#define RPI_PCM_TXC_CH1WID(val) ((val & 0x0f) << 16) +#define RPI_PCM_TXC_CH2WEX (1 << 15) +#define RPI_PCM_TXC_CH2EN (1 << 14) +#define RPI_PCM_TXC_CH2POS(val) ((val & 0x3ff) << 4) +#define RPI_PCM_TXC_CH2WID(val) ((val & 0x0f) << 0) + uint32_t dreq; +#define RPI_PCM_DREQ_TX_PANIC(val) ((val & 0x7f) << 24) +#define RPI_PCM_DREQ_RX_PANIC(val) ((val & 0x7f) << 16) +#define RPI_PCM_DREQ_TX(val) ((val & 0x7f) << 8) +#define RPI_PCM_DREQ_RX(val) ((val & 0x7f) << 0) + uint32_t inten; +#define RPI_PCM_INTEN_RXERR (1 << 3) +#define RPI_PCM_INTEN_TXERR (1 << 2) +#define RPI_PCM_INTEN_RXR (1 << 1) +#define RPI_PCM_INTEN_TXW (1 << 0) + uint32_t intstc; +#define RPI_PCM_INTSTC_RXERR (1 << 3) +#define RPI_PCM_INTSTC_TXERR (1 << 2) +#define RPI_PCM_INTSTC_RXR (1 << 1) +#define RPI_PCM_INTSTC_TXW (1 << 0) + uint32_t gray; +#define RPI_PCM_GRAY_RXFIFOLEVEL(val) ((val & 0x3f) << 16) +#define RPI_PCM_GRAY_FLUSHED(val) ((val & 0x3f) << 10 +#define RPI_PCM_GRAY_RXLEVEL(val) ((val & 0x3f) << 4) +#define RPI_PCM_GRAY_FLUSH (1 << 2) +#define RPI_PCM_GRAY_CLR (1 << 1) +#define RPI_PCM_GRAY_EN (1 << 0) +} __attribute__((packed, aligned(4))) pcm_t; + + +#define PCM_OFFSET (0x00203000) +#define PCM_PERIPH_PHYS (0x7e203000) + +#define NUM_PCMFUNS 4 +#define PCMFUN_CLK 0 +#define PCMFUN_FS 1 +#define PCMFUN_DIN 2 +#define PCMFUN_DOUT 3 + +typedef struct +{ + int pinnum; + int altnum; +} pcm_pin_table_t; + +typedef struct +{ + const int count; + const pcm_pin_table_t *pins; +} pcm_pin_tables_t; + + +int pcm_pin_alt(int pcmfun, int pinnum); + + +#endif /* __PCM_H__ */ diff --git a/pwm.c b/pwm.c new file mode 100644 index 0000000..aaea7f4 --- /dev/null +++ b/pwm.c @@ -0,0 +1,104 @@ +/* + * pwm.c + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include + +#include "ws2811.h" + +#include "pwm.h" + + +// Mapping of Pin to alternate function for PWM channel 0 +const pwm_pin_table_t pwm_pin_chan0[] = +{ + { + .pinnum = 12, + .altnum = 0, + }, + { + .pinnum = 18, + .altnum = 5, + }, + { + .pinnum = 40, + .altnum = 0, + }, +}; + +// Mapping of Pin to alternate function for PWM channel 1 +const pwm_pin_table_t pwm_pin_chan1[] = +{ + { + .pinnum = 13, + .altnum = 0, + }, + { + .pinnum = 19, + .altnum = 5, + }, + { + .pinnum = 41, + .altnum = 0, + }, + { + .pinnum = 45, + .altnum = 0, + }, +}; + +const pwm_pin_tables_t pwm_pin_tables[RPI_PWM_CHANNELS] = +{ + { + .pins = pwm_pin_chan0, + .count = sizeof(pwm_pin_chan0) / sizeof(pwm_pin_chan0[0]), + }, + { + .pins = pwm_pin_chan1, + .count = sizeof(pwm_pin_chan1) / sizeof(pwm_pin_chan1[0]), + }, +}; + + +int pwm_pin_alt(int chan, int pinnum) +{ + const pwm_pin_tables_t *pintable = &pwm_pin_tables[chan]; + int i; + + for (i = 0; i < pintable->count; i++) + { + if (pintable->pins[i].pinnum == pinnum) + { + return pintable->pins[i].altnum; + } + } + + return -1; +} + diff --git a/pwm.h b/pwm.h new file mode 100644 index 0000000..663aa1e --- /dev/null +++ b/pwm.h @@ -0,0 +1,122 @@ +/* + * pwm.h + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __PWM_H__ +#define __PWM_H__ + + +/* + * + * Pin mappint of alternate pin configuration for PWM + * + * GPIO ALT PWM0 ALT PWM1 + * + * 12 0 + * 13 0 + * 18 5 + * 19 5 + * 40 0 + * 41 0 + * 45 0 + * 52 1 + * 53 1 + * + */ + + +#define RPI_PWM_CHANNELS 2 + + +typedef struct +{ + uint32_t ctl; +#define RPI_PWM_CTL_MSEN2 (1 << 15) +#define RPI_PWM_CTL_USEF2 (1 << 13) +#define RPI_PWM_CTL_POLA2 (1 << 12) +#define RPI_PWM_CTL_SBIT2 (1 << 11) +#define RPI_PWM_CTL_RPTL2 (1 << 10) +#define RPI_PWM_CTL_MODE2 (1 << 9) +#define RPI_PWM_CTL_PWEN2 (1 << 8) +#define RPI_PWM_CTL_MSEN1 (1 << 7) +#define RPI_PWM_CTL_CLRF1 (1 << 6) +#define RPI_PWM_CTL_USEF1 (1 << 5) +#define RPI_PWM_CTL_POLA1 (1 << 4) +#define RPI_PWM_CTL_SBIT1 (1 << 3) +#define RPI_PWM_CTL_RPTL1 (1 << 2) +#define RPI_PWM_CTL_MODE1 (1 << 1) +#define RPI_PWM_CTL_PWEN1 (1 << 0) + uint32_t sta; +#define RPI_PWM_STA_STA4 (1 << 12) +#define RPI_PWM_STA_STA3 (1 << 11) +#define RPI_PWM_STA_STA2 (1 << 10) +#define RPI_PWM_STA_STA1 (1 << 9) +#define RPI_PWM_STA_BERR (1 << 8) +#define RPI_PWM_STA_GAP04 (1 << 7) +#define RPI_PWM_STA_GAP03 (1 << 6) +#define RPI_PWM_STA_GAP02 (1 << 5) +#define RPI_PWM_STA_GAP01 (1 << 4) +#define RPI_PWM_STA_RERR1 (1 << 3) +#define RPI_PWM_STA_WERR1 (1 << 2) +#define RPI_PWM_STA_EMPT1 (1 << 1) +#define RPI_PWM_STA_FULL1 (1 << 0) + uint32_t dmac; +#define RPI_PWM_DMAC_ENAB (1 << 31) +#define RPI_PWM_DMAC_PANIC(val) ((val & 0xff) << 8) +#define RPI_PWM_DMAC_DREQ(val) ((val & 0xff) << 0) + uint32_t resvd_0x0c; + uint32_t rng1; + uint32_t dat1; + uint32_t fif1; + uint32_t resvd_0x1c; + uint32_t rng2; + uint32_t dat2; +} __attribute__((packed, aligned(4))) pwm_t; + + +#define PWM_OFFSET (0x0020c000) +#define PWM_PERIPH_PHYS (0x7e20c000) + + +typedef struct +{ + int pinnum; + int altnum; +} pwm_pin_table_t; + +typedef struct +{ + const int count; + const pwm_pin_table_t *pins; +} pwm_pin_tables_t; + + +int pwm_pin_alt(int chan, int pinnum); + + +#endif /* __PWM_H__ */ diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..4e4afa4 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,9 @@ +*.pyc +*.so +build/ +*.egg-info/ +rpi_ws281x.py +rpi_ws281x_wrap.c +_rpi_ws281x.py +*.egg +*.zip diff --git a/python/README.md b/python/README.md new file mode 100644 index 0000000..ad0b96a --- /dev/null +++ b/python/README.md @@ -0,0 +1,49 @@ +## Deprecated + +This Python code is being phased out and replaced with https://github.com/rpi-ws281x/rpi-ws281x-python + +If you're just looking to install the Python library, you can: `sudo pip install rpi_ws281x` or `sudo pip3 install rpi_ws281x` depending on your Python version of choice or find releases here: https://github.com/rpi-ws281x/rpi-ws281x-python/releases + +For issues and bugs with (or contributions to) the Python library, please see: https://github.com/rpi-ws281x/rpi-ws281x-python/issues + +---- + +## Build + +As this is just a python wrapper for the library you must first follow +the build instructions in the parent directory. +When complete, you can build this python wrapper: +``` + sudo apt-get install python-dev swig + python ./setup.py build +``` + + +If you are rebuilding after fetching some updated commits, you might need to +remove the build directory first +``` + rm -rf ./build +``` + +## Install + +If you want to install the library (in a virtualenv or in the system), so that you can `import neopixel` from anywhere, you need to run: + +``` + python ./setup.py install +``` + +Depending on where you are installing, root privileges may be needed (prepend `sudo` to the install command). + + +## Run a demo + +``` + sudo PYTHONPATH=".:build/lib.linux-armv7l-2.7" python examples/strandtest.py +``` + +If you installed the library, there is no need to specify `PYTHONPATH`: + +``` + sudo python examples/strandtest.py +``` diff --git a/python/aniamte_blink.py b/python/aniamte_blink.py new file mode 100644 index 0000000..430e3ff --- /dev/null +++ b/python/aniamte_blink.py @@ -0,0 +1,75 @@ +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. +import time + +from neopixel import * + + +# LED strip configuration: +LED_COUNT = 494 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_CHANNEL = 0 +LED_STRIP = ws.SK6812_STRIP_RGBW +#LED_STRIP = ws.SK6812W_STRIP + +class Led: + leds = [] + def __init__(self, id, x, y): + self.x = x + self.y = y + Led.leds.append(self) + + + +def colorFromIntensity(intensity): + #intensity should be 0-255 + if intensity>20: + return Color(0,int(intensity),int(intensity/2),int(intensity/8)+20) + else: + return Color(0,0,0,20) + +def intensityFromDistance(distance): + if distance<0: + return 0 + intensity = 255-(distance/3) + if intensity < 0: + return 0 + else: + return intensity + +# Main program logic follows: +if __name__ == '__main__': + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP) + # Intialize the library (must be called once before other functions). + strip.begin() + + print ('Press Ctrl-C to quit.') + + with open("leds.txt", "r") as infile: + for line in infile: + entry = [int(x) for x in line.split(',')] + Led(entry[0], entry[1], entry[2]) + + # all LEDs now ready + edge = -500 + t = 0 + while True: + t+=1 + for i, led in enumerate(Led.leds): + if t % 2 ==0: + strip.setPixelColor(i, Color(0b10101010, + 0b10101010, + 0b10101010, + 0b10101010)) + else: + strip.setPixelColor(i, Color(0,0,0,0)) + strip.show() + time.sleep(1) diff --git a/python/aniamte_christmastree.py b/python/aniamte_christmastree.py new file mode 100644 index 0000000..6d7ca96 --- /dev/null +++ b/python/aniamte_christmastree.py @@ -0,0 +1,221 @@ +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. +import time +import random + +from neopixel import * + + +# LED strip configuration: +LED_COUNT = 494 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_CHANNEL = 0 +LED_STRIP = ws.SK6812_STRIP_RGBW +#LED_STRIP = ws.SK6812W_STRIP + +class Led: + leds = [] + def __init__(self, id, x, y): + self.x = x + self.y = y + Led.leds.append(self) + +class Star: + stars = [] + def __init__(self): + self.starstate = 0 + self.starBrightness = 0 + self.starCountdown = 0 + self.starLED = 50 + Star.stars.append(self) + + def update(self, strip): + if self.starstate == 0: + self.starBrightness += 3 + if self.starBrightness > 120: + self.starstate = 1 + if self.starstate == 1: + self.starBrightness -= self.starBrightness/100.0 + if self.starBrightness < 5: + self.starstate = 2 + self.starBrightness = 0 + self.starCountdown = random.randint(100, 500) + if self.starstate == 2: + self.starCountdown -= 1 + if self.starCountdown == 0: + self.starLED = 0 + otherStars = [s.starLED for s in Star.stars] + goodPlace = False + while not goodPlace: + self.starLED = random.randint(0,493) + goodPlace = True + if self.starLED in range(236, 349): + goodPlace = False + if Led.leds[self.starLED].y < random.random(): + goodPlace = False + for otherStar in otherStars: + if abs(self.starLED-otherStar)<4: + goodPlace = False + + self.starstate = 0 + + brightness = int(self.starBrightness + (self.starBrightness * random.random())/20.0) + + strip.setPixelColor(self.starLED, Color(0,0,int(brightness/2),brightness)) + + + +def wheel(pos): + """Generate rainbow colors across 0-255 positions.""" + if pos < 85: + return Color(pos * 3, 255 - pos * 3, 0) + elif pos < 170: + pos -= 85 + return Color(255 - pos * 3, 0, pos * 3) + else: + pos -= 170 + return Color(0, pos * 3, 255 - pos * 3) + + +def colorFromIntensity(intensity): + #intensity should be 0-255 + if intensity>20: + return Color(0,intensity,intensity/2,intensity/8+20) + else: + return Color(0,0,0,20) + +def intensityFromDistance(distance): + if distance<0: + return 0 + intensity = 255-(distance/3) + if intensity < 0: + return 0 + else: + return intensity + +def turnOnChain(chain): + for light in chain: + strip.setPixelColor(light, Color(0,255,0,0)) + strip.setPixelColor(light+1, Color(0,255,0,0)) + strip.setPixelColor(light+2, Color(0,0,0,0)) + strip.setPixelColor(light-1, Color(0,0,0,0)) + + +# Main program logic follows: +if __name__ == '__main__': + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP) + # Intialize the library (must be called once before other functions). + strip.begin() + + print ('Press Ctrl-C to quit.') + + with open("leds.txt", "r") as infile: + for line in infile: + entry = [int(x) for x in line.split(',')] + Led(entry[0], entry[1], entry[2]) + + limits = [ min([l.x for l in Led.leds]), max([l.x for l in Led.leds]), min([l.y for l in Led.leds]), max([l.y for l in Led.leds])] + for l in Led.leds: + l.y = ( float(l.y - float(limits[3])) / (limits[2]-limits[3]) ) + l.x = ( float(l.x - float(limits[0])) / (limits[3]-limits[2]) ) + + x_max = max([l.x for l in Led.leds]) + print ("Maximum X: {}".format(x_max)) + + + # all LEDs now ready + edge = -500 + counter = 1 + hider = 0 + animation = 0 + + stars = [] + for i in range(0, 10): + Star() + + while True: + for i, led in enumerate(Led.leds): + # intensity = intensityFromDistance(edge-(led.x - led.y/3)) + # strip.setPixelColor(i, colorFromIntensity(intensity)) + # blueness = int(i/2)%25 + if(i > 236 and i<349): + # Gör bokstaven A grön + strip.setPixelColor(i, Color(70,13,0,5)) + else: + blueness = int((1.0-led.y)**3 * 60) if led.y>0.10 else 0 + if blueness < 5: + blueness = 5 + #redness = int((led.x / x_max)**2 * 255) + redness = int((1.0-led.y)**8 * 4) + strip.setPixelColor(i, Color(0,redness,blueness,0)) + + + # strip.setPixelColor(440, Color(random.randint(68,72),random.randint(68,72),random.randint(68,72),random.randint(4,6))) + + + # lights = [257, 334, 283, 249, 290, 346, 299, 311, 242] + # for light in lights: + # strip.setPixelColor(light, Color(0,255,0,0)) + # strip.setPixelColor(light+1, Color(0,255,0,0)) + # strip.setPixelColor(light+2, Color(0,0,0,0)) + # strip.setPixelColor(light-1, Color(0,0,0,0)) + chain0 = [260, 274] + chain1 = [254, 334, 283] + chain2 = [247, 290, 346] + chain3 = [299, 314, 241] + + strip.setPixelColor(264, Color(0,0,0,50)) + strip.setPixelColor(265, Color(160,255,0, 20)) + strip.setPixelColor(266, Color(160,255,0, 20)) + strip.setPixelColor(267, Color(160,255,0, 20)) + strip.setPixelColor(268, Color(0,0,0,50)) + + animation+=1 + animation %= 400 + if animation>= 0 and animation <50: + turnOnChain(chain0) + turnOnChain(chain2) + if animation >=50 and animation <100: + turnOnChain(chain1) + turnOnChain(chain2) + if animation >=100 and animation <150: + turnOnChain(chain1) + turnOnChain(chain3) + if animation >=150 and animation <200: + turnOnChain(chain2) + turnOnChain(chain3) + if animation >=200 and animation <250: + turnOnChain(chain0) + turnOnChain(chain2) + if animation >=250 and animation <300: + turnOnChain(chain0) + turnOnChain(chain3) + if animation >=300 and animation <350: + turnOnChain(chain1) + turnOnChain(chain3) + if animation >=350 and animation <400: + turnOnChain(chain1) + turnOnChain(chain0) + + + for star in Star.stars: + star.update(strip) + + + #time.sleep(0.05) + counter +=1 + strip.show() + + + # edge+=10 + # if edge>5000: + # edge = -500 + # time.sleep(0.01) diff --git a/python/aniamte_starrynight.py b/python/aniamte_starrynight.py new file mode 100644 index 0000000..69f3257 --- /dev/null +++ b/python/aniamte_starrynight.py @@ -0,0 +1,160 @@ +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. +import random +from neopixel import ws, Adafruit_NeoPixel, Color + + +# LED strip configuration: +LED_COUNT = 494 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!). +LED_FREQ_HZ = 1200000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # Invert when using NPN transistor level shift +LED_CHANNEL = 0 +LED_STRIP = ws.SK6812_STRIP_RGBW + + +class Led: + leds = [] + + def __init__(self, id, x, y): + self.x = x + self.y = y + Led.leds.append(self) + + +class Star: + stars = [] + + def __init__(self): + self.starstate = 0 + self.starBrightness = 0 + self.starCountdown = 0 + self.starLED = 50 + Star.stars.append(self) + + def update(self, strip): + if self.starstate == 0: + self.starBrightness += 3 + if self.starBrightness > 120: + self.starstate = 1 + if self.starstate == 1: + self.starBrightness -= self.starBrightness/100.0 + if self.starBrightness < 5: + self.starstate = 2 + self.starBrightness = 0 + self.starCountdown = random.randint(100, 500) + if self.starstate == 2: + self.starCountdown -= 1 + if self.starCountdown == 0: + self.starLED = 0 + otherStars = [s.starLED for s in Star.stars] + goodPlace = False + while not goodPlace: + self.starLED = random.randint(0, 493) + goodPlace = True + if Led.leds[self.starLED].y < random.random(): + goodPlace = False + for otherStar in otherStars: + if abs(self.starLED-otherStar) < 4: + goodPlace = False + + self.starstate = 0 + + brightness = int(self.starBrightness + (self.starBrightness * random.random())/20.0) + + strip.setPixelColor(self.starLED, Color(0, 0, int(brightness/2), brightness)) + + +def wheel(pos): + """Generate rainbow colors across 0-255 positions.""" + if pos < 85: + return Color(pos * 3, 255 - pos * 3, 0) + elif pos < 170: + pos -= 85 + return Color(255 - pos * 3, 0, pos * 3) + else: + pos -= 170 + return Color(0, pos * 3, 255 - pos * 3) + + +def colorFromIntensity(intensity): + # Intensity should be 0-255 + if intensity > 20: + return Color(0, intensity, intensity/2, intensity/8+20) + else: + return Color(0, 0, 0, 20) + + +def intensityFromDistance(distance): + if distance < 0: + return 0 + intensity = 255-(distance/3) + if intensity < 0: + return 0 + else: + return intensity + + +def turnOnChain(chain): + for light in chain: + strip.setPixelColor(light, Color(0, 255, 0, 0)) + strip.setPixelColor(light+1, Color(0, 255, 0, 0)) + strip.setPixelColor(light+2, Color(0, 0, 0, 0)) + strip.setPixelColor(light-1, Color(0, 0, 0, 0)) + + +# Main program logic follows: +if __name__ == '__main__': + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, + LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, + LED_STRIP) + # Intialize the library (must be called once before other functions). + strip.begin() + + print('Press Ctrl-C to quit.') + + with open("leds.txt", "r") as infile: + for line in infile: + entry = [int(x) for x in line.split(',')] + Led(entry[0], entry[1], entry[2]) + + limits = [min([l.x for l in Led.leds]), + max([l.x for l in Led.leds]), + min([l.y for l in Led.leds]), + max([l.y for l in Led.leds])] + for l in Led.leds: + l.y = (float(l.y - float(limits[3])) / (limits[2]-limits[3])) + l.x = (float(l.x - float(limits[0])) / (limits[3]-limits[2])) + + x_max = max([l.x for l in Led.leds]) + print("Maximum X: {}".format(x_max)) + + # all LEDs now ready + edge = -500 + counter = 1 + hider = 0 + animation = 0 + + stars = [] + for i in range(0, 10): + Star() + + while True: + for i, led in enumerate(Led.leds): + blueness = int((1.0-led.y)**3 * 60) if led.y > 0.10 else 0 + if blueness < 5: + blueness = 5 + redness = int((1.0-led.y)**8 * 4) + strip.setPixelColor(i, Color(0, redness, blueness, 0)) + + for star in Star.stars: + star.update(strip) + + counter += 1 + strip.show() diff --git a/python/aniamte_starrynight.py~ b/python/aniamte_starrynight.py~ new file mode 100644 index 0000000..63e29b1 --- /dev/null +++ b/python/aniamte_starrynight.py~ @@ -0,0 +1,160 @@ +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. +import random +from neopixel import ws, Adafruit_NeoPixel, Color + + +# LED strip configuration: +LED_COUNT = 494 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # Invert when using NPN transistor level shift +LED_CHANNEL = 0 +LED_STRIP = ws.SK6812_STRIP_RGBW + + +class Led: + leds = [] + + def __init__(self, id, x, y): + self.x = x + self.y = y + Led.leds.append(self) + + +class Star: + stars = [] + + def __init__(self): + self.starstate = 0 + self.starBrightness = 0 + self.starCountdown = 0 + self.starLED = 50 + Star.stars.append(self) + + def update(self, strip): + if self.starstate == 0: + self.starBrightness += 3 + if self.starBrightness > 120: + self.starstate = 1 + if self.starstate == 1: + self.starBrightness -= self.starBrightness/100.0 + if self.starBrightness < 5: + self.starstate = 2 + self.starBrightness = 0 + self.starCountdown = random.randint(100, 500) + if self.starstate == 2: + self.starCountdown -= 1 + if self.starCountdown == 0: + self.starLED = 0 + otherStars = [s.starLED for s in Star.stars] + goodPlace = False + while not goodPlace: + self.starLED = random.randint(0, 493) + goodPlace = True + if Led.leds[self.starLED].y < random.random(): + goodPlace = False + for otherStar in otherStars: + if abs(self.starLED-otherStar) < 4: + goodPlace = False + + self.starstate = 0 + + brightness = int(self.starBrightness + (self.starBrightness * random.random())/20.0) + + strip.setPixelColor(self.starLED, Color(0, 0, int(brightness/2), brightness)) + + +def wheel(pos): + """Generate rainbow colors across 0-255 positions.""" + if pos < 85: + return Color(pos * 3, 255 - pos * 3, 0) + elif pos < 170: + pos -= 85 + return Color(255 - pos * 3, 0, pos * 3) + else: + pos -= 170 + return Color(0, pos * 3, 255 - pos * 3) + + +def colorFromIntensity(intensity): + # Intensity should be 0-255 + if intensity > 20: + return Color(0, intensity, intensity/2, intensity/8+20) + else: + return Color(0, 0, 0, 20) + + +def intensityFromDistance(distance): + if distance < 0: + return 0 + intensity = 255-(distance/3) + if intensity < 0: + return 0 + else: + return intensity + + +def turnOnChain(chain): + for light in chain: + strip.setPixelColor(light, Color(0, 255, 0, 0)) + strip.setPixelColor(light+1, Color(0, 255, 0, 0)) + strip.setPixelColor(light+2, Color(0, 0, 0, 0)) + strip.setPixelColor(light-1, Color(0, 0, 0, 0)) + + +# Main program logic follows: +if __name__ == '__main__': + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, + LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, + LED_STRIP) + # Intialize the library (must be called once before other functions). + strip.begin() + + print('Press Ctrl-C to quit.') + + with open("leds.txt", "r") as infile: + for line in infile: + entry = [int(x) for x in line.split(',')] + Led(entry[0], entry[1], entry[2]) + + limits = [min([l.x for l in Led.leds]), + max([l.x for l in Led.leds]), + min([l.y for l in Led.leds]), + max([l.y for l in Led.leds])] + for l in Led.leds: + l.y = (float(l.y - float(limits[3])) / (limits[2]-limits[3])) + l.x = (float(l.x - float(limits[0])) / (limits[3]-limits[2])) + + x_max = max([l.x for l in Led.leds]) + print("Maximum X: {}".format(x_max)) + + # all LEDs now ready + edge = -500 + counter = 1 + hider = 0 + animation = 0 + + stars = [] + for i in range(0, 10): + Star() + + while True: + for i, led in enumerate(Led.leds): + blueness = int((1.0-led.y)**3 * 60) if led.y > 0.10 else 0 + if blueness < 5: + blueness = 5 + redness = int((1.0-led.y)**8 * 4) + strip.setPixelColor(i, Color(0, redness, blueness, 0)) + + for star in Star.stars: + star.update(strip) + + counter += 1 + strip.show() diff --git a/python/animate b/python/animate new file mode 100755 index 0000000..1c0e6f8 --- /dev/null +++ b/python/animate @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +sudo PYTHONPATH=".:build/lib.linux-armv7l-2.7" python3 animate.py diff --git a/python/animate.py b/python/animate.py new file mode 100644 index 0000000..edfc274 --- /dev/null +++ b/python/animate.py @@ -0,0 +1,82 @@ +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. +import time + +from neopixel import * + + +# LED strip configuration: +LED_COUNT = 494 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_CHANNEL = 0 +LED_STRIP = ws.SK6812_STRIP_RGBW +#LED_STRIP = ws.SK6812W_STRIP + +class Led: + leds = [] + def __init__(self, id, x, y): + self.x = x + self.y = y + Led.leds.append(self) + +def wheel(pos): + """Generate rainbow colors across 0-255 positions.""" + if pos < 85: + return Color(pos * 3, 255 - pos * 3, 0) + elif pos < 170: + pos -= 85 + return Color(255 - pos * 3, 0, pos * 3) + else: + pos -= 170 + return Color(0, pos * 3, 255 - pos * 3) + + +def colorFromIntensity(intensity): + #intensity should be 0-255 + if intensity>20: + return Color(int(intensity/2),int(intensity/2),int(intensity/2),max(int(intensity),20)) + else: + return Color(0,0,0,20) + +def intensityFromDistance(distance): + if distance<0: + return 0 + intensity = 255-(distance/3) + if intensity < 0: + return 0 + else: + return intensity + +# Main program logic follows: +if __name__ == '__main__': + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP) + # Intialize the library (must be called once before other functions). + strip.begin() + + print ('Press Ctrl-C to quit.') + + with open("leds.txt", "r") as infile: + for line in infile: + entry = [int(x) for x in line.split(',')] + Led(entry[0], entry[1], entry[2]) + + # all LEDs now ready + edge = -500 + while True: + # counter = 0 + for i, led in enumerate(Led.leds): + intensity = intensityFromDistance(edge-(led.x - led.y/3)) + strip.setPixelColor(i, colorFromIntensity(intensity)) + strip.show() + edge+=10 + if edge>5000: + edge = -500 + # time.sleep(0.01) diff --git a/python/animinate-xmas.py b/python/animinate-xmas.py new file mode 120000 index 0000000..cefe73f --- /dev/null +++ b/python/animinate-xmas.py @@ -0,0 +1 @@ +aniamte_starrynight.py \ No newline at end of file diff --git a/python/examples/SK6812_lowlevel.py b/python/examples/SK6812_lowlevel.py new file mode 100644 index 0000000..67ff899 --- /dev/null +++ b/python/examples/SK6812_lowlevel.py @@ -0,0 +1,104 @@ +# Example of low-level Python wrapper for rpi_ws281x library. +# Author: Tony DiCola (tony@tonydicola.com), Jeremy Garff (jer@jers.net) +# +# This is an example of how to use the SWIG-generated _rpi_ws281x module. +# You probably don't want to use this unless you are building your own library, +# because the SWIG generated module is clunky and verbose. Instead look at the +# high level Python port of Adafruit's NeoPixel Arduino library in strandtest.py. +# +# This code will animate a number of WS281x LEDs displaying rainbow colors. +import time + +import _rpi_ws281x as ws + +# LED configuration. +LED_CHANNEL = 0 +LED_COUNT = 16 # How many LEDs to light. +LED_FREQ_HZ = 800000 # Frequency of the LED signal. Should be 800khz or 400khz. +LED_DMA_NUM = 10 # DMA channel to use, can be 0-14. +LED_GPIO = 18 # GPIO connected to the LED signal line. Must support PWM! +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = 0 # Set to 1 to invert the LED signal, good if using NPN + # transistor as a 3.3V->5V level converter. Keep at 0 + # for a normal/non-inverted signal. +#LED_STRIP = ws.WS2811_STRIP_RGB +#LED_STRIP = ws.WS2811_STRIP_GBR +#LED_STRIP = ws.SK6812_STRIP_RGBW +LED_STRIP = ws.SK6812W_STRIP + + +# Define colors which will be used by the example. Each color is an unsigned +# 32-bit value where the lower 24 bits define the red, green, blue data (each +# being 8 bits long). +DOT_COLORS = [ 0x200000, # red + 0x201000, # orange + 0x202000, # yellow + 0x002000, # green + 0x002020, # lightblue + 0x000020, # blue + 0x100010, # purple + 0x200010 ] # pink + + +# Create a ws2811_t structure from the LED configuration. +# Note that this structure will be created on the heap so you need to be careful +# that you delete its memory by calling delete_ws2811_t when it's not needed. +leds = ws.new_ws2811_t() + +# Initialize all channels to off +for channum in range(2): + channel = ws.ws2811_channel_get(leds, channum) + ws.ws2811_channel_t_count_set(channel, 0) + ws.ws2811_channel_t_gpionum_set(channel, 0) + ws.ws2811_channel_t_invert_set(channel, 0) + ws.ws2811_channel_t_brightness_set(channel, 0) + +channel = ws.ws2811_channel_get(leds, LED_CHANNEL) + +ws.ws2811_channel_t_count_set(channel, LED_COUNT) +ws.ws2811_channel_t_gpionum_set(channel, LED_GPIO) +ws.ws2811_channel_t_invert_set(channel, LED_INVERT) +ws.ws2811_channel_t_brightness_set(channel, LED_BRIGHTNESS) +ws.ws2811_channel_t_strip_type_set(channel, LED_STRIP) + +ws.ws2811_t_freq_set(leds, LED_FREQ_HZ) +ws.ws2811_t_dmanum_set(leds, LED_DMA_NUM) + +# Initialize library with LED configuration. +resp = ws.ws2811_init(leds) +if resp != ws.WS2811_SUCCESS: + message = ws.ws2811_get_return_t_str(resp) + raise RuntimeError('ws2811_init failed with code {0} ({1})'.format(resp, message)) + +# Wrap following code in a try/finally to ensure cleanup functions are called +# after library is initialized. +try: + offset = 0 + while True: + # Update each LED color in the buffer. + for i in range(LED_COUNT): + # Pick a color based on LED position and an offset for animation. + color = DOT_COLORS[(i + offset) % len(DOT_COLORS)] + + # Set the LED color buffer value. + ws.ws2811_led_set(channel, i, color) + + # Send the LED color data to the hardware. + resp = ws.ws2811_render(leds) + if resp != ws.WS2811_SUCCESS: + message = ws.ws2811_get_return_t_str(resp) + raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, message)) + + # Delay for a small period of time. + time.sleep(0.25) + + # Increase offset to animate colors moving. Will eventually overflow, which + # is fine. + offset += 1 + +finally: + # Ensure ws2811_fini is called before the program quits. + ws.ws2811_fini(leds) + # Example of calling delete function to clean up structure memory. Isn't + # strictly necessary at the end of the program execution here, but is good practice. + ws.delete_ws2811_t(leds) diff --git a/python/examples/SK6812_strandtest.py b/python/examples/SK6812_strandtest.py new file mode 100644 index 0000000..696a3ce --- /dev/null +++ b/python/examples/SK6812_strandtest.py @@ -0,0 +1,107 @@ +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. +import time + +from neopixel import * + + +# LED strip configuration: +LED_COUNT = 494 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_CHANNEL = 0 +LED_STRIP = ws.SK6812_STRIP_RGBW +#LED_STRIP = ws.SK6812W_STRIP + + +# Define functions which animate LEDs in various ways. +def colorWipe(strip, color, wait_ms=15): + """Wipe color across display a pixel at a time.""" + for i in range(strip.numPixels()): + strip.setPixelColor(i, color) + strip.show() + time.sleep(wait_ms/1000.0) + +def theaterChase(strip, color, wait_ms=15, iterations=10): + """Movie theater light style chaser animation.""" + for j in range(iterations): + for q in range(3): + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, color) + strip.show() + time.sleep(wait_ms/1000.0) + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, 0) + +def wheel(pos): + """Generate rainbow colors across 0-255 positions.""" + if pos < 85: + return Color(pos * 3, 255 - pos * 3, 0) + elif pos < 170: + pos -= 85 + return Color(255 - pos * 3, 0, pos * 3) + else: + pos -= 170 + return Color(0, pos * 3, 255 - pos * 3) + +def rainbow(strip, wait_ms=15, iterations=1): + """Draw rainbow that fades across all pixels at once.""" + for j in range(256*iterations): + for i in range(strip.numPixels()): + strip.setPixelColor(i, wheel((i+j) & 255)) + strip.show() + time.sleep(wait_ms/1000.0) + +def rainbowCycle(strip, wait_ms=15, iterations=5): + """Draw rainbow that uniformly distributes itself across all pixels.""" + for j in range(256*iterations): + for i in range(strip.numPixels()): + strip.setPixelColor(i, wheel(((i * 256 // strip.numPixels()) + j) & 255)) + strip.show() + time.sleep(wait_ms/1000.0) + +def theaterChaseRainbow(strip, wait_ms=15): + """Rainbow movie theater light style chaser animation.""" + for j in range(256): + for q in range(3): + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, wheel((i+j) % 255)) + strip.show() + time.sleep(wait_ms/1000.0) + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, 0) + + +# Main program logic follows: +if __name__ == '__main__': + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP) + # Intialize the library (must be called once before other functions). + strip.begin() + + print ('Press Ctrl-C to quit.') + while True: + # Color wipe animations. + colorWipe(strip, Color(255, 0, 0)) # Red wipe + colorWipe(strip, Color(0, 255, 0)) # Blue wipe + colorWipe(strip, Color(0, 0, 255)) # Green wipe + colorWipe(strip, Color(0, 0, 0, 255)) # White wipe + colorWipe(strip, Color(255, 255, 255)) # Composite White wipe + colorWipe(strip, Color(255, 255, 255, 255)) # Composite White + White LED wipe + # Theater chase animations. + theaterChase(strip, Color(127, 0, 0)) # Red theater chase + theaterChase(strip, Color(0, 127, 0)) # Green theater chase + theaterChase(strip, Color(0, 0, 127)) # Blue theater chase + theaterChase(strip, Color(0, 0, 0, 127)) # White theater chase + theaterChase(strip, Color(127, 127, 127, 0)) # Composite White theater chase + theaterChase(strip, Color(127, 127, 127, 127)) # Composite White + White theater chase + # Rainbow animations. + rainbow(strip) + rainbowCycle(strip) + theaterChaseRainbow(strip) diff --git a/python/examples/SK6812_white_test.py b/python/examples/SK6812_white_test.py new file mode 100644 index 0000000..ba0815a --- /dev/null +++ b/python/examples/SK6812_white_test.py @@ -0,0 +1,52 @@ +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. +import time + +from neopixel import * + +# LED strip configuration: +LED_COUNT = 30 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_CHANNEL = 0 +#LED_STRIP = ws.SK6812_STRIP_RGBW +LED_STRIP = ws.SK6812W_STRIP + + +# Define functions which animate LEDs in various ways. +def colorWipe(strip, color, wait_ms=50): + """Wipe color across display a pixel at a time.""" + for i in range(strip.numPixels()): + strip.setPixelColor(i, color) + strip.show() + time.sleep(wait_ms/1000.0) + + +# Main program logic follows: +if __name__ == '__main__': + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP) + # Intialize the library (must be called once before other functions). + strip.begin() + + print ('Press Ctrl-C to quit.') + while True: + # Color wipe animations. + colorWipe(strip, Color(255, 0, 0), 0) # Red wipe + time.sleep(2) + colorWipe(strip, Color(0, 255, 0), 0) # Blue wipe + time.sleep(2) + colorWipe(strip, Color(0, 0, 255), 0) # Green wipe + time.sleep(2) + colorWipe(strip, Color(0, 0, 0, 255), 0) # White wipe + time.sleep(2) + colorWipe(strip, Color(255, 255, 255), 0) # Composite White wipe + time.sleep(2) + colorWipe(strip, Color(255, 255, 255, 255), 0) # Composite White + White LED wipe + time.sleep(2) diff --git a/python/examples/lightpaint.py b/python/examples/lightpaint.py new file mode 100644 index 0000000..02a1413 --- /dev/null +++ b/python/examples/lightpaint.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# NeoPixel library light-painting example +# Author: Gary Servin (garyservin@gmail.com) +# +# Lightpainting example for displaying images one column at a time and capturing +# it by taking a long exposure photograph. +# Based on https://github.com/scottjgibson/PixelPi + +import time +from neopixel import * +import argparse + +# Button +import RPi.GPIO as GPIO + +# Lightpainting +from PIL import Image + +# LED strip configuration: +LED_COUNT = 128 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (18 uses PWM!). +#LED_PIN = 10 # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_CHANNEL = 0 # set to '1' for GPIOs 13, 19, 41, 45 or 53 + +BUTTON_CHANNEL = 19 # GPIO pin connected to the start button + +def UnColor(color): + return ((color >> 24) & 0xFF , (color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF) + +def lightpaint(filename, frame_rate=100, column_rate=1, reverse_x=False, reverse_y=False, loop=False): + img = Image.open(filename).convert("RGB") + + # Check that the height of the image is greater than or equal the number of LEDs on the strip + if(img.size[1] < LED_COUNT): + raise Exception("Image height is smaller than led strip size. Required height = {}".format(LED_COUNT)) + elif(img.size[1] > LED_COUNT): + print "Resizing image" + new_width = LED_COUNT * img.size[0] / img.size[1] + img = img.resize((new_width, LED_COUNT), Image.ANTIALIAS) + + input_image = img.load() + image_width = img.size[0] + + column = [0 for x in range(image_width)] + for x in range(image_width): + column[x] = [None] * (LED_COUNT) + + for x in range(image_width): + for y in range(LED_COUNT): + value = input_image[x, y] + column[x][y] = Color(value[1], value[0], value[2]) + + while True: + # Wait for button to be pressed before displaying image + if not loop: + print("Waiting for button to be pressed") + GPIO.wait_for_edge(BUTTON_CHANNEL, GPIO.FALLING) + time.sleep(0.5) + + x_range = range(image_width) + if reverse_x: + x_range.reverse() + + y_range = range(LED_COUNT) + if reverse_y: + y_range.reverse() + + for x in x_range: + led_pos = 0 + for y in y_range: + strip.setPixelColor(led_pos, column[x][y]) + led_pos += 1 + strip.show() + time.sleep(column_rate / 1000.0) + + # Wait for `frame_rate` ms before drawing a new frame + time.sleep(frame_rate / 1000.0) + +# Define functions which animate LEDs in various ways. +def colorWipe(strip, color, wait_ms=50): + """Wipe color across display a pixel at a time.""" + for i in range(strip.numPixels()): + strip.setPixelColor(i, color) + strip.show() + time.sleep(wait_ms/1000.0) + +# Main program logic follows: +if __name__ == '__main__': + # Process arguments + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--clear', action='store_true', help='clear the display on exit') + parser.add_argument('-f', '--file', action='store', help='Filename to display') + parser.add_argument('-r', '--frame_rate', action='store', default=100, help='Rate between each frame. Defines how fast a new frame is displayed') + parser.add_argument('-l', '--column_rate', action='store', default=1, help='Rate between each column. Defines how fast or slow you need to move the stick') + parser.add_argument('-b', '--brightness', action='store', default=10, help='Brightness of the LED. Set to 0 for darkest and 255 for brightest') + parser.add_argument('-x', '--reverse_x', action='store_true', help='Reverse the image in the X direction') + parser.add_argument('-y', '--reverse_y', action='store_true', help='Reverse the image in the Y direction') + parser.add_argument('--loop', action='store_true', help='Play frames in a loop') + args = parser.parse_args() + + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, args.brightness, LED_CHANNEL) + # Intialize the library (must be called once before other functions). + strip.begin() + + # Button + GPIO.setmode(GPIO.BOARD) + GPIO.setup(BUTTON_CHANNEL, GPIO.IN, pull_up_down=GPIO.PUD_UP) + + print ('Press Ctrl-C to quit.') + if not args.clear: + print('Use "-c" argument to clear LEDs on exit') + + try: + while True: + print ('Lightpaint.') + lightpaint(args.file, float(args.frame_rate), float(args.column_rate), args.reverse_x, args.reverse_y, args.loop) + + except KeyboardInterrupt: + if args.clear: + colorWipe(strip, Color(0,0,0), 10) + GPIO.cleanup() diff --git a/python/examples/lowlevel.py b/python/examples/lowlevel.py new file mode 100644 index 0000000..39d6908 --- /dev/null +++ b/python/examples/lowlevel.py @@ -0,0 +1,98 @@ +# Example of low-level Python wrapper for rpi_ws281x library. +# Author: Tony DiCola (tony@tonydicola.com), Jeremy Garff (jer@jers.net) +# +# This is an example of how to use the SWIG-generated _rpi_ws281x module. +# You probably don't want to use this unless you are building your own library, +# because the SWIG generated module is clunky and verbose. Instead look at the +# high level Python port of Adafruit's NeoPixel Arduino library in strandtest.py. +# +# This code will animate a number of WS281x LEDs displaying rainbow colors. +import time + +import _rpi_ws281x as ws + +# LED configuration. +LED_CHANNEL = 0 +LED_COUNT = 16 # How many LEDs to light. +LED_FREQ_HZ = 800000 # Frequency of the LED signal. Should be 800khz or 400khz. +LED_DMA_NUM = 10 # DMA channel to use, can be 0-14. +LED_GPIO = 18 # GPIO connected to the LED signal line. Must support PWM! +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = 0 # Set to 1 to invert the LED signal, good if using NPN + # transistor as a 3.3V->5V level converter. Keep at 0 + # for a normal/non-inverted signal. + +# Define colors which will be used by the example. Each color is an unsigned +# 32-bit value where the lower 24 bits define the red, green, blue data (each +# being 8 bits long). +DOT_COLORS = [ 0x200000, # red + 0x201000, # orange + 0x202000, # yellow + 0x002000, # green + 0x002020, # lightblue + 0x000020, # blue + 0x100010, # purple + 0x200010 ] # pink + + +# Create a ws2811_t structure from the LED configuration. +# Note that this structure will be created on the heap so you need to be careful +# that you delete its memory by calling delete_ws2811_t when it's not needed. +leds = ws.new_ws2811_t() + +# Initialize all channels to off +for channum in range(2): + channel = ws.ws2811_channel_get(leds, channum) + ws.ws2811_channel_t_count_set(channel, 0) + ws.ws2811_channel_t_gpionum_set(channel, 0) + ws.ws2811_channel_t_invert_set(channel, 0) + ws.ws2811_channel_t_brightness_set(channel, 0) + +channel = ws.ws2811_channel_get(leds, LED_CHANNEL) + +ws.ws2811_channel_t_count_set(channel, LED_COUNT) +ws.ws2811_channel_t_gpionum_set(channel, LED_GPIO) +ws.ws2811_channel_t_invert_set(channel, LED_INVERT) +ws.ws2811_channel_t_brightness_set(channel, LED_BRIGHTNESS) + +ws.ws2811_t_freq_set(leds, LED_FREQ_HZ) +ws.ws2811_t_dmanum_set(leds, LED_DMA_NUM) + +# Initialize library with LED configuration. +resp = ws.ws2811_init(leds) +if resp != ws.WS2811_SUCCESS: + message = ws.ws2811_get_return_t_str(resp) + raise RuntimeError('ws2811_init failed with code {0} ({1})'.format(resp, message)) + +# Wrap following code in a try/finally to ensure cleanup functions are called +# after library is initialized. +try: + offset = 0 + while True: + # Update each LED color in the buffer. + for i in range(LED_COUNT): + # Pick a color based on LED position and an offset for animation. + color = DOT_COLORS[(i + offset) % len(DOT_COLORS)] + + # Set the LED color buffer value. + ws.ws2811_led_set(channel, i, color) + + # Send the LED color data to the hardware. + resp = ws.ws2811_render(leds) + if resp != ws.WS2811_SUCCESS: + message = ws.ws2811_get_return_t_str(resp) + raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, message)) + + # Delay for a small period of time. + time.sleep(0.25) + + # Increase offset to animate colors moving. Will eventually overflow, which + # is fine. + offset += 1 + +finally: + # Ensure ws2811_fini is called before the program quits. + ws.ws2811_fini(leds) + # Example of calling delete function to clean up structure memory. Isn't + # strictly necessary at the end of the program execution here, but is good practice. + ws.delete_ws2811_t(leds) diff --git a/python/examples/multistrandtest.py b/python/examples/multistrandtest.py new file mode 100644 index 0000000..afde32c --- /dev/null +++ b/python/examples/multistrandtest.py @@ -0,0 +1,78 @@ +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. +import time + +from neopixel import * + +# LED strip configuration: +LED_1_COUNT = 30 # Number of LED pixels. +LED_1_PIN = 18 # GPIO pin connected to the pixels (must support PWM! GPIO 13 and 18 on RPi 3). +LED_1_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_1_DMA = 10 # DMA channel to use for generating signal (Between 1 and 14) +LED_1_BRIGHTNESS = 128 # Set to 0 for darkest and 255 for brightest +LED_1_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_1_CHANNEL = 0 # 0 or 1 +LED_1_STRIP = ws.SK6812_STRIP_GRBW + +LED_2_COUNT = 15 # Number of LED pixels. +LED_2_PIN = 13 # GPIO pin connected to the pixels (must support PWM! GPIO 13 or 18 on RPi 3). +LED_2_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_2_DMA = 11 # DMA channel to use for generating signal (Between 1 and 14) +LED_2_BRIGHTNESS = 128 # Set to 0 for darkest and 255 for brightest +LED_2_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_2_CHANNEL = 1 # 0 or 1 +LED_2_STRIP = ws.WS2811_STRIP_GRB + +def multiColorWipe(color1, color2, wait_ms=5): + global strip1 + global strip2 + """Wipe color across multiple LED strips a pixel at a time.""" + for i in range(strip1.numPixels()): + if i % 2: + # even number + strip1.setPixelColor(i, color1) + strip2.setPixelColor(i / 2, color2) + strip1.show() + time.sleep(wait_ms/1000.0) + strip2.show() + time.sleep(wait_ms/1000.0) + else: + # odd number + strip1.setPixelColor(i, color1) + strip1.show() + time.sleep(wait_ms/1000.0) + time.sleep(1) + +def blackout(strip): + for i in range(max(strip1.numPixels(), strip1.numPixels())): + strip.setPixelColor(i, Color(0,0,0)) + strip.show() + +# Main program logic follows: +if __name__ == '__main__': + # Create NeoPixel objects with appropriate configuration for each strip. + strip1 = Adafruit_NeoPixel(LED_1_COUNT, LED_1_PIN, LED_1_FREQ_HZ, LED_1_DMA, LED_1_INVERT, LED_1_BRIGHTNESS, LED_1_CHANNEL, LED_1_STRIP) + strip2 = Adafruit_NeoPixel(LED_2_COUNT, LED_2_PIN, LED_2_FREQ_HZ, LED_2_DMA, LED_2_INVERT, LED_2_BRIGHTNESS, LED_2_CHANNEL, LED_2_STRIP) + + # Intialize the library (must be called once before other functions). + strip1.begin() + strip2.begin() + + print ('Press Ctrl-C to quit.') + + # Black out any LEDs that may be still on for the last run + blackout(strip1) + blackout(strip2) + + while True: + + # Multi Color wipe animations. + multiColorWipe(Color(255, 0, 0), Color(255, 0, 0)) # Red wipe + multiColorWipe(Color(0, 255, 0), Color(0, 255, 0)) # Blue wipe + multiColorWipe(Color(0, 0, 255), Color(0, 0, 255)) # Green wipe + multiColorWipe(Color(255, 255, 255), Color(255, 255, 255)) # Composite White wipe + multiColorWipe(Color(0, 0, 0, 255), Color(0, 0, 0)) # White wipe + multiColorWipe(Color(255, 255, 255, 255), Color(0, 0, 0)) # Composite White + White LED wipe diff --git a/python/examples/neopixelclock.py b/python/examples/neopixelclock.py new file mode 100644 index 0000000..e950157 --- /dev/null +++ b/python/examples/neopixelclock.py @@ -0,0 +1,72 @@ +# Based on NeoPixel library and strandtest example by Tony DiCola (tony@tonydicola.com) +# To be used with a 12x1 NeoPixel LED stripe. +# Place the LEDs in a circle an watch the time go by ... +# red = hours +# blue = minutes 1-5 +# green = seconds +# (To run the program permanently and with autostart use systemd.) + +import time +import datetime +import math + +from neopixel import * + +# LED strip configuration: +LED_COUNT = 12 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +# True to invert the signal (when using NPN transistor level shift) +LED_INVERT = False + +# Main program logic follows: +if __name__ == '__main__': + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel( + LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS) + # Intialize the library (must be called once before other functions). + strip.begin() + + for i in range(0, strip.numPixels(), 1): + strip.setPixelColor(i, Color(0, 0, 0)) + while True: + now = datetime.datetime.now() + + # Low light during 19-8 o'clock + if(8 < now.hour < 19): + strip.setBrightness(200) + else: + strip.setBrightness(25) + + hour = now.hour % 12 + minute = now.minute / 5 + second = now.second / 5 + secondmodulo = now.second % 5 + timeslot_in_microseconds = secondmodulo * 1000000 + now.microsecond + for i in range(0, strip.numPixels(), 1): + secondplusone = second + 1 if(second < 11) else 0 + secondminusone = second - 1 if(second > 0) else 11 + colorarray = [0, 0, 0] + + if i == second: + if timeslot_in_microseconds < 2500000: + colorarray[0] = int( + 0.0000508 * timeslot_in_microseconds) + 126 + else: + colorarray[0] = 382 - \ + int(0.0000508 * timeslot_in_microseconds) + if i == secondplusone: + colorarray[0] = int(0.0000256 * timeslot_in_microseconds) + if i == secondminusone: + colorarray[0] = int( + 0.0000256 * timeslot_in_microseconds) * -1 + 128 + if i == minute: + colorarray[2] = 200 + if i == hour: + colorarray[1] = 200 + strip.setPixelColor( + i, Color(colorarray[0], colorarray[1], colorarray[2])) + strip.show() + time.sleep(0.1) diff --git a/python/examples/strandtest.py b/python/examples/strandtest.py new file mode 100644 index 0000000..0f4c32b --- /dev/null +++ b/python/examples/strandtest.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. + +import time +from neopixel import * +import argparse + +# LED strip configuration: +LED_COUNT = 16 # Number of LED pixels. +LED_PIN = 18 # GPIO pin connected to the pixels (18 uses PWM!). +#LED_PIN = 10 # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_CHANNEL = 0 # set to '1' for GPIOs 13, 19, 41, 45 or 53 + + + +# Define functions which animate LEDs in various ways. +def colorWipe(strip, color, wait_ms=50): + """Wipe color across display a pixel at a time.""" + for i in range(strip.numPixels()): + strip.setPixelColor(i, color) + strip.show() + time.sleep(wait_ms/1000.0) + +def theaterChase(strip, color, wait_ms=50, iterations=10): + """Movie theater light style chaser animation.""" + for j in range(iterations): + for q in range(3): + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, color) + strip.show() + time.sleep(wait_ms/1000.0) + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, 0) + +def wheel(pos): + """Generate rainbow colors across 0-255 positions.""" + if pos < 85: + return Color(pos * 3, 255 - pos * 3, 0) + elif pos < 170: + pos -= 85 + return Color(255 - pos * 3, 0, pos * 3) + else: + pos -= 170 + return Color(0, pos * 3, 255 - pos * 3) + +def rainbow(strip, wait_ms=20, iterations=1): + """Draw rainbow that fades across all pixels at once.""" + for j in range(256*iterations): + for i in range(strip.numPixels()): + strip.setPixelColor(i, wheel((i+j) & 255)) + strip.show() + time.sleep(wait_ms/1000.0) + +def rainbowCycle(strip, wait_ms=20, iterations=5): + """Draw rainbow that uniformly distributes itself across all pixels.""" + for j in range(256*iterations): + for i in range(strip.numPixels()): + strip.setPixelColor(i, wheel((int(i * 256 / strip.numPixels()) + j) & 255)) + strip.show() + time.sleep(wait_ms/1000.0) + +def theaterChaseRainbow(strip, wait_ms=50): + """Rainbow movie theater light style chaser animation.""" + for j in range(256): + for q in range(3): + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, wheel((i+j) % 255)) + strip.show() + time.sleep(wait_ms/1000.0) + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, 0) + +# Main program logic follows: +if __name__ == '__main__': + # Process arguments + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--clear', action='store_true', help='clear the display on exit') + args = parser.parse_args() + + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL) + # Intialize the library (must be called once before other functions). + strip.begin() + + print ('Press Ctrl-C to quit.') + if not args.clear: + print('Use "-c" argument to clear LEDs on exit') + + try: + + while True: + print ('Color wipe animations.') + colorWipe(strip, Color(255, 0, 0)) # Red wipe + colorWipe(strip, Color(0, 255, 0)) # Blue wipe + colorWipe(strip, Color(0, 0, 255)) # Green wipe + print ('Theater chase animations.') + theaterChase(strip, Color(127, 127, 127)) # White theater chase + theaterChase(strip, Color(127, 0, 0)) # Red theater chase + theaterChase(strip, Color( 0, 0, 127)) # Blue theater chase + print ('Rainbow animations.') + rainbow(strip) + rainbowCycle(strip) + theaterChaseRainbow(strip) + + except KeyboardInterrupt: + if args.clear: + colorWipe(strip, Color(0,0,0), 10) diff --git a/python/ez_setup.py b/python/ez_setup.py new file mode 100644 index 0000000..52acd34 --- /dev/null +++ b/python/ez_setup.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python +"""Bootstrap setuptools installation + +To use setuptools in your package's setup.py, include this +file in the same directory and add this to the top of your setup.py:: + + from ez_setup import use_setuptools + use_setuptools() + +To require a specific version of setuptools, set a download +mirror, or use an alternate download directory, simply supply +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import os +import shutil +import sys +import tempfile +import zipfile +import optparse +import subprocess +import platform +import textwrap +import contextlib + +from distutils import log + +try: + from urllib.request import urlopen +except ImportError: + from urllib2 import urlopen + +try: + from site import USER_SITE +except ImportError: + USER_SITE = None + +DEFAULT_VERSION = "5.7" +DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" + +def _python_cmd(*args): + """ + Return True if the command succeeded. + """ + args = (sys.executable,) + args + return subprocess.call(args) == 0 + + +def _install(archive_filename, install_args=()): + with archive_context(archive_filename): + # installing + log.warn('Installing Setuptools') + if not _python_cmd('setup.py', 'install', *install_args): + log.warn('Something went wrong during the installation.') + log.warn('See the error message above.') + # exitcode will be 2 + return 2 + + +def _build_egg(egg, archive_filename, to_dir): + with archive_context(archive_filename): + # building an egg + log.warn('Building a Setuptools egg in %s', to_dir) + _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) + # returning the result + log.warn(egg) + if not os.path.exists(egg): + raise IOError('Could not build the egg.') + + +class ContextualZipFile(zipfile.ZipFile): + """ + Supplement ZipFile class to support context manager for Python 2.6 + """ + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def __new__(cls, *args, **kwargs): + """ + Construct a ZipFile or ContextualZipFile as appropriate + """ + if hasattr(zipfile.ZipFile, '__exit__'): + return zipfile.ZipFile(*args, **kwargs) + return super(ContextualZipFile, cls).__new__(cls) + + +@contextlib.contextmanager +def archive_context(filename): + # extracting the archive + tmpdir = tempfile.mkdtemp() + log.warn('Extracting in %s', tmpdir) + old_wd = os.getcwd() + try: + os.chdir(tmpdir) + with ContextualZipFile(filename) as archive: + archive.extractall() + + # going in the directory + subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) + os.chdir(subdir) + log.warn('Now working in %s', subdir) + yield + + finally: + os.chdir(old_wd) + shutil.rmtree(tmpdir) + + +def _do_download(version, download_base, to_dir, download_delay): + egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' + % (version, sys.version_info[0], sys.version_info[1])) + if not os.path.exists(egg): + archive = download_setuptools(version, download_base, + to_dir, download_delay) + _build_egg(egg, archive, to_dir) + sys.path.insert(0, egg) + + # Remove previously-imported pkg_resources if present (see + # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). + if 'pkg_resources' in sys.modules: + del sys.modules['pkg_resources'] + + import setuptools + setuptools.bootstrap_install_from = egg + + +def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, + to_dir=os.curdir, download_delay=15): + to_dir = os.path.abspath(to_dir) + rep_modules = 'pkg_resources', 'setuptools' + imported = set(sys.modules).intersection(rep_modules) + try: + import pkg_resources + except ImportError: + return _do_download(version, download_base, to_dir, download_delay) + try: + pkg_resources.require("setuptools>=" + version) + return + except pkg_resources.DistributionNotFound: + return _do_download(version, download_base, to_dir, download_delay) + except pkg_resources.VersionConflict as VC_err: + if imported: + msg = textwrap.dedent(""" + The required version of setuptools (>={version}) is not available, + and can't be installed while this script is running. Please + install a more recent version first, using + 'easy_install -U setuptools'. + + (Currently using {VC_err.args[0]!r}) + """).format(VC_err=VC_err, version=version) + sys.stderr.write(msg) + sys.exit(2) + + # otherwise, reload ok + del pkg_resources, sys.modules['pkg_resources'] + return _do_download(version, download_base, to_dir, download_delay) + +def _clean_check(cmd, target): + """ + Run the command to download target. If the command fails, clean up before + re-raising the error. + """ + try: + subprocess.check_call(cmd) + except subprocess.CalledProcessError: + if os.access(target, os.F_OK): + os.unlink(target) + raise + +def download_file_powershell(url, target): + """ + Download the file at url to target using Powershell (which will validate + trust). Raise an exception if the command cannot complete. + """ + target = os.path.abspath(target) + ps_cmd = ( + "[System.Net.WebRequest]::DefaultWebProxy.Credentials = " + "[System.Net.CredentialCache]::DefaultCredentials; " + "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" + % vars() + ) + cmd = [ + 'powershell', + '-Command', + ps_cmd, + ] + _clean_check(cmd, target) + +def has_powershell(): + if platform.system() != 'Windows': + return False + cmd = ['powershell', '-Command', 'echo test'] + with open(os.path.devnull, 'wb') as devnull: + try: + subprocess.check_call(cmd, stdout=devnull, stderr=devnull) + except Exception: + return False + return True + +download_file_powershell.viable = has_powershell + +def download_file_curl(url, target): + cmd = ['curl', '-L', url, '--silent', '--output', target] + _clean_check(cmd, target) + +def has_curl(): + cmd = ['curl', '--version'] + with open(os.path.devnull, 'wb') as devnull: + try: + subprocess.check_call(cmd, stdout=devnull, stderr=devnull) + except Exception: + return False + return True + +download_file_curl.viable = has_curl + +def download_file_wget(url, target): + cmd = ['wget', url, '--quiet', '--output-document', target] + _clean_check(cmd, target) + +def has_wget(): + cmd = ['wget', '--version'] + with open(os.path.devnull, 'wb') as devnull: + try: + subprocess.check_call(cmd, stdout=devnull, stderr=devnull) + except Exception: + return False + return True + +download_file_wget.viable = has_wget + +def download_file_insecure(url, target): + """ + Use Python to download the file, even though it cannot authenticate the + connection. + """ + src = urlopen(url) + try: + # Read all the data in one block. + data = src.read() + finally: + src.close() + + # Write all the data in one block to avoid creating a partial file. + with open(target, "wb") as dst: + dst.write(data) + +download_file_insecure.viable = lambda: True + +def get_best_downloader(): + downloaders = ( + download_file_powershell, + download_file_curl, + download_file_wget, + download_file_insecure, + ) + viable_downloaders = (dl for dl in downloaders if dl.viable()) + return next(viable_downloaders, None) + +def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, + to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader): + """ + Download setuptools from a specified location and return its filename + + `version` should be a valid setuptools version number that is available + as an sdist for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download + attempt. + + ``downloader_factory`` should be a function taking no arguments and + returning a function for downloading a URL to a target. + """ + # making sure we use the absolute path + to_dir = os.path.abspath(to_dir) + zip_name = "setuptools-%s.zip" % version + url = download_base + zip_name + saveto = os.path.join(to_dir, zip_name) + if not os.path.exists(saveto): # Avoid repeated downloads + log.warn("Downloading %s", url) + downloader = downloader_factory() + downloader(url, saveto) + return os.path.realpath(saveto) + +def _build_install_args(options): + """ + Build the arguments to 'python setup.py install' on the setuptools package + """ + return ['--user'] if options.user_install else [] + +def _parse_args(): + """ + Parse the command line for options + """ + parser = optparse.OptionParser() + parser.add_option( + '--user', dest='user_install', action='store_true', default=False, + help='install in user site package (requires Python 2.6 or later)') + parser.add_option( + '--download-base', dest='download_base', metavar="URL", + default=DEFAULT_URL, + help='alternative URL from where to download the setuptools package') + parser.add_option( + '--insecure', dest='downloader_factory', action='store_const', + const=lambda: download_file_insecure, default=get_best_downloader, + help='Use internal, non-validating downloader' + ) + parser.add_option( + '--version', help="Specify which version to download", + default=DEFAULT_VERSION, + ) + options, args = parser.parse_args() + # positional arguments are ignored + return options + +def main(): + """Install or upgrade setuptools and EasyInstall""" + options = _parse_args() + archive = download_setuptools( + version=options.version, + download_base=options.download_base, + downloader_factory=options.downloader_factory, + ) + return _install(archive, _build_install_args(options)) + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file diff --git a/python/leds.txt b/python/leds.txt new file mode 100644 index 0000000..44f1073 --- /dev/null +++ b/python/leds.txt @@ -0,0 +1,494 @@ +0,63,408 +1,57,397 +2,55,387 +3,50,374 +4,44,356 +5,41,343 +6,42,330 +7,42,320 +8,41,308 +9,41,298 +10,42,286 +11,41,273 +12,41,264 +13,41,253 +14,41,239 +15,41,230 +16,40,219 +17,40,209 +18,43,192 +19,49,176 +20,52,164 +21,57,151 +22,61,143 +23,64,141 +24,76,136 +25,89,132 +26,102,128 +27,115,126 +28,126,126 +29,137,126 +30,148,126 +31,159,126 +32,170,128 +33,182,129 +34,193,128 +35,203,128 +36,214,128 +37,226,128 +38,236,129 +39,249,128 +40,259,126 +41,272,125 +42,287,128 +43,299,131 +44,312,135 +45,323,137 +46,333,141 +47,334,148 +48,335,164 +49,336,177 +50,336,190 +51,334,205 +52,334,209 +53,324,211 +54,316,213 +55,305,216 +56,292,219 +57,279,220 +58,264,221 +59,251,220 +60,241,221 +61,230,221 +62,219,221 +63,207,221 +64,199,221 +65,193,221 +66,187,222 +67,181,222 +68,180,222 +69,180,223 +70,179,224 +71,180,224 +72,185,224 +73,192,224 +74,199,223 +75,206,224 +76,218,224 +77,228,224 +78,244,226 +79,260,230 +80,272,233 +81,283,235 +82,293,237 +83,304,238 +84,305,243 +85,305,253 +86,305,267 +87,305,282 +88,309,293 +89,301,300 +90,293,301 +91,284,302 +92,270,306 +93,257,310 +94,241,315 +95,229,316 +96,217,315 +97,206,316 +98,199,316 +99,192,316 +100,187,316 +101,181,316 +102,183,317 +103,182,318 +104,182,319 +105,186,319 +106,189,319 +107,197,319 +108,203,319 +109,211,319 +110,223,319 +111,234,319 +112,244,318 +113,255,318 +114,269,318 +115,280,317 +116,296,318 +117,307,320 +118,321,322 +119,330,325 +120,339,329 +121,345,335 +122,348,346 +123,349,362 +124,349,376 +125,349,391 +126,344,401 +127,334,405 +128,322,408 +129,310,412 +130,295,416 +131,280,419 +132,270,420 +133,259,419 +134,248,420 +135,238,421 +136,227,419 +137,215,419 +138,205,419 +139,193,419 +140,182,419 +141,171,419 +142,160,419 +143,149,419 +144,138,420 +145,127,420 +146,115,420 +147,103,419 +148,86,416 +149,73,414 +150,391,397 +151,388,389 +152,382,374 +153,378,360 +154,375,348 +155,374,336 +156,374,324 +157,374,313 +158,374,302 +159,373,291 +160,373,279 +161,373,267 +162,372,256 +163,372,243 +164,372,234 +165,372,222 +166,371,212 +167,371,201 +168,374,185 +169,378,170 +170,382,156 +171,385,145 +172,391,135 +173,402,132 +174,417,128 +175,434,125 +176,454,127 +177,467,129 +178,479,133 +179,490,137 +180,499,142 +181,502,154 +182,506,166 +183,508,178 +184,511,192 +185,511,204 +186,512,216 +187,512,227 +188,513,239 +189,513,249 +190,511,263 +191,513,273 +192,513,281 +193,514,288 +194,514,294 +195,514,300 +196,513,305 +197,519,306 +198,526,305 +199,533,305 +200,540,305 +201,550,305 +202,561,304 +203,573,305 +204,583,305 +205,596,306 +206,613,310 +207,624,312 +208,636,315 +209,647,318 +210,655,322 +211,658,334 +212,658,347 +213,659,362 +214,657,378 +215,656,393 +216,645,399 +217,633,404 +218,622,408 +219,606,412 +220,591,415 +221,580,416 +222,571,416 +223,559,417 +224,548,417 +225,537,417 +226,524,417 +227,514,417 +228,502,417 +229,491,418 +230,480,418 +231,470,417 +232,459,417 +233,448,417 +234,434,415 +235,420,412 +236,405,407 +237,685,392 +238,686,380 +239,686,369 +240,686,354 +241,689,339 +242,693,326 +243,696,316 +244,701,305 +245,706,295 +246,710,285 +247,714,275 +248,718,264 +249,723,253 +250,727,243 +251,732,232 +252,736,223 +253,741,211 +254,745,202 +255,749,191 +256,754,181 +257,760,169 +258,768,157 +259,776,145 +260,783,135 +261,795,128 +262,804,125 +263,817,122 +264,832,119 +265,844,117 +266,855,115 +267,869,116 +268,885,118 +269,898,120 +270,912,123 +271,922,125 +272,935,136 +273,942,145 +274,951,158 +275,958,170 +276,964,182 +277,969,193 +278,973,203 +279,978,214 +280,982,224 +281,987,236 +282,991,246 +283,996,257 +284,1000,267 +285,1004,276 +286,1009,288 +287,1013,298 +288,1018,309 +289,1022,320 +290,1026,331 +291,1028,348 +292,1029,363 +293,1030,376 +294,1030,389 +295,1028,399 +296,1018,401 +297,1006,404 +298,991,408 +299,977,414 +300,959,414 +301,946,412 +302,933,411 +303,920,409 +304,913,405 +305,907,400 +306,898,394 +307,888,393 +308,879,392 +309,871,391 +310,862,390 +311,852,391 +312,843,392 +313,835,393 +314,825,394 +315,814,395 +316,805,398 +317,801,403 +318,791,412 +319,783,410 +320,770,412 +321,757,414 +322,742,415 +323,723,409 +324,711,405 +325,699,403 +326,688,399 +327,852,285 +328,855,280 +329,855,277 +330,855,275 +331,854,273 +332,855,268 +333,855,263 +334,856,255 +335,857,255 +336,857,260 +337,858,267 +338,858,273 +339,858,275 +340,857,277 +341,857,279 +342,859,284 +343,859,287 +344,857,286 +345,856,286 +346,856,286 +347,855,286 +348,853,288 +349,1069,389 +350,1065,376 +351,1062,363 +352,1059,348 +353,1057,335 +354,1057,323 +355,1057,311 +356,1058,299 +357,1058,288 +358,1058,277 +359,1057,267 +360,1058,252 +361,1058,242 +362,1059,232 +363,1058,224 +364,1058,207 +365,1058,197 +366,1059,184 +367,1061,171 +368,1064,158 +369,1068,144 +370,1071,131 +371,1079,124 +372,1090,121 +373,1103,117 +374,1114,114 +375,1131,110 +376,1146,108 +377,1158,107 +378,1169,108 +379,1182,108 +380,1194,109 +381,1206,109 +382,1218,110 +383,1230,110 +384,1239,112 +385,1253,112 +386,1266,114 +387,1277,111 +388,1289,108 +389,1302,108 +390,1316,108 +391,1329,109 +392,1341,111 +393,1355,115 +394,1369,120 +395,1383,128 +396,1395,137 +397,1403,148 +398,1409,162 +399,1413,177 +400,1413,196 +401,1413,212 +402,1413,223 +403,1411,231 +404,1406,239 +405,1402,245 +406,1397,249 +407,1403,252 +408,1407,255 +409,1412,261 +410,1414,269 +411,1416,279 +412,1418,292 +413,1420,309 +414,1420,327 +415,1416,348 +416,1413,361 +417,1407,372 +418,1399,383 +419,1388,393 +420,1374,400 +421,1361,405 +422,1349,408 +423,1334,411 +424,1320,412 +425,1308,413 +426,1296,414 +427,1283,414 +428,1271,414 +429,1259,414 +430,1247,413 +431,1234,413 +432,1222,413 +433,1211,413 +434,1200,414 +435,1188,413 +436,1176,413 +437,1164,413 +438,1152,413 +439,1140,413 +440,1125,411 +441,1109,407 +442,1096,403 +443,1083,399 +444,1072,396 +445,1205,307 +446,1207,306 +447,1207,304 +448,1206,303 +449,1205,302 +450,1210,300 +451,1215,300 +452,1221,301 +453,1229,302 +454,1237,300 +455,1247,300 +456,1255,302 +457,1260,302 +458,1265,303 +459,1268,303 +460,1270,305 +461,1269,306 +462,1266,307 +463,1261,307 +464,1254,307 +465,1246,307 +466,1237,307 +467,1228,307 +468,1223,307 +469,1217,307 +470,1210,307 +471,1206,211 +472,1207,209 +473,1207,207 +474,1207,206 +475,1212,206 +476,1218,206 +477,1224,207 +478,1231,207 +479,1238,206 +480,1245,206 +481,1251,206 +482,1256,207 +483,1261,207 +484,1264,208 +485,1263,209 +486,1259,209 +487,1253,214 +488,1247,213 +489,1239,212 +490,1231,210 +491,1224,211 +492,1217,214 +493,1212,214 \ No newline at end of file diff --git a/python/neopixel.py b/python/neopixel.py new file mode 100644 index 0000000..aef8367 --- /dev/null +++ b/python/neopixel.py @@ -0,0 +1,151 @@ +# Adafruit NeoPixel library port to the rpi_ws281x library. +# Author: Tony DiCola (tony@tonydicola.com), Jeremy Garff (jer@jers.net) +import atexit + +import _rpi_ws281x as ws + + +def Color(red, green, blue, white = 0): + """Convert the provided red, green, blue color to a 24-bit color value. + Each color component should be a value 0-255 where 0 is the lowest intensity + and 255 is the highest intensity. + """ + return (white << 24) | (red << 16)| (green << 8) | blue + + +class _LED_Data(object): + """Wrapper class which makes a SWIG LED color data array look and feel like + a Python list of integers. + """ + def __init__(self, channel, size): + self.size = size + self.channel = channel + + def __getitem__(self, pos): + """Return the 24-bit RGB color value at the provided position or slice + of positions. + """ + # Handle if a slice of positions are passed in by grabbing all the values + # and returning them in a list. + if isinstance(pos, slice): + return [ws.ws2811_led_get(self.channel, n) for n in xrange(*pos.indices(self.size))] + # Else assume the passed in value is a number to the position. + else: + return ws.ws2811_led_get(self.channel, pos) + + def __setitem__(self, pos, value): + """Set the 24-bit RGB color value at the provided position or slice of + positions. + """ + # Handle if a slice of positions are passed in by setting the appropriate + # LED data values to the provided values. + if isinstance(pos, slice): + index = 0 + for n in xrange(*pos.indices(self.size)): + ws.ws2811_led_set(self.channel, n, value[index]) + index += 1 + # Else assume the passed in value is a number to the position. + else: + return ws.ws2811_led_set(self.channel, pos, value) + + +class Adafruit_NeoPixel(object): + def __init__(self, num, pin, freq_hz=800000, dma=10, invert=False, + brightness=255, channel=0, strip_type=ws.WS2811_STRIP_RGB): + """Class to represent a NeoPixel/WS281x LED display. Num should be the + number of pixels in the display, and pin should be the GPIO pin connected + to the display signal line (must be a PWM pin like 18!). Optional + parameters are freq, the frequency of the display signal in hertz (default + 800khz), dma, the DMA channel to use (default 10), invert, a boolean + specifying if the signal line should be inverted (default False), and + channel, the PWM channel to use (defaults to 0). + """ + # Create ws2811_t structure and fill in parameters. + self._leds = ws.new_ws2811_t() + + # Initialize the channels to zero + for channum in range(2): + chan = ws.ws2811_channel_get(self._leds, channum) + ws.ws2811_channel_t_count_set(chan, 0) + ws.ws2811_channel_t_gpionum_set(chan, 0) + ws.ws2811_channel_t_invert_set(chan, 0) + ws.ws2811_channel_t_brightness_set(chan, 0) + + # Initialize the channel in use + self._channel = ws.ws2811_channel_get(self._leds, channel) + ws.ws2811_channel_t_count_set(self._channel, num) + ws.ws2811_channel_t_gpionum_set(self._channel, pin) + ws.ws2811_channel_t_invert_set(self._channel, 0 if not invert else 1) + ws.ws2811_channel_t_brightness_set(self._channel, brightness) + ws.ws2811_channel_t_strip_type_set(self._channel, strip_type) + + # Initialize the controller + ws.ws2811_t_freq_set(self._leds, freq_hz) + ws.ws2811_t_dmanum_set(self._leds, dma) + + # Grab the led data array. + self._led_data = _LED_Data(self._channel, num) + + # Substitute for __del__, traps an exit condition and cleans up properly + atexit.register(self._cleanup) + + def _cleanup(self): + # Clean up memory used by the library when not needed anymore. + if self._leds is not None: + ws.delete_ws2811_t(self._leds) + self._leds = None + self._channel = None + + def begin(self): + """Initialize library, must be called once before other functions are + called. + """ + resp = ws.ws2811_init(self._leds) + if resp != ws.WS2811_SUCCESS: + message = ws.ws2811_get_return_t_str(resp) + raise RuntimeError('ws2811_init failed with code {0} ({1})'.format(resp, message)) + + def show(self): + """Update the display with the data from the LED buffer.""" + resp = ws.ws2811_render(self._leds) + if resp != ws.WS2811_SUCCESS: + message = ws.ws2811_get_return_t_str(resp) + raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, message)) + + def setPixelColor(self, n, color): + """Set LED at position n to the provided 24-bit color value (in RGB order). + """ + self._led_data[n] = color + + def setPixelColorRGB(self, n, red, green, blue, white = 0): + """Set LED at position n to the provided red, green, and blue color. + Each color component should be a value from 0 to 255 (where 0 is the + lowest intensity and 255 is the highest intensity). + """ + self.setPixelColor(n, Color(red, green, blue, white)) + + def setBrightness(self, brightness): + """Scale each LED in the buffer by the provided brightness. A brightness + of 0 is the darkest and 255 is the brightest. + """ + ws.ws2811_channel_t_brightness_set(self._channel, brightness) + + def getBrightness(self): + """Get the brightness value for each LED in the buffer. A brightness + of 0 is the darkest and 255 is the brightest. + """ + return ws.ws2811_channel_t_brightness_get(self._channel) + + def getPixels(self): + """Return an object which allows access to the LED display data as if + it were a sequence of 24-bit RGB values. + """ + return self._led_data + + def numPixels(self): + """Return the number of pixels in the display.""" + return ws.ws2811_channel_t_count_get(self._channel) + + def getPixelColor(self, n): + """Get the 24-bit RGB color value for the LED at position n.""" + return self._led_data[n] diff --git a/python/nohup.out b/python/nohup.out new file mode 100644 index 0000000..e69de29 diff --git a/python/overclock.txt b/python/overclock.txt new file mode 100644 index 0000000..122b64e --- /dev/null +++ b/python/overclock.txt @@ -0,0 +1,26 @@ +For overclocing needs, the pulse times MEASURED on the sign were: + +0: +high: 294ns +low: 954ns + +1: +high: 626ns +low: 618ns + + +The HIGH pulse times are always the same, the LOW pulse times are shortened by increasing frequency + + + +Current speed 800Khz + +494 LED theoretical maximum framerate: +1/((494*32)/800000 + 80e-6) = 50.4Hz :( + +925KHz is still fully in spec of the LEDs and provides: +58.2Hz + +955KHz is only slightly out of spec and proviced a smooth +60.1fps + diff --git a/python/rpi_ws281x.i b/python/rpi_ws281x.i new file mode 100644 index 0000000..72634e0 --- /dev/null +++ b/python/rpi_ws281x.i @@ -0,0 +1,48 @@ +// SWIG interface file to define rpi_ws281x library python wrapper. +// Author: Tony DiCola (tony@tonydicola.com), Jeremy Garff (jer@jers.net) + +// Define module name rpi_ws281x. This will actually be imported under +// the name _rpi_ws281x following the SWIG & Python conventions. +%module rpi_ws281x + +// Include standard SWIG types & array support for support of uint32_t +// parameters and arrays. +%include "stdint.i" +%include "carrays.i" + +// Declare functions which will be exported as anything in the ws2811.h header. +%{ +#include "../ws2811.h" +%} + +// Process ws2811.h header and export all included functions. +%include "../ws2811.h" + +%inline %{ + uint32_t ws2811_led_get(ws2811_channel_t *channel, int lednum) + { + if (lednum >= channel->count) + { + return -1; + } + + return channel->leds[lednum]; + } + + int ws2811_led_set(ws2811_channel_t *channel, int lednum, uint32_t color) + { + if (lednum >= channel->count) + { + return -1; + } + + channel->leds[lednum] = color; + + return 0; + } + + ws2811_channel_t *ws2811_channel_get(ws2811_t *ws, int channelnum) + { + return &ws->channel[channelnum]; + } +%} diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..76ddfab --- /dev/null +++ b/python/setup.py @@ -0,0 +1,18 @@ +# Python wrapper for the rpi_ws281x library. +# Author: Tony DiCola (tony@tonydicola.com) +from ez_setup import use_setuptools +use_setuptools() +from setuptools import setup, find_packages, Extension + +setup(name = 'rpi_ws281x', + version = '1.0.0', + author = 'Jeremy Garff', + author_email = 'jer@jers.net', + description = 'Userspace Raspberry Pi PWM library for WS281X LEDs.', + license = 'MIT', + url = 'https://github.com/jgarff/rpi_ws281x/', + py_modules = ['neopixel'], + ext_modules = [Extension('_rpi_ws281x', + sources=['rpi_ws281x.i'], + library_dirs=['../.'], + libraries=['ws2811', 'rt'])]) diff --git a/rpihw.c b/rpihw.c new file mode 100644 index 0000000..941437c --- /dev/null +++ b/rpihw.c @@ -0,0 +1,466 @@ +/* + * rpihw.c + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "rpihw.h" + + +#define LINE_WIDTH_MAX 80 +#define HW_VER_STRING "Revision" + +#define PERIPH_BASE_RPI 0x20000000 +#define PERIPH_BASE_RPI2 0x3f000000 +#define PERIPH_BASE_RPI4 0xfe000000 + +#define VIDEOCORE_BASE_RPI 0x40000000 +#define VIDEOCORE_BASE_RPI2 0xc0000000 + +#define RPI_MANUFACTURER_MASK (0xf << 16) +#define RPI_WARRANTY_MASK (0x3 << 24) + +static const rpi_hw_t rpi_hw_info[] = { + // + // Raspberry Pi 4 + // + { + .hwver = 0xA03111, + .type = RPI_HWVER_TYPE_PI4, + .periph_base = PERIPH_BASE_RPI4, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 4 Model B - 1GB" + }, + { + .hwver = 0xB03111, + .type = RPI_HWVER_TYPE_PI4, + .periph_base = PERIPH_BASE_RPI4, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 4 Model B - 2GB" + }, + { + .hwver = 0xC03111, + .type = RPI_HWVER_TYPE_PI4, + .periph_base = PERIPH_BASE_RPI4, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 4 Model B - 4GB" + }, + // + // Model B Rev 1.0 + // + { + .hwver = 0x02, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B", + }, + { + .hwver = 0x03, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B", + }, + + // + // Model B Rev 2.0 + // + { + .hwver = 0x04, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B", + }, + { + .hwver = 0x05, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B", + }, + { + .hwver = 0x06, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B", + }, + + // + // Model A + // + { + .hwver = 0x07, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model A", + }, + { + .hwver = 0x08, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model A", + }, + { + .hwver = 0x09, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model A", + }, + + // + // Model B + // + { + .hwver = 0x0d, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B", + }, + { + .hwver = 0x0e, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B", + }, + { + .hwver = 0x0f, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B", + }, + + // + // Model B+ + // + { + .hwver = 0x10, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B+", + }, + { + .hwver = 0x13, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B+", + }, + { + .hwver = 0x900032, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model B+", + }, + + // + // Compute Module + // + { + .hwver = 0x11, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Compute Module 1", + }, + { + .hwver = 0x14, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Compute Module 1", + }, + + // + // Pi Zero + // + { + .hwver = 0x900092, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Pi Zero v1.2", + }, + { + .hwver = 0x900093, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Pi Zero v1.3", + }, + { + .hwver = 0x920093, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Pi Zero v1.3", + }, + { + .hwver = 0x9200c1, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Pi Zero W v1.1", + }, + { + .hwver = 0x9000c1, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Pi Zero W v1.1", + }, + + // + // Model A+ + // + { + .hwver = 0x12, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model A+", + }, + { + .hwver = 0x15, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model A+", + }, + { + .hwver = 0x900021, + .type = RPI_HWVER_TYPE_PI1, + .periph_base = PERIPH_BASE_RPI, + .videocore_base = VIDEOCORE_BASE_RPI, + .desc = "Model A+", + }, + + // + // Pi 2 Model B + // + { + .hwver = 0xa01041, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 2", + }, + { + .hwver = 0xa01040, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 2", + }, + { + .hwver = 0xa21041, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 2", + }, + // + // Pi 2 with BCM2837 + // + { + .hwver = 0xa22042, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 2", + }, + // + // Pi 3 Model B + // + { + .hwver = 0xa020d3, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 3 B+", + }, + { + .hwver = 0xa02082, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 3", + }, + { + .hwver = 0xa02083, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 3", + }, + { + .hwver = 0xa22082, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 3", + }, + { + .hwver = 0xa22083, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Pi 3", + }, + { + .hwver = 0x9020e0, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Model 3 A+", + }, + + // + // Pi Compute Module 3 + // + { + .hwver = 0xa020a0, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Compute Module 3/L3", + }, + // + // Pi Compute Module 3+ + // + { + .hwver = 0xa02100, + .type = RPI_HWVER_TYPE_PI2, + .periph_base = PERIPH_BASE_RPI2, + .videocore_base = VIDEOCORE_BASE_RPI2, + .desc = "Compute Module 3+", + }, + + +}; + + +const rpi_hw_t *rpi_hw_detect(void) +{ + const rpi_hw_t *result = NULL; + uint32_t rev; + unsigned i; + +#ifdef __aarch64__ + // On ARM64, read revision from /proc/device-tree as it is not shown in + // /proc/cpuinfo + FILE *f = fopen("/proc/device-tree/system/linux,revision", "r"); + if (!f) + { + return NULL; + } + size_t read = fread(&rev, sizeof(uint32_t), 1, f); + if (read != sizeof(uint32_t)) + goto done; + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + rev = bswap_32(rev); // linux,revision appears to be in big endian + #endif + + for (i = 0; i < (sizeof(rpi_hw_info) / sizeof(rpi_hw_info[0])); i++) + { + uint32_t hwver = rpi_hw_info[i].hwver; + if (rev == hwver) + { + result = &rpi_hw_info[i]; + + goto done; + } + } +#else + FILE *f = fopen("/proc/cpuinfo", "r"); + char line[LINE_WIDTH_MAX]; + + if (!f) + { + return NULL; + } + + while (fgets(line, LINE_WIDTH_MAX - 1, f)) + { + if (strstr(line, HW_VER_STRING)) + { + char *substr; + + substr = strstr(line, ": "); + if (!substr) + { + continue; + } + + errno = 0; + rev = strtoul(&substr[1], NULL, 16); // Base 16 + if (errno) + { + continue; + } + + for (i = 0; i < (sizeof(rpi_hw_info) / sizeof(rpi_hw_info[0])); i++) + { + uint32_t hwver = rpi_hw_info[i].hwver; + + // Take out warranty and manufacturer bits + hwver &= ~(RPI_WARRANTY_MASK | RPI_MANUFACTURER_MASK); + rev &= ~(RPI_WARRANTY_MASK | RPI_MANUFACTURER_MASK); + + if (rev == hwver) + { + result = &rpi_hw_info[i]; + + goto done; + } + } + } + } +#endif +done: + fclose(f); + + return result; +} + diff --git a/rpihw.h b/rpihw.h new file mode 100644 index 0000000..e38ab1b --- /dev/null +++ b/rpihw.h @@ -0,0 +1,51 @@ +/* + * rpihw.h + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef __RPIHW_H__ +#define __RPIHW_H__ + + +typedef struct { + uint32_t type; +#define RPI_HWVER_TYPE_UNKNOWN 0 +#define RPI_HWVER_TYPE_PI1 1 +#define RPI_HWVER_TYPE_PI2 2 +#define RPI_HWVER_TYPE_PI4 3 + uint32_t hwver; + uint32_t periph_base; + uint32_t videocore_base; + char *desc; +} rpi_hw_t; + + +const rpi_hw_t *rpi_hw_detect(void); + + +#endif /* __RPIHW_H__ */ diff --git a/version b/version new file mode 100644 index 0000000..9084fa2 --- /dev/null +++ b/version @@ -0,0 +1 @@ +1.1.0 diff --git a/version.py b/version.py new file mode 100644 index 0000000..4ddd3b3 --- /dev/null +++ b/version.py @@ -0,0 +1,71 @@ +# +# SConstruct +# +# Copyright (c) 2016 Jeremy Garff +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted +# provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other materials +# provided with the distribution. +# 3. Neither the name of the owner nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +import SCons, os + +def version_flags(env): + if not env['V']: + env['VERSIONCOMSTR'] = 'Version ${TARGET}' + +def version_builders(env): + def generate_version_header(target, source, env): + headername = os.path.basename(target[0].abspath) + headerdef = headername.replace('.', '_').replace('-', '_').upper() + + try: + version = open(source[0].abspath, 'r').readline().strip().split('.') + except: + version = [ '0', '0', '0' ] + + f = open(headername, 'w') + f.write('/* Auto Generated Header built by version.py - DO NOT MODIFY */\n') + f.write('\n') + f.write('#ifndef __%s__\n' % (headerdef)) + f.write('#define __%s__\n' % (headerdef)) + f.write('\n') + f.write('#define VERSION_MAJOR %s\n' % version[0]) + f.write('#define VERSION_MINOR %s\n' % version[1]) + f.write('#define VERSION_MICRO %s\n' % version[2]) + f.write('\n') + f.write('#endif /* __%s__ */\n' % (headerdef)) + f.close() + + env.Append(BUILDERS = { + 'Version' : SCons.Builder.Builder( + action = SCons.Action.Action(generate_version_header, '${VERSIONCOMSTR}'), + suffix = '.h', + ), + }) + +def exists(env): + return 1 + +def generate(env, **kwargs): + [f(env) for f in (version_flags, version_builders)] + + diff --git a/ws2811.c b/ws2811.c new file mode 100644 index 0000000..945de96 --- /dev/null +++ b/ws2811.c @@ -0,0 +1,1284 @@ +/* + * ws2811.c + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mailbox.h" +#include "clk.h" +#include "gpio.h" +#include "dma.h" +#include "pwm.h" +#include "pcm.h" +#include "rpihw.h" + +#include "ws2811.h" + + +#define BUS_TO_PHYS(x) ((x)&~0xC0000000) + +#define OSC_FREQ 19200000 // crystal frequency +#define OSC_FREQ_PI4 54000000 // Pi 4 crystal frequency + +/* 4 colors (R, G, B + W), 8 bits per byte, 3 symbols per bit + 55uS low for reset signal */ +#define LED_COLOURS 4 +#define LED_RESET_uS 55 +#define LED_BIT_COUNT(leds, freq) ((leds * LED_COLOURS * 8 * 3) + ((LED_RESET_uS * \ + (freq * 3)) / 1000000)) + +/* Minimum time to wait for reset to occur in microseconds. */ +#define LED_RESET_WAIT_TIME 300 + +// Pad out to the nearest uint32 + 32-bits for idle low/high times the number of channels +#define PWM_BYTE_COUNT(leds, freq) (((((LED_BIT_COUNT(leds, freq) >> 3) & ~0x7) + 4) + 4) * \ + RPI_PWM_CHANNELS) +#define PCM_BYTE_COUNT(leds, freq) ((((LED_BIT_COUNT(leds, freq) >> 3) & ~0x7) + 4) + 4) + +// Symbol definitions +#define SYMBOL_HIGH 0x6 // 1 1 0 +#define SYMBOL_LOW 0x4 // 1 0 0 + +// Symbol definitions for software inversion (PCM and SPI only) +#define SYMBOL_HIGH_INV 0x1 // 0 0 1 +#define SYMBOL_LOW_INV 0x3 // 0 1 1 + +// Driver mode definitions +#define NONE 0 +#define PWM 1 +#define PCM 2 +#define SPI 3 + +// We use the mailbox interface to request memory from the VideoCore. +// This lets us request one physically contiguous chunk, find its +// physical address, and map it 'uncached' so that writes from this +// code are immediately visible to the DMA controller. This struct +// holds data relevant to the mailbox interface. +typedef struct videocore_mbox { + int handle; /* From mbox_open() */ + unsigned mem_ref; /* From mem_alloc() */ + unsigned bus_addr; /* From mem_lock() */ + unsigned size; /* Size of allocation */ + uint8_t *virt_addr; /* From mapmem() */ +} videocore_mbox_t; + +typedef struct ws2811_device +{ + int driver_mode; + volatile uint8_t *pxl_raw; + volatile dma_t *dma; + volatile pwm_t *pwm; + volatile pcm_t *pcm; + int spi_fd; + volatile dma_cb_t *dma_cb; + uint32_t dma_cb_addr; + volatile gpio_t *gpio; + volatile cm_clk_t *cm_clk; + videocore_mbox_t mbox; + int max_count; +} ws2811_device_t; + +/** + * Provides monotonic timestamp in microseconds. + * + * @returns Current timestamp in microseconds or 0 on error. + */ +static uint64_t get_microsecond_timestamp() +{ + struct timespec t; + + if (clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) { + return 0; + } + + return (uint64_t) t.tv_sec * 1000000 + t.tv_nsec / 1000; +} + +/** + * Iterate through the channels and find the largest led count. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns Maximum number of LEDs in all channels. + */ +static int max_channel_led_count(ws2811_t *ws2811) +{ + int chan, max = 0; + + for (chan = 0; chan < RPI_PWM_CHANNELS; chan++) + { + if (ws2811->channel[chan].count > max) + { + max = ws2811->channel[chan].count; + } + } + + return max; +} + +/** + * Map all devices into userspace memory. + * Not called for SPI + * + * @param ws2811 ws2811 instance pointer. + * + * @returns 0 on success, -1 otherwise. + */ +static int map_registers(ws2811_t *ws2811) +{ + ws2811_device_t *device = ws2811->device; + const rpi_hw_t *rpi_hw = ws2811->rpi_hw; + uint32_t base = ws2811->rpi_hw->periph_base; + uint32_t dma_addr; + uint32_t offset = 0; + + dma_addr = dmanum_to_offset(ws2811->dmanum); + if (!dma_addr) + { + return -1; + } + dma_addr += rpi_hw->periph_base; + + device->dma = mapmem(dma_addr, sizeof(dma_t), DEV_MEM); + if (!device->dma) + { + return -1; + } + + switch (device->driver_mode) { + case PWM: + device->pwm = mapmem(PWM_OFFSET + base, sizeof(pwm_t), DEV_MEM); + if (!device->pwm) + { + return -1; + } + break; + + case PCM: + device->pcm = mapmem(PCM_OFFSET + base, sizeof(pcm_t), DEV_MEM); + if (!device->pcm) + { + return -1; + } + break; + } + + /* + * The below call can potentially work with /dev/gpiomem instead. + * However, it used /dev/mem before, so I'm leaving it as such. + */ + + device->gpio = mapmem(GPIO_OFFSET + base, sizeof(gpio_t), DEV_MEM); + if (!device->gpio) + { + return -1; + } + + switch (device->driver_mode) { + case PWM: + offset = CM_PWM_OFFSET; + break; + case PCM: + offset = CM_PCM_OFFSET; + break; + } + device->cm_clk = mapmem(offset + base, sizeof(cm_clk_t), DEV_MEM); + if (!device->cm_clk) + { + return -1; + } + + return 0; +} + +/** + * Unmap all devices from virtual memory. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +static void unmap_registers(ws2811_t *ws2811) +{ + ws2811_device_t *device = ws2811->device; + + if (device->dma) + { + unmapmem((void *)device->dma, sizeof(dma_t)); + } + + if (device->pwm) + { + unmapmem((void *)device->pwm, sizeof(pwm_t)); + } + + if (device->pcm) + { + unmapmem((void *)device->pcm, sizeof(pcm_t)); + } + + if (device->cm_clk) + { + unmapmem((void *)device->cm_clk, sizeof(cm_clk_t)); + } + + if (device->gpio) + { + unmapmem((void *)device->gpio, sizeof(gpio_t)); + } +} + +/** + * Given a userspace address pointer, return the matching bus address used by DMA. + * Note: The bus address is not the same as the CPU physical address. + * + * @param addr Userspace virtual address pointer. + * + * @returns Bus address for use by DMA. + */ +static uint32_t addr_to_bus(ws2811_device_t *device, const volatile void *virt) +{ + videocore_mbox_t *mbox = &device->mbox; + + uint32_t offset = (uint8_t *)virt - mbox->virt_addr; + + return mbox->bus_addr + offset; +} + +/** + * Stop the PWM controller. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +static void stop_pwm(ws2811_t *ws2811) +{ + ws2811_device_t *device = ws2811->device; + volatile pwm_t *pwm = device->pwm; + volatile cm_clk_t *cm_clk = device->cm_clk; + + // Turn off the PWM in case already running + pwm->ctl = 0; + usleep(10); + + // Kill the clock if it was already running + cm_clk->ctl = CM_CLK_CTL_PASSWD | CM_CLK_CTL_KILL; + usleep(10); + while (cm_clk->ctl & CM_CLK_CTL_BUSY) + ; +} + +/** + * Stop the PCM controller. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +static void stop_pcm(ws2811_t *ws2811) +{ + ws2811_device_t *device = ws2811->device; + volatile pcm_t *pcm = device->pcm; + volatile cm_clk_t *cm_clk = device->cm_clk; + + // Turn off the PCM in case already running + pcm->cs = 0; + usleep(10); + + // Kill the clock if it was already running + cm_clk->ctl = CM_CLK_CTL_PASSWD | CM_CLK_CTL_KILL; + usleep(10); + while (cm_clk->ctl & CM_CLK_CTL_BUSY) + ; +} + +/** + * Setup the PWM controller in serial mode on both channels using DMA to feed the PWM FIFO. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +static int setup_pwm(ws2811_t *ws2811) +{ + ws2811_device_t *device = ws2811->device; + volatile dma_t *dma = device->dma; + volatile dma_cb_t *dma_cb = device->dma_cb; + volatile pwm_t *pwm = device->pwm; + volatile cm_clk_t *cm_clk = device->cm_clk; + int maxcount = device->max_count; + uint32_t freq = ws2811->freq; + int32_t byte_count; + + const rpi_hw_t *rpi_hw = ws2811->rpi_hw; + const uint32_t rpi_type = rpi_hw->type; + uint32_t osc_freq = OSC_FREQ; + + if(rpi_type == RPI_HWVER_TYPE_PI4){ + osc_freq = OSC_FREQ_PI4; + } + + stop_pwm(ws2811); + + // Setup the Clock - Use OSC @ 19.2Mhz w/ 3 clocks/tick + cm_clk->div = CM_CLK_DIV_PASSWD | CM_CLK_DIV_DIVI(osc_freq / (3 * freq)); + cm_clk->ctl = CM_CLK_CTL_PASSWD | CM_CLK_CTL_SRC_OSC; + cm_clk->ctl = CM_CLK_CTL_PASSWD | CM_CLK_CTL_SRC_OSC | CM_CLK_CTL_ENAB; + usleep(10); + while (!(cm_clk->ctl & CM_CLK_CTL_BUSY)) + ; + + // Setup the PWM, use delays as the block is rumored to lock up without them. Make + // sure to use a high enough priority to avoid any FIFO underruns, especially if + // the CPU is busy doing lots of memory accesses, or another DMA controller is + // busy. The FIFO will clock out data at a much slower rate (2.6Mhz max), so + // the odds of a DMA priority boost are extremely low. + + pwm->rng1 = 32; // 32-bits per word to serialize + usleep(10); + pwm->ctl = RPI_PWM_CTL_CLRF1; + usleep(10); + pwm->dmac = RPI_PWM_DMAC_ENAB | RPI_PWM_DMAC_PANIC(7) | RPI_PWM_DMAC_DREQ(3); + usleep(10); + pwm->ctl = RPI_PWM_CTL_USEF1 | RPI_PWM_CTL_MODE1 | + RPI_PWM_CTL_USEF2 | RPI_PWM_CTL_MODE2; + if (ws2811->channel[0].invert) + { + pwm->ctl |= RPI_PWM_CTL_POLA1; + } + if (ws2811->channel[1].invert) + { + pwm->ctl |= RPI_PWM_CTL_POLA2; + } + usleep(10); + pwm->ctl |= RPI_PWM_CTL_PWEN1 | RPI_PWM_CTL_PWEN2; + + // Initialize the DMA control block + byte_count = PWM_BYTE_COUNT(maxcount, freq); + dma_cb->ti = RPI_DMA_TI_NO_WIDE_BURSTS | // 32-bit transfers + RPI_DMA_TI_WAIT_RESP | // wait for write complete + RPI_DMA_TI_DEST_DREQ | // user peripheral flow control + RPI_DMA_TI_PERMAP(5) | // PWM peripheral + RPI_DMA_TI_SRC_INC; // Increment src addr + + dma_cb->source_ad = addr_to_bus(device, device->pxl_raw); + + dma_cb->dest_ad = (uintptr_t)&((pwm_t *)PWM_PERIPH_PHYS)->fif1; + dma_cb->txfr_len = byte_count; + dma_cb->stride = 0; + dma_cb->nextconbk = 0; + + dma->cs = 0; + dma->txfr_len = 0; + + return 0; +} + +/** + * Setup the PCM controller with one 32-bit channel in a 32-bit frame using DMA to feed the PCM FIFO. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +static int setup_pcm(ws2811_t *ws2811) +{ + ws2811_device_t *device = ws2811->device; + volatile dma_t *dma = device->dma; + volatile dma_cb_t *dma_cb = device->dma_cb; + volatile pcm_t *pcm = device->pcm; + volatile cm_clk_t *cm_clk = device->cm_clk; + //int maxcount = max_channel_led_count(ws2811); + int maxcount = device->max_count; + uint32_t freq = ws2811->freq; + int32_t byte_count; + + const rpi_hw_t *rpi_hw = ws2811->rpi_hw; + const uint32_t rpi_type = rpi_hw->type; + uint32_t osc_freq = OSC_FREQ; + + if(rpi_type == RPI_HWVER_TYPE_PI4){ + osc_freq = OSC_FREQ_PI4; + } + + stop_pcm(ws2811); + + // Setup the PCM Clock - Use OSC @ 19.2Mhz w/ 3 clocks/tick + cm_clk->div = CM_CLK_DIV_PASSWD | CM_CLK_DIV_DIVI(osc_freq / (3 * freq)); + cm_clk->ctl = CM_CLK_CTL_PASSWD | CM_CLK_CTL_SRC_OSC; + cm_clk->ctl = CM_CLK_CTL_PASSWD | CM_CLK_CTL_SRC_OSC | CM_CLK_CTL_ENAB; + usleep(10); + while (!(cm_clk->ctl & CM_CLK_CTL_BUSY)) + ; + + // Setup the PCM, use delays as the block is rumored to lock up without them. Make + // sure to use a high enough priority to avoid any FIFO underruns, especially if + // the CPU is busy doing lots of memory accesses, or another DMA controller is + // busy. The FIFO will clock out data at a much slower rate (2.6Mhz max), so + // the odds of a DMA priority boost are extremely low. + + pcm->cs = RPI_PCM_CS_EN; // Enable PCM hardware + pcm->mode = (RPI_PCM_MODE_FLEN(31) | RPI_PCM_MODE_FSLEN(1)); + // Framelength 32, clock enabled, frame sync pulse + pcm->txc = RPI_PCM_TXC_CH1WEX | RPI_PCM_TXC_CH1EN | RPI_PCM_TXC_CH1POS(0) | RPI_PCM_TXC_CH1WID(8); + // Single 32-bit channel + pcm->cs |= RPI_PCM_CS_TXCLR; // Reset transmit fifo + usleep(10); + pcm->cs |= RPI_PCM_CS_DMAEN; // Enable DMA DREQ + pcm->dreq = (RPI_PCM_DREQ_TX(0x3F) | RPI_PCM_DREQ_TX_PANIC(0x10)); // Set FIFO tresholds + + // Initialize the DMA control block + byte_count = PCM_BYTE_COUNT(maxcount, freq); + dma_cb->ti = RPI_DMA_TI_NO_WIDE_BURSTS | // 32-bit transfers + RPI_DMA_TI_WAIT_RESP | // wait for write complete + RPI_DMA_TI_DEST_DREQ | // user peripheral flow control + RPI_DMA_TI_PERMAP(2) | // PCM TX peripheral + RPI_DMA_TI_SRC_INC; // Increment src addr + + dma_cb->source_ad = addr_to_bus(device, device->pxl_raw); + dma_cb->dest_ad = (uintptr_t)&((pcm_t *)PCM_PERIPH_PHYS)->fifo; + dma_cb->txfr_len = byte_count; + dma_cb->stride = 0; + dma_cb->nextconbk = 0; + + dma->cs = 0; + dma->txfr_len = 0; + + return 0; +} + +/** + * Start the DMA feeding the PWM FIFO. This will stream the entire DMA buffer out of both + * PWM channels. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +static void dma_start(ws2811_t *ws2811) +{ + ws2811_device_t *device = ws2811->device; + volatile dma_t *dma = device->dma; + volatile pcm_t *pcm = device->pcm; + uint32_t dma_cb_addr = device->dma_cb_addr; + + dma->cs = RPI_DMA_CS_RESET; + usleep(10); + + dma->cs = RPI_DMA_CS_INT | RPI_DMA_CS_END; + usleep(10); + + dma->conblk_ad = dma_cb_addr; + dma->debug = 7; // clear debug error flags + dma->cs = RPI_DMA_CS_WAIT_OUTSTANDING_WRITES | + RPI_DMA_CS_PANIC_PRIORITY(15) | + RPI_DMA_CS_PRIORITY(15) | + RPI_DMA_CS_ACTIVE; + + if (device->driver_mode == PCM) + { + pcm->cs |= RPI_PCM_CS_TXON; // Start transmission + } +} + +/** + * Initialize the application selected GPIO pins for PWM/PCM operation. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns 0 on success, -1 on unsupported pin + */ +static int gpio_init(ws2811_t *ws2811) +{ + volatile gpio_t *gpio = ws2811->device->gpio; + int chan; + int altnum; + + for (chan = 0; chan < RPI_PWM_CHANNELS; chan++) + { + int pinnum = ws2811->channel[chan].gpionum; + + if (pinnum) + { + switch (ws2811->device->driver_mode) + { + case PWM: + altnum = pwm_pin_alt(chan, pinnum); + break; + case PCM: + altnum = pcm_pin_alt(PCMFUN_DOUT, pinnum); + break; + default: + altnum = -1; + } + + if (altnum < 0) + { + return -1; + } + + gpio_function_set(gpio, pinnum, altnum); + } + } + + return 0; +} + +/** + * Initialize the PWM DMA buffer with all zeros, inverted operation will be + * handled by hardware. The DMA buffer length is assumed to be a word + * multiple. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +void pwm_raw_init(ws2811_t *ws2811) +{ + volatile uint32_t *pxl_raw = (uint32_t *)ws2811->device->pxl_raw; + int maxcount = ws2811->device->max_count; + int wordcount = (PWM_BYTE_COUNT(maxcount, ws2811->freq) / sizeof(uint32_t)) / + RPI_PWM_CHANNELS; + int chan; + + for (chan = 0; chan < RPI_PWM_CHANNELS; chan++) + { + int i, wordpos = chan; + + for (i = 0; i < wordcount; i++) + { + pxl_raw[wordpos] = 0x0; + wordpos += 2; + } + } +} + +/** + * Initialize the PCM DMA buffer with all zeros. + * The DMA buffer length is assumed to be a word multiple. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +void pcm_raw_init(ws2811_t *ws2811) +{ + volatile uint32_t *pxl_raw = (uint32_t *)ws2811->device->pxl_raw; + int maxcount = ws2811->device->max_count; + int wordcount = PCM_BYTE_COUNT(maxcount, ws2811->freq) / sizeof(uint32_t); + int i; + + for (i = 0; i < wordcount; i++) + { + pxl_raw[i] = 0x0; + } +} + +/** + * Cleanup previously allocated device memory and buffers. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +void ws2811_cleanup(ws2811_t *ws2811) +{ + ws2811_device_t *device = ws2811->device; + int chan; + + for (chan = 0; chan < RPI_PWM_CHANNELS; chan++) + { + if (ws2811->channel && ws2811->channel[chan].leds) + { + free(ws2811->channel[chan].leds); + } + ws2811->channel[chan].leds = NULL; + if (ws2811->channel && ws2811->channel[chan].gamma) + { + free(ws2811->channel[chan].gamma); + } + ws2811->channel[chan].gamma = NULL; + } + + if (device->mbox.handle != -1) + { + videocore_mbox_t *mbox = &device->mbox; + + unmapmem(mbox->virt_addr, mbox->size); + mem_unlock(mbox->handle, mbox->mem_ref); + mem_free(mbox->handle, mbox->mem_ref); + mbox_close(mbox->handle); + + mbox->handle = -1; + } + + if (device && (device->spi_fd > 0)) + { + close(device->spi_fd); + } + + if (device) { + free(device); + } + ws2811->device = NULL; +} + +static int set_driver_mode(ws2811_t *ws2811, int gpionum) +{ + int gpionum2; + + if (gpionum == 18 || gpionum == 12) { + ws2811->device->driver_mode = PWM; + // Check gpio for PWM1 (2nd channel) is OK if used + gpionum2 = ws2811->channel[1].gpionum; + if (gpionum2 == 0 || gpionum2 == 13 || gpionum2 == 19) { + return 0; + } + } + else if (gpionum == 21 || gpionum == 31) { + ws2811->device->driver_mode = PCM; + } + else if (gpionum == 10) { + ws2811->device->driver_mode = SPI; + } + else { + fprintf(stderr, "gpionum %d not allowed\n", gpionum); + return -1; + } + // For PCM and SPI zero the 2nd channel + memset(&ws2811->channel[1], 0, sizeof(ws2811_channel_t)); + + return 0; +} + +static int check_hwver_and_gpionum(ws2811_t *ws2811) +{ + const rpi_hw_t *rpi_hw; + int hwver, gpionum; + int gpionums_B1[] = { 10, 18, 21 }; + int gpionums_B2[] = { 10, 18, 31 }; + int gpionums_40p[] = { 10, 12, 18, 21}; + int i; + + rpi_hw = ws2811->rpi_hw; + hwver = rpi_hw->hwver & 0x0000ffff; + gpionum = ws2811->channel[0].gpionum; + if (hwver < 0x0004) // Model B Rev 1 + { + for ( i = 0; i < (int)(sizeof(gpionums_B1) / sizeof(gpionums_B1[0])); i++) + { + if (gpionums_B1[i] == gpionum) { + // Set driver mode (PWM, PCM, or SPI) + return set_driver_mode(ws2811, gpionum); + } + } + } + else if (hwver >= 0x0004 && hwver <= 0x000f) // Models B Rev2, A + { + for ( i = 0; i < (int)(sizeof(gpionums_B2) / sizeof(gpionums_B2[0])); i++) + { + if (gpionums_B2[i] == gpionum) { + // Set driver mode (PWM, PCM, or SPI) + return set_driver_mode(ws2811, gpionum); + } + } + } + else if (hwver >= 0x010) // Models B+, A+, 2B, 3B, Zero Zero-W + { + if ((ws2811->channel[0].count == 0) && (ws2811->channel[1].count > 0)) + { + // Special case: nothing in channel 0, channel 1 only PWM1 allowed + // PWM1 only available on 40 pin GPIO interface + gpionum = ws2811->channel[1].gpionum; + if ((gpionum == 13) || (gpionum == 19)) + { + ws2811->device->driver_mode = PWM; + return 0; + } + else { + return -1; + } + } + for ( i = 0; i < (int)(sizeof(gpionums_40p) / sizeof(gpionums_40p[0])); i++) + { + if (gpionums_40p[i] == gpionum) { + // Set driver mode (PWM, PCM, or SPI) + return set_driver_mode(ws2811, gpionum); + } + } + } + fprintf(stderr, "Gpio %d is illegal for LED channel 0\n", gpionum); + return -1; +} + +static ws2811_return_t spi_init(ws2811_t *ws2811) +{ + int spi_fd; + static uint8_t mode; + static uint8_t bits = 8; + uint32_t speed = ws2811->freq * 3; + ws2811_device_t *device = ws2811->device; + uint32_t base = ws2811->rpi_hw->periph_base; + int pinnum = ws2811->channel[0].gpionum; + + spi_fd = open("/dev/spidev0.0", O_RDWR); + if (spi_fd < 0) { + fprintf(stderr, "Cannot open /dev/spidev0.0. spi_bcm2835 module not loaded?\n"); + return WS2811_ERROR_SPI_SETUP; + } + device->spi_fd = spi_fd; + + // SPI mode + if (ioctl(spi_fd, SPI_IOC_WR_MODE, &mode) < 0) + { + return WS2811_ERROR_SPI_SETUP; + } + if (ioctl(spi_fd, SPI_IOC_RD_MODE, &mode) < 0) + { + return WS2811_ERROR_SPI_SETUP; + } + + // Bits per word + if (ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) + { + return WS2811_ERROR_SPI_SETUP; + } + if (ioctl(spi_fd, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0) + { + return WS2811_ERROR_SPI_SETUP; + } + + // Max speed Hz + if (ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) + { + return WS2811_ERROR_SPI_SETUP; + } + if (ioctl(spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0) + { + return WS2811_ERROR_SPI_SETUP; + } + + // Initialize device structure elements to not used + // except driver_mode, spi_fd and max_count (already defined when spi_init called) + device->pxl_raw = NULL; + device->dma = NULL; + device->pwm = NULL; + device->pcm = NULL; + device->dma_cb = NULL; + device->dma_cb_addr = 0; + device->cm_clk = NULL; + device->mbox.handle = -1; + + // Set SPI-MOSI pin + device->gpio = mapmem(GPIO_OFFSET + base, sizeof(gpio_t), DEV_GPIOMEM); + if (!device->gpio) + { + return WS2811_ERROR_SPI_SETUP; + } + gpio_function_set(device->gpio, pinnum, 0); // SPI-MOSI ALT0 + + // Allocate LED buffer + ws2811_channel_t *channel = &ws2811->channel[0]; + channel->leds = malloc(sizeof(ws2811_led_t) * channel->count); + if (!channel->leds) + { + ws2811_cleanup(ws2811); + return WS2811_ERROR_OUT_OF_MEMORY; + } + memset(channel->leds, 0, sizeof(ws2811_led_t) * channel->count); + if (!channel->strip_type) + { + channel->strip_type=WS2811_STRIP_RGB; + } + + // Set default uncorrected gamma table + if (!channel->gamma) + { + channel->gamma = malloc(sizeof(uint8_t) * 256); + int x; + for(x = 0; x < 256; x++){ + channel->gamma[x] = x; + } + } + + channel->wshift = (channel->strip_type >> 24) & 0xff; + channel->rshift = (channel->strip_type >> 16) & 0xff; + channel->gshift = (channel->strip_type >> 8) & 0xff; + channel->bshift = (channel->strip_type >> 0) & 0xff; + + // Allocate SPI transmit buffer (same size as PCM) + device->pxl_raw = malloc(PCM_BYTE_COUNT(device->max_count, ws2811->freq)); + if (device->pxl_raw == NULL) + { + ws2811_cleanup(ws2811); + return WS2811_ERROR_OUT_OF_MEMORY; + } + pcm_raw_init(ws2811); + + return WS2811_SUCCESS; +} + +static ws2811_return_t spi_transfer(ws2811_t *ws2811) +{ + int ret; + struct spi_ioc_transfer tr; + + memset(&tr, 0, sizeof(struct spi_ioc_transfer)); + tr.tx_buf = (unsigned long)ws2811->device->pxl_raw; + tr.rx_buf = 0; + tr.len = PCM_BYTE_COUNT(ws2811->device->max_count, ws2811->freq); + + ret = ioctl(ws2811->device->spi_fd, SPI_IOC_MESSAGE(1), &tr); + if (ret < 1) + { + fprintf(stderr, "Can't send spi message"); + return WS2811_ERROR_SPI_TRANSFER; + } + + return WS2811_SUCCESS; +} + + +/* + * + * Application API Functions + * + */ + + +/** + * Allocate and initialize memory, buffers, pages, PWM, DMA, and GPIO. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns 0 on success, -1 otherwise. + */ +ws2811_return_t ws2811_init(ws2811_t *ws2811) +{ + ws2811_device_t *device; + const rpi_hw_t *rpi_hw; + int chan; + + ws2811->rpi_hw = rpi_hw_detect(); + if (!ws2811->rpi_hw) + { + return WS2811_ERROR_HW_NOT_SUPPORTED; + } + rpi_hw = ws2811->rpi_hw; + + ws2811->device = malloc(sizeof(*ws2811->device)); + if (!ws2811->device) + { + return WS2811_ERROR_OUT_OF_MEMORY; + } + memset(ws2811->device, 0, sizeof(*ws2811->device)); + device = ws2811->device; + + if (check_hwver_and_gpionum(ws2811) < 0) + { + return WS2811_ERROR_ILLEGAL_GPIO; + } + + device->max_count = max_channel_led_count(ws2811); + + if (device->driver_mode == SPI) { + return spi_init(ws2811); + } + + // Determine how much physical memory we need for DMA + switch (device->driver_mode) { + case PWM: + device->mbox.size = PWM_BYTE_COUNT(device->max_count, ws2811->freq) + + sizeof(dma_cb_t); + break; + + case PCM: + device->mbox.size = PCM_BYTE_COUNT(device->max_count, ws2811->freq) + + sizeof(dma_cb_t); + break; + } + // Round up to page size multiple + device->mbox.size = (device->mbox.size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); + + device->mbox.handle = mbox_open(); + if (device->mbox.handle == -1) + { + return WS2811_ERROR_MAILBOX_DEVICE; + } + + device->mbox.mem_ref = mem_alloc(device->mbox.handle, device->mbox.size, PAGE_SIZE, + rpi_hw->videocore_base == 0x40000000 ? 0xC : 0x4); + if (device->mbox.mem_ref == 0) + { + return WS2811_ERROR_OUT_OF_MEMORY; + } + + device->mbox.bus_addr = mem_lock(device->mbox.handle, device->mbox.mem_ref); + if (device->mbox.bus_addr == (uint32_t) ~0UL) + { + mem_free(device->mbox.handle, device->mbox.size); + return WS2811_ERROR_MEM_LOCK; + } + + device->mbox.virt_addr = mapmem(BUS_TO_PHYS(device->mbox.bus_addr), device->mbox.size, DEV_MEM); + if (!device->mbox.virt_addr) + { + mem_unlock(device->mbox.handle, device->mbox.mem_ref); + mem_free(device->mbox.handle, device->mbox.size); + + ws2811_cleanup(ws2811); + return WS2811_ERROR_MMAP; + } + + // Initialize all pointers to NULL. Any non-NULL pointers will be freed on cleanup. + device->pxl_raw = NULL; + device->dma_cb = NULL; + for (chan = 0; chan < RPI_PWM_CHANNELS; chan++) + { + ws2811->channel[chan].leds = NULL; + } + + // Allocate the LED buffers + for (chan = 0; chan < RPI_PWM_CHANNELS; chan++) + { + ws2811_channel_t *channel = &ws2811->channel[chan]; + + channel->leds = malloc(sizeof(ws2811_led_t) * channel->count); + if (!channel->leds) + { + ws2811_cleanup(ws2811); + return WS2811_ERROR_OUT_OF_MEMORY; + } + + memset(channel->leds, 0, sizeof(ws2811_led_t) * channel->count); + + if (!channel->strip_type) + { + channel->strip_type=WS2811_STRIP_RGB; + } + + // Set default uncorrected gamma table + if (!channel->gamma) + { + channel->gamma = malloc(sizeof(uint8_t) * 256); + int x; + for(x = 0; x < 256; x++){ + channel->gamma[x] = x; + } + } + + channel->wshift = (channel->strip_type >> 24) & 0xff; + channel->rshift = (channel->strip_type >> 16) & 0xff; + channel->gshift = (channel->strip_type >> 8) & 0xff; + channel->bshift = (channel->strip_type >> 0) & 0xff; + + } + + device->dma_cb = (dma_cb_t *)device->mbox.virt_addr; + device->pxl_raw = (uint8_t *)device->mbox.virt_addr + sizeof(dma_cb_t); + + switch (device->driver_mode) { + case PWM: + pwm_raw_init(ws2811); + break; + + case PCM: + pcm_raw_init(ws2811); + break; + } + + memset((dma_cb_t *)device->dma_cb, 0, sizeof(dma_cb_t)); + + // Cache the DMA control block bus address + device->dma_cb_addr = addr_to_bus(device, device->dma_cb); + + // Map the physical registers into userspace + if (map_registers(ws2811)) + { + ws2811_cleanup(ws2811); + return WS2811_ERROR_MAP_REGISTERS; + } + + // Initialize the GPIO pins + if (gpio_init(ws2811)) + { + unmap_registers(ws2811); + ws2811_cleanup(ws2811); + return WS2811_ERROR_GPIO_INIT; + } + + switch (device->driver_mode) { + case PWM: + // Setup the PWM, clocks, and DMA + if (setup_pwm(ws2811)) + { + unmap_registers(ws2811); + ws2811_cleanup(ws2811); + return WS2811_ERROR_PWM_SETUP; + } + break; + case PCM: + // Setup the PCM, clock, and DMA + if (setup_pcm(ws2811)) + { + unmap_registers(ws2811); + ws2811_cleanup(ws2811); + return WS2811_ERROR_PCM_SETUP; + } + break; + } + + return WS2811_SUCCESS; +} + +/** + * Shut down DMA, PWM, and cleanup memory. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +void ws2811_fini(ws2811_t *ws2811) +{ + volatile pcm_t *pcm = ws2811->device->pcm; + + ws2811_wait(ws2811); + switch (ws2811->device->driver_mode) { + case PWM: + stop_pwm(ws2811); + break; + case PCM: + while (!(pcm->cs & RPI_PCM_CS_TXE)) ; // Wait till TX FIFO is empty + stop_pcm(ws2811); + break; + } + + unmap_registers(ws2811); + + ws2811_cleanup(ws2811); +} + +/** + * Wait for any executing DMA operation to complete before returning. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns 0 on success, -1 on DMA competion error + */ +ws2811_return_t ws2811_wait(ws2811_t *ws2811) +{ + volatile dma_t *dma = ws2811->device->dma; + + if (ws2811->device->driver_mode == SPI) // Nothing to do for SPI + { + return WS2811_SUCCESS; + } + + while ((dma->cs & RPI_DMA_CS_ACTIVE) && + !(dma->cs & RPI_DMA_CS_ERROR)) + { + usleep(10); + } + + if (dma->cs & RPI_DMA_CS_ERROR) + { + fprintf(stderr, "DMA Error: %08x\n", dma->debug); + return WS2811_ERROR_DMA; + } + + return WS2811_SUCCESS; +} + +/** + * Render the DMA buffer from the user supplied LED arrays and start the DMA + * controller. This will update all LEDs on both PWM channels. + * + * @param ws2811 ws2811 instance pointer. + * + * @returns None + */ +ws2811_return_t ws2811_render(ws2811_t *ws2811) +{ + volatile uint8_t *pxl_raw = ws2811->device->pxl_raw; + int driver_mode = ws2811->device->driver_mode; + int bitpos; + int i, k, l, chan; + unsigned j; + ws2811_return_t ret = WS2811_SUCCESS; + uint32_t protocol_time = 0; + static uint64_t previous_timestamp = 0; + + bitpos = (driver_mode == SPI ? 7 : 31); + + for (chan = 0; chan < RPI_PWM_CHANNELS; chan++) // Channel + { + ws2811_channel_t *channel = &ws2811->channel[chan]; + + int wordpos = chan; // PWM & PCM + int bytepos = 0; // SPI + const int scale = (channel->brightness & 0xff) + 1; + uint8_t array_size = 3; // Assume 3 color LEDs, RGB + + // If our shift mask includes the highest nibble, then we have 4 LEDs, RBGW. + if (channel->strip_type & SK6812_SHIFT_WMASK) + { + array_size = 4; + } + + // 1.25µs per bit + const uint32_t channel_protocol_time = channel->count * array_size * 8 * 1.25; + + // Only using the channel which takes the longest as both run in parallel + if (channel_protocol_time > protocol_time) + { + protocol_time = channel_protocol_time; + } + + for (i = 0; i < channel->count; i++) // Led + { + uint8_t color[] = + { + channel->gamma[(((channel->leds[i] >> channel->rshift) & 0xff) * scale) >> 8], // red + channel->gamma[(((channel->leds[i] >> channel->gshift) & 0xff) * scale) >> 8], // green + channel->gamma[(((channel->leds[i] >> channel->bshift) & 0xff) * scale) >> 8], // blue + channel->gamma[(((channel->leds[i] >> channel->wshift) & 0xff) * scale) >> 8], // white + }; + + for (j = 0; j < array_size; j++) // Color + { + for (k = 7; k >= 0; k--) // Bit + { + // Inversion is handled by hardware for PWM, otherwise by software here + uint8_t symbol = SYMBOL_LOW; + if ((driver_mode != PWM) && channel->invert) symbol = SYMBOL_LOW_INV; + + if (color[j] & (1 << k)) + { + symbol = SYMBOL_HIGH; + if ((driver_mode != PWM) && channel->invert) symbol = SYMBOL_HIGH_INV; + } + + for (l = 2; l >= 0; l--) // Symbol + { + uint32_t *wordptr = &((uint32_t *)pxl_raw)[wordpos]; // PWM & PCM + volatile uint8_t *byteptr = &pxl_raw[bytepos]; // SPI + + if (driver_mode == SPI) + { + *byteptr &= ~(1 << bitpos); + if (symbol & (1 << l)) + { + *byteptr |= (1 << bitpos); + } + } + else // PWM & PCM + { + *wordptr &= ~(1 << bitpos); + if (symbol & (1 << l)) + { + *wordptr |= (1 << bitpos); + } + } + + bitpos--; + if (bitpos < 0) + { + if (driver_mode == SPI) + { + bytepos++; + bitpos = 7; + } + else // PWM & PCM + { + // Every other word is on the same channel for PWM + wordpos += (driver_mode == PWM ? 2 : 1); + bitpos = 31; + } + } + } + } + } + } + } + + // Wait for any previous DMA operation to complete. + if ((ret = ws2811_wait(ws2811)) != WS2811_SUCCESS) + { + return ret; + } + + if (ws2811->render_wait_time != 0) { + const uint64_t current_timestamp = get_microsecond_timestamp(); + uint64_t time_diff = current_timestamp - previous_timestamp; + + if (ws2811->render_wait_time > time_diff) { + usleep(ws2811->render_wait_time - time_diff); + } + } + + if (driver_mode != SPI) + { + dma_start(ws2811); + } + else + { + ret = spi_transfer(ws2811); + } + + // LED_RESET_WAIT_TIME is added to allow enough time for the reset to occur. + previous_timestamp = get_microsecond_timestamp(); + ws2811->render_wait_time = protocol_time + LED_RESET_WAIT_TIME; + + return ret; +} + +const char * ws2811_get_return_t_str(const ws2811_return_t state) +{ + const int index = -state; + static const char * const ret_state_str[] = { WS2811_RETURN_STATES(WS2811_RETURN_STATES_STRING) }; + + if (index < (int)(sizeof(ret_state_str) / sizeof(ret_state_str[0]))) + { + return ret_state_str[index]; + } + + return ""; +} diff --git a/ws2811.h b/ws2811.h new file mode 100644 index 0000000..cf1f0d8 --- /dev/null +++ b/ws2811.h @@ -0,0 +1,130 @@ +/* + * ws2811.h + * + * Copyright (c) 2014 Jeremy Garff + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the owner nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef __WS2811_H__ +#define __WS2811_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "rpihw.h" +#include "pwm.h" + + +#define WS2811_TARGET_FREQ 800000 // Can go as low as 400000 + +// 4 color R, G, B and W ordering +#define SK6812_STRIP_RGBW 0x18100800 +#define SK6812_STRIP_RBGW 0x18100008 +#define SK6812_STRIP_GRBW 0x18081000 +#define SK6812_STRIP_GBRW 0x18080010 +#define SK6812_STRIP_BRGW 0x18001008 +#define SK6812_STRIP_BGRW 0x18000810 +#define SK6812_SHIFT_WMASK 0xf0000000 + +// 3 color R, G and B ordering +#define WS2811_STRIP_RGB 0x00100800 +#define WS2811_STRIP_RBG 0x00100008 +#define WS2811_STRIP_GRB 0x00081000 +#define WS2811_STRIP_GBR 0x00080010 +#define WS2811_STRIP_BRG 0x00001008 +#define WS2811_STRIP_BGR 0x00000810 + +// predefined fixed LED types +#define WS2812_STRIP WS2811_STRIP_GRB +#define SK6812_STRIP WS2811_STRIP_GRB +#define SK6812W_STRIP SK6812_STRIP_GRBW + +struct ws2811_device; + +typedef uint32_t ws2811_led_t; //< 0xWWRRGGBB +typedef struct ws2811_channel_t +{ + int gpionum; //< GPIO Pin with PWM alternate function, 0 if unused + int invert; //< Invert output signal + int count; //< Number of LEDs, 0 if channel is unused + int strip_type; //< Strip color layout -- one of WS2811_STRIP_xxx constants + ws2811_led_t *leds; //< LED buffers, allocated by driver based on count + uint8_t brightness; //< Brightness value between 0 and 255 + uint8_t wshift; //< White shift value + uint8_t rshift; //< Red shift value + uint8_t gshift; //< Green shift value + uint8_t bshift; //< Blue shift value + uint8_t *gamma; //< Gamma correction table +} ws2811_channel_t; + +typedef struct ws2811_t +{ + uint64_t render_wait_time; //< time in µs before the next render can run + struct ws2811_device *device; //< Private data for driver use + const rpi_hw_t *rpi_hw; //< RPI Hardware Information + uint32_t freq; //< Required output frequency + int dmanum; //< DMA number _not_ already in use + ws2811_channel_t channel[RPI_PWM_CHANNELS]; +} ws2811_t; + +#define WS2811_RETURN_STATES(X) \ + X(0, WS2811_SUCCESS, "Success"), \ + X(-1, WS2811_ERROR_GENERIC, "Generic failure"), \ + X(-2, WS2811_ERROR_OUT_OF_MEMORY, "Out of memory"), \ + X(-3, WS2811_ERROR_HW_NOT_SUPPORTED, "Hardware revision is not supported"), \ + X(-4, WS2811_ERROR_MEM_LOCK, "Memory lock failed"), \ + X(-5, WS2811_ERROR_MMAP, "mmap() failed"), \ + X(-6, WS2811_ERROR_MAP_REGISTERS, "Unable to map registers into userspace"), \ + X(-7, WS2811_ERROR_GPIO_INIT, "Unable to initialize GPIO"), \ + X(-8, WS2811_ERROR_PWM_SETUP, "Unable to initialize PWM"), \ + X(-9, WS2811_ERROR_MAILBOX_DEVICE, "Failed to create mailbox device"), \ + X(-10, WS2811_ERROR_DMA, "DMA error"), \ + X(-11, WS2811_ERROR_ILLEGAL_GPIO, "Selected GPIO not possible"), \ + X(-12, WS2811_ERROR_PCM_SETUP, "Unable to initialize PCM"), \ + X(-13, WS2811_ERROR_SPI_SETUP, "Unable to initialize SPI"), \ + X(-14, WS2811_ERROR_SPI_TRANSFER, "SPI transfer error") \ + +#define WS2811_RETURN_STATES_ENUM(state, name, str) name = state +#define WS2811_RETURN_STATES_STRING(state, name, str) str + +typedef enum { + WS2811_RETURN_STATES(WS2811_RETURN_STATES_ENUM), + + WS2811_RETURN_STATE_COUNT +} ws2811_return_t; + +ws2811_return_t ws2811_init(ws2811_t *ws2811); //< Initialize buffers/hardware +void ws2811_fini(ws2811_t *ws2811); //< Tear it all down +ws2811_return_t ws2811_render(ws2811_t *ws2811); //< Send LEDs off to hardware +ws2811_return_t ws2811_wait(ws2811_t *ws2811); //< Wait for DMA completion +const char * ws2811_get_return_t_str(const ws2811_return_t state); //< Get string representation of the given return state + +#ifdef __cplusplus +} +#endif + +#endif /* __WS2811_H__ */