Page 1 of 1

Kogna Analog Values

Posted: Sat Apr 15, 2023 3:04 pm
by jtremel
I am getting my first testing in with the new Kogna Board (this board is AWESOME - thanks!!!).

I can't find the Kogna's ADC and DAC values in the bulk status update. Am I missing something here?

I know I can read Kogna's ADC with a "ADCx" console query (with x being 8,9,10 or 11). Should this also be updated in CurrentStatus.ADC[x]?

I haven't figured out how to query the DAC setting through the .NET interface yet. What is the best way to continually query these from a .NET application.

Thanks - and I am really excited about this new board.

Re: Kogna Analog Values

Posted: Sat Apr 15, 2023 11:40 pm
by jtremel
After digging into this a bit deeper, I realized the Kogna ADC and DAC values are indeed uploaded with the "GetStatus" command, they are just not parsed and exposed through the MainStatus structure in the Kmotion_dotNET dll (unless I am missing something).

To workaround this I have:
- made a C# structure to replicate the MAIN_STATUS struct in PC-DSP.h
- periodically request "GetStatus" to upload the binary data in Hex string format
- convert the hex string into a continuous byte array
- marshal the byte array into the C# KognaMainStatus structure

This seems to be working well. I have played with setting various parameters in the Kmotion.exe GUI and seeing the changes being marshalled correctly in my C# data structure.

I am sure I have a few bugs to work out yet, but in case it is of interest...

The marshalling code:

Code: Select all

            //above this point the main_status_byte_array was populated by parsing the string returned from KM.WriteLine("GetStatus")

            // Pin the managed memory, marshal it into the structure, and the free it back up
            GCHandle handle = GCHandle.Alloc(main_status_byte_array, GCHandleType.Pinned);
            KognaMainStatus KogMainStat = (KognaMainStatus)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(KognaMainStatus));
            handle.Free();
My C# definition of the KognaMainStatus derived from PC-DSP.h

Code: Select all

        public const int N_PC_COMM_PERSIST = 8;

        public const int N_DACS = 8;         // Kanalog
        public const int N_ADCS = 8;
        public const int N_PWMS = 8;
        public const int N_ENCS = 8;
        public const int N_ENCS_KOGNA = 20;
        public const int N_DACS_KOGNA = 8;   // referenced after Kanalog as 8-15
        public const int N_ADCS_KOGNA = 4;   // referenced after Kanalog as 8-11

        public const int N_ADCS_SNAP= 8;      // per snap amp 
        public const int N_PWMS_SNAP =4;      // per snap amp
        public const int N_ENCS_SNAP =4;

        public const int N_CHANNELS_KOGNA = 16;
        public const int N_IO_PWMS = 8;
        public const int N_KOGNA_HRPWM = 4;

        public struct KognaMainStatus
        {

            int VersionAndSize;   //bits 16-23 = version, bits 0-15 = size in words
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_ADCS+2* N_ADCS_SNAP)] int[] ADC;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_DACS)] int[] DAC;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_PWMS + 2 * N_PWMS_SNAP)] int[] PWM;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_CHANNELS_KOGNA)] double[] Position;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_CHANNELS_KOGNA)] double[] Dest;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_CHANNELS_KOGNA)] byte[] OutputChan0;

            int InputModes;      // 4 bits for each axis (not sure why only using 16 of 32 bits) 
            int InputModes2;     // 4 bits for each axis 
            int InputModes3;     // 4 bits for each axis 
            int InputModes4;     // 4 bits for each axis 
            int OutputModes;     // 4 bits for each axis 
            int OutputModes2;    // 4 bits for each axis 
            int OutputModes3;    // 4 bits for each axis 
            int OutputModes4;    // 4 bits for each axis 
            int Enables;         // 1 bit  for each axis 
            int AxisDone;        // 1 bit  for each axis 

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] int[] BitsDirection;   // KFLOP - 64 bits of I/O direction 1 = output
            int BitsDirection200;                                                       // Kogna - 24 bits 200-223 of I/O direction 1 = output
            int BitsDirection280;                                                       // Kogna - 10 bits 280-289 of I/O direction 1 = output
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] int[] BitsState;       // KFLOP - 64 bits of state lsb=I/O bit0
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] int[] BitsState200;    // Kogna - 90 bits 200-289 of state lsb=I/O bit200
            int PinMuxModes;                                                            // Kogna - 10 Pin function modes 2 bits per pin 4 HRPWM + 6 SPI

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_ADCS_KOGNA)] short[] Kogna_ADC;   //  format  12 bits data (signed extended to 16 bits)
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_DACS_KOGNA)] short[] Kogna_DAC;   //  format  12 bits data

            byte Kogna_PWM_Prescale;                  // Prescale sets frequency of all 8 8-bit PWMs

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_IO_PWMS)] byte[] Kogna_PWM;                // current pulse settings
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_IO_PWMS)] byte[] Kogna_PWM_Enables;        // PWM Enables to control Pin?

            UInt16 HRPWMPeriod01;  // Kogna HRPWMs Periods
            UInt16 HRPWMPeriod2;
            UInt16 HRPWMPeriod3;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_KOGNA_HRPWM)] UInt16[] HRPWM;    // Kogna HRPWMs Pulse widths

            int SnapBitsDirection0;   // Snap - 32 bits of I/O direction 1=output 16-29 GPIO only, Card 0
            int SnapBitsDirection1;   // Snap - 32 bits of I/O direction 1=output 16-29 GPIO only, Card 1
            int SnapBitsState0;       // Snap - 32 bits of state  16-29 GPIO 0-7 Diff 8-15 OPTO, Card 0
            int SnapBitsState1;       // Snap - 32 bits of state  16-29 GPIO 0-7 Diff 8-15 OPTO, Card 1

            int KanalogBitsStateInputs;   // Kanalog - 16 bits 128-143
            int KanalogBitsStateOutputs;  // Kanalog - 24 bits 144-167

            int RunOnStartUp;    // word Bits 1-7 selects which threads to execute on startup   

            int ThreadActive;           // one bit for each thread, 1=active, bit 0 - primary
            int StopImmediateState;    // Status of Stop Coordinated Motion Immediately

            double TimeStamp;   // Time in seconds (since KFlop boot) this status was aquired
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = N_PC_COMM_PERSIST)] int[] PC_comm;// 8 persist Variables constantly uploaded to send misc commands/data to PC

            UInt32 VirtualBits;        // Virtual I/O bits simulated in memory
            UInt32 VirtualBitsEx0;     // only upload 32 1024 Expanded Virtual Bits 
        }

Re: Kogna Analog Values

Posted: Sun Apr 16, 2023 4:32 pm
by jtremel
For completeness sake I am adding the code below that parses the "GetStatus" query into the managed KognaMainStatus structure.

Code: Select all

 /* getBulkStatusUpload
         * --------------------------------------------------------------------------------------------------------------------------------------------
         * Commonly needed status information can be uploaded from the dynomotion board in bulk as a single binary image with a call to "GetStatus"
         * The binary data is formatted /packed into a MAIN_STATUS structure that is defined in PC-DSP.h
         * 
         * The data is tranmitted over several strings (each terminated with a CR).
         * Each string contains (up to 16) continous 32-bit words in hexidecimal format. Each word is delimited by a space character.
         * Below is an example of the upload data recieved from a Kogna Board (version 5.0.6).
         * In Kogna ver5.0.6, the data is 174 words long, spread over 11 seperature strings
         * Note that the first word (0x013500AE in this example), contains the version and size of the returned data.  This will be used to check validity.
         * 
         * 013500AE FFFFF800 FFFFF800 FFFFF800 FFFFF800 FFFFF800 FFFFF800 FFFFF800 FFFFF800 00000000 00000000 00000000 00000000 00000000 00000000 00000000
         * 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
         * 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
         * 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
         * 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 BFF00000 00000000 00000000
         * 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
         * 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
         * 00000000 00000000 06040200 0E0C0A08 0E0E0E0E 0E0E0E0E 00001111 00001111 00001111 00001111 00000000 00000000 00000000 00000000 00000000 00000000
         * 00000000 0000C000 00000000 00000000 00000000 FFFFC000 FE000000 000177F7 03600000 000A0000 00DD0000 0137044C 00000199 00000000 00000000 00000000
         * 00000000 00000000 00000000 00000000 0FFF0000 10001000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
         * 00000000 00000000 1CD43A38 40F24F2B 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000FFFF FFFFFFFF
         * 
         -------------------------------------------------------------------------------------------------------------------------------------------------*/
        /// <summary>
        /// requests a bulk status upload from the Kogna/Kflop board, parses into binary data, and attempts to marshal the raw data into a KognaMainStatus 
        /// structure that mirrors the format of the structure defined in PC-DSP.h
        /// If the call is successful, "true" will be returned and the main_status_to_populate object will be populated from the bulk status data.  
        /// If the call fails, "false" will be returned and main_status_to_populate object will remain untouched
        /// </summary>

        private bool getBulkStatusUpload(ref KognaMainStatus main_status_to_populate)
        {
            const int STAT_VERSION = 309;                               // the version of MAIN_STATUS from PC-DSP.h
            const int num_words_expected = 174;                         // the number of words returned from the GetStatus call
            int num_words_processed = 0;                                // var to keep track of the number of words we have parsed 
            UInt32[] status_words = new UInt32[num_words_expected];     // array to hold words parsed from bulk status upload

            if (verifylKognaStructureSize(num_words_expected) == false) // verify size of our internal Kogna Structure, this could be commented out after prove-out
                return false;                                                   

            if (KM.WaitToken(100) != KMOTION_TOKEN.KMOTION_LOCKED)      // get lock to prevent outside access to the board while we build / gather the status upload
                return false;

            try
            {
                KM.WriteLine("GetStatus");                                  // send command to query the bulk status:
                while (num_words_processed < num_words_expected)            // loop through the returned strings and parse out the binary data
                {
                    string strline = "";                                    // string to hold the next line of the status update
                    bool read_ok = KM.ReadLineTimeout(ref strline, 5000);   // get the next line of hexidecimal words from the GetStatus call
                    if (read_ok != true)                                    // exit routine if there was a readline timeout
                    {
                        MessageBox.Show("Read Timeout in GetStatus");
                        KM.ReleaseToken();
                        return false;
                    }

                    string[] strWords = strline.Trim().Split(' ');              // split the current line into an array of strings, each element being one hexidecimal word 
                    for (int i = 0; i < strWords.Length; i++)                   // loop through each word in the array
                    {
                        UInt32 currentWord = Convert.ToUInt32(strWords[i], 16); // parse the value from the formatted string
                        status_words[num_words_processed] = currentWord;        // add it to our array
                        num_words_processed++;                                  // incriment the number of words processed counter
                        if (num_words_processed == 1)                           // check version and size info from the bulk upload if this is the first word 
                        {
                            if (checkMainStatusVersionAndSize(STAT_VERSION, num_words_expected, currentWord) == false)
                            {
                                KM.ReleaseToken();                              // release token
                                return false;                                   // and exit routine if there is a version mismatch    
                            }
                        }
                    }
                }

                KM.ReleaseToken();                                                                                                          // release the lock on the board
                GCHandle handle = GCHandle.Alloc(status_words, GCHandleType.Pinned);                                                        // pin the managed array so it can be marshalled
                main_status_to_populate = (KognaMainStatus)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(KognaMainStatus));    // marshal the data into the main_status_to_populate object
                handle.Free();                                                                                                              // free the pinned object           
                return true;                                                                                                                // return true, indicating success
            }
            catch (Exception)
            {
                return false;  //todo, capture exception message and report
            }
        }

        /// <summary>
        /// checks the Version and Size word caputured during the GetStatus queruy against expected values
        /// </summary>
        private bool checkMainStatusVersionAndSize(int expectedVersion, int expectedWords, UInt32 VersionAndSizeWord)
        {
            uint num_words_from_upload = VersionAndSizeWord & 0xFFFF;         // size (in words) is bits 0-15
            uint version_from_upload = (VersionAndSizeWord & 0xFFFF0000)>>16; // version is bits 16-31

            // check size, error if we don't get expected value
            if (num_words_from_upload != expectedWords)
            {
                MessageBox.Show("Error in size check of GetStatus upload");
                return false;
            }

            // check size, error if we don't get expected value
            if (version_from_upload != expectedVersion)
            {
                MessageBox.Show("Error in version check of GetStatus upload");
                return false;
            }
            return true;
        }

        /// <summary>
        /// checks the size of our internal KognaMainStatus structure against the known from PC-DSP.h 
        /// </summary>
        private bool verifylKognaStructureSize(int expectedWords)
        {
            // check the size of our MainStatus structure.  For Kogna - Kmotion5.0.6 this will/should be 696 bytes 
            if (Marshal.SizeOf<KognaMainStatus>() != expectedWords * 4)
            {
                MessageBox.Show("Size of C# KognaMainStatus structure does not equal expected value");
                return false;
            }
            return true;
        }

Re: Kogna Analog Values

Posted: Mon Apr 17, 2023 7:04 pm
by TomKerekes
Hi Jim,

Sorry for the late reply.

You are correct the new Kogna bulk status fields were not exposed to the .NET interface.

Your method seems more efficient than than the method we used. We will incorporate something similar in the next release.

Thanks!