'=================================================================== ' ttisc8if.bas v1.1 '=================================================================== ' ' Interface module to Tom's RC SC8000 RC Servo Interface ' ' Copyright (C) 2005 David Buckley, dbuckley at tex dot com ' ' This program is free software; you can redistribute it and/or ' modify it under the terms of the GNU General Public License ' as published by the Free Software Foundation; either version 2 ' of the License, or (at your option) any later version. ' ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY; without even the implied warranty of ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ' GNU General Public License for more details.' ' ' You should have received a copy of the GNU General Public License ' along with this program; if not, write to the Free Software ' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ' MA 02110-1301, USA. '=================================================================== ' ' Compile with PowerBuilder 7.04 or later ' ' v1.1 changes the exported defintion of SC8_CleanUp() to SC8_CleanUP ' (note case change of 'P') to agree with documentation. ' '=================================================================== ' ' Sizes of integers for building header interfaces: ' ' PB long is VB.NET and Delphi Integer (4 byte, 32 bit) ' PB Integer (or word) is VB.NET Short (2 bytes, 16 bit) ' '=================================================================== $DIM ALL $COMPILE DLL #INCLUDE "Win32API.inc" '=================================================================== ' Global data '=================================================================== ' ' This is a structure that holds two things, the handle of the serial ' port connected to a SC8000, and what the current set of digital ' output bits are, as we need to know the current state of these for ' every transmission. ' ' If the serial port handle is zero, then the port is unused. ' FREEFILE never RETURNS zero, so this is safe. ' ' '=================================================================== %MAXBOARDS = 4 TYPE Boarddata fh AS LONG ' Handle of port portnum AS LONG ' COMn digitalmask AS BYTE ' persistent END TYPE GLOBAL board() AS boarddata ' Dimensioned at startup '=================================================================== ' getcph() '=================================================================== ' ' Returns pointer to entry of the Board structure. Checks the ' incoming BoardNum for validity first. ' ' '=================================================================== FUNCTION getcph(BYVAL BoardNum AS LONG) AS LONG IF BoardNum <= 0 OR BoardNum > %MAXBOARDS THEN EXIT FUNCTION 'returns zero FUNCTION = VARPTR(board(BoardNum)) END FUNCTION '=================================================================== ' commclear() '=================================================================== ' ' Empties commport receive buffer ' '=================================================================== SUB commclear(BYVAL fh AS LONG) DIM bytes AS LONG DIM junk AS STRING bytes = COMM(#fh, RXQUE) IF bytes = 0 THEN EXIT SUB COMM RECV #fh, bytes, junk END SUB '=================================================================== ' SC8_Initialize() '=================================================================== ' ' int SC8_Initialize(int PortNum); ' ' ' If the call is successful, the Board Number will exit a value ' starting with 1 (then 2, 3, etc... for subsequent boards). ' ' If the communication fails (ex: not connected). The exit value is 0. ' ' The PortNum parameter is the COM port value. ' '=================================================================== FUNCTION SC8_Initialize SDECL ALIAS "SC8_Initialize" (BYVAL PortNum AS LONG) EXPORT AS INTEGER DIM commport AS STRING REGISTER fh AS LONG REGISTER cphindex AS LONG IF PortNum = 0 THEN EXIT FUNCTION ' See if we havel aready opened this port, and if so, ignore. FOR cphindex = 1 TO %MAXBOARDS IF board(cphindex).portnum = PortNum THEN EXIT FUNCTION NEXT ' Find an available slot, or return zero FOR cphindex = 1 TO %MAXBOARDS IF board(cphindex).fh = 0 THEN EXIT FOR IF cphindex = %MAXBOARDS THEN EXIT FUNCTION NEXT ' we now have an available slot ' attempt to open COMx fh = FREEFILE commport = "COM" & LTRIM$(STR$(portNum)) TRY COMM OPEN commport AS #fh COMM SET #fh, BAUD = 9600 COMM SET #fh, BYTE = 8 COMM SET #fh, PARITY = 0 COMM SET #fh, STOP = 0 ' thats one stop bit COMM SET #fh, TXBUFFER = 512 COMM SET #fh, RXBUFFER = 512 COMM SET #fh, CTSFLOW = 0 COMM SET #fh, RTSFLOW = 0 COMM SET #fh, XINPFLOW = 0 COMM SET #fh, XOUTFLOW = 0 CATCH COMM CLOSE #fh EXIT FUNCTION ' will return zero. END TRY ' All ok, save the port handle board(cphindex).fh = fh board(cphindex).portnum = PortNum FUNCTION = cphindex ' return the BoardNum for subsequent calls commclear(fh) END FUNCTION '=================================================================== ' SC8_SendPositions() '=================================================================== ' ' void SC8_SendPositions (int BoardNum, unsigned char Mask, ' char DIO, unsigned short *pValue); ' ' The SC8_SendPositions is the most complex function. It is used ' send data to update both the digital output and servo axis. ' ' The parameter BoardNum is the Board Number exited from the ' SC8_Initialze function above. If the board has not been ' initialized, the function does nothing. ' ' The parameter Mask is a one byte (unsigned char) value for ' which each axis is one bit in the binary representation. ' If the bit value is a binary 1, then the axis will be updated ' with position data to be followed. ' ' Example: Mask is decimal 27, or binary 00011011, would mean ' four axis are to be updated, the axis are (starting from ' the right, least significant): ' ' Axis 1: YES ' Axis 2: YES ' Axis 3: NO ' Axis 4: YES ' Axis 5: YES ' Axis 6: NO ' Axis 7: NO ' Axis 8: NO ' ' The parameter DIO is a bit tricky. Since it's a ASCII representation ' of the Hex value. ' ' Example: you would like to turn on the digital output 1, 2 and 4, ' and turn OFF digital outputs 3. In binary (easier to see than hex), ' that would be 1011, or B as a hex value; then DIO is the ' "ASCII character" B. ' ' Similarly, for 0011, or 3 as a hex; DIO is the "ASCII character" 3 ' ' The parameter pValue is a pointer to an array of unsigned ' short values. The number of values in the array must match ' the number of Axis that position data is expected, indicated ' earlier in the Mask parameter. ' ' Using the example above for Mask, the array will contains four ' values, the first value is for Axis1, then Axis2, then ' Axis4, then Axis5. ' '=================================================================== SUB SC8_SendPositions SDECL ALIAS "SC8_SendPositions" _ (BYVAL BoardNum AS INTEGER, BYVAL servomask AS BYTE, _ BYVAL DIO AS BYTE, BYVAL pValue AS WORD POINTER) EXPORT MSGBOX "SC8_SendPositions not implemented yet" END SUB '=================================================================== ' SC8_SendPos() '=================================================================== ' ' void SC8_SendPos(int BoardNum, int Axis, unsigned short Position); ' ' The SC8_SendPos is a simpler function to send position data ' to one servo axis. ' ' The parameter BoardNum is the Board Number exited from ' the SC8_Initialze function above. If the board has not ' been initialized, the function does nothing. ' ' The parameter Axis is the Axis number (1 to 8) to be updated. ' ' The parameter Position is the position data. The acceptable ' range for position for the SC8 is 8000 to 22000 in units of ' 0.1uS. Note that not all servos can go this wide! ' ' '=================================================================== SUB SC8_SendPos SDECL ALIAS "SC8_SendPos" (BYVAL BoardNum AS LONG, _ BYVAL axis AS LONG, BYVAL position AS WORD) EXPORT DIM pBoard AS Boarddata POINTER DIM servomask AS BYTE IF axis < 1 OR axis > 8 THEN EXIT SUB IF position < 8000 OR position > 22000 THEN EXIT SUB pBoard = getcph(BoardNum) IF pBoard = 0 THEN EXIT SUB ' generate the mask servomask = 0 BIT CALC servomask, axis - 1, 1 COMM SEND @pBoard.fh, CHR$("~~", servomask, HEX$(@pBoard.digitalmask, 1), _ HIBYT(position), LOBYT(position)) ' No return code, so we just assume it works... commclear(@pBoard.fh) END SUB '=================================================================== ' SC8_SendDigital() '=================================================================== ' ' void SC8_SendDigital(int BoardNum, int DigitalAxis, int ONOFF); ' ' The SC8_SendDigital is a simpler function to turn ON or OFF ' one digital IO. ' ' The parameter BoardNum is the Board Number exited from ' the SC8_Initialze function above. If the board has not been ' initialized, the function does nothing. ' ' The parameter DigitalAxis is the digital output (1 to 4) to ' be updated ' ' The parameter ONOFF is a value for the state of the digital ' output. A value of 0 will turn the digital output OFF (low), ' any other values will turn the output ON (high). ' ' '=================================================================== SUB SC8_SendDigital SDECL ALIAS "SC8_SendDigital" (BYVAL BoardNum AS LONG, _ BYVAL DigitalAxis AS LONG, BYVAL ONOFF AS LONG) EXPORT DIM pBoard AS Boarddata POINTER IF DigitalAxis <= 0 OR DigitalAxis > 4 THEN EXIT SUB pBoard = getcph(BoardNum) IF pBoard = 0 THEN EXIT SUB BIT CALC @pBoard.digitalmask, DigitalAxis - 1, onoff <> 0 COMM SEND @pBoard.fh, CHR$("~~", 0, HEX$(@pBoard.digitalmask,1)) commclear(@pBoard.fh) END SUB '=================================================================== ' SC8_CleanUP '=================================================================== ' ' void SC8_CleanUP(int BoardNum); ' ' The SC8_CleanUp function should be used just before your ' application end. The parameter BoardNum is the Board Number ' exited from the SC8_Initialze function above ' ' '=================================================================== SUB SC8_CleanUP SDECL ALIAS "SC8_CleanUP" (BYVAL BoardNum AS INTEGER) EXPORT DIM pBoard AS Boarddata POINTER pBoard = getcph(BoardNum) IF pBoard = 0 THEN EXIT SUB COMM CLOSE @pBoard.fh ' Set handle back to zero to mark this entry as available @pBoard.fh = 0 @pBoard.portnum = 0 @pBoard.digitalmask = 0 ' in case of reopenings... END SUB '=============================================================================== '= Program Load / DLL Load stuff... '=============================================================================== '= '= '= '=============================================================================== #IF %PB_EXE '---------------- FUNCTION PBMAIN DIM servo AS LONG DIM posn AS WORD DIM commport AS LONG DIM boardnum AS LONG REDIM board(%MAXBOARDS) commport = 6 MSGBOX "about to open COM" & STR$(commport) BoardNum = SC8_Initialize(commport) MSGBOX "BoardNum exited was " & STR$(BoardNum) IF BoardNum = 0 THEN EXIT FUNCTION FOR servo = 1 TO 3 FOR posn = 10000 TO 20000 STEP 2000 SC8_SendPos(BoardNum, servo, posn) MSGBOX "Servo=" & STR$(servo) & " Posn=" & STR$(posn) NEXT SC8_SendPos(BoardNum, servo, 15000) NEXT ' Need this msgbox to allow time for the serial datastream to get ' out to the RISC chip, otherwise the last servo stays stuck at FSD! MSGBOX "About to close commport" SC8_CleanUP(BoardNum) MSGBOX "ByeBye" END FUNCTION '---------------- #ELSE '---------------- FUNCTION LIBMAIN (BYVAL hInstance AS LONG, _ BYVAL fwdReason AS LONG, _ BYVAL lpvReserved AS LONG) AS LONG 'MSGBOX "LIBMAIN(" & STR$(hInstance) & ", " & STR$(fwdReason) & ", " & STR$(lpvReserved) & ")" SELECT CASE fwdReason CASE %DLL_PROCESS_ATTACH REDIM board(%MAXBOARDS) FUNCTION = 1 'success! CASE %DLL_PROCESS_DETACH FUNCTION = 1 'success! CASE %DLL_THREAD_ATTACH FUNCTION = 1 'success! CASE %DLL_THREAD_DETACH FUNCTION = 1 'success! END SELECT END FUNCTION '---------------- #ENDIF