/*
Programm zur Ansteuerung von 64 PWM-Ausgaengen per Schieberegister
C4094, 74HC4094 oder 74HC595 mit einem ATmega48.
Vorgabe der PWM-Werte (0-255) per TWI. Maximal 8 Mega48 koennen zur IIC-
Basisadresse 0x40 adressiert werden: insgesamt 512 PWM-Ausgaenge.

Der interne Takt laeuft mit max. Frequenz von ca 15MHz und erzeugt eine
minimale PWM-Frequenz fuer die Werte 1 und 255. Die maximale PWM-Frequenz
betraegt ca. 8kHz f�r den PWM-Wert 128.

Verwendung der Ports:
PB.0	Strobe zur Datenuebernahme in die Ausgangsregister
PB.1	Output-Enable der Schieberegister
PB.3	SR-Daten
PB.5	SR-Takt

PC.0 - PC.2
	Offset zur IIC-Basisadresse 0x40 in negativer Logik.
	Alle offen (111) ergibt 0x40;
	PC.0 auf GND (110) ergibt 0x42;
	PC.1 auf GND (101) ergibt 0x46;

Michael Nowak, www.mino-elektronik.de, 2011-06

Alle Angaben ohne Gew„hr!
*/

#define MAX_KANAL	64

#include <iom48.h>
#include <ina90.h>	// fuer _WDR() + __enable_interrupt()

#define NACHLADE_WERT_T0	150	// 105 * 8 Takte

#define MSLA		0x40	// my slave address
#define TW_INT		0x80	// INT-flag = 1
#define TW_INIT		0xc4	// clear TWINT, set TWEA + TWEN

#define	MY_ADR_RX	0x60	// got my address + recieve
#define	RX_BYTE_RDY	0x80	// byte recieved + ack
#define	TW_STOP		0xa0	// got stop or repeated start
#define	MY_ADR_TX	0xa8	// got my address + slave transm.
#define TX_BYTE_RDY	0xb8	// byte transmitted + ack.



unsigned char kanal[MAX_KANAL];
unsigned char kanal_summe[MAX_KANAL];


extern void pwm_ausgabe();

void pwm_strobe()	// nicht zu kurz fuer C4094-Register
{
  PORTB |= 1;
  PORTB |= 1;
  PORTB |= 1;
  PORTB |= 1;
  PORTB |= 1;
  PORTB |= 0x03;	// zusaetzlich OE aktivieren
  PORTB &= 0xfe;
}

#pragma vector = TIMER0_OVF_vect
__interrupt  void timer0_comp_int()
{
  TCNT0 = NACHLADE_WERT_T0;
  pwm_strobe();            	// Schreibimpuls zur letzten Ausgabe
  pwm_ausgabe();
//  while(!(SPSR & 0x80));	// unguenstig, braucht nur Wartezeit!

}

void teste_twi()
{
unsigned char tw_status;
static unsigned char kanal_nr, kanal_aktiv;
    if(TWCR & TW_INT) {
      tw_status = TWSR & 0xfc;	// ignore bit1+bit0
      switch(tw_status) {
	case RX_BYTE_RDY:	// 1. Byte ist Kanal-Nr., danach Einzelwerte
		if(!kanal_aktiv) {
		  kanal_nr = TWDR;	// erster Wert == Adresse
		  kanal_aktiv = 1;	// merken
		} else {
		  if(kanal_nr < MAX_KANAL) {
		    kanal[kanal_nr] = TWDR;	// PWM-Werte uebernehmen
		    kanal_nr++;		// auto-increment
		  }
		}
		break;
	case 0:
		TWCR = 0x0c;	// clear slave addressed on bus error
	case TW_STOP:
		kanal_aktiv = 0;// auf neue Adressierung warten
		break;
      }
      TWCR = TW_INIT;		// clear TWINT-flag
    }
}

void init_timer0()
{
  TCNT0 = NACHLADE_WERT_T0;
  TCCR0B = 0x2;		// vorteiler /8
  TIMSK0 |= 0x01;	// overflow_int_enable
  __enable_interrupt();
}

void init_twi()
{
  TWBR = 1;		// dummy: bitrate wird hier nicht festgelegt
  TWAR = MSLA | ((PINC ^7) & 7) << 1; // slave address
  TWCR = TW_INIT;	// clear TWINT, set TWEA+TWEN
}

C_task main()
{
unsigned char i,k;
  CLKPR = 0x80; CLKPR = 0x0;	// Vorteiler ./.1
  OSCCAL = 0xff;	// hoechste interne Taktfrequenz ca. 15MHz
  DDRB = 0x2f;		// Ausgaenge fuer Schieberegister
  PORTC |= 0x07;	// pullups einschalten fuer TWI-Adresse

  init_timer0();
  SPCR = 0x5c;		// SPI master-mode, pos. clock, 1/4 Takt
  init_twi();		// nur empfangen neuer PWM-Werte

  pwm_ausgabe();	// Register auf 0 setzen

  k = 0;		// ungleiche Startwerte verringern den Spitzenstrom
  for(i = 0; i < MAX_KANAL; i++) {
    kanal_summe[i] = k;	
    k += 4;             // 4er Abstand
  }
  pwm_strobe();		// muá verzoegert nach der PWM-Ausgabe kommem !

// testmuster
  kanal[63] = 128;
  kanal[0] = 16;
  kanal[1] = 32;
  kanal[2] = 128;
  kanal[3] = 255;

  do {
    _WDR();		// WD ist default aktiv
    teste_twi();	// neue PWM-Werte einstellen
  } while(1);
}
