Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

8 bit mario coin sound effect on PIC?

Status
Not open for further replies.
Hi Pamela,

Glad you find it useful. Let us know if you manage to replicate other sound effects in the games. Mario, especially the MS-DOS version, has been one of my favourite games for years - read my blog post 2 years ago on this exact DOS version of the games:

**broken link removed**

I am a full time software developer but played with electronics including micro controllers as a hobby too. And yes, my room is full of electronics stuff :)
 
Hi mdanh2002

I downloaded the code and went to the .pas source file.

It has been a really long time since I was using TurboPascal but I remember it was one of my favorites Borland compilers.

But time has taken its toss and can't remember exactly what the notes mean. Can you tell me where to find the beep() function documentation?

I see there is a string? assinged to a variable that is latter passed as a parameter to the beep() function. I remember that in TurboPascal and CRT library there was no control volume.

The code assigns:

CoinMusic = #1+f5+#1+#0;

#1 and #0 seems like musical notes, or tones, but the F5 keeps me wondering. Later this string is passed to the beep() function with this code:

procedure PlayMusic;
var
c: Char;
begin
if not BeeperSound then Exit;
NoSound;
if (iPos = 0) or (iPos > Length (sMusic)) then
Exit;
c := sMusic[iPos];
if c > #1 then
sMusic[iPos] := Pred (c)
else
begin
Inc (iPos);
c := sMusic[iPos];
if c > #0 then
Beep (aiNote[Ord (c)]);
Inc (iPos);
end;
end;

The line Beep (aiNote[Ord (c)]); seems to deliver the note (tone) to be played. Again I remember there is no volume control or duty cycle control on TurboPascal CRT. but I still wonder what is the f5 note here.
 
It might be F5 is the musical note and the sound is C6 F5 C6 then silence... ? but the string CoinMusic = #1+f5+#1+#0; gets to play aiNote[Ord (c)] ... The Mario Coin sound is 1000 Hz for 0.08 seconds, constant 100% volume then 1333 Hz for 0.80 seconds, linear fade from 100% to 0%
https://www.dropbox.com/s/ueyjeig3o962tva/Mario Coin.mp3

I added some echo and sub tones to make it sound more like a bell or gong.


I was able to program my PIC16F628 to output the tones using the PWM output and the right tone duration using calculated delay loops. The output pulse is then feed to a 1 transistor buffer to drive a speaker.

Now my problem is the linear volume control on my circuit.

My first approximation is a DAC converter using pins RA0 RA1 RA2 RA3 to feed a 4 bit DAC that controls the biasing of a transitor, but, this approach is not linear :( I need to get a CD4067 and a series of resistors to implement a linear volume potentiometer/selector. Unfortunately the vendors near my neighborhood are out of CD4067 stock.
 
Hi Pamela,

Thanks for sharing the link to the dropbox sound file. It sounds great.

If you look at the top of the MUSIC.PAS file, you will see the list of units (Pascal term for 'libraries') that this file is using:

uses
Buffers,
Crt;

By looking inside unit Buffers (file BUFFERS.PAS), you will be able to see the definition of the Beep function:

procedure Beep (Freq: Word);
begin
if BeeperSound then
if Freq = 0 then
Crt.NoSound
else
Crt.Sound (Freq);
end;

It's just a wrapper for the Pascal's standard Sound procedure. 'Beep' will play a sound with the given frequency (via the CRT unit Sound procedure) if Freq > 0 and stop playing any existing sound (via the NoSound procedure) if Freq = 0.

Keep us updated on your progress :)
 
Hello Ian. You are correct. Those are the tones. But still the E7 note has a linear fade from 100% volume to 0% in 0.8 secs.

I have good news. I wrote a simple PIC program for 16F628A and added some hardware. Basically it is a transistor audio output as a follower emitter to drive a loudspeaker and 2R-R DAC converter and resistor divider network to feed it to the transistor audio output.

Here is the program and an ugly picture of the board. Excuse me, i have limited time for it and yes it is ugly. Wait for the next version for better placements.

The audio ouput is taken from the RB3/CCP pin.

Digital lines RA0-RA3 represent the bit values for the volume. RA0 is the LSB and RA3 is the MSB. You can feed that to a Digital-to-Analog converter to get a signal to use as a volume control.

A push button (normal open) is connected at RA5 pin. The pin has a 1K resistor from RA5 to ground, and the push button is connected from RA5 to VDD. This button change the operation of the output from the coin sound to a 440 Hz output, with a slow volume down from MAX to 0 for testing purposes.

Code:
LIST  P=PIC16F628A
  include "P16F628A.inc"

   __config h'3f18'       ;   sets the configuration settings - internal oscillator, watchdog timer OFF
                 ;   Power Up Timer DISABLED, Master Clear DISABLED, Brown Out Detect DISABLED,
                 ;   Low Voltage Programming DISABLED, Data EE Read Protect Disabled,
                 ;   Code Protect OFF. (this will remain the same for all tutorials)

; Start by setting bits.

; We want RA5 to be an input and we will use a debouce funcion to use the input to do something

                 ; We use prescaler of 16 to have a wider audio range at PWM
PeriodTicks equ 62       ; And a PR2 of 62. Running at internal OSC of 4 Mhz it gives a clock of 1 Mhz
                 ; that programs the PWM to output 1008 Hz using PR2 = 62 and PRESCALER = 16
   ;DutyCycle    equ 31*4     ; Duty cicle almost 50% of PR2
CCPREGv     equ b'00001100' ; Value for CCP1 CCP1CON = 2 LSBs of DutyCycle and set PWM operation
CCP1RLv     equ b'00011110' ; 8 MSB of the Duty Cycle
TIMER2v     equ b'00000110' ; Set prescaler to 16, activate TIMER2
  

   cblock h'20'         ;   Within this cblock and endc, we can define our variables. More info on this, later.
   delay_1             ;   These next two lines will set aside 1byte of ram for delay_1 and 1byte for delay_2
   delay_2             ;   what we will do with these variables is load them with a number each so that we can
   delay_3             ;   count down from that number to zero. when we reach zero, we continue with the program!  
  
   debounce1           ;  for using a debouce routine on our button at RA5
   button1             ;   when >0 button 1 has been pressed.
   volume             ;  Value to output on PORTA ... between 0 and 0x0F
   endc            

   org h'0000'           ;   This line just tells the microcontroller what address to start running our program from.
                 ;   It will always be 0000 hex for all the tutorials.

   movlw h'07'           ;   This will turn the comparators OFF.  
   movwf CMCON          ;   (we just want to use the ports as digital ports)

setup               ;   I always have a title labeled 'setup' this is where we set everything up
                 ;   i.e. our variables and flags etc.. so they are ready for the main program

   movlw 0x0F
   movwf volume         ;  set maximum volume 00001111b we only use PORTA0 thru PORTA3

   ;bsf STATUS, RP0        ;   select bank 1 (to enable us to change the Input / Output status of our ports)
  banksel PR2
   movlw d'62'           ;  Write the period to the PR2 register
   movwf PR2

   movlw b'00000000'        ;   set PORTB all outputs (A '0' means output, A '1' means input.
   movwf TRISB           ;   We can set each bit individualy. Each port having 8-bits or 8 pins.
        
                 ;  this sets the CCP1 pin as output.
   movlw b'00100000'        ;   set PORTA all outputs except for bit 5. Bit 5 is an input ONLY pin.
   movwf TRISA          ;   It cannot be set to an output!

   ;bcf STATUS, RP0        ;   select bank 0
   banksel CCPR1L

   movlw CCP1RLv         ;  
   movwf CCPR1L         ;  Set values for the PWM period
   movlw CCPREGv        
   movwf CCP1CON         ;  Set LSB of PWM period and activate PWM operation
   movlw TIMER2v        
   movwf T2CON           ;  Set TIMER2 Control Operation, prescaler and activate TIMER2
                 ;  PWM must start working on RB3/CCP1

                 ;  Write PRESCALER VALUE
                 ;  Write DUTY CYCLE VALUE

begin               ;   This is where our main program starts.          
   movf    volume,0       ;  volume -> W
   movwf   PORTA          ;  W -> PORTA Output volume value

   bcf PORTB, 0         ;   clear PORTB pin 0 to a logic 0 (turns the LED off)
   call chirp1           ;   call the delay so that the LED stays on for a while
   call CheckButton1       ;   See if button was pressed
   bsf PORTB, 0         ;   set PORTB pin 0 to a logic 1 (turns the LED on)
   call chirp2           ;   call the delay so that the LED stays off for a while
   call CheckButton1       ;   See if button was pressed
   bcf PORTB, 0         ;   clear PORTB pin 0 to a logic 0 (turns the LED off)
   call CheckButton1       ;   See if button was pressed
   call Silence
   goto setup           ;   and now go and do it all again (it will run in a continuos loop)
  
Delay

   clrf  debounce1         ;  clear debounce counter

   movlw d'100'         ;   copy the maximum number to our working register (decimal 255)
   movwf delay_1         ;   and now copy it from the w register to delay_1 and delay_2
   movwf delay_2         ;   Now the rest of the routine will focus on counting down to zero.

delay_loop             ;   We come back to this label when we have not yet reached zero.
   decfsz delay_1, f       ;   decrement whatever is in delay_1 by 1 and store the answer back in delay_1
     goto delay_loop       ;   if the answer is not zero, then go back to the delay_loop label. but if the
   movwf delay_1         ;   and now copy it from the w register to delay_1 and delay_2
   decfsz delay_2, f       ;   answer is zero then decrement delay_2 by one and store the answer in delay_2
     goto delay_loop       ;   if the answer is not zero, then go back to delay_loop label. but if the answer
   movwf delay_2         ;   is zero, then we have completed our delay and now we can return to our main program!

   btfsc PORTA,5         ;  let's check for some input in RA5. If button is pressed it will read 0, otherwise it reads 1
   incf  debounce1,1         ;  if RA5 is set then add 1 to deboucel
   btfss PORTA,5
   clrf  debounce1         ;   if RA5 is not set then we drop the count as it could be a switch bounce
   btfsc debounce1,2       ;   if bit 2 is set on debocel then the RA5 has been pressed and found RA5=1 for 5 cycles and is validated.
   bsf  button1,0
   decfsz delay_3, f       ;  
     goto delay_loop
  return

DelayVolumeDown

   clrf  debounce1         ;  clear debounce counter
   movf    volume,0       ;  volume -> W
   movwf   PORTA          ;  W -> PORTA Output volume value

   movlw d'125'         ;   125 loops yield 250 clock cycles yield 0.0625 secs delay
   movwf delay_1         ;   and now copy it from the w register to delay_1 and delay_2
   movwf delay_2         ;   Now the rest of the routine will focus on counting down to zero.

delay_loop_volumedown             ;   We come back to this label when we have not yet reached zero.
   decfsz delay_1, f       ;   decrement whatever is in delay_1 by 1 and store the answer back in delay_1
     goto delay_loop_volumedown     ;   if the answer is not zero, then go back to the delay_loop label. but if the
   movwf delay_1         ;   and now copy it from the w register to delay_1 and delay_2
   decfsz delay_2, f       ;   answer is zero then decrement delay_2 by one and store the answer in delay_2
     goto delay_loop_volumedown     ;   if the answer is not zero, then go back to delay_loop label. but if the answer
   movwf delay_2         ;   is zero, then we have completed our delay and now we can return to our main program!
  
   movf    volume,0       ;  volume -> W
   movwf   PORTA          ;  W -> PORTA Output volume value
  decf   volume,1       ;  This is for having the volume decreased in 1 step
  
   movlw d'125'         ;  
   decfsz delay_3, f       ;  
     goto delay_loop_volumedown
  return

chirp1
   ;bsf STATUS, RP0        ;   select bank 1 (to enable us to change the Input / Output status of our ports)
  banksel PR2
   movlw d'62'           ;  Write the period to the PR2 register, output 1008 Hz
   movwf PR2
   banksel 0

   movlw d'2'
   movwf delay_3
   call Delay
   return

chirp2              
  banksel PR2
   movlw d'47'           ;  Write the period to the PR2 register for 1329 Hz
   movwf PR2
   banksel 0

   movlw 0x1F
   movwf volume         ;  restore maximum volume

   movlw d'16'           ;  16 loops of DelayVolumeDown  aproximate 1 sec (original coin sound is 0.8 secs)
   movwf delay_3  
;buscar forma de ir bajando el volumen de 15 a 0 en 20 pasos
;
;
   call DelayVolumeDown
   return

chirp3
  banksel PR2
   movlw d'142'         ;  Write the period to the PR2 register for 440 Hz
   movwf PR2
   banksel 0

   movlw d'25'           ; 25 delays = 1 sec
   movwf delay_3
   call Delay
   return

CCP1Reset
   banksel CCP1CON         ;  select bank 0
   clrw         
   movwf CCP1CON         ;  reset/stop PWM operation
   return

Silence
   call CCP1Reset
   movlw d'25'
   movwf delay_3
   call Delay         ;   Delay about 1 seconds
   return

CheckButton1
   clrw
   movwf   volume         ; put 0 on volume
   decf  volume,1
   movf    button1,1       ;  Test if button1 is Z 
   btfsc    STATUS,2       ;  button1 is Zero, do nothing, return
   return             ;  
  
   clrf button1         ;   set button1 to Zero
keeptone
   movf    volume,0       ;  volume -> W
   movwf   PORTA          ;  W -> PORTA Output volume value
  decf   volume,1       ;  This is for having the volume decreased in 1 step

   call chirp3

   movf button1,0         ;  Test if button1 is Z 
   btfsc STATUS,Z         ;  Button1 pressed again?
   goto  keeptone         ;  button1 is Zero, keep output of chirp3
   clrf button1
   movlw 0x0F
   movwf volume         ;  restore maximum volume
   return             ;  button1 is non Zero, clear it & return

   end               ;   We always need to have end at the end, even if we don't want the program
                 ;   to actually end, it still must be here![/FONT]
I know there are many improvements available for this program, but hey, i haven't touch the PIC development in about 8 years.
I took a tutorial template by someone else. So here are the credits:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; PIC Microcontroller Tutorial Template ;
; ;
; By Brad Slattery 2009 ;
; www.bradsprojects.com ;
; brad@bradsprojects.com ;
; ;
; 'Designing electronic projects, ;
; to spread the name of Jesus' ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

And I share my program to spread the name of the Beatles and Science!.
Sound File - Recorded.
**broken link removed**
Surprise. Output is not square and it has harmonics.
 
Last edited:
It sounds terrible I know. A lot of distortion thanks my funky 1 transistor design. I made some improvements on the hardware, added some RC filters. I found the DAC biasing method introduces abrupt pulses on the audio output. I filter it with a 1uF Cap. I had. Really the value needs to be more like 0.2 uF

Any way here are some improved results.

Check the audio and some screen shots of the Audio PC Scope with a lot of artifacts.
**broken link removed** audio looks more like the ideal generated audio.
 
After several trips to local Electronics Supply stores and $40 USD less finally I got 2 IC CD4067. I made a linear volume control using this multiplexor IC. And it sounds great. I improved the audio amplifier stage to drive a good punch (5W i guess) to the speaker. Any one interested on the audio amplifier? I also connected it to my iPhone for some time listening to relaxing music (pop rock!).
 
It sounds terrible I know. A lot of distortion thanks my funky 1 transistor design. I made some improvements on the hardware, added some RC filters. I found the DAC biasing method introduces abrupt pulses on the audio output. I filter it with a 1uF Cap. I had. Really the value needs
It sounds fine to me.... It's definitely the coin sound!!!

BTW.. I put your code in code tags so members can read it easily...
 
Thank you Ian! Now it is easy to read the code. Will do the same on my following code post. I'm preparing the schematic if somebody ever wants to do it's own. I want to finish this before moving into other sound projects. [I'm adding 2 more signal generators on the PIC]
 
Here is a quick schematic.

The transistor TIP122 can be any darlington transistor or power transistor. You can try TIP102 or TIP31. This is experimental, it means you need to find proper values for R15, R10 and R11. The idea is to bias the transistor with the speaker in a point where the DAC puts the transistor from cut (0 v out of the DAC) to linear driven (2.5 v out of the DAC when all bits are set). When the transistor is at cut no audio will be heard on the speaker. When the DAC feeds its maximum voltage (2.5 v) to it, the transistor should be properly biased to deliver good current to the speaker, might the current across the collector of the transistor be 500 mA or even 700 mA ... that equals the voltage across R9 be around 2.3 volts peak.

Assembly the board and start with R15 at his maximum value. Set R10 around 2 K and choose R11 around 47K. Use the push button to output the 440 Hz tone with a sliding count from 15 to 0. This makes the DAC output a sliding down voltage from 2.5 V to 0. First adjust R10 to have a silence at the end of the count. Later adjust R15 to have more volume at the begining of the count. You will hear the volume sliding. Each volume step last about 1 second, so it takes about 15 seconds to go from full volume to silence.

If you press the push button again it will change back to the coin sound.

If you don't get the desired sound increase the values of the PRESETS R10 and R15 to 50K and try again.

Disclaimer: This is experimental. Use it at your own risk.

On my next version I use a better audio amplifier output and the CD4067 multiplexer that delivers a way better sound combination. Stay tuned. (or not? :( :( :( )
 

Attachments

  • Mario Coin Beta 1.png
    Mario Coin Beta 1.png
    38.6 KB · Views: 457
I'm getting closer to assembly of the final version. This is another beta.
I use the same program but now it controls the volume with the CD4067 multiplexer and the output is feed to a small transistor amplifier/small speaker.
The amplifier I took one design with BC337/BC327 push pull and changed it a little to use 5 Volt only. It sounds good! and have enough power for a good speaker to use as permanent basis, like those satellite surround speakers.

Here is the link to the amplifier schematic : Push Push Small Amplifier

For operating at 5V change the following values:
R2=47K
R3=16K

And here is an schematic using the **broken link removed** with 16 steps. I use all resistors of inputs as 1.8K for having a linear volume control, not a logaritmic one like in this diagram. Use only the IC4 part of the schematic.

Also I added a volume control 10K preset between the CCP output pin of the PIC and the input of the CD4067 to have a better control of the audio. The actual signal is very strong and overloads the amplifier. You may add a 4.7K resistor between the CD4067 out and the amplifier input.
 
Last edited:
Well, I would use one digital pot if my local vendors carry such parts. I was lucky to get the CD4067. They don't stock the MCP415X or other of MicroChip Series POT. Do you have some other part numbers? I can look for at my local vendors but sure they don't carry the MCP41XX digital potentiometers, or the MAXIM digital potentiometers.
 
I'm not complaining... I think the design shows newbies how it is done...

I have little or no encumbrance when purchasing parts here in the UK... I sometimes forget that there are places where parts are scarce...
 
No problem Ian. I'm learning a lot about PIC family of MCUs. It just happens I'm too impatient to wait 1 and half week to get my order from big online vendors like Mouser or Future Electronics, besides my budget is tight. Surprise! I use components scrapped from other boards when available.

I thank you all for making this website possible. It is a great place for newbies like me.

I moved the audio amplifier and the PIC base to a ready made perforated board. Seems I forgot to make room for the DAC.
SAM_6512.JPG SAM_6510.JPG
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top