DavidEQ 4 Posted August 7, 2011 Author Share Posted August 7, 2011 Sine or square wave output, David? A simple NCO (numerically controlled oscillator) might work but generating a 4 kHz sine wave from a 32 kHz loop would only provide eight (8) sine samples per cycle so a 4 kHz output would be a crude looking sine wave. Maybe I'm not really sure but I thought you could get a decent sine wave out up to 40% or so of the sample (nco/dds) rate if I have a good low pass filter on the output. It works for radio. I checked and the highest tone I really need to make is 3400 hz and I have simulated 4khz or so (its a littler higher I lowered the series resistors to make the oupt at 3400 a little higher) in a design program using a quad op amp. The simulation shows a low level ~2khz spur with no input I have to look into however. The whole project looks like it would be easier with a TMS32 chip, they have a built in DAC. more IO pins and about the same price at Mouser. http://hackaday.com/2011/08/03/an-arm-dev-board-you-can-make-at-home/ Quote Link to post Share on other sites
oPossum 1,083 Posted August 7, 2011 Share Posted August 7, 2011 Maybe I'm not really sure but I thought you could get a decent sine wave out up to 40% or so of the sample (nco/dds) rate if I have a good low pass filter on the output. That is correct. The low pass filter is important to get a good sine wave at near 50% the sample rate. A 12 db/oct sallen-key or similar filter is generally practical for 40% of sample rate. The whole project looks like it would be easier with a STM32 chip, they have a built in DAC. more IO pins and about the same price at Mouser. The dsPIC33FJ128GP802 is also great chip for audio synth. It has dual 16 bit oversampling audio DACs and comes in DIP and SMD packages. It is about twice as fast as the fastest MSP430. It have written several variations of NCO code for the dsPIC in assembly. Here is an example of a NCO with interpolation and level control... sl osc01_phacch, WREG ; Get phase accumulator whole * 2 and W0, W1, W0 ; Limit to table size add osc01_wave, WREG ; Add to wave table address mov.d [W0], W2 ; Get 2 samples from wave table (Sample[phase] and Sample[phase + 1] ) sub W3, W2, W3 ; Calculate Sample Delta ( Sample[phase + 1] - Sample[phase] ) mov osc01_phaccl, WREG ; Get phase accumulator fraction mul.us W0, W3, W4 ; Multiply Phase Fraction * Sample Delta add W2, W5, W4 ; Sample = Sample[phase] + ( (Phase Fraction * Sample Delta) >> 16 ) mov osc01_level, W5 ; Get level mac W4 * W5, A ; Multiply sample * level and add to voice mov osc01_phincl, WREG ; Get phase increment LSB add osc01_phaccl ; Add to phase accumulator LSB mov osc01_phinch, WREG ; Get phase increment MSB addc osc01_phacch ; Add to phase accumuator MSB with carry from LSB add DavidEQ 1 Quote Link to post Share on other sites
DavidEQ 4 Posted August 7, 2011 Author Share Posted August 7, 2011 I'll check out the data sheet, I haven't really looked at pic chips in years. Back then the small page size of program memory turned me off. The piclist was a great place to find ways to do a lot with few pins. Quote Link to post Share on other sites
oPossum 1,083 Posted October 9, 2011 Share Posted October 9, 2011 Here is some updated code with 1 mHz (0.001 Hz) resolution. The usual way to get higher resolution is to make the phase accumulator larger. The previous code used 16 bits and had 0.5 Hz resolution. 32768 Hz / 2^16 = 0.5 Hz Using a 32 bit phase accumulator would give a resolution of... 32768 Hz / 2^32 = 0.00000763 Hz That is a rather cumbersome unit of measure to work with. To use units of 0.1 Hz would require multiplying by 13,107.2 To get a nice resolution, a phase accumulator with a fractional number of bits can be used. For 0.001 Hz resolution... 32768 Hz / 0.001 Hz = 32,768,000 log(32,768,000) / log(2) = 24.966 bits The fractional bit size requires that the phase accumulator must be tested for the max value instead of simply rolling over. The obvious way to do this would be... const long int phase_max = 32768000; phase_acc += phase_inc; if(phase_acc >= phase_max) phase_acc -= phase_max The code posted below simplifies this by testing for underflow rather than doing a comparison. The upper 8.966 bits are used to index the LUT, the middle 8 are used for interpolation, and the lower 8 are ignored. LUT sample size has been increased from 8 bits to 16 bits. // main.c /* Copyright (C) 2011 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 . */ #include "msp430g2231.h" /* 1 Vcc - 2 P1.0 Out -- IO LED1 3 P1.1 Out -- IO Txd 4 P1.2 In PU IO Rxd 5 P1.3 In PU IO Switch 6 P1.4 Out -- Alt SMCLK out 7 P1.5 Out -- IO Not Used 8 P1.6 Out -- Alt PWM Audio Out 9 P1.7 Out -- IO Not Used 10 RST --- -- -- SBW 11 TEST --- -- -- SBW 12 XOUT - 32768 Hz xtal 13 XIN - 32768 Hz xtal 14 Vss - */ int interpolate(int, int, unsigned); // Sample rate * (1 / resolution in Hz) static const long int phase_limit = 32768 * 1000; static long int phase_inc1 = 0; // Phase increment for frequency 1 static long int phase_inc2 = 0; // Phase increment for frequency 2 static const unsigned long Off[2] = { 0, 0 }; static const long unsigned DTMF[16][2] = { 941000, 1336000, // 0 697000, 1209000, // 1 697000, 1336000, // 2 697000, 1477000, // 3 770000, 1209000, // 4 770000, 1336000, // 5 770000, 1477000, // 6 852000, 1209000, // 7 852000, 1336000, // 8 852000, 1477000, // 9 697000, 1633000, // A 770000, 1633000, // B 852000, 1633000, // C 941000, 1633000, // D 941000, 1209000, // * 941000, 1477000, // # }; static const long unsigned Dial[2] = { 350000, 440000 }; static const long unsigned Ring[2] = { 440000, 480000 }; static const long unsigned Busy[2] = { 480000, 620000 }; static const long unsigned tt[][2] = { 350000, 440000, // Dial 440000, 480000, // Ring 480000, 620000, // Busy 941000, 1336000, // 0 697000, 1209000, // 1 697000, 1336000, // 2 697000, 1477000, // 3 770000, 1209000, // 4 770000, 1336000, // 5 770000, 1477000, // 6 852000, 1209000, // 7 852000, 1336000, // 8 852000, 1477000 // 9 }; // Frequency is in units of 0.001 Hz (1 mHz) void SetF1(const unsigned long f) { phase_inc1 = f - phase_limit; } void SetF2(const unsigned long f) { phase_inc2 = f - phase_limit; } void SetFreq(const unsigned long *f) { SetF1(f[0]); SetF2(f[1]); } static unsigned delay; void Delay(unsigned n) { delay = n; while(delay); } void main(void) { unsigned i; WDTCTL = WDTPW | WDTHOLD; // Disable watchdog BCSCTL1 = XT2OFF | 15; // Set DCO to aprox 16 MHz DCOCTL = 0x80; // P1DIR = 0xF3; // I/O assignment P1REN = 0x00; // P1OUT = 0x02; // P1SEL = 0x50; // Enable Timer A output, SMCLK output TACTL = TASSEL_1 | MC_1 | TAIE; // Timer A config: ACLK, count up, ovrflow int enabled TAR = 0xFF00; // Force interrupt soon TACCR0 = 320; // Setup Timer A period for under 32768 kHz TACCR1 = TACCR0 / 2; // Setup Timer A compare to midpoint TACCTL1 = OUTMOD_7; // Setup Timer A reset/set output mode _EINT(); // Enable interrupts //SetF1(440000); SetF2(880000); for(;; //SetFreq(Ring); Delay(60000); SetFreq(Off); Delay(30000); for(; for(i = 0; i < 13; ++i) { SetFreq(tt[i]); Delay(32000); SetFreq(Off); Delay(16000); } } // Sine wave -32767 to +32767 static const int sine[501] = { 0, 412, 823, 1235, 1646, 2057, 2468, 2879, 3289, 3698, 4107, 4515, 4922, 5329, 5735, 6140, 6544, 6947, 7349, 7749, 8149, 8547, 8944, 9339, 9733, 10126, 10516, 10905, 11293, 11679, 12062, 12444, 12824, 13202, 13578, 13952, 14323, 14692, 15059, 15424, 15786, 16145, 16502, 16857, 17208, 17557, 17904, 18247, 18588, 18925, 19260, 19592, 19920, 20245, 20568, 20886, 21202, 21514, 21823, 22129, 22431, 22729, 23024, 23315, 23602, 23886, 24166, 24442, 24715, 24983, 25247, 25508, 25764, 26017, 26265, 26509, 26749, 26985, 27216, 27443, 27666, 27885, 28099, 28308, 28513, 28714, 28910, 29102, 29289, 29471, 29648, 29821, 29990, 30153, 30312, 30466, 30615, 30759, 30899, 31034, 31163, 31288, 31408, 31523, 31633, 31738, 31837, 31932, 32022, 32107, 32187, 32261, 32331, 32395, 32454, 32509, 32558, 32602, 32640, 32674, 32702, 32726, 32744, 32757, 32764, 32767, 32764, 32757, 32744, 32726, 32702, 32674, 32640, 32602, 32558, 32509, 32454, 32395, 32331, 32261, 32187, 32107, 32022, 31932, 31837, 31738, 31633, 31523, 31408, 31288, 31163, 31034, 30899, 30759, 30615, 30466, 30312, 30153, 29990, 29821, 29648, 29471, 29289, 29102, 28910, 28714, 28513, 28308, 28099, 27885, 27666, 27443, 27216, 26985, 26749, 26509, 26265, 26017, 25764, 25508, 25247, 24983, 24715, 24442, 24166, 23886, 23602, 23315, 23024, 22729, 22431, 22129, 21823, 21514, 21202, 20886, 20568, 20245, 19920, 19592, 19260, 18925, 18588, 18247, 17904, 17557, 17208, 16857, 16502, 16145, 15786, 15424, 15059, 14692, 14323, 13952, 13578, 13202, 12824, 12444, 12062, 11679, 11293, 10905, 10516, 10126, 9733, 9339, 8944, 8547, 8149, 7749, 7349, 6947, 6544, 6140, 5735, 5329, 4922, 4515, 4107, 3698, 3289, 2879, 2468, 2057, 1646, 1235, 823, 412, 0, -412, -823, -1235, -1646, -2057, -2468, -2879, -3289, -3698, -4107, -4515, -4922, -5329, -5735, -6140, -6544, -6947, -7349, -7749, -8149, -8547, -8944, -9339, -9733, -10126, -10516, -10905, -11293, -11679, -12062, -12444, -12824, -13202, -13578, -13952, -14323, -14692, -15059, -15424, -15786, -16145, -16502, -16857, -17208, -17557, -17904, -18247, -18588, -18925, -19260, -19592, -19920, -20245, -20568, -20886, -21202, -21514, -21823, -22129, -22431, -22729, -23024, -23315, -23602, -23886, -24166, -24442, -24715, -24983, -25247, -25508, -25764, -26017, -26265, -26509, -26749, -26985, -27216, -27443, -27666, -27885, -28099, -28308, -28513, -28714, -28910, -29102, -29289, -29471, -29648, -29821, -29990, -30153, -30312, -30466, -30615, -30759, -30899, -31034, -31163, -31288, -31408, -31523, -31633, -31738, -31837, -31932, -32022, -32107, -32187, -32261, -32331, -32395, -32454, -32509, -32558, -32602, -32640, -32674, -32702, -32726, -32744, -32757, -32764, -32767, -32764, -32757, -32744, -32726, -32702, -32674, -32640, -32602, -32558, -32509, -32454, -32395, -32331, -32261, -32187, -32107, -32022, -31932, -31837, -31738, -31633, -31523, -31408, -31288, -31163, -31034, -30899, -30759, -30615, -30466, -30312, -30153, -29990, -29821, -29648, -29471, -29289, -29102, -28910, -28714, -28513, -28308, -28099, -27885, -27666, -27443, -27216, -26985, -26749, -26509, -26265, -26017, -25764, -25508, -25247, -24983, -24715, -24442, -24166, -23886, -23602, -23315, -23024, -22729, -22431, -22129, -21823, -21514, -21202, -20886, -20568, -20245, -19920, -19592, -19260, -18925, -18588, -18247, -17904, -17557, -17208, -16857, -16502, -16145, -15786, -15424, -15059, -14692, -14323, -13952, -13578, -13202, -12824, -12444, -12062, -11679, -11293, -10905, -10516, -10126, -9733, -9339, -8944, -8547, -8149, -7749, -7349, -6947, -6544, -6140, -5735, -5329, -4922, -4515, -4107, -3698, -3289, -2879, -2468, -2057, -1646, -1235, -823, -412, 0, }; #pragma vector = TIMERA0_VECTOR // Timer A Period interrupt __interrupt void timer_a0_isr(void) // This interrupt will occur when the PWM period is complete { // //static unsigned n; if(++n == 0) P1OUT ^= 1; // TACCTL0 &= ~CCIE; // Disable period interrupt TACTL = TASSEL_1 | MC_1 | TAIE; // Timer A config: ACLK, count up, overflow interrupt TAR = 0xFFFF; // Interrupt at next clock edge } // #pragma vector = TIMERA1_VECTOR // Timer A Overflow interrupt __interrupt void timer_a1_isr(void) // This interrupt will occur when the 32 kHz xtal clock // (ACLK) causes Timer A to overflow from 65535 to 0 { // static long int pa1 = 0; // Phase accumulator for tone 1 static long int pa2 = 0; // Phase accumulator for tone 2 static unsigned sample = 180; // PWM sample // TACCR1 = sample; // Output previous PWM sample volatile unsigned z = TAIV; // Clear any pending interrupts --delay; // Decrement delay TACTL = TASSEL_2 | MC_1 | TACLR; // Timer A config: SMCLK, count up, clear TACCTL0 &= CCIFG; // Clear any pending interrupt TACCTL0 |= CCIE; // Enable period interrupt // // Get pointer to sine table entry int const *ps1 = sine + (pa1 >> 16); int const *ps2 = sine + (pa2 >> 16); #if 1 // Interpolate sine table entry and next sine table entry sample = 180 + \ ((((long int)interpolate(ps1[0], ps1[1], (unsigned)pa1) + \ interpolate(ps2[0], ps2[1], (unsigned)pa2)) >> 8) >> 1); #else // No interpolation sample = 180 + ((((long int)*ps1 + *ps2) >> 8) >> 1); #endif // pa1 += phase_inc1; // Update phase accumulators if(pa1 < 0) pa1 += phase_limit; // pa2 += phase_inc2; // if(pa2 < 0) pa2 += phase_limit; // } // ;interpolate.asm ; ; Copyright (C) 2011 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 . ; .cdecls C, LIST, "msp430G2231.h" .text .def interpolate ; int interpolate(int a, int b, unsigned f); ; interpolate ; Calculate a + (((b - a) * f) >> 16) using bits 8 to 15 of f ; R12 = a ; R13 = b ; R14 = f ; push R10 ; push R11 ; sub R12, R13 ; R13 = b - a clr R15 ; clr R11 ; Initialize accumulator mov #0x0080, R10 ; .loop 8 ; rra R13 ; Divide R13/R15 by 2 rrc R15 ; rla R14 ; Test msb of f jnc $ + 6 ; Skip add if clear add R15, R10 ; Add R13/R15 to accumulator R10/R11 addc R13, R11 ; .endloop ; add R11, R12 ; Add fraction to a pop R11 ; pop R10 ; ret ; bluehash and RobG 2 Quote Link to post Share on other sites
DavidEQ 4 Posted October 20, 2011 Author Share Posted October 20, 2011 I've been playing with the STM32F4Discovery board and man its fast. I wrote a loop to see how fast it could compute the sin function on the fly so I could do it without a look up table. I used the sinf() in math.h. I found the sine (32 bit float not double) of 10 values between -3.0 and plus 3.0 (sinf() takes inputs from -pi to +pi if you haven't used it) and looped that to see if I could do 44.1khz sampling. It turns out it can do 10 sinf() but some other delays so I can see the signal on my slow scope at over 141 kHz when clocked at 168 mhz and using the FPU. . Combine that with the on board 24 bit audio dac and headphone amp and the project is almost no work. The 430 is great and would have worked but the overkill of this new board is kind of fun! Quote Link to post Share on other sites
zborgerd 62 Posted October 20, 2011 Share Posted October 20, 2011 I've been playing with the STM32F4Discovery board and man its fast. I wrote a loop to see how fast it could compute the sin function on the fly so I could do it without a look up table. I used the sinf() in math.h. I found the sine (32 bit float not double) of 10 values between -3.0 and plus 3.0 (sinf() takes inputs from -pi to +pi if you haven't used it) and looped that to see if I could do 44.1khz sampling. It turns out it can do 10 sinf() but some other delays so I can see the signal on my slow scope at over 141 kHz when clocked at 168 mhz and using the FPU.. Combine that with the on board 24 bit audio dac and headphone amp and the project is almost no work. The 430 is great and would have worked but the overkill of this new board is kind of fun! Yeah. Since I got mine up and running last weekend I'd like to work with it a bit more. Wish I had more time. Weekends are the only chance that I get to work on stuff like this. 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.