POV Clock Algorithm

I am trying to implement a POV analog clock like this.

I made it, displayed simple text and patterns successfully but when I got further into displaying complex stuff like text and clock hands at the time, things are getting difficult.

For displaying texts only I started from the sensor readings and displayed the patterns for each line/segment in real time, but when it comes to combining text and patterns I though it would be better to manipulate a 2D array to store graphic information and display it’s content after that for each rotation.
That is causing memory problem in Atmega328. If I divide the whole surface of display by 360 the array size will be 360x10(number of LEDs), which is getting bigger than the SRAM(2048 bytes).

I tried to understand the code from the above link, but it’s commented in a different language.

How should I approach the problem?

Here is the code

#define HALL_INPUT 2

#define RED_STRIP 4
#define RED1 12
#define RED2 11
#define RED3 10
#define RED4 9
#define RED5 8
#define RED6 7
#define RED7 6
#define BLUE 5
#define WHITE 13

#define SEC_LED 1

/*
 * Stores the display matrix
 */
boolean displayMatrix[10][360];

/* Stores time for each rotation in microseconds
 * TODO: Make this value dynamic
 */
unsigned long RotationTime = 30500;
unsigned long oneDegTime = RotationTime/360;

void setup() {
  pinMode(RED_STRIP, OUTPUT);
  pinMode(RED1, OUTPUT);
  pinMode(RED2, OUTPUT);
  pinMode(RED3, OUTPUT);
  pinMode(RED4, OUTPUT);
  pinMode(RED5, OUTPUT);
  pinMode(RED6, OUTPUT);
  pinMode(RED7, OUTPUT);
  pinMode(BLUE, OUTPUT);
  pinMode(WHITE, OUTPUT);
  
  pinMode(HALL_INPUT, INPUT_PULLUP);

  //attachInterrupt(digitalPinToInterrupt(HALL_INPUT), getRotationTime, FALLING);

  for(int i = 0; i < 10; i++){
  	for(int j = 0; j < 360; j++){
  		displayMatrix[i][j] = false;
  	}
  }
  
  Serial.begin(9600);
}

unsigned long lastCount = 0;
int secCount = 0;
int minCount = 10;
int hourCount = 50;
void loop() {
	
	int interval = 1000;
	if(interval < millis() - lastCount){
		lastCount = millis();

		if(secCount < 60)
			secCount++;
		else{
			secCount = 1;

			if(minCount < 60)
				minCount++;
			else
				minCount = 1;
			
			if(minCount == 12 || minCount == 24 || minCount == 36 || minCount == 48 || minCount == 60){
				if(hourCount < 60)
					hourCount++;
				else
					hourCount = 1;
			}
		}
	}

	clearMatrix();
	adjustMatrix(secCount, minCount, hourCount);

  if(!digitalRead(HALL_INPUT)){
  	//testLoop();

  	displayMat();

  	/*
  	Serial.print(hourCount);
  	Serial.print(" : ");
  	Serial.print(minCount);
  	Serial.print(" : ");
  	Serial.print(secCount);
  	Serial.println();
  	*/
  }
}

void clearMatrix(){
	for(int i = 0; i < 10; i++){
  	for(int j = 0; j < 360; j++){
  		displayMatrix[i][j] = false;
  	}
  }
}

void adjustMatrix(int sec, int min, int hour){
	for(int i = 0; i < (sec*3); i++){
		displayMatrix[SEC_LED][i] = true;
	}
}

void displayMat(){
	for(int i=0;i<360;i++){
		for(int j=0;j<10;j++){
			if(displayMatrix[j][i])
				writeLED(j, true);
			else
				writeLED(j, false);
		}

		delaySegment(1, 360);
	}
}

void writeLED(int i, bool state){
	if(state){
		switch(i){
			case 0:
				digitalWrite(WHITE, HIGH);
				break;
			case 1:
				digitalWrite(BLUE, HIGH);
				break;
			case 2:
				digitalWrite(RED7, HIGH);
				break;
			case 3:
				digitalWrite(RED6, HIGH);
				break;
			case 4:
				digitalWrite(RED5, HIGH);
				break;
			case 5:
				digitalWrite(RED4, HIGH);
				break;
			case 6:
				digitalWrite(RED3, HIGH);
				break;
			case 7:
				digitalWrite(RED2, HIGH);
				break;
			case 8:
				digitalWrite(RED1, HIGH);
				break;
			case 9:
				digitalWrite(RED_STRIP, HIGH);
				break;
		}
	}
	else{
		switch(i){
			case 0:
				digitalWrite(WHITE, LOW);
				break;
			case 1:
				digitalWrite(BLUE, LOW);
				break;
			case 2:
				digitalWrite(RED7, LOW);
				break;
			case 3:
				digitalWrite(RED6, LOW);
				break;
			case 4:
				digitalWrite(RED5, LOW);
				break;
			case 5:
				digitalWrite(RED4, LOW);
				break;
			case 6:
				digitalWrite(RED3, LOW);
				break;
			case 7:
				digitalWrite(RED2, LOW);
				break;
			case 8:
				digitalWrite(RED1, LOW);
				break;
			case 9:
				digitalWrite(RED_STRIP, LOW);
				break;
		}
	}	
}

/* 
 * Displays seconds, minutes and hour
 */
void display(int sec, int min, int hour){
	int displaySeq[] = {sec, min, hour};

	sort(displaySeq, 3);

	digitalWrite(BLUE, HIGH);

	digitalWrite(RED6, HIGH);

	digitalWrite(RED1, HIGH);
	digitalWrite(RED2, HIGH);
	
	int delayedMicros = 0;
	for(int i = 0; i < 3; i++){
		delaySegment(displaySeq[i] - delayedMicros, 60);
		delayedMicros = displaySeq[i];

		if(displaySeq[i] == sec)
			digitalWrite(BLUE, LOW);
		if(displaySeq[i] == min)
			digitalWrite(RED6, LOW);
		if(displaySeq[i] == hour){
			digitalWrite(RED1, LOW);
 			digitalWrite(RED2, LOW);
		}
	}		
}

/* 
 * Handles the delay in microseconds smoothly
 * @params 
 * int num: Number of segemnts to delay
 * int divisor: Total number of segments 
 */
void delaySegment(int num, int divisor){
	unsigned int segmentLength = RotationTime/divisor;

	if(segmentLength*num < 16000)
		delayMicroseconds(segmentLength*num);
	else{
		delayMicroseconds(segmentLength*num/2);
		delayMicroseconds(segmentLength*num/2);
	}
}

/*
 * Performs a selection sort
 * @params
 * int arr[]: The array
 * int len: Length of the array
 */
void sort(int arr[], int len){
	int temp, pos;

	for(int i = 0; i < len - 1; i++){
		pos = i;
		for(int j = i + 1; j < len; j++){
			if(arr[pos] > arr[j])
				pos = j;
		}
		if(pos != i){
			temp = arr[i];
			arr[i] = arr[pos];
			arr[pos] = temp;
		}
	}
}

/* 
 * Displays fours bars at 90 deg interval
 */
void displayClock(){
  delayMicroseconds(oneDegTime*44);  

  digitalWrite(BLUE, HIGH);
  delayMicroseconds(oneDegTime*2);

  digitalWrite(BLUE, LOW);
  delayMicroseconds(oneDegTime*88); 

  digitalWrite(BLUE, HIGH);
  delayMicroseconds(oneDegTime*2);

  digitalWrite(BLUE, LOW);
  delayMicroseconds(oneDegTime*88); 

  digitalWrite(BLUE, HIGH);
  delayMicroseconds(oneDegTime*2);

  digitalWrite(BLUE, LOW);
  delayMicroseconds(oneDegTime*88); 

  digitalWrite(BLUE, HIGH);
  delayMicroseconds(oneDegTime*2);

  digitalWrite(BLUE, LOW);
  delayMicroseconds(oneDegTime*44);
}

/*
 * Display a circle to test out the speed
 */
void testLoop(){
	digitalWrite(BLUE, HIGH);
	digitalWrite(WHITE, HIGH);

	delayMicroseconds(180*oneDegTime);
	delayMicroseconds(180*oneDegTime);

	digitalWrite(BLUE, LOW);
	digitalWrite(WHITE, LOW);
}


/*
 * External Interrupt function: Calcutates the time for each rotation and store it in RotationTime
 * TODO: Use later
 */
unsigned long prevRotationTime = 0;
void getRotationTime(){
  RotationTime = millis() - prevRotationTime;
  prevRotationTime = millis();
}

There are some extra functions that is used earlier and not used currently.

It would be much more convenient if you posted the code here, but before you do, please read this and note the advice about using code tags if posting inline with your message.

I would store the image as 360 unsigned ints (720 bytes) and do bit manipulation to pack the 10 rows into the 16-bit ints.

Jimut:
I am trying to implement a POV analog clock like this.

You are trying to create two of such clocks, don't you?

And this one is different from that one in this thread:

So what is the different problem with this clock than the clock you wrote about in the other thread?

jurs:
You are trying to create two of such clocks, don't you?

No, I just wanted to move the thread to this topic, because everyone told me that.

I just rewrote the code with fewer divisions (120), but it’s still not working correctly somehow. It’s flickering too much.

What might be the problem? Are there too much instructions before the display function which may cause it to miss one or two rotations?

#define HALL_INPUT 2

#define RED_STRIP 4
#define RED1 12
#define RED2 11
#define RED3 10
#define RED4 9
#define RED5 8
#define RED6 7
#define RED7 6
#define BLUE 5
#define WHITE 13

#define SEC_LED 1

#define NO_OF_DIVISIONS 120
#define NO_OF_LEDS 10


/*
 * Stores the display matrix
 */
boolean displayMatrix[NO_OF_LEDS][NO_OF_DIVISIONS];

/* Stores time for each rotation in microseconds
 * TODO: Make this value dynamic
 */
unsigned int RotationTime = 30000;


void setup() {
  pinMode(RED_STRIP, OUTPUT);
  pinMode(RED1, OUTPUT);
  pinMode(RED2, OUTPUT);
  pinMode(RED3, OUTPUT);
  pinMode(RED4, OUTPUT);
  pinMode(RED5, OUTPUT);
  pinMode(RED6, OUTPUT);
  pinMode(RED7, OUTPUT);
  pinMode(BLUE, OUTPUT);
  pinMode(WHITE, OUTPUT);
  
  pinMode(HALL_INPUT, INPUT_PULLUP);

  // attachInterrupt(digitalPinToInterrupt(HALL_INPUT), getRotationTime, FALLING);
  
  // Serial.begin(9600);
}


int secCount = 0;
int minCount = 10;
int hourCount = 50;
void loop() {
	
	// getTime();

	clearMatrix();

	adjustMatrix(secCount, minCount, hourCount);

  if(!digitalRead(HALL_INPUT)){

  	displayMat();

  	/*
  	Serial.print(hourCount);
  	Serial.print(" : ");
  	Serial.print(minCount);
  	Serial.print(" : ");
  	Serial.print(secCount);
  	Serial.println();
  	*/
  }
}

unsigned long lastCount = 0;
void getTime(){
	if(1000 < millis() - lastCount){
		lastCount = millis();

		if(secCount < 60)
			secCount++;
		else{
			secCount = 1;

			if(minCount < 60)
				minCount++;
			else
				minCount = 1;
			
			if(minCount == 12 || minCount == 24 || minCount == 36 || minCount == 48 || minCount == 60){
				if(hourCount < 60)
					hourCount++;
				else
					hourCount = 1;
			}
		}
	}
}

void clearMatrix(){
	for(int i = 0; i < NO_OF_LEDS; i++){
  	for(int j = 0; j < NO_OF_DIVISIONS; j++){
  		displayMatrix[i][j] = false;
  	}
  }

  for(int i = 0; i < NO_OF_LEDS; i++)
  	writeLED(i, false);
}

void adjustMatrix(int sec, int min, int hour){
	/*
	for(int i = 0; i < sec; i++){
		displayMatrix[SEC_LED][i] = true;
	}
	*/

	for(int i=0;i<NO_OF_DIVISIONS;i++){
		if((i+1)%2 == 0){
			for(int j=0;j<NO_OF_LEDS;j++)
				displayMatrix[j][i] = true;
		}
		else{
			for(int j=0;j<NO_OF_LEDS;j++)
				displayMatrix[j][i] = false;
		}
	}
}

void displayMat(){
	for(int i = 0; i < NO_OF_DIVISIONS; i++){
		for(int j = 0; j < NO_OF_LEDS; j++){
			if(displayMatrix[j][i])
				writeLED(j, true);
			else
				writeLED(j, false);
		}

		delaySegment(1, NO_OF_DIVISIONS);
	}
}

void writeLED(int i, bool state){
	if(state){
		switch(i){
			case 0:
				digitalWrite(WHITE, HIGH);
				break;
			case 1:
				digitalWrite(BLUE, HIGH);
				break;
			case 2:
				digitalWrite(RED7, HIGH);
				break;
			case 3:
				digitalWrite(RED6, HIGH);
				break;
			case 4:
				digitalWrite(RED5, HIGH);
				break;
			case 5:
				digitalWrite(RED4, HIGH);
				break;
			case 6:
				digitalWrite(RED3, HIGH);
				break;
			case 7:
				digitalWrite(RED2, HIGH);
				break;
			case 8:
				digitalWrite(RED1, HIGH);
				break;
			case 9:
				digitalWrite(RED_STRIP, HIGH);
				break;
		}
	}
	else{
		switch(i){
			case 0:
				digitalWrite(WHITE, LOW);
				break;
			case 1:
				digitalWrite(BLUE, LOW);
				break;
			case 2:
				digitalWrite(RED7, LOW);
				break;
			case 3:
				digitalWrite(RED6, LOW);
				break;
			case 4:
				digitalWrite(RED5, LOW);
				break;
			case 5:
				digitalWrite(RED4, LOW);
				break;
			case 6:
				digitalWrite(RED3, LOW);
				break;
			case 7:
				digitalWrite(RED2, LOW);
				break;
			case 8:
				digitalWrite(RED1, LOW);
				break;
			case 9:
				digitalWrite(RED_STRIP, LOW);
				break;
		}
	}	
}

/* 
 * Handles the delay in microseconds smoothly
 * @params 
 * int num: Number of segemnts to delay
 * int divisor: Total number of segments 
 */
void delaySegment(int num, int divisor){
	unsigned int segmentLength = RotationTime/divisor;

	if(segmentLength*num < 16000)
		delayMicroseconds(segmentLength*num);
	else{
		delayMicroseconds(segmentLength*num/2);
		delayMicroseconds(segmentLength*num/2);
	}
}