## Pin current consistency / atmega 4809

39 posts / 0 new
Author
Message

I've got a breadboard up and running with an adapter that uses four LTP-305G displays, each is 5x7, so this is 20 columns by 7 rows.

Using some dual logic level mosfets (DMN2004DMK-7) for the rows.

Using a 182 ohm resistor for each column.

I've targeted the current so that it is around 12.5mA pulse as 14% duty cycle which results in 1.785mA average pixel current.  All pixels on should be around 250 mA.

On the AVR side, the datasheet says:

current in/out vdd/gnd pins - 200mA - the device has 3 sets of pins.

i/o sink/source - 40mA

max sink/source per group i/o pin group - 200mA @ 25C

the footnotes say that PA, PC, an PD are in independent groups.  I've got 8 columns on A, 4 on C, 8 on D.

All off = 5mA.

All segments on 178-5=173mA, a far bit from 250mA.  5 columns on, 15 columns off (1 char all on) 64-5 = 59mA.

Looking at a pin that was left on while toggling the others off I saw a 0.35V difference in the pin voltage.

If I turn down the voltage from 5V to 4V or 3V, the problem of still present.

I finally measured the current starting with 1 column and turning on 1 more at a time - the first column is total current, the second is average.

 14.9 14.9 26.5 13.3 37.8 12.6 48.8 12.2 59.2 11.8 69.4 11.6 78.2 11.2 86.5 10.8 95.9 10.7 104.2 10.4 111.8 10.2 119.2 9.9 126.8 9.8 135.5 9.7 142.2 9.5 149 9.3 156 9.2 162.1 9 168.3 8.9 174.3 8.7

So the question is what could be done to improve consistency?

I thought about doing 10 columns by 14 rows, but then I'd have to double the pulse current and I suspect that might be even worse than what I am doing now!

If I can't drive the columns from the AVR, it just won't be a workable project.

You have several things happening, here.

1) The forward voltage of LEDs has a fair variation at any specific current. It is even more obvious when it is not constant current drive, which is your case. A series resistor DOES NOT make for constant current.

2) The port output transistor drop at a given current has some variation. In the standard AVRs, there used to be a graph called "pin strength" or something like that. It showed the typical voltage drop across an output transistor as a function of current. For an M328P, the simple Vdrop/Iout was on the order of 80 ohms to 100 ohms. The drop tends to be higher at low supply voltages (for a given load current) because the transistor is not turned on as hard.

3) If different ports are powered "independently", then there can be differing I*R drops inside the chip.

I don't know for sure if any or all of these can explain what you see. But, I would not be at all surprised if they all played a role.

It is important to note that the voltage drops for diodes and drive transistors depends on ON current, not average current (unless device heating starts to become a factor).

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Thu. Aug 29, 2019 - 11:09 PM

Thanks Jim.  That is sort of what I was wondering.

Oddly I can light a single row (all columns on) and it will use around 30mA, but as I turn on more rows it sure won't get to 210mA even though they are in an independent time slot from each other.

I realize it isn't a constant current driver, I'm trying to see what I can pack onto a very tiny display board, but maybe I need to rethink the whole thing or live with the variation.  I could try to adjust for it in software, but I'm not excited by that.

Maybe this isn't too bad to do a software correction for.  I already have a nice way to control row brightness and I can just apply a ratio correction to it depending on how many pixels are on.

The compensation MIGHT not be absolutely consistent from MCU chip to chip.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

>So the question is what could be done to improve consistency?

What do your eyes say? (see?)

Your eyes may already perceive many segments on as being brighter even if less current is running through each segment as a result. So it may be beneficial in the end and to some degree may balance the perceived brightness on both ends. In this case your eyes are what you have to satisfy, not your multi-meter.

How are you measuring current? Did you took into account ammeter voltage burden?

Edit: that is, you measure like this

Or

Last Edited: Fri. Aug 30, 2019 - 12:03 PM

The variances are easiest seen under certain test conditions.  One character left solid on (5 columns) and then blinking the other three characters will show a brightness change/pulsing in the left solid on character for example.  It is a row by row issue so displaying four letter A's for example where one row is solid on (20 pixels) and others have 12 or 8 pixels does not look bad at casual glance, but if you toggle back and forth between applying a dimming correction to those 12/8 lines, you can see that the uncorrected is flawed.  I think I'm going to add an option bit that the user can select that "balances brightness" unless I can think of a better term.  It has a penalty of reducing the display brightness by an average of 15%, so the user could decide if they would rather have uniformity or maximum brightness (which in this case just means brighter pixels if a row contains less than all 20 turned on).

El Tangas - various ways - some as you describe with a Fluke 189 measuring the current (it has two burden modes depending on the sensitivity you want) and also a digital power supply with built in measurements.  The issue scales with the voltage.  If you go down to 4V or 3V and light up one character and toggle the other three, it will be just as present.

I know it was stated the OP does not want to use external drivers, but a few tiny SOT-457 FET array chips would solve this problem by providing the needed drive.

Jim

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get \$5 free gold/silver https://www.onegold.com/join/713...

Do you mean for the columns Jim?  Do you have some parts I can look at?  I would consider it if they could be fit in...

alank2 wrote:
Do you mean for the columns Jim?

I just did a search at Digikey for a "fet array", choose one that fits your application.

Jim

edit: added part number the first one's I looked at were N/P pairs, it sounds like you would need one of the quad N-fets.

so maybe one of these: ALD110814,   not sure.

edit2: those don't have a very good Rds(500ohm) so maybe not.

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get \$5 free gold/silver https://www.onegold.com/join/713...

Last Edited: Fri. Aug 30, 2019 - 03:53 PM

They would have to be a high side driver essentially, not a low side ones like the N channels I am using for the rows.

I know what you mean about not finding what you want, lots of N/P pairs, etc.

One problem is that it doesn't look like there are any larger than 2 fets and that would be 10 parts...

I've targeted the current so that it is around 12.5mA pulse as 14% duty cycle which results in 1.785mA average pixel current

you could add an extra transistor to each column, so its current is more regulated by the driver itself:

https://electronics.stackexchange.com/questions/275433/what-is-the-formula-for-the-dependence-between-modulating-input-voltage-output

With one led, it would probably still work well at 5V, & much more uniform than what you have (any common npn/pnp is fine)...you can get the pairs in an smd package, maybe even with the resistor

Maybe set this up for Max brightness (at your max duty cycle), then if you run it at 50% ON/OFF of that, everything will be uniformly half as bright as the max setting.

Take a look here, might be some goodies (though sounds like you have no room for extra chips....but maybe)

also,

https://www.digikey.com/product-detail/en/microchip-technology/CL2N3-G/CL2N3-G-ND/4902353&?gclid=EAIaIQobChMIhr63-Y-v5AIVw8DACh3sRAevEAQYASABEgLCkPD_BwE

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sun. Sep 1, 2019 - 07:43 AM

from #1 you wrote :

I've got a breadboard up and running

Are you 100% sure that the power to the AVR are consistent in your setup, and it's not just a voltage drop over the chip.

At close to 100% cpu IO power load make sure that you have the correct caps over power (near the cpu).

Are you sure that the 14% (I guess 1/7) don't have a power overlap (I guess your know that the program don't have it, but perhaps you'r drivers turn on faster that off).

(just for test make it 12 or 10% with well defined off time).

In a 328PB design among other things I have 20 LED's and to save IO's I have 2x6 LED's charlie-plexed and the rest on the same amount of time. There I don't have have any problems.

Here is my code so far - still not polished by any means.

```#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <string.h>

#include "cdelay.h"

#define DISPLAY_ROWS 7

volatile uint8_t rowa[DISPLAY_ROWS],rowc[DISPLAY_ROWS],rowd[DISPLAY_ROWS];
volatile uint16_t rowbright[DISPLAY_ROWS];

volatile uint8_t displaybright=15,displayuniform=0;
volatile uint8_t commandbuffer,textbuffer[4],graphicsbuffer[20];
volatile uint8_t controlregisters[3] = {0b000000,0b000000,0b000011};

char s1[128];

const uint8_t bitson[] PROGMEM = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,
};

const uint8_t font[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 000/0x00
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 001/0x01
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 002/0x02
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 003/0x03
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 004/0x04
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 005/0x05
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 006/0x06
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 007/0x07
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 008/0x08
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 009/0x09
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 010/0x0a
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 011/0x0b
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 012/0x0c
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 013/0x0d
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 014/0x0e
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 015/0x0f
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 016/0x10
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 017/0x11
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 018/0x12
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 019/0x13
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 020/0x14
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 021/0x15
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 022/0x16
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 023/0x17
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 024/0x18
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 025/0x19
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 026/0x1a
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 027/0x1b
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 028/0x1c
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 029/0x1d
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 030/0x1e
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, // 031/0x1f
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 032/0x20
0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x04, // 033/0x21 !
0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, // 034/0x22 "
0x0a, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x0a, // 035/0x23 #
0x04, 0x1e, 0x05, 0x0e, 0x14, 0x0f, 0x04, // 036/0x24 \$
0x03, 0x13, 0x08, 0x04, 0x02, 0x19, 0x18, // 037/0x25 %
0x06, 0x09, 0x05, 0x02, 0x15, 0x09, 0x16, // 038/0x26 &
0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, // 039/0x27 '
0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, // 040/0x28 (
0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02, // 041/0x29 )
0x00, 0x04, 0x15, 0x0e, 0x15, 0x04, 0x00, // 042/0x2a *
0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00, // 043/0x2b +
0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x02, // 044/0x2c ,
0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, // 045/0x2d -
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, // 046/0x2e .
0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, // 047/0x2f /
0x0e, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0e, // 048/0x30 0
0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x0e, // 049/0x31 1
0x0e, 0x11, 0x10, 0x08, 0x04, 0x02, 0x1f, // 050/0x32 2
0x1f, 0x08, 0x04, 0x08, 0x10, 0x11, 0x0e, // 051/0x33 3
0x08, 0x0c, 0x0a, 0x09, 0x1f, 0x08, 0x08, // 052/0x34 4
0x1f, 0x01, 0x0f, 0x10, 0x10, 0x11, 0x0e, // 053/0x35 5
0x0c, 0x02, 0x01, 0x0f, 0x11, 0x11, 0x0e, // 054/0x36 6
0x1f, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02, // 055/0x37 7
0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e, // 056/0x38 8
0x0e, 0x11, 0x11, 0x1e, 0x10, 0x08, 0x06, // 057/0x39 9
0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00, // 058/0x3a :
0x00, 0x06, 0x06, 0x00, 0x06, 0x04, 0x02, // 059/0x3b ;
0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08, // 060/0x3c <
0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, // 061/0x3d =
0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, // 062/0x3e >
0x0e, 0x11, 0x10, 0x08, 0x04, 0x00, 0x04, // 063/0x3f ?
0x0e, 0x11, 0x10, 0x16, 0x15, 0x15, 0x0e, // 064/0x40 @
0x0e, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11, // 065/0x41 A
0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f, // 066/0x42 B
0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e, // 067/0x43 C
0x07, 0x09, 0x11, 0x11, 0x11, 0x09, 0x07, // 068/0x44 D
0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f, // 069/0x45 E
0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x01, // 070/0x46 F
0x0e, 0x11, 0x01, 0x1d, 0x11, 0x11, 0x1e, // 071/0x47 G
0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11, // 072/0x48 H
0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e, // 073/0x49 I
0x0e, 0x08, 0x08, 0x08, 0x08, 0x09, 0x06, // 074/0x4a J
0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11, // 075/0x4b K
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1f, // 076/0x4c L
0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11, // 077/0x4d M
0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11, // 078/0x4e N
0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e, // 079/0x4f O
0x0f, 0x11, 0x11, 0x0f, 0x01, 0x01, 0x01, // 080/0x50 P
0x0e, 0x11, 0x11, 0x11, 0x15, 0x09, 0x16, // 081/0x51 Q
0x0f, 0x11, 0x11, 0x0f, 0x05, 0x09, 0x11, // 082/0x52 R
0x1e, 0x01, 0x01, 0x0e, 0x10, 0x10, 0x0f, // 083/0x53 S
0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, // 084/0x54 T
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e, // 085/0x55 U
0x11, 0x11, 0x11, 0x11, 0x11, 0x0a, 0x04, // 086/0x56 V
0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0a, // 087/0x57 W
0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11, // 088/0x58 X
0x11, 0x11, 0x11, 0x0a, 0x04, 0x04, 0x04, // 089/0x59 Y
0x1f, 0x10, 0x08, 0x04, 0x02, 0x01, 0x1f, // 090/0x5a Z
0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0e, // 091/0x5b [
0x11, 0x0a, 0x1f, 0x04, 0x1f, 0x04, 0x04, // 092/0x5c Yen
0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e, // 093/0x5d ]
0x04, 0x0a, 0x11, 0x00, 0x00, 0x00, 0x00, // 094/0x5e ^
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, // 095/0x5f _
0x02, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, // 096/0x60 `
0x00, 0x00, 0x0e, 0x10, 0x1e, 0x11, 0x1e, // 097/0x61 a
0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x0f, // 098/0x62 b
0x00, 0x00, 0x0e, 0x01, 0x01, 0x11, 0x0e, // 099/0x63 c
0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1e, // 100/0x64 d
0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x0e, // 101/0x65 e
0x0c, 0x12, 0x02, 0x07, 0x02, 0x02, 0x02, // 102/0x66 f
0x00, 0x1e, 0x11, 0x11, 0x1e, 0x10, 0x0e, // 103/0x67 g
0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11, // 104/0x68 h
0x04, 0x00, 0x06, 0x04, 0x04, 0x04, 0x0e, // 105/0x69 i
0x08, 0x00, 0x0c, 0x08, 0x08, 0x09, 0x06, // 106/0x6a j
0x01, 0x01, 0x09, 0x05, 0x03, 0x05, 0x09, // 107/0x6b k
0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f, // 108/0x6c l
0x00, 0x00, 0x0b, 0x15, 0x15, 0x11, 0x11, // 109/0x6d m
0x00, 0x00, 0x0d, 0x13, 0x11, 0x11, 0x11, // 110/0x6e n
0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e, // 111/0x6f o
0x00, 0x00, 0x0f, 0x11, 0x0f, 0x01, 0x01, // 112/0x70 p
0x00, 0x00, 0x16, 0x19, 0x1e, 0x10, 0x10, // 113/0x71 q
0x00, 0x00, 0x0d, 0x13, 0x01, 0x01, 0x01, // 114/0x72 r
0x00, 0x00, 0x0e, 0x01, 0x0e, 0x10, 0x0f, // 115/0x73 s
0x02, 0x02, 0x07, 0x02, 0x02, 0x12, 0x0c, // 116/0x74 t
0x00, 0x00, 0x11, 0x11, 0x11, 0x19, 0x16, // 117/0x75 u
0x00, 0x00, 0x11, 0x11, 0x11, 0x0a, 0x04, // 118/0x76 v
0x00, 0x00, 0x11, 0x11, 0x15, 0x15, 0x0a, // 119/0x77 w
0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11, // 120/0x78 x
0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0e, // 121/0x79 y
0x00, 0x00, 0x1f, 0x08, 0x04, 0x02, 0x1f, // 122/0x7a z
0x08, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, // 123/0x7b {
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, // 124/0x7c |
0x02, 0x04, 0x04, 0x08, 0x04, 0x04, 0x02, // 125/0x7d }
0x00, 0x04, 0x08, 0x1f, 0x08, 0x04, 0x00, // 126/0x7e ->
0x00, 0x04, 0x02, 0x1f, 0x02, 0x04, 0x00, // 127/0x7f <-
};

const uint16_t gammabrightness[16] PROGMEM ={0, 100, 208, 381, 633, 983, 1450, 2053, 2812, 3748, 4881, 6233, 7827, 9684, 11829, 14285};
const uint16_t uniformbrightness[19] PROGMEM ={43272, 44723, 46154, 47518, 49041, 50623, 51971, 53606, 54364, 55162, 56084, 57096, 58059, 59070, 60117, 61234, 62363, 63485, 64490};

void apply_display_uniform()
{
uint8_t c1,c2;

for (c1=0;c1<DISPLAY_ROWS;c1++)
{
if (c2 && c2<20)
}
}

void render_from_text(char *AText)
{
uint8_t c1;

for (c1=0;c1<DISPLAY_ROWS;c1++)
{
}

if (displayuniform)
apply_display_uniform();
}

void render_from_graphics(char *AGraphics)
{
uint8_t c1;

for (c1=0;c1<DISPLAY_ROWS;c1++)
{
rowa[c1]=((AGraphics[0] & _BV(c1))?_BV(0):0) |
((AGraphics[1] & _BV(c1))?_BV(1):0) |
((AGraphics[2] & _BV(c1))?_BV(2):0) |
((AGraphics[3] & _BV(c1))?_BV(3):0) |
((AGraphics[4] & _BV(c1))?_BV(4):0) |
((AGraphics[5] & _BV(c1))?_BV(5):0) |
((AGraphics[6] & _BV(c1))?_BV(6):0) |
((AGraphics[7] & _BV(c1))?_BV(7):0);

rowc[c1]=((AGraphics[8] & _BV(c1))?_BV(0):0) |
((AGraphics[9] & _BV(c1))?_BV(1):0) |
((AGraphics[10] & _BV(c1))?_BV(2):0) |
((AGraphics[11] & _BV(c1))?_BV(3):0);

rowd[c1]=((AGraphics[12] & _BV(c1))?_BV(0):0) |
((AGraphics[13] & _BV(c1))?_BV(1):0) |
((AGraphics[14] & _BV(c1))?_BV(2):0) |
((AGraphics[15] & _BV(c1))?_BV(3):0) |
((AGraphics[16] & _BV(c1))?_BV(4):0) |
((AGraphics[17] & _BV(c1))?_BV(5):0) |
((AGraphics[18] & _BV(c1))?_BV(6):0) |
((AGraphics[19] & _BV(c1))?_BV(7):0);

}

if (displayuniform)
apply_display_uniform();
}

/*

need to enable slave spi

need to execute upon ss going high (interrupt)

need to have an interrupt on an rs change

*/

int main()
{
uint8_t c1;

//switch to 10 MHz
_PROTECTED_WRITE(CLKCTRL_MCLKCTRLB,0x01);

//disable dip disconnected inputs
PORTB_PIN0CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN1CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN2CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN3CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN4CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN5CTRL=PORT_ISC_INPUT_DISABLE_gc;

PORTC_PIN6CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTC_PIN7CTRL=PORT_ISC_INPUT_DISABLE_gc;

//portmux move spi to pe0-pe3
PORTMUX_TWISPIROUTEA=PORTMUX_SPI0_ALT2_gc;

//enable interrupts
sei();

//set columns to output
PORTA_DIRSET=0xff;
PORTC_DIRSET=0x0f;
PORTD_DIRSET=0xff;

//set rows to output
PORTF_DIRSET=0xff;

//rs select
PORTC_PIN4CTRL=PORT_ISC_BOTHEDGES_gc | PORT_PULLUPEN_bm;    //PORT_ISC_RISING_gc

//set debug as output
PORTC_DIRSET=_BV(5);

//set TCB0 to 700 Hz
TCB0.CTRLA=0x01;
TCB0.INTCTRL=0x01;
TCB0.CCMP=14285;

//set TCB1 for mode for brightness reducer
TCB1.CTRLA=0x01;
TCB1.CCMP=14285;

strcpy(s1,"ABCD");
for(;;)
for(c1=0;c1<16;c1++)
{
displaybright=c1;
s1[0]=c1/10+'0';
s1[1]=c1%10+'0';

s1[0]=0x1f;s1[1]=0x1f;s1[2]=0x1f;s1[3]=0x1f;

render_from_text(s1);

Delay_s(2);
}

for(;;);
}

ISR (PORTC_PORT_vect)
{
PORTC_OUTSET=_BV(5);

PORTC_INTFLAGS=_BV(4);

PORTC_OUTCLR=_BV(5);
}

ISR (TCB0_INT_vect)                       // Interrupt Service Request for TCB0 Overflow
{
static uint8_t row;

//PORTC_OUTSET=_BV(5);

//setup disable interrupt
TCB1.CNT=0;
TCB1.INTFLAGS=0x01;
TCB1.INTCTRL=0x01;

//disable all rows
PORTF_OUTCLR=0xff;

row++;
if (row==DISPLAY_ROWS)
row=0;

//set next data
//PORTD_OUT=data[row];
PORTA_OUT=rowa[row];
PORTC_OUT=rowc[row] /*| _BV(5)*/;
PORTD_OUT=rowd[row];
TCB1.CCMP=rowbright[row];

//clear this interrupt
TCB0.INTFLAGS=0x01;

//enable new row
if (rowbright[row])
PORTF_OUTSET=_BV(row);

//PORTC_OUTCLR=_BV(5);
}

ISR (TCB1_INT_vect)                       // Interrupt Service Request for TCB1 Overflow
{
//disable all rows
PORTF_OUTCLR=0xff;

//disable this interrupt
TCB1.INTCTRL=0x00;

}
```

avrcandies wrote:
you can get the pairs in an smd package, maybe even with the resistor
Indeed

Also, a JFET CCS; IIRC, factory trimmed to reduce the current variability and some with an external resistor to fine tune the LED string's brightness.

copied from TND6093 - Low VCE(sat) BJT's in Automotive Applications (ON Semiconductor, page 3)

NSI-series is 10mA to 350mA : ON Semiconductor - Linear LED Drivers

NSS60201L: Low V<sub>CE(sat)</sub> Transistor, NPN, 60 V, 4.0 A, SOT-23 Package

"Dare to be naïve." - Buckminster Fuller

alank2 wrote:

...

All segments on 178-5=173mA, a far bit from 250mA.  5 columns on, 15 columns off (1 char all on) 64-5 = 59mA.

..

Looking at a pin that was left on while toggling the others off I saw a 0.35V difference in the pin voltage.

..

If I can't drive the columns from the AVR, it just won't be a workable project.

Hmm.. you are asking quite a lot.

First problem is you are trying to source from the MCU, and MCUs work much better sinking, and the last time I measured an AVR, the source impedances were not great and included some common mode metal resistances.

Those numbers indicate you have about 2 ohms of common mode resistance, which will cause crosstalk. (0.35/173m  = 2.023 Ohms)

That can be in PCB, Drivers, displays and MCU.

Measure the voltage drop across the Vdd-Pin, and check how that varies with distance from VDD pins.

Those  DMN2004DMK-7 fets do not look great, lower RDS would help you.

`const uint8_t bitson[] PROGMEM = {`

PROGMEM? In 2019?

Unless this is C++ not C then a line like:

`      c2=pgm_read_byte(bitson+rowa[c1])+pgm_read_byte(bitson+rowc[c1])+pgm_read_byte(bitson+rowd[c1]);`

can drop all the pgm_read_byte() stuff (though your array indexing is then "unorthodox"!). I guess this becomes:

`      c2 = bitson[rowa[c1]] + bitson[rowc[c1]] + bitson[rowd[c1]];`

BTW spaces between operators in C cost nothing and make the code 100 times more readable.

In case you eventually decide to go for driver chips, take a look at these: http://www.abov.co.kr/en/index.p...

Then there are the many clones and derivatives: XX1628, XX1638, XX1668 (XX = TM, GN, AIP etc...)

For your project you would need 2 of these.

Holtek also has some options: https://www.holtek.com/product?p...

Finally, since manufacturers from the East seem to have more variety in the LED driver arena, among all these there must be something interesting: https://lcsc.com/products/LED-Dr...

For example, this one seems to be some kind of current sink array: https://datasheet.lcsc.com/szlcs...

Not interesting for your project, but interesting in general.

clawson wrote:

PROGMEM? In 2019?

BTW spaces between operators in C cost nothing and make the code 100 times more readable.

Thanks clawson, I've been in the "you can pry PROGMEM from my cold dead fingers" for too long.  I'll pull the PROGMEM and try to reformat and post a newer listing.

Again, not super polished, but SPI bits are in and it is working.

```#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>

#include "cdelay.h"

#define DISPLAY_ROWS 7

volatile uint8_t rowa[DISPLAY_ROWS], rowc[DISPLAY_ROWS], rowd[DISPLAY_ROWS];
volatile uint16_t rowbright[DISPLAY_ROWS];

volatile uint8_t displaybright=7, displayuniform=0, execute_now;
volatile uint8_t commandbuffer, textbuffer[4], textpos, graphicsbuffer[20], graphicspos;
volatile uint8_t controlregisters[3] = {0b000000,0b000000,0b000011};

/*

Register select (low=data, high=control) selects whether we are shifting data to the displays or control.

The control bytes are defined:

The top two bits [76] indicate the control register:

00 - control register 0 [default 000000]
bit 5:    mode (0=text, 1=graphics)
bit 4:    brightness balancing (1=yes, 0=no)
bits 3-0: brightness (0=0%, 15=100%)
01 - control register 1 [default 000000]
bit 5:    reset display (set to 1 to cause the display to reset when SS is deasserted)
bit 4:    all pixels off
bit 3:    all pixels on
bit 2:    direction (0=right to left, 1=left to right)
bit 1:    graphics shift (0=do not shift, 1=shift)
bit 0:    graphics invert (0=do not invert (bit0 up), 1=invert (bit0 down))
10 - control register 2 [default 000011]
bits 5-3: -
bits 2-0: text cursor bit (0=blank char, 1=bottom 1 row overlay, 7=solid cursor overlay)
11 - nop
bits 5-0: -

Graphics shift/invert table bits to dots

row        bits     inv      shift    invshift
row 1      0        7        1        6
row 2      1        6        2        5
row 3      2        5        3        4
row 4      3        4        4        3
row 5      4        3        5        2
row 6      5        2        6        1
row 7      6        1        7        0        (comma is in this row)
unused     7        0        0        7

Shifting is done depending on the status of register select and graphics/text mode.

Mode                        Queue Size
____                        __________

control                     1
text                        4
graphics                    20

*/

char s1[32], s2[32];

__flash const uint8_t bitson[] = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,
};

__flash const uint8_t font[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 000/0x00
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 001/0x01
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 002/0x02
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 003/0x03
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 004/0x04
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 005/0x05
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 006/0x06
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 007/0x07
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 008/0x08
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 009/0x09
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 010/0x0a
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 011/0x0b
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 012/0x0c
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 013/0x0d
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 014/0x0e
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 015/0x0f
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 016/0x10
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 017/0x11
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 018/0x12
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 019/0x13
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 020/0x14
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 021/0x15
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 022/0x16
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 023/0x17
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 024/0x18
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 025/0x19
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 026/0x1a
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 027/0x1b
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 028/0x1c
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 029/0x1d
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 030/0x1e
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, // 031/0x1f block
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 032/0x20 space
0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x04, // 033/0x21 !
0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, // 034/0x22 "
0x0a, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x0a, // 035/0x23 #
0x04, 0x1e, 0x05, 0x0e, 0x14, 0x0f, 0x04, // 036/0x24 \$
0x03, 0x13, 0x08, 0x04, 0x02, 0x19, 0x18, // 037/0x25 %
0x06, 0x09, 0x05, 0x02, 0x15, 0x09, 0x16, // 038/0x26 &
0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, // 039/0x27 '
0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, // 040/0x28 (
0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02, // 041/0x29 )
0x00, 0x04, 0x15, 0x0e, 0x15, 0x04, 0x00, // 042/0x2a *
0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00, // 043/0x2b +
0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x02, // 044/0x2c ,
0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, // 045/0x2d -
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, // 046/0x2e .
0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, // 047/0x2f /
0x0e, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0e, // 048/0x30 0
0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x0e, // 049/0x31 1
0x0e, 0x11, 0x10, 0x08, 0x04, 0x02, 0x1f, // 050/0x32 2
0x1f, 0x08, 0x04, 0x08, 0x10, 0x11, 0x0e, // 051/0x33 3
0x08, 0x0c, 0x0a, 0x09, 0x1f, 0x08, 0x08, // 052/0x34 4
0x1f, 0x01, 0x0f, 0x10, 0x10, 0x11, 0x0e, // 053/0x35 5
0x0c, 0x02, 0x01, 0x0f, 0x11, 0x11, 0x0e, // 054/0x36 6
0x1f, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02, // 055/0x37 7
0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e, // 056/0x38 8
0x0e, 0x11, 0x11, 0x1e, 0x10, 0x08, 0x06, // 057/0x39 9
0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00, // 058/0x3a :
0x00, 0x06, 0x06, 0x00, 0x06, 0x04, 0x02, // 059/0x3b ;
0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08, // 060/0x3c <
0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, // 061/0x3d =
0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, // 062/0x3e >
0x0e, 0x11, 0x10, 0x08, 0x04, 0x00, 0x04, // 063/0x3f ?
0x0e, 0x11, 0x10, 0x16, 0x15, 0x15, 0x0e, // 064/0x40 @
0x0e, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11, // 065/0x41 A
0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f, // 066/0x42 B
0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e, // 067/0x43 C
0x07, 0x09, 0x11, 0x11, 0x11, 0x09, 0x07, // 068/0x44 D
0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f, // 069/0x45 E
0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x01, // 070/0x46 F
0x0e, 0x11, 0x01, 0x1d, 0x11, 0x11, 0x1e, // 071/0x47 G
0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11, // 072/0x48 H
0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e, // 073/0x49 I
0x0e, 0x08, 0x08, 0x08, 0x08, 0x09, 0x06, // 074/0x4a J
0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11, // 075/0x4b K
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1f, // 076/0x4c L
0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11, // 077/0x4d M
0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11, // 078/0x4e N
0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e, // 079/0x4f O
0x0f, 0x11, 0x11, 0x0f, 0x01, 0x01, 0x01, // 080/0x50 P
0x0e, 0x11, 0x11, 0x11, 0x15, 0x09, 0x16, // 081/0x51 Q
0x0f, 0x11, 0x11, 0x0f, 0x05, 0x09, 0x11, // 082/0x52 R
0x1e, 0x01, 0x01, 0x0e, 0x10, 0x10, 0x0f, // 083/0x53 S
0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, // 084/0x54 T
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e, // 085/0x55 U
0x11, 0x11, 0x11, 0x11, 0x11, 0x0a, 0x04, // 086/0x56 V
0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0a, // 087/0x57 W
0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11, // 088/0x58 X
0x11, 0x11, 0x11, 0x0a, 0x04, 0x04, 0x04, // 089/0x59 Y
0x1f, 0x10, 0x08, 0x04, 0x02, 0x01, 0x1f, // 090/0x5a Z
0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0e, // 091/0x5b [
0x11, 0x0a, 0x1f, 0x04, 0x1f, 0x04, 0x04, // 092/0x5c Yen
0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e, // 093/0x5d ]
0x04, 0x0a, 0x11, 0x00, 0x00, 0x00, 0x00, // 094/0x5e ^
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, // 095/0x5f _
0x02, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, // 096/0x60 `
0x00, 0x00, 0x0e, 0x10, 0x1e, 0x11, 0x1e, // 097/0x61 a
0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x0f, // 098/0x62 b
0x00, 0x00, 0x0e, 0x01, 0x01, 0x11, 0x0e, // 099/0x63 c
0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1e, // 100/0x64 d
0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x0e, // 101/0x65 e
0x0c, 0x12, 0x02, 0x07, 0x02, 0x02, 0x02, // 102/0x66 f
0x00, 0x1e, 0x11, 0x11, 0x1e, 0x10, 0x0e, // 103/0x67 g
0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11, // 104/0x68 h
0x04, 0x00, 0x06, 0x04, 0x04, 0x04, 0x0e, // 105/0x69 i
0x08, 0x00, 0x0c, 0x08, 0x08, 0x09, 0x06, // 106/0x6a j
0x01, 0x01, 0x09, 0x05, 0x03, 0x05, 0x09, // 107/0x6b k
0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1f, // 108/0x6c l
0x00, 0x00, 0x0b, 0x15, 0x15, 0x11, 0x11, // 109/0x6d m
0x00, 0x00, 0x0d, 0x13, 0x11, 0x11, 0x11, // 110/0x6e n
0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e, // 111/0x6f o
0x00, 0x00, 0x0f, 0x11, 0x0f, 0x01, 0x01, // 112/0x70 p
0x00, 0x00, 0x16, 0x19, 0x1e, 0x10, 0x10, // 113/0x71 q
0x00, 0x00, 0x0d, 0x13, 0x01, 0x01, 0x01, // 114/0x72 r
0x00, 0x00, 0x0e, 0x01, 0x0e, 0x10, 0x0f, // 115/0x73 s
0x02, 0x02, 0x07, 0x02, 0x02, 0x12, 0x0c, // 116/0x74 t
0x00, 0x00, 0x11, 0x11, 0x11, 0x19, 0x16, // 117/0x75 u
0x00, 0x00, 0x11, 0x11, 0x11, 0x0a, 0x04, // 118/0x76 v
0x00, 0x00, 0x11, 0x11, 0x15, 0x15, 0x0a, // 119/0x77 w
0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11, // 120/0x78 x
0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0e, // 121/0x79 y
0x00, 0x00, 0x1f, 0x08, 0x04, 0x02, 0x1f, // 122/0x7a z
0x08, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, // 123/0x7b {
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, // 124/0x7c |
0x02, 0x04, 0x04, 0x08, 0x04, 0x04, 0x02, // 125/0x7d }
0x00, 0x04, 0x08, 0x1f, 0x08, 0x04, 0x00, // 126/0x7e ->
0x00, 0x04, 0x02, 0x1f, 0x02, 0x04, 0x00, // 127/0x7f <-
};

__flash const uint16_t gammabrightness[16] = {0, 100, 208, 381, 633, 983, 1450, 2053, 2812, 3748, 4881, 6233, 7827, 9684, 11829, 14285};
__flash const uint16_t uniformbrightness[19] = {43272, 44723, 46154, 47518, 49041, 50623, 51971, 53606, 54364, 55162, 56084, 57096, 58059, 59070, 60117, 61234, 62363, 63485, 64490};

void apply_display_uniform()
{
uint8_t c1,c2;

for (c1=0; c1<DISPLAY_ROWS; c1++)
{
c2=bitson[rowa[c1]] + bitson[rowc[c1]] + bitson[rowd[c1]];
if (c2 && c2<20)
rowbright[c1]=(uint16_t)((uint32_t)rowbright[c1] * uniformbrightness[c2 - 1] / 65536);
}
}

void render_from_text(char *AText)
{
uint8_t c1;

for (c1=0; c1<DISPLAY_ROWS; c1++)
{
rowa[c1]=(font[AText[0] * 7 + c1]) | (font[AText[1] * 7 + c1] << 5);
rowc[c1]=(font[AText[1] * 7 + c1] >> 3) | ((font[AText[2] * 7 + c1]&3) << 2);
rowd[c1]=(font[AText[2] * 7 + c1] >> 2) | (font[AText[3] * 7 + c1] << 3);

rowbright[c1]=gammabrightness[displaybright];
}

if (displayuniform)
apply_display_uniform();
}

void render_from_graphics(char *AGraphics)
{
uint8_t c1;

for (c1=0; c1<DISPLAY_ROWS; c1++)
{
rowa[c1]=((AGraphics[0] & _BV(c1)) ? _BV(0) : 0) |
((AGraphics[1] & _BV(c1)) ? _BV(1) : 0) |
((AGraphics[2] & _BV(c1)) ? _BV(2) : 0) |
((AGraphics[3] & _BV(c1)) ? _BV(3) : 0) |
((AGraphics[4] & _BV(c1)) ? _BV(4) : 0) |
((AGraphics[5] & _BV(c1)) ? _BV(5) : 0) |
((AGraphics[6] & _BV(c1)) ? _BV(6) : 0) |
((AGraphics[7] & _BV(c1)) ? _BV(7) : 0);

rowc[c1]=((AGraphics[8] & _BV(c1)) ? _BV(0) : 0) |
((AGraphics[9] & _BV(c1)) ? _BV(1) : 0) |
((AGraphics[10] & _BV(c1)) ? _BV(2) : 0) |
((AGraphics[11] & _BV(c1)) ? _BV(3) : 0);

rowd[c1]=((AGraphics[12] & _BV(c1)) ? _BV(0) : 0) |
((AGraphics[13] & _BV(c1)) ? _BV(1) : 0) |
((AGraphics[14] & _BV(c1)) ? _BV(2) : 0) |
((AGraphics[15] & _BV(c1)) ? _BV(3) : 0) |
((AGraphics[16] & _BV(c1)) ? _BV(4) : 0) |
((AGraphics[17] & _BV(c1)) ? _BV(5) : 0) |
((AGraphics[18] & _BV(c1)) ? _BV(6) : 0) |
((AGraphics[19] & _BV(c1)) ? _BV(7) : 0);

rowbright[c1]=gammabrightness[displaybright];
}

if (displayuniform)
apply_display_uniform();
}

/*

need to enable slave spi

need to execute upon ss going high (interrupt)

need to have an interrupt on an rs change

*/

int main()
{
uint8_t c1,c2;

//switch to 10 MHz
_PROTECTED_WRITE(CLKCTRL_MCLKCTRLB,0x01);

//disable dip disconnected inputs
PORTB_PIN0CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN1CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN2CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN3CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN4CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTB_PIN5CTRL=PORT_ISC_INPUT_DISABLE_gc;

PORTC_PIN6CTRL=PORT_ISC_INPUT_DISABLE_gc;
PORTC_PIN7CTRL=PORT_ISC_INPUT_DISABLE_gc;

//portmux move spi to pe0-pe3
PORTMUX_TWISPIROUTEA=PORTMUX_SPI0_ALT2_gc;

//enable interrupts
sei();

//set columns to output
PORTA_DIRSET=0xff;
PORTC_DIRSET=0x0f;
PORTD_DIRSET=0xff;

//set rows to output
PORTF_DIRSET=0xff;

//rs select (PC4)
PORTC_PIN4CTRL=PORT_ISC_BOTHEDGES_gc | PORT_PULLUPEN_bm;

//spi ss (PE3)    mosi=PE0, miso=PE1, sck=PE2, ss=PE3
PORTE_PIN0CTRL=PORT_PULLUPEN_bm;
PORTE_DIRSET=_BV(1);
PORTE_PIN2CTRL=PORT_PULLUPEN_bm;
PORTE_PIN3CTRL=PORT_ISC_RISING_gc | PORT_PULLUPEN_bm;
SPI0_CTRLA=SPI_ENABLE_bm;
SPI0_INTCTRL=SPI_IE_bm;

//set debug as output
PORTC_DIRSET=_BV(5);

//set TCB0 to 700 Hz
TCB0.CTRLA=0x01;
TCB0.INTCTRL=0x01;
TCB0.CCMP=14285;

//set TCB1 for mode for brightness reducer
TCB1.CTRLA=0x01;
TCB1.CCMP=14285;

for (;;)
{
if (execute_now)
{
if (PORTC_IN & _BV(4))
{
//command mode
c1=commandbuffer;

switch (c1 >> 6)
{
case 0 : controlregisters[0]=c1 & 0x3f;
displaybright=c1 & 0x0f;
displayuniform=c1 & 0x10;

break;
case 1 : controlregisters[1]=c1 & 0x3f; break;
case 2 : controlregisters[2]=c1 & 0x3f; break;
}
}

//render
if (controlregisters[0] & _BV(5))
{
//graphics
c2=graphicspos;
memcpy(s1,(char*)graphicsbuffer,20);
for (c1=0; c1<20; c1++)
{
s2[c1]=s1[c2++];
if (c2==20)
c2=0;
}

render_from_graphics(s2);
}
else
{
//text
c2=textpos;
memcpy(s1,(char*)textbuffer,4);
for (c1=0; c1<4; c1++)
{
s2[c1]=s1[c2++];
if (c2==4)
c2=0;
}

render_from_text(s2);
}

execute_now=0;
}
}
for (;;);
}

void set_next_spi_data()
{
//are we in command or data mode?
if (PORTC_IN & _BV(4))
SPI0_DATA=commandbuffer;
else
{
if (controlregisters[0] & _BV(5))
SPI0_DATA=graphicsbuffer[graphicspos];
else SPI0_DATA=textbuffer[textpos];
}
}

ISR (PORTC_PORT_vect)
{
//RS has changed (low=data, high=control)

//clear this interrupt
PORTC_INTFLAGS=_BV(4);

//setup next data
set_next_spi_data();

//PORTC_OUTSET=_BV(5);
//PORTC_OUTCLR=_BV(5);
}

ISR (PORTE_PORT_vect)
{
//clear this interrupt
PORTE_INTFLAGS=_BV(3);

//SS has been released, execute what is in buffer
execute_now=1;
}

ISR (SPI0_INT_vect)
{
uint8_t c1;

//PORTC_OUTSET=_BV(5);

//clear this interrupt
SPI0_INTFLAGS=SPI_IF_bm;

//get this data
c1=SPI0_DATA;

if (PORTC_IN & _BV(4))
commandbuffer=c1;
else
{
if (controlregisters[0] & _BV(5))
{
graphicsbuffer[graphicspos++]=c1;
if (graphicspos==20)
graphicspos=0;
}
else
{
textbuffer[textpos++]=c1;
if (textpos==4)
textpos=0;
}
}

//setup next data
set_next_spi_data();

//PORTC_OUTCLR=_BV(5);
}

ISR (TCB0_INT_vect)                       // Interrupt Service Request for TCB0 Overflow
{
static uint8_t row;

//clear this interrupt
TCB0.INTFLAGS=0x01;

//setup disable interrupt
TCB1.CNT=0;
TCB1.INTFLAGS=0x01;
TCB1.INTCTRL=0x01;

//disable all rows
PORTF_OUTCLR=0xff;

row++;
if (row==DISPLAY_ROWS)
row=0;

//set next data
PORTA_OUT=rowa[row];
PORTC_OUTCLR=0x0f;                        //the CLR/SET allow us to keep the debug working
PORTC_OUTSET=rowc[row] /*| _BV(5)*/;
//PORTC_OUT=rowc[row] /*| _BV(5)*/;
PORTD_OUT=rowd[row];
TCB1.CCMP=rowbright[row];

//enable new row
if (rowbright[row])
PORTF_OUTSET=_BV(row);
}

ISR (TCB1_INT_vect)                       // Interrupt Service Request for TCB1 Overflow
{
//clear this interrupt
TCB1.INTFLAGS=0x01;

//disable all rows
PORTF_OUTCLR=0xff;

//disable this interrupt
TCB1.INTCTRL=0x00;
}
```

Your sei() happens very (too?) early. It's done long before:

```  //set TCB0 to 700 Hz
TCB0.CTRLA=0x01;
TCB0.INTCTRL=0x01;
TCB0.CCMP=14285;

//set TCB1 for mode for brightness reducer
TCB1.CTRLA=0x01;
TCB1.CCMP=14285;```

Are you really ready for an interrupt to fire part way through such initialisation?

The usual structure for an embedded program is:

```int main(void) {
init_this();
init_that();
// all set so...
sei();
while(1) {
do_this();
do_that();
}
}```

probably unwise to sei() before all the init()s are done.

Good point Clawson; I'll fix that.

After much routing pain, and a cramping mouse hand, a completed a 4 layer board that is the size of the displays only.  Here it is rendered at Oshpark.  C4/C5 are some 1206 pads for 1 or 2 100uF ceramic caps, not sure how much will be needed, maybe just one.  Q1-Q4 are dual mosfets.  RN1-RN5 are quad iso resistors.  U5 is atmega4809.  I didn't have room for a programming header so I just routed UPDI to another DIP pin.

. x V . . x V . . S x . . R x .

.     . .     . .     . .     .

.     . .     . .     . .     .

.     . .     . .     . .     .

.     . .     . .     . .     .

.       .       .       .

. O I . . C U . . x G . . x G .

x - pin present, but unused/disconnected

V - vcc

S - slave select

I - data in (mosi)

O - data out (miso)

R - register select

C - clock (sck)

G - ground

U - UPDI

I tried to make it so that if you install it upside down, pins that matter will not be connected.  GND and VCC for example will fall into an unused pin that is disconnected.

My plan to build it is to align 4 displays and then use some masking tape to keep them aligned to each other.  Then try to put a consistent amount of solder on each top circular pad, the apply some flux, then place displays on top as aligned as I can muster and try the skillet method for reflow.  Maybe put a small weight on the top of the displays to make sure they fall into place.  Then hand solder the underside.  Finally use a breadboard will the 8 sets of dual pins put into it and lay the board on top.  Then carefully solder the headers on from the sides (between the displays and the pcb there should be room).

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Thanks avrcandies!  I hope it works NICE when I get the boards back and build one!

For anyone entertained by such things, here is the routing for anyone interested in the crazy I did yesterday:

Some of the vias are ground vias.

Top layer:

VCC inner layer - has half the copper thickness so I made the traces a little larger (when I had to put traces on it...):

GND inner layer - has half the copper thickness so I made the traces a little larger (when I had to put traces on it...):

Bottom layer:

The toughest thing was finding a place for a via that didn't have anything running into it on all 4 layers, and keeping the DRC happy.

My plan to build it is to align 4 displays and then use some masking tape to keep them aligned to each other.

If that does't pan out  , you could  "mount" sockets & plug the displays in....then you'd be able to tilt & wiggle the displays around slightly to keep the side-to-side distances exact.  However, the tape should do the trick

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

You are the master of puns!  I was thinking of doing a socket version too, so one could change the color from red to green.  I'll post some pics when I get everything done in a couple of weeks!

alank2 wrote:

My plan to build it is to align 4 displays and then use some masking tape to keep them aligned to each other.  Then try to put a consistent amount of solder on each top circular pad, the apply some flux, then place displays on top as aligned as I can muster and try the skillet method for reflow.  Maybe put a small weight on the top of the displays to make sure they fall into place.  Then hand solder the underside.  Finally use a breadboard will the 8 sets of dual pins put into it and lay the board on top.  Then carefully solder the headers on from the sides (between the displays and the pcb there should be room).

You may be better to use solder paste, as that can also help 'stick' the displays in place.

Another trick we have used, is to make mounting partially thru hole, and j-lead the SMD ones.

The thru hole pads 'fit where they can', and if you target the outer corners those can be tack soldered, and the inner pads can have oval pads for the J-Leads.

alank2 wrote:

GND inner layer - has half the copper thickness so I made the traces a little larger (when I had to put traces on it...):

VCC inner layer - has half the copper thickness so I made the traces a little larger (when I had to put traces on it...):

Does your CAD pgm not support copper pour ? - that lowers both electrical and thermal resistance.

The VCC and GND layers are poured (as are the top and bottom as well).  I just didn't show them that way because it is tougher to see the traces.  The above traces on the VCC/GND layers are exceptions that I couldn't fit on the top/bottom layers.

That’s the busiest little board I’ve seen in a while! Nice work!

Tom Pappano
Tulsa, Oklahoma

I'm surprised no one has spotted the extra trace going nowhere on the VCC plane yet!  Thankfully it does no harm!

Well ... my first attempt at it was a total fail.  I tried it with regular solder and displays aligned with tape.  It was going ok until I couldn't adjust it because of the heat and then the heat got to the tape and curled up and pulled the displays out of alignment.  So I pulled them off and tried to get the board out and it slipped and fell upside down in the skillet and smeared solder all over the place.

Solder paste is the only way to get the displays on the top of the board.  Luckily I know the master of solder paste and baking boards and went over to tpappano's shop.  After running a display through his oven that has broken pin and finding that it did not melt (I was surprised), he solder pasted two boards and I carefully placed the displays and he baked them.  I finished hand soldering the bottom of it last night and it works great.

I left off the one or two main caps so far to do some voltage drop testing when switching between all on an all off pixels.

Last Edited: Tue. Sep 17, 2019 - 02:53 PM

Board looks excellent!  Too bad nothing shows up on the displays...keep at it..you'll get it it to say BEER or something.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!