Converting frequency to delay time

Hi, I am trying to make a program capable to switching on a 6x6 led/pixel display. An added complexity is that the outputs must be pulsed at a certain frequency (currently 50Hz and 1800Hz). I wanted to be able to set the delay using a frequency rather than a time delay so I attempted to convert the frequency input into a delay. This worked fine with a 4x2 display although I had to minus 117us to ensure the output was correct (checked on oscilloscope). The problem with the 6x6 display is that at 1800Hz the output goes down to 10Hz.

I can subtract around 90us from the delay before things go wrong. The higher the frequency the worse things get. e.g. at 50Hz all works well, but at 10KHz the timing is totally wrong. Could anybody please shed some light on the situation?

I’ve tried using the delay function instead and that was even worse.

The part of the code I’m referring to is below and it’s the ‘void freqConv’ at the end which appears to be causing trouble:

byte scatterDisplay =
{
B111111,
B111111,
B111111,
B111111,
B111111,
B111111

};

byte clearDisplay =
{
B111111,
B111111,
B111111,
B111111,
B111111,
B111111

};

int columnPins = {23, 25, 27, 29, 31, 33};
int rowPins = {22, 24, 26, 28, 30, 32}; //pins at 5V
int numPixels = 36;
int numCols = 6;
int numRows = 6;
int scatterFreq = 50;
int scatterPulses = 5;
int clearFreq = 1800;
int clearPulses = 600;

void setup()
{
for(int pixel = 0; pixel < numPixels; pixel++)
{
pinMode(rowPins[pixel], OUTPUT);
pinMode(columnPins[pixel], OUTPUT);
digitalWrite(columnPins[pixel], HIGH);
}
Serial.begin(9600);
}

void loop()
{
char ch;
if (Serial.available() > 0)
{
ch = Serial.read();
if (ch == ‘s’)
{
Serial.println(“Scattering…”);
operate(scatterDisplay, scatterFreq, scatterPulses);
}
else if (ch == ‘c’)
{
Serial.println(“Clearing…”);
operate(clearDisplay, clearFreq, clearPulses);
}
}
}

void operate(byte * image, int freq, int pulses)
{
Serial.print("Frequency = ");
Serial.print(freq);
Serial.println(“Hz”);
Serial.print("Number of pulses = “);
Serial.println(pulses);
for(int row = 0; row < numRows ; row++) //loop following code for each
row
{
for (int i = 0; i < pulses; i++)
{
digitalWrite(rowPins[row], HIGH); // set rows to +5V
for(int column = 0; column < numCols; column++) //count/loop though columns
{
boolean pixel = bitRead(image[row],column); //read the bitmap image
if(pixel == 1)
{
digitalWrite(columnPins[column], LOW); //if pixel is 1 connect column to gnd
}
freqConv(freq, numCols);
digitalWrite(columnPins[column], HIGH); //return column to high
}
digitalWrite(rowPins[row], LOW); //end process by switching rows
off
}
}
Serial.println(“Done”);
Serial.println(” ");
}

void freqConv(float freq, int numCols)
{
float f = freq;
float T, d, f1;

T = (1/f)1000;
d = T/numCols;
delayMicroseconds((d
1000)-117); //delay in microseconds minus a correction factor for computation time }

At 1800 Hz and 6 columns your delay comes out to 92-117 microseconds. I'm surprised it does not treat that negative number as a huge unsigned number and fail completely to operate.

Might be time to learn to programm one of the built-in timers and use an interrupt to refresh your columns.

Thank you for your reply. I was starting to think the problem was something to do with that. So by using hardware interrupts do you mean using the millis function? Are there any other issues which you can see with the code - this is my first program so everything is a learning curve!

If possible, raise your baud rate as high as it will go, such as 115,200. Your serial communication will take less time that way.
Also use # button for posting code. It is next to the quote button.

Serial.begin(9600);
 
 change to 
Serial.begin(115200);

Chris6100:
Thank you for your reply. I was starting to think the problem was something to do with that. So by using hardware interrupts do you mean using the millis function? Are there any other issues which you can see with the code - this is my first program so everything is a learning curve!

You light only one LED at a time. You can get better brightness if you light all pixels in a row, do the delay, and then turn off all the pixels (using a second loop across the columns). That will mean one delay per row so the delay can be six times as long.

If that algorithm works at all, it'll only be by chance.

If you want to do something repeatedly at a fixed and fairly high frequency it'd be best to do all your calculations up front to work out what you need to do in the loop, so you only need to do all those calcs once.

Currently, you're turning each LED on and off in turn. Do you mean to flash them all together? If so, it would be far easier to set the appropriate column pins and then just turn the whole row on and off at the require frequency. Doing a single delay for the whole flash interval will be a lot easier to get accurate than doing lots of small delays for each separate LED.

If y6ou intend to achieve a fixed frequency then rather than adding delays and fixed offsets to compensate for processing delays I suggest you measure how much time has elapsed since the last event and spin until it reaches your required interval. In order to keep an accurate phase, you should increment your timer by your intended interval rather than recording the current time; this avoids drifting due to processing latency.

Thanks for all the responses. The reason why I am switching each LED in turn is partly because for the application in mind, the current requirements might draw too much from the power supply if the entire row is turned on. But aside from that I wasn't sure if it would read the bitmap correctly if it was a mixture of 1's and 0's. I understand the idea of turning appropriate column pins on and then turning the entire row on which would work so I will try to implement this.

To clarify... would the best way to turn the whole column on be to loop through the column and switch on the appropriate pixels, and then perform a loop to switch the whole row on and off at the desired frequency, and finally switch off the column pixels?

Back to my previous point - should I use the millis function or just a normal delay if I turn the entire row on?

Chris6100:
To clarify… would the best way to turn the whole column on be to loop through the column and switch on the appropriate pixels, and then perform a loop to switch the whole row on and off at the desired frequency, and finally switch off the column pixels?

Yes, that’s it exactly.

Chris6100:
Back to my previous point - should I use the millis function or just a normal delay if I turn the entire row on?

You can use delay(), but calculate how long you need to delay for based on the value of millis() when you started the flash, and the value of millis() at the start of the delay. Remember that you will have to have separate delays for the ‘on’ and ‘off’ part of the flash cycle.

Ok I'm on the case. I've been trying to figure out how to use the hardware timers with limited success. Is the extra effort to get them to work worth the time do you think?

I don't see that anything you're trying to do here strictly needs them - but it's your time at the end of the day.