Showing posts with label 18f2455. Show all posts
Showing posts with label 18f2455. Show all posts

Friday, September 28, 2012

Inkjet printer teardown - get at those stepper motors!

At last night's BuildBrighton open evening, we set to work opening up an old HP Inkjet printer, using our favourite opening-tools - hammers and hacksaws!


We were after a few parts from inside the printer - namely the stepper motors and linear rods (that hold the print head carriage). Before setting to work with the hammer, we located which parts of the printer we wanted to avoid hitting (too hard)

There's a stepper motor, tucked away in the corner of the enclosure - best go easy down there!

It didn't take long before the plastic casing was off...


And we had hold of a feast of salvaged goodies:


Of particular interest to us was the dc motor (used to make the print carriage travel left-to-right) the nice chunky steel rod with precision made carriage (actually just some moulded plastic with brass rings that slide along the rod - no fancy linear bearings here!), the rubber paper-pickup wheels (for our future lathe project) and of course, the stepper motors (complete with pulley heads and drive belts).

We started to get to work straight away on the stepper motors. They're five-wire connections so already we're considering uni-polar steppers. To find out exactly what we've got, we drew up a table of connections, put an ohm-meter across each of the pins in turn and noted down the respective resistances across each set of pins.

(Here Justin is testing the resistance between pins 1 and 5 - about 60 ohms)

We noticed that all connections to pin three were roughly half the resistance of the others (about thirty ohms). From this table of resistances we worked out that we had a unipolar stepper motor with a common "centre tap" - i,e the centres of both coils were connected together


To work out the wiring for the other coils, we had to do some trial-and-error investigating. We put power onto our centre tap (pin 3) then put a ground connection onto one of the other wires (pin5, we called this pin d). The motor energised, moved slightly and locked into place.

The idea now is to identify the other three pins, such that we can label them pin a, b, c and energise them in sequence a,b,c,d or d,c,b,a to get the motor to spin in either direction.

Here we can see our centre tap (pin3) connected to power, with pin5 grounded and pin1 at the same time.


With pin d still grounded, we then grounded the other three pins, one at a time. Where the stepper motor moved slightly anticlockwise, we'd found our pin c. When the motor moved slightly clockwise, we'd hit upon pin a. When there was no movement in the motor, we'd grounded the (remaining) pin b.
Using this rough-and-ready approach, we successfully labelled all four pins on our stepper motor.


With the pins sucessfully identified, we replaced our bit of wire that poked at the pins, connecting them to ground with some IRF640 power FETs. These are like big chunky transistors, with a flyback diode built into them. By putting them on the ground side of  the coil and activating them in the correct sequence, we managed to get the stepper motor to spin at quite a reasonable rate:



The driver chip is one of our old friends the PIC 18F2455, configured as a USB/HID device. Our custom software simply sends a "command byte" to tell the board which way to spin the motor.
252 = spin anti-clockwise
253 = spin clockwise
250 = stop spinning
249 = change the delay beween pulses.

We found with these steppers, that they ran quickly and quietly with a delay of 2ms between step pulses. We could increase this delay to slow the rate of rotation, but movement got more and more "jerky". A delay of just 1ms caused the stepper to hum without turning at all.

Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x38
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x03
Define CONFIG4L = 0x80
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

UsbSetVendorId 0x1923
UsbSetProductId 0x1694
UsbSetVersionNumber 0x1001
UsbSetManufacturerString "Nerd Club"
UsbSetProductString "USB stepper driver"
UsbSetSerialNumberString "1111111111"
UsbOnIoInGosub input_report_before_sending
UsbOnIoOutGosub output_report_received
UsbOnFtInGosub feature_report_before_sending
UsbOnFtOutGosub feature_report_received

AllDigital
Dim i As Byte
Dim spindir As Byte
Dim state As Byte
Dim dly As Word
Config PORTB = Output
Config PORTA.0 = Input

UsbStart
spindir = 0
state = 0
dly = 10

loop:
      UsbService

      If PORTA.0 = 1 Then state = 0

      Select Case state

            Case 250
            Low PORTB.3
            Low PORTB.2
            Low PORTB.1
            Low PORTB.0

            Case 1
            High PORTB.3
            Low PORTB.2
            Low PORTB.1
            Low PORTB.0
           

            Case 2
            High PORTB.3
            High PORTB.2
            Low PORTB.1
            Low PORTB.0

            Case 3
            Low PORTB.3
            High PORTB.2
            Low PORTB.1
            Low PORTB.0
           
            Case 4
            Low PORTB.3
            High PORTB.2
            High PORTB.1
            Low PORTB.0
                       
            Case 5
            Low PORTB.3
            Low PORTB.2
            High PORTB.1
            Low PORTB.0

            Case 6
            Low PORTB.3
            Low PORTB.2
            High PORTB.1
            High PORTB.0

            Case 7
            Low PORTB.3
            Low PORTB.2
            Low PORTB.1
            High PORTB.0

            Case 8
            High PORTB.3
            Low PORTB.2
            Low PORTB.1
            High PORTB.0

      EndSelect
      WaitMs dly

      Select Case spindir
            Case 1
            state = state - 1
            If state < 1 Then state = 8

            Case 2
            state = state + 1
            If state > 8 Then state = 1

      EndSelect
           
Goto loop

End


feature_report_received:
Return


feature_report_before_sending:
Return


output_report_received:
      'PC has sent us some data - use it here
      i = UsbIoBuffer(7)
      Select Case i

            Case 249
            dly.HB = UsbIoBuffer(1)
            dly.LB = UsbIoBuffer(0)

            Case 250
            state = 250
            spindir = 0

            Case 252
            spindir = 1

            Case 253
            spindir = 2

      EndSelect

Return


input_report_before_sending:
      'we're sending data back to the host
      UsbIoBuffer(0) = spindir
      UsbIoBuffer(1) = state

Return

Friday, September 21, 2012

PIC based stepper driver controller for CNC

Here's the PIC code for our USB-HID device/stepper motor controller. Here's how it works:

Data is sent to the device through an 8-byte wide buffer. The seventh byte in the buffer is a "command byte" and (amongst other things) can be used to


  • set the number of steps to move on the x-axis
  • set the number of steps to move on the y-axis
  • start the head moving
  • plunge the drill
  • set the backlash values for x/y axis
  • set the minimum/maximum drill height
While the board is connected, you can poll the device and it will report

  • if the head is still moving (x/y direction)
  • if the drill is activated (spinning/plunging)
  • if the machine is ready to receive the next command

The code is written for Oshonsoft PIC18 Simulator/compiler and runs on an 18F2455 PIC microcontroller


Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x38
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x03
Define CONFIG4L = 0x80
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

UsbSetVendorId 0x1234
UsbSetProductId 0x1234
UsbSetVersionNumber 0x1122
UsbSetManufacturerString "Nerd Club"
UsbSetProductString "CNC drilling machine"
UsbSetSerialNumberString "1111111111"
UsbOnIoInGosub input_report_before_sending
UsbOnIoOutGosub output_report_received
UsbOnFtInGosub feature_report_before_sending
UsbOnFtOutGosub feature_report_received

AllDigital
Dim i As Byte
Dim stepsx As Long
Dim stepsy As Long
Dim dirx As Byte
Dim diry As Byte
Dim domove As Bit
Dim doplunge As Bit
Dim isdrilling As Bit
Dim booting As Bit
Dim atdestination As Bit
Dim snapback As Bit
Dim tmp As Byte
Dim tmpw As Word
Dim tmpl As Long

Dim statex As Byte
Dim statey As Byte
Dim statez As Byte
Dim zaxisvalue As Byte
Dim drillspeedvalue As Byte
Dim zaxisspeed As Byte
Dim zservoon As Bit
Dim dservoon As Bit
Dim holdzaxis As Bit
Dim timeoutcount As Byte
Dim zmin As Byte
Dim zmax As Byte
Dim drilldir As Byte
Dim feedbackvalues As Byte

Config PORTB = Output
Config PORTC.7 = Output
Config PORTC.6 = Output

Symbol coila_1 = PORTB.0
Symbol coilb_1 = PORTB.1
Symbol coilc_1 = PORTB.2
Symbol coild_1 = PORTB.3

Symbol coila_2 = PORTB.4
Symbol coilb_2 = PORTB.5
Symbol coilc_2 = PORTB.6
Symbol coild_2 = PORTB.7

Symbol zaxis = PORTC.7
Symbol drillspeed = PORTC.6

init:
      booting = 1
      domove = 0
      doplunge = 0
      atdestination = 1
      statex = 1
      statey = 1
      statez = 0
      snapback = 1

      zservoon = 1
      zaxisvalue = zmin 'retracted
      dservoon = 1
      drillspeedvalue = 90 'midpoint=stopped
      zaxisspeed = 50
      holdzaxis = 0

      timeoutcount = 0
      zmin = 150
      zmax = 175
      drilldir = 0
      isdrilling = 0

      feedbackvalues = 1

      UsbStart

      'wait 1 second before sending any servo commands
      '(so the servo controller doesn't freak out)
      WaitMs 1000

      'set up a timer1 interrupt to occur every 1/20th second
      '(for servo control commands- particularly motor spin speed)
      '(we use this for servos)
      'enable timer1 interrupts to trigger every 1ms
      INTCON1.GIE = 1 'enable global interrupts
      PIE1.0 = 1 'enable TMR1 interupts
      INTCON.6 = 1 'enable all unmasked interrupts
      INTCON.7 = 1 'enable Global interrupts
      PIR1.0 = 0 'reset interupt flag
      T1CON.TMR1CS = 0 'use fosc/4
      T1CON.TMR1ON = 1 'turn on timer1

      'enable timer0 in 16-bit mode
      T0CON.T08BIT = 0
      T0CON.TMR0ON = 1
      T0CON.T0CS = 0 'use fosc/4

      'connect to the brushless motor controller
      '(it has a specific startup sequence)
      drillspeedvalue = 90

      'make sure the drill is retracted(~200ms)
      zservoon = 1
      zaxisvalue = zmin
      drillspeedvalue = 90


loop:
      booting = 0
     
      While domove = 1

            Select Case statex
                  Case 1
                  High coila_1
                  Low coilb_1
                  Low coilc_1
                  Low coild_1

                  Case 2
                  High coila_1
                  High coilb_1
                  Low coilc_1
                  Low coild_1

                  Case 3
                  Low coila_1
                  High coilb_1
                  Low coilc_1
                  Low coild_1

                  Case 4
                  Low coila_1
                  High coilb_1
                  High coilc_1
                  Low coild_1

                  Case 5
                  Low coila_1
                  Low coilb_1
                  High coilc_1
                  Low coild_1

                  Case 6
                  Low coila_1
                  Low coilb_1
                  High coilc_1
                  High coild_1

                  Case 7
                  Low coila_1
                  Low coilb_1
                  Low coilc_1
                  High coild_1

                  Case 8
                  High coila_1
                  Low coilb_1
                  Low coilc_1
                  High coild_1
                       
            EndSelect

           
            Select Case statey
                  Case 1
                  High coila_2
                  Low coilb_2
                  Low coilc_2
                  Low coild_2

                  Case 2
                  High coila_2
                  High coilb_2
                  Low coilc_2
                  Low coild_2

                  Case 3
                  Low coila_2
                  High coilb_2
                  Low coilc_2
                  Low coild_2

                  Case 4
                  Low coila_2
                  High coilb_2
                  High coilc_2
                  Low coild_2

                  Case 5
                  Low coila_2
                  Low coilb_2
                  High coilc_2
                  Low coild_2

                  Case 6
                  Low coila_2
                  Low coilb_2
                  High coilc_2
                  High coild_2

                  Case 7
                  Low coila_2
                  Low coilb_2
                  Low coilc_2
                  High coild_2

                  Case 8
                  High coila_2
                  Low coilb_2
                  Low coilc_2
                  High coild_2
                       
            EndSelect

                       
            If stepsx > 0 Then
                  If dirx = 1 Then
                        statex = statex + 1
                        If statex > 8 Then statex = 1
                  Else
                        statex = statex - 1
                        If statex < 1 Then statex = 8
                  Endif
                  stepsx = stepsx - 1
            Endif

            If stepsy > 0 Then
                  If diry = 1 Then
                        statey = statey + 1
                        If statey > 8 Then statey = 1
                  Else
                        statey = statey - 1
                        If statey < 1 Then statey = 8
                  Endif
                  stepsy = stepsy - 1
            Endif

            If stepsx = 0 And stepsy = 0 Then
                  'set the flag to say we've arrived at the destination
                  atdestination = 1
                  domove = 0
            Else
                  atdestination = 0
                  'we need a delay for the stepper to respond
                  WaitMs 1
            Endif

            'poll the usb just to keep it alive
            UsbService
      Wend

      'important: only plunge when NOT moving!
      If domove = 0 Then

            If statez > 0 Then
                 
                  domove = 0
                  atdestination = 0
                  isdrilling = 1

                  Select Case statez

                  '--------------------------------
                  Case 1 'start the drill spinning
                  '--------------------------------
                  drillspeedvalue = 255
                  WaitMs 100
                  statez = 2

                  '-------------------
                  Case 2 'moving down
                  '-------------------
                  If zaxisvalue < zmax Then
                        zaxisvalue = zaxisvalue + 1
                        If zaxisspeed > 0 Then WaitMs zaxisspeed
                  Else
                        zaxisvalue = zmax
                        statez = 3
                  Endif

                  '----------------------------------------
                  Case 3 'wait at the bottom of the stroke
                  '----------------------------------------
                  WaitMs 500
                  statez = 4

                  '-------------------
                  Case 4 'moving up
                  '-------------------
                  If zaxisvalue > zmin Then
                        zaxisvalue = zaxisvalue - 1
                        If snapback = 1 Then
                              'don't delay
                        Else
                              If zaxisspeed > 0 Then WaitMs zaxisspeed
                        Endif
                  Else
                        zaxisvalue = zmin
                        statez = 5
                  Endif

                  '-----------------------------------
                  Case 5 'brake (not break) the drill
                  '-----------------------------------
                  drillspeedvalue = 60
                  WaitMs 100
                  statez = 6

                  '-------------------------------
                  Case 6 'stop the drill spinning
                  '-------------------------------
                  drillspeedvalue = 90
                  statez = 0
                  WaitMs 100
                  atdestination = 1
                  isdrilling = 0

                  EndSelect
            Endif
      Endif

      'poll the usb to keep it alive
      UsbService

Goto loop

End


feature_report_received:
Return


feature_report_before_sending:
Return


output_report_received:

      Select Case UsbIoBuffer(7)
            '--------------------------------------
            Case 1 'set X step count/direction
            '--------------------------------------
            dirx = UsbIoBuffer(4)
            tmpw.HB = UsbIoBuffer(3)
            tmpw.LB = UsbIoBuffer(2)
            tmpl.HW = tmpw
            tmpw.HB = UsbIoBuffer(1)
            tmpw.LB = UsbIoBuffer(0)
            tmpl.LW = tmpw
            stepsx = tmpl
            domove = 0

            '--------------------------------------
            Case 2 'set Y step count/direction
            '--------------------------------------
            diry = UsbIoBuffer(4)
            tmpw.HB = UsbIoBuffer(3)
            tmpw.LB = UsbIoBuffer(2)
            tmpl.HW = tmpw
            tmpw.HB = UsbIoBuffer(1)
            tmpw.LB = UsbIoBuffer(0)
            tmpl.LW = tmpw
            stepsy = tmpl
            domove = 0

            '---------------------------------
            Case 240 'reset the flag buffers
            '---------------------------------
            zaxisvalue = zmin
            domove = 0
            statez = 0
            drillspeedvalue = 90
            WaitMs 300
            isdrilling = 0
            atdestination = 1

            '----------------------------------------------------------
            Case 241 'set which values you want to read back from usb
            '----------------------------------------------------------
            feedbackvalues = UsbIoBuffer(0)

            '--------------------------------------
            Case 247 'zmin value
            '--------------------------------------
            zmin = UsbIoBuffer(0)

            '--------------------------------------
            Case 248 'zmax value
            '--------------------------------------
            zmax = UsbIoBuffer(0)

            '--------------------------------------
            Case 249 'set the z-axis step speed
            '--------------------------------------
            zaxisspeed = UsbIoBuffer(0)

            '--------------------------------------
            Case 250 'set the drill speed
            '--------------------------------------
            drillspeedvalue = UsbIoBuffer(0)

            '--------------------------------------
            Case 251 'set the zaxis servo depth
            '--------------------------------------
            zaxisvalue = UsbIoBuffer(0)
            statez = 0

            '--------------------------------------
            Case 253 'plunge the drill
            '--------------------------------------
            statez = 1
            domove = 0

            '--------------------------------------
            Case 254 'start moving
            '--------------------------------------
            atdestination = 0
            domove = 1

      EndSelect
Return


input_report_before_sending:
      'tell the PC our current status
      '(so when PC sees we're at our destination, for example
      'it can move onto the next command in the script)
      tmp = 0
      tmp.0 = domove
      tmp.1 = atdestination
      tmp.2 = isdrilling
      tmp.7 = booting
     
      Select Case feedbackvalues

            Case 0
            UsbIoBuffer(0) = 0
            UsbIoBuffer(1) = 0
            UsbIoBuffer(2) = 0
            UsbIoBuffer(3) = statex
            UsbIoBuffer(4) = zaxisvalue
            UsbIoBuffer(5) = drillspeedvalue
            UsbIoBuffer(6) = tmp
            UsbIoBuffer(7) = 255

            Case 1
            tmpw = stepsx.LW
            UsbIoBuffer(0) = tmpw.LB
            UsbIoBuffer(1) = tmpw.HB
            tmpw = stepsy.LW
            UsbIoBuffer(2) = tmpw.LB
            UsbIoBuffer(3) = tmpw.HB
            UsbIoBuffer(4) = zaxisvalue
            UsbIoBuffer(5) = drillspeedvalue
            UsbIoBuffer(6) = tmp
            UsbIoBuffer(7) = 255

      EndSelect

Return


preloadtimer1:
      'pre-load to 15535 (65,535-50,000 where 1ms=5000)
      'so this causes a timeout interrupt every 10m/s
      TMR1H = 50 '216 (2ms)
      TMR1L = 175 '239 (2ms)
Return


On High Interrupt

      'if timer1 has rolled over (hit 65535) then
      If PIR1.TMR1IF = 1 Then

            'save system state/working address etc
            Save System

            'reset the timer1 interrupt flag
            PIR1.TMR1IF = 0

            'preload timer1 with a number to count to
            'so that it rolls over (hits 65535) after
            '2ms. As it happens, at 20Mhz, we need to
            'count up to 5000 for 1ms to elapse.
            Gosub preloadtimer1

            timeoutcount = timeoutcount + 1
            If timeoutcount > 1 Then
     
                  'send the servo position control
                  If zservoon = 1 Then
                        ServoOut zaxis, zaxisvalue
                  Endif

                  'send the drill speed control
                  If dservoon = 1 Then
                        ServoOut drillspeed, drillspeedvalue
                  Endif
                  timeoutcount = 0
            Endif
     
      Endif
     
Resume



CNC drill schematic

Wednesday, August 29, 2012

CNC motor testing via USB

There's an often held belief that you can't do CNCs without a parallel port on your PC and cleverly timed move instructions. That may have been the case back in the day, but we've had the nineties guys, USB is all the rage!

So we're building a USB CNC controller board.
We're quite lucky that these cheap little 5V 28BYJ48 steppers have already been stepped down. They're supposed to be 1/64 but inside they're geared down again. There are loads of places all over the internet which say that they're stepped down again by 1/32 - meaning you need 64 * 32 = 2048 steps for one complete revolution.

If that's the case, we should be able to get pretty precise movement, even with a massive gear/cog riding on the shaft, and without having to bother with the complexities of micro-stepping. There's only one way to be sure - and that's spin one around and count the steps!

In-keeping with our NC drill software, we're looking to build a USB controller which we can give a number of steps and have the motor(s) play out those steps. We've no idea at the minute how many steps we may need to move up to (depending on how many steps per revolution these motors actually need) so we've allowed for a 4-byte value to be sent to our trusty 18F2455 PIC microcontroller.

We're using (as ever) a generic HID device interface and sending data in 8 byte packets.
  • The first byte (byte zero) is our "command byte".
  • If the value is one, it's a command to set the x motor step count
  • If the value is two, it's a command to set the y motor step count
  • The second, third, fourth and fifth bytes make up our 4-byte value (0-2,147,483,647)
  • The sixth byte (byte 5) is a direction - one is anit-clockwise, zero (or any other value) clockwise.

After sending the x-axis step count (or the y) the controller board stops all motor activity (since if the motors are spinning when new values come in, the x- and y- axis will go out of alignment with each other, as the earlier axis will be ahead of the later one).

Only once the command byte 254 is sent do the motors actually spin up.
For as long as the x/y step count has a value greater than zero, the motor(s) are given a signal to move them onto the next step. The step-count value is decreased by one each time one of the axis motors steps. Once both motors have a step-count value of zero, a flag is set to tell the PC that the motors have stopped spinning and the head is now in it's correct position.

Here's a video of some early testing:


What's happening here? Thanks to the autofocus on our camera-phone it's not too clear - but if you squint and stand back from the monitor you might see:

Firstly, the command byte (7th byte) is given the value 1 (set x motor step count), along with the second byte (from the right) set to 16. Since our x count is a 4-byte value, we're setting it to 16*256 = 4096.
We repeat these values with the command byte set to value 2 (set y motor step count) then clear the buffer and send the command value 254 to get the motors spinning.

Giving our control board a value of 4096 makes the motor complete one full rotation.
So there we have it. Our stepper motor has a 1/64 step angle, geared down, not by 1/32 as some other forums suggest, but a full 1/64 again. 64 * 64 = 4096 so this is the number of steps required for a full rotation.

The video then skips back to a blurry laptop screen, where we enter the same values, but this time setting byte 4 to 1. This is the direction byte. When this is set to one, the motor spins in the opposite direction.

All in all, we've had quite a successful evening - we've got both axis motors spinning from a custom-built PIC-based USB (HID device) board and some software which we can talk to the board with and get predictable results. Now we just need to remember how to work with Timer1 to create a 20m/s interrupt on the PIC and we can use this to send servo commands for the z-axis (drill up, drill down and motor speed).

Tomorrow is another BuildBrighton open evening.
There's even a slim chance that after the beers and pizza, we might actually get something working......

Thursday, September 1, 2011

Miniature guitar - working prototype

Another exciting day at Nerd Towers as we've finally completed our first working miniature guitar prototype. Not only does the instrument work as expected (touch sensitive guitar frets, and a touch-activated strummer) but the software is coming along quite nicely too.

Although the miniature instruments are HID/USB devices using a simple 8-byte buffer interface to talk back to the PC, some people may be a little uncomfortable working at such a low level. So we're creating an easy-to-use, event-driven COM object, so anyone can create their own custom software to work with the miniature instrument range.

No worrying about bitmasking, byte buffers or retrieving data - all that has been taken care of. Simply create an instance of the COM object, and write your code to respond to events such as
  • x_instrumentConnected
  • x_keyPressed
  • x_keyReleased
  • x_guitarFretChanged
  • x_guitarStrummed
and so on.

Obviously, not all events are available to all instruments.

The key press/release events are specific to the synths, for example, whereas only the guitar and bass ever raise the guitarFretChanged event. Such an exciting post as this wouldn't be complete without a video demonstrating a working guitar - so here goes:

[youtube video goes here]

Tuesday, August 16, 2011

Miniature playable synth

While waiting for the boards from PCBCart to arrive, we decided to look at other miniature instruments for the range.



Of course we'll need a miniature drum kit, for laying down the beats, and a bass guitar for laying down the grooves, will be more-or-less the same as a guitar (same functionality, maybe a bigger beefier shape). We've already made up a simple drumkit prototype, using some piezos and an existing miniature drumkit bought off eBay.



the gold coloured disks mounted under each drum skin are piezo tranducers, connected to digital inputs on the PIC microcontroller



What we need now is to manufacture the 'kit from scratch, now we've proved that the concept works. That can wait until we're visiting the local hardware store and can investigate the diameter of different plastic tubing! Another instrument we've yet to have a go at is a miniature synthesizer. There's a debate about which style to do first - a Moog, a Roland or a Yamaha Keytar?



At the minute it's not important - we're focussing on the functionality; the enclosure can be designed later! Here are some photos of the early synth development....







Rather than mess about with hinges and moving parts, we've just made the keyboard out of a series of "sprues", connected to a thick bar along the top. The idea is that there should be enough flex in the acrylic to allow the user to press the acrylic keys onto a series of soft-touch tactile push-buttons mounted onto a PCB underneath. These key presses will simply be digital inputs, allowing true polyphonic sound to be achieved.



Our miniature synth will have 17 keys, starting at middle C.

Why seventeen keys and not any other number? I wish I could say it was because of the number of available inputs or some other technical reason, but the truth is we all watched classic 80s synth clips on YouTube and after seeing how to play Axel F decided that 17 was the minimum number of keys required!







Whether the 18F2455 will have enough inputs for what we're trying to do (there will be other buttons such as pedal sustain, LED outputs and so on) or whether we'll need to move up to the bigger 40-pin 18F4550 still remains to be seen.......



Friday, August 12, 2011

Audio triggering device

After another late night at BuildBrighton, Harry and I finally got a working version of his audio triggering device. It's following the requirements from this earlier post and I think we've pretty much covered everything that was asked for.



It's not the prettiest thing to look at, but it does function as requested:







The device is yet another PIC-based 18F2455 USB/HID device.

The state of the device is reported back to the host PC over USB, as well as the audio input level (after being converted through the built-in A-to-D convertor).



Although relatively straight forward to design, we did have problems with one little bit, but it turned out to be a major headache and took a few hours to resolve. The problem we hit was the input voltage level was in quite a low range - not more than about 1 volt. This meant that when put onto an A-to-D pin that converts voltages from between 0V-5V into a value in the range 0-255, our input values were only ever in the region of 0-50 (20% of 5V ~ 20% of 255). Complicating things further, the audio in from the PC had a background noise level, so instead of a value range of 0-50, we were getting a value range of about 30-80.



But this was only at absolute peak volume, which didn't really occur very often, so our input value range hovered around a relatively low 30-50. If we turned the volume up on the PC, to get a higher peak voltage, we also increased the background hiss, so the value range went up to 50-80, but the size of the range didn't actually increase.



Although this relatively small range did create a working trigger threshold, we were uncomfortable about it being so narrow. We didn't want to mess about with op-amps and extra components, so set about finding a way of increasing the voltage range on the A-to-D output.



The answer was to provide an alternative reference voltage onto pin RA3 on the PIC (also labelled VREF+). To achieve this, we put the wiper of a potentiometer onto the input pin and connected the other two pins to power and ground (creating a voltage divider and allowing us to create a variable reference voltage of 0V-5V)







Another addition to the circuit was to include a 0.1uF capacitor in series with the left and right audio inputs. The idea being that as we're detecting white noise rather than regular, relatively smooth speech or sound, the wild fluctuations in the input voltage can be negated through the use of the capacitor. As we get a spike in the input voltage, this can charge the capacitor and when the white noise voltage level goes low, the input signal into the PIC can be sustained.



In practice this worked well, and coupled with the variable reference voltage gave us input values of 0-200+ but meant that after the audio was stopped, the input on the analogue pin retained a relatively high value. We added some "bleed" resistors across the capacitor pins of about 10K, so that when the audio signal is removed, the capacitors discharge relatively quickly (after about a second) and the input signal always returns to zero.







Frustratingly, we also spent about an hour trying to solve a problem that wasn't really present. With the extra capacitor in place, we noticed that there was a slight delay between starting the movie off (that contained the white noise soundtrack) and the device recognising the noise and triggering it's outputs.

After an hour of changing capacitor values, altering resistor values, changing analogue reference voltages, introducing software thresholds and so on, we unplugged the device and listened to the soundtrack as the movie played.

Sure enough, after hitting "play" on the movie player, the images moved instantly, but the sound only came in a fraction of a second later - the problem wasn't with the device at all, but the movie player software used to start and stop the audio signal!



Here's the latest, updated schematic, including last night's changes:



Audio Input Circuit_v2

note some input/output pins may have changed from the original design

Wednesday, August 10, 2011

Working SMT USB HID device

The ICSP/SOIC chip clip arrived from Farnell today.

They really are excellent for next-day delivery. By UPS courier no less, not just Royal Mail (which can take three or more days to arrive!). If you're prototyping and can't wait for the delivery times from eBay suppliers (or just want to buy from someone with proven customer service) we can't recommend Farnell enough!





Anyway, the clip arrived - it is like a sprung loaded bulldog clip, with each of the fine-pitch pins being taken up to a 0.1" pitch header on the tops of the handles.

Before we could use it we had to make a simple pass-through board for my PICKit2 Clone iCP01 programmer (from piccircuit.com). The idea is to have a board with five pins that we can push the connector from the programmer onto. The other side of the board will be connected to a length of 5-way IDE cable, which in turn is connected to some 0.1" pitch pin header sockets. The sockets slip over the ends of the "chip clip", connecting the PIC programmer to the appropriate pins on the device.









Following the programmer pinout, we had to identify which wires needed connecting to which pins on the headers on the top of the clip handles.







Here's the whole thing assembled







And here it is, in place and ready to program the chip.







By either good luck or good judgement, the chip was immediately recognised by the PICKit2 programming software, and we were able to download some test firmware onto the device.



All went well and after the programming clip was removed, the device was plugged into the laptop via it's miniature USB socket. The usual bing-bong sound told us that the device had connected properly, and further inspection in Device Manager confirmed that our SMT soldered USB/HID device was indeed working.



The last bit of the puzzle now is to update the firmware so that it matches the new pin layout we used (the pin assignments were changed from the earlier version, to make SMT layout easier)

Monday, August 8, 2011

Audio level triggering device



This is Dr Harry Witchel. A doctor no less. He's a scientist specialising in Neuroscience and Psychobiology. We've always found stuff to do with thinking and the brain to be quite fascinating (hey, this isn't called Nerd Club for nothing!) so were thrilled when one of us met up with him recently to discuss helping out with a project he's working on.



The full brief is here but the basic idea is this:

Harry needs a device that he can connect to the audio out of a PC and will trigger a number of outputs when it detects a loud sound. The threshold volume of the input sound needs to be selectable via a potentiometer or similar rotary dial. The same device needs to display what is happening through a series of coloured LEDs and have a number of operational modes selectable through a series of buttons.



It's just crying out for a microcontroller.

And not just any microcontroller, but our old friend the PIC 18F2455.

Using a number of analogue-to-digital pins, digital inputs and outputs and a few discrete components, we could even create a USB-compatible device that can display exactly what is going on, in a nice onscreen display - not just a few twinkling LEDs!



Here's Harry's original spec with our comments in red:

Specifications for Audio Trigger





Here's the first version of the circuit diagram to acheive what he's after:

Audio Input Circuit



We're hoping to have a working version of this circuit (on a breadboard at least) in time for next Thursday's BuildBrighton meeting. More details (and photos) will be posted here in good time.....

Wednesday, June 29, 2011

Multiple touch sensitive inputs

In a previous post we talked about how to use a darlington transistor to create a touch sensitive switch.

For a device with multiple touch-sensitive inputs, it seems obvious that we repeat the single input for each pin we want to use as a touch sensor:



However, for each input our component count increases.
In the example above, we're using PORTB on the microcontroller, which has a built-in pull-up resistor on each input pin. If we need to use a different port, or want to use more than 8 inputs, we're going to have to add a pull-up resistor on each input pin. Also required - though not shown on the diagram above for the sake of simplicity - is a 1M pull-down resistor on the base pin of each darlington transistor.
So for each additional input, we're introducing three extra components.

It's worth noting, at this point, that for each touch sensitive input, we need two pads per input: one pad connected to the base pin on a transistor, and one pad connected to the 5V supply (the user touches and effectively creates a bridge between the 5V supply and the base input pin).

However, we can simplify our design massively by swapping the pads around:
instead of a fixed 5V supply and multiple darlington transistors, we can use a single transistor and multiple, variable 5V supplies. How to do this?
We turn each potential input pin to an OUTPUT and use this output to drive what would otherwise be a fixed 5V supply on the first pad. So each pair of pads that make up a touch-sensitive switch consist of an output pin (from the microcontroller) and a pin connected to the base of the darlington transistor.

a simple PIC/usb device with 16 touch sensitive inputs

Every touch contact would consist of a pair of pins/pads - one going to each of the numbered pads (PAD1, PAD2 etc) and the other a common input (COM_INPUT) so there would be a total of 16 COM_INPUT pads all tied together.

The pseudo-code would go something like this:
  • turn off all outputs
  • turn on output RA2 (PAD1)
  • has RA0 gone low? (yes=finger present across PAD1 + COM_INPUT)
  • turn off output RA2
  • allow time for input to return high
  • turn on output RA3 (PAD2)
  • has RA0 gone low? (yes=finger present across PAD2 + COM_INPUT)
  • turn off output RA2
  • allow time for input to return high
  • etc.

It depends on the type of darlington transistor you use (different transistors have different response/switching times) and the size of the pull-down resistor on the base pin, but in practice, with a 1M pull-down resistor, we found that 0.5ms (500us) worked well. Using this approach, we were able to poll all pins in under 10ms (0.5*16 = 8ms). At 100 times per second, this was more than responsive enough for our needs!

Until we can use the CNC again....

...the most exciting thing about getting a CNC machine working is the ability to quickly and easily drill our home-etched PCBs. But also, the ability to carve shapes and make enclosures for forthcoming projects is pretty cool too.

We've a few projects in the pipeline, which make use of some pretty simple but powerful underlying technology. After running a few workshops in and around Brighton and receiving a few emails from previous posts on other projects, we're going to document these in their entirety.

We'll be using the 18F2455 and 18F4550 PIC microcontrollers to create USB/HID devices. And we're also incorporating a simple touch-sensitive interface. At the minute, capacitive touch sensors are all the rage. What this basically means is that each touch pad connects to a microcontroller and when the user places their finger over the pad, a simple capacitor is created. The relative capacitance of the pad is compared over time and when the capacitance changes, the microcontroller can detect whether a finger has been placed near or removed from the pad.

The downside of capacitance touch sensing is the need for relatively large pads - or dedicated capacitive sensing hardware.

To keep our project simple - both in writing the firmware (capacitive sensing firmware can be quite convoluted and multiple readings averaged over time to smooth out any rogue analogue readings) and in sourcing the hardware - we're going to use an alternative approach:

A transistor is often used as an electronic switch, but it can also be used as an amplifier. A tiny current onto the base pin of an NPN transistor allows a much larger current to flow through the collector and emitter pins.



Whatever current is directed onto the base pin is amplified onto the collector pin.
By "feeding" the output from one transistor into the base pin of a second transistor, we can amplify the input signal many thousands of times over



In fact, by feeding one transistor into another, even the tiny amount of current that passes over the surface of your skin can be used as a switch. Such transistor pairs are available in a single package, known as a Darlington transistor.

Here's an example of how we can use a darlington transistor as a touch sensitive input device for a PIC microcontroller:

The schematic above uses a darlington transistor, such as a BC517 as a single discrete component. Although a darlington transistor is actually two transistors connected as shown above, we will draw it as a single transistor for simplicity.

Touching the two pads - however large or small they may be - causes the transistor to switch, forcing the current to flow from the input pin to ground. While this may seem counter-intuitive (normally you might expect voltage to flow into an input pin to indicate an input switch) the reason for this should become clear: on a lot of controllers (and had we put this input onto PORTB) you can use internal pull up resistors on the inputs, removing the need for the external resistor as shown in this example. If your controller does not have internal pull-ups, the resistor is there to stop the input pin "floating" when no finger is present on the contacts. The resistor should be quite a high value, say 100K.
Now, when the pads are touched, a tiny current flows from 5v on PAD1, over your finger, onto PAD2 and into the base of the darlington transistor. The transistor amplifies this current, creating a "switching effect" and causes the input pin to go low.
When the finger is removed off the pads, no more current flows into the base pin, the transistor closes the "switch" and no current can flow from the input pin to ground. The pull-up resistor causes the input pin to go high when the pads are not touched.

Although the above gives us a working touch-sensitive switch, we're not quite done. If you try the schematic out, you might find - depending on the type of darlington transistor used - that while the "on" trigger works (i.e. the input goes low immediately after touching the pads) the "off" time can be quite slow (i.e. the input pin remains low for a second or more after removing your finger from the pads).

The reason for this is that the darlington transistor can amplify even the tiniest little current - even residual electrical noise can be used as a trigger; it's a bit like leaving an input pin floating - the base pin of the transistor is so sensitive it can switch on and off almost at random. And like a floating input pin, it can remain active even when the input is removed.



The answer is to put a pull-down resistor on the base pin.
Now, when your finger is removed, any residual current on the base pin has a path to ground, and the switch closes. The size of the resistor determines the response time. If the resistor value is too low, it may stop the transistor switching on at all (the human body has an electric resistance of around 40k-100k so this base resistor needs to be much higher) but too high and the residual current on the base pin may take too long to be pulled to ground, resulting in slow response times. In practice, we found that a resistor with a 1M resistor on the base pin (R2), with a 100K pull-up resistor (R1) on the input pin worked well and gave reasonable response times.