Maker Pro
Maker Pro

Trouble with tribbles.. erm, timers.

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,510
Joined
Jan 21, 2010
Messages
25,510
Sorry I didn't get back to you earlier.

I had a quick look at the code, but I couldn't see anything that was doing any timing.

I presume the program just scans the array as fast as possible.

The problem with this approach is that it takes a different amount of time to process LEDs in different states (especially because you're doing processing additional to just scanning the array within the loop). This means that the brightness of LEDs in some columns will vary due to the difference in time taken to process the subsequent column.

Remember that I have said that each column must be processed for the same fixed period. In your case, the LedAnode routine will take a different amount of time depending on the LED state. This will presumably result in the previous column being displayed for a different amount of time.

Because your code is intermingling the scanning and the brightness/fading, you have created a rod for your own back. It would be far better to have a single routine that scans the entire array in constant time, and then (with the LEDs off) you adjust any values you want to before scanning the array again. Even then, any change in the amount of processing you do between scans would have the effect of altering the brightness of the array. Thus, without any control of timing, you need to ensure that your processing is as close to constant time as possible.

One option is to have a timer that generates a periodic interrupt. Each time it runs, it sets a flag (time to scan the array). Your main loop simply processes each key in turn, doing whatever it needs to do to the brightness of each LED (perhaps the interrupt also increments a counter so you know how much time has passed). After each button is processed you check the flag, and if it is set you reset it and scan the array. This will ensure that your scans are done at fairly constant intervals.

Whatever you do, you need to ensure that the LEDs at a given intensity are always on for the same proportion of every second or you'll get weird effects that will be hard to understand, and thus even harder to control.
 

The Hammer

Sep 17, 2013
164
Joined
Sep 17, 2013
Messages
164
Sorry I didn't get back to you earlier.

I had a quick look at the code, but I couldn't see anything that was doing any timing.

I presume the program just scans the array as fast as possible.
Code:
TCCR0B |= (1 << CS01);
Code:
if (124 <= TCNT0)

This is a pre-scale of 8 with an 18MHz clock.

These are intended to run one column for one brightness period every 18kHz interval. With the 18MHz clock source it finishes the code before TCNT0==124, but it does run too slowly... about 3 times a second like you were saying.. but I have no accurate way to measure that. It's a guesstimate from what I can see is happening. I don't understand the math I suppose, but I guess I'll have to re-investigate what you had been saying earlier.

There were some bits of the code when I was using "1==1" in place of "TCNT0" just to see how fast it would run when it was going as fast as possible, and it was not as fast as I needed (fast enough to not see any flicker). That may be what you were seeing before.

I was looking into the buffer idea, too. Either I mis-understand the concepts you guys are referring to, or it just won't work in this case, because I can't use a buffer for the fade steps as it would consume almost 14kB..
 
Last edited:

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,510
Joined
Jan 21, 2010
Messages
25,510
I was looking at the code in this post. I *thought* that was the one you pointed me to when I asked to see the timing code you were using.

I suspect that your code takes longer than 55us to run.

if (124 <= TCNT0)

so this is a count of 124 at a frequency of 18/8 MHz? (55.1uS)

In order to do this you need to make the scanning far more efficient. And that means leaving out all of the calculations so you can try to squeeze them in between the processing of columns in your scanning. Something like:

Code:
i = 0;
col = 0;

while(true)
{
    if (time to do next column scan)
    {
        reset time

        scan column col;
        col++
    }

    process key i;
    i++
}

I haven't sown any code to wrap the column or key back to 0, but I hope you get the idea.

The processing of the column should be pretty much as I suggested in an earlier post.

  1. For each LED in the column
    • Determine if the LED is on
    • load a bit into the register
  2. Update the column data
  3. Turn off the column driver.
  4. Latch the row data.
  5. Latch the column data
  6. Turn on the column driver
The "process key" above is used to do everything else (typically deciding what colour the LED should be, doing the fading, etc.

It also needs to operate quickly so that it takes well under the time taken to update a column (so it causes the minimum of overrun of time)

This algorithm will allow the updating of keys to happen between scans, as many as possible that will fit in. To handle fading, you'll probably need to have a clock counter unless you decide to (say) only update a single key each time a column is updated (actually that's not such a bad idea in terms of complexity.)

The algorithm would change to

Code:
i = 0;
col = 0;

while(true)
{
    if (time to do next column scan)
    {
        reset time

        scan column col;
        col++
        process key i;
        i++
    }
}

This might mean your keys are updated at 1/6 the rate you do now (or something like that), but this will probably allow your scanning to operate faster. If you have enough time, you could process more keys each time, but remember to leave yourself time! You also need to be able to read the keys and communicate to the PC.
 

The Hammer

Sep 17, 2013
164
Joined
Sep 17, 2013
Messages
164
I was looking at the code in this post. I *thought* that was the one you pointed me to when I asked to see the timing code you were using.

I suspect that your code takes longer than 55us to run.
Ah, I'm sorry, I was trying to stay on your earlier suggestion to do one column for 20 brightness levels before moving to the next column.

Surprisingly, it does finish running the code in 55us (although, only while the chip's been running at 18MHz). It was giving visual confirmation for finishing on time for the next column to be run. The thing that is confusing me is that even at 55us it was quite visible that it was flickering.

so this is a count of 124 at a frequency of 18/8 MHz? (55.1uS)

In order to do this you need to make the scanning far more efficient. And that means leaving out all of the calculations so you can try to squeeze them in between the processing of columns in your scanning. Something like:

Code:
i = 0;
col = 0;

while(true)
{
    if (time to do next column scan)
    {
        reset time

        scan column col;
        col++
    }

    process key i;
    i++
}

I haven't sown any code to wrap the column or key back to 0, but I hope you get the idea.

The processing of the column should be pretty much as I suggested in an earlier post.

  1. For each LED in the column
    • Determine if the LED is on
    • load a bit into the register
  2. Update the column data
  3. Turn off the column driver.
  4. Latch the row data.
  5. Latch the column data
  6. Turn on the column driver
The "process key" above is used to do everything else (typically deciding what colour the LED should be, doing the fading, etc.

It also needs to operate quickly so that it takes well under the time taken to update a column (so it causes the minimum of overrun of time)

This algorithm will allow the updating of keys to happen between scans, as many as possible that will fit in. To handle fading, you'll probably need to have a clock counter unless you decide to (say) only update a single key each time a column is updated (actually that's not such a bad idea in terms of complexity.)

The algorithm would change to

Code:
i = 0;
col = 0;

while(true)
{
    if (time to do next column scan)
    {
        reset time

        scan column col;
        col++
        process key i;
        i++
    }
}

This might mean your keys are updated at 1/6 the rate you do now (or something like that), but this will probably allow your scanning to operate faster. If you have enough time, you could process more keys each time, but remember to leave yourself time! You also need to be able to read the keys and communicate to the PC.
I've more or less done what you suggested already. I'll whip up some more efficient code with comments that you can follow if you wouldn't mind looking through it again. Thank you for working with me. I don't mind a slower scanning rate for the keys. I was originally planning on running a poll of all the keys every 200Hz, but have been focusing on the display first.. with many problems. :( The USB functionality shouldn't be too bad either in terms of processing time, as I'll be using an ARM chip with Hi-Speed USB built in eventually. I've still got very little idea with how to deal with that...
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,510
Joined
Jan 21, 2010
Messages
25,510
ok, what I suggest you do is set up the display for a static chequerboard pattern, remove all code that does anything other than scan the keys and see what you get.

then try it at varying intensities and colours. Do you see the same pattern of behaviour in all cases?

if the behavior is not right, does the colour make a difference? The column? The row?

If you're seeing ghosting, is it everywhere, or just in certain columns or rows?

If every LED that's on is on at 100%, then the only reason for changes of brightness are either timing (which should affect an entire column) or accessing the wrong data (which could cause anything but which would likely corrupt the chequerboard).
 

The Hammer

Sep 17, 2013
164
Joined
Sep 17, 2013
Messages
164
ok, what I suggest you do is set up the display for a static chequerboard pattern, remove all code that does anything other than scan the keys and see what you get.

then try it at varying intensities and colours. Do you see the same pattern of behaviour in all cases?

if the behavior is not right, does the colour make a difference? The column? The row?

If you're seeing ghosting, is it everywhere, or just in certain columns or rows?

If every LED that's on is on at 100%, then the only reason for changes of brightness are either timing (which should affect an entire column) or accessing the wrong data (which could cause anything but which would likely corrupt the chequerboard).
The ghosting only happens when I run the entire array at brightness level 0, then increment upward every time the array finishes one scan. The ghosting is proportional to the brightness level, it happens even on specific colors, and only happens on the next column to be run on that specific LED in the column (E.G. key[0] ghosting to key[6], 11 to 17, ect..).

I was having key[0] ghosting to key[6] with scanning each column's brightness level before moving into the next column, but could not reproduce it on any other key. This was the way you suggested I make the array operate.
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,510
Joined
Jan 21, 2010
Messages
25,510
OK, the important thing that you do is to verify the following:
  1. As you are shifting bits into the row 74HC595 that these values DO NOT appear at the outputs. They should only appear at the outputs once the full set has been loaded and you latch them.
  2. That you disable either (or both) the column or the row outputs before you latch the row outputs and while you are changing the column from one value to another.
  3. That with the outputs disabled the column drivers turn off.
Because of the speed at which you are running the scanning, capacitance in the column drivers can cause ghosting as they may not turn off fast enough. This will be exhibited as a ghosting between columns in the direction of scanning.

If you are not correctly controlling the row outputs you will see a ghosting that is stronger at the end the bits are fed in, and weaker (or nonexistent) at the other end.

This is actually a reason to run the scanning VERY slowly (say 1/4 of a second delay between loading each bit into the rows, and a second between columns. Preferably the delay should be spread out so some of it is between every action you do on the array. Doing it this way you can confirm if the row and columns are handled correctly (you should see a whole column appear, then the next, then the next, ... you should NOT see anything shifting into place or one column briefly appearing in the subsequent column, etc.

Note that the capacitance issues mean that at some speed, you will get ghosting. If this becomes an issue (you can check that by seeing if the ghosting gets weaker and weaker at slower scan rates) then you may be able to modify the way the signals are driven to the column drivers (perhaps by forcing them low rather than setting them high impedance while switching).

Whatever you do, you need to get the scanning operating correctly first, then leave this code completely unchanged while you add the additional behaviour.
 

The Hammer

Sep 17, 2013
164
Joined
Sep 17, 2013
Messages
164
So, unless I'm mistaken.. you're saying that essentially the BJT transistors I'm using in the columns as drivers have some sort of internal capacitance that is causing this ghosting? Would pull down resistors on the base or elsewhere help at all if that is the case?
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,510
Joined
Jan 21, 2010
Messages
25,510
I'm not saying its THE cause, but it's A cause.

Once you've eliminated other possible causes (like changing columns before disabling rows) then this is a possibility.

It is especially likely considering that you're doing rapid switching.

Actively pulling the transistor's base to it's emitter rather than just removing base drive will help. If you're changing outputs to a high impedance state, then pull down resistors may help. For mosfets they're essential.
 

gorgon

Jun 6, 2011
603
Joined
Jun 6, 2011
Messages
603
The ghosting problem is one of the reasons you should complete each column with all 20 values before changing to the next column. If you don't do it already.
Pulldown resistors is essential to turn off the column drive transistor.
 

The Hammer

Sep 17, 2013
164
Joined
Sep 17, 2013
Messages
164
It's ghosting in the reverse direction that the scan is traveling in. I've confirmed that it is scanning correctly as I slowed it way down again as you suggested. What is an appropriate pull-down resistor for an application like this (tried using 1k between base and emitter as you suggested with no success), or maybe these transistors are not suitable for this application? I may revert to using the method gorgon suggests if there are no better transistors available, but as this method provides smoother visible effects in the case that there's some delay in the MCU for whatever unforeseeable reason I'd like to pursue this avenue first.

I'm using these transistors, but have also tried using ULN2803, which has worse ghosting.

http://www.digikey.com/product-detail/en/PN2222TF/PN2222TFDKR-ND/4745427
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,510
Joined
Jan 21, 2010
Messages
25,510
Ghosting on the reverse direction means your column drivers are slow to turn off.

You may need to order things to allow the drivers to turn off.

turn off the column driver
Turn off the row driver
load new data into the row array
enable the row driver.
turn on the column driver (next column)

A 4017, for example has a small overlap between the the states. To eliminate ghosting you need to be absolutely sure the rows are off before you step to the next column (since you can't disable the outputs).

If you're using 595's for the logic, then you can disable the outputs whenever you feel like it. Note that this makes them high impedance, not low, so you will need pull-down resistors to ensure the driver does off quickly.
 

gorgon

Jun 6, 2011
603
Joined
Jun 6, 2011
Messages
603
As long as you don't change the row and column data synchronous, you need to turn off the column driver before you change row data. To do this without setting the column driver signal to hi-z, you could add a gate after the register output, to make it possible to activly drive the driver signal low with a blanking signal.

Another thing to look at, is the base current you are feeding to the column transistor. If this is much too large, the amount of charge you need to bleed off before the transistor cuts off, will be time consuming. Relative to the short timing you have in the display matrix.
 

The Hammer

Sep 17, 2013
164
Joined
Sep 17, 2013
Messages
164
O.K., so I'm trying a direct from MCU to transistor drivers approach right now, but what can I do about this aside from turning off the LEDs for longer periods of time which will lower overall brightness? Do I try N-channel MOSFETs, are the BJTs the wrong type? I'd rather solve this by changing equipment out. These are only running at 18khz, not even close to the supposed 300mhz they're capable of.
 

gorgon

Jun 6, 2011
603
Joined
Jun 6, 2011
Messages
603
What are the current values you are using on the different components in your matrix? Can you draw one column driver and one row driver chematics, with all the component types and values? I suppose all drivers of each type is equal?
Regarding the ghosting, as long as it is trailing, you need to look at the timing for the column driver. If there is a smear in one column between rows, the problem is a memory/timing in the row driver. The ghosting will always be a conflict between the driver turning off and the driver turning on. The normal way to fix this is to insert a short blanking period when the column drivers change. It goes like this:

Turn off columnA, change to row dataB, turn on columnB.

If the columnA output is still active when new dataB is present, you will get a ghost effect.

If you have a oscilloscope you can look at the column driver and see how it turns off, relative to the input signal. You can the trim the values of resistors and pulldowns to get the best timing. Remember to use a single LED load for this, since this is the worst case for ghosting. When you have reached a minimum timing, check that the max load of LEDs will work properly too. If you are going to use this in a commersial product, you will need to specify the Hfe of the transistor, and trim components around the driver to work for the whole specified range, and the selected component accuracy.

Before you have maximized the hardware there is little use to fiddle with the software. When you know the timing of the drivers, you also know the minimum blanking time you will need in software, add a safety margin of some %, and implement the blanking in the software. Due to this blanking you should finish each column with all 20 rows, before swithing to the next.

One thing to have in mind is the efficiency of your matrix. If you are going to use a SPI controller to feed the 595 registers, you will be better off using all the 24 outputs from the 3 595s for your matrix. This way you only need to use 14 column drivers, and will reduce the number of switches, and blankings between columns.
 

The Hammer

Sep 17, 2013
164
Joined
Sep 17, 2013
Messages
164
I'm using the standard 2N3904 BJT for row drivers, and PN2222 for columns.

array2-png.13967
 

Attachments

  • array2.png
    array2.png
    12.7 KB · Views: 176

gorgon

Jun 6, 2011
603
Joined
Jun 6, 2011
Messages
603
You should add some base resistors on all the transistors. The way you do it draw a lot of extra current and strain both the driver outputs and the transistors. You also ovedrive the transistors and that may be the reason for you ghosting. Use 2k2 for the NPN, and 4k7 for the PNP. It could even be higher, but use that as a start.
 

The Hammer

Sep 17, 2013
164
Joined
Sep 17, 2013
Messages
164
You should add some base resistors on all the transistors. The way you do it draw a lot of extra current and strain both the driver outputs and the transistors. You also ovedrive the transistors and that may be the reason for you ghosting. Use 2k2 for the NPN, and 4k7 for the PNP. It could even be higher, but use that as a start.
Do you mean 2k2 resistors? I'm also not sure how I'm over-driving the transistors. One has 200mA max transistors being used to power single 20mA LEDs with.. if memory serves.. 500mA max transistors for the common cathodes on the LEDs.
 

The Hammer

Sep 17, 2013
164
Joined
Sep 17, 2013
Messages
164
There's also no variation in the ghosting when 1 LED on the matrix is lit versus all of them.
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Jan 21, 2010
25,510
Joined
Jan 21, 2010
Messages
25,510
Your diagram of the circuit above shows no resistor for the column driver transistor. The emitter is grounded and the base connects directly to the output pin. You need a resistor here to both save the transistor (and uC pin) from damage, but also to improve switching speed
 
Top