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:
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:
public partial class Program
this.displayT43.SimpleGraphics.DisplayText("Tunes Tester", Resources.GetFont(Resources.FontResources.NinaB), GT.Color.White, 0, 0);
this.displayT43.SimpleGraphics.DisplayText("Playing the tone now", Resources.GetFont(Resources.FontResources.NinaB), GT.Color.White, 0, 0);
@ 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.
@ 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.
@ 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 - 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)
@ 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:
@ 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
Thread.Sleep(tone duration time)
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.