FEZ Raptor with Tunes

We are new to NETMF technology and using the FEZ Raptor board with T43, USBClientDP, SDCard, RS232 and Tunes (NETMF 4.3 and Gadgeteer Package 2014 R2) in a case study.
Thanks to this forum and the brilliant idea behind Gadgeteer we are making good progress in doing so, except in one point:
After our application has been running a while, immediately after the garbage collector has been triggered automatically, the following exception is thrown:


#### Exception System.InvalidOperationException - CLR_E_INVALID_OPERATION (14) ####
#### Message: 
#### Microsoft.SPOT.Hardware.Port::ReservePin [IP: 0000] ####
#### Microsoft.SPOT.Hardware.PWM::Dispose [IP: 0017] ####

A first chance exception of type ‘System.InvalidOperationException’ occurred in Microsoft.SPOT.Hardware.dll
An unhandled exception of type ‘System.InvalidOperationException’ occurred in Microsoft.SPOT.Hardware.dll

The line with “Microsoft.SPOT.Hardware.PWM::Dispose” suggests that this has something to do with the Tunes module.
Further investigation by using and modifying the simple Tunes_Tester code from CodePlex as follows:


namespace Tunes_Tester
{
    public partial class Program
    {
        void ProgramStarted()
        {
            this.displayT43.SimpleGraphics.DisplayText("Tunes Tester", Resources.GetFont(Resources.FontResources.NinaB), GT.Color.White, 0, 0);
            Thread.Sleep(1000);

            this.displayT43.SimpleGraphics.Clear();
            this.displayT43.SimpleGraphics.DisplayText("Playing the tone now", Resources.GetFont(Resources.FontResources.NinaB), GT.Color.White, 0, 0);        
            
            this.tunes.Play(120, 200);
            Thread.Sleep(1000);
            Microsoft.SPOT.Debug.GC(true);
        }
    }
}

reproducibly shows, that when calling the GC (here manually) after playing a sound with Tunes, always the above described exception is thrown.
Maybe the exception has a similar cause as described in this post: https://www.ghielectronics.com/community/forum/topic?id=11125&page=1 ?

Has anyone an explanation or a workaround for this behaviour?

@ Harald - You are correct in that it is a bug with PWM. Regretfully, it appears to be a bug in NETMF and we don’t have an easy work around at this time.

If you’re interested in a more technical explanation:
Gadgeteer provides a Tunes wrapper that we use in our Tunes driver. When Tunes stops playing, we disable PWM. Gadgeteer then disposes of the underlying NETMF PWM object which frees the pin associated with it. The NETMF PWM driver does not implement the Disposable/Finalizer pattern properly through. When you call Debug.GC(true), NETMF runs all pending finalizers. Since the PWM object provides a finalizer and Dispose did not call GC.SurpressFinalizer, it gets run. Looking at the PWM finalizer source, it calls Dispose again. So you get a call to Dispose twice which in this instance tries to unreserve a pin twice which gives you the exception you see.

You could potentially modify the Gadgeteer PWM wrapper to not dispose of the PWM object so that this exception is not thrown until after the program ends.

2 Likes

@ Harald - I made an issue on NETMF Codeplex here: https://netmf.codeplex.com/workitem/2326

3 Likes

This is an old bug. The item on codeplex needs some votes people!

1 Like

@ John - Many thanks for your quick, detailed and competent answer, that’s what confirms our conviction that we’re backing the right horse if we carry on using GHI products such as G400S in our case studies and maybe also for series production when the time comes.

We will try modifiying the wrapper in between, maybe we can also try to catch, identify and ignore the exception under this special circumstances.

@ Harald - I would just use PWM directly as it is very easy. We look forward to seeing all the great things you will do with NETMF.

Welcome to the community.

1 Like

@ John - Based on your description, an interim workaround might be to not disable PWM in the Tunes driver and rather just set the frequency to 0. That way the PWM instance is not eligible for GC. Not ideal, but should work?

@ taylorza - That is another possible work around. I haven’t tested it myself yet, but you can try replacing the “this.pwm.IsActive = false;” on line 372 and 328 with “this.pwm.Set(0, 0.5);” in Tunes_43.cs found at https://bitbucket.org/ghi_elect/gadgeteer/src/2567b2403077bdc7235f3697308d0f8966f6c6de/Modules/GHIElectronics/Tunes/Tunes_43/Tunes_43.cs?at=master

@ taylorza - Tried it, but throws a Division by Zero Exception in “Microsoft.SPOT.Hardware.PWM::PeriodFromFrequency” ‘cause period would be infinite then due to frequency = 0 :’(
(A pity,would have been a nice quick hack)

@ Harald - Could you try setting the duty cycle to zero instead?

@ John - Yes, that could be an option. Especially as I already planned to use the duty cycle as a new volume property :wink:

@ John - Replacing the “this.pwm.IsActive = false;” in lines 328 and 372 by any “this.pwm.Set();”-statement, even with valid freuency and duty cycle parameters (e.g. 100, 0.05), throws the following exception at the end of the worker thread execution:

#### Exception System.InvalidOperationException - CLR_E_INVALID_OPERATION (11) ####
#### Message: 
#### Microsoft.SPOT.Hardware.Port::ReservePin [IP: 0000] ####
#### Microsoft.SPOT.Hardware.PWM::.ctor [IP: 004d] ####
#### Gadgeteer.SocketInterfaces.NativePwmOutput::Set [IP: 0046] ####
#### PWMSGadget.Tunes::DoWork [IP: 005a] ####

@ Harald - It looks like your code is still using the old assembly without the fix. If it isn’t, I’ll take a look before the next SDK and see if I can’t rework the driver to avoid the issue. In the mean time, it might not be too difficult for you to write your own thin wrapper around either Microsoft.SPOT.Hardware.PWM or, if you don’t set IsActive to false, Gadgeteer.SocketInterfaces.PwmOutput. When it gets down to it, we just set the PWM to the frequency of the tone you want to play with a 0.5 duty cycle.

@ John -
I was using the newest code from the bitbucket-link you provided in #8.
Avoiding the bug in the next SDK should be appreciated.
For the moment I switched to Gus’ advice and tried using PWM directly in the manner of

  • PWM pwm = new PWM(Cpu.PWMChannel.PWM_2, 100, 0.5, false) // init for Raptor socket 18, Pin 9
  • pwm.Frequency = some double value
  • pwm.DutyCycle = 0.5
  • pwm.Start()
  • Thread.Sleep(tone duration time)
  • pwm.Stop()

This seems to work fine (although missing the comfort of the tunes class),
even if garbage collection takes place later, either automatically or manually via Debug.GC.