Inhaltsverzeichnis

Beteiligte

Idee

Led-Cube 4x4x4 mit blauen Leds, gesteuert von einem Arduino-Mega.

Im weiteren Verlauf soll der Arduino über RS232 mit Daten versorgt werden.

Aktuell umgesetzt: Sprektrumsanzeige (Mic-In → PulseaudioSample → C-Programm → Python-Script → Seriell an Arduino) → Funktioniert BounceBall Sinuskurve

Umsetzung

Erste Ebene

4 Ebenen

Cube fertig

Verlängerung 16 Leds pro Ebene

Verlängerung 4 Ebenen

Hardware komplett mit Arduino Mega

Hardware komplett mit Arduino Mega

Erster Funktionstest

Version 1 (4x4 Grün) um Grundlagen von Charlieplexing zu verstehen

Probleme

Code

Arduino

Der Cube kann z.B. über einen Arduino angesteuert werden, wofür unten passender Code gepasted ist. Der Code läuft ohne Anpassungen auf folgendem Setup:

LedCube.c
/*
 cube 
 */
 
#define LAYERs 4
#define COLs 4
#define ROWs 4
 
/* global variable definitions */
unsigned char LEDs[LAYERs][COLs * ROWs];
unsigned char pwmpos = 0; // actual step of the PWM
unsigned char layer = 1; // actual controlled layer
 
/*
 * cmp_carry_shift_left compares the two parameters
 * which results in the carry bit to be set/unset
 * that carry bit is rotated into para_io
 * the complete function consumes only 2 cycles
 */
static inline unsigned char cmp_carry_shift_left ( 
	unsigned char para_io,
	unsigned char cmp_para_1,
	unsigned char cmp_para_2 )
{
	asm volatile (
		// compare the parameters
		// (get the carry flag set/unset)
		"cp %r[cmp2], %r[cmp1]"	"\n\t"
 
		// rotate carry bit into paramter
		"rol %r[out]"		"\n\t"
 
		// output operand
		: [out] "+r" (para_io)		
		// input operands
		: [in] "r" (para_io),
		  [cmp1] "r" (cmp_para_1),
		  [cmp2] "r" (cmp_para_2)
	);
	return para_io;
}
 
/*
 * The timer3 overflow interrupt routine builds the pin states
 * to be output. the actual output is done in the beginning
 * (of the next execution) of the routine to achieve a more
 * constant time behaviour
 */
ISR ( SIG_OVERFLOW3 )
{
	static unsigned char porta, portc, portf;
	PORTB = 0x80; // turn on pin B7 for measuring the time consumption
 
	PORTA = porta; // turn on the pins of PORTA and B in resull of
	PORTC = portc; // the previous cycles outcome
	PORTF = portf;
 
	pwmpos++;
	if ( pwmpos >= 128 ) pwmpos = 0;
	if ( pwmpos == 0 )
	{
		layer++;
		if (layer == LAYERs)
		{
			layer = 0;
			PORTB |= 0x10;	// get an impulse on Pin B4 as debug output
			PORTB &= ~0x10;	// -"-
			PORTA = 0;
			PORTC = 0;
		}
		portf = 1 << layer;
		PORTB |= 0x40;		// get an impulse on Pin B6 as debug output
		PORTB &= ~0x40;		// -"-
	}
 
	porta = cmp_carry_shift_left ( porta, LEDs[layer][ 7], pwmpos ); // LED at A7
	porta = cmp_carry_shift_left ( porta, LEDs[layer][ 6], pwmpos ); // LED at A6
	porta = cmp_carry_shift_left ( porta, LEDs[layer][ 5], pwmpos );
	porta = cmp_carry_shift_left ( porta, LEDs[layer][ 4], pwmpos );
	porta = cmp_carry_shift_left ( porta, LEDs[layer][ 3], pwmpos );
	porta = cmp_carry_shift_left ( porta, LEDs[layer][ 2], pwmpos );
	porta = cmp_carry_shift_left ( porta, LEDs[layer][ 1], pwmpos );
	porta = cmp_carry_shift_left ( porta, LEDs[layer][ 0], pwmpos ); // LED at A0
 
	portc = cmp_carry_shift_left ( portc, LEDs[layer][15], pwmpos ); // LED at C7
	portc = cmp_carry_shift_left ( portc, LEDs[layer][14], pwmpos ); // LED at C6
	portc = cmp_carry_shift_left ( portc, LEDs[layer][13], pwmpos );
	portc = cmp_carry_shift_left ( portc, LEDs[layer][12], pwmpos );
	portc = cmp_carry_shift_left ( portc, LEDs[layer][11], pwmpos );
	portc = cmp_carry_shift_left ( portc, LEDs[layer][10], pwmpos );
	portc = cmp_carry_shift_left ( portc, LEDs[layer][ 9], pwmpos );
	portc = cmp_carry_shift_left ( portc, LEDs[layer][ 8], pwmpos ); // LED at C0
 
	PORTB &= ~0x80;
}
 
void serialEvent() {
	static unsigned char RX_pos = 0;
	unsigned char rx_char;
 
	PORTB |= 0x08;	// turn on B3 as debug output
	rx_char = (unsigned char)Serial.read();
	if ( rx_char == 0xFF )
	{ // if FF is rxed, a new buffer is expected
		RX_pos = 0;
	}
	else
	{
		( (unsigned char *) LEDs )[RX_pos] = rx_char % 128;
		RX_pos = ( RX_pos >= LAYERs * COLs * ROWs ) ? 0 : RX_pos + 1;
	}
	PORTB &= ~0x08;	 // turn off B3 as debug output
}
 
void setup()  { 
	unsigned char i;
 
	// Initialize port directions and output values
	DDRA = 0xFF; PORTA = 0xFF; // first 8 LEDs
	DDRC = 0xFF; PORTC = 0xFF; // second 8 LEDs
	DDRF = 0x0F; PORTF = 0x01; // 4 layers
	DDRB = 0xFF; PORTB = 0x00; // debug output LEDs
 
	// Initialize timer
	TCCR3A = (1 << WGM31) | (1 << WGM30) | (0x00 << COM3B0);
	TCCR3B = (0x01 << CS30) | (1 << WGM32) | (1 << WGM33);
	TIMSK3 |= 1 << TOIE3;
	OCR3A = 0xFF; // */
 
	// init LEDs array
	for ( i = 0; i < LAYERs * COLs * ROWs; i++ )
		( (unsigned char *) LEDs )[i] = 1;
 
	// Enable interrupts
	sei();
 
	// listen on RS232 for new data
	Serial.begin(115200);
}
 
void loop()  { 
	PORTB ^= 0x02; // debug output (toggles pin B1 if nothing else is done)
}

PC

Der mit dem Compilat des obigen Codes laufende Arduino macht nichts anderes, als alle LEDs auf der untersten Helligkeitsstufe anzusteuern, solange nichts anderes über die serielle Schnittstelle herreinkommt. Am einfachsten wird der Arduino über ein Script gefüttern, das auf dem Gerät am anderen Ende der USB-Leitung hängt. Das erwartete Datenformat sieht so aus:

Die angehängten Scripte lassen eine Kugelwelle durch den Cube wabern bzw. stellen den unix-timestamp darauf dar.

sine.py
#! /usr/bin/env python
 
from serial import serialposix as ser
import time
import math
import random
 
 
serport = ser.Serial ( port='/dev/ttyACM0', baudrate=115200 )
time.sleep ( 1 ) # wait for the Arduino bootloader to quit
 
def linear ( val ):	return max ( 0, min ( 127, int ( 127 * val**2 ) ) )
def picfunc ( pix, t ): return picfunc_xyz ( pix % 4, (pix / 4) % 4, (pix / 16) % 4, t )
 
def picfunc_xyz ( x, y, z, t ):
	return linear ( math.sin ( 2 * t + (x + y + z)/3. ) ** 2 )
 
 
t = 0.0
try:
	while True:
		time.sleep ( 0.01 )
		t += 0.01
		serport.write ( chr ( 255 ) + ''.join ( map ( chr, [linear ( random.random ( ) ** 256 ) for x in range ( 64 )] ) ) )
except KeyboardInterrupt:
	serport.write ( chr ( 255 ) + ''.join ( map ( chr, [0 for x in range ( 64 )] ) ) )
	serport.close ( )
unix_timestamp.py
#! /usr/bin/env python
 
from serial import serialposix as ser
import time
import math
 
 
serport = ser.Serial ( port='/dev/ttyACM0', baudrate=115200 )
time.sleep ( 1 ) # wait for the Arduino bootloader to quit
 
pix_buffer = [0. for i in range ( 64 )]
def linear ( val ):	return max ( 0, min ( 127, int ( 127 * val**2 ) ) )
def picfunc ( pix, t ):
	pix_buffer[pix] += ( 0.6 * ( 1 & ( int ( time.time ( ) ) >> (63 - pix) ) ) - pix_buffer[pix] ) / 20
	return linear ( pix_buffer[pix] )
 
t = 0.0
try:
	while True:
		time.sleep ( 0.01 )
		t += 0.01
		serport.write ( chr ( 255 ) + ''.join ( map ( chr, [picfunc ( x, t ) for x in range ( 64 )] ) ) )
except KeyboardInterrupt:
	serport.write ( chr ( 255 ) + ''.join ( map ( chr, [0 for x in range ( 64 )] ) ) )
	serport.close ( )