File: PWMPortMM

Details

File: PWMPortMM.cs
Date: Thu, Mar 7, 2019
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using Mono.Unix;
using Mono.Unix.Native;

/// +------------------------------------------------------------------------------------------------------------------------------+
/// |                                                   TERMS OF USE: MIT License                                                  |
/// +------------------------------------------------------------------------------------------------------------------------------|
/// |Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    |
/// |files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    |
/// |modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software|
/// |is furnished to do so, subject to the following conditions:                                                                   |
/// |                                                                                                                              |
/// |The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.|
/// |                                                                                                                              |
/// |THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          |
/// |WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         |
/// |COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   |
/// |ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         |
/// +------------------------------------------------------------------------------------------------------------------------------+

namespace BBBCSIO
{
    /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
    /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
    /// <summary>
    /// Provides the Pulse Width Modulation Port functionality for a BeagleBone Black
    /// This is the Memory Mapped version
    /// 
    /// Be aware that you need to ensure the PWM port is configured in the Device
    /// Tree before this code will work. Usually this is done by adding the 
    /// overlay to the uEnv.txt file. The Beaglebone Black and Device Tree Overlays
    /// technical note has more information.
    /// 
    /// http://www.ofitselfso.com/BeagleNotes/Beaglebone_Black_And_Device_Tree_Overlays.php
    ///
    /// NOTE: The Pulse Width Modulation Subsystem (PWMSS) of the Beaglebone Black 
    ///       contains other things than pulse width modulation (PWM), there is also
    ///       a  Enhanced Capture (eCAP), and Encoded Pulse (eQEP) module. The actual
    ///       PWM is in the Enhanced High Resolution Pulse Width Modulator (eHRPWM)
    /// 
    ///       As a naming convention, in the code below a variable with 'PWMSS' refers
    ///       to something in the PWMSS system itself and things with names like EHRPWM refer
    ///       to the PWM module of that system.
    /// 
    ///       Furthermore, there are three PWMSS subsystems on the BBB (0,1,2). Each of
    ///       these has a eHRPWM, eQEP and eCap module. Each eHRPWM module has two PWM
    ///       outputs (A and B). This gives a total of six PWM outputs for the BBB.
    ///
    ///       Some things like the PWM frequency are set at the PWMSS level, and other things
    ///       such as the duty cycle are set individually at the eHRPWM level on each PWM
    ///       output (A or B). This means that, for any one PWMSS subsystem, both PWM outputs
    ///       have to use the same frequency but that they can have independently settable
    ///       duty cycles.
    /// 
    ///       This is IMPORTANT so I will say it again. If you configure 
    ///       two PWM outputs in the same device (PWM1_A, PWM1_B for example) 
    ///       then they MUST be configured with the same frequency. 
    /// 
    ///       They will use the same frequency anyways and if
    ///       you change one you change the other. Changing the frequency on the
    ///       B output will instantly change the frequency on the A output and
    ///       will really mess up any pulse widths/duty cycles the A output is using
    /// 
    ///       Always set the frequency first then the Pulse Width/duty cycle. The pulse 
    ///       width is calculated from whatever frequency is currently set and it is
    ///       not adjusted if the frequency is later changed.
    /// 
    /// In writing this class much reference was made to the BBBIOlib open source C code 
    /// which implements a Memory Mapped PWM module. The BBBIOlib code can be found here:
    ///    https://github.com/VegetableAvenger/BBBIOlib
    /// thank you very much Shabaz, VegetableAvenger
    /// 
    /// </summary>
    /// <history>
    ///    21 Nov 15  Cynic - Originally written
    /// </history>
    public class PWMPortMM : PortMM
    {
        // some constants
        private const int BBB_CONTROL_MODULE = 0x44e10000;
        private const int BBB_CONTROL_LEN = 0x2000;
        private const int BBB_CONTROL_PWMSS_CTRLREG = 0x664;
        private const int BBB_CLOCKMODULE_PERIPHERAL_ADDR = 0x44e00000;
        private const int BBB_CLOCKMODULE_PERIPHERAL_LEN = 0x4000;
        private const int BBB_CLOCKMODULE_PERIPHERAL_EPWMSS0_CLKCTRL = 0xD4; // yes! these are out of order
        private const int BBB_CLOCKMODULE_PERIPHERAL_EPWMSS1_CLKCTRL = 0xCC; // yes! these are out of order
        private const int BBB_CLOCKMODULE_PERIPHERAL_EPWMSS2_CLKCTRL = 0xD8; // yes! these are out of order
        private const int PWMSS0_MODULE_ADDR = 0x48300000;
        private const int PWMSS1_MODULE_ADDR = 0x48302000;
        private const int PWMSS2_MODULE_ADDR = 0x48304000;
        private const int PWMSS_MMAP_LEN = 0x1000;
        private const int PWMSS_MODULE_ECAP_OFFSET = 0x100;
        private const int PWMSS_MODULE_EQEP_OFFSET = 0x180;
        private const int PWMSS_MODULE_EPWM_OFFSET = 0x200;
        private const int PWMSS_MODULE_EPWM_AQCTLA = 0x16;
        private const int PWMSS_MODULE_EPWM_AQCTLB = 0x18;
        private const int PWMSS_MODULE_EPWM_TBCNT = 0x8;
        private const int PWMSS_MODULE_EPWM_TBCTL = 0x0;
        private const int PWMSS_MODULE_EPWM_TBPRD = 0xA;
        private const int PWMSS_MODULE_EPWM_CMPA = 0x12;
        private const int PWMSS_MODULE_EPWM_CMPB = 0x14;
        // set ZRO to 0x02 and CAU to 0x03, everthing else 0
        private const short AQCTLA_SETTING_ON = 0x001A;
        // set ZRO to 0x02 and CBU to 0x03, everthing else 0
        private const short AQCTLB_SETTING_ON = 0x010A;
        // set ZRO to 0x01 - force it low, everthing else 0
        private const short AQCTLA_SETTING_OFF = 0x1;
        private const short AQCTLB_SETTING_OFF = 0x1;

        // the PWM port we use
        private PWMPortEnum pwmPort = PWMPortEnum.PWM_NONE; 
        // memory mapped pointers
        private IntPtr bbbControlModule_ptr = IntPtr.Zero;
        private IntPtr bbbClockModulePeripheral_ptr = IntPtr.Zero;
        private IntPtr pwmssModule_ptr = IntPtr.Zero;
        // calculated by adding offsets to the memory mapped pointers
        // these are the same for either the A or B PWM Output
        private IntPtr pwmssModuleEpwm_ptr = IntPtr.Zero;
        private IntPtr pwmssModuleEpwmTBCNT_ptr  = IntPtr.Zero;
        private IntPtr pwmssModuleEpwmTBCTL_ptr = IntPtr.Zero;
        private IntPtr pwmssModuleEpwmTBPRD_ptr = IntPtr.Zero;
        // these are specific to the A or B PWM Output
        private IntPtr pwmssModuleEpwmAQCTLA_ptr = IntPtr.Zero;
        private IntPtr pwmssModuleEpwmAQCTLB_ptr = IntPtr.Zero;
        private IntPtr pwmssModuleEpwmCMP_ptr = IntPtr.Zero;
           
        private float[] clkdivArr = {1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f, 128.0f};
        private float[] hspclkdivArr = {1.0f, 2.0f, 4.0f, 6.0f, 8.0f, 10.0f, 12.0f, 14.0f};
        // the TBCLK base frequency
        private const float BASE_FREQUENCY = 100000000.0f;
        // the maximum we can count up to
        private const float MAX_TBPRD_COUNT = 0xFFFF;

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="pwmPortIn">The PWM port we use</param>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        public PWMPortMM(PWMPortEnum pwmPortIn) : base(GpioEnum.GPIO_NONE)
        {
            pwmPort = pwmPortIn;

            //            Console.WriteLine("PWMPort Starts");
            OpenPort();
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Opens the port. Throws an exception on failure
        /// 
        /// </summary>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        protected override void OpenPort()
        { 
            int fdDevMem = -1;
            bool retBool = false;

            //Console.WriteLine("PWMPort Port Opening: "+ PWMPort.ToString());
            // some tests
            if (PWMPort == PWMPortEnum.PWM_NONE) 
            {
                throw new Exception ("Cannot open PWM port. Invalid port: " + PWMPort.ToString ());
            }

            // open the device memory file 
            fdDevMem = Syscall.open (BBBDefinitions.DEVMEM_FILE, OpenFlags.O_RDWR, FilePermissions.DEFFILEMODE);
            if (fdDevMem == -1) 
            {
                throw new Exception ("DEVMEM_FILE did not open");
            }
            //Console.WriteLine("  device memory file opened");

            // ########
            // ######## Create memory maps to memory areas we are interested in
            // ########

            // map in the Control Module Register, we will need to check
            // the pwmss_ctrl register to make sure the timebase clock is enabled
            bbbControlModule_ptr = Syscall.mmap (IntPtr.Zero, BBB_CONTROL_LEN, MmapProts.PROT_WRITE | MmapProts.PROT_READ, MmapFlags.MAP_SHARED, fdDevMem, BBB_CONTROL_MODULE);
            if (bbbControlModule_ptr == (IntPtr)(-1))
            {
                throw new IOException ("mmap failed for CRM " + "(" + BBB_CONTROL_MODULE + ", " + BBB_CONTROL_LEN + ")");
            }
            //Console.WriteLine("  bbbControlModule_ptr=0x"+bbbControlModule_ptr.ToString("X8"));

            // map in the Clock Module Peripheral Register 
            bbbClockModulePeripheral_ptr = Syscall.mmap (IntPtr.Zero, BBB_CLOCKMODULE_PERIPHERAL_LEN, MmapProts.PROT_WRITE | MmapProts.PROT_READ, MmapFlags.MAP_SHARED, fdDevMem, BBB_CLOCKMODULE_PERIPHERAL_ADDR);
            if (bbbClockModulePeripheral_ptr == (IntPtr)(-1))
            {
                throw new IOException ("mmap failed for PCRM " + "(" + BBB_CLOCKMODULE_PERIPHERAL_ADDR + ", " + BBB_CLOCKMODULE_PERIPHERAL_LEN + ")");
            }
            //Console.WriteLine("  bbbClockModulePeripheral_ptr=0x"+bbbClockModulePeripheral_ptr.ToString("X8"));

            // create memory map for the PWMSS module
            int pwmssModuleBaseAddress = GetPWMSSBaseAddressFromPWMPortEnum(PWMPort);
            pwmssModule_ptr = Syscall.mmap (IntPtr.Zero, PWMSS_MMAP_LEN, MmapProts.PROT_WRITE | MmapProts.PROT_READ, MmapFlags.MAP_SHARED, fdDevMem, pwmssModuleBaseAddress);
            if (pwmssModule_ptr == (IntPtr)(-1))
            {
                throw new IOException ("mmap failed for CRM " + "(" + PWMSS1_MODULE_ADDR + ", " + PWMSS_MMAP_LEN + ")");
            }
            //Console.WriteLine("  pwmssModuleBaseAddress=0x"+pwmssModuleBaseAddress.ToString("X8"));

            // ########
            // ######## Now set up everything so we can run when required
            // ########

            retBool = IsControlModuleClockEnabled();
            if (retBool == false)
            {
                // NOTE: I do not think it is possible to set the Control Module from 
                //       a userspace program - even one running as root. This sort of 
                //       thing is typically done from the Device Tree
                //Console.WriteLine("  Control Module Clock Not Enabled");
                throw new Exception("Control Module Clock Not Enabled - Is Device Tree Set?");
            }
            else
            {
                //Console.WriteLine("  Control Module Clock Is Enabled");
            }

            retBool = IsClockModulePeripheralClockEnabled();
            if (retBool == false)
            {
                //Console.WriteLine("  Clock Module Peripheral Clock Not Enabled");
                EnableClockModulePeripheralClock();
            }
            else
            {
                //Console.WriteLine("  Clock Module Peripheral Clock Already Enabled");
            }

            retBool = IsClockModulePeripheralClockEnabled();
            if (retBool == false)
            {
                //Console.WriteLine("  Clock Module Peripheral Clock Not Enabled");
            }
            else
            {
                //Console.WriteLine("  Clock Module Peripheral Clock Already Enabled");
            }

            // this points at the EPWM sub module of the PWMSS module, remember we also
            // have the EQEP and ECAP modules in there as well
            pwmssModuleEpwm_ptr = new IntPtr(pwmssModule_ptr.ToInt64() + PWMSS_MODULE_EPWM_OFFSET );
            //Console.WriteLine("  pwmssModuleEpwm_ptr=0x"+pwmssModuleEpwm_ptr.ToString("X8"));

            // these are all based off the pwmssModuleEpwm_ptr and will be used by other
            // functions to set the frequency, duty cycle and enable/disable the PWM
            pwmssModuleEpwmTBCNT_ptr = new IntPtr(pwmssModuleEpwm_ptr.ToInt64() + PWMSS_MODULE_EPWM_TBCNT);
            pwmssModuleEpwmTBCTL_ptr = new IntPtr(pwmssModuleEpwm_ptr.ToInt64() + PWMSS_MODULE_EPWM_TBCTL);
            pwmssModuleEpwmTBPRD_ptr = new IntPtr(pwmssModuleEpwm_ptr.ToInt64() + PWMSS_MODULE_EPWM_TBPRD);
            // we need both of these
            pwmssModuleEpwmAQCTLA_ptr = new IntPtr(pwmssModuleEpwm_ptr.ToInt64() + PWMSS_MODULE_EPWM_AQCTLA);
            pwmssModuleEpwmAQCTLB_ptr = new IntPtr(pwmssModuleEpwm_ptr.ToInt64() + PWMSS_MODULE_EPWM_AQCTLB);

            // these are specific to the A or B PWM output
            if (IsThisPortForPWMOutputA(PWMPort) == true)
            {
                // set up for the A output
                pwmssModuleEpwmCMP_ptr = new IntPtr(pwmssModuleEpwm_ptr.ToInt64() + PWMSS_MODULE_EPWM_CMPA);
            }
            else
            {
                // set up for the B output
                pwmssModuleEpwmAQCTLB_ptr = new IntPtr(pwmssModuleEpwm_ptr.ToInt64() + PWMSS_MODULE_EPWM_AQCTLB);
                pwmssModuleEpwmCMP_ptr = new IntPtr(pwmssModuleEpwm_ptr.ToInt64() + PWMSS_MODULE_EPWM_CMPB);
            }
                
            // set this flag
            portIsOpen = true;

            //    Console.WriteLine("PWMPort Port Opened: "+ PWMPort.ToString());

            // once the memory is mapped the device file can be closed.
            if (fdDevMem > 0)
            {
                Syscall.close(fdDevMem);
                fdDevMem = -1;
            }

        }
                      
        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Closes the port. 
        /// 
        /// </summary>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        public override void ClosePort()
        {
            //  Console.WriteLine("ClosePort() called");
            if (portIsOpen == true)
            {
                //Console.WriteLine("portIsOpen == true");
                RunState = false;           
                portIsOpen = false;
            }                
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets/Sets the period of the PWM output square wave form
        /// 
        /// Note both outputs from the same PWM module will use the same period
        ///      See the notes in this codes header. Always set the period/frequency 
        ///      before setting the pulse width/duty cycle
        /// 
        /// </summary>
        /// <value>the period (1/Freq) in nano seconds</value>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        public uint PeriodNS
        { 
            get
            {
                uint freqInHz = (uint)GetBaseFrequency();
                if (freqInHz == 0)
                {
                    throw new Exception("Invalid base frequency of " + freqInHz.ToString()); 
                }
                return 1000000000/freqInHz;
            }
            set
            {
                uint freqInHz = 1000000000/value;
                SetBaseFrequency((int)freqInHz);
            }
        }
       
        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets/Sets the frequency of the PWM output square wave form
        /// 
        /// Note both outputs from the same PWM module will use the same frequency
        ///      See the notes in this codes header. Always set the period/frequency 
        ///      before setting the pulse width/duty cycle
        /// 
        /// </summary>
        /// <value>frequency in Hz</value>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        public uint FrequencyHz
        { 
            get
            {
                return (uint)GetBaseFrequency();
            }
            set
            {
                SetBaseFrequency((int)value);
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets/Sets the duty cycle of the PWM output square wave form as a percent
        /// 
        /// Note both outputs from the same PWM module will use the same frequency
        ///      but can have different duty cycles. Always set the period/frequency
        ///      before setting the dutyNs/duty cycle
        /// 
        /// </summary>
        /// <value>percentage of the input value. Must be between 0 and 100 </value>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        public float DutyPercent
        { 
            get
            {
                return (float)GetDutyCycle();
            }
            set
            {
                SetDutyCycle((float)value);
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets/Sets the duty cycle of the PWM output square wave form in nanoseconds
        /// 
        /// Note both outputs from the same PWM module will use the same frequency
        ///      but can have different duty cycles. Always set the period/frequency
        ///      before setting the dutyNs/duty cycle
        /// 
        /// </summary>
        /// <value>the duty cycle of the PWM output in nanoseconds. This should always
        /// be less than the PeriodNS</value>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        public uint DutyNS
        { 
            get
            {
                uint periodNS = PeriodNS;
                return (uint)((float)periodNS * (float)DutyPercent) / 100;
            }
            set
            {
                 DutyPercent = (((float)value * 100)/ (float)PeriodNS) ;
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets/Sets the run state of the PWM output square wave
        /// 
        /// </summary>
        /// <value>true - begin running, false - stop running</value>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        public bool RunState
        { 
            get
            {
                short out16Val=0;

                if (portIsOpen == false) return false;

                // check TBCTL 
                out16Val = Marshal.ReadInt16(pwmssModuleEpwmTBCTL_ptr);
                if ((out16Val & 0x03) != 0x00)
                {
                    // neither PWM output configured 
                    return false;
                }

                if (IsThisPortForPWMOutputA(PWMPort) == true)
                {
                    // read the A side AQCTLA register
                    out16Val = Marshal.ReadInt16(pwmssModuleEpwmAQCTLA_ptr);
                    if (out16Val == AQCTLA_SETTING_ON) return true;
                }
                else
                {
                    // read the B side AQCTLB register
                    out16Val = Marshal.ReadInt16(pwmssModuleEpwmAQCTLB_ptr);
                    if (out16Val == AQCTLB_SETTING_ON) return true;
                }
                return false;
            }
            set
            {
                short out16Val=0;
               
                //           Console.WriteLine("Set Run State : " + value.ToString());
                if (portIsOpen == false) return;

                // set the run state
                if (value == true)
                {
                    // enable
                    if (IsThisPortForPWMOutputA(PWMPort) == true)
                    {
                        out16Val = AQCTLA_SETTING_ON;
                        Marshal.WriteInt16(pwmssModuleEpwmAQCTLA_ptr, out16Val);
                    }
                    else
                    {
                        out16Val = AQCTLB_SETTING_ON;
                        Marshal.WriteInt16(pwmssModuleEpwmAQCTLB_ptr, out16Val);
                    }

                    // test to see if the other PWM port is active
                    if (IsTheOtherPWMOutputActive() == false)
                    {
                        // not active, start counting from 0
                        out16Val = 0;
                        Marshal.WriteInt16(pwmssModuleEpwmTBCNT_ptr, out16Val);
                    }

                    // set the TBCTL going - if it is not already, both PWM
                    // outputs use this so we have to be careful here
                    out16Val = Marshal.ReadInt16(pwmssModuleEpwmTBCTL_ptr);
                    if ((out16Val & 0x03) != 0x00)
                    {
                        // not configured, clear the bottom two bits to 
                        // start it running in increment counter mode
                        out16Val &= ~0x03;
                        Marshal.WriteInt16(pwmssModuleEpwmTBCTL_ptr, out16Val);
                    }
                }
                else
                {
                    // disable

                    if (IsThisPortForPWMOutputA(PWMPort) == true)
                    {
                        // just force the output low on the next cycle, and configure it
                        // to never change
                        out16Val = AQCTLA_SETTING_OFF;
                        Marshal.WriteInt16(pwmssModuleEpwmAQCTLA_ptr, out16Val);
                    }
                    else
                    {
                        // just force the output low on the next cycle, and configure it
                        // to never change
                        out16Val = AQCTLB_SETTING_OFF;
                        Marshal.WriteInt16(pwmssModuleEpwmAQCTLB_ptr, out16Val);
                    }

                    // test to see if the other PWM port is active
                    if(IsTheOtherPWMOutputActive()==false)
                    {
                        // no, it is not active. We can disable the TBCTL
                        // setting the bottom two bits high (0x03), stops the counter for
                        // both PWM ports
                        out16Val = Marshal.ReadInt16(pwmssModuleEpwmTBCTL_ptr);
                        out16Val|=0x3;
                        Marshal.WriteInt16(pwmssModuleEpwmTBCTL_ptr, out16Val );
                    }

                    // test to see if the other PWM port is active
                    if (IsTheOtherPWMOutputActive() == false)
                    {
                        // reset the counter. it is shared
                        out16Val = 0;
                        Marshal.WriteInt16(pwmssModuleEpwmTBCNT_ptr, out16Val);
                    }
                }
            }
        }            
            
        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets the PWM Port. There is no Set accessor this is set in the constructor
        /// </summary>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        public PWMPortEnum PWMPort
        {
            get
            {
                return pwmPort;
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets the PortDirection
        /// </summary>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        public override PortDirectionEnum PortDirection()
        {
            return PortDirectionEnum.PORTDIR_OUTPUT;
        }   

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Sets the DutyCycle - 
        /// 
        /// NOTE this code expects the base frequency to have been set. It uses that
        ///      value to calculate the UP and DOWN pulse timing
        /// 
        /// </summary>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        private void SetDutyCycle(float dutyCyclePercent)
        {
            if ((dutyCyclePercent < 0) || (dutyCyclePercent > 100))
            {
                throw new Exception("The percentage duty cycle of " + dutyCyclePercent.ToString() + " is not valid");
            }
            short tbprd16Val=0;
            short cmpReg16Val=0;

            //  Console.WriteLine("  SDS: dutyCyclePercent=" + dutyCyclePercent.ToString());

            // we read the contents of the TBPRD to get the current pulse count
            tbprd16Val = Marshal.ReadInt16(pwmssModuleEpwmTBPRD_ptr);
            if (dutyCyclePercent == 0)
            {
                // 0% duty cycle is this count
                cmpReg16Val = 0;
            }
            else if (dutyCyclePercent == 100)
            {
                // 100% duty cycle is this count
                cmpReg16Val = tbprd16Val;
            }
            else
            {
                // calc the total high count based on a percentage of the total pulse width
                ushort ushort16Val = (ushort)tbprd16Val;
                float tmpVal = ((float)ushort16Val * dutyCyclePercent);
                cmpReg16Val = (short)( tmpVal/ 100);
                //            Console.WriteLine("  SDS setting: tmpVal="+tmpVal.ToString()); 
            }
            // at this point we have the count for the cmpReg16Val to be calulated
            // we write it back out to CMPA or CMPB depending on the output we 
            // are using. The pwmssModuleEpwmCMP_ptr points appropriately
            Marshal.WriteInt16(pwmssModuleEpwmCMP_ptr, cmpReg16Val);
            //        Console.WriteLine("  SDS setting: CMP val="+cmpReg16Val.ToString()); 
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets the DutyCycle 
        /// 
        /// </summary>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        private float GetDutyCycle()
        {
            short tbprd16Val=0;
            short cmpReg16Val=0;

            // we read the contents of the TBPRD to get the current pulse count
            tbprd16Val = Marshal.ReadInt16(pwmssModuleEpwmTBPRD_ptr);
            //Console.WriteLine("GDS: TBPRD=" + tbprd16Val.ToString());

            // we read the contents of the TBPRD to get the current pulse count
            cmpReg16Val = Marshal.ReadInt16(pwmssModuleEpwmCMP_ptr);
            //Console.WriteLine("GDS: TBPRD=" + tbprd16Val.ToString());

            if (tbprd16Val==0)
            {
                throw new Exception("Invalid TBPRD count of " + tbprd16Val.ToString());
            }
            if ((ushort)cmpReg16Val > (ushort)tbprd16Val)
            {
                throw new Exception("cmpReg16Val > tbprd16Val (" + cmpReg16Val.ToString() + "," + tbprd16Val.ToString()+")");
            }

            return ((float)((ushort)cmpReg16Val)*100)/(float)((ushort)tbprd16Val);
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Recalculates the current base frequency from the TBCTL divisors
        /// 
        /// </summary>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        private int GetBaseFrequency()
        {
            // read the TBCTL
            short out16Val = Marshal.ReadInt16(pwmssModuleEpwmTBCTL_ptr);
            //Console.WriteLine("  GBF: TBCTL val="+out16Val.ToString("X4")); 
            int workingCLKDIV = (int)((out16Val & 0x1C00) >> 10);
            int workingHSPCLKDIV = (int)((out16Val & 0x0380) >> 7);
            if ((workingCLKDIV < 0) || (workingCLKDIV > clkdivArr.Length))
            {
                throw new Exception("invalid CLKDIV=" + workingCLKDIV.ToString());
            }
            if ((workingHSPCLKDIV < 0) || (workingHSPCLKDIV > hspclkdivArr.Length))
            {
                throw new Exception("invalid HSPCLKDIV=" + workingHSPCLKDIV.ToString());
            }

            // read the TBPRD - we have to know how many clocks we are counting
            out16Val = Marshal.ReadInt16(pwmssModuleEpwmTBPRD_ptr);
            if (out16Val == 0)
            {
                throw new Exception("invalid count in TBPRD =" + out16Val.ToString());
            }
            float tbPRDCount = (float)((ushort)(out16Val));
            //Console.WriteLine("  GBF: tbPRDCount="+tbPRDCount.ToString()); 

            //Console.WriteLine("  workingCLKDIV="+workingCLKDIV.ToString()); 
            //Console.WriteLine("  workingHSPCLKDIV="+workingHSPCLKDIV.ToString()); 
            //Console.WriteLine("  clkdivArr[workingCLKDIV]="+clkdivArr[workingCLKDIV].ToString()); 
            //Console.WriteLine("  hspclkdivArr[workingHSPCLKDIV]="+hspclkdivArr[workingHSPCLKDIV].ToString()); 
            return (int)((BASE_FREQUENCY / (clkdivArr[workingCLKDIV] *  hspclkdivArr[workingHSPCLKDIV]))/tbPRDCount);           
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Sets the TBPRD count and the TBCTL divisors based on the specified input
        /// frequency.
        /// 
        /// NOTE:
        /// The grid-it-out brute force method of finding the best divisors was 
        /// inspired by the similar method used in the BBBIOlib open source C code.
        /// Thank you very much Shabaz, VegetableAvenger, the BBBIOlib code can be 
        /// found here:
        ///    https://github.com/VegetableAvenger/BBBIOlib
        /// 
        /// Note both outputs from the same PWM module will use the same frequency
        ///      See the notes in this codes header. Always set the period/frequency 
        ///      before setting the pulse width/duty cycle
        /// 
        /// </summary>
        /// <param name="frequencyInHz">The frequency in Hz</param>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        private void SetBaseFrequency(int frequencyInHz)
        {
            int bestCLKDIV = -1;
            int bestHSPCLKDIV = -1;
            float bestDividedClocksPerPulse = 0;
            short out16Val=0;

            if (frequencyInHz == 0)
            {
                throw new Exception("Zero is not a valid base frequency");

            }
            // set this now
            float numberUndividedClocksPerPulse = BASE_FREQUENCY / (float)frequencyInHz;

            // now figure out a good set of high and low divisors. The maximum we pulses
            // we can count for any total pulse width is MAXCOUNT
            for (int i = 0; i < clkdivArr.Length; i++)
            {
                for (int j = 0; j < hspclkdivArr.Length; j++)
                {
                    float workingDivisor = (clkdivArr[i] * hspclkdivArr[j]);
                    float numberDividedClocksPerPulse = numberUndividedClocksPerPulse / workingDivisor;
                    // now some tests, we cannot use a count greater than MAX_TBPRD_COUNT
                    if (numberDividedClocksPerPulse > MAX_TBPRD_COUNT) continue;
                    // ideally we want the highest number of pulses possible for best accuracy
                    // especially when we split it down for the duty cycle part
                    if (numberDividedClocksPerPulse > bestDividedClocksPerPulse)
                    {
                        // we want this one
                        bestCLKDIV = i;
                        bestHSPCLKDIV = j;
                        bestDividedClocksPerPulse = numberDividedClocksPerPulse;
                    }                        
                }
            }
            // sanity check
            if ((bestCLKDIV < 0) || (bestHSPCLKDIV<0))
            {
                throw new Exception("The base frequency "+ frequencyInHz.ToString() +" cannot be output");

            }

            //Console.WriteLine("    numberDividedClocksPerPulse=" + (numberUndividedClocksPerPulse/(clkdivArr[bestCLKDIV] * hspclkdivArr[bestHSPCLKDIV])).ToString());
            //Console.WriteLine("    bestCLKDIV=" + bestCLKDIV.ToString());
            //Console.WriteLine("   bestHSPCLKDIV=" + bestHSPCLKDIV.ToString());
            //Console.WriteLine("   divisor=" + (clkdivArr[bestCLKDIV] * hspclkdivArr[bestHSPCLKDIV]).ToString());

            // set the TBCTL register now
            out16Val = (short)((int)0x03 | (bestCLKDIV << 10) | (bestHSPCLKDIV << 7));
            Marshal.WriteInt16(pwmssModuleEpwmTBCTL_ptr, out16Val);
            //Console.WriteLine("  settings: TBCTL val="+out16Val.ToString("X8")); 

            // set the period
            out16Val=unchecked((short)(numberUndividedClocksPerPulse/(clkdivArr[bestCLKDIV] * hspclkdivArr[bestHSPCLKDIV])));
            Marshal.WriteInt16(pwmssModuleEpwmTBPRD_ptr, out16Val);
            //Console.WriteLine("  settings: TBPRD val="+out16Val.ToString("X8")); 

            // reset the clock counter
            out16Val=0x0000;
            Marshal.WriteInt16(pwmssModuleEpwmTBCNT_ptr, out16Val);

        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets the PWMSSBase address given a PWMPortEnum
        /// </summary>
        /// <param name="pwmPortIn">The PWM port we use</param>
        /// <returns>the PMSS module base address</returns>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        private int GetPWMSSBaseAddressFromPWMPortEnum(PWMPortEnum pwmPortIn)
        {
            uint pwmssModuleNumber = ConvertPWMPortEnumToPWMSSModuleNumber(pwmPortIn);
            if (pwmssModuleNumber == 0) return PWMSS0_MODULE_ADDR;
            if (pwmssModuleNumber == 1) return PWMSS1_MODULE_ADDR;
            if (pwmssModuleNumber == 2) return PWMSS2_MODULE_ADDR;
            throw new Exception("Unknown PWM module number: "+ pwmssModuleNumber.ToString());
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Converts the PWMPort Enum to a PWMSS module number
        /// </summary>
        /// <param name="pwmPortIn">The PWM port we use</param>
        /// <returns>the PMSS module number</returns>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        private uint ConvertPWMPortEnumToPWMSSModuleNumber(PWMPortEnum pwmPortIn)
        {
            if (pwmPortIn == PWMPortEnum.PWM0_A) return 0; // EHRPWM0A
            if (pwmPortIn == PWMPortEnum.PWM0_B) return 0; // EHRPWM0B    
            if (pwmPortIn == PWMPortEnum.PWM1_A) return 1; // EHRPWM1A
            if (pwmPortIn == PWMPortEnum.PWM1_B) return 1; // EHRPWM1B
            if (pwmPortIn == PWMPortEnum.PWM2_A) return 2; // EHRPWM2A
            if (pwmPortIn == PWMPortEnum.PWM2_B) return 2; // EHRPWM2B
            // if (pwmPortIn == PWMPortEnum.PWM_P9_28) return 7;          // 7 - ECAPPWM2
            // if (pwmPortIn == PWMPortEnum.PWM_P9_42) return 2;          // 2 - ECAPPWM0
            throw new Exception("Unknown PWM Port: "+ pwmPortIn.ToString());
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Detects if this is the A or B PWM output for this EHPWMSS subsystem
        /// </summary>
        /// <param name="pwmPortIn">The PWM port we use</param>
        /// <returns>true - this is the A output, false - this is the B output</returns>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        private bool IsThisPortForPWMOutputA(PWMPortEnum pwmPortIn)
        {
            if (pwmPortIn == PWMPortEnum.PWM0_A) return true; // EHRPWM0A
            if (pwmPortIn == PWMPortEnum.PWM0_B) return false; // EHRPWM0B    
            if (pwmPortIn == PWMPortEnum.PWM1_A) return true; // EHRPWM1A
            if (pwmPortIn == PWMPortEnum.PWM1_B) return false; // EHRPWM1B
            if (pwmPortIn == PWMPortEnum.PWM2_A) return true; // EHRPWM2A
            if (pwmPortIn == PWMPortEnum.PWM2_B) return false; // EHRPWM2B
            // if (pwmPortIn == PWMPortEnum.PWM_P9_28) return 7;          // 7 - ECAPPWM2
            // if (pwmPortIn == PWMPortEnum.PWM_P9_42) return 2;          // 2 - ECAPPWM0
            throw new Exception("Unknown PWM Port: "+ pwmPortIn.ToString());
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Checks to see if the other PWM output in the PWMSS module is active
        /// We check the AQCTL register for this since these are the only registers
        /// we independently configure for the A and B side
        /// </summary>
        /// <returns>true - is enabled, false - is not enabled</returns>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        private bool IsTheOtherPWMOutputActive()
        {
            short out16Val=0;
            if (IsThisPortForPWMOutputA(PWMPort) == true)
            {
                // read the B side AQCTLB register
                out16Val = Marshal.ReadInt16(pwmssModuleEpwmAQCTLB_ptr);
                if (out16Val == AQCTLB_SETTING_ON) return true;
                else return false;
            }
            else
            {
                // read the A side AQCTLA register
                out16Val = Marshal.ReadInt16(pwmssModuleEpwmAQCTLA_ptr);
                if (out16Val == AQCTLA_SETTING_ON) return true;
                else return false;
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Checks to see if the PWMSS Module Clock is Enabled in the Control Module
        /// </summary>
        /// <returns>true - is enabled, false - is not enabled</returns>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        private bool IsControlModuleClockEnabled()
        {            
            //Console.WriteLine("IsControlModuleClockEnabled");

            // get an integer 0,1,2 which represents the PWMSS module
            uint pwmssModuleNumber = ConvertPWMPortEnumToPWMSSModuleNumber(PWMPort);

            // set up our pointer
            IntPtr cmClockCheck_ptr = new IntPtr(bbbControlModule_ptr.ToInt64() + BBB_CONTROL_PWMSS_CTRLREG);
            // read in our value from the register
            int regVal = Marshal.ReadInt32(cmClockCheck_ptr);
            // mask off the bits we are interested in. 
            int outVal =(regVal & (1 << (int)pwmssModuleNumber));

            //Console.WriteLine("  cmClockCheck_ptr=0x"+cmClockCheck_ptr.ToString("X8"));
            //Console.WriteLine("  regVal=0x" + regVal.ToString("X8"));
            //Console.WriteLine("  outVal=0x" + outVal.ToString("X8"));

            // return the enabled state
            if (outVal != 0) return true;
            return false;
 
        }
            
        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Checks to see if the Clock Module Peripheral Clock is enabled
        /// </summary>
        /// <returns>true - is enabled, false - is not enabled</returns>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        private bool IsClockModulePeripheralClockEnabled()
        {            
            //Console.WriteLine("IsClockModulePeripheralClockEnabled");

            // get an integer 0,1,2 which represents the PWMSS module
            uint pwmssModuleNumber = ConvertPWMPortEnumToPWMSSModuleNumber(PWMPort);

            // get our offset into the Clock Module Peripheral register
            int clkctrlRegOffset = 0;
            if (pwmssModuleNumber == 0) clkctrlRegOffset = BBB_CLOCKMODULE_PERIPHERAL_EPWMSS0_CLKCTRL;
            else if (pwmssModuleNumber == 1) clkctrlRegOffset = BBB_CLOCKMODULE_PERIPHERAL_EPWMSS1_CLKCTRL;
            else if (pwmssModuleNumber == 2) clkctrlRegOffset = BBB_CLOCKMODULE_PERIPHERAL_EPWMSS2_CLKCTRL;
            else
            {
                throw new Exception("Unknown pwmss Module Number: "+ pwmssModuleNumber.ToString());
            }
            // set up our pointer
            IntPtr cmPerClk_ptr = new IntPtr(bbbClockModulePeripheral_ptr.ToInt64() + clkctrlRegOffset);
            // read in our value from the register
            int regVal = Marshal.ReadInt32(cmPerClk_ptr);
            // mask off the bits we are not interested in. 
            int outVal =(regVal & 3);
            // Console.WriteLine("  regVal=0x" + regVal.ToString("X8"));
            // Console.WriteLine("  outVal=0x" + outVal.ToString("X8"));

            // return the enabled state, 2=enabled
            if (outVal == 2) return true;
            return false;
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Enables the Clock Module Peripheral register Clock
        /// </summary>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        private void EnableClockModulePeripheralClock()
        {
            //Console.WriteLine("EnableClockModulePeripheralClock");

            // get an integer 0,1,2 which represents the PWMSS module
            uint pwmssModuleNumber = ConvertPWMPortEnumToPWMSSModuleNumber(PWMPort);

            // get our offset into the Clock Module Peripheral register
            int clkctrlRegOffset = 0;
            if (pwmssModuleNumber == 0) clkctrlRegOffset = BBB_CLOCKMODULE_PERIPHERAL_EPWMSS0_CLKCTRL;
            else if (pwmssModuleNumber == 1) clkctrlRegOffset = BBB_CLOCKMODULE_PERIPHERAL_EPWMSS1_CLKCTRL;
            else if (pwmssModuleNumber == 2) clkctrlRegOffset = BBB_CLOCKMODULE_PERIPHERAL_EPWMSS2_CLKCTRL;
            else
            {
                throw new Exception("Unknown pwmss Module Number: "+ pwmssModuleNumber.ToString());
            }

            // build a pointer to appropriate memory location
            IntPtr cmPerClk_ptr = new IntPtr(bbbClockModulePeripheral_ptr.ToInt64() + clkctrlRegOffset);
            // set the appropriate bits - 2=enable
            Marshal.WriteInt32(cmPerClk_ptr, 2);
            //Console.WriteLine("  cmPerClk_ptr=0x"+cmPerClk_ptr.ToString("X8"));
            //Console.WriteLine("  clkctrlRegOffset=0x" + clkctrlRegOffset.ToString("X8"));
 
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Disables the Clock Module Peripheral register Clock. Be aware that 
        /// two PWM's share a PWMSS module. Disabling this disables it for both
        /// </summary>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        private void DisableClockModulePeripheralClock()
        {
            //Console.WriteLine("DisableClockModulePeripheralClock");

            // get an integer 0,1,2 which represents the PWMSS module
            uint pwmssModuleNumber = ConvertPWMPortEnumToPWMSSModuleNumber(PWMPort);

            // get our offset into the Clock Module Peripheral register
            int clkctrlRegOffset = 0;
            if (pwmssModuleNumber == 0) clkctrlRegOffset = BBB_CLOCKMODULE_PERIPHERAL_EPWMSS0_CLKCTRL;
            else if (pwmssModuleNumber == 1) clkctrlRegOffset = BBB_CLOCKMODULE_PERIPHERAL_EPWMSS1_CLKCTRL;
            else if (pwmssModuleNumber == 2) clkctrlRegOffset = BBB_CLOCKMODULE_PERIPHERAL_EPWMSS2_CLKCTRL;
            else
            {
                throw new Exception("Unknown pwmss Module Number: "+ pwmssModuleNumber.ToString());
            }

            // build a pointer to appropriate memory location
            IntPtr cmPerClk_ptr = new IntPtr(bbbClockModulePeripheral_ptr.ToInt64() + clkctrlRegOffset);
            // set the appropriate bits - 0=disable
            Marshal.WriteInt32(cmPerClk_ptr, 0);
            //Console.WriteLine("  cmPerClk_ptr=0x"+cmPerClk_ptr.ToString("X8"));
            //Console.WriteLine("  clkctrlRegOffset=0x" + clkctrlRegOffset.ToString("X8"));

        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Useful diagnostic function. 
        /// </summary>
        /// <history>
        ///    21 Nov 15  Cynic - Originally written
        /// </history>
        public void DumpRegs()
        {
            short out16Val = Marshal.ReadInt16(pwmssModuleEpwmTBCTL_ptr);
            Console.WriteLine("y: TBCTL=0x" + out16Val.ToString("X4"));
            out16Val = Marshal.ReadInt16(pwmssModuleEpwmCMP_ptr);
            Console.WriteLine("y: CMP=0x" + out16Val.ToString("X4"));
            out16Val = Marshal.ReadInt16(pwmssModuleEpwmTBPRD_ptr);
            Console.WriteLine("y: TBPRD=0x" + out16Val.ToString("X4"));
            out16Val = Marshal.ReadInt16(pwmssModuleEpwmAQCTLA_ptr);
            Console.WriteLine("y: AQCTLA=0x" + out16Val.ToString("X4"));
            out16Val = Marshal.ReadInt16(pwmssModuleEpwmAQCTLB_ptr);
            Console.WriteLine("y: AQCTLB=0x" + out16Val.ToString("X4"));
        }

        // #########################################################################
        // ### Dispose Code
        // #########################################################################
        #region

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Implement IDisposable. 
        /// Dispose(bool disposing) executes in two distinct scenarios. 
        /// 
        ///    If disposing equals true, the method has been called directly 
        ///    or indirectly by a user's code. Managed and unmanaged resources 
        ///    can be disposed.
        ///  
        ///    If disposing equals false, the method has been called by the 
        ///    runtime from inside the finalizer and you should not reference 
        ///    other objects. Only unmanaged resources can be disposed. 
        /// 
        ///  see: http://msdn.microsoft.com/en-us/library/system.idisposable.dispose%28v=vs.110%29.aspx
        /// 
        /// </summary>
        /// <history>
        ///    21 Nov 15 Cynic - Originally written
        /// </history>
        protected override void Dispose(bool disposing)
        {
            //Console.WriteLine("Disposed called");
            // Check to see if Dispose has already been called. 
            if(Disposed==false)
            {
                //Console.WriteLine("  Disposed called Disposed==false");
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing==true)
                {
                    //Console.WriteLine("  Disposed called disposing==true");
                    if (PortIsOpen == true)
                    {
                        ClosePort();
                    }
                    // Dispose managed resources.
                    if (bbbControlModule_ptr != IntPtr.Zero) Syscall.munmap(bbbControlModule_ptr, BBB_CONTROL_LEN);
                    bbbControlModule_ptr = IntPtr.Zero;

                    if (bbbClockModulePeripheral_ptr != IntPtr.Zero) Syscall.munmap(bbbClockModulePeripheral_ptr, BBB_CLOCKMODULE_PERIPHERAL_LEN);
                    bbbClockModulePeripheral_ptr = IntPtr.Zero;

                    if (pwmssModule_ptr != IntPtr.Zero) Syscall.munmap(pwmssModule_ptr, PWMSS_MMAP_LEN);
                    pwmssModule_ptr = IntPtr.Zero;

                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here. If disposing is false, 
                // only the following code is executed.

                // Clean up our code
                //  Console.WriteLine("Disposing PWMPORT");
         
                //Console.WriteLine("  base dispose being called");
                // call the base to dispose there
                base.Dispose(disposing);

            }
        }
        #endregion

    }
}

HTML Code Generated by CoDocker v00.90 Beta