SYNTHINGS is a digital synthesizer development platform that I've been building.

Pattern editor (part 2), and rotary encoder

jason | 06 January, 2011 23:20

I started to implement the pattern editor like the text input - you select which row you want to edit, and then select which field you want to edit from that. However, I had a bit of a brain-wave. I could reduce the number of editing levels by one if I reorganised how the keys worked.

If I made the rotary encoder change the current value up/down, and the left key moves to the field to the left of the current field, and the right key moves to the right, then you wouldn't need to select the row, and then select the field. Instead, you would select which row you were editing if the field you were 'editing' was the row number. If the field you were editing was the note, then the rotary encoder would change the note.

This didn't need a great deal of change to implement - highlighting the current field had already been implemented (when you previously selected the row, it highlighted all of the fields - which was actually implemented as a bitfield to say which field was highlighted).

I also allowed MIDI events to be interpteted as keyboard events insofaras you can ask the keyboard code for a key press, and it would return a MIDI note on (for example) as a "key press". This meant that if you are currently using the pattern editor, you can press a key on a MIDI keyboard, and it'll automatically insert the required note at the current location (and also scroll down).

If the current field is the line number, then pushing the rotary encoder down will take you back to select which of the pattern fields you want to edit.

This is what a basic pattern looks like:

Pattern editor (2)

I don't currently have a way to actually use the rotary encoder to do anything other than scroll up and down, and I've not even written the code to display the sample number or effects.

I will need to invent some way to select the current sample more easily, as well as insert/delete rows - this will probably be some form of context menu which will be displayed when you press the rotary encoder down.

The only thing was that the rotary encoder was really behaving badly - and I absolutely wanted to get this sorted now.

I got the Digilent Spartan-3E development board out, and found my Digilent test project - which happened to be the precursor to the rotary encoder code I was using. It controls the LEDs on the development board, and they display the binary value for the rotary position. The only difference is that I was using a 10-bit counter for the Digilent board, and a 9-bit counter for my board due to the detents being every 4 pulses in the Digilent board, and 2 on mine.

This worked perfectly - which was a bit annoying, but also good in that I knew that my Verilog code was fine. I added a DCM to get the 100MHz clock that I'm using in my board - and it still worked (just in case the DCM was causing the problems).

I looked closer at the rotary encoder I'd been using - it didn't have a screw fitting like the Digilent one did, and also like one I'd used in an earlier project (which had a software implementation of a rotary encoder - this would be running at a very much slower speed than 100MHz). This made me think that the rotary encoder I'd used wasn't the same manufacturer - and could be a lower quality part.

A quick desolder/resolder session, and I plugged it back in. Low and behold, it worked - albeit twice the number of pulses, so in order to get to odd number positions, I needed to hold the rotary encoder between detents. This is the difference between the 10-bit and 9-bit counter.

A quick rework of the Verilog code to make it a 10-bit counter showed that it was now fine. The only niggles are that the detents are less well defined (it turns much easier), and that there's a bit of a slack zone between detents which feels a bit odd. The Digilent board also has this (it's basically backlash in CNC terms).

I then removed some guard code I'd put in to prevent massive jumps in the returned value from the rotary encoder (to catch out the other encoder's problems).

It's now much much much better. And today's code size:

Program Memory  [Origin = 0x200, Length = 0x2aa00]

section                    address   length (PC units)   length (bytes) (dec)
-------                    -------   -----------------   --------------------
.text                        0x200              0x1200          0x1b00  (6912)
.const                      0x1400              0x249e          0x36ed  (14061)
.text                       0x389e              0xa148          0xf1ec  (61932)
.dinit                      0xd9e6               0x1d0           0x2b8  (696)
.text                       0xdbb6               0xd0c          0x1392  (5010)
.isr                        0xe8c2                 0x2             0x3  (3)

                     Total program memory used (bytes):        0x15a26  (88614) 33%

Data Memory  [Origin = 0x800, Length = 0x7800]

section                    address      alignment gaps    total length  (dec)
-------                    -------      --------------    -------------------
.nbss                        0x800                   0           0x9ea  (2538)
.ndata                      0x11ea                   0             0x2  (2)
_03604ee04d23a5fe           0x11ec                   0           0x200  (512)
_036070704d23a5fe           0x13ec                   0           0x200  (512)
.nbss                       0x15ec                   0           0x190  (400)
.ndata                      0x177c                   0            0x86  (134)
.nbss                       0x1802                   0            0xb6  (182)
.ndata                      0x18b8                   0            0x26  (38)
.nbss                       0x18de                   0             0xe  (14)
.ndata                      0x18ec                   0             0x8  (8)
.nbss                       0x18f4                   0            0x1a  (26)
.ndata                      0x190e                   0             0x2  (2)
.nbss                       0x1910                   0             0x4  (4)
.ndata                      0x1914                   0             0x4  (4)
.data                       0x1918                   0            0xbc  (188)
.dconst                     0x19d4                   0             0x8  (8)
.data                       0x19dc                   0             0x6  (6)
.bss                        0x19e2                   0             0x4  (4)
.data                       0x19e6                   0             0x2  (2)
.heap                       0x19e8                   0          0x2000  (8192)

                        Total data memory used (bytes):         0x31e8  (12776) 41%

Dynamic Memory Usage

region                     address                      maximum length  (dec)
------                     -------                      ---------------------
heap                        0x19e8                              0x2000  (8192)
stack                       0x39e8                              0x4618  (17944)

                        Maximum dynamic memory (bytes):         0x6618  (26136)

Just over 0.5K added - that was probably feeding the MIDI events through the keypress code.

Oh - and one final thing - I'm pretty sure the FPGA lockups are actually a dirty contact between the top and bottom boards. Occasionally it'll do a few lockups, but if I wobble the board about, it then starts to work fine. When I get on to make SYNTHINGS 2, it'll be a single board, so won't suffer from this.

Powered by LifeType - Design by BalearWeb and Jason Tribbeck
Copyright © 2010 | | Jason Tribbeck
All trademarks are the property of their respective owners