langJapanese
HOME > RT-C Language Controller > Sample "Analog Input Control""
> Sample for RT-C Language Controller <

 Analog Input Control
Here we describe how to get data from analog input board using PCI board control function of RT-C Language Controller standard library "RTC-APILib"
■Equipments
RT-C Language Controller
Analog Input Board:CONTEC AD12-16(PCI)

1. Flow
Let me introduce as an example of "CONTEC AD12-16(PCI)".
Board Settings (※ Execute once when program boots)
(1) Search Board
Get IO port to be used by PciFindDevice() of pcibus class.
UInt16 VenID = 0x1221;    // Vendor ID
UInt16 DevID = 0x8153;    // Device ID
UInt16 DevIndex = 0;      // Board ID
UInt32 BaseAddr;

// Search Board
//
// PciFindDevice
//    UInt16 VendorID        :Vendor ID
//    UInt16 DeviceID        :Device ID
//    UInt16 DeviceIndex     :Board ID
//    UInt16 AddrIndex       :Top of acquired base address
//    UInt16 AddrCount       :Numbers of acquired base address
//    ref UInt32 BaseAddr    :Variable where base address is stored
//
pcibus.PciFindDevice(VenID, DevID, DevIndex, 0, 1, ref BaseAddr);    // Get top of base address

// Get IO port address
IOPort = (UInt16)(BaseAddr & 0xFFFC);
(2) Initialize board
Output "Initialize process command" to IO port to initialize board
// Issue board initialize command
pcibus.OutBYTE((ushort)(IOPort + 0x0008), 0);
(3) Set Sampling temrs
After outputing "setting command of sampling terms" to IO port, output setting data ([input mode][Channel mode][clock source to be used][sampling mode]).

Byte bAIMode = 0;        // Input mode                 0:Single end         1:Differential
Byte bChMode = 1;        // Channel mode          0:Single            1:Multi
Byte bSmpClok = 0;       // Clock source to be used    0:Internal clock     1:External clock
Byte bSmpMode = 0;       // Sampling mode         0:Manual Issue       1:Automatic Issue

// +----+----+----+----+----------+----------------+-----------------------+------------------+
// | D7 | D6 | D5 | D4 |    D3    |       D2       |        D1             |        D0        |
// +----+----+----+----+----------+----------------+-----------------------+------------------+
// | -- | -- | -- | -- |Input mode|Channel mode   |Clock source to be used|Sampling mode     |
// +----+----+----+----+----------+----------------+-----------------------+------------------+
Byte byDATA = (Byte)((bAIMode << 0x03) | (bChMode << 0x02) | (bSmpClok << 0x01) | bSmpMode);

// Issue command of Sampling temrs reset
pcibus.OutBYTE((ushort)(IOPort + 0x0008), 1);
// Write Sampling temrs
pcibus.OutBYTE((ushort)(IOPort + 0x000C), byDATA);
(4) Set Range Width
After outputting "Setting command of Input Range" to IO port, output settings of Input Range per each channel.
// 0x00:±10[V]、0x01:±5[V]、0x02:±2.5[V]、0x03:±1.25[V]、
// 0x04:0-10[V]、0x05:0-5[V]、0x06:0-2.5[V]、0x07:0-1.25[V]
Byte bRange = 0x00;

// Issue command of Range setting
pcibus.OutBYTE((ushort)(IOPort + 0x0008), 2);

// process of Range setting
for (Byte i = 0; i < bUseCh; i++)
{
    // Set CH to be set
    pcibus.OutBYTE((ushort)(IOPort + 0x000C), i);
    // Set range width
    pcibus.OutBYTE((ushort)(IOPort + 0x000D), bRange);
}
(5) Set interrupt mask
After outputting "Setting command of Interrupt factor" to IO port, output settings of factor for interrupt signal occurrance.
※ All must be prohibited.
Byte bFcr0 = 0xFF;        // Interrupt mask0    ※Bit Unit (0:Occur 1:Prohibit)
Byte bFcr1 = 0xFF;        // Interrupt mask1    ※Bit Unit (0:Occur 1:Prohibit)

// Issue setting command of Interrupt mask
pcibus.OutBYTE((ushort)(IOPort + 0x0008), 4);

// Set Interrupt mask
pcibus.OutBYTE((ushort)(IOPort + 0x000C), bFcr0);
pcibus.OutBYTE((ushort)(IOPort + 0x000D), bFcr1);

Data Sampling (※ Always executing in operation)
(6) Issue Sampling start command
Output "Sampling start command" to IO port.
if sampling mode is software command, it takes a sampling to the designated channel.
Byte bUseCh = 16;        //CH numbers to be used  1~16 ※1~8 in differential

// Clear analog input status
pcibus.InBYTE((ushort)(IOPort + 0x0006));

// Issue Sampling start command
pcibus.OutBYTE((ushort)(IOPort + 0x0004), bUseCh - 1));    // Issue CH0~CH to be used
(7) Wait for sampling
Get "analog input status" from IO port, check the state of "DRE(DataReadEnable:0x02)"
If DRE is ON, you can get sampling data.
//------------------------------------------------------------------------
// Check analog input data to confirm whether DRE(DataReadEnable:0x02) is ON or not.
//------------------------------------------------------------------------

// Check analog input status
if (pcibus.InBYTE((ushort)(IOPort + 0x0006)) & 0x02)
{
    // Complete sampling
}
(8) Get Data
Get "analog input data" from IO port.
// Get data of CH0~CH to be used
for (Byte i = 0; i < bUseCh; i++)
{
    // Get Sampling Data
    wData[i] = (UInt16)(pcibus.InHWORD((ushort)(IOPort + 0x0000)) & 0x0FFF);
}
※ Repeat (6)~(8)

2. Sample Program(C# Source Code)
This sample runs 2 tasks such as【Main Process】which does initialization and does arbitrary processes & 【Sampling Process】which gets input data from board and store it to memory area.
■Main Process:
It does board settings and secures memory area to store the input data when program boots.
You can know instantaneous value acquired by sampling process and stored data in the memory area.
Main Process
■Sampling Process:
After completing board settng in main process, it starts sampling of input data.
it gets input data from board and stores raw values and voltage values to memory area while program is running.
Sampling Process
    // *** Constant ***

    /// <summary>
    /// Input Range
    /// </summary>
    public enum RANGE : byte
    {
        B_1000 = 0x00,       // Bipolar:-10 .. 10[V]
        B_0500 = 0x01,       // Bipolar:-5 .. 5[V]
        B_0250 = 0x02,       // Bipolar:-2.5 .. 2.5[V]
        B_0125 = 0x03,       // Bipolar:-1.25 .. 1.25[V]
        U_1000 = 0x04,       // Unipolar:0 .. 10[V]
        U_0500 = 0x05,       // Unipolar:0 .. 5[V]
        U_0250 = 0x06,       // Unipolar:0 .. 2.5[V]
        U_0125 = 0x07        // Unipolar:0 .. 1.25[V]
    }

    /// <summary>
    /// Define global variables
    /// </summary>
    public unsafe class GlobalVar
    {
        public static Boolean inExe;            // Request Execution
        public static Boolean inBusy;           // Flag for Running
        public static Boolean inErr;            // Fral for In Error
        public static UInt32 inErrCode;         // Error Code
        public static UInt16 IOPort = 0;        // IO port of analaog board
        public static Single fLo;               // Voltage of minimum input
        public static Single fHi;               // Voltage of maximum input
        public static UInt32 uiPos = 0;         // Stored position of sampling data
        public static UInt32 uiOvr = 0;         // Buffer over flag for sampling data
        public static void* lpData = null;      // Buffer for sampling data raw value
        public static void* lpVolt = null;      // Buffer for sampling data[V]
        public static UInt32 uiBuffSize;        // Maximum buffer counts
        public static UInt16 wData;             // Buffer for sampling data instantaneous value
        public static Single fData;             // Sampling data [V]instantaneous value
    }

    /// <summary>
    /// Define board setting value
    /// </summary>
    public unsafe class Board
    {
        // Target Board
        public static UInt16 VenID = 0x1221;            // CONTEC
        public static UInt16 DevID = 0x8153;            // AD12-16(PCI)
        public static UInt16 DevIndex = 0;              // Board ID:0
        public static Byte bMaxCh = 16;                 // Board CH number
        public static UInt16 Resolution = 0x0FFF;       // Resolution

Byte bAIMode = 0;        // Input mode                 0:Single end         1:Differential
Byte bChMode = 1;        // Channel mode          0:Single            1:Multi
Byte bSmpClok = 0;       // Clock source to be used    0:Internal clock     1:External clock
Byte bSmpMode = 0;       // Sampling mode         0:Manual Issue       1:Automatic Issue


        // Smapling terms Setting value
        public static Byte bAIMode = 0;                 // Input mode                 0:Single end       1:Differential
        public static Byte bChMode = 1;                 // Channel mode          0:Single           1:Multi
        public static Byte bSmpClok = 0;                // Clock source to be used    0:Internal clock   1:External clock
        public static Byte bSmpMode = 0;                // Sampling mode         0:Manual Issue     1:Automatic Issue
        public static Byte bUseCh = 1;                  // CH numbers to be used
                                                            ※In case of Differential input, half of board CH is maximum.
        // Range Width Setting value
        public static Byte bRange = (Byte)RANGE.B_1000; // Input Range
        // Inerrupt Mask Setting value
        public static Byte bFcr0 = 0xFF;                // Interrupt mask0    ※Bit Unit (0:Occur 1:Prohibit)
        public static Byte bFcr1 = 0xFF;                // Interrupt mask1    ※Bit Unit (0:Occur 1:Prohibit)
    }
    /// <summary>
    /// *****************************************************************************************************
    ///  Main Process Block
    /// *****************************************************************************************************
    /// </summary>
    [FUNCTION_BLOCK]
    public class _main
    {
        // *** Internal Variables ***
        private RtMemory rmData;    // Memory for Sampling Data
        private RtMemory rmVolt;    // Memory for Sampling Data[V]


        // *** Internal Variables ***
        /// <summary>
        /// Search Target board
        /// </summary>
        private Boolean FindBoard()
        {
            UInt32 BaseAddr = 0;

            if (pcibus.PciFindDevice(Board.VenID, Board.DevID, Board.DevIndex, 0, 1, ref BaseAddr) == false)
            {
                return false;
            }
            GlobalVar.IOPort = (UInt16)(BaseAddr & 0xFFFC);
            return true;
        }

        /// <summary>
        /// Intialize board
        /// </summary>
        private void InitalizeBord()
        {
            pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x0008), 0);
        }

        /// <summary>
        /// Set Sampling terms
        /// </summary>
        private void SetSampling()
        {
            // +----------+----------------+--------------------------+------------------+
            // |    D3    |       D2       |            D1            |        D0        |
            // +----------+----------------+--------------------------+------------------+
            // |Input mode|Channel mode   |Sampling Clock source     |Sampling mode     |
            // +----------+----------------+--------------------------+------------------+
            Byte byDATA = (Byte)(
                        (Board.bAIMode << 0x03)
                      | (Board.bChMode << 0x02)
                      | (Board.bSmpClok << 0x01)
                      | Board.bSmpMode
                          );

            pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x0008), 0x01);      // Issue set command for Sampling terms
            pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x000C), byDATA);    // Write Sampling terms

        }

        /// <summary>
        /// Set range width
        /// </summary>
        private void SetRange()
        {
            // Issue set command for range
            pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x0008), 0x02);

            // Process for range setting
            for (Byte i = 0; i < Board.bUseCh; i++)
            {
                pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x000C), i);                     // Set CH to be set
                pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x000D), (byte)Board.bRange);    // Set range width
            }
            // Set Voltage of minimum input / Voltage of maximum input
            switch ((byte)Board.bRange)
            {
                case (Byte)RANGE.B_1000:
                    GlobalVar.fLo = -10.0f;
                    GlobalVar.fHi = 10.0f;
                    break;
                case (Byte)RANGE.B_0500:
                    GlobalVar.fLo = -5.0f;
                    GlobalVar.fHi = 5.0f;
                    break;
                case (Byte)RANGE.B_0250:
                    GlobalVar.fLo = -2.5f;
                    GlobalVar.fHi = 2.5f;
                    break;
                case (Byte)RANGE.B_0125:
                    GlobalVar.fLo = -1.25f;
                    GlobalVar.fHi = 1.25f;
                    break;
                case (Byte)RANGE.U_1000:
                    GlobalVar.fLo = 0.0f;
                    GlobalVar.fHi = 10.0f;
                    break;
                case (Byte)RANGE.U_0500:
                    GlobalVar.fLo = 0.0f;
                    GlobalVar.fHi = 5.0f;
                    break;
                case (Byte)RANGE.U_0250:
                    GlobalVar.fLo = 0.0f;
                    GlobalVar.fHi = 2.5f;
                    break;
                case (Byte)RANGE.U_0125:
                    GlobalVar.fLo = 0.0f;
                    GlobalVar.fHi = 1.25f;
                    break;
            }

        }

        /// <summary>
        /// Set Interrupt mask
        /// </summary>
        private void SetIntrMask()
        {
            // Issue set command for Interrupt mask
            pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x0008), 0x04);

            // Set Interrupt mask
            pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x000C), Board.bFcr0);
            pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x000D), Board.bFcr1);
        }



        /// <summary>
        /// PLC Program - Process in download
        /// </summary>
        public unsafe _main()
        {

            // Analog board setting
            if (!FindBoard())      // Search analog board
            {
                GlobalVar.inErr = true;     // Error
                GlobalVar.inErrCode = 1;    // Board
                return;
            }
            InitalizeBord();        // Initialize board
            SetSampling();          // Set Sampling terms
            SetRange();             // Set Range Width
            SetIntrMask();          // Set Interrupt mask

            IecString80 sMemName = new IecString80();
            sMemName.ctor();

            GlobalVar.uiBuffSize = 4096;

            // Secure area to save sampling data
            sMemName.s.Init(\"RTC_AI_Data\");
            rmData = new RtMemory(4096 * 2, (byte)HandleSelection.ROOT_PROCESS, sMemName);
            if (rmData.GetLastErr() != 0)
            {
                IecString80 sProcName = new IecString80();
                sProcName.ctor();
                sProcName.s.Init(\"\");
                rmData = new RtMemory(sProcName, sMemName);
                if (rmData.GetLastErr() != 0)
                {
                    GlobalVar.inErr = true;
                    GlobalVar.inErrCode = rmData.GetLastErr();
                    return;
                }
            }

            // Secure area to save sampling data[V]
            sMemName.s.Init(\"RTC_AI_Volt\");
            rmVolt = new RtMemory(4096 * 4, (byte)HandleSelection.ROOT_PROCESS, sMemName);
            if (rmVolt.GetLastErr() != 0)
            {
                IecString80 sProcName = new IecString80();
                sProcName.ctor();
                sProcName.s.Init(\"\");
                rmVolt = new RtMemory(sProcName, sMemName);
                if (rmVolt.GetLastErr() != 0)
                {
                    GlobalVar.inErr = true;
                    GlobalVar.inErrCode = rmVolt.GetLastErr();

                    rmData.Delete();        // Release data area
                    rmData = null;
                    return;
                }
            }

            // Initialize global variables
            GlobalVar.inErr = false;
            GlobalVar.inErrCode = 0;
            GlobalVar.lpData = rmData.GetMemPtr();
            GlobalVar.lpVolt = rmVolt.GetMemPtr();
            GlobalVar.uiPos = 0;
            GlobalVar.uiOvr = 0;
        }

        /// <summary>
        /// PLC Program - Process in reset
        /// </summary>
        ~_main()
        {
            if (rmData != null)
            {
                rmData.Delete();        // Release data area
                rmData = null;
            }
            if (rmVolt != null)
            {
                rmVolt.Delete();        // Release data area
                rmVolt = null;
            }
        }

        /// <summary>
        /// PLC Program - Process in start
        /// </summary>
        public void __Init()
        {
            GlobalVar.inExe = false;
        }

        /// <summary>
        /// PLC Program - Process in executes
        /// </summary>
        public unsafe void __Process()
        {
            if (GlobalVar.inErr)
            {
                GlobalVar.inExe = false;       // Execute request OFF
                return;
            }

            if (GlobalVar.inExe != true)       // If Execute request is not issued
            {
                GlobalVar.inExe = true;        // Execute request ON and start sampling
            }


            /* --- Write ToDo here --- */
        }
    }
    /// <summary>
    /// *****************************************************************************************************
    ///  Data Sampling Process Block
    /// *****************************************************************************************************
    /// </summary>
    [FUNCTION_BLOCK]
    public class _sampling
    {
        // *** Constant ***
        private const UInt16 NEXT_STAGE = 0x0000;
        private const UInt16 BREAK_STAGE = 0xFFFF;
        private enum STAGE : ushort
        {
            _00 = 0,        // Issue Sampling start command
            _01 = 1,        // Wait for sampling
            _02 = 2         // Get Data
        }


        // *** Internal Variables ***
        private STAGE m_NextStage;


        // *** Internal Variables ***

        /// <summary>
        /// Stage00 : Issue Sampling start command
        /// </summary>
        private UInt16 STAGE_00(ref STAGE stNext)
        {
            // If Execute Flag is OFF, complete stage process.
            if (GlobalVar.inExe == false) return BREAK_STAGE;

            // Clear analog input status
            pcibus.InBYTE((ushort)(GlobalVar.IOPort + 0x0006));

            // Issue Sampling start command
            pcibus.OutBYTE((ushort)(GlobalVar.IOPort + 0x0004), (byte)(Board.bUseCh - 1));    // CH0~CH to used

            stNext = STAGE._01;    // NEXT: Go to sampling waiting stage

            // Go to next stage
            return NEXT_STAGE;
        }

        /// <summary>
        /// Stage01 : sampling waiting
        /// </summary>
        private UInt16 STAGE_01(ref STAGE stNext)
        {
            // If Execute Flag is OFF, complete stage process.
            if (GlobalVar.inExe == false) return BREAK_STAGE;

            //------------------------------------------------------------------------
			// Check analog input data to confirm whether DRE(DataReadEnable:0x02) is ON or not.
            // "DRE ON means to complete sampling.
            //------------------------------------------------------------------------
            // Check analog input status
            if (0 == (0x02 & pcibus.InBYTE((ushort)(GlobalVar.IOPort + 0x0006))))
            {
                return BREAK_STAGE;    // brake for same stage is complete since it is in sampling
            }

            // Complete sampling
            stNext = STAGE._02;    // NEXT: Data get stage

            // Go to next stage
            return NEXT_STAGE;
        }

        /// <summary>
        /// Stage02 : Get data
        /// </summary>
        private unsafe UInt16 STAGE_02(ref STAGE stNext)
        {
            // get data from CH0~designated CH
            for (Byte i = 0; i < Board.bUseCh; i++)
            {
                // Get Sampling data
                GlobalVar.wData = (UInt16)(pcibus.InHWORD(GlobalVar.IOPort) & 0x0FFF); // Get Sampling data
                ((UInt16*)GlobalVar.lpData)[GlobalVar.uiPos] = GlobalVar.wData;        // Store acquired data

                // Get Sampling data[V]
                GlobalVar.fData = CalcInputVolt(GlobalVar.wData);               // Change acquired data to voltage value
                ((Single*)GlobalVar.lpVolt)[GlobalVar.uiPos] = GlobalVar.fData; // Store changed data to memory

                // Refresh next write position
                GlobalVar.uiPos = (GlobalVar.uiPos + 1) % GlobalVar.uiBuffSize;
                if (GlobalVar.uiPos == 0)
                {
                    GlobalVar.uiOvr=1;    // Over flag ON since write to buffer end.
                }
            }
            stNext = STAGE._00;    // NEXT: First stage

            // Since complete getting data, finish stage process
            return BREAK_STAGE;
        }

        /// <summary>
        /// Calculate input voltage
        /// </summary>
        private Single CalcInputVolt(UInt16 wData)
        {
            //                     Input Data
            // Input Voltage[V] = (───── * (Output Voltage[V] - Min Voltage[V])) + Max Volage[V]
            //                      Resolution
            return (((Single)wData / (Single)Board.Resolution) * (GlobalVar.fHi - GlobalVar.fLo)) + GlobalVar.fLo;
        }


        /// <summary>
        /// PLC Program - Process in download
        /// </summary>
        public _sampling()
        {
        }

        /// <summary>
        /// PLC Program - Process in reset
        /// </summary>
        ~_sampling()
        {
        }

        /// <summary>
        /// PLC Program - Process in start
        /// </summary>
        public unsafe void __Init()
        {
            GlobalVar.inBusy = false;   // Execute Flag OFF
            m_NextStage = STAGE._00;    // Set first stage
        }

        /// <summary>
        /// PLC Program - Process in execute
        /// </summary>
        public unsafe void __Process()
        {
            UInt16 wRet;
            UInt16 woLoopCount;

            // Sampling is not executed when error is occurring
            if (GlobalVar.inErr)
            {
                GlobalVar.inBusy = false;
                return;
            }
            // Sampling is not executed when buffer is not get
            if (GlobalVar.lpData == null)
            {
                GlobalVar.inBusy = false;
                return;
            }

            // Refresh Execute Flag according to execution request
            if (GlobalVar.inBusy != GlobalVar.inExe)
            {
                GlobalVar.inBusy = GlobalVar.inExe;
            }

            // If there is an execution request, start stage process
            if (GlobalVar.inExe)
            {
                for(woLoopCount = 0; woLoopCount < 1000; woLoopCount++)
                {
                    switch (m_NextStage)
                    {
                        case STAGE._00:
                            wRet = STAGE_00(ref m_NextStage);    // Stage00:Issue Sampling start command

                            break;
                        case STAGE._01:
                            wRet = STAGE_01(ref m_NextStage);    // Stage01:Wait for sampling
                            break;
                        case STAGE._02:
                            wRet = STAGE_02(ref m_NextStage);    // Stage02:Get Data
                            break;
                        default:
                            wRet = BREAK_STAGE;                  //
                            break;
                    }

                    if (wRet == BREAK_STAGE)
                    {
                        break;    // Complete stage process
                    }
                }
            }
            return;
        }
    }