davebranton 11 Posted February 13, 2013 Share Posted February 13, 2013 Hi, Since I have two of the MSP430's left over from the two launchpads I bought, I though I'd breathe new life and fun into the now-obsolete PlayStation one light guns. Using an MS430, an 8-pin DAC in a DIP package (an MCP4911) and an LM386 for amplification. Thus far, the basic plan is: Run the 430 of two AAs directly Use a DC converter (TPS61041) to step this up to 5 volts ish for the LM386, Use a FET to turn this 5v side off when in sleep mode Use the 430 to generate some incredibly awesome laser gun sound... ..and actually a cool reloading (yes I know laser guns don't reload) sound too, because the gun has these cool buttons on the sides too. Steps one and two are no problem, and talking to the DAC is over SPI so that should be straightforwards too. This should be a fun project, but I've never done any synthesis before. I guess I could put an SD card in there and use digital samples, but I'd like to create the sounds generatively. Has anyone got any ideas? I've heard of FM synthesis, but it doesn't sound all that flash to me. Also, I've come across this rather amazing thing, which might also be a possibility if I could get the numbers right: http://countercomplex.blogspot.co.nz/2011/10/algorithmic-symphonies-from-one-line-of.html And just to add a picture too, here's the surface-mount DC converter soldered onto a bit of perfboard next to the schottky diode. Quote Link to post Share on other sites
oPossum 1,083 Posted February 13, 2013 Share Posted February 13, 2013 Keep in mind that alkaline cells (AAA, AA, C, D, etc) begin at 1.5V and have an almost linear decline to 0.9V over their useful life. The MSP430 can run on 1.8V, but only at 6 MHz. It requires at least 3.3V to run at 16 MHz. Here is a simple laser sound using an exponential change in frequency of a square wave on P1.6. #include <msp430.h> void main(void) { WDTCTL = WDTPW | WDTHOLD; // Disable watchdog // DCOCTL = 0; // Run DCO at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // // // I/O assignment P1DIR = BIT7 | BIT6 | BIT5 | BIT4 | BIT2 | BIT0; // Direction P1REN = BIT3; // Resistors P1OUT = BIT2; // Output P1SEL = BIT6 | BIT4 | BIT1; // Select Timer A output, SMCLK output, UART Rx // TA0CTL = TASSEL_2 | MC_1; // Timer A config: SMCLK, count up TA0CCR1 = 1; // // for(; { // Forever unsigned n; // TA0CCTL1 = OUTMOD_4; // Timer out mode toggle //for(n = 200; n < 7000; ++n) { // High -> low freq for(n = 7000; n > 200; --n) { // Low -> high freq TA0CCR0 = n; // Set timer period __delay_cycles(200); // Rate of frequency change } // TA0CCTL1 = OUTMOD_0; // Timer out mode off __delay_cycles(16000000L * 2); // Wait 2 seconds } // } Here is the "algorithmic symphonies" using PWM audio on P1.6. Only three formulas in this code - just enough to test it. /* Copyright © 2012 Kevin Timmerman This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <msp430.h> #include <stdint.h> static volatile unsigned sample = 244; // PWM sample static volatile unsigned s = 0; // Timer void main(void) { WDTCTL = WDTPW | WDTHOLD; // Disable watchdog // DCOCTL = 0; // Run DCO at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // // // I/O assignment P1DIR = BIT7 | BIT6 | BIT5 | BIT4 | BIT2 | BIT0; // Direction P1REN = BIT3; // Resistors P1OUT = BIT2; // Output P1SEL = BIT6 | BIT4 | BIT1; // Select Timer A output, SMCLK output, UART Rx // TA0CCR0 = (16000000L / 32768L) - 1; // Setup Timer A for 32768 Hz period TA0CCR1 = TACCR0 / 2; // Setup Timer A compare to midpoint TA0CCTL1 = OUTMOD_7; // Setup Timer A reset/set output mode TA0CTL = TASSEL_2 | MC_1; // Timer A config: SMCLK, count up TA0CCTL0 |= CCIE; // Enable period interrupt // _enable_interrupts(); // Enable interrupts // unsigned n = 0; // unsigned x = 244; // for(; { // Forever long int t; // long l = 8192L * 10; // Track length for(t = 0; --l; t++) { // s = 4; // 4 PWM pulses per PCM sample sample = x; // Update PCM sample x = 116; // Set PWM bias switch(n) { // Calculate next sample default: // n = 0; // Start over case 0: // x += (char)(t * (t >> 12 | t >> 8) & 63 & t >> 4); break; // case 1: // x += (char)((t * (t >> 5 | t >> 8)) >> (t >> 16)); break; // case 2: // x += (char)(t * ((t >> 9 | t >> 13) & 25 & t >> 6)); break; // } // __bis_SR_register(LPM0_bits + GIE); // Wait for PCM sample period to end } // ++n; // Next track __delay_cycles(16000000L * 2); // Wait 2 seconds } // } #pragma vector = TIMER0_A0_VECTOR // Timer A CCR0 interrupt __interrupt void timer_a0_isr(void) // This interrupt will occur when the PWM period is near complete // when TAR == CCR0 - 1 { // // TA0CCR1 = sample; // Output sample // if(s) if(!--s) __bic_SR_register_on_exit(LPM0_bits); } // timotet 1 Quote Link to post Share on other sites
davebranton 11 Posted February 13, 2013 Author Share Posted February 13, 2013 That's a very good point about the supply voltage. I'll have to run some experiments to see how much speed I'll need to generate good sound effects. 6Mhz might well be enough because I'll be sending samples to a DAC at probably only 20kHz or so. At 16 bits per sample (even though it's only a 10 bit dac, it still wants 16 bits before it'll output...) that's about 320kHz, or 0.3Mhz SPI clock speed. So from that point of view 6Mhz would be plenty to drive the DAC. Then it's just a question of writing some really tight code with a lookup table / algorithmic outputs and seeing what comes out. Quote Link to post Share on other sites
davebranton 11 Posted February 18, 2013 Author Share Posted February 18, 2013 So, with an eye to the exponential chirp code above, and following some-one else's advice in youtube, I've got a phased linear chirp plus some filtered white noise sounding quite nice in matlab (m-file attached for those who happen to have access to matlab and are interested laser.m.txt). I'm probably going to need to code this in assembly in order to keep up with the 20kHz ouput frequency, since at 8Mhz I'll have only 400 cycles to calculate each sample. Actually, that's probably plenty, but assembly is interesting anyway, and presumably there are some great resources on assembly on this site somewhere. I don't need a basic introduction, just a nice table of the assembly instructions and what they do (different kinds of shifts, how the flags are effected, addressing modes, etc etc). If anyone could point me in that direction, I would be extremely grateful, and will of course post the final code that I come up with. Thanks very much in advance. -dave Quote Link to post Share on other sites
oPossum 1,083 Posted February 18, 2013 Share Posted February 18, 2013 Wikipedia has a nice table of instructions: http://en.wikipedia.org/wiki/TI_MSP430#MSP430_CPU Details of the instruction set are in the 2000 series users guide: http://www.ti.com/litv/pdf/slau144i An example of using C & asm for sound synthesis: http://forum.43oh.com/topic/912-32k-interupts-a-second/?p=10283 Details of using assembly: http://www.ti.com/litv/pdf/slau132g and http://www.ti.com/litv/pdf/slau131e Basically R12 to R15 are passed from C code and you can trash them - preserve all others. The multiplications in your code (in the IIR filter) will take by far the most time because the 2000 series does not have a hardware multiplier,. davebranton 1 Quote Link to post Share on other sites
davebranton 11 Posted February 19, 2013 Author Share Posted February 19, 2013 Thanks for the hint about the multiplier, I decomposed the multiplies into various shifts and adds, and the c version of the code works great! ...So I thought to myself, even though I don't actually need to, I'll have a crack at using the inline assembly to get the same results and free up some cycles. ...But... inline assembly is a strange beast in msp430-gcc land. I honestly can't make head nor tail of the documentation, and very strange things started to happen. But in any case, I did have a question about this code: mov &fade_shifts_counter, r15 add #llo(-1), r15 mov r15, &fade_shifts_counter mov &fade_shifts_counter, r15 cmp #0, r15 jne .L5 mov.b &fade_shifts, r15 add.b #1, r15 mov.b r15, &fade_shifts mov #2000, &fade_shifts_counter .L5: This is what msp430-gcc produces in response to the following c code fade_shifts_counter--; if (fade_shifts_counter == 0) { fade_shifts++; fade_shifts_counter = FADE_SHIFTS_STEP; } Now, to me the assembly looks pretty strange, and very poorly optimised. My attempt at hand-coding the assembly gives: dec.w &fade_shifts_counter jnz .CounterNotZero inc.b &fade_shifts mov.w #2000,&fade_shifts_counter .CounterNotZero:" Which seems like it should perform a bit better and doesn't clobber any registers, but doesn't appear to actually work. Or rather, appears to behave in strange ways that I haven't quite got to the bottom of yet. I rather suspect that this may be related to the strange syntax employed by msp430-gcc for inline assembly. This document http://mspgcc.sourceforge.net/manual/c1308.html describes the inline assembly construct that I'm struggling with. On the plus side, my synthesiser is working, and sounds pretty cool, and looks like it should run from two AA's (although at the moment it doesn't, which seems to be something to do with the boost circuit - the msp430 is perfectly happy). And soon I'll squeeze it onto some perfboard and stick it into one of the light guns. Quote Link to post Share on other sites
oPossum 1,083 Posted February 19, 2013 Share Posted February 19, 2013 But... inline assembly is a strange beast in msp430-gcc land. I honestly can't make head nor tail of the documentation, and very strange things started to happen. It is. One of many reasons I use CCS - easy to mix asm with C, better documentation, usually faster code, and overall just much more productive. But in any case, I did have a question about this code: Try this, Think in asm, write in C. if(--fade_shifts_counter) { fade_shifts_counter = FADE_SHIFTS_STEP; ++fade_shift; } davebranton 1 Quote Link to post Share on other sites
davebranton 11 Posted February 19, 2013 Author Share Posted February 19, 2013 Well I ran the same code through CCS, and it's a damn clever compiler. It even figured out that my sequence of shifts and adds constituted a multiply and jumped off into some subroutine apparently called __mspabi_mpyi. Impressive - assuming that this would be faster than the shifts and adds of course, but I'll give it the benefit of the doubt. So I'll be sticking with CCS, even though I'll have to run it in a VM it's going to save me a lot of time. Thanks very much for your advice. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.