LPC2378 Example making DAC generate sounds..

Download pre-release library modules and new examples to use with Astrobe for LPC2000. Forum members can also upload their own source code examples.
Post Reply
4GlCoder
Posts: 27
Joined: Fri Jul 22, 2011 2:47 pm

LPC2378 Example making DAC generate sounds..

Post by 4GlCoder » Wed Sep 21, 2011 2:14 pm

This is a basic example of making sounds out of the Headphone output on the LPC-2378-STK board.

Low level module to address the DAC:

Code: Select all

MODULE DAC2378;

IMPORT SYSTEM, LPC := LPC2378;

CONST 
  BiasBit = 16; (* = 010000H *)
VAR
  UseBias : BOOLEAN;
  
PROCEDURE SetBias*(CONST v : BOOLEAN);
BEGIN
  UseBias := v
END SetBias;

PROCEDURE GetBias*(): BOOLEAN;
BEGIN
  RETURN UseBias
END GetBias;

(****************************************************************************
** Description:    initialize DAC channel
*****************************************************************************)
PROCEDURE Init*;
(* PCLSKSEL0 {23:22} = Peripheral Clock selection for DAC, default 00 = 72Mhz/4=18Mhz *)
(* PINMODE1 P0.26, {21:20}  default after reset =00=> on-chip pullup resistor enabled
                                                 01-> reserved, do not use
                                                 10-> no pull-up/down resistor enabled 
                                                 11-> pull-down resistor enabled *)
(* PINSEL1 P0.26, {21:20} = {1:0} enables DAC = AOUT *)
VAR 
  bits : SET;
BEGIN
  (* Clock:  reset to CCLK/4 *)
  SYSTEM.GET(LPC.PCLKSEL0, bits); 
  SYSTEM.PUT(LPC.PCLKSEL0, bits - {22,23}); 
  (* setup the related pins to DAC output *)
  SYSTEM.GET(LPC.PINSEL1, bits);
  SYSTEM.PUT(LPC.PINSEL1, bits + {21} - {20}); (*/* set p0.26 to DAC output *)
  (* setup pin modes *)
  SYSTEM.GET(LPC.PINMODE1, bits);  
  SYSTEM.PUT(LPC.PINMODE1, bits +{21} - {20}); (* no pull-up/down resistor enabled *)
END Init;

(* Place a value (0..1023) into the DAC register and bring it out *)
PROCEDURE* Convert*( CONST v : INTEGER);  
VAR
  bits : INTEGER;
BEGIN
  bits := LSL(v MOD 1024, 6);
  IF UseBias THEN bits := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, bits) + {BiasBit}) END;
  SYSTEM.PUT(LPC.DACR, bits)
END Convert;

END DAC2378.
To generate some basic sounds and or beeps we have to implement some procedures to calculate values and a limited buffer to put our calculated samples in.

Code: Select all

MODULE DACWave;

IMPORT Timer, DAC := DAC2378,  Math0;

(* Max resolution = 10 bit = value 1023. Min value = 0 = silence.
 a beep of 1000 hz should be given with 2000 samples/sec, so for a max of 4 khz, we
 should use sample delivery of about 8khz = is one sample every 125 usecs.  *)
CONST
  SampleTime = 125;  (* microseconds for each sample *)
VAR 
  (* This array limits our frequency spectrum from 40Hz to 4Khz *) 
  ampl : ARRAY 200 OF INTEGER;  (* unpacked values, not efficient using 32 bits for each 10bit value *)
    
(* to produce a tone of 1khz  we need 8 samples *) 
(* limiting Lower Range to 40Hz, means we need store 0,025 secs of 10-bit values for a waveform 

PROCEDURE Test1k; 
CONST
  ofs = 512;
VAR
  a : ARRAY 8 OF INTEGER;
  i, v : INTEGER;
BEGIN
  a[0] := 0; a[1] := 282;  a[2] := 400; a[3] := 282;
  a[4] := 0; a[5] := -282; a[6] := -400; a[7] := -282;
  FOR i := 0 TO 7 DO 
    v := ofs + a[i]; 
    DAC.Convert(v); 
    Timer.uSecDelay(SampleTime);
  END
END Test1k;
*)

PROCEDURE NoOfSamples(CONST f : INTEGER) : INTEGER;
VAR
  SampleTimeFloat, ff : REAL;
BEGIN
  SampleTimeFloat := FLT(SampleTime) / 1000000.0; ff := FLT(f); 
  RETURN FLOOR((1.0 / ff / SampleTimeFloat) + 0.5)
END NoOfSamples;

PROCEDURE CalcAmplitudes(ns : INTEGER);
CONST
  maxv = 400.0;
VAR
  delta : REAL;
  idx : INTEGER;
BEGIN
  delta := 360.0 / FLT(ns);
  ampl[0] := 0;
  FOR idx := 1 TO ns-1 DO
    ampl[idx] := FLOOR(maxv * Math0.DegSin(delta * FLT(idx)));
  END;
END CalcAmplitudes;

(* For debugging the values 
PROCEDURE DisplayAmplitudes(ns : INTEGER);
VAR 
  idx : INTEGER;
BEGIN
  CalcAmplitudes(ns);  
  FOR idx := 0 TO ns - 1 DO
    Out.Int(ampl[idx],0); Out.Char(' ')
  END;
  Out.Ln;
END DisplayAmplitudes;
*)
(* Calculate the number of iterations we need to replay the ampl-buffer 
   to let it sound for -duration- OF milliseconds *)
   
PROCEDURE CalcIterations(CONST freq, duration: INTEGER) : INTEGER;
VAR 
  st, numiter : INTEGER;
BEGIN
  st := 10000 DIV freq;  (* duration of 1 full wave sample of -freq- Hz. in ms*)
  IF duration < st THEN
     numiter := 1
   ELSE
     numiter := duration DIV st
   END
   RETURN numiter
END CalcIterations;

(* play a tone of -freq- herz, and repeat that during -duration- of milliseconds
   A tone will be played at least one full sample *)

PROCEDURE Tone*(CONST freq, duration : INTEGER);
CONST
  ofs  = 512;  (* Use this as the center-amplitude value *)
VAR
  idx, jdx, ns, iter, f : INTEGER;
BEGIN
  IF freq < 40 THEN f := 40
  ELSIF freq > 4000 THEN f := 4000 
  ELSE f := freq END;
  ns := NoOfSamples(f);
  iter := CalcIterations(f, duration);
  CalcAmplitudes(ns);
  FOR jdx := 1 TO iter DO
    FOR idx := 0 TO ns-1 DO     (* playing a full wave sample *)
      DAC.Convert(ofs + ampl[idx]); 
      Timer.uSecDelay(SampleTime)
    END
  END
END Tone;

(* play a tone of -freq- herz, and repeat that - iterations- times.
   Remember that a 40Hz tone will take 25 ms so to iterate that 40 times takes 1 second.
   A Tone of 1200 hz iterating 40 times will take 33ms *)

PROCEDURE Beep*(CONST freq, iterations : INTEGER);
CONST
  ofs  = 512;  (* Use this as the center-amplitude value *)
VAR
  idx, jdx, ns, f : INTEGER;
BEGIN
  IF freq < 40 THEN f := 40
  ELSIF freq > 4000 THEN f := 4000 
  ELSE f := freq END;
  ns := NoOfSamples(f);
  CalcAmplitudes(ns);
  FOR jdx := 1 TO iterations DO
    FOR idx := 0 TO ns-1 DO     (* playing a full wave sample *)
      DAC.Convert(ofs + ampl[idx]); 
      Timer.uSecDelay(SampleTime)
    END
  END
END Beep;

PROCEDURE Init*;
BEGIN
  DAC.Init;
  Timer.Init(Timer.uSecs);
END Init;

END DACWave.
After that we need to define some general noise making procedures :

Code: Select all

MODULE Sound;
IMPORT Timer, DACWave;
(**********************************************************************************************\
 * Geef een geluidssignaal met toonhoogte van 'frequentie' in Herz en tijdsduur 'duration' 
 * in milliseconden.
 * LET OP: toonhoogte is slechts een grove indicatie. Deze routine is bedoeld als signaalfunctie
 * en is niet bruikbaar voor toepassingen waar de toonhoogte zuiver/exact moet zijn.
 * Routine wordt verlaten na beeindiging van de pieptoon.
  \*********************************************************************************************)
(* LPC2378 has monaural headphone-jack, so connect your speaker & enjoy ;-) .. *)
  
PROCEDURE Alarm*(CONST Variant, Duration: INTEGER);
VAR
   x, y, v, n : INTEGER;
BEGIN
  IF Duration < 1 THEN n :=1 ELSE n := Duration END;
  v := Variant MOD 8;
  CASE v OF 
    0 : DACWave.Beep(2000, 20*n);
  | 1 : (* four beeps *)
      FOR y := 1 TO n DO 
        DACWave.Beep(3000, 30); Timer.uSecDelay(100000);
        DACWave.Beep(3000, 30); Timer.uSecDelay(100000);
        DACWave.Beep(3000, 30); Timer.uSecDelay(100000);
        DACWave.Beep(3000, 30); Timer.uSecDelay(1000000)
      END
  | 2 :  (* whoop up *)
      FOR y := 1 TO Duration DO
        FOR x := 1 TO 50 DO
          DACWave.Beep(250*x DIV 4, 20)
        END
      END
  | 3 : (* whoop down *)
      FOR y := 1 TO n DO 
        FOR x := 50 TO 1 BY -1 DO
          DACWave.Beep(250*x DIV 4, 20)
        END
      END
  | 4 : (* S.O.S. *)
      FOR y := 1 TO n DO
        DACWave.Beep(1200, 50); Timer.uSecDelay(100000);
        DACWave.Beep(1200, 50); Timer.uSecDelay(100000);
        DACWave.Beep(1200, 50); Timer.uSecDelay(200000);
        DACWave.Beep(1200, 300); Timer.uSecDelay(100000);
        DACWave.Beep(1200, 300); Timer.uSecDelay(100000);
        DACWave.Beep(1200, 300); Timer.uSecDelay(200000);
        DACWave.Beep(1200, 50); Timer.uSecDelay(100000);
        DACWave.Beep(1200, 50); Timer.uSecDelay(100000);
        DACWave.Beep(1200, 50);
        IF y > 1 THEN Timer.uSecDelay(500000) END  (* NOT n *)
      END
  | 5 : (* ding-dong *)
      FOR x := 0 TO n DO
        IF x > 0 THEN Timer.uSecDelay(2000000) END;
        DACWave.Beep(1500, 500); DACWave.Beep(1200, 500)
      END
  | 6: (* phone ring *)
      FOR x := 0 TO 15*n DO
        DACWave.Beep(1000, 40); DACWave.Beep(750, 40)
      END
  | 7: (* boot *)
        DACWave.Beep(1500, 100); DACWave.Beep(1000, 100)
  END (* CASE *)
END Alarm;

BEGIN
  DACWave.Init;
END Sound.
And finally a testmodule to put the above code at work:

Code: Select all

MODULE TestDAC;

IMPORT Main, Timer, Sound;

PROCEDURE MakeAllNoises;
VAR
  idx : INTEGER;
BEGIN
  REPEAT
    FOR idx := 0 TO 7 DO
      Sound.Alarm(idx, 5);      (* make the sound *)
      Timer.uSecDelay(1000000); (* just a sec of silence *)
    END
  UNTIL FALSE
END MakeAllNoises;

BEGIN
  MakeAllNoises
END TestDAC.
Note: To get all compiled you also need the Math0.mod from the SunSet/Rise example in this forum.

cfbsoftware
Site Admin
Posts: 545
Joined: Fri Dec 31, 2010 12:30 pm
Contact:

Re: LPC2378 Example making DAC generate sounds..

Post by cfbsoftware » Sat Sep 24, 2011 12:09 am

Well done! I particularly like the whoop-up and whoop-down sounds :)

Post Reply