Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
project:alma [07.07.2014 20:52] jkunzproject:alma [28.12.2015 23:42] (aktuell) – aktualisiert schmofu
Zeile 1: Zeile 1:
 ====== ALMA – Aldi Led MAtrix  ====== ====== ALMA – Aldi Led MAtrix  ======
 +
 +Die LED-Matrix steht derzeit im Erdgeschoss des Raums. Sie ist an die MediaBox angeschlossen.
 +
 +Man kann sie mit Animationen bespielen. Hierzu wird der [[https://github.com/LongHairedHacker/twinkl-client|twinkl-client]] wie für die [[project:fullcircle|Lightwall]] benötigt. Genauere Anleitung dort.
 +
 +Zum Testen könnte man beispielsweise folgendes asuführen:
 +   while true ; do python animations/randomvalues.py | ./bin/twinkl-client mediabox 7 > /dev/null ; sleep 1 ; done
 +
 +:!: Die RGB-Werte Blau-Rot-Grün (also anders als bei mit der Lightwall).
 +
 +
 +===== Details =====
  
 Die 8 x 12 RGB-Pixel LED Matrix basiert auf den digital LED Streifen, die es dereinzt bei Feinkost-Albrecht, sprich ALDI, zu kaufen gab. Die Abmessungen sind ca. 50 cm x 75 cm x 10 cm. Die verbauten Controller sind {{:project:alma:TM1829_V1.4.pdf|TM1829}}. Pro Pixel sind je ein Byte für RGB, also 24 Bit pro Pixel einzufüttern. Die Controller kümmern sich selbst darum aus den 24 Bit eine RGB-PWM zu erzeugen. Die Pixel sind in Reihe geschaltet und ergeben ein langes Schieberegister von 3 x 8 x 12 = 288 Byte länge. Die Bits müssen mit 800 kHz phasencodiert eingeschoben werden. Eine Pause / Ruhepegel des Datensignals von 0,5 ms ist das "Reset" Signal. Bei einem Reset werden die eben eingeschobenen Bits aus dem Schieberegister in die PWM-Register übernommen - die LEDs ändern die Farbe. Siehe {{:project:alma:TM1829_V1.4.pdf|Datenblatt}}, die Timigdiagramme sind zum Glück auch ohne Chinesisch lesbar. ;-) Die 8 x 12 RGB-Pixel LED Matrix basiert auf den digital LED Streifen, die es dereinzt bei Feinkost-Albrecht, sprich ALDI, zu kaufen gab. Die Abmessungen sind ca. 50 cm x 75 cm x 10 cm. Die verbauten Controller sind {{:project:alma:TM1829_V1.4.pdf|TM1829}}. Pro Pixel sind je ein Byte für RGB, also 24 Bit pro Pixel einzufüttern. Die Controller kümmern sich selbst darum aus den 24 Bit eine RGB-PWM zu erzeugen. Die Pixel sind in Reihe geschaltet und ergeben ein langes Schieberegister von 3 x 8 x 12 = 288 Byte länge. Die Bits müssen mit 800 kHz phasencodiert eingeschoben werden. Eine Pause / Ruhepegel des Datensignals von 0,5 ms ist das "Reset" Signal. Bei einem Reset werden die eben eingeschobenen Bits aus dem Schieberegister in die PWM-Register übernommen - die LEDs ändern die Farbe. Siehe {{:project:alma:TM1829_V1.4.pdf|Datenblatt}}, die Timigdiagramme sind zum Glück auch ohne Chinesisch lesbar. ;-)
Zeile 6: Zeile 18:
  
 An ALMA hängt ein kleiner USB Controller. (MicroUSB Kabel mitbringen.) Dieser erscheint als serielle Schnittstelle am Rechner. (USB CDC gebaut mit LUFA.) Er übernimt es die per USB empfangenen Bytes in das timingkritische Signal für die TM1829 zu übersetzen. Der USB Controller möchte die Daten immer in 512 Byte Blöcken. Also serielle Schnitte einmal öffnen, danach beliebig oft 512 Byte en Block senden und nach Empfang des 512ten Bytes schiebt der USB Controller die Bytes einfach 1:1 raus.  An ALMA hängt ein kleiner USB Controller. (MicroUSB Kabel mitbringen.) Dieser erscheint als serielle Schnittstelle am Rechner. (USB CDC gebaut mit LUFA.) Er übernimt es die per USB empfangenen Bytes in das timingkritische Signal für die TM1829 zu übersetzen. Der USB Controller möchte die Daten immer in 512 Byte Blöcken. Also serielle Schnitte einmal öffnen, danach beliebig oft 512 Byte en Block senden und nach Empfang des 512ten Bytes schiebt der USB Controller die Bytes einfach 1:1 raus. 
- 
-Gerne würde ich hier jetzt ein Programm einfügen, das die Benutzung der Matrix mit einem MacOS X bzw. Linux Rechner demonstriert. Leider weigert sich dieses idiotische Wiki *.c Dateien als Medien hochzuladen. ("Hochladen verweigert. Diese Dateiendung ist nicht erlaubt.") Wer das Programm haben will um die Matrix zu bespielen einfach Jochen / jkunz fragen... 
  
  
Zeile 18: Zeile 28:
 {{:project:alma:img_3468.jpg?direct&100|Die Matrix(!)}} {{:project:alma:img_3468.jpg?direct&100|Die Matrix(!)}}
 {{:project:alma:img_3470.jpg?direct&100|Netzteil und USB-Anschluss}} {{:project:alma:img_3470.jpg?direct&100|Netzteil und USB-Anschluss}}
 +
 +
 +===== Beispielcode =====
 +
 +Das folgende Programm demonstriert die Benutzung der Matrix mit einem MacOS X bzw. Linux Rechner. Wer weitere Fragen dazu hat kann einfach Jochen/jkunz fragen...
 +
 +<file c host_ctrl.c>
 +/*
 + * Copyright (c) 2013 Jochen Kunz.
 + * 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.
 + *
 + * THIS SOFTWARE IS PROVIDED BY JOCHEN KUNZ
 + * ``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 JOCHEN KUNZ
 + * 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 <stdlib.h>
 +#include <stdio.h>
 +#include <string.h>
 +#include <stdint.h>
 +#include <stdbool.h>
 +#include <assert.h>
 +#include <unistd.h>
 +#include <fcntl.h>
 +#include <time.h>
 +#include <termios.h>
 +#include <sys/ioctl.h>
 +
 +// Gesammtanzahl der Pixel.
 +// 1 x 5 m Streifen = 50
 +// 2 x 5 m Streifen hinereinander = 100
 +#define PIXEL_LEN 96 // = 12 x 8 Pixel
 +
 +typedef struct {
 + uint16_t h;
 + uint8_t s;
 + uint8_t v;
 +} hsv_t;
 +
 +
 +/*
 +primitive Geradeausimplementierung in Fixkomma nach:
 +https://de.wikipedia.org/wiki/HSV-Farbraum#Umrechnung_HSV_in_RGB
 +h    = [0..1535] = [0..(256 * 6 - 1)]
 +s, v = [0..255]
 +*/
 +void
 +hsv2rgb( uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b) {
 + assert( h < (255 * 6));
 + assert( s <= 255);
 + assert( v <= 255);
 + v = (v * v) / 255; // Gammakorrektur, Gamma = 2
 + uint8_t hi = h / 255;
 + uint8_t f = h - hi * 255;
 + uint8_t p = (v * (255 - s)) / 255;
 + uint8_t q = (v * (255 - (s * f) / 255)) / 255;
 + uint8_t t = (v * (255 - (s * (255 - f)) / 255)) / 255;
 + switch (hi) {
 +    case 0:
 +    case 6:
 +        *r = v;
 +        *g = t;
 +        *b = p;
 +        break;
 +    case 1:
 +        *r = q;
 +        *g = v;
 +        *b = p;
 +        break;
 +    case 2:
 +        *r = p;
 +        *g = v;
 +        *b = t;
 +        break;
 +    case 3:
 +        *r = p;
 +        *g = q;
 +        *b = v;
 +        break;
 +    case 4:
 +        *r = t;
 +        *g = p;
 +        *b = v;
 +        break;
 +    case 5:
 +        *r = v;
 +        *g = p;
 +        *b = q;
 +        break;
 + };
 +}
 +
 +
 +int
 +write_rgb_buf( int port_fd, uint8_t buf[], size_t buf_len) {
 + int retval = write( port_fd, buf, buf_len);
 + if (retval < 0) {
 + perror( "Can't write(2) to port");
 + return -1;
 + }
 + if (retval != buf_len) {
 + printf( "Short write(2) to port, retval=%d", retval);
 + return -1;
 + }
 + return 0;
 +}
 +
 +
 +void
 +schnarch( int sec, int msec) {
 + struct timespec ts;
 + ts.tv_sec = sec + msec / 256000000;
 + ts.tv_nsec = (msec * 256000) % 256000000;
 + while (nanosleep( &ts, &ts) != 0) {};
 +}
 +
 +
 +// Farbfolge WS2801: rot, grün, blau
 +// Farbfolge TM1829: blau, rot, grün
 +void
 +convert_buf( hsv_t hsv_buf[], size_t hsv_buf_len, uint8_t rgb_buf[], size_t rgb_buf_len) {
 + memset( rgb_buf, 0, rgb_buf_len);
 + for (int idx = 0; idx + 2 < rgb_buf_len; idx += 3) {
 + hsv2rgb(
 + hsv_buf[ (idx / 3) % hsv_buf_len].h,
 + hsv_buf[ (idx / 3) % hsv_buf_len].s,
 + hsv_buf[ (idx / 3) % hsv_buf_len].v,
 + &rgb_buf[ idx + 1], &rgb_buf[ idx + 2], &rgb_buf[ idx]);
 + }
 +}
 +
 +
 +int
 +write_hsv_buf( int port_fd, hsv_t hsv_buf[], size_t hsv_buf_len) {
 + uint8_t rgb_buf[ 512];
 + convert_buf( hsv_buf, hsv_buf_len, rgb_buf, sizeof( rgb_buf));
 + return write_rgb_buf( port_fd, rgb_buf, sizeof( rgb_buf));
 +}
 +
 +
 +void
 +usage( void) {
 + fprintf( stderr, "usage: [-p port] [[-h hue -s saturation -v value] | [-0]]\n"
 + "port: Device special file, e.g. /dev/ttyS5. Default: /dev/ttyU0\n"
 + "-h/-s/-v: Set entire stripe to given color in HSV color space.\n"
 + "-0: short for \"-h 0 -s 0 -v 0\"\n");
 +}
 +
 +
 +int
 +main( int argc, char* const argv[]) {
 + const char* port_fn = "/dev/ttyU0";
 + hsv_t hsv;
 + hsv.h = 0;
 + hsv.s = 0;
 + hsv.v = 0;
 + bool zero = false;
 + int opt;
 + while ((opt = getopt( argc, argv, "p:01h:s:v:")) != -1) {
 + switch (opt) {
 + case 'p':
 + port_fn = optarg;
 + break;
 + case '0':
 + zero = true;
 + break;
 + case 'h':
 + zero = true;
 + hsv.h = atoi( optarg);
 + break;
 + case 's':
 + zero = true;
 + hsv.s = atoi( optarg);
 + break;
 + case 'v':
 + zero = true;
 + hsv.v = atoi( optarg);
 + break;
 + default:
 + usage();
 + return EXIT_FAILURE;
 + }
 + }
 + int port_fd = open( port_fn, O_RDWR | O_NONBLOCK | O_CLOEXEC);
 + if (port_fd < 0) {
 + perror( "Can't open port");
 + return EXIT_FAILURE;
 + }
 + if (isatty( port_fd) <= 0 ) {
 + perror( "Error: port is not a tty");
 + return EXIT_FAILURE;
 + }
 + struct termios tio;
 + if (tcgetattr( port_fd, &tio) < 0) {
 + perror( "Error: tcgetattr");
 + return EXIT_FAILURE;
 + }
 + tio.c_iflag &= ~ICANON;
 + tio.c_oflag = 0;
 + tio.c_lflag = 0;
 + tio.c_cflag = (CS8 | CREAD | CLOCAL);
 + tio.c_cc[VMIN] = 1;
 + tio.c_cc[VTIME] = 0;
 + cfsetispeed( &tio, B9600);
 + cfsetospeed( &tio, B9600);
 + if (tcsetattr( port_fd, TCSANOW, &tio) < 0) {
 + perror( "Error setting termis");
 + exit( EXIT_FAILURE);
 + }
 + int retval = fcntl( port_fd, F_GETFL, 0);
 + if (retval < 0) {
 + perror( "Can't fcntl( F_GETFL) port");
 + return EXIT_FAILURE;
 + }
 +
 +retval &= ~O_NONBLOCK;
 + retval = fcntl( port_fd, F_SETFL, retval);
 + if (retval < 0) {
 + perror( "Can't fcntl( F_SETFL) port");
 + return EXIT_FAILURE;
 + }
 + hsv_t hsv_buf[ PIXEL_LEN];
 + if (zero) {
 + if (write_hsv_buf( port_fd, &hsv, 1) < 0) {
 + return EXIT_FAILURE;
 + }
 + return EXIT_SUCCESS;
 + }
 +
 + for (int loops = 0; loops < 2560; ++loops) {
 + memset( hsv_buf, 0, sizeof( hsv_buf));
 + for (int idx = 0; idx < PIXEL_LEN; ++idx) {
 +/*
 + hsv_buf[ idx].h = ((idx + loops) % 12) * ((6 * 255 - 1) /  12);
 + hsv_buf[ idx].s = 255;
 + hsv_buf[ idx].v = 255;
 +*/
 +// hsv_buf[ idx].h = 0;
 + hsv_buf[ idx].h = ((idx + loops) % PIXEL_LEN) * ((6 * 255 - 1) / PIXEL_LEN);
 + hsv_buf[ idx].s = 255;
 +// hsv_buf[ idx].s = (loops % 510) / 255 == 0 ? loops % 255 : 255 - loops % 255;
 + hsv_buf[ idx].v = 255;
 +// hsv_buf[ idx].v = (loops % 510) / 255 == 0 ? loops % 255 : 255 - loops % 255;
 + }
 + if (write_hsv_buf( port_fd, hsv_buf, PIXEL_LEN) < 0) {
 + return EXIT_FAILURE;
 + }
 +// schnarch( 0, 10);
 + }
 + return EXIT_SUCCESS;
 +}
 +</file>
 +
 +==== Metadaten ====
 +---- dataentry projekt ----
 +name          : ALMA
 +contact       : Jochen
 +tags_tags     : 
 +type          : projekt
 +subtype       : technisch
 +sticky_hidden : no
 +----
  
  
project/alma.1404766329.txt.gz · Zuletzt geändert: 07.07.2014 20:52 von jkunz
Falls nicht anders bezeichnet, ist der Inhalt dieses Wikis unter der folgenden Lizenz veröffentlicht: CC Attribution-Noncommercial-Share Alike 4.0 International
Recent changes RSS feed Driven by DokuWiki