Main Site Documentation

Reading and writing from ADC for audio output


#1

Hello All,
I recently acquired a FEZ Cerberus board and am attempting to playback records with lasers using it. Therefore I could use some help on how to code sending an analog voltage signal to a GHI breakout board to power the laser diode. Likewise I then need to code for reading another pin’s analog voltage on either the same or another breakout board using the ADC. Finally what I want to do is take input the readings from the ADC and output a matching audio analog signal which can then be used to either directly or through an amplifier power a set of computer speakers. If anyone has any Ideas for how to either code these functions or also what circuit is necessary for connecting the speakers PLEASE let me know!! Thanks!


#2

You didn’t specify where you’re “sending” an analog voltage from, but if you’re trying to play back an audio recording from your computer, why not just use a cheap USB sound card’s output?

Your second question is equally puzzling: if you want to sample an audio signal with an ADC and then output a matched version of that signal, why not just use a… wire?

Maybe you can clarify the project a bit more?

(To answer your question more naïvely, any audio sampling you want to do on your FEZ Cerberus will require native (C++) code; .NET Micro Framework is too slow to do any of that stuff)


#3

Ok sorry yeah my project is using interferometry to play back the records so a laser bounces off the record and measures the varying groove depth distances which are in micrometers.
Then the laser strikes a photo-detector with the varying intensity light. however the changes in intensity will be extremely slight. well actually the voltage being input into the analog pin is from a photo-detector and is sensing the intensity of a light source with very minimal differences. the thing is these differences in intensity will be extremely small so I would like to make a more suitably varying audio signal with the board and have that output to an external amplifier or speaker.
That output is the second part of my question in which the voltage being read in in just a varying sensor input and not really an audio one.
Also the outgoing analog or even possibly digital signal would be an output to either directly or through a transistor activate an op-amp circuit which will supply power to the laser driver I intend to use. This will allow the microcontroller to turn the laser on and off when the push-button on the push-button module for the FEZ cerberus is pushed.
Thanks for your help!!!


#4

Also I will be using a breakout module from GHI so if you could help with how to code for interfacing specific pins that would be helpful


#5

If the analog signal is really as tiny as you describe it, you will in all likelihood need to amplify it significantly before it even hits the ADC pins. With ADC you have quantization effects, and if the signal changes are below the sampling resolution, then you may not even see any digital changes.

Sorry, this lost me. Why would the analog signal control the laser being used to read that analog signal? It’s like a snake eating its own tail. If you just want a digital output to control the on/off state of the laser, you won’t even need a an op-amp. Just a transistor or relay.


#6

@ ryans92

Sampling of an input analog signal, from an ADC, and passing it to an DAC for output, will have to be done at a minimum rate of 8KHZ/second. This would involve 16,000 I/O operations/sec, not considering any processing of the input signal. To achieve this rate, RLP will have to be used. Managed code will not provide the sampling rate required.

“sending an analog voltage signal to a GHI breakout board to power the laser diode”

What voltage and current is required by the laser diode? Are the Cerburus pins capable of driving the laser?


#7

well yes that is what I intend with the laser is to simply have a transistor connected to the breakout module so that by sending a digital signal to the transistor it will turn it on and when no signal is output the laser driver will be turned off. The transistor would act as an ordinary power switch but the micro-controller is supposed to control it.
I need help with the code because I have tried a few things and they have all had issues what I need to be able to do is set a pin on the breakout module board as a digital output and output a signal with it. However I am having issues with how to address the breakout board and its pins.


#8

Also do you have any suggestions for how to handle the amplification of the input signal from the photo-detector. the circuit will use a power supply and resistor with the feedback current turned into a voltage by the resistor. so the detector might actually be able to give back mA to probably 1 amp of current and maybe 3 volts but I am not sure. What is the minimum requirements for the ADC to be able to read the detector values.


#9

I’m still not entirely clear where a FEZ Cerberus (or any other microcontroller / digital device) comes into play?

It sounds like you have a laser pointed at a record that you turn on when you want to start playback. You have a photodetector that puts out a very low-voltage audio signal, and you simply want to amplify it to line-level.

Is that what you’re trying to do?

If so, I’d suggest using a high-bandwidth op-amp with a high slew rate – it will be able to amplify the signal by a factor of several hundred, without having to worry about oscillating (going unstable). I’d suggest wiring it in a non-inverting configuration (to provide higher input impedance). If you have impedance problems, you can also use a JFET op-amp as an input stage.

As for powering on the laser, I’m not sure what you need transistors for – just use a toggle switch? Connect it in between the laser and the laser’s power switch.

I can help you with the circuit when I know more about the voltages involved – what’s the peak-to-peak voltage of the audio signal that comes out of the photodiode?

And what exactly were you planning on using the FEZ Cerberus for?


#10

Ok sorry I also forgot that the main point of the micro controller would be to filter out noise in the signal from the detector so it outputs only a certain frequency range probably above 50 Hz and below 30 kHz also from my micro controller I plan to directly connect a 3 watt velleman mono amplifier kit . With the kit it says use a potentiometer connected to the input but I was thinking that with the micro controller I could decrease or increase the signal sent to the amp by using two push button modules as up and down for volume. Like I said I would need help coding this.


#11

But yes you have the main concept correct


#12

How are you going to filter using the micro controller? Are you going to do an FFT of the input, remove elements above and below, and the reconstitute the output level?

Usually this the of processing is done with a DSP chip. I am not sure that the processor on the board would be able to handle this load.

As I said before, this will require RLP. RLP programming is done in C/C++.

I have been working/playing with GHI devices for over two years, and I have never heard of anyone doing anything similar.

Are you prepared for the challenge?


#13

Unless you’re familiar with fixed-point DSP, biquads, and elliptic filters, you probably don’t want to tackle that bandpass filtering on a microprocessor. Although the STM32F4 processor on your FEZ Cerberus is capable of doing that, if you’ve never done something like this before, you’ll spend weeks getting code working. You’ll need MATLAB or some other program to design and test the filter’s performance, and even once you have it working in MATLAB, that’s still a huge leap to getting it working on an MCU (even one with an FPU like the STM32F4)

To add insult to injury, you could accomplish the same filtering using two sets of resistor/capacitor circuits and an op-amp.

If you really want to do it using DSP, I can help you if you have specific questions, but I can’t teach you a college-level DSP class from start to finish.

I honestly think there are way more fun problems to work on with your project than the filtering stuff. How are you going to track the vinyl properly? And remove cracks and pops? (Traditional styluses push most of the dust out of the way while reading the disc)


#14

Ok well Thanks guys I would like to learn more about the circuits to do the filtering using two sets of resistor/capacitor circuits and an op-amp. can you send me example diagrams. also could you help me with at least getting the board to turn the laser on and off as well as reading the detector and outputting the signal cause since this is for a research project and the ME department already bought the Cerberus I really cant tell them I don’t actually need it.


#15

Also this is actually for Edison cylinder which have a feedscrew to move it across and brushes to remove dust.


#16

@ ryans92 - check the support section of the forums. lots of tutorials.


#17

The first step is to amplify your signal (which also effectively buffers it). You’ll need to use an oscilloscope to see what the photodetector’s signal looks like when you play a record. Look at the peak-to-peak voltage to see what the signal looks like. Once you come up with the voltage, you’ll need to figure out how much you want to amplify it. Line-level audio is 1V RMS max (typically), so if your maximum output from the photodiode is 10 mV, you’ll want to amplify it by a factor of 100.

Op-amp amplifiers are trivial to design. For your application, I’d use a non-inverting design:

So, start by running your photodetector output into your op-amp circuit. Observe the output, and make sure there’s no clipping/distortion. Next, it sounds like you want to create a bandpass filter. This page explains active RC-based bandpass filters:
http://www.electronics-tutorials.ws/filter/filter_7.html

[quote=“ryans92”]
also could you help me with at least getting the board to turn the laser on and off [/quote]
If you want a user-activated switch to turn the laser on and off, just use a toggle switch directly wired to the laser’s power. No Cerberus needed. If you want to turn the laser on/off based on some sort of event you want the Cerberus to detect, you’ll need to explain exactly what the Cerberus is supposed to detect before it turns the laser on.

[quote=“ryans92”]
as well as reading the detector and outputting the signal cause since this is for a research project and the ME department already bought the Cerberus I really cant tell them I don’t actually need it.[/quote]
You should explain to them that you don’t actually need the Cerberus for anything. Otherwise, you’re really going to look like a dumbass when you demonstrate your project that uses a 168 MHz processor to simply read a value from the ADC and output the exact same value to a different pin.

But if you really want to proceed with the charade:
CD-quality audio is sampled 44,100 times per second. Voice-quality audio is usually sampled at least 16,000 times per second. As I’ve said, you simply won’t be able to read the pin, process it, and output it fast enough in .NET Micro Framework.

So you’ll need to erase the firmware from your Cerberus and just write a program in C++ to do it.

To do native programming, you’ll need an ARM C++ compiler. I recommend MDK. It sounds like it’s for school, so you may already have access to a copy of it. Otherwise, it’s about $500 with educational pricing. The free, codesize-limited version is probably sufficient for your project, so that’s another option.

Another free option is ARM GCC:
https://launchpad.net/gcc-arm-embedded

CooCox is a free IDE that’s getting fairly popular, so you may want to check it out, too.

You’ll need an ARM SWD debugger as well. The Segger J Link is extremely popular, but a cheaper option would be to snag an STM32F4Discovery and just hijack the JTAG interface on it. You’ll have to wire the pins properly to the Cerberus (consult the schematics to find out where to locate the SWD and SWCLK pins, along with the reset line).

Setting up the development environment probably takes longer than actually developing the code. Start a new project, copy the appropriate startup assembly file into your project for the STM32F4, and create a your main function.

Setting up the ADC can be clunky, but once you initialize it, it’s pretty easy to perform the sampling. Here’s a blog post I found when googling “STM32F4 ADC”:

Then, if you just want to write that value to the output, you can use the DAC. Here’s some code from an RTOS that should get you started:
https://code.google.com/p/stm32f4-discovery-freertos/source/browse/Drivers/ST/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_dac.c?r=f292ed54da1e328fd129e87bf8dbb73a5b150783

Again, I have to reiterate how stupid this is. If you’ve never done this before, you’ll spend a week setting up your environment, and probably another two or three weeks getting your code to compile and function properly.

All just so you can take a signal on one pin and output the exact same signal on another pin.

Essentially, you’d be turning your FEZ Cerberus into a wire.


#18

all right thank you.
i will actually be using a monoamplifier kit from radioshack to amplify the audio signal from the detector and one of the filters from that website link you gave http://www.electronics-tutorials.ws/filter/filter_7.html
was there a specific one that you thought might be helpful.

also i have changed my design now. i will be directly using the signal from the detector and sending it to the amplifier. therefore it negates needing the cerberus to read and send the same signal.

however i would like to still use the cerberus as an overall control for the project because if i decide to i can then later add one of the ghi IR remote modules and program in remote control capabilities. also one of the goals of this project was to be a bit of an intro into control systems, cs programing with open source hardware, and especially mechatronics and trying to use a microcontroller as a control system for external electronics and components.

By use the cerberus as a control for the system i mean that i would like to use a push button module as the toggle switch for turning on my amplifier, detector, and laser driver circuits either simultaneously or one after another which ever proves better. therefore i simply need help with getting the boards code correct for creating digital outputs on three of the pins on a breakout module similar to the extender module. I would then be using these digital outputs to activate transistors or relays to turn on the individual circuits but keep them isolated from the cerberus. I have created the code already but although there are no errors according to Microsoft visual the code still throws this error when it is run.
this happens only once the ssbutton has been pushed

“A first chance exception of type ‘Gadgeteer.Socket.InvalidSocketException’ occurred in Gadgeteer.dll
Error invoking method “Gadgeteer.Interfaces.InterruptInput” (check arguments to Program.BeginInvoke are correct)”

Again I am aware of the fact i could simply use a plain toggle switch or three to do this job. However I would like to do it this way because it should not be very complicated but particularly because it will introduce me to how i can use a microcontroller to accomplish these tasks. Also i could then expand my project later so that it is remote controlled, or perhaps uses timers in the code or additional sensors on the phonograph to provide automatic start and stop capabilities.

Furthermore I have decided to use the cerberus as a way to control the volume I plan to use two pushbutton modules as momentary switches, one for volume increase and the other to decrease it.
for this the cerberus would either be using however many additional pins on the breakout board are necessary, probably two or three. However I have two full breakout boards so the number of pins isn’t really a big concern.
the cerberus would be expected to either run a servo motor backwards or forwards to turn the volume up or down respectively in order to turn a potentiometer and change the level. however I have looked at the example code for analog input and output and was thinking the cerberus could also be directly used to scale the voltage. perhaps with the setlinearscale code?however this would probably pose the same speed issue you already mentioned therefore the motor would be fine too.

Like I said this may seem to be overcomplicating something simple because i could just use basic switches and a potentiometer. However the point is to introduce myself to mechatronic control systems and embedded coding. therefore i need help with creating some basic digital outputs and driving the motor/changing volume based on pushbutton inputs. Then if I later want to I can expand on this by making it remote controlled or based on other sensors such as motion. Thank you again also here is the code i have


using Microsoft.SPOT;
using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;
using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using Gadgeteer.Interfaces;

namespace PhonographReader
{
    public partial class Program : Gadgeteer.Program
    {  

        void ProgramStarted()
        {
            //create boolean for if start stop pushbutton is pressed
            bool SSPressed = SSbutton.IsPressed;
            // This method is run when the mainboard is powered up or reset.   
            //******************************************************************************************

            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");
            SSbutton.TurnLEDOff();//turn off led on SSButton
            VolumeUp.TurnLEDOff();//turn off led on Volume Up Button
            VolumeDown.TurnLEDOff();//turn off led on Volume down Button


            //***************************************  OUTPUT WELCOME MESSAGES   **********************************************
            Debug.Print("/t/t Welcome To The /nEdison Phonograph Laser Reproducer/n/t/tCreated By: Ryan Sandor");
            Debug.Print("Please Press Start/Stop Button To Begin Sampling");

            //*****************************************   Main Class  *********************************************************
            // if (SSCounter == 0)//when SSCounter is 0 execute
            //turn off led on SSButton
            SSbutton.TurnLEDOff();
            //turn off led on Volume Up Button
            VolumeUp.TurnLEDOff();
            //turn off led on Volume down Button
            VolumeDown.TurnLEDOff();
            //activate cerberus mainboard led
            Mainboard.SetDebugLED(true);
            //*****************************************   Button intitializers Class  ******************************************
            //create event handler for when start stop button is pressed
            SSbutton.ButtonPressed += new Button.ButtonEventHandler(SSbutton_ButtonPressed);
            //create event handler for when volume down button is pressed
            VolumeDown.ButtonPressed += new Button.ButtonEventHandler(VolumeDown_ButtonPressed);
            //create event handler for when volume down button is released
            VolumeDown.ButtonReleased += new Button.ButtonEventHandler(VolumeDown_ButtonReleased);
            //create event handler for when volume up button is pressed
            VolumeUp.ButtonPressed += new Button.ButtonEventHandler(VolumeUp_ButtonPressed);
            //create event handler for when volume up button is released
            VolumeUp.ButtonReleased += new Button.ButtonEventHandler(VolumeUp_ButtonReleased);
        }

        // This method is run when the mainboard is powered up or reset.   

        //*************************************************   INDIVIDUAL CLASSES   ********************************************************
        //call this function when start stop button is pressed
        void SSbutton_ButtonPressed(Button sender, Button.ButtonState state)
        {    
            //create breakout socket variables
            GT.Socket TrigSocket;
            GT.Socket LDSocket;

            //when start stop button is pressed toggle led on or off
            SSbutton.ToggleLED();

            //declare sockets for Breakout boards on cerberus
            //socket for trigger circuit breakout
            TrigSocket = GT.Socket.GetSocket(5, false, TriggerBO, "y");
            //socket for laser and detector circuits breakout
            LDSocket = GT.Socket.GetSocket(6, false, LaserBO, "y");

            //declare digital outputs for trigger circuits
            //declare  digital output for Laser 
            DigitalOutput LaserTrig = new DigitalOutput(TrigSocket, GT.Socket.Pin.Three, false, TriggerBO);
            //declare  digital output for Audio Amplifier    
            DigitalOutput AmpTrig = new DigitalOutput(TrigSocket, GT.Socket.Pin.Four, false, TriggerBO);
            //declare  digital output for PhotoDetector    
            DigitalOutput DetectTrig = new DigitalOutput(TrigSocket, GT.Socket.Pin.Five, false, TriggerBO);

            if (SSbutton.IsLedOn == true)//if ssbutton led is on
            {//output start message
                Debug.Print("Now Sampling From Phonograph");
                //activate digital outputs
               LaserTrig.Write(true); // Turn ON Laser 
                AmpTrig.Write(true); // Turn ON Audio Amplifier
               DetectTrig.Write(true); // Turn ON detector
            }

            if (SSbutton.IsLedOn == false)//if ssbutton led is off
            {
                //output end message
                Debug.Print("Sampling From Phonograph Terminated");
                //deactivate digital outputs
                AmpTrig.Write(false); // Turn OFF Audio Amplifier
                LaserTrig.Write(false); // Turn OFF Laser 
                DetectTrig.Write(false); // Turn OFF detector
            }
        }
        //call this function when Volume Up button is pressed
        void VolumeUp_ButtonPressed(Button sender, Button.ButtonState state)
        {
            Debug.Print("Increase Output Volume");//output message
            VolumeUp.TurnLEDOn();//when button is pressed toggle led on or off
        }
        //call this function once the Volume Up button is released
        void VolumeUp_ButtonReleased(Button sender, Button.ButtonState state)
        {
            Debug.Print("Stop Increasing Output Volume");//output message
            VolumeUp.TurnLEDOff();//when button is pressed toggle led on or off
        }
        //call this function once the Volume Down button is pressed
        void VolumeDown_ButtonPressed(Button sender, Button.ButtonState state)
        {
            Debug.Print("Decrease Output Volume");//output message
            VolumeDown.TurnLEDOn();//when button is pressed toggle led on or off
        }
        //call this function once the Volume Down button is released
        void VolumeDown_ButtonReleased(Button sender, Button.ButtonState state)
        {
            Debug.Print("Stop Decreasing Output Volume");//output message
            VolumeDown.TurnLEDOff();//when button is pressed toggle led on or off
        }
    }
}


and here is the current generated code

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the Gadgeteer Designer.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using Gadgeteer;
using GTM = Gadgeteer.Modules;

namespace PhonographReader
{
    public partial class Program : Gadgeteer.Program
    {
        // GTM.Module definitions
        Gadgeteer.Modules.GHIElectronics.Breakout LaserBO;
        Gadgeteer.Modules.GHIElectronics.Button SSbutton;
        Gadgeteer.Modules.GHIElectronics.UsbClientSP usbClientSP;
        Gadgeteer.Modules.GHIElectronics.Button VolumeUp;
        Gadgeteer.Modules.GHIElectronics.Button VolumeDown;
        Gadgeteer.Modules.GHIElectronics.Breakout TriggerBO;

        public static void Main()
        {
            //Important to initialize the Mainboard first
            Mainboard = new GHIElectronics.Gadgeteer.FEZCerberus();			

            Program program = new Program();
            program.InitializeModules();
            program.ProgramStarted();
            program.Run(); // Starts Dispatcher
        }

        private void InitializeModules()
        {   
            // Initialize GTM.Modules and event handlers here.		
            VolumeDown = new GTM.GHIElectronics.Button(2);
		
            SSbutton = new GTM.GHIElectronics.Button(3);
		
            VolumeUp = new GTM.GHIElectronics.Button(4);
		
            LaserBO = new GTM.GHIElectronics.Breakout(5);
		
            TriggerBO = new GTM.GHIElectronics.Breakout(6);
		
            usbClientSP = new GTM.GHIElectronics.UsbClientSP(8);

        }
    }
}

and finally the display in microsoft studio where I used the graphics to connect the modules (sorry not sure what the right term is.) is attached

THANK YOU again.


#19

First things first, can you click the pencil icon on your post above, select each block of code, and then hit the 101010 (the grey one above the text box, not the orangey one below) so that they’re properly coded as program code?


#20

Well, it looks like you’re trying to create an interrupt on a pin that doesn’t support that functionality. That’s odd, because from this page it looks like basically every socket (2, 3, 4, 5, 6, 7) have an interrupt-capable pin on pin 3.

Having said that, this page seems to indicate there might be a problem sharing interrupt channels – however, you have things plugged into sockets that don’t seem to share channels, so I’m not sure if that applies to you.

Since you’re still learning the ropes, let me give you a tip: always work iteratively. Add the modules to your project, and without typing any code, compile and run it. Then start adding code. Frequently compile and run your project. If you don’t do it, it’s going to be very hard to figure out what’s wrong.

And that also helps forum members help you. There’s a lot of smart people on this forum, but no one here is a human compiler, so it’s very hard for us to parse your code for you to see what’s wrong. If you just had three or four lines of code that threw the same exception, we’d be able to get it fixed a lot sooner.

Try taking out all of your code inside ProgramStarted() and see if you can run the project. If you can, then try adding interrupts to the buttons as you’ve done. Re-run the project. Keep trying things until it breaks. But keep things simple!