Ad Widget

Collapse

Announcement

Collapse
No announcement yet.

Is anyone here an expert in PIC?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #31
    I can wire up an OLED display to print values to, but it does steal some coding space. With PIC I used to set up the schematic in Proteus, run the code in the simulator and use simulated inputs. Really easy to monitor any aspect of the code, voltages and signals. Not so sure with the Arduino boards because they are more than just a processor.

    I'll get the stats off tomorrow as I'm on a different computer right now.

    Comment


    • #32
      Everything got derailed due to a family emergency yesterday so it looks like I won't get much chance to do anything on this project for a few days. I did find though that the DAC isn't responding so I need to read what's being sent. It's addressed correctly and works with a test script, though.

      Comment


      • #33
        Originally posted by Mick Bailey View Post
        I did find though that the DAC isn't responding so I need to read what's being sent. It's addressed correctly and works with a test script, though.
        I wouldn't be surprised if dac_value is zero because of the way the calculation is being done. Step through the simulator and put a watch on your variables. Simulators don't always work, especially because when we use them they are usually burdened with assumptions we have made. If the simulator doesn't get you there I would hook up the OLED and print out the variables so you can see what it is REALLY happening.

        This ugly rewrite might get you there.

        mv_conversion = log2(frequency / 55) ; // frequency to voltage in volts conversion
        dac_value = mv_conversion * 819; // get DAC value as an integer

        Comment


        • #34
          I did the calculation change and still no output. I like the coding suggestion - much neater and I understand the reasoning. I added a few print statements to see what's going on and used the Arduino serial monitor, which was either giving nothing or high values. Just to be sure I connected the OLED and saw mostly values are out of range or zero. I added max and min statements to confine the values to 0-4095 and experimented with the input circuit level. The Arduino seems to be critical on voltage level and I think there are harmonics off the square wave that are causing out of range values. When I added some filtering things improved, but the frequency detection isn't accurate enough. When listening to the square wave it sounds in tune, but the Arduino picks something else up and it jumps out of range of the DAC.

          I'm thinking of trying a simple analog input with zero-crossing detection and heavy filtering up-front, but keep to the same output calculation as this works fine.

          The original PIC circuit works superbly for pitch detection apart from the latency, though ported to Arduino would be much easier to tweak to get the timing down. I don't understand how it works and even though the magazine article explains it I still can't figure it out. A symptom of post-Covid brain fog, which is really getting me down right now.

          Getting there, but slowly. at least the DAC side is sorted out.

          Comment


          • #35
            You have a few Nanos so try a different one. Maybe there is something wrong with the one you are using.

            Try using the old PIC board as input to the Nano. Just snatch the signal off the board where it is about to go into the PIC. Then you can see whether the signal is an issue.

            I can see how zero values would be present, but out of range is worrisome. Specifically which values are out of range?

            If you comment out the calls to pulseIn and just hardcode reasonable values does the program work as expected?

            I believe that calls to pulseIn are "blocking", which means that it just sits and waits until it detects a signal unless you call it with a timeout parameter. So I don't think it should be giving you any numbers that would be either zero or out of range. It might be a good idea to put a timeout on the pulseIn call and make it blink a status led if it does time out. (You could use a status led to blink error codes for a number of different situations.)

            Comment


            • #36
              I've tested the Arduino and it works fine.

              Testing it with a signal generator and I get the correct DAC values and output voltage and it works pretty much as expected, though when I vary the frequency I sometimes get a random output before the DAC settles to the correct value. The out of range values are always 5000-5999.

              When trying it with a guitar it can be reasonably stable when playing across the neck between the 5th and 9th fret. Quite promising really, because it accurately tracks bends, vibrato and slides but I notice that if the note dies out it doesn't get a fix on a new note as easily as picking a new note before the previous one has decayed. Playing higher or lower than the middle of the neck doesn't work well at all - note acquisition is very slow on lower notes, and high notes don't sound at all. I need to dig out my scope and see what's going on with the input - maybe there needs to be some signal conditioning or perhaps the circuit is picking up noise due to being on a breadboard.

              In the meantime I'll try including a timeout on the pulseIn.





              Comment


              • #37
                Oh, I thought by out of range you meant bigger than the variable can hold. It would be interesting to see how the high and low pulses contribute to the total. You could calculate the delta between the two and see how that tracks. I would have thought you could assume that they would be more or less equal and thus be able to cheat by just finding just one of them and doubling the value.

                So in this case does out of range mean that the frequency being detected is higher than expected? Perhaps it is not an error in the program.

                Comment


                • #38
                  I'm also thinking the error isn't in the program and lies external to the circuit. My thought is the circuit may be so sensitive that it's picking up pot noise when I adjust my signal generator because the DAC values once it settles are perfect. My fundamental extractor could have inaudible artefacts that are doing the same thing.

                  I've studied the original PIC description some more and conclude that this uses a software implementation of the Boss OC2 fundamental extractor. That circuit splits the signal into two; one half creates a positive pulse and the other a negative one for each half of the waveform. Comparators are used, so the pulses are derived from levels, not frequencies (though their duration is that of each half cycle). The pulses are fed into a flipflop - the positive pulse sets and the negative resets. In each respective logic state any additional peaks of noise or harmonics on that half of the waveform are ignored - once set, it cannot be set again until reset (and vice-versa).

                  This circuit then has excellent noise immunity and provides a clean digital output. I noticed yesterday when playing through my PIC setup just how well the software version of this behaves. Perhaps this is what's missing from my basic design. Much more could be done in software, such as setting a variable signal threshold which could be adjusted just above any noise floor.

                  I could build a hardware version of the Boss circuit, but the PIC developer had already gone down that route and implemented it in software, which is much less complicated and is more easily modified.

                  If I have time today I'll include some print statements in the Arduino program to trap any time periods that exceed the guitar's range. I hadn't played about with the Arduino USB interface before and discovered that the compiler has full printing and data logging for reporting and debugging.​

                  Comment


                  • #39
                    When you use the frequency generator the voltage is going to remain fairly constant across the range. With the guitar the sweet spot is currently between the 5th and 9th frets. Is low note acquisition slow when using the frequency generator?

                    What if you ran the signal through an equalizer? Then you could mess with the signal and see if boosting parts of it can make the sweet spot bigger. The impact of distortion on frequency detection may be interesting as well.

                    Comment


                    • #40
                      I hadn't tried anything other than sweeping the signal generator and the numbers change too quickly to notice if there's a lag. The signal generator produces the full range of output 0 to 4095 and I sample-checked the DAC values with the signal generator frequency and they match fine. I'll try it with a debounced momentary switch to check latency. Need to check EQing the signal.

                      Comment


                      • #41
                        EQing the signal didn't work, but switching generator output signal did. I went back to the fundamental extractor and removed some of the filtering. I also saw it was peaking just over the Arduino input level so added a pair of clamping diodes to the pin 8 input. It now works to the extent that I can see how good it could be with some (a lot) more work. Anyhow, the basic frequency detection is really quick and any low-note latency is hardly noticeable at all. The Arduino responds practically instantly to bends and vibrato.

                        The circuit is very prone to electrical noise and any stray string rubbing causes random squeaks and odd notes.

                        There are plenty of issues to overcome, mainly in the fundamental extraction. Maybe the Boss OC-2 circuit would work better. I also need to think of how to detect a new note and add a gate to the input so that anything below a certain threshold is ignored. When a note stops playing and there's no input the DAC outputs a low voltage - it needs to continue to output the last note detected. Anyhow, good progress today and really encouraging.

                        Comment


                        • #42
                          I don't know what was going on with the DAC, but everything is fine now and I'm really pleased with how it works. I'm using Ray Wilson's guitar to gate circuit to start and stop the frequency detection, though the circuit needs a little more tweaking to get it just right. When the parts are delivered I'll build the OC-2 fundamental extractor.

                          One thing that needs coding is to ignore any sudden spurious DAC values. The frequency detection is really sensitive and string squeaks before the note has ended can result in sudden momentary jumps lasting a few milliseconds. Thinking out loud, if a note has a bend, vibrato or slide, the next DAC value would always be within a narrow range of values of the previous one (within a certain time period during the note length). If a new note is detected then it may well be a large interval from the previous one, but that doesn't seem to be an issue - a new note is almost always accurately pitched. I'm thinking of ignoring any out of range values, though some though needs to be given to hammer-ons or trills.

                          Here's the latest code (Can't upload the .ino, so formatting is lost);

                          Guitar to CV v2.1.txt

                          Comment


                          • #43
                            These changes may run faster because of integer math instead of fp and also getting rid of one calculation. AFAIK Arduino has no fp hardware, it needs help from a library.

                            Instead of the ternary operator you could use an if statement. In any event it is probably more efficient than a function call.

                            int Htime; // integer for storing high time
                            int Ltime; // integer for storing low time
                            int Ttime; // integer for storing total time of a cycle
                            int frequency55; // for storing frequency divided by 55
                            float voltage_conversion; // frequency to voltage conversion
                            int dac_output; // rounded DAC output (0 to 4095)


                            frequency55 = 18182 / Ttime; // calculate frequency
                            voltage_conversion = log2(frequency55) ; // frequency to voltage in volts conversion
                            dac_output = voltage_conversion * 819; // get DAC value as an integer
                            dac_output = (dac_output < 0) ? 0 : dac_output;
                            dac_output = (dac_output > 4095) ? 4095 : dac_output;

                            You can also probably do the following because testing to see if digitalRead is HIGH is redundant. We know this because digitalRead only ever returns 1 or 0. Strictly speaking the test is a better way to write the code because it is easier to read, but we really want to optimize the code to cut down on latency.

                            if (digitalRead(env_det)) {

                            ​Do you need to test dac_output range? It is dependent on frequency so you could check the frequency instead. Then you could set the dac_output directly without needing to calculate the log. You could work that in to your desqueaking routine.

                            Comment


                            • #44
                              I really appreciate the advice. I like the elegance of if (digitalRead(env_det)) {. I'll try to remember that technique for the future. Will recompile using your suggestion regarding the calculation.

                              An improvement in the circuit itself would be to get rid of the digital read on the envelope detection pin altogether, as none of the circuits I've used are good enough. There's always some ripple at the end of a note that causes problems which are fixable at the expense of sensitivity, but then timing issues arise and latency from the envelope detection starts to increase. Rather than detect amplitude with an analogue circuit, I thought it would better to monitor the frequency to generate the gate pulse. Allowing for a drop-D tuning and a little bit of tuning disparity anything below 65Hz could be considered to be 'note off'. I set up an if-else routine to test this and it doesn't work too well - the output sometimes latches high and when it does work there's a delay of around 2 seconds after a note stops before the output goes low (I've installed pull down resistors on the guitar in and gate out pins with no difference).

                              The check is

                              if (frequency >= 65) {

                              Only if true does the DAC calculation etc. and turning on the gate take place. Else the gate port and LED are turned off (no note).

                              Printing the frequency calculation to the serial monitor repeatedly gives INF INF INF.......maybe because the fp number is too big to display. I need to research that. I suppose an easier test just to see what's happening would be to check the DAC value for a note off (DAC value less than say, 360).​

                              EDIT; just worked out where INF comes from - it's due to a divide by zero when the Ttime is zero (no signal) in the line; frequency = 1000000 / Ttime; // calculate frequency.
                              Last edited by Mick Bailey; 09-02-2023, 08:29 AM.

                              Comment


                              • #45
                                You may find something like this useful in your travels.

                                frequency55 = (Ttime > 280) ? 18182 / Ttime : 0; // calculate frequency

                                I am surprised that Ttime can be zero. I was under the impression that pulseIn would block until it detected a pulse and therefore never return zero unless you set a timeout in the call, pulseIn(pin, value, timeout). However this blocking behaviour could explain your latching problem.

                                Comment

                                Working...
                                X