Flipping Back to Basics - Transistor Trickery

19 April 2014

After working out in my last post that I wouldn't be able to control these dots directly from a microcontroller I'm going to take a look at the PCB that's connected to it.

Using the continuity checker on the multimeter I can map the 70 pins on the display panel connector: 70 pin connections on display panel

The right-most 16 pins don't seem to be connected to anything.

I can also map the relevant pin connections on the attached PCB: Annotated PCB on the attached board

This looks like both the row positive and negative are managed on another separate board. The columns, however, are managed on this board. The 4 A2982SLW's on the right are the current source for the columns, meaning the 30 little 6-pin components on the left must provide some way to sink the current.

Both the current sources and sinks are switched from the shift registers at the top of the board. These shift registers are connected to one side of the 6 jumper pins at the top right. The jumper allows the main control board to address a column on any of the 3 display panels that make up the whole by sending the serial data over the correct pin.

Due to the rows being managed back on the main control board I can't interface purely with this board to control the dots. I'm going to change my approach slightly and build my own replacement to this board that will talk directly to the 70 pins on the display panel.

Transistors

A transistor, as I understand them, can be used to amplify current in a switch type manner. There are 2 types: NPN and PNP. NPN transistors are used to switch on the low voltage side of the load, whereas PNP transistors are used to switch on the high voltage side of the load. The load, in this case, will be the coil on each dot. I don't know why the 2 different types are used differently but I'm going to stick to this as it's what I've read.

They each have 3 connectors: emitter, base, and collector. When the base of an NPN transistor is connected to a positive voltage, it allows current to flow from the collector through to the emitter. Conversely, when the base of a PNP transistor is connected to ground, it allows current to flow from the emitter through to the collector.

With this in mind, the following circuit should be able to flip a single dot back and forth: Single dot circuit schematic

When switches A1 and A2 are closed, the dot should flip one way. Closing B1 and B2 should flip the dot back again.

I've built the circuit on a breadboard, replacing the 4 switches with 4 pins on a microcontroller.
Single dot circuit on a breadboard

Code

const uint8_t columnVcc = 1 << PORTD7;
const uint8_t columnGnd = 1 << PORTD4;
const uint8_t rowVcc = 1 << PORTD5;
const uint8_t rowGnd = 1 << PORTD6;

void resetPins()
{
    PORTD = rowVcc | columnVcc;
}

void on()
{
    PORTD |= rowGnd;
    PORTD &= ~columnVcc;
    _delay_ms(1);
    resetPins();
}

void off()
{
    PORTD |= columnGnd;
    PORTD &= ~rowVcc;
    _delay_ms(1);
    resetPins();
}

int main(void)
{
    DDRD = 0xFF;
    resetPins();
    off();
    _delay_ms(250);

    while(1)
    {
        on();
        _delay_ms(50);
        off();
        _delay_ms(50);
    }
}

By default, all of the general purpose input/output pins are high impedance. This means they are essentially disconnected from the circuit. The positive lines to each side of the coil go through a PNP transistor, which require a low base to turn on. Taking the base high should turn them off. Likewise the negative lines to each side of the coil go through an NPN transistor which requires a low base to turn them off. The resetPins function takes care of this.

Next Step

I'm going to make things a little less boring than a single dot. That is all.

Flipping Back to Basics - Current Situation

24 March 2014

I'm eager to get a microcontroller hooked up to these dots now that I know how to address individual pixels. The microcontrollers I have to hand are ATmega48PA's and they have 23 input/output pins. To control all 240 dots I need to be able to control 46 wires so as it stands I could only control 7 columns of 8 rows, as a row takes up 2 wires.

Fortunately I have a couple of 8-bit shift registers, these take 3 connections as input and offer up to 8 as ouput. They are serial in parallel out (SIPO) and can be daisy-chained, meaning that the same 3 connections can control any multiple of 8 outputs. Having 2 of these means I get an extra 16 outputs by sacrificing 3, allowing me to control a total of 20 columns of 8 rows. That's two thirds of the display so I'm happy with that for a first attempt.

These microcontrollers are delicate things so I want to be sure I'm not going to break them. I made a reading of the current across the coil of a single dot to be 53mA. The datasheet for the ATmega48PA says it can only handle 40mA on any pin though.

Maximum ratings from the ATmega48PA datasheet

Bad times. Fortunately Ohm's Law states that I = V/R, that is, current is equal to voltage divided by resistance. This can be rearranged to R = V/I, meaning that if I know the desired current and voltage I can work out what resistance I need in total. The current is 0.04A, voltage will be 4.5v, so 4.5/0.04 = 112.5, meaning I need a total resistance of 112.5Ω. The multimeter tells me the resistance of the coil by itself is 83.6Ω, 112.5 - 83.6 = 28.9, the closest resistor I have is 27Ω so I'll measure some values using that in series with the coil to see what current is drawn.

It only draws 41mA, which is great, I'm happy to risk that tiny bit extra with the microcontroller. Unfortunately at that current it doesn't activate the dot and perform any flipping action.

That would explain what these boys are doing on the connecting PCB that I've not yet looked at much. 4 source drivers on adjoining PCB These are 8-channel source drivers. They allow 8 low-current inputs to drive 8 high-current outputs. 4 of them make 32 high-current outputs which is just enough for the 30 columns.

Next Step

I'm not going to be able to control the dots without the extra components on the adjoining PCB. I'm going to have to follow the same process again with that in order to work out how to control that which will in turn control the display dots.

Flipping Back to Basics - Multiplexing

24 March 2014

Last time I deconstructed the dots to find out how they work, and was able to flip a single dot back and forth by applying a voltage across 2 connectors. Now I'd like to know how 240 dots, with 4 connections each, are controlled from just 60 wires.

Tracing

I'm ignoring the existence of the LEDs for the time being and focussing on the flipping side of things. Using the multimeter to check for continuity I'm able to construct the following trace of connections up on the rear of the display panel PCB. I'm not fantastic at editing images so please bear with me throughout this.

Rear of display PCB with connections traced

Columns are made up of one end of the electropermanent magnet coil. Rows are made up of the other end of the coil. The rows are passing through a tiny IC, visible in the zoomed box, and whilst I have no idea what this is all about just yet, this still looks to me like multiplexing. I've seen examples of multiplexing LEDs before and it's a fairly basic concept. All the anodes in a row are connected together, and all the cathodes in a column are connected together. This enables the addressing of individual LEDs by applying a positive voltage to the desired row, and grounding the desired column. This leaves just a single route for the current to flow, like so:

Diagram showing current flow through multiplexed LEDs

This multiplexing has 2 wires per LED, one positive and the other negative. The traced connections on the display, however, reveals 3 wires per coil. I need to dig in to multiplexing a bit further to see if I can make sense of this.

Multiplexing

One of the reasons multiplexing works is that LEDs only let current flow in one direction. If this was done with bulbs instead of LEDs then all of them would be illuminated since the current could freely travel through the connected circuit. The coils that control the flipping in this circuit will let current flow in either direction so without some further magic there would be no way to address one by itself. This suggests to me that the little IC is perhaps some sort of diode, but diodes have only 2 leads, one anode and one cathode. Multimeter to the rescue again with its diode tester.

Dual diode IC with diodes traced

The little IC houses 2 diodes arranged as in the above amendment to the original trace. This is becoming clearer now. The double connections to each row are to allow the multiplexing to take place with current flowing in either direction. When we want to turn a dot on we apply a positive voltage to the rows red wire and connect the columns green wire to ground. To turn the dot back off we apply a positive voltage to the columns green wire and connect the rows blue wire to ground. I've (tried to) illustrate this below. Current flow to turn dot on Current flow to turn dot off

The designers of this board could easily have given the columns 2 wires and a diode and had the row use only 1 wire, but there's a good reason for how it's done. With 30 column wires plus 16 (2 * 8 rows) row wires, there is a total of 46 connections required in order to address every single dot individually. If we turn it round and calculate the alternative, we get 8 row wires plus 60 (2 * 30 columns) column wires giving a total of 68 connections required. Clearly the former, with fewer required connections, is more efficient.

Next step

Now that the mechanics of addressing individual dots are known I want to construct a simple circuit connected to this board and turn some dots on and off using a microcontroller. This should be a bit of fun and give some nice visible results.

Flipping Back to Basics - Deconstruction

23 March 2014

Last time I was able to work out the make-up of a message to the display over RS-485, there are still many unknowns in the protocol though. The main one that concerns me is the apparent reset of all the dots after 50 seconds. The next logical step, therefore, is to discern what the different control characters are used for.

Software is logical, I am not.

My overall goal is to control arbitrary dots, rather than just send text messages, so I'm going to dive into the other end of this now and focus on the display hardware. By understanding what flipping a dot entails I hope to be able to control them without the use of the RS-485 interface. I'll be working backwards, starting with the visible parts and tracing them back through the circuitry.

Deconstruction

The 90x8 display I used last time is the victim, it doesn't put up much of a fight as I fiddle with its nuts and cables. Inside it's made up of the main control board which has the RS-485 and power connectors. Connected to this board, by a single 60 ribbon cable with multiple connectors, are 3 panels containing the dots. I'll take one of these panels out to inspect further.

Single 30x8 display panel

The Dots

The dots are the visible portion of the panel and are very simple things. Each dot is a black plastic square with one half painted a nice fluorescent yellow. On the fluorescent half there is a small hole in the corner to allow light to shine through from the LED underneath.

5x1 plastic dots removed from the panel

There's then a small piece of plastic, half the size of the square, pivoted about the centre. One side is painted fluorescent yellow such that when flipped correctly the entire dot appears either yellow or black. The key to their functionality is a small permanent magnet near the pivot point, this enables it to be flipped using magnetic force very easily.

Electromagnets and LEDs on the panel

The plastic dots clip on to these little things. Each one of those is a coil of wire wrapped around a magnetic core creating an electropermanent magnet. Electropermanent magnets work by using the coil to change the polarity of the magnetic core. This means that the magnet will continue attracting or repelling after power is cut and so current only needs to flow through it for a very short period of time.
Next to each coil is the LED that shines through the hole in the plastic dot.

Flipping Out

Turning this display panel over allows us to see the connections for both the electropermanent magnet and the LED. I've highlighted them on the diagram that follows. Connections for a single dot on the PCB The electropermanent magnet is connected at 1 and 2, the LED is at 3 and 4

I want to find out how much voltage needs to be applied for the dot to flip but I don't want to damage anything by using too much. I've got a variable transformer with 1.5v intervals so I can start out low and move up until I get it to flip.

We get some flips at 4.5v. The dot flips to show the yellow side when the current flow is from 2 to 1, and back to black when the current flows in the opposite direction, from 1 to 2. I measure the current draw for a single coil at 4.5v to be 53mA.

Next Step

The next step I want to take is tracing the circuit connections backwards from the dot to see some of the techniques used to control 240 dots, with 4 connections each, from just 60 wires in the ribbon cable.

Analysing the Flip Dot Display Command

20 March 2014

On the back of my previous post I've modified the LINQPad script to allow me to define a conversation with the display and set it up with the observed conversation from last time.

static int baudRate = 9600;
static int dataBits = 8;
void Main()
{
    using (var port = new SerialPort("COM3", baudRate, Parity.None, dataBits, StopBits.One))
    {
        try {
            port.Open();

            port.Send(2, 49, 3, 225, 213, 19);
            port.Expect(2, 49, 4, 224, 0, 144, 26);
            port.Send(2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 76, 97, 110, 103, 115, 116, 111, 110, 101, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188);
            port.Expect(2, 49, 3, 232, 68, 1);

            port.Close();
        }
        catch (Exception e) {
            e.Dump();
        }
    }
}

static class Extensions {
    public static string ToDebugString(this IEnumerable<byte> bytes) {
        return string.Join(", ", bytes.Select(x=>x.ToString()).ToArray());
    }

    public static void Send(this SerialPort port, params byte[] data) {
        port.Write(data, 0, data.Length);
        data.ToDebugString().Dump("Sent");
    }

    public static void Expect(this SerialPort port, params byte[] expectedData) {
        var timeout = TimeSpan.FromMilliseconds(500);

        var received = new List<byte>();
        var dataReceived = new SerialDataReceivedEventHandler((sender, args) => {
            while (port.BytesToRead != 0) {
                received.Add((byte)port.ReadByte());
            }
        });

        try {
            port.DataReceived += dataReceived;
            Thread.Sleep(timeout);
            if (received.SequenceEqual(expectedData)) {
                received.ToDebugString().Dump("Received");
            }
            else {
                throw new DataException("Expected { " + expectedData.ToDebugString() + " } but got { " + received.ToDebugString() + " }");
            }
        }
        finally {
            port.DataReceived -= dataReceived;
        }
    }
}

This will allow me to quickly test ideas about the protocol to see how the display responds.

Idea 1 - Addressing Dots/Pixels

I'm assuming that the protocol sends data about individual pixels and that all the brains behind converting text into pixels sits in the controller, which my code is replacing. In order to address each pixel I would expect some form of xy coordinates to be sent so I'm going to think about how that might be encoded in the command.

This display is 8 dots tall so encoding a y-coordinate could use 3 bits. It's also 90 dots wide, to encode the the x-coordinate would take 7 bits. This makes 10 bits, perhaps an 11th bit to indicate whether the dot should be on or off, meaning each pixel would be 2 bytes long. Turning the pixel at (42,3) could look something like this:

0 0 1 0 1 0 0 1 | 0 0 0 0 0 1 0 1
  ------x------ |         --y-- on/off

Not only is that lot of wasted bits, it also means that just the first L on the screen would take 34 bytes to communicate yet we only have 40 bytes in the command after the initial 2 display address bytes. Interestingly the 3rd byte is 39, could this be representing the size of the message that follows? I'm going to assume yes for now, I can always backtrack.

Another possibility for addressing individual pixels is for it to send a full sweep of data for the whole display. So one byte for every column, with the 8 bits representing each row. This would be more efficient with no wasted bits, however it would still require 90 bytes in the message.

Idea 2 - Hack, Prod, Hack

I don't have another idea, so I'm going to try changing numbers in the command to see what the outcome is.

I choose 101, I think because it's palindromic, I like palindromes. I'll change it to 121. Sending this modified command results in no response from the display and no change in the dots on show, this is not what I expected or wanted. I was hoping for some form of error code if I sent a bad command. Reverting my change and running the original to make sure I've not broken something elsewhere results in no data being returned after the first polling command. But wait...

Display showing "Langstony"

The display has updated and a character has changed!

This is interesting, I'm assuming that 2, 49, 3, 225, 213, 19 somehow made the modified command I sent take effect. Let's change some more characters and send that extra message afterwards.

port.Send(2, 49, 3, 225, 213, 19);
port.Expect(2, 49, 4, 224, 0, 144, 26);
port.Send(2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 78, 98, 110, 103, 115, 116, 141, 110, 121, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188);
port.Send(2, 49, 3, 225, 213, 19);
port.Expect(2, 49, 3, 232, 68, 1);

Display showing random characters

ASCII and ye shall receive

It's hit me. These are ASCII codes.

I'm running the original command through a bit of conversion like so:

data.Select(b => new {
    dec = b,
    hex = b.ToString("X2"),
    bin = Convert.ToString(b, 2).PadLeft(8, '0'),
    ascii = ASCIIEncoding.ASCII.GetString(new []{ b }),
}).Dump();
dec hex bin ascii
2 02 00000010
49 31 00110001 1
39 27 00100111 '
229 E5 11100101 ?
229 E5 11100101 ?
32 20 00100000  
156 9C 10011100 ?
170 AA 10101010 ?
57 39 00111001 9
228 E4 11100100 ?
32 20 00100000  
40 28 00101000 (
166 A6 10100110 ?
76 4C 01001100 L
dec hex bin ascii
97 61 01100001 a
110 6E 01101110 n
103 67 01100111 g
115 73 01110011 s
116 74 01110100 t
111 6F 01101111 o
110 6E 01101110 n
101 65 01100101 e
32 20 00100000  
66 42 01000010 B
117 75 01110101 u
115 73 01110011 s
105 69 01101001 i
110 6E 01101110 n
dec hex bin ascii
101 65 01100101 e
115 73 01110011 s
115 73 01110011 s
228 E4 11100100 ?
40 28 00101000 (
78 4E 01001110 N
166 A6 10100110 ?
80 50 01010000 P
97 61 01100001 a
114 72 01110010 r
107 6B 01101011 k
254 FE 11111110 ?
152 98 10011000 ?
188 BC 10111100 ?

The text is quite clear in there, 9 Langstone Business Park. There are spaces between the first two words but some sort of control characters between the rest. Before both the number 9 and the text there is a 32 followed by 2 characters. I think these 2 characters indicate the beginning of some segment of the route and the 229, 229, 32 is the type of command, which is displaying a bus route on the display.

Modifying the Message

I want to display mattscode.com on this, my current thinking is summarised as:

  • 2 bytes - display address
  • 1 byte - command size
  • 3 bytes - type of command
  • 2 bytes - control characters indicating start of some command section
  • 13 bytes - ASCII string
  • 3 bytes - control characters indicating end of command

Putting this together I have:

port.Send(
    2, 49,          // display address
    21,             // command size
    229, 229, 32,   // type of command
    40, 166,        // start of command section
    109, 97, 116, 116, 115, 99, 111, 100, 101, 46, 99, 111, 109,    // ASCII string
    254, 152, 188   // end of command
);

Display showing "mattscode.com"

Excellent!

Next Step

I've now got the ability to write my own text and I know the make-up of a command. Next I need to work out what the different control characters mean for different command sections and why the display resets after 50 seconds.