Jump to content
43oh

32k interupts a second?


Recommended Posts

 

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/

Link to post
Share on other sites

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

Link to post
Share on other sites
  • 2 months later...

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                     ;

Link to post
Share on other sites
  • 2 weeks later...

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!

Link to post
Share on other sites
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.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...