Wednesday, 30 September 2015

Cleaning a solder pot

Burning the enamel coating off magnet wire is hard work. It burns at about 390 degrees, meaning you need to have your iron really, really hot. Then when the enamel burns, it makes a mess of your tip. And sometimes it doesn't all come off cleanly, so you end up re-doing it, after failing to solder the wire to a PCB two or three times.

What we really need is a pot of super-hot moulten solder, into which we can simply dip the ends of the wires to both strip the coating as well as pre-tin the wires at the same time. What we really need is a solder pot.

We used to have a solder pot, which did make soldering a little easier. Except it was really smoky. Not a little bit, but really, really smoky. The reason for this is the flux core of most lead-free solder on the market. After melting a tube of Maplin's finest in the pot, there's a sticky, tarry, residue left in the pot. And when this gets hot, it really smokes!


What didn't help was that the pot we borrowed off Matt (ours had gone missing during one of numerous moves over the years) looked like it had been used as an ashtray! Even after loads of scrubbing, and even with brand new, fresh solder, it still smoked like crazy.

We decided that we needed to clean the solder, as well as the solder pot. So we melted a whole tube of solder and left the pot, switched on, outside to smoke as much as it liked! The solder inside was covered in a filthy scum, and the inside of the pot was black with tar.


We skimmed as much crud off the top of the solder as possible, while it was still moulten. We then placed some kitchen foil inside an eggcup and poured the solder into it. Then left everything to cool down.


Cleaning the solder pot was no easy task! Using a screwdriver head, craft knife and pan scourers, we managed to get a lot of the black off the inside of the pot. It wasn't perfect, but we managed to get a lot of junk out.


The actual solder, meanwhile, looked bright and shiny, with no trace of the impurities that made it so scummy-looking when it was in the solder pot earlier. With both solder and pot cleaned of any flux residue, we put the solder in the pot and let it melt.


This time, the solder melted without even the teeniest whiff of smoke! So little, in fact, that it made our heath-robinson smoke extractor (a PC fan and a piece of aquarium carbon filter) unnecessary. Now we had a nice, clean, smoke-free way of preparing our magnet wires, prior to soldering.

If you have any trouble with a smoking solder pot, try cleaning the solder, as well as the pot, and see if it improves things!

Testing rig for soldering pcb wires

After numerous builds, re-build, teardowns and start-again-from-the-ground-up-builds, we've got a pcb design we're happy with and firmware that works consistently well in all environments. All that remains is to wire the thing up to out board panels.

Since we're multiplexing the hall sensor array, we need only 15 wires (7 rows, 7 columns and absolutely not forgetting, no-way-we've-never-left-out-before, the ground connection) which is a massive improvement over the 50 or so we originally had to hand wire!

So far so good.
Except.....

...actually wiring the thing is a right pain!
Since the PCB is on the underside of the board, it's a laborious task to solder one wire on the board, thread it through to the sensor row on the other side, solder it down to the correct row/column, then get the next wire and repeat. Not least because we always have to leave enough wire so the PCB isn't held up close against the back of the panel, so that it can be repeatedly flipped over for more wires to be soldered to it. The end result is a board with loads of big long loops of wire all over the place!


As well as being a pain to solder because of all the back-and-forth and board flipping, the AWG gauge wire is still a little too thick to be manageable for a final design.


So we had to come up with an idea to simplify the wiring process.
Firstly, we had to use the thinnest wire we could get hold of - which meant 0.125mm enamelled wire. Easy enough to get hold of, but tricky to solder (without the use of a solder pot). Then we had to come up with a method that would allow us to solder all the wires onto the PCB in one go, thread them through the board and solder them to the appropriate row/column on the topside.


The obvious solution would be to use a different coloured wire for every connection on the PCB. This seems obvious. Except it also means having to use relatively thick gauge wire (the coloured sleeve making up the bulk of the wire) and having a selection of at least 15 different coloured wires. It also means we'd have to be absolutely consistent with every coloured wire - so, for example, red would always have to go to pin1, blue to pin2 etc.

Instead of this, we built a "test rig".


The test rig allows us to "probe" each wire to find out which row/column it's connected to. We do this by putting the PCB into "test mode" when it boots up. We then send it serial commands to raise just one of the seven source-driver outputs high at any one time. So we send R1 and the driver output relating to row one goes high. R2 and all but the driver output relating to row two goes high. R3 and... well, you get the idea.

We now have a bunch of seven wires, one of which has been raised high to 5v. Simply brush each wire along the anode leg of an LED and when it lights up, you've found which wire is connected to which "row" output on the PCB.

Similarly the other seven wires are connected to the "column" inputs on the PCB. The PCB is continually monitoring these input pins (with internal pull-up resistors) waiting for one (or more) to go low (this is the basis of how the electronic board game actually works). So when one input goes from high to low, it sends out a serial message along the lines of C1 (for column one) C2 (for column two, and so on).

Touching each of these wires to ground sends a serial message back to our test rig, showing which column wire has been grounded - so we know which column on the board panel to connect it to.

All serial messages are displayed on the 16x2 character display (for no other reason than we had a few lying around!) and the status of each row/column is displayed so we can tell exactly which pin is wired to which wire, without having to keep flipping the board over and tracing each wire back to the PCB.

Being wired up on a breadboard, our test rig works. But it's a bit shonky. What it really needs is a nice PCB and enclosure, just to tidy it all up and make it a bit more reliable. Maybe we'll get round to that, once we've got a couple more of these board panels soldered up!

Thursday, 17 September 2015

I2C bit-banging between two PIC microcontrollers

After soldering up a few new style PCBs and trying out a couple of board game panel sections, we came across a fatal flaw in the design. Currently, we're using a three-pin audio stereo cable to join our board game sections.


In a perfect test environment, these work really well. But when we put four or five board panels onto a table and tried connecting them, things started to go a bit awry.

Firstly, we started getting spurious data at the controller end. If the plug was slightly tight fitting into the audio socket (as some were, the first time the socket was used) we received peculiar characters over serial at the controller. Not a major problem - we simply add a checksum to the end of each message and if the contents of the message don't match the checksum, we just ignore the entire message.

But something else started happening too - some of the previously connected boards started to reset. After placing pieces on them and tracking where they are, a board reset is a really bad thing. It makes the board section forget everything it knows about the pieces placed above it, so when you pick a piece up, it registers that something has changed (one of the hall sensors has changed state) but not that you've removed a piece (because the board has "forgotten" the piece above, it thinks the change is placing not removing a piece). Again, this is something that could be factored out using firmware changes, but it's a bit alarming that some boards are resetting.

So what's going on?
Well, it seems that putting the power on the tip of our stereo plug isn't great. The idea was that the power pins would be the last pins to connect as the plug is pushed into the socket, but in truth, it doesn't matter which (tip, centre or ring) carries the power and/or ground lines - if the plug isn't inserted absolutely square to the socket (so any part of the plug touches any part of the socket during insertion) we can get power shorts and reset conditions on the power line.

So now we're back to looking at keeping our pins a row, rather than "line them up" behind each other. Thanks to the popularity of RGB LED strips, four-way plugs and sockets can be found really cheaply.


And if we're going to use these, we'll have four (rather than three) wires in total. The shape of the plug and socket mean you can't plug them in the wrong way around (without really making an effort) and there are two cables, rather than one, available for sending data.

Now, with two wires, rather than a single one, and a topology consisting of a master controller and a whole load of clients/slaves on a common bus, and immediately we're thinking about I2C as a communications protocol.

I2C is a great protocol for common bus communications. Single wire is ok, but it does rather depend on each device only trying to talk one-at-a-time. I2C actually enforces this rule, using a clock and data line. To send data, you simply check the state of the clock line and if it's in use, wait for it to become free. But the best thing of all about I2C (when implemented properly) is that there's no danger of "power clashes".

I2C basically consists of a clock line and a data line. The sending device raises or lowers the data line, then toggles the clock line. When the listening device sees the clock line go high, it checks the state of the data line. If it is high, that's a binary one. If it's low, it's binary zero. Simple huh?

But it gets even better than that. Let's say two devices - for whatever reason - try to use the clock and data lines. One device sets the data line high (sending a binary value one) but the other device tries to send it low (sending a binary value zero). If we're actively driving the line(s) high and low, we've got a "power clash" - we're effectively shorting our power and our ground lines together!

To avoid this, I2C doesn't actually drive the lines high and low. It uses external pull-up resistors to let the lines float high, and actively pulls them down to ground. So instead of driving the line "up" and "down" we either drive the line low/down or simply let go of it (and it "rises" high of it's own accord).

This is the crucial idea behind the I2C bus. It allows loads of devices to share the lines, but if two or more devices try to talk at the same time, we don't get power shorts. Of course, the data coming off the lines would be garbage, but we've not actually damaged anything. Consider two devices now trying to talk at the same time. The first device drives the data line low (sending binary zero) but the second device - instead of driving the line high (which would create a short) - simply lets the line go (to send a binary one). The result is that binary zero gets sent: not what device two wanted, but it's not trying to force the line high against device one pulling it low.




To make the lines work properly, we need open drain collector transistors to pull the data and clock lines to ground. Rather than using external components, however, we're going to try to use our PIC i/o pins.

Now, PIC microcontrollers don't all have the ability to make their i/o pins "open collector" but they do have a tri-state: input, output, hi-z (high impedence). If we disable the internal pull-up resistors on an i/o pin and make it an input, it's treated as a hi-z pin (high impedence means we can treat it as if disconnected from the rest of the circuit). So rather than turn an output pin on and off to create our high and low signals, we're going to toggle the pins between output/low (drive the line to ground) and input/hi-z (disconnect and allow the line to  "float high").

With all this in mind, we threw together a few routines to generate an I2C output signal. One last thing of note: in the I2C protocol, the data line should only be changed while the clock line is being held low. There are only two scenarios when we should allow the data line state to change while the clock line is high - these are to generate the "start" and "stop" conditions on the line(s).




Here's some code to send a message out over two I2C wires:

Define CONFIG1 = 0x0984
Define CONFIG2 = 0x1dff
Define CLOCK_FREQUENCY = 32
AllDigital

declarations:
     Symbol i2c_clock = PORTA.0
     Symbol i2c_data = PORTA.1
     Dim msg_out As String
     
initialise:
     WaitMs 1000
     msg_out = "Let's go"
     Gosub send_msg
     
loop:

Goto loop
End



send_msg:
     
     Gosub wait_for_free_clock
     
     'now we've got the clock line, wrench it low to tell everyone else we're talking
     'to pull the line low, we need to set the pin to an output then set it low
     'to raise the line, however, we set the pin to input (hi-z) and let it float high

     Gosub clock_low
     WaitMs 2 'give it a few milliseconds for everyone to start listening
     
     j = InStr(msg_out, 0x0d)
     If j = 0 Then msg_out = msg_out + CrLf
     j = Len(msg_out)
     j = j - 1
     
     For i = 0 To j
          k = msg_out(i)
          'send the data out, MSB-first
          For t = 0 To 7
               h = k And 10000000b
               If h = 0 Then
                    Gosub data_low
               Else
                    Gosub data_high
               Endif
                         
               Gosub clock_high
               WaitUs 2
               Gosub clock_low
                    
               k = ShiftLeft(k, 1)
          Next t
     Next i

     'release the clock line to let it float high for the next message
     '(from whichever device on the bus wants to use it)
     Gosub clock_high
     
     'the stop command in i2c is SDA goes high while SCL is high
     Gosub data_high
               
Return



wait_for_free_clock:
     WaitMs 1
     While i2c_clock = 0
          'do nothing
     Wend
Return



clock_low:
     ConfigPin i2c_clock = Output
     Low i2c_clock
Return



clock_high:
     ConfigPin i2c_clock = Input
Return



data_low:
     ConfigPin i2c_data = Output
     Low i2c_data
Return



data_high:
     ConfigPin i2c_data = Input
Return


As it turns out, our PicKit2 programmer (clone) has a really handy feature- a simple three channel logic analyser. So all we needed to do was add the pull-up resistors between our clock and data lines and the power supply, set the clock line as the trigger condition (rising edge) and set our code running.

The results looked something like this:

Amazingly, we had a first-time success. The chart above shows the clock and data lines - the data line leads the clock line by a fraction of a millisecond (on the graphic they sometimes appear to be changing at exactly the same time). But it's important to note that the data line only ever changes when the clock line is  low (with the exception of the start and end bits).

We've also decoded the data line at each rising clock edge, and written out the ASCII characters for each byte received. And we can see that the output on the data line exactly matches the message we sent out!

Wednesday, 9 September 2015

New PCBs for electronic board sections

After getting our multiplexed hall sensor array working last week - and failing to demonstrate them at a Unity meetup in Brighton recently after wiring the one working prototype up incorrectly! - we made up some smaller sections, this time in a 7x7 grid.

This means we can use a single input port (PORTB) with pull-up resistors (since the hall sensors pull to ground when activated) and a single port to activate each transistor individually to turn on each row of hall sensors at a time. This means loads of discrete components:

For prototyping (and because of space restrictions on our breadboard) we've only actually wired up five of the seven transistors, but after proving the concept worked, went on to use all 7 in a test (we went on to wire up two of the SMT transistors at the top of the board).

Ok, not exactly loads. But quite a few. Enough to make wiring up each controller circuit for each board section quite fiddly. We etched up a few different circuit boards, using a variety of different mcus, surface-mount and through-hole components.


Even after making as many components as possible surface mounted, there were still a lot of discrete components to stick down on each PCB! Enough to make it worthwhile investigating an alternative, all-in-one integrated (IC) chip to do most of the work for us...

The A2982 may be relatively expensive, at about a quid a time when bought in bulk, but it does simplify the circuit a great deal.


The A2982 is a source current driver. It includes resistors on the transistor bases, to allow them to work at 5v logic levels, and each channel can source up to 500mA if needed. So all those discrete components are gone - and the entire circuit is now just two IC units: a single PIC 16F1829 and a single AS2982 chip.

Each panel is now much simplified from our earlier 12x12 design - both in terms of hall sensors and the controller PCB. Here we're using some thin hook-up wire just to get the board working (the final version will use ultra-thin magnet wire)


We're using 3.5mm stereo jack sockets to connect our PCBs to power/ground/data. So here's the final PCB design all soldered up and ready for the new firmware.


Right now we've got a couple of panels soldered up and working individually - so we're in for a marathon etching session tonight, to make up half-a-dozen or more controller PCBs for all the other panels. A few more like this and some updated firmware and we can create one, big, playing surface....

Monday, 7 September 2015

1206 resistor array

This is a 1206 component (third from left). It's pretty small.



When I recently created a pcb with 8 pull-down resistors, each connecting microcontroller pins to a common ground. Whenever I've got a load of resistors connected to a common point (whether pulled-up to Vcc or pulled-down to ground) the first thing that springs to mind is "resistor array".


So when there are a load of 1206 resistors that all need connecting to a common point, what about a 1206 resistor array? Here's a 595 shift register DIP with 8 x 1206 resistors (and, of course, a penny for scale).


What would be really cool would be a block of eight resistors, where one side all shared a "common bus". Then we'd have a nice big block that should be easily manageable. So what about those 1206 resistor arrays? A quick trip to uk.farnell.com and 16 hours later, and we've got some to try out


Except they're not quite what I was expecting. While the description read like it said "8 x 1206-sized resistors in an array", it turns out it was more like "8 x resistors in a 1206-sized array" (note the subtle difference).


That's right - all 8 resistors and a common connection squashed into a single, tiny, 1206 package. Given the size of those ridiculously tiny pins, I'm not entirely sure how this is less fiddly than dealing with 8 of the little buggers individually!

Reading and writing EEPROM on a PIC

It's amazing that's it's taken 'til now to need to write a routine like this. For ages, we've been using compilers that allow you to simply write

Read address, value
or
Write address, value

and store away byte values to the microcontroller's own internal eeprom.
But the latest Oshonsoft PIC compilers (still the best simulators, if not exactly the best compilers for PIC microcontrollers around) only have limited support for some of the latest PIC range.

Which means that we've had to roll our sleeves up and get dirty with some assembly language, just to write a couple of bytes to/from eeprom. We were at the stage of giving up (having worked almost all weekend getting nowhere with it) and finally made a breakthrough.

There are a couple of gotchas to look out for, so here's a couple of routines for anyone else having similar problems.


read_eeprom:
     EEADRL = eeprom_addr
     EECON1.CFGS = 0
     EECON1.RD = 1 'set the read bit to start reading
     ASM: nop
     eeprom_byte = EEDATL
Return


write_eeprom:
     
     'disable interrupts while we write to eeprom
     b = INTCON.GIE
     INTCON.GIE = 0

     'set the eeprom address
     EEADRL = eeprom_addr

     EECON1.CFGS = 0
     EECON1.EEPGD = 0
     EECON1.WREN = 1 'set the eeprom write enable bit to start writing

     'set the eeprom value
     EEDATL = eeprom_byte

     'these commands need to be executed with no other instructions between them
     'so we've written them out in assembly, just to be sure the compiler doesn't
     'add in any extra crap that might stop it from working!
     
     ASM: banksel EECON2
     ASM: movlw 0x55 ;
     ASM: movwf EECON2 ;Write 55h
     ASM: movlw 0xaa ;
     ASM: movwf EECON2 ;Write aah
     ASM: bsf EECON1,WR ;set WR Bit To begin Write
     
     While EECON1.WR = 1
          'wait for it to finish
     Wend
     
     'disable writing to eeprom until this has finished
     EECON1.WREN = 0

     'although we don't use eeprom interrupts, the EEIF flag will still be set.
     PIR2.EEIF = 0 'clear the eeprom write done interrupt flag
     
     'now re-enable global interrupts if they were previously set.
     INTCON.GIE = b
     
     'this shouldn't be necessary since we waited for the write cycle to complete
     WaitMs 4
     
Return

When it comes to reading eeprom, the datasheet makes things nice a clear. Set the address you want to read from, set the "start reading" bit in the corresponding register, and the value you're after appears in the EEDATL register. Nice and easy.

Writing data to eeprom is not so easy.
And until we'd actually proved that our reading routine - which seemed so laughably simple by comparison - was working properly, finding the actual cause of the problems, when nothing seemed to work, was a real headache.

Now the main gotcha when writing to eeprom is that there's a very specific bit sequence that you have to write, in a very specific way, to a very specific register - that doesn't actually exist as a physical register - just in case the chip were to boot up in a peculiar state and go rogue, overwriting eeprom memory (and, in a worst case program) locations with junk.

The critical thing to do, when writing to eeprom, before you can set the "start writing" bit, is send the bit pattern 01010101 (0x55) followed by 10101010 (0xaa) to the virtual register EECON2. Then - and only then - can you set the "start writing" bit WR of EECON1. And you must do it immediately after sending the 0x55, 0xaa pattern.

The instructions have to be written as five consecutive commands, with no other instructions between them. That means no delays or wait commands between each, global interrupts really need to be turned off, and you need to be sure that your compiler isn't adding in any extra junk you don't want.

In fact, if you're not careful writing these instructions, you can easily get extra stuff in there that you didn't even think about. For example, EECON1.WR=1 seems innocuous enough - set the WR bit of the EECON1 register. But how is the compiler doing it?

Maybe it's taking the existing EECON1 value, bitwise OR-ing it with some bitmask that identifies the WR bit (if WR is the bit two, this mask would be 00000100) and then setting EECON1 to this new result. If this is how the compiler works, then there's a whole load of other junk in the middle of the eeprom-specific sequence.

So to make sure that our compiler isn't adding anything else in, we actually hand-crafted some assembly in the write_eeprom routine (it's been a good few years since any of us did any assembly programming, and took a while for the rusty cogs to get spinning again!). The key part is highlighted above

     ASM: movlw 0x55 ;
     ASM: movwf EECON2 ;Write 55h
     ASM: movlw 0xaa ;
     ASM: movwf EECON2 ;Write aah
     ASM: bsf EECON1,WR ;set WR Bit To begin Write

These five lines have to appear exactly as written in order to write to the eeprom register(s). Yet despite writing this assembly directly into the write_eeprom routine, we still couldn't get our values committed to eeprom between power-cycles (the sure-fire way of testing that the value has been written to non-volatile memory, rather than pluck a value from RAM).

It has taken us nearly three days, copious amounts of coffee and a massive "a-ha!" when the answer was revealed - before accessing the EECON2 register, we have to make sure that the correct bank is selected on the PIC.

This is a gotcha from over twelve years ago, when a few of us were starting out, writing simple (and sometimes not-so-simple) routines in assembly on the trusty old PIC16F877A range. Quite often we'd spend hours debugging assembly routines, only to find we'd not switched to the correct "bank" before trying to access a register.

And - over a decade later - the same problem once-again bit at least one of us cleanly on the arse. And, having remembered about bank-switching, our read and write routines suddenly started working properly! So here they are, for anyone else having a similar problem. Whatever language you're working in, here are a couple of routines for writing to onboard eeprom, old-school....