Mega 2560

Hi there,
Im very new at all of this and ive built a ambilight setup and saw this project and was wondering if anyone knows how to convert his code to use a Mega 2560?

http://code.google.com/p/unolight/

Thanks for any help

Carl

/*
 * UnoLight - Atmolight Arduino Client for ATmega 328p versions (Tested with ATmega 328p on Arduino UNO R2)
 * Copyright 2011 by Oskar Wicha
 */

/*
  Color |  Arduino Uno Pins for
        | LED1  LED2  LED3  LED4
  BLUE    2     5     8     11 
  GREEN   3     6     9     12 
  RED     4     7     10    13 
 */
 
//#ifndef __AVR_ATmega328P__
//#error Code created for ATmega 328p and Arduino UNO. To compile for another Arduino boards you will need to change register names in source code.
//#endif

#define NUM_LEDS 6           // max number of RGB LEDs (ATmega 328p in Arduino UNO or NANO 3.0 sypports only up to 4 RGB LEDs on 12 output pins)
#define IDLE_TIME_LIMIT 5530 // time (in seconds) without data transmision after which LEDs will by turn off  

#include <avr/io.h>
#include "ledPorts.h"
#include "macros.h"
#include "gammaTable.h"

volatile uint16_t ledChannels[NUM_LEDS][3] = {0};
boolean ledsOff = false;  // flag indicating if LEDs ar OFF or ON
uint8_t incomingAtmo[16]; // buffer array for incomming data over serial

// order is reversed, first uint8_t is last module in the chain
// 0=sum,1=left,2=right,3=top,4=bottom
const uint8_t channelOrder[NUM_LEDS] = {1,2,3,4,0,0};
uint32_t timeOfLastTransmition = millis(); //stores time of last serial transmition

void setup() 
{
  cli();
  DDRD |= 0xFC;      // direction variable for port D - make all pins outputs except serial pins 0 & 1
  DDRB |= 0xFF;      // direction variable for port B - all outputs

  //----------------Initialize Interupts------------------
  initTimer2();

  //----------------Initialize serial connection ---------
  Serial.begin(38400); // Setting serial speed correct for Atmolight
  sei();               // Enable global interrupts
}

void loop()
{
  getAtmoCommand();
}

// TIMER2 Overflow interrupt
ISR(TIMER2_OVF_vect)
{
  static uint16_t pwmCounter;

  volatile uint16_t *ledValPtr = &(ledChannels[0][0]);

  cli();
  //LED 1
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTD,LED1_RED_PIN)   : LEDOFF(PORTD,LED1_RED_PIN);
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTD,LED1_GREEN_PIN) : LEDOFF(PORTD,LED1_GREEN_PIN);
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTD,LED1_BLUE_PIN)  : LEDOFF(PORTD,LED1_BLUE_PIN);

  //LED 2
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTD,LED2_RED_PIN)   : LEDOFF(PORTD,LED2_RED_PIN);
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTD,LED2_GREEN_PIN) : LEDOFF(PORTD,LED2_GREEN_PIN);
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTD,LED2_BLUE_PIN)  : LEDOFF(PORTD,LED2_BLUE_PIN);

  //LED 3
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTB,LED3_RED_PIN)   : LEDOFF(PORTB,LED3_RED_PIN);
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTB,LED3_GREEN_PIN) : LEDOFF(PORTB,LED3_GREEN_PIN);
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTB,LED3_BLUE_PIN)  : LEDOFF(PORTB,LED3_BLUE_PIN);

  //LED 4
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTB,LED4_RED_PIN)   : LEDOFF(PORTB,LED4_RED_PIN);
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTB,LED4_GREEN_PIN) : LEDOFF(PORTB,LED4_GREEN_PIN);
  (*ledValPtr++ >= pwmCounter) ? LEDON(PORTB,LED4_BLUE_PIN)  : LEDOFF(PORTB,LED4_BLUE_PIN);

  (pwmCounter < 1023) ? pwmCounter++ : pwmCounter = 0;

  TCNT2 = 0xff; // necessery for not triggering commperator ISR also sets frequency
  sei();
}

//---------------ADDITIONAL FUNCTIONS----------------------------

inline void initTimer2()
{
  // Initialize TIMER2
  TCCR2B &= ~_BV(WGM22);
  TCCR2A |= _BV(WGM21);
  TCCR2A &= ~_BV(WGM20);   // Fast PWM

  TCCR2B |= _BV(CS22);
  TCCR2B &= ~_BV(CS21);
  TCCR2B &= ~_BV(CS20);   // Timer Prescaler 64

  TIMSK2 |= _BV(TOIE2);   // Enable Overflow Interrupt
  TCNT2 = 0;

  TIMSK1 &= ~_BV(TOIE1);  // turn off Timer1 (not used interrupt)
}

inline void turnOffLEDArray()
{
  // Turn off all LEDs an Timer2 interrupt
  TIMSK2 &= ~_BV(TOIE2);	
  PORTD = 0x00;
  PORTC = 0x00;  
  ledsOff = true;
}

// get LED values sent over serial (Atmolight protocol)
inline void getAtmoCommand()
{
  if( Serial.available() > 18 && Serial.read() == 0xff && Serial.read() == 0x00 && Serial.read() == 0x00 )
  {
    timeOfLastTransmition = millis();

    uint8_t i = 0; 
    while( i < 16 )
      *(incomingAtmo + i++) = Serial.read();

    volatile uint16_t *ledChannelAndColorPointer = &ledChannels[0][0]; 
    cli();
    uint8_t channel = 0;
    while( channel < NUM_LEDS )
    {  
      uint8_t *incomingValuePointer = incomingAtmo + *(channelOrder + channel++) * 3 + 1; 

      *ledChannelAndColorPointer++ = *(gammaTable + *incomingValuePointer++); // red
      *ledChannelAndColorPointer++ = *(gammaTable + *incomingValuePointer++); // green
      *ledChannelAndColorPointer++ = *(gammaTable + *incomingValuePointer);   // blue              
    }
    sei();
    if(ledsOff)
      TIMSK2 |= _BV(TOIE2); // LEDs ON

  }
  else
  {
    // turn off leds if no data for defined period of time 
    if( millis() - timeOfLastTransmition > IDLE_TIME_LIMIT * 1000 )
      turnOffLEDArray();
  }
}

Or could someone point out, why do I get some RGB flickering when I load the below code and run Atomwin??

Cheers

#define RedPin1 2
#define GreenPin1 3
#define BluePin1 4
#define RedPin2 5
#define GreenPin2 6
#define BluePin2 7
#define RedPin3 8
#define GreenPin3 9
#define BluePin3 10
#define RedPin4 11
#define GreenPin4 12
#define BluePin4 13

int SerialBuffer = 0;

int RedSum = 0;
int GreenSum = 0;
int BlueSum = 0;

int RedLeft = 0;
int GreenLeft = 0;
int BlueLeft = 0;

int RedRight = 0;
int GreenRight = 0;
int BlueRight = 0;

int RedTop = 0;
int GreenTop = 0;
int BlueTop = 0;

int RedBottom = 0;
int GreenBottom = 0;
int BlueBottom = 0;

void setup()
{
  Serial.begin(38400);
   pinMode(RedPin1, OUTPUT);
   pinMode(GreenPin1, OUTPUT);
   pinMode(BluePin1, OUTPUT);
   pinMode(RedPin2, OUTPUT);
   pinMode(GreenPin2, OUTPUT);
   pinMode(BluePin2, OUTPUT);
   pinMode(RedPin3, OUTPUT);
   pinMode(GreenPin3, OUTPUT);
   pinMode(BluePin3, OUTPUT);
   pinMode(RedPin4, OUTPUT);
   pinMode(GreenPin4, OUTPUT);
   pinMode(BluePin4, OUTPUT);
}

//packet structure: FF 00 00 0F [rr gg bb (sum)] [rr gg bb left] [rr gg bb (right)] [rr gg bb (top)] [rr gg bb (bottom)]

void loop()
{
    if( Serial.available() > 18 && Serial.read() == 0xff && Serial.read() == 0x00 && Serial.read() == 0x00 )
  {
      while(Serial.available() == 0) {} //wait for red (sum) value
      RedSum = Serial.read();
      while(Serial.available() == 0) {} //wait for green (sum) value
      GreenSum = Serial.read();
      while(Serial.available() == 0) {} //wait for blue (sum) value
      BlueSum = Serial.read();

      while(Serial.available() == 0) {} //wait for red (left) value
      RedLeft = Serial.read();
      while(Serial.available() == 0) {} //wait for green (left) value
      GreenLeft = Serial.read();
      while(Serial.available() == 0) {} //wait for blue (left) value
      BlueLeft = Serial.read();
      
      while(Serial.available() == 0) {} //wait for red (right) value
      RedRight = Serial.read();
      while(Serial.available() == 0) {} //wait for green (right) value
      GreenRight = Serial.read();
      while(Serial.available() == 0) {} //wait for blue (right) value
      BlueRight = Serial.read();
      
      while(Serial.available() == 0) {} //wait for red (top) value
      RedTop = Serial.read();
      while(Serial.available() == 0) {} //wait for green (top) value
      GreenTop = Serial.read();
      while(Serial.available() == 0) {} //wait for blue (top) value
      BlueTop = Serial.read();
      
      while(Serial.available() == 0) {} //wait for red (bottom) value
      RedBottom = Serial.read();
      while(Serial.available() == 0) {} //wait for green (bottom) value
      GreenBottom = Serial.read();
      while(Serial.available() == 0) {} //wait for blue (bottom) value
      BlueBottom = Serial.read();
      
      analogWrite(RedPin1, RedLeft); //change to RedSum for sum, RedRight for right, RedTop for top or RedBottom for bottom
      analogWrite(GreenPin1, GreenLeft); //change to GreenSum for sum, GreenRight for right, GreenTop for top or GreenBottom for bottom
      analogWrite(BluePin1, BlueLeft); //change to BlueSum for sum, BlueRight for right, BlueTop for top or BlueBottom for bottom
 
      analogWrite(RedPin2, RedTop); //change to RedLeft for left, RedSum for sum, RedTop for top or RedBottom for bottom
      analogWrite(GreenPin2, GreenTop); //change to GreenLeft for left, GreenSum for sum, GreenTop for top or GreenBottom for bottom
      analogWrite(BluePin2, BlueTop); //change to BlueLeft for left, BlueSum for sum, BlueTop for top or BlueBottom for bottom     
     
       analogWrite(RedPin3, RedRight); //change to RedLeft for left, RedSum for sum, RedTop for top or RedBottom for bottom
      analogWrite(GreenPin3, GreenRight); //change to GreenLeft for left, GreenSum for sum, GreenTop for top or GreenBottom for bottom
      analogWrite(BluePin3, BlueRight); //change to BlueLeft for left, BlueSum for sum, BlueTop for top or BlueBottom for bottom   
     
       analogWrite(RedPin4, RedBottom); //change to RedLeft for left, RedSum for sum, RedTop for top or RedBottom for bottom
      analogWrite(GreenPin4, GreenBottom); //change to GreenLeft for left, GreenSum for sum, GreenTop for top or GreenBottom for bottom
      analogWrite(BluePin4, BlueBottom); //change to BlueLeft for left, BlueSum for sum, BlueTop for top or BlueBottom for bottom    
      }
   }
    if( Serial.available() > 18 && Serial.read() == 0xff && Serial.read() == 0x00 && Serial.read() == 0x00 )
  {
      while(Serial.available() == 0) {} //wait for red (sum) value
      RedSum = Serial.read();

If there are 18 bytes available to be read, read 3 of them, and then do nothing until there is at least one byte to be read. What? Get rid of the while loop.

Hi Carl,

In your program using Serial.available() takes comparatively a lot of CPU time so limit use of it. In this case using while(Serial.available() == 0) {} is not needed because before it is executed it is already known base on "if" statement that there are at least 19 bytes in serial receive buffer (Atmolight standard packet size containing data for 5 RGB light sources values). First 4 bytes of Atmolight packet are 0xFF 0x00 0x00 and byte informing about how many more bytes there are in this data packet. For standard Atmolight packet value of this byte is 15, so in HEX 0x0F. And there is your second mistake hiding. In this line "RedSum = Serial.read();" you read and assigning byte containing information about how many more bytes there are in this data packet and not byte containing value for red channel of first light source.
Solution is simple add Serial.read(); before "RedSum = Serial.read();"

Sincerely,
Oskar Wicha, aka author of UnoLight

P.S
To make things faster you can use data from serial without intermediate use of variables like that:
analogWrite( RedPin1, Serial.read() );.

using Serial.available() takes a lot of CPU time so limit use of it in your programs.

The Serial.available() function returns the difference between the read position and the write position in a circular ring buffer. It does not take a lot of CPU time.

I see that I wrote it in to general way, thanks PaulS. What I mean is that using it in that way :

if( Serial.available() > 18 && Serial.read() == 0xff && Serial.read() == 0x00 && Serial.read() == 0x00 )
{
      while(Serial.available() == 0) {} //wait for red (sum) value
      RedSum = Serial.read();
      while(Serial.available() == 0) {} //wait for green (sum) value
      GreenSum = Serial.read();
      while(Serial.available() == 0) {} //wait for blue (sum) value
      BlueSum = Serial.read();
}

takes a lot of cpu time. Especially if we already know that there are all bytes that we need in receive buffer it is just waste of cpu resources.

Oskar Wicha

Thanks for the info guys.

Oskar, I see you wrote the code for unolight. Is it very easy to convert this to work on the mega 2560?

Cheers

In next couple of days I will publish next version of UnoLight with couple improvements.
Now to answer your question. To make it work on Mega should be easy probably just couple of name changes (ports,timers etc.). All the info you should need about timer names and port names is in "pins_arduino.c" file of arduino source code and in ATmega2560 datasheet. Using it in that way will enable you to use all pins as outputs for your LEDs. But the easier way is to work with analogWrite() and use only PWM enabled pins as you started to do already in your code sample, just apply advises from my first post and you should be ok.

Oskar Wicha

Hi,

I had a go at this but couldnt work it out :frowning:
Do you think you will be adding more arduino models in the future?

Cheers

What I'm aiming is to have it work on cheap and small arduinos: Uno, Duemilanove, Nano, Nano 3.0 and Leonardo.
In my opinion Arduino Mega is too expensive and takes to much space behind TV, moreover it has 14 PWM pins (or something like that) accessible using analogWrite, so if 4 RGB LEDs is enough there is no need for direct port access. The biggest problem in creating Arduino Mega version is that I don't have Arduino Mega.

Cheers