Monday, 5 October 2015

Guitar tutor with PIC 16F1829 microcontroller and 74HC595 shift registers

Griff Hamlin, Steve Stine and Papastache all have one thing in common. Ok, they have lots of things in common. Mostly, they're all awesome guitarists. Not just "a bit good" - but amazing. I love Griff Hamlin's way of teaching blues guitar with his Blues Guitar Unleashed course. I've even got a copy (though haven't yet gone much beyond the very basics). And, being a bit of a how-does-it-work geek, I love how well Steve Stine explains not just what notes to play, but why you should play them. And while Steve plays some great blues licks too, he's a wizard on just about any genre (his Thunder Rock Riffs looks pretty cool for example).

But for me, +papastache102 has some of the best "how to play and why you're playing it that way" video lessons on the interwebs (I also have quite a few of his DVDs). His mixing of major and minor pentatonic sounds, and targeting notes in the underlying chords is incredible. His moustache is almost as impressive as his guitar playing, and he regularly teams up with session supremo Tim Pierce (whose guitar playing is on a whole other level). But there's something about Bret Papa's playing that immediately identifies it as him. It's kind of Hendrix-y mixed in with some late 70s classic rock and even a bit of 80s stadium heavy metal. Just my kind of thing!

Anyway, enough about how great these guys are (but, honestly, they really are) and how great their lessons are (but, honestly, they really are) and more about microcontrollers and shift registers. And how they have anything to do with guitar playing anyway.

Well, over at Nerd Towers, we've a whole heap of LEDs and plenty of 595 shift registers knocking about. After creating a MIDI keyboard to learn how to play chords and scales on the piano, a guitar tutor was a project that some of us (mostly me, I admit it) were keen to make. And after watching a recent Papastache video (https://www.youtube.com/watch?v=jUycCxd_6GI) we just knew it's time had come!

Yeeaaaahhh. Welcome, welcome, welcome to the Nerd Club blog, Papastache!

The key to a lot of getting a guitar to sound "right" is to target the notes in the underlying chords. Sure you can just noodle around on a blues/pentatonic scale right over the top of a standard I-IV-V progression - but it'll only ever sound like someone noodling around with a scale over a chord progression. Maybe throw in a few "stock licks" to break it up a bit, but it's quite difficult to sound musical with just one scale under your fingertips!

Bret's video demonstrates how different scales can "attach" to different chords. And he talks a lot about mixing major and minor scales by seeing them over one another in the same place on the fretboard. Which is great. I love the theory behind it. I'm totally with Papa' all the way. Until I pick up my guitar - and then look blankly at the fretboard, as all the notes and positions fall away and I don't really even know where to start.

Which is why, with a bunch of surface mount LEDs, a handful of shift registers and an afternoon free to play about with a vat of ferric chloride, we decided it was time to start our guitar tutor in earnest.

On a very basic level, it's simply a bunch of LEDs connected to some shift registers that we can turn on and off. The firmware will need to get quite complex after that, but it's basically just a rework of the MIDI keyboard but with knobs on. The keyboard was a great "starter" project for LEDs, but it did involve a lot of wiring. This time, we're simplifying things, by making a few PCBs.



We decided to make the guitar fingerboard in sections, rather than one great big long PCB. The most obvious reason was, of course, because most of our copper clad board is 160mm x 100mm eurocard size, and our etching tank doesn't really like taking great big boards. But it also means we can build and test the "light-up-fingerboard" in sections, instead of having to build the whole thing, only to find one little bit doesn't work, and it junks the rest of the project!

Here's the top section (first four frets) with LED markers for each of the strings.


This design gets us six strings worth of markers for four frets, using three shift registers. The whole design can be repeated, but moving the LEDs closer together (vertically) and spacing them horizontally further apart for the next four frets on the guitar neck (look at a guitar fingerboard - the frets get closer together, and the neck widens as it gets closer to the guitar body).

The modular design meant we could build and test our idea in sections


And soldering up each section wasn't so daunting. Once you've done a (horizontal) row of six surface mount LEDs by hand, you're a quarter of the way through each section!



For testing, we connected wires to each section and plugged it into the breadboard testing rig. We created a single 8-bit value (starting at 0x01) and bit-shifted it left, one place every half a second. This allows us to check that every LED is working as it should, before trying to debug more complex problems in the future.



With one board out of the way, and inspired by the success, it didn't take long to create another two sections, to give us a total of 12 frets-worth of markers (as any guitar-player knows, the patterns simply repeat from the 12th fret onwards, so we'll stick at 12 for now).

Now we had to create some firmware to display chords, scales, patterns and shapes. That's not quite so easy! Because the boards were designed to be easy to create (single-sided, compact, etch-at-home) it means that the output patterns for the shift registers are not very intuitive.

We're using 32-bit values to record the positions of which LEDs we want to light up. So let's say we wanted to light up a simple C-major chord. In guitar-land, that looks like this:


So our 32-bit value for the second (A) string shown above would be 001000000000000
Similarly, the 32-bit value representing the D string would be 0100000000000
And the B string would be represented by 1000000000 (we've not included the open strings just for now, but in the final version, they'll need to be added in: we're just trying to keep things relatively simple by making the patterns match the charts in this example).

But if you watch the video above, you'll see that the first LED that lights up is not the first position on the first string. It's the fourth position/fret on the second string. Then the third (on the second string) then the second fret, then the first fret. Confusingly, the pattern doesn't repeat: it then goes to the first fret on the first string, then the second fret, third then fourth (all on the first string).

Then it skips down to the fourth fret on the fourth string (not the third), works it's way back to the first fret, before jumping up to the third string, and moving back out, from the first to the fourth fret position. This pattern repeats on the sixth and fifth strings also.

Which means, we've got to create some clever algorithms (or, if +Steve Carpenter were writing it, maybe just a bucketload of IF statements!) to turn our chord and scale patterns into "shift register" patterns, to get the right LEDs to light up at the right time.

Here's what we came up with:


Const pentatonic_box_one=1
root_fret = 1
pattern_to_show=pentatonic_box_one
Gosub load_pattern
Gosub strings_to_shift_patterns
Gosub send_shift_data

loop:
     'do nothing
Goto loop

load_pattern:
     Select Case pattern_to_show
          Case pentatonic_box_one
          string6.4B = 00001001b
          string5.4B = 00001001b
          string4.4B = 00001010b
          string3.4B = 00001010b
          string2.4B = 00001010b
          string1.4B = 00001001b
          
          If blues_as_pentatonic = 1 Then
               string2.4B = string2.4B Or 00000100b
               string5.4B = string5.4B Or 00000010b
          Endif
     EndSelect
     
     'now we've drawn these onto fret 5 (A minor for
     'pentatonic scales, E-shape chords and for A-shape
     'chords, we've already started on fret12 and
     'D-shape chords, root on fret 10) so shift these
     'along depending on the start fret required
     
     If root_fret < 5 Then
          k = 5 - root_fret 'this is how many "spaces" left we need to move the box
          string6 = ShiftLeft(string6, k)
          string5 = ShiftLeft(string5, k)
          string4 = ShiftLeft(string4, k)
          string3 = ShiftLeft(string3, k)
          string2 = ShiftLeft(string2, k)
          string1 = ShiftLeft(string1, k)
     Endif
     
     If root_fret > 5 Then
          k = root_fret - 5 'we need to move the box up k frets
          string6 = ShiftRight(string6, k)
          string5 = ShiftRight(string5, k)
          string4 = ShiftRight(string4, k)
          string3 = ShiftRight(string3, k)
          string2 = ShiftRight(string2, k)
          string1 = ShiftRight(string1, k)
     Endif
     
Return

strings_to_shift_patterns:

     'firstly, make sure that any pattern on the strings is repeated
     'up and down, beyond the 12th fret
     For i = 1 To 6
          If i = 1 Then tmp_w = string1
          If i = 2 Then tmp_w = string2
          If i = 3 Then tmp_w = string3
          If i = 4 Then tmp_w = string4
          If i = 5 Then tmp_w = string5
          If i = 6 Then tmp_w = string6
          
          tmp_w = ShiftLeft(tmp_w, 12)
          
          If i = 1 Then string1 = string1 Or tmp_w
          If i = 2 Then string2 = string2 Or tmp_w
          If i = 3 Then string3 = string3 Or tmp_w
          If i = 4 Then string4 = string4 Or tmp_w
          If i = 5 Then string5 = string5 Or tmp_w
          If i = 6 Then string6 = string6 Or tmp_w

          If i = 1 Then tmp_w = string1
          If i = 2 Then tmp_w = string2
          If i = 3 Then tmp_w = string3
          If i = 4 Then tmp_w = string4
          If i = 5 Then tmp_w = string5
          If i = 6 Then tmp_w = string6
          
          tmp_w = ShiftRight(tmp_w, 12)

          If i = 1 Then string1 = string1 Or tmp_w
          If i = 2 Then string2 = string2 Or tmp_w
          If i = 3 Then string3 = string3 Or tmp_w
          If i = 4 Then string4 = string4 Or tmp_w
          If i = 5 Then string5 = string5 Or tmp_w
          If i = 6 Then string6 = string6 Or tmp_w
     Next i
     
     'because each shift register is wired up in an weird way
     'we need to translate the strings into shift patterns
     
     'shift pattern one is first four bits of string 2 followed by
     'the first four bits of string 1 in reverse
     
     k = string2.4B And 11110000b
     k = ShiftRight(k, 4)
     j = string1.4B And 11110000b
     Gosub reverse_j
     j = ShiftLeft(j, 4)
     shift_pattern1 = k Or j

     'similarly, shift pattern two is first four bits of string 4 followed by
     'the first four bits of string 3 in reverse

     k = string4.4B And 11110000b
     k = ShiftRight(k, 4)
     j = string3.4B And 11110000b
     Gosub reverse_j
     j = ShiftLeft(j, 4)
     shift_pattern2 = k Or j

     'and shift pattern three is first four bits of String 6 followed by
     'the first four bits of string 5 in reverse

     k = string6.4B And 11110000b
     k = ShiftRight(k, 4)
     j = string5.4B And 11110000b
     Gosub reverse_j
     j = ShiftLeft(j, 4)
     shift_pattern3 = k Or j
                    
Return

reverse_j:
     Dim tmp_j As Byte
     tmp_j = 0
     If j.0 = 1 Then tmp_j.7 = 1
     If j.1 = 1 Then tmp_j.6 = 1
     If j.2 = 1 Then tmp_j.5 = 1
     If j.3 = 1 Then tmp_j.4 = 1
     If j.4 = 1 Then tmp_j.3 = 1
     If j.5 = 1 Then tmp_j.2 = 1
     If j.6 = 1 Then tmp_j.1 = 1
     If j.7 = 1 Then tmp_j.0 = 1
     j = tmp_j
Return

send_shift_data:
     
     Low shift_latch

     'Since we're sinking not sourcing current to make
     'our LEDs light up, we want to set the shift register
     'output LOW to get it to light, not high. Easiest
     'way to do this is to XOR with 0xFF to invert.
     
     For i = 1 To 3
          Select Case i
               Case 1
               k = shift_pattern1 Xor 0xff
               
               Case 2
               k = shift_pattern2 Xor 0xff

               
               Case 3
               k = shift_pattern3 Xor 0xff

          EndSelect
          
          'start with the MSB (bit 7)
          pattern = 128
          
          For j = 0 To 7
               bit_pattern = k And pattern
               If bit_pattern = 0 Then
                    Low shift_data
               Else
                    High shift_data
               Endif
               
               'toggle the clock line
               High shift_clock
               Low shift_clock
               
               'move the pattern along, to send out bit6, then 5, then 4 etc.
               pattern = ShiftRight(pattern, 1)
          Next j
     Next i
     
     'rising edge on the latch sends shift register data to the output pins
     High shift_latch
     Low shift_latch
     
Return


Amazingly, it actually works!
Here's one four-fret section showing the famous "box one" pattern of the pentatonic scale (with added "blues notes" on the flatted 5ths)



We're not done yet.
There's still plenty of coding to do, to get all the different scales, chords and patterns programmed in. But it's a very encouraging start. And hopefully it won't be too long before we can "dial in" a chord sequence, and have the guitar tutor show us which box shapes to play, where, and when........



No comments:

Post a Comment