/*
max72.cpp - Arduino Library for MAX7219.
Copyright 2014 - Laurent Menu-Kerforn
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Arduino.h"
#include "max72.h"
/*
protected:
	int loadpin;
	int nbmax;
	int clockpin;
	int datapin;
	char orientation;
*/

void max72::varinit(int loadpin,int clockpin,int datapin){
	this->loadpin=loadpin;
	this->clockpin=clockpin;
	this->datapin=datapin;
	nbmax=1;
	orientation='O';
	}
void max72::varinit(int nbmax,int loadpin,int clockpin,int datapin){
	varinit(loadpin,clockpin,datapin);
	this->nbmax=nbmax;
	}

void max72::setup() {
	pinMode(loadpin,OUTPUT);
	pinMode(clockpin,OUTPUT);
	pinMode(datapin,OUTPUT);
	digitalWrite(loadpin,HIGH);
	sendcommand(M72_SCANLIMIT,0x07);      
	sendcommand(M72_DECODEMODE,0x00);  // led matrix
	switchon();  // using an led matrix (not digits)
	sendcommand(M72_DISPLAYTEST,0x00);
	clear();
	intensity(0);
	}

void max72::setup(int loadpin,int clockpin,int datapin){
	varinit(loadpin,clockpin,datapin);
	setup();
	}
void max72::setup(int nbmax,int loadpin,int clockpin,int datapin){
	varinit(nbmax,loadpin,clockpin,datapin);
	setup();
	}

max72::max72(int loadpin,int clockpin,int datapin){
	varinit(loadpin,clockpin,datapin);
	}
max72::max72(int nbmax,int loadpin,int clockpin,int datapin){
	varinit(nbmax,loadpin,clockpin,datapin);
	}

void max72::setblock(byte v1,byte v2,byte v3,byte v4, byte v5, byte v6, byte v7, byte v8){
	byte tab[8];
	tab [0]=v1;
	tab [1]=v2;
	tab [2]=v3;
	tab [3]=v4;
	tab [4]=v5;
	tab [5]=v6;
	tab [6]=v7;
	tab [7]=v8;
	setblock(tab);
	}

void max72::setMblock(int max, byte v1,byte v2,byte v3,byte v4, byte v5, byte v6, byte v7, byte v8){
	byte tab[8];
	tab [0]=v1;
	tab [1]=v2;
	tab [2]=v3;
	tab [3]=v4;
	tab [4]=v5;
	tab [5]=v6;
	tab [6]=v7;
	tab [7]=v8;
	setMblock(max,tab);
	}

void max72::sendbyte(byte b) {
	int i;
	for(i=0;i<8;i++) {
		digitalWrite(clockpin,LOW);
		digitalWrite(datapin,(b&0x80)? HIGH:LOW);
		digitalWrite(clockpin,HIGH); //ecriture sur transition montante
		b<<=1;
		}
	}
void max72::send2bytes(byte com, byte parm) {
	sendbyte(com);
	sendbyte(parm);
	}
void max72::sendcommand(byte com,byte parm){
	int i;
	digitalWrite(loadpin,LOW);
	for(i=0;i<nbmax;i++) send2bytes(com,parm); //if multiple MAX, all the same	
	digitalWrite(loadpin,HIGH);	
	}
void max72::sendMcommand(int max,byte com,byte parm){ //MAX numbered from 0 to nbmax-1
	int i;
	digitalWrite(loadpin,LOW);
	for(i=0;i<nbmax;i++) (i==max)? send2bytes(com,parm):send2bytes(0x00,0x00);	
	digitalWrite(loadpin,HIGH);	
	}
void max72::setrow(int row,byte val){ //row numbered from 1 to 8
	if(orientation=='M') val=esrevni(val);
	if(row>0 && row<9) sendcommand((byte)row,val);
	}

byte max72::esrevni(byte b) {
	return  (b<<7&0b10000000)
		|(b<<5&0b01000000)
		|(b<<3&0b00100000)
		|(b<<1&0b00010000)
		|(b>>1&0b00001000)
		|(b>>3&0b00000100)
		|(b>>5&0b00000010)
		|(b>>7&0b00000001);
	}

byte *max72::rotate(byte *tab,byte *rtab) {
	int i,j;
	byte col;
	//for(i=0;i<8;rtab[i++]=0x00);
	for(i=0;i<8;i++) {
		switch(orientation) {
		case 'O':
		case 'M': //done in setrow
			rtab[i]=tab[i];
			break;
		case 'U' :
			rtab[7-i]=esrevni(tab[i]);
			break;
		case 'R':
			col=1<<(7-i);
			rtab[i]=0;
			for(j=0;j<8;j++) if(tab[j]&col) rtab[i]|=1<<j;
			break;
		case 'L':
			col=1<<i;
			rtab[i]=0;
			for(j=0;j<8;j++) if(tab[j]&col) rtab[i]|=1<<(7-j);
			break;
		};
		};
	return rtab;
	}

void max72::setblock(byte *tab){
	int i;
	byte *ptr;
	byte rtab[8];
	rotate(tab,rtab);
	ptr=rtab;
	for(i=1;i<9;i++) setrow(i,*ptr++);
	}
void max72::setMblock(int max,byte *tab){
	int i;
	byte rtab[8];
	rotate(tab,rtab);
	tab=rtab;
	for(i=1;i<9;i++) setMrow(max,i,*tab++);
	}

void max72::setMrow(int max,int row,byte val){
	if(orientation=='M') val=esrevni(val);
	if(row>0 && row<9) sendMcommand(max,(byte)row,val);
	}
void max72::setorientation(char o){
	o&=0b11011111; //UPPERCASE
	switch(o) {
		case 'O':
		case 'M': 
		case 'R':
		case 'L':
		case 'U':
			orientation=o;
			break;
		default:
			orientation='O';
		};
	}
void max72::intensity(byte val){
	sendcommand(M72_INTENSITY,val&0x0F);
	}
void max72::shutdown(){
	sendcommand(M72_SHUTDOWN,0x00);
	}
void max72::switchon(){
	sendcommand(M72_SHUTDOWN,0x01);
	}
void max72::Mclear(int max){
	setMblock(max,(byte)0,(byte)0,(byte)0,(byte)0,(byte)0,(byte)0,(byte)0,(byte)0);
	}
void max72::clear(){
	setblock((byte)0,(byte)0,(byte)0,(byte)0,(byte)0,(byte)0,(byte)0,(byte)0);
	}

