Added MS/TP on Win32 port using external USB RS-485 device from SerialGear. Only completed RS-485 and dlmst unit testing. It is communicating but not nicely.

This commit is contained in:
skarg
2007-06-26 22:32:43 +00:00
parent e6c21e9bb7
commit b3fda233cf
10 changed files with 2978 additions and 122 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
#
# Simple makefile to build an RTB executable for RTOS-32
# Simple makefile to build an executable for Win32 console
#
# This makefile assumes Borland bcc32 development environment
# on Windows NT/9x/2000/XP
# on Windows NT/9x/2000/XP, which includes make, bcc32, and ilink
#
!ifndef BORLAND_DIR
+433
View File
@@ -0,0 +1,433 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#if PRINT_ENABLED
#include <stdio.h>
#endif
#include "bacdef.h"
#include "mstp.h"
#include "dlmstp.h"
#include "rs485.h"
#include "npdu.h"
#define WIN32_LEAN_AND_MEAN
#define STRICT 1
#include <windows.h>
/* Number of MS/TP Packets Rx/Tx */
uint16_t MSTP_Packets = 0;
/* receive buffer */
static DLMSTP_PACKET Receive_Buffer;
/* temp buffer for NPDU insertion */
/* local MS/TP port data - shared with RS-485 */
volatile struct mstp_port_struct_t MSTP_Port;
#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;}
void dlmstp_millisecond_timer(void)
{
INCREMENT_AND_LIMIT_UINT16(MSTP_Port.SilenceTimer);
}
void dlmstp_reinit(void)
{
//RS485_Reinit();
dlmstp_set_my_address(DEFAULT_MAC_ADDRESS);
dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES);
dlmstp_set_max_master(DEFAULT_MAX_MASTER);
}
void dlmstp_init(void)
{
uint8_t data;
/* initialize buffer */
Receive_Buffer.ready = false;
Receive_Buffer.pdu_len = 0;
/* initialize hardware */
RS485_Initialize();
MSTP_Port.InputBuffer = &Receive_Buffer.pdu[0];
MSTP_Init(&MSTP_Port);
/* FIXME: implement your data storage */
data = 64; /* I2C_Read_Byte(
EEPROM_DEVICE_ADDRESS,
EEPROM_MSTP_MAC_ADDR); */
if (data <= 127)
MSTP_Port.This_Station = data;
else
dlmstp_set_my_address(DEFAULT_MAC_ADDRESS);
/* FIXME: implement your data storage */
data = 127; /* I2C_Read_Byte(
EEPROM_DEVICE_ADDRESS,
EEPROM_MSTP_MAX_MASTER_ADDR); */
if ((data <= 127) && (data >= MSTP_Port.This_Station))
MSTP_Port.Nmax_master = data;
else
dlmstp_set_max_master(DEFAULT_MAX_MASTER);
/* FIXME: implement your data storage */
data = 1;
/* I2C_Read_Byte(
EEPROM_DEVICE_ADDRESS,
EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */
if (data >= 1)
MSTP_Port.Nmax_info_frames = data;
else
dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES);
}
void dlmstp_cleanup(void)
{
/* nothing to do for static buffers */
}
/* returns number of bytes sent on success, zero on failure */
int dlmstp_send_pdu(BACNET_ADDRESS * dest, /* destination address */
BACNET_NPDU_DATA * npdu_data, /* network information */
uint8_t * pdu, /* any data to be sent - may be null */
unsigned pdu_len)
{ /* number of bytes of data */
int bytes_sent = 0;
uint8_t destination = 0; /* destination address */
BACNET_ADDRESS src;
if (MSTP_Port.TxReady == false) {
if (npdu_data->data_expecting_reply)
MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY;
else
MSTP_Port.TxFrameType =
FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY;
/* load destination MAC address */
if (dest && dest->mac_len == 1) {
destination = dest->mac[0];
} else {
return -2;
}
dlmstp_get_my_address(&src);
if ((8 /* header len */ + pdu_len) > MAX_MPDU) {
return -4;
}
bytes_sent = MSTP_Create_Frame(
(uint8_t *) & MSTP_Port.TxBuffer[0],
sizeof(MSTP_Port.TxBuffer),
MSTP_Port.TxFrameType,
destination, MSTP_Port.This_Station, pdu, pdu_len);
MSTP_Port.TxLength = bytes_sent;
MSTP_Port.TxReady = true;
MSTP_Packets++;
}
return bytes_sent;
}
static void dlmstp_receive_fsm_task(void *pArg)
{
uint8_t bytes_remaining;
bool received_frame;
//(void)SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
while (TRUE) {
/* only do receive state machine while we don't have a frame */
if ((MSTP_Port.ReceivedValidFrame == false) &&
(MSTP_Port.ReceivedInvalidFrame == false)) {
do {
bytes_remaining = RS485_Check_UART_Data(&MSTP_Port);
MSTP_Receive_Frame_FSM(&MSTP_Port);
received_frame = MSTP_Port.ReceivedValidFrame ||
MSTP_Port.ReceivedInvalidFrame;
if (received_frame)
break;
} while (bytes_remaining);
}
}
}
static void dlmstp_master_fsm_task(void *pArg)
{
//(void)SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
while (TRUE) {
/* only do master state machine while rx is idle */
if (MSTP_Port.receive_state == MSTP_RECEIVE_STATE_IDLE) {
while (MSTP_Master_Node_FSM(&MSTP_Port)) {
Sleep(1);
};
}
/* see if there is a packet available, and a place
to put the reply (if necessary) and process it */
if (Receive_Buffer.ready && !MSTP_Port.TxReady) {
if (Receive_Buffer.pdu_len) {
MSTP_Packets++;
npdu_handler(&Receive_Buffer.address,
&Receive_Buffer.pdu[0], Receive_Buffer.pdu_len);
}
Receive_Buffer.ready = false;
}
}
}
void dlmstp_fill_bacnet_address(BACNET_ADDRESS * src, uint8_t mstp_address)
{
int i = 0;
if (mstp_address == MSTP_BROADCAST_ADDRESS) {
/* mac_len = 0 if broadcast address */
src->mac_len = 0;
src->mac[0] = 0;
} else {
src->mac_len = 1;
src->mac[0] = mstp_address;
}
/* fill with 0's starting with index 1; index 0 filled above */
for (i = 1; i < MAX_MAC_LEN; i++) {
src->mac[i] = 0;
}
src->net = 0;
src->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = 0;
}
}
/* for the MS/TP state machine to use for putting received data */
uint16_t dlmstp_put_receive(uint8_t src, /* source MS/TP address */
uint8_t * pdu, /* PDU data */
uint16_t pdu_len)
{ /* amount of PDU data */
/* PDU is already in the Receive_Buffer */
dlmstp_fill_bacnet_address(&Receive_Buffer.address, src);
Receive_Buffer.pdu_len = pdu_len;
Receive_Buffer.ready = true;
return pdu_len;
}
void dlmstp_set_my_address(uint8_t mac_address)
{
/* Master Nodes can only have address 0-127 */
if (mac_address <= 127) {
MSTP_Port.This_Station = mac_address;
/* FIXME: implement your data storage */
/* I2C_Write_Byte(
EEPROM_DEVICE_ADDRESS,
mac_address,
EEPROM_MSTP_MAC_ADDR); */
if (mac_address > MSTP_Port.Nmax_master)
dlmstp_set_max_master(mac_address);
}
return;
}
uint8_t dlmstp_my_address(void)
{
return MSTP_Port.This_Station;
}
/* This parameter represents the value of the Max_Info_Frames property of */
/* the node's Device object. The value of Max_Info_Frames specifies the */
/* maximum number of information frames the node may send before it must */
/* pass the token. Max_Info_Frames may have different values on different */
/* nodes. This may be used to allocate more or less of the available link */
/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */
/* node, its value shall be 1. */
void dlmstp_set_max_info_frames(uint8_t max_info_frames)
{
if (max_info_frames >= 1) {
MSTP_Port.Nmax_info_frames = max_info_frames;
/* FIXME: implement your data storage */
/* I2C_Write_Byte(
EEPROM_DEVICE_ADDRESS,
(uint8_t)max_info_frames,
EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */
}
return;
}
unsigned dlmstp_max_info_frames(void)
{
return MSTP_Port.Nmax_info_frames;
}
/* This parameter represents the value of the Max_Master property of the */
/* node's Device object. The value of Max_Master specifies the highest */
/* allowable address for master nodes. The value of Max_Master shall be */
/* less than or equal to 127. If Max_Master is not writable in a node, */
/* its value shall be 127. */
void dlmstp_set_max_master(uint8_t max_master)
{
if (max_master <= 127) {
if (MSTP_Port.This_Station <= max_master) {
MSTP_Port.Nmax_master = max_master;
/* FIXME: implement your data storage */
/* I2C_Write_Byte(
EEPROM_DEVICE_ADDRESS,
max_master,
EEPROM_MSTP_MAX_MASTER_ADDR); */
}
}
return;
}
uint8_t dlmstp_max_master(void)
{
return MSTP_Port.Nmax_master;
}
void dlmstp_get_my_address(BACNET_ADDRESS * my_address)
{
int i = 0; /* counter */
my_address->mac_len = 1;
my_address->mac[0] = MSTP_Port.This_Station;
my_address->net = 0; /* local only, no routing */
my_address->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
my_address->adr[i] = 0;
}
return;
}
void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest)
{ /* destination address */
int i = 0; /* counter */
if (dest) {
dest->mac_len = 1;
dest->mac[0] = MSTP_BROADCAST_ADDRESS;
dest->net = BACNET_BROADCAST_NETWORK;
dest->len = 0; /* always zero when DNET is broadcast */
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
#ifdef TEST_DLMSTP
#include <stdio.h>
void npdu_handler(
BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t pdu_len) /* length PDU */
{
(void)src;
(void)pdu;
(void)pdu_len;
fprintf(stderr, "NPDU: received PDU!\n");
}
static void test_transmit_task(void *pArg)
{
while (TRUE) {
Sleep(1000);
MSTP_Create_And_Send_Frame(
&MSTP_Port,
FRAME_TYPE_TEST_REQUEST,
MSTP_Port.SourceAddress,
MSTP_Port.This_Station,
NULL, 0);
}
}
/* returns a delta timestamp */
uint32_t timestamp_ms(void)
{
DWORD ticks = 0, delta_ticks = 0;
static DWORD last_ticks = 0;
ticks = GetTickCount();
delta_ticks =
(ticks >= last_ticks ?
ticks - last_ticks :
MAXDWORD - last_ticks);
last_ticks = ticks;
return delta_ticks;
}
static void test_millisecond_task(void *pArg)
{
DWORD ticks = 0;
DWORD last_ticks = GetTickCount();
DWORD total_ticks = 0;
(void)SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
while (TRUE) {
ticks = GetTickCount();
if (ticks > last_ticks) {
total_ticks = ticks - last_ticks;
} else if (ticks == last_ticks) {
Sleep(1);
continue;
} else {
/* integer rollover */
total_ticks = (MAXDWORD - last_ticks) + ticks;
}
while (total_ticks) {
dlmstp_millisecond_timer();
total_ticks--;
}
last_ticks = ticks;
Sleep(1);
}
}
int main(void)
{
unsigned long hThread = 0;
uint32_t arg_value = 0;
RS485_Set_Interface("COM4");
RS485_Set_Baud_Rate(38400);
dlmstp_set_my_address(0x05);
dlmstp_init();
hThread = _beginthread(test_millisecond_task,4096,&arg_value);
if (hThread == 0) {
fprintf(stderr, "Failed to start timer task\n");
}
hThread = _beginthread(dlmstp_receive_fsm_task,4096,&arg_value);
if (hThread == 0) {
fprintf(stderr, "Failed to start recive FSM task\n");
}
hThread = _beginthread(dlmstp_master_fsm_task,4096,&arg_value);
if (hThread == 0) {
fprintf(stderr, "Failed to start Master Node FSM task\n");
}
/* forever task */
for (;;) {
Sleep(10000);
}
return 0;
}
#endif
+68
View File
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="dlmstp" />
<Option pch_mode="2" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
<Option output="bin\Debug\dlmstp.exe" prefix_auto="1" extension_auto="1" />
<Option object_output="obj\Debug\" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-g" />
</Compiler>
</Target>
<Target title="Release">
<Option output="bin\Release\dlmstp.exe" prefix_auto="1" extension_auto="1" />
<Option object_output="obj\Release\" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-O2" />
</Compiler>
<Linker>
<Add option="-s" />
</Linker>
</Target>
</Build>
<Compiler>
<Add option="-Wall" />
<Add option="-fexceptions" />
<Add option="-DBACDL_MSTP=1" />
<Add option="-DTEST_DLMSTP=1" />
<Add option="-DMAX_APDU=50" />
<Add option="-DMAX_TSM_TRANSACTIONS=16" />
<Add option="-DMAX_ADDRESS_CACHE=16" />
<Add option="-DPRINT_ENABLED=1" />
<Add directory="." />
<Add directory="..\..\..\bacnet-stack" />
<Add directory="..\..\demo\handler" />
<Add directory="..\..\demo\object" />
</Compiler>
<Unit filename="..\..\bacdef.h" />
<Unit filename="..\..\crc.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="..\..\crc.h" />
<Unit filename="..\..\datalink.h" />
<Unit filename="..\..\npdu.h" />
<Unit filename="dlmstp.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="dlmstp.h" />
<Unit filename="mstp.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="mstp.h" />
<Unit filename="rs485.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="rs485.h" />
<Extensions>
<code_completion />
</Extensions>
</Project>
</CodeBlocks_project_file>
+110
View File
@@ -0,0 +1,110 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
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.
59 Temple Place - Suite 330
Boston, MA 02111-1307
USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#ifndef DLMSTP_H
#define DLMSTP_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "bacdef.h"
#include "npdu.h"
/* defines specific to MS/TP */
#define MAX_HEADER (2+1+1+1+2+1+2+1)
#define MAX_MPDU (MAX_HEADER+MAX_PDU)
typedef struct dlmstp_packet {
bool ready; /* true if ready to be sent or received */
BACNET_ADDRESS address; /* source address */
uint8_t frame_type; /* type of message */
unsigned pdu_len; /* packet length */
uint8_t pdu[MAX_MPDU]; /* packet */
} DLMSTP_PACKET;
/* number of MS/TP tx/rx packets */
extern uint16_t MSTP_Packets;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void dlmstp_reinit(void);
void dlmstp_init(void);
void dlmstp_cleanup(void);
void dlmstp_millisecond_timer(void);
void dlmstp_task(void);
/* returns number of bytes sent on success, negative on failure */
int dlmstp_send_pdu(BACNET_ADDRESS * dest, /* destination address */
BACNET_NPDU_DATA * npdu_data, /* network information */
uint8_t * pdu, /* any data to be sent - may be null */
unsigned pdu_len); /* number of bytes of data */
/* This parameter represents the value of the Max_Info_Frames property of */
/* the node's Device object. The value of Max_Info_Frames specifies the */
/* maximum number of information frames the node may send before it must */
/* pass the token. Max_Info_Frames may have different values on different */
/* nodes. This may be used to allocate more or less of the available link */
/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */
/* node, its value shall be 1. */
void dlmstp_set_max_info_frames(uint8_t max_info_frames);
unsigned dlmstp_max_info_frames(void);
/* This parameter represents the value of the Max_Master property of the */
/* node's Device object. The value of Max_Master specifies the highest */
/* allowable address for master nodes. The value of Max_Master shall be */
/* less than or equal to 127. If Max_Master is not writable in a node, */
/* its value shall be 127. */
void dlmstp_set_max_master(uint8_t max_master);
uint8_t dlmstp_max_master(void);
/* MAC address for MS/TP */
void dlmstp_set_my_address(uint8_t my_address);
uint8_t dlmstp_my_address(void);
/* BACnet address used in datalink */
void dlmstp_get_my_address(BACNET_ADDRESS * my_address);
void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */
/* MS/TP state machine functions */
uint16_t dlmstp_put_receive(uint8_t src, /* source MS/TP address */
uint8_t * pdu, /* PDU data */
uint16_t pdu_len);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
File diff suppressed because it is too large Load Diff
+254
View File
@@ -0,0 +1,254 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2004 Steve Karg
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.
59 Temple Place - Suite 330
Boston, MA 02111-1307
USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#ifndef MSTP_H
#define MSTP_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "bacdef.h"
#include "dlmstp.h"
/* The value 255 is used to denote broadcast when used as a */
/* destination address but is not allowed as a value for a station. */
/* Station addresses for master nodes can be 0-127. */
/* Station addresses for slave nodes can be 127-254. */
#define MSTP_BROADCAST_ADDRESS 255
/* MS/TP Frame Type */
/* Frame Types 8 through 127 are reserved by ASHRAE. */
#define FRAME_TYPE_TOKEN 0
#define FRAME_TYPE_POLL_FOR_MASTER 1
#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2
#define FRAME_TYPE_TEST_REQUEST 3
#define FRAME_TYPE_TEST_RESPONSE 4
#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5
#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6
#define FRAME_TYPE_REPLY_POSTPONED 7
/* Frame Types 128 through 255: Proprietary Frames */
/* These frames are available to vendors as proprietary (non-BACnet) frames. */
/* The first two octets of the Data field shall specify the unique vendor */
/* identification code, most significant octet first, for the type of */
/* vendor-proprietary frame to be conveyed. The length of the data portion */
/* of a Proprietary frame shall be in the range of 2 to 501 octets. */
#define FRAME_TYPE_PROPRIETARY_MIN 128
#define FRAME_TYPE_PROPRIETARY_MAX 255
/* The initial CRC16 checksum value */
#define CRC16_INITIAL_VALUE (0xFFFF)
/* receive FSM states */
typedef enum {
MSTP_RECEIVE_STATE_IDLE = 0,
MSTP_RECEIVE_STATE_PREAMBLE = 1,
MSTP_RECEIVE_STATE_HEADER = 2,
MSTP_RECEIVE_STATE_HEADER_CRC = 3,
MSTP_RECEIVE_STATE_DATA = 4
} MSTP_RECEIVE_STATE;
/* master node FSM states */
typedef enum {
MSTP_MASTER_STATE_INITIALIZE = 0,
MSTP_MASTER_STATE_IDLE = 1,
MSTP_MASTER_STATE_USE_TOKEN = 2,
MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3,
MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4,
MSTP_MASTER_STATE_PASS_TOKEN = 5,
MSTP_MASTER_STATE_NO_TOKEN = 6,
MSTP_MASTER_STATE_POLL_FOR_MASTER = 7,
MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8
} MSTP_MASTER_STATE;
struct mstp_port_struct_t {
MSTP_RECEIVE_STATE receive_state;
/* When a master node is powered up or reset, */
/* it shall unconditionally enter the INITIALIZE state. */
MSTP_MASTER_STATE master_state;
/* A Boolean flag set to TRUE by the Receive State Machine */
/* if an error is detected during the reception of a frame. */
/* Set to FALSE by the main state machine. */
unsigned ReceiveError:1;
/* There is data in the buffer */
unsigned DataAvailable:1;
unsigned ReceivedInvalidFrame:1;
/* A Boolean flag set to TRUE by the Receive State Machine */
/* if a valid frame is received. */
/* Set to FALSE by the main state machine. */
unsigned ReceivedValidFrame:1;
/* A Boolean flag set to TRUE by the master machine if this node is the */
/* only known master node. */
unsigned SoleMaster:1;
/* stores the latest received data */
uint8_t DataRegister;
/* Used to accumulate the CRC on the data field of a frame. */
uint16_t DataCRC;
/* Used to store the data length of a received frame. */
unsigned DataLength;
/* Used to store the destination address of a received frame. */
uint8_t DestinationAddress;
/* Used to count the number of received octets or errors. */
/* This is used in the detection of link activity. */
/* Compared to Nmin_octets */
uint8_t EventCount;
/* Used to store the frame type of a received frame. */
uint8_t FrameType;
/* The number of frames sent by this node during a single token hold. */
/* When this counter reaches the value Nmax_info_frames, the node must */
/* pass the token. */
unsigned FrameCount;
/* Used to accumulate the CRC on the header of a frame. */
uint8_t HeaderCRC;
/* Used as an index by the Receive State Machine, up to a maximum value of */
/* InputBufferSize. */
unsigned Index;
/* An array of octets, used to store octets as they are received. */
/* InputBuffer is indexed from 0 to InputBufferSize-1. */
/* The maximum size of a frame is 501 octets. */
uint8_t *InputBuffer;
/* "Next Station," the MAC address of the node to which This Station passes */
/* the token. If the Next_Station is unknown, Next_Station shall be equal to */
/* This_Station. */
uint8_t Next_Station;
/* "Poll Station," the MAC address of the node to which This Station last */
/* sent a Poll For Master. This is used during token maintenance. */
uint8_t Poll_Station;
/* A counter of transmission retries used for Token and Poll For Master */
/* transmission. */
unsigned RetryCount;
/* A timer with nominal 5 millisecond resolution used to measure and */
/* generate silence on the medium between octets. It is incremented by a */
/* timer process and is cleared by the Receive State Machine when activity */
/* is detected and by the SendFrame procedure as each octet is transmitted. */
/* Since the timer resolution is limited and the timer is not necessarily */
/* synchronized to other machine events, a timer value of N will actually */
/* denote intervals between N-1 and N */
uint16_t SilenceTimer;
/* A timer used to measure and generate Reply Postponed frames. It is */
/* incremented by a timer process and is cleared by the Master Node State */
/* Machine when a Data Expecting Reply Answer activity is completed. */
/* note: we always send a reply postponed since a message other than
the reply may be in the transmit queue */
/* uint16_t ReplyPostponedTimer; */
/* Used to store the Source Address of a received frame. */
uint8_t SourceAddress;
/* The number of tokens received by this node. When this counter reaches the */
/* value Npoll, the node polls the address range between TS and NS for */
/* additional master nodes. TokenCount is set to zero at the end of the */
/* polling process. */
unsigned TokenCount;
/* "This Station," the MAC address of this node. TS is generally read from a */
/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */
/* 0 to 254. The value 255 is used to denote broadcast when used as a */
/* destination address but is not allowed as a value for TS. */
uint8_t This_Station;
/* This parameter represents the value of the Max_Info_Frames property of */
/* the node's Device object. The value of Max_Info_Frames specifies the */
/* maximum number of information frames the node may send before it must */
/* pass the token. Max_Info_Frames may have different values on different */
/* nodes. This may be used to allocate more or less of the available link */
/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */
/* node, its value shall be 1. */
unsigned Nmax_info_frames;
/* This parameter represents the value of the Max_Master property of the */
/* node's Device object. The value of Max_Master specifies the highest */
/* allowable address for master nodes. The value of Max_Master shall be */
/* less than or equal to 127. If Max_Master is not writable in a node, */
/* its value shall be 127. */
unsigned Nmax_master;
/* An array of octets, used to store PDU octets prior to being transmitted. */
/* This array is only used for APDU messages */
uint8_t TxBuffer[MAX_MPDU];
unsigned TxLength;
bool TxReady; /* true if ready to be sent or received */
uint8_t TxFrameType; /* type of message - needed by MS/TP */
};
#define DEFAULT_MAX_INFO_FRAMES 1
#define DEFAULT_MAX_MASTER 127
#define DEFAULT_MAC_ADDRESS 127
/* The minimum time after the end of the stop bit of the final octet of a */
/* received frame before a node may enable its EIA-485 driver: 40 bit times. */
/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */
/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */
/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */
/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */
/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */
/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */
/* 40 bits is 4 octets including a start and stop bit with each octet */
#define Tturnaround 40
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port);
void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t
*mstp_port);
bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t
*mstp_port);
/* returns true if line is active */
bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port);
unsigned MSTP_Create_Frame(
uint8_t * buffer, /* where frame is loaded */
unsigned buffer_len, /* amount of space available */
uint8_t frame_type, /* type of frame to send - see defines */
uint8_t destination, /* destination address */
uint8_t source, /* source address */
uint8_t * data, /* any data to be sent - may be null */
unsigned data_len); /* number of bytes of data (up to 501) */
void MSTP_Create_And_Send_Frame(
volatile struct mstp_port_struct_t *mstp_port, /* port to send from */
uint8_t frame_type, /* type of frame to send - see defines */
uint8_t destination, /* destination address */
uint8_t source, /* source address */
uint8_t * data, /* any data to be sent - may be null */
unsigned data_len);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+177 -120
View File
@@ -39,6 +39,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include "mstp.h"
@@ -52,10 +53,12 @@
#define MAX_READ_BUFFER 2048
/* Win32 handle for the port */
static HANDLE RS485_Handle;
HANDLE RS485_Handle;
/* Original COM Timeouts */
static COMMTIMEOUTS RS485_Timeouts;
/* COM port name COM1, COM2, etc */
static char *RS485_Port_Name = "COM1";
/* baud rate - MS enumerated
/* baud rate - MS enumerated
CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400,
CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400,
CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000 */
@@ -72,38 +75,10 @@ static DWORD RS485_StopBits = ONESTOPBIT;
DTR_CONTROL_ENABLE, DTR_CONTROL_DISABLE, DTR_CONTROL_HANDSHAKE */
static DWORD RS485_DTRControl = DTR_CONTROL_DISABLE;
/* RTSControl - MS enumerated:
RTS_CONTROL_ENABLE, RTS_CONTROL_DISABLE,
RTS_CONTROL_ENABLE, RTS_CONTROL_DISABLE,
RTS_CONTROL_HANDSHAKE, RTS_CONTROL_TOGGLE */
static DWORD RS485_RTSControl = RTS_CONTROL_DISABLE;
/* FIXME: GetCommProperties? */
char *RS485_Port_Name(int port)
{
switch (port) {
case 2: return "COM2";
case 3: return "COM3";
case 4: return "COM4";
case 5: return "COM5";
case 6: return "COM6";
case 7: return "COM7";
case 8: return "COM8";
case 9: return "COM9";
default:
case 1: return "COM1";
}
}
int RS485_Port_Number(void)
{
return RS485_Port;
}
void RS485_Set_Port_Number(int port)
{
RS485_Port = port;
}
/****************************************************************************
* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in
* receive mode.
@@ -111,33 +86,47 @@ void RS485_Set_Port_Number(int port)
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Initialize_Port(void)
void RS485_Set_Interface(char *ifname)
{
/* note: expects a constant char, or char from the heap */
RS485_Port_Name = ifname;
}
static void RS485_Print_Error(void)
{
char * szExtended = ""; // error string translated from error code
DWORD dwExtSize;
DWORD dwErr;
dwErr = GetLastError();
fprintf(stderr,"Error %lu:\r\n", dwErr);
/* Get error string from system */
dwExtSize = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | 80 ,
NULL, dwErr,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(LPTSTR) szExtended, 0, NULL);
fprintf(stderr,"%s\r\n", szExtended);
return;
}
static void RS485_Configure_Status(void)
{
DCB dcb = {0};
COMMTIMEOUTS ctNew;
RS485_Handle = CreateFile(
RS485_Port_Name(RS485_Port),
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if (RS485_Handle == INVALID_HANDLE_VALUE) {
/* error opening port; abort */
exit();
}
dcb.DCBlength = sizeof(dcb);
/* get current DCB settings */
if (!GetCommState(RS485_Handle, &dcb)) {
/* FIXME: error message? */
exit();
fprintf(stderr,"Unable to get status from %s\n", RS485_Port_Name);
RS485_Print_Error();
exit(1);
}
/* update DCB rate, byte size, parity, and stop bits size */
dcb.BaudRate = RS485_Baud_Rate;
dcb.BaudRate = RS485_Baud;
dcb.ByteSize = RS485_ByteSize;
dcb.Parity = RS485_Parity;
dcb.StopBits = RS485_StopBits;
@@ -160,28 +149,73 @@ void RS485_Initialize_Port(void)
dcb.fParity = TRUE;
*/
if (!SetCommState(RS485_Handle, &dcb)) {
/* FIXME: message? */
fprintf(stderr,"Unable to set status on %s\n", RS485_Port_Name);
RS485_Print_Error();
}
/*
if (!SetCommTimeouts(COMDEV(TTYInfo), &(TIMEOUTSNEW(TTYInfo))))
ErrorReporter("SetCommTimeouts");
*/
/* configure the COM port timeout values */
ctNew.ReadIntervalTimeout = MAXDWORD;
ctNew.ReadTotalTimeoutMultiplier = MAXDWORD;
ctNew.ReadTotalTimeoutConstant = 1000;
ctNew.WriteTotalTimeoutMultiplier = 0;
ctNew.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(RS485_Handle, &ctNew)) {
RS485_Print_Error();
}
/* Get rid of any stray characters */
if (!PurgeComm(RS485_Handle, PURGE_TXABORT | PURGE_RXABORT)) {
/* FIXME: message? */
fprintf(stderr,"Unable to purge %s\n", RS485_Port_Name);
RS485_Print_Error();
}
/* Set the Comm buffer size */
/* Set the Comm buffer size */
SetupComm(RS485_Handle, MAX_READ_BUFFER, MAX_WRITE_BUFFER);
/* raise DTR */
if (!EscapeCommFunction(RS485_Handle, SETDTR)) {
/* FIXME: message? */
fprintf(stderr,"Unable to set DTR on %s\n", RS485_Port_Name);
RS485_Print_Error();
}
return TRUE;
}
/****************************************************************************
* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in
* receive mode.
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Initialize(void)
{
RS485_Handle = CreateFile(
RS485_Port_Name,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
/*FILE_FLAG_OVERLAPPED*/0,
0);
if (RS485_Handle == INVALID_HANDLE_VALUE) {
fprintf(stderr,"Unable to open %s\n", RS485_Port_Name);
RS485_Print_Error();
exit(1);
}
if (!GetCommTimeouts( RS485_Handle, &RS485_Timeouts)) {
RS485_Print_Error();
}
RS485_Configure_Status();
return;
}
void RS485_Cleanup(void)
{
if (!EscapeCommFunction(RS485_Handle, CLRDTR)) {
RS485_Print_Error();
}
if (!SetCommTimeouts(RS485_Handle, &RS485_Timeouts)) {
RS485_Print_Error();
}
CloseHandle(RS485_Handle);
}
/****************************************************************************
@@ -192,8 +226,7 @@ void RS485_Initialize_Port(void)
*****************************************************************************/
uint32_t RS485_Get_Baud_Rate(void)
{
switch (RS485_Baud_Rate) {
case CBR_9600: return 9600;
switch (RS485_Baud) {
case CBR_19200: return 19200;
case CBR_38400: return 38400;
case CBR_57600: return 57600;
@@ -215,20 +248,20 @@ bool RS485_Set_Baud_Rate(uint32_t baud)
switch (baud) {
case 9600:
RS485_Baud_Rate = CBR_9600;
break;
RS485_Baud = CBR_9600;
break;
case 19200:
RS485_Baud_Rate = CBR_19200;
break;
RS485_Baud = CBR_19200;
break;
case 38400:
RS485_Baud_Rate = CBR_38400;
break;
RS485_Baud = CBR_38400;
break;
case 57600:
RS485_Baud_Rate = CBR_57600;
break;
RS485_Baud = CBR_57600;
break;
case 115200:
RS485_Baud_Rate = CBR_115200;
break;
RS485_Baud = CBR_115200;
break;
default:
valid = false;
break;
@@ -242,86 +275,110 @@ bool RS485_Set_Baud_Rate(uint32_t baud)
}
/* Transmits a Frame on the wire */
void RS485_Send_Frame(struct mstp_port_struct_t *mstp_port, /* port specific data */
uint8_t * buffer, /* frame to send (up to 501 bytes of data) */
uint16_t nbytes)
{ /* number of bytes of data (up to 501) */
void RS485_Send_Frame(
struct mstp_port_struct_t *mstp_port, /* port specific data */
uint8_t * buffer, /* frame to send (up to 501 bytes of data) */
uint16_t nbytes) /* number of bytes of data (up to 501) */
{
uint8_t turnaround_time;
uint32_t baud;
DWORD dwWritten = 0;
/* in order to avoid line contention */
while (mstp_port->Turn_Around_Waiting) {
/* wait, yield, or whatever */
if (mstp_port) {
baud = RS485_Get_Baud_Rate();
/* wait about 40 bit times since reception */
if (baud == 9600)
turnaround_time = 4;
else if (baud == 19200)
turnaround_time = 2;
else
turnaround_time = 1;
while (mstp_port->SilenceTimer < turnaround_time) {
/* do nothing - wait for timer to increment */
};
}
WriteFile(RS485_Handle, buffer, nbytes, &dwWritten, NULL);
/* Disable the receiver, and enable the transmit line driver. */
while (nbytes) {
putc(*buffer, stderr);
buffer++;
nbytes--;
/* per MSTP spec, sort of */
if (mstp_port) {
mstp_port->SilenceTimer = 0;
}
/* Wait until the final stop bit of the most significant CRC octet */
/* has been transmitted but not more than Tpostdrive. */
/* Disable the transmit line driver. */
return;
}
/* called by timer, interrupt(?) or other thread */
void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port)
{
char lpBuf[1];
DWORD dwRead = 0;
if (mstp_port->ReceiveError == true) {
/* wait for state machine to clear this */
}
/* wait for state machine to read from the DataRegister */
else if (mstp_port->DataAvailable == false) {
/* check for data */
/* if error, */
/* ReceiveError = TRUE; */
/* return; */
mstp_port->DataRegister = 0; /* FIXME: Get this data from UART or buffer */
/* if data is ready, */
/* DataAvailable = TRUE; */
/* return; */
if (!ReadFile(RS485_Handle, lpBuf, sizeof(lpBuf), &dwRead, NULL)) {
if (GetLastError() != ERROR_IO_PENDING) {
mstp_port->ReceiveError = TRUE;
}
} else {
if (dwRead) {
mstp_port->DataRegister = lpBuf[0]; /* FIXME: Get this data from UART or buffer */
/* if data is ready, */
mstp_port->DataAvailable = TRUE;
}
}
}
}
#ifdef TEST
#ifdef TEST_RS485
static void test_transmit_task(void *pArg)
{
char *TxBuf = "BACnet MS/TP";
size_t len = strlen(TxBuf)+1;
while (TRUE) {
Sleep(1000);
RS485_Send_Frame(NULL, &TxBuf[0], len);
}
}
int main(void)
{
char lpBuf[AMOUNT_TO_READ];
DWORD dwRead; // bytes actually read
OVERLAPPED osReader = {0}; // overlapped structure for read operations
RS485_Set_Port_Number("COM4");
RS485_Set_Baud_Rate(38400);
RS485_Initialize_Port();
unsigned long hThread = 0;
uint32_t arg_value = 0;
char lpBuf[1];
DWORD dwRead = 0;
unsigned i = 0;
RS485_Set_Interface("COM4");
RS485_Set_Baud_Rate(38400);
RS485_Initialize();
#if 0
/* create a task for synchronous transmit */
hThread = _beginthread(test_transmit_task,4096,&arg_value);
if (hThread == 0) {
fprintf(stderr, "Failed to start transmit task\n");
}
#endif
/* receive task */
for (;;) {
if (!ReadFile(RS485_Handle, lpBuf, sizeof(lpBuf), &dwRead, &osReader)) {
if (!ReadFile(RS485_Handle, lpBuf, sizeof(lpBuf), &dwRead, NULL)) {
if (GetLastError() != ERROR_IO_PENDING) {
/* error in comm */
RS485_Print_Error();
}
} else {
/* read completed immediately */
/* print any characters received */
if (dwRead) {
for (i = 0; i < dwRead; i++) {
fprintf(stderr,"%02X ",lpBuf[i]);
}
fprintf(stderr,"\n");
}
dwRead = 0;
}
}
return 0;
}
#endif /* TEST_ABORT */
#endif /* TEST */
+52
View File
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="rs485" />
<Option pch_mode="2" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
<Option output="bin\Debug\rs485.exe" prefix_auto="1" extension_auto="1" />
<Option object_output="obj\Debug\" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-g" />
</Compiler>
</Target>
<Target title="Release">
<Option output="bin\Release\rs485.exe" prefix_auto="1" extension_auto="1" />
<Option object_output="obj\Release\" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-O2" />
</Compiler>
<Linker>
<Add option="-s" />
</Linker>
</Target>
</Build>
<Compiler>
<Add option="-Wall" />
<Add option="-fexceptions" />
<Add option="-DBACDL_MSTP=1" />
<Add option="-DTEST" />
<Add option="-DTEST_RS485" />
<Add directory="." />
<Add directory="..\..\..\bacnet-stack" />
<Add directory="..\..\demo\handler" />
<Add directory="..\..\demo\object" />
</Compiler>
<Unit filename="dlmstp.h" />
<Unit filename="mstp.h" />
<Unit filename="rs485.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="rs485.h" />
<Extensions>
<code_completion />
</Extensions>
</Project>
</CodeBlocks_project_file>
+62
View File
@@ -0,0 +1,62 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2004 Steve Karg
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.
59 Temple Place - Suite 330
Boston, MA 02111-1307
USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#ifndef RS485_H
#define RS485_H
#include <stdint.h>
#include "mstp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void RS485_Set_Interface(char *ifname);
void RS485_Initialize(void);
void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */
uint8_t * buffer, /* frame to send (up to 501 bytes of data) */
uint16_t nbytes); /* number of bytes of data (up to 501) */
uint8_t RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port); /* port specific data */
uint32_t RS485_Get_Baud_Rate(void);
bool RS485_Set_Baud_Rate(uint32_t baud);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+110
View File
@@ -0,0 +1,110 @@
#
# Simple makefile to build an executable for Win32 console
#
# This makefile assumes Borland development environment
# on Windows NT/9x/2000/XP
# Tools: bcc32, make, ilink
!ifndef BORLAND_DIR
BORLAND_DIR_Not_Defined:
@echo .
@echo You must define environment variable BORLAND_DIR to compile.
!endif
PRODUCT = rs485
PRODUCT_EXE = $(PRODUCT).exe
# Choose the Data Link Layer to Enable
DEFINES = -DBACDL_MSTP=1;TEST_RS485
SRCS = rs485.c
OBJS = $(SRCS:.c=.obj)
# Compiler definitions
#
BCC_CFG = bcc32.cfg
CC = $(BORLAND_DIR)\bin\bcc32 +$(BCC_CFG)
#LINK = $(BORLAND_DIR)\bin\tlink32
LINK = $(BORLAND_DIR)\bin\ilink32
TLIB = $(BORLAND_DIR)\bin\tlib
#
# Include directories
#
CC_DIR = $(BORLAND_DIR)\BIN
BACNET_INCL = ..\..\;..\..\demo\handler\;..\..\demo\object\;.
INCL_DIRS = -I$(BORLAND_DIR)\include;$(BACNET_INCL)
CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES)
# Libraries
#
C_LIB_DIR = $(BORLAND_DIR)\lib
LIBS = $(C_LIB_DIR)\IMPORT32.lib \
$(C_LIB_DIR)\CW32MT.lib
#
# Main target
#
# This should be the first one in the makefile
all : $(BCC_CFG) $(PRODUCT_EXE)
# Linker specific: the link below is for BCC linker/compiler. If you link
# with a different linker - please change accordingly.
#
# need a temp response file (@&&) because command line is too long
$(PRODUCT_EXE) : $(OBJS)
@echo Running Linker for $(PRODUCT_EXE)
$(LINK) -L$(C_LIB_DIR) -m -c -s -v @&&| # temp response file, starts with |
$(BORLAND_DIR)\lib\c0x32.obj $** # $** lists each dependency
$<
$*.map
$(LIBS)
| # end of temp response file
#
# Utilities
clean :
@echo Deleting obj files, $(PRODUCT_EXE) and map files.
del *.obj
del ..\..\*.obj
del ..\..\demo\handler\*.obj
del ..\..\demo\object\*.obj
del $(PRODUCT_EXE)
del *.map
del $(BCC_CFG)
#
# Generic rules
#
.SUFFIXES: .cpp .c .sbr .obj
#
# cc generic rule
#
.c.obj:
$(CC) -o$@ $<
# Compiler configuration file
$(BCC_CFG) :
Copy &&|
$(CFLAGS)
-c
-y #include line numbers in OBJ's
-v #include debug info
-w+ #turn on all warnings
-Od #disable all optimizations
#-a4 #32 bit data alignment
#-M # generate link map
#-ls # linker options
#-WM- #not multithread
-WM #multithread
-w-aus # ignore warning assigned a value that is never used
-w-sig # ignore warning conversion may lose sig digits
| $@
# EOF: makefile