Monday, August 9, 2010

Microchip AN556

Well..

This isn't really only related to my sequencer, but since I'm programming a PIC to use with it I'll tag it as Step4x16 sequencer related anyway. Oh, I changed the name of the sequencer again to "Step4x16". I found it's the most logical name since.. well.. it's got 4 patterns with 16 steps each..

Anyway, there is a bug in Microhip's application notes "AN556 - Implementing a Table Read"...

The PIC16F886 and its lovely pages...

Timing
Ok, I decided to make a lookup table for the BPM timer calculation. After spending like a whole day on considering different best, coolest, smallest, most flexible or simple solutions, I decided:

Timer1 (16-bit timer with 8:1 prescaler) will be used to trigger 64th-note events (I need it for the gate lengths for each step). I have chosen a 20Mhz crystal as external oscillator to get low latency and good accuracy, but that also means that the longest timer value I can get with the 16-bit timer, including the 3-bit prescaler, is 104,8476 milliseconds. If that is the longest delay I can get and I want to use it for triggering 64th-notes, I'll get a minimum tempo of about 35,7662 BPM. Since I'll only use integer BPMs I'll round that up to 36 BPM.
I'll be using a byte for the tempo setting and if I let 0 represent 36 then of course 255 will represent 291 BPM.

Now, even with a 20MHz crystal it's impossible (partly due to the prescaler I think) to get exact timer values for the BPMs. I calculated the lowest 16 BPMs and found that BMP 50 is the only one that will be "exactly right", of course ignoring tolerances in the crystal and all that...

The comparator used together with the timer is of course also 16-bit. I figure it's simpler to just store the timer values as 2 bytes in program flash in a lookup table rather than trying to calculate them on the fly. Yes, I know each program word consists of 14 bytes and I could probably compact the values a little better, but I'm not that desperate, yet..

Lookup tables and page crossing
Anyway. There is a risk here. If the table crosses from one page to another, the offset calculation used to jump into the table will just wrap and you will end up way before the table instead of in the far end.
Luckily, Microchip have warned developers that this should be taken into consideration and have kindly provided application note:
AN556 - Implementing a Table Read

To cut it short. Either you make sure your tables are small and are located so they don't cross page boundaries, or you use "Example 5":


org 0x80
movlw LOW Table ;get low 8 bits of address
addwf offset,F ;do an 8-bit add operation
movlw HIGH Table ;get high 5 bits of address
btfsc status,c ;page crossed?
addlw 1 ;yes then increment high address
movwf PCLATH ;load high address in latch
movf offset,w ;load computed offset in w reg
call Table
.
.
org 0x9FD
Table:
movwf PCL,F ;load computed offset in PCL
retlw ’A’ ;return the ASCII char A
retlw ’B’ ;return the ASCII char B
retlw ’C’ ;return the ASCII char C


Problem with this is that if you give offset 0, the code will get stuck on the "movwf PCL,F" row. This is because the offset is wrong. You give it offset 1 and you will get the "A" in the example, which really should be represented by offset 0.
So, there is a bug in Microchips Application Notes. Lovely, as if there aren't enough problems with the paging :) Ok, luckily I haven't even tried to run this on a real PIC yet. I have only simulated in MPLAB and I am happy I can do that :)
I noticed the bug and made some alteration to the AN556 notes:
1- Replacing "addwf offset,F" with "addwf offset,W" causes the operation to not store the carry-check addition back in the offset variable.
2- Replacing the "movwf PCL,F" at the table start with "addwf PCL,F" causes the code to actually "jump" instead of setting the counter.

So, a working Example 5 should probably look like this:


org 0x80
movlw LOW Table ;get low 8 bits of address
addwf offset,W ;do an 8-bit add operation
movlw HIGH Table ;get high 5 bits of address
btfsc status,c ;page crossed?
addlw 1 ;yes then increment high address
movwf PCLATH ;load high address in latch
movf offset,w ;load computed offset in w reg
call Table
.
.
org 0x9FD
Table:
addwf PCL,F ;load computed offset in PCL
retlw ’A’ ;return the ASCII char A
retlw ’B’ ;return the ASCII char B
retlw ’C’ ;return the ASCII char C



Sorry about the shitty formatting. I'll see if I can fix it.

No comments:

Post a Comment