1 /* 2 * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. 3 * Copyright (C) 2013 Armink <[email protected]> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * File: $Id: mbfunccoils_m.c,v 1.60 2013/10/12 15:10:12 Armink Add Master Functions 29 */ 30 31 /* ----------------------- System includes ----------------------------------*/ 32 #include "stdlib.h" 33 #include "string.h" 34 35 /* ----------------------- Platform includes --------------------------------*/ 36 #include "port.h" 37 38 /* ----------------------- Modbus includes ----------------------------------*/ 39 #include "mb.h" 40 #include "mb_m.h" 41 #include "mbframe.h" 42 #include "mbproto.h" 43 #include "mbconfig.h" 44 45 /* ----------------------- Defines ------------------------------------------*/ 46 #define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 ) 47 #define MB_PDU_REQ_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 ) 48 #define MB_PDU_REQ_READ_SIZE ( 4 ) 49 #define MB_PDU_FUNC_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 0 ) 50 #define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 ) 51 #define MB_PDU_FUNC_READ_SIZE_MIN ( 1 ) 52 53 #define MB_PDU_REQ_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF ) 54 #define MB_PDU_REQ_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 ) 55 #define MB_PDU_REQ_WRITE_SIZE ( 4 ) 56 #define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF ) 57 #define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 ) 58 #define MB_PDU_FUNC_WRITE_SIZE ( 4 ) 59 60 #define MB_PDU_REQ_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF ) 61 #define MB_PDU_REQ_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 ) 62 #define MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 ) 63 #define MB_PDU_REQ_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 ) 64 #define MB_PDU_REQ_WRITE_MUL_SIZE_MIN ( 5 ) 65 #define MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ( 0x07B0 ) 66 #define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF ) 67 #define MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 ) 68 #define MB_PDU_FUNC_WRITE_MUL_SIZE ( 5 ) 69 70 /* ----------------------- Static functions ---------------------------------*/ 71 eMBException prveMBError2Exception( eMBErrorCode eErrorCode ); 72 73 /* ----------------------- Start implementation -----------------------------*/ 74 #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0 75 #if MB_FUNC_READ_COILS_ENABLED > 0 76 77 /** 78 * This function will request read coil. 79 * 80 * @param ucSndAddr salve address 81 * @param usCoilAddr coil start address 82 * @param usNCoils coil total number 83 * @param lTimeOut timeout (-1 will waiting forever) 84 * 85 * @return error code 86 */ 87 eMBMasterReqErrCode 88 eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils ,LONG lTimeOut ) 89 { 90 UCHAR *ucMBFrame; 91 eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR; 92 93 if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG; 94 else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY; 95 else 96 { 97 vMBMasterGetPDUSndBuf(&ucMBFrame); 98 vMBMasterSetDestAddress(ucSndAddr); 99 ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_COILS; 100 ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usCoilAddr >> 8; 101 ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usCoilAddr; 102 ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF ] = usNCoils >> 8; 103 ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] = usNCoils; 104 vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE ); 105 ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT ); 106 eErrStatus = eMBMasterWaitRequestFinish( ); 107 108 } 109 return eErrStatus; 110 } 111 112 eMBException 113 eMBMasterFuncReadCoils( UCHAR * pucFrame, USHORT * usLen ) 114 { 115 UCHAR *ucMBFrame; 116 USHORT usRegAddress; 117 USHORT usCoilCount; 118 UCHAR ucByteCount; 119 120 eMBException eStatus = MB_EX_NONE; 121 eMBErrorCode eRegStatus; 122 123 /* If this request is broadcast, and it's read mode. This request don't need execute. */ 124 if ( xMBMasterRequestIsBroadcast() ) 125 { 126 eStatus = MB_EX_NONE; 127 } 128 else if ( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN ) 129 { 130 vMBMasterGetPDUSndBuf(&ucMBFrame); 131 usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 ); 132 usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] ); 133 usRegAddress++; 134 135 usCoilCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF] << 8 ); 136 usCoilCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] ); 137 138 /* Test if the quantity of coils is a multiple of 8. If not last 139 * byte is only partially field with unused coils set to zero. */ 140 if( ( usCoilCount & 0x0007 ) != 0 ) 141 { 142 ucByteCount = ( UCHAR )( usCoilCount / 8 + 1 ); 143 } 144 else 145 { 146 ucByteCount = ( UCHAR )( usCoilCount / 8 ); 147 } 148 149 /* Check if the number of registers to read is valid. If not 150 * return Modbus illegal data value exception. 151 */ 152 if( ( usCoilCount >= 1 ) && 153 ( ucByteCount == pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] ) ) 154 { 155 /* Make callback to fill the buffer. */ 156 eRegStatus = eMBMasterRegCoilsCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usCoilCount, MB_REG_READ ); 157 158 /* If an error occured convert it into a Modbus exception. */ 159 if( eRegStatus != MB_ENOERR ) 160 { 161 eStatus = prveMBError2Exception( eRegStatus ); 162 } 163 } 164 else 165 { 166 eStatus = MB_EX_ILLEGAL_DATA_VALUE; 167 } 168 } 169 else 170 { 171 /* Can't be a valid read coil register request because the length 172 * is incorrect. */ 173 eStatus = MB_EX_ILLEGAL_DATA_VALUE; 174 } 175 return eStatus; 176 } 177 #endif 178 179 #if MB_FUNC_WRITE_COIL_ENABLED > 0 180 181 /** 182 * This function will request write one coil. 183 * 184 * @param ucSndAddr salve address 185 * @param usCoilAddr coil start address 186 * @param usCoilData data to be written 187 * @param lTimeOut timeout (-1 will waiting forever) 188 * 189 * @return error code 190 * 191 * @see eMBMasterReqWriteMultipleCoils 192 */ 193 eMBMasterReqErrCode 194 eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LONG lTimeOut ) 195 { 196 UCHAR *ucMBFrame; 197 eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR; 198 199 if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG; 200 else if ( ( usCoilData != 0xFF00 ) && ( usCoilData != 0x0000 ) ) eErrStatus = MB_MRE_ILL_ARG; 201 else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY; 202 else 203 { 204 vMBMasterGetPDUSndBuf(&ucMBFrame); 205 vMBMasterSetDestAddress(ucSndAddr); 206 ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_SINGLE_COIL; 207 ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF] = usCoilAddr >> 8; 208 ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF + 1] = usCoilAddr; 209 ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF ] = usCoilData >> 8; 210 ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usCoilData; 211 vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE ); 212 ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT ); 213 eErrStatus = eMBMasterWaitRequestFinish( ); 214 } 215 return eErrStatus; 216 } 217 218 eMBException 219 eMBMasterFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen ) 220 { 221 USHORT usRegAddress; 222 UCHAR ucBuf[2]; 223 224 eMBException eStatus = MB_EX_NONE; 225 eMBErrorCode eRegStatus; 226 227 if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) ) 228 { 229 usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 ); 230 usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] ); 231 usRegAddress++; 232 233 if( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF + 1] == 0x00 ) && 234 ( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) || 235 ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0x00 ) ) ) 236 { 237 ucBuf[1] = 0; 238 if( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) 239 { 240 ucBuf[0] = 1; 241 } 242 else 243 { 244 ucBuf[0] = 0; 245 } 246 eRegStatus = 247 eMBMasterRegCoilsCB( &ucBuf[0], usRegAddress, 1, MB_REG_WRITE ); 248 249 /* If an error occured convert it into a Modbus exception. */ 250 if( eRegStatus != MB_ENOERR ) 251 { 252 eStatus = prveMBError2Exception( eRegStatus ); 253 } 254 } 255 else 256 { 257 eStatus = MB_EX_ILLEGAL_DATA_VALUE; 258 } 259 } 260 else 261 { 262 /* Can't be a valid write coil register request because the length 263 * is incorrect. */ 264 eStatus = MB_EX_ILLEGAL_DATA_VALUE; 265 } 266 return eStatus; 267 } 268 269 #endif 270 271 #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0 272 273 /** 274 * This function will request write multiple coils. 275 * 276 * @param ucSndAddr salve address 277 * @param usCoilAddr coil start address 278 * @param usNCoils coil total number 279 * @param usCoilData data to be written 280 * @param lTimeOut timeout (-1 will waiting forever) 281 * 282 * @return error code 283 * 284 * @see eMBMasterReqWriteCoil 285 */ 286 eMBMasterReqErrCode 287 eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr, 288 USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut) 289 { 290 UCHAR *ucMBFrame; 291 USHORT usRegIndex = 0; 292 UCHAR ucByteCount; 293 eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR; 294 295 if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG; 296 else if ( usNCoils > MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ) eErrStatus = MB_MRE_ILL_ARG; 297 else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY; 298 else 299 { 300 vMBMasterGetPDUSndBuf(&ucMBFrame); 301 vMBMasterSetDestAddress(ucSndAddr); 302 ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_MULTIPLE_COILS; 303 ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] = usCoilAddr >> 8; 304 ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] = usCoilAddr; 305 ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF] = usNCoils >> 8; 306 ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF + 1] = usNCoils ; 307 if( ( usNCoils & 0x0007 ) != 0 ) 308 { 309 ucByteCount = ( UCHAR )( usNCoils / 8 + 1 ); 310 } 311 else 312 { 313 ucByteCount = ( UCHAR )( usNCoils / 8 ); 314 } 315 ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF] = ucByteCount; 316 ucMBFrame += MB_PDU_REQ_WRITE_MUL_VALUES_OFF; 317 while( ucByteCount > usRegIndex) 318 { 319 *ucMBFrame++ = pucDataBuffer[usRegIndex++]; 320 } 321 vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + ucByteCount ); 322 ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT ); 323 eErrStatus = eMBMasterWaitRequestFinish( ); 324 } 325 return eErrStatus; 326 } 327 328 eMBException 329 eMBMasterFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen ) 330 { 331 USHORT usRegAddress; 332 USHORT usCoilCnt; 333 UCHAR ucByteCount; 334 UCHAR ucByteCountVerify; 335 UCHAR *ucMBFrame; 336 337 eMBException eStatus = MB_EX_NONE; 338 eMBErrorCode eRegStatus; 339 340 /* If this request is broadcast, the *usLen is not need check. */ 341 if( ( *usLen == MB_PDU_FUNC_WRITE_MUL_SIZE ) || xMBMasterRequestIsBroadcast() ) 342 { 343 vMBMasterGetPDUSndBuf(&ucMBFrame); 344 usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 ); 345 usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] ); 346 usRegAddress++; 347 348 usCoilCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF] << 8 ); 349 usCoilCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF + 1] ); 350 351 ucByteCount = ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF]; 352 353 /* Compute the number of expected bytes in the request. */ 354 if( ( usCoilCnt & 0x0007 ) != 0 ) 355 { 356 ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 + 1 ); 357 } 358 else 359 { 360 ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 ); 361 } 362 363 if( ( usCoilCnt >= 1 ) && ( ucByteCountVerify == ucByteCount ) ) 364 { 365 eRegStatus = 366 eMBMasterRegCoilsCB( &ucMBFrame[MB_PDU_REQ_WRITE_MUL_VALUES_OFF], 367 usRegAddress, usCoilCnt, MB_REG_WRITE ); 368 369 /* If an error occured convert it into a Modbus exception. */ 370 if( eRegStatus != MB_ENOERR ) 371 { 372 eStatus = prveMBError2Exception( eRegStatus ); 373 } 374 } 375 else 376 { 377 eStatus = MB_EX_ILLEGAL_DATA_VALUE; 378 } 379 } 380 else 381 { 382 /* Can't be a valid write coil register request because the length 383 * is incorrect. */ 384 eStatus = MB_EX_ILLEGAL_DATA_VALUE; 385 } 386 return eStatus; 387 } 388 389 #endif 390 #endif 391