So I'm working on yet another improvement to my ongoing midi synth project: adding a 12-bit DAC, and reworking all the synthesis code.
I'm using an MCP4922 SPI DAC.
If I use the normal SPI library functions, I can successfully write values to the DAC.
But I have an interrupt routine running at ~32KHz, so I'm using the GCC inline assembler to code the interrupt in assembly.
Here is the interrupt routine, stripped down to what I believe are the problem sections:
ISR(TIMER1_OVF_vect) { //ASSEMBLER WOOT
byte tempOutput;
__asm__ volatile (
//some port addresses, to make things easier
".equ PORTB,0x05" "\n\t"
".equ SPDR,0x2e" "\n\t"
".equ SPSR,0x2d" "\n\t"
//------------------------------------------------------------------------
"mov %[debugByte],%B[outputA]" "\n\t"
//output first, most significant, byte for DAC thru SPI
//this variable changes in the first "batch" of oscillators, so a copy must be made
"mov %[tempOutput],%A[outputA]" "\n\t"
//slave select pin is pin B2, for the DAC
"cbi PORTB,2" "\n\t"
//begin SPI transfer
"out SPDR,%B[outputA]" "\n\t"
//reset outputA. done after transfer, to have a buffer.
//set all the neccessary config bits.
//and set the output to the middle value. 0x78 = 0b01111000
"ldi %B[outputA],0x78" "\n\t"
//lower bit of output is all data, no config bits
"ldi %A[outputA], 0x00" "\n\t"
//OSCILLATOR CODE
//send lower byte, previously moved, since the first "batch" edited the original output byte
"Wait1:" "\n\t"
//wait until bit 7 of SPSR is set, the SPI flag. SPI should be done by now. but just in case.
//annoyingly, SPSR is not usable in the sbis command. so put in a register first.
"in r1,SPSR" "\n\t"
"sbrs r1,7" "\n\t"
"rjmp Wait1" "\n\t"
"out SPDR,%[tempOutput]" "\n\t"
//OSCILLATOR CODE
"Wait2:" "\n\t"
//wait until bit 7 of SPSR is set, the SPI flag. SPI should be done by now. but just in case.
//annoyingly, SPSR is not usable in the sbis command. so put in a register first.
"in r1,SPSR" "\n\t"
"sbrs r1,7" "\n\t"
"rjmp Wait2" "\n\t"
"rjmp AllDoneYay" "\n\t"
//--------------------------------------------------------------------
"AllDoneYay:" "\n\t"
"sbi PORTB,2" "\n\t"
//=======================================================================
: //outputs
[outputA] "=&d" (outputA),
[tempOutput] "=&d" (tempOutput),
[debugByte] "=&d" (debugByte)
: //inputs
: //clobbered
);
}
OutputA is a global volatile int declared at the beginning of the program.
DebugByte is a volatile global byte used to monitor the value of outputA. In loop(), debugByte is sent repeatedly thru serial.
The OSCILLATOR CODE parts are where code would go that would calculate the outputs of each oscillator. But the problem persists with or without these parts, so they're not relevant.
I interspersed both SPI transfers so that no time is wasted transferring; the oscillator code is executed while the transfer is carried out. The oscillator code, though, modifies the outputA variable, which is why I had to transfer the low byte of the output to the tempOutput byte.
With the above code, I get loads of garbage.
If I move the debugByte mov instruction to after the ldi's of outputA, I get the expected message "120", or 0x78.
If I move the outputA ldi's to before the SPI transfers, the transfers work fine. I get the expected ~2.5V output. (the DAC takes 2 bytes to set one channel. The first nybble of the high byte is configuration bits for the DAC; they should be 0x7. the 12 other bits are data bits, so 0x7800 gets an output in the middle of the range)
I want the oscillator code to come after the SPI transfers (and the moves to temporary bytes) so the sample rate is constant regardless of any variation in the speed of the interrupt function.
But something's just getting messed up somewhere.
Any ideas?