Release BACnet Protocol Stack 0.3.0

This commit is contained in:
skarg
2007-02-23 13:19:57 +00:00
parent 6c5b409dcf
commit 63e18a0f1f
315 changed files with 57198 additions and 0 deletions
+377
View File
@@ -0,0 +1,377 @@
/*####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####*/
#include <stdint.h>
#include "bacdef.h"
#include "npdu.h"
#include "arcnet.h"
#include "net.h"
/* my local device data - MAC address */
uint8_t ARCNET_MAC_Address = 0;
/* ARCNET file handle */
static int ARCNET_Sock_FD = -1;
/* ARCNET socket address (has the interface name) */
static struct sockaddr ARCNET_Socket_Address;
/* Broadcast address */
#define ARCNET_BROADCAST 0
/*
Hints:
When using a PCI20-485D ARCNET card from Contemporary Controls,
you might need to know about the following settings:
Assuming a 20MHz clock on the COM20020 chip:
clockp Clock Prescaler DataRate
------ --------------- --------
0 8 2.5 Mbps
1 16 1.25 Mbps
2 32 625 Kbps
3 64 312.5 Kbps
4 128 156.25Kbps
1. Install the arcnet driver and arcnet raw mode driver:
# modprobe com20020_pci clockp=4
# modprobe arc_rawmode
2. Use ifconfig to bring up the interface
# ifconfig arc0 up
3. The hardware address (MAC address) is set using the dipswitch
on the back of the card. 0 is broadcast, so don't use 0.
4. The backplane mode on the PCI20-485D card is done in hardware,
so the driver does not need to do backplane mode. If you
use another type of PCI20 card, you could pass in backplane=1 or
backplane=0 as an option to the modprobe of com20020_pci.
*/
bool arcnet_valid(void)
{
return (ARCNET_Sock_FD >= 0);
}
void arcnet_cleanup(void)
{
if (arcnet_valid())
close(ARCNET_Sock_FD);
ARCNET_Sock_FD = -1;
return;
}
static int arcnet_bind(char *interface_name)
{
int sock_fd = -1; /* return value */
struct ifreq ifr;
int rv; /* return value - error value from df or ioctl call */
int uid = 0;
/* check to see if we are being run as root */
uid = getuid();
if (uid != 0) {
fprintf(stderr,
"arcnet: Unable to open an af_packet socket. "
"Try running with root priveleges.\n");
return sock_fd;
}
fprintf(stderr, "arcnet: opening \"%s\"\n", interface_name);
/* note: on some systems you may have to add or enable in */
/* modules.conf (or in modutils/alias on Debian with update-modules) */
/* alias net-pf-17 af_packet */
/* Then follow it by: # modprobe af_packet */
if ((sock_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) {
/* Error occured */
fprintf(stderr,
"arcnet: Error opening socket: %s\n", strerror(errno));
fprintf(stderr,
"You might need to add the following to modules.conf\n"
"(or in /etc/modutils/alias on Debian with update-modules):\n"
"alias net-pf-17 af_packet\n"
"Also, add af_packet to /etc/modules.\n"
"Then follow it by:\n" "# modprobe af_packet\n");
exit(-1);
}
if (ARCNET_Sock_FD >= 0) {
/* Bind the socket to an interface name so we only get packets from it */
ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET;
/*ARCNET_Socket_Address.sa_family = PF_INET; */
/* Clear the memory before copying */
memset(ARCNET_Socket_Address.sa_data, '\0',
sizeof(ARCNET_Socket_Address.sa_data));
/* Strcpy the interface name into the address */
strncpy(ARCNET_Socket_Address.sa_data, interface_name,
sizeof(ARCNET_Socket_Address.sa_data) - 1);
fprintf(stderr, "arcnet: binding \"%s\"\n",
ARCNET_Socket_Address.sa_data);
if (bind(sock_fd, &ARCNET_Socket_Address,
sizeof(ARCNET_Socket_Address)) != 0) {
/* Bind problem, close socket and return */
fprintf(stderr,
"arcnet: Unable to bind socket : %s\n", strerror(errno));
fprintf(stderr,
"You might need to add the following to modules.conf\n"
"(or in /etc/modutils/alias on Debian with update-modules):\n"
"alias net-pf-17 af_packet\n"
"Also, add af_packet to /etc/modules.\n"
"Then follow it by:\n" "# modprobe af_packet\n");
/* Close the socket */
close(sock_fd);
exit(-1);
}
}
strncpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name));
rv = ioctl(sock_fd, SIOCGIFHWADDR, &ifr);
if (rv != -1) /* worked okay */
ARCNET_MAC_Address = ifr.ifr_hwaddr.sa_data[0];
/* copy this info into the local copy since bind wiped it out */
ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET;
/*ARCNET_Socket_Address.sa_family = PF_INET; */
/* Clear the memory before copying */
memset(ARCNET_Socket_Address.sa_data, '\0',
sizeof(ARCNET_Socket_Address.sa_data));
/* Strcpy the interface name into the address */
strncpy(ARCNET_Socket_Address.sa_data, interface_name,
sizeof(ARCNET_Socket_Address.sa_data) - 1);
fprintf(stderr, "arcnet: MAC=%02Xh iface=\"%s\"\n",
ARCNET_MAC_Address, ARCNET_Socket_Address.sa_data);
atexit(arcnet_cleanup);
return sock_fd;
}
bool arcnet_init(char *interface_name)
{
ARCNET_Sock_FD = arcnet_bind(interface_name);
return arcnet_valid();
}
/* function to send a PDU out the socket */
/* returns number of bytes sent on success, negative on failure */
int arcnet_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 */
BACNET_ADDRESS src = { 0 }; /* source address */
int bytes = 0;
uint8_t mtu[512] = { 0 };
int mtu_len = 0;
struct archdr *pkt = (struct archdr *) mtu;
(void) npdu_data;
src.mac[0] = ARCNET_MAC_Address;
src.mac_len = 1;
/* don't waste time if the socket is not valid */
if (ARCNET_Sock_FD < 0) {
fprintf(stderr, "arcnet: socket is invalid!\n");
return -1;
}
/* load destination MAC address */
if (dest->mac_len == 1)
pkt->hard.dest = dest->mac[0];
else {
fprintf(stderr, "arcnet: invalid destination MAC address!\n");
return -2;
}
if (src.mac_len == 1)
pkt->hard.source = src.mac[0];
else {
fprintf(stderr, "arcnet: invalid source MAC address!\n");
return -3;
}
/* Logical PDU portion */
pkt->soft.raw[0] = 0xCD; /* SC for BACnet */
pkt->soft.raw[1] = 0x82; /* DSAP for BACnet */
pkt->soft.raw[2] = 0x82; /* SSAP for BACnet */
pkt->soft.raw[3] = 0x03; /* LLC Control byte in header */
/* packet length */
mtu_len = ARC_HDR_SIZE + 4 /*SC,DSAP,SSAP,LLC */ + pdu_len;
if (mtu_len > 512) {
fprintf(stderr, "arcnet: PDU is too big to send!\n");
return -4;
}
memcpy(&pkt->soft.raw[4], pdu, pdu_len);
/* Send the packet */
bytes =
sendto(ARCNET_Sock_FD, &mtu, mtu_len, 0,
(struct sockaddr *) &ARCNET_Socket_Address,
sizeof(ARCNET_Socket_Address));
/* did it get sent? */
if (bytes < 0)
fprintf(stderr, "arcnet: Error sending packet: %s\n",
strerror(errno));
return bytes;
}
/* receives an framed packet */
/* returns the number of octets in the PDU, or zero on failure */
uint16_t arcnet_receive(BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t max_pdu, /* amount of space available in the PDU */
unsigned timeout)
{ /* milliseconds to wait for a packet */
int received_bytes;
uint8_t buf[512] = { 0 }; /* data */
uint16_t pdu_len = 0; /* return value */
fd_set read_fds;
int max;
struct timeval select_timeout;
struct archdr *pkt = (struct archdr *) buf;
/* Make sure the socket is open */
if (ARCNET_Sock_FD <= 0)
return 0;
/* we could just use a non-blocking socket, but that consumes all
the CPU time. We can use a timeout; it is only supported as
a select. */
if (timeout >= 1000) {
select_timeout.tv_sec = timeout / 1000;
select_timeout.tv_usec =
1000 * (timeout - select_timeout.tv_sec * 1000);
} else {
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 1000 * timeout;
}
FD_ZERO(&read_fds);
FD_SET(ARCNET_Sock_FD, &read_fds);
max = ARCNET_Sock_FD;
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
received_bytes = read(ARCNET_Sock_FD, &buf[0], sizeof(buf));
else
return 0;
/* See if there is a problem */
if (received_bytes < 0) {
/* EAGAIN Non-blocking I/O has been selected */
/* using O_NONBLOCK and no data */
/* was immediately available for reading. */
if (errno != EAGAIN)
fprintf(stderr,
"ethernet: Read error in receiving packet: %s\n",
strerror(errno));
return 0;
}
if (received_bytes == 0)
return 0;
/* printf("arcnet: received %u bytes (offset=%02Xh %02Xh) "
"from %02Xh (proto==%02Xh)\n",
received_bytes, pkt->offset[0], pkt->offset[1],
pkt->hard.source, pkt->soft.raw[0]);
*/
if (pkt->hard.source == ARCNET_MAC_Address) {
fprintf(stderr, "arcnet: self sent packet?\n");
return 0;
}
if (pkt->soft.raw[0] != 0xCD) {
/* fprintf(stderr,"arcnet: Non-BACnet packet.\n"); */
return 0;
}
if ((pkt->hard.dest != ARCNET_MAC_Address) &&
(pkt->hard.dest != ARCNET_BROADCAST)) {
fprintf(stderr, "arcnet: This packet is not for us.\n");
return 0;
}
if ((pkt->soft.raw[1] != 0x82) || /* DSAP */
(pkt->soft.raw[2] != 0x82) || /* LSAP */
(pkt->soft.raw[3] != 0x03)) { /* LLC Control */
fprintf(stderr, "arcnet: BACnet packet has invalid LLC.\n");
return 0;
}
/* It must be addressed to us or be a Broadcast */
if ((pkt->hard.dest != ARCNET_MAC_Address) &&
(pkt->hard.dest != ARCNET_BROADCAST)) {
fprintf(stderr, "arcnet: This packet is not for us.\n");
return 0;
}
/* copy the source address */
src->mac_len = 1;
src->mac[0] = pkt->hard.source;
/* compute the PDU length */
pdu_len = received_bytes - ARC_HDR_SIZE;
pdu_len -= 4 /* SC, DSAP, SSAP, LLC Control */ ;
/* copy the buffer into the PDU */
if (pdu_len < max_pdu)
memmove(&pdu[0], &pkt->soft.raw[4], pdu_len);
/* silently ignore packets that are too large */
else
pdu_len = 0;
return pdu_len;
}
void arcnet_get_my_address(BACNET_ADDRESS * my_address)
{
int i = 0;
my_address->mac_len = 1;
my_address->mac[0] = ARCNET_MAC_Address;
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 arcnet_get_broadcast_address(BACNET_ADDRESS * dest)
{ /* destination address */
int i = 0; /* counter */
if (dest) {
dest->mac[0] = ARCNET_BROADCAST;
dest->mac_len = 1;
dest->net = BACNET_BROADCAST_NETWORK;
dest->len = 0; /* len=0 denotes broadcast address */
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
+138
View File
@@ -0,0 +1,138 @@
/*####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####*/
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include "bacdcode.h"
#include "bip.h"
#include "net.h"
static int get_local_ifr_ioctl(char *ifname, struct ifreq *ifr,
int request)
{
int fd;
int rv; /* return value */
strncpy(ifr->ifr_name, ifname, sizeof(ifr->ifr_name));
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0)
rv = fd;
else
rv = ioctl(fd, request, ifr);
return rv;
}
static int get_local_address_ioctl(char *ifname,
struct in_addr *addr, int request)
{
struct ifreq ifr = { {{0}} };
struct sockaddr_in *tcpip_address;
int rv; /* return value */
rv = get_local_ifr_ioctl(ifname, &ifr, request);
if (rv >= 0) {
tcpip_address = (struct sockaddr_in *) &ifr.ifr_addr;
memcpy(addr, &tcpip_address->sin_addr, sizeof(struct in_addr));
}
return rv;
}
void bip_set_interface(char *ifname)
{
struct in_addr local_address;
struct in_addr broadcast_address;
/* setup local address */
get_local_address_ioctl(ifname, &local_address, SIOCGIFADDR);
bip_set_addr(local_address.s_addr);
#ifdef BIP_DEBUG
fprintf(stderr, "IP Address: %s\n", inet_ntoa(local_address));
#endif
/* setup local broadcast address */
get_local_address_ioctl(ifname, &broadcast_address, SIOCGIFBRDADDR);
bip_set_broadcast_addr(broadcast_address.s_addr);
#ifdef BIP_DEBUG
fprintf(stderr, "Broadcast Address: %s\n",
inet_ntoa(broadcast_address));
#endif
}
bool bip_init(void)
{
int status = 0; /* return from socket lib calls */
struct sockaddr_in sin;
int sockopt = 0;
int sock_fd = -1;
/* assumes that the driver has already been initialized */
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
bip_set_socket(sock_fd);
if (sock_fd < 0)
return false;
/* Allow us to use the same socket for sending and receiving */
/* This makes sure that the src port is correct when sending */
sockopt = 1;
status = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
&sockopt, sizeof(sockopt));
if (status < 0) {
close(sock_fd);
bip_set_socket(-1);
return status;
}
/* allow us to send a broadcast */
status = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST,
&sockopt, sizeof(sockopt));
if (status < 0) {
close(sock_fd);
bip_set_socket(-1);
return status;
}
/* bind the socket to the local port number and IP address */
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(bip_get_port());
memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero));
status = bind(sock_fd,
(const struct sockaddr *) &sin, sizeof(struct sockaddr));
if (status < 0) {
close(sock_fd);
bip_set_socket(-1);
return false;
}
return true;
}
+404
View File
@@ -0,0 +1,404 @@
/*####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####*/
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include "net.h"
#include "bacdef.h"
#include "ethernet.h"
#include "bacdcode.h"
/* commonly used comparison address for ethernet */
uint8_t Ethernet_Broadcast[MAX_MAC_LEN] =
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/* commonly used empty address for ethernet quick compare */
uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 };
/* my local device data - MAC address */
uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0 };
static int eth802_sockfd = -1; /* 802.2 file handle */
static struct sockaddr eth_addr = { 0 }; /* used for binding 802.2 */
bool ethernet_valid(void)
{
return (eth802_sockfd >= 0);
}
void ethernet_cleanup(void)
{
if (ethernet_valid())
close(eth802_sockfd);
eth802_sockfd = -1;
return;
}
/*----------------------------------------------------------------------
Portable function to set a socket into nonblocking mode.
Calling this on a socket causes all future read() and write() calls on
that socket to do only as much as they can immediately, and return
without waiting.
If no data can be read or written, they return -1 and set errno
to EAGAIN (or EWOULDBLOCK).
Thanks to Bjorn Reese for this code.
----------------------------------------------------------------------*/
int setNonblocking(int fd)
{
int flags;
if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
flags = 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
/* opens an 802.2 socket to receive and send packets */
static int ethernet_bind(struct sockaddr *eth_addr, char *interface_name)
{
int sock_fd = -1; /* return value */
int uid = 0;
fprintf(stderr, "ethernet: opening \"%s\"\n", interface_name);
/* check to see if we are being run as root */
uid = getuid();
if (uid != 0) {
fprintf(stderr,
"ethernet: Unable to open an 802.2 socket. "
"Try running with root priveleges.\n");
return sock_fd;
}
/* note: on some systems you may have to add or enable in */
/* modules.conf (or in modutils/alias on Debian with update-modules) */
/* alias net-pf-17 af_packet */
/* Then follow it by: # modprobe af_packet */
/* Attempt to open the socket for 802.2 ethernet frames */
if ((sock_fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_802_2))) < 0) {
/* Error occured */
fprintf(stderr,
"ethernet: Error opening socket: %s\n", strerror(errno));
fprintf(stderr,
"You might need to add the following to modules.conf\n"
"(or in /etc/modutils/alias on Debian with update-modules):\n"
"alias net-pf-17 af_packet\n"
"Also, add af_packet to /etc/modules.\n"
"Then follow it by:\n" "# modprobe af_packet\n");
exit(-1);
}
/* Bind the socket to an address */
eth_addr->sa_family = PF_INET;
/* Clear the memory before copying */
memset(eth_addr->sa_data, '\0', sizeof(eth_addr->sa_data));
/* Strcpy the interface name into the address */
strncpy(eth_addr->sa_data, interface_name,
sizeof(eth_addr->sa_data) - 1);
fprintf(stderr, "ethernet: binding \"%s\"\n", eth_addr->sa_data);
/* Attempt to bind the socket to the interface */
if (bind(sock_fd, eth_addr, sizeof(struct sockaddr)) != 0) {
/* Bind problem, close socket and return */
fprintf(stderr,
"ethernet: Unable to bind 802.2 socket : %s\n",
strerror(errno));
fprintf(stderr,
"You might need to add the following to modules.conf\n"
"(or in /etc/modutils/alias on Debian with update-modules):\n"
"alias net-pf-17 af_packet\n"
"Also, add af_packet to /etc/modules.\n"
"Then follow it by:\n" "# modprobe af_packet\n");
/* Close the socket */
close(sock_fd);
exit(-1);
}
atexit(ethernet_cleanup);
return sock_fd;
}
/* function to find the local ethernet MAC address */
static int get_local_hwaddr(const char *ifname, unsigned char *mac)
{
struct ifreq ifr;
int fd;
int rv; /* return value - error value from df or ioctl call */
/* determine the local MAC address */
strcpy(ifr.ifr_name, ifname);
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0)
rv = fd;
else {
rv = ioctl(fd, SIOCGIFHWADDR, &ifr);
if (rv >= 0) /* worked okay */
memcpy(mac, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
}
return rv;
}
bool ethernet_init(char *interface_name)
{
get_local_hwaddr(interface_name, Ethernet_MAC_Address);
eth802_sockfd = ethernet_bind(&eth_addr, interface_name);
return ethernet_valid();
}
/* function to send a packet out the 802.2 socket */
/* returns number of bytes sent on success, negative on failure */
int ethernet_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 i = 0; /* counter */
int bytes = 0;
BACNET_ADDRESS src = { 0 }; /* source address for npdu */
uint8_t mtu[MAX_MPDU] = { 0 }; /* our buffer */
int mtu_len = 0;
(void) npdu_data;
/* load the BACnet address for NPDU data */
for (i = 0; i < 6; i++) {
src.mac[i] = Ethernet_MAC_Address[i];
src.mac_len++;
}
/* don't waste time if the socket is not valid */
if (eth802_sockfd < 0) {
fprintf(stderr, "ethernet: 802.2 socket is invalid!\n");
return -1;
}
/* load destination ethernet MAC address */
if (dest->mac_len == 6) {
for (i = 0; i < 6; i++) {
mtu[i] = dest->mac[i];
}
} else {
fprintf(stderr, "ethernet: invalid destination MAC address!\n");
return -2;
}
/* load source ethernet MAC address */
if (src.mac_len == 6) {
for (i = 0; i < 6; i++) {
mtu[6 + i] = src.mac[i];
}
} else {
fprintf(stderr, "ethernet: invalid source MAC address!\n");
return -3;
}
/* Logical PDU portion */
mtu[14] = 0x82; /* DSAP for BACnet */
mtu[15] = 0x82; /* SSAP for BACnet */
mtu[16] = 0x03; /* Control byte in header */
mtu_len = 17;
if ((mtu_len + pdu_len) > MAX_MPDU) {
fprintf(stderr, "ethernet: PDU is too big to send!\n");
return -4;
}
memcpy(&mtu[mtu_len], pdu, pdu_len);
mtu_len += pdu_len;
/* packet length - only the logical portion, not the address */
encode_unsigned16(&mtu[12], 3 + pdu_len);
/* Send the packet */
bytes =
sendto(eth802_sockfd, &mtu, mtu_len, 0,
(struct sockaddr *) &eth_addr, sizeof(struct sockaddr));
/* did it get sent? */
if (bytes < 0)
fprintf(stderr, "ethernet: Error sending packet: %s\n",
strerror(errno));
return bytes;
}
/* receives an 802.2 framed packet */
/* returns the number of octets in the PDU, or zero on failure */
uint16_t ethernet_receive(BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t max_pdu, /* amount of space available in the PDU */
unsigned timeout)
{ /* number of milliseconds to wait for a packet */
int received_bytes;
uint8_t buf[MAX_MPDU] = { 0 }; /* data */
uint16_t pdu_len = 0; /* return value */
fd_set read_fds;
int max;
struct timeval select_timeout;
/* Make sure the socket is open */
if (eth802_sockfd <= 0)
return 0;
/* we could just use a non-blocking socket, but that consumes all
the CPU time. We can use a timeout; it is only supported as
a select. */
if (timeout >= 1000) {
select_timeout.tv_sec = timeout / 1000;
select_timeout.tv_usec =
1000 * (timeout - select_timeout.tv_sec * 1000);
} else {
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 1000 * timeout;
}
FD_ZERO(&read_fds);
FD_SET(eth802_sockfd, &read_fds);
max = eth802_sockfd;
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
received_bytes = read(eth802_sockfd, &buf[0], sizeof(buf));
else
return 0;
/* See if there is a problem */
if (received_bytes < 0) {
/* EAGAIN Non-blocking I/O has been selected */
/* using O_NONBLOCK and no data */
/* was immediately available for reading. */
if (errno != EAGAIN)
fprintf(stderr,
"ethernet: Read error in receiving packet: %s\n",
strerror(errno));
return 0;
}
if (received_bytes == 0)
return 0;
/* the signature of an 802.2 BACnet packet */
if ((buf[14] != 0x82) && (buf[15] != 0x82)) {
/*fprintf(stderr,"ethernet: Non-BACnet packet\n"); */
return 0;
}
/* copy the source address */
src->mac_len = 6;
memmove(src->mac, &buf[6], 6);
/* check destination address for when */
/* the Ethernet card is in promiscious mode */
if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0)
&& (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) {
/*fprintf(stderr, "ethernet: This packet isn't for us\n"); */
return 0;
}
(void) decode_unsigned16(&buf[12], &pdu_len);
pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ;
/* copy the buffer into the PDU */
if (pdu_len < max_pdu)
memmove(&pdu[0], &buf[17], pdu_len);
/* ignore packets that are too large */
else
pdu_len = 0;
return pdu_len;
}
void ethernet_set_my_address(BACNET_ADDRESS * my_address)
{
int i = 0;
for (i = 0; i < 6; i++) {
Ethernet_MAC_Address[i] = my_address->mac[i];
}
return;
}
void ethernet_get_my_address(BACNET_ADDRESS * my_address)
{
int i = 0;
my_address->mac_len = 0;
for (i = 0; i < 6; i++) {
my_address->mac[i] = Ethernet_MAC_Address[i];
my_address->mac_len++;
}
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 ethernet_get_broadcast_address(BACNET_ADDRESS * dest)
{ /* destination address */
int i = 0; /* counter */
if (dest) {
for (i = 0; i < 6; i++) {
dest->mac[i] = Ethernet_Broadcast[i];
}
dest->mac_len = 6;
dest->net = BACNET_BROADCAST_NETWORK;
dest->len = 0; /* denotes broadcast address */
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
void ethernet_debug_address(const char *info, BACNET_ADDRESS * dest)
{
int i = 0; /* counter */
if (info)
fprintf(stderr, "%s", info);
if (dest) {
fprintf(stderr, "Address:\n");
fprintf(stderr, " MAC Length=%d\n", dest->mac_len);
fprintf(stderr, " MAC Address=");
for (i = 0; i < MAX_MAC_LEN; i++) {
fprintf(stderr, "%02X ", (unsigned) dest->mac[i]);
}
fprintf(stderr, "\n");
fprintf(stderr, " Net=%hu\n", dest->net);
fprintf(stderr, " Len=%d\n", dest->len);
fprintf(stderr, " Adr=");
for (i = 0; i < MAX_MAC_LEN; i++) {
fprintf(stderr, "%02X ", (unsigned) dest->adr[i]);
}
fprintf(stderr, "\n");
}
return;
}
+298
View File
@@ -0,0 +1,298 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include "config.h"
#include "address.h"
#include "bacdef.h"
#include "handlers.h"
#include "client.h"
#include "bacdcode.h"
#include "npdu.h"
#include "apdu.h"
#include "iam.h"
#include "tsm.h"
#include "device.h"
#include "bacfile.h"
#include "datalink.h"
#include "net.h"
#include "txbuf.h"
/* This is an example application using the BACnet Stack on Linux */
bool Who_Is_Request = true;
/* buffers used for receiving */
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
static void LocalIAmHandler(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src)
{
int len = 0;
uint32_t device_id = 0;
unsigned max_apdu = 0;
int segmentation = 0;
uint16_t vendor_id = 0;
(void) src;
(void) service_len;
len = iam_decode_service_request(service_request,
&device_id, &max_apdu, &segmentation, &vendor_id);
fprintf(stderr, "Received I-Am Request");
if (len != -1) {
fprintf(stderr, " from %u!\n", device_id);
address_add(device_id, max_apdu, src);
} else
fprintf(stderr, "!\n");
return;
}
static void Read_Properties(void)
{
uint32_t device_id = 0;
bool status = false;
unsigned max_apdu = 0;
BACNET_ADDRESS src;
bool next_device = false;
static unsigned index = 0;
static unsigned property = 0;
/* list of required (and some optional and proprietary)
properties in the Device Object. Note that this demo
tests for error messages so that the device doesn't have
to have all the properties listed here. */
const int object_props[] = {
PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME,
PROP_OBJECT_TYPE,
PROP_SYSTEM_STATUS,
PROP_VENDOR_NAME,
PROP_VENDOR_IDENTIFIER,
PROP_MODEL_NAME,
PROP_FIRMWARE_REVISION,
PROP_APPLICATION_SOFTWARE_VERSION,
PROP_PROTOCOL_VERSION,
PROP_PROTOCOL_CONFORMANCE_CLASS,
PROP_PROTOCOL_SERVICES_SUPPORTED,
PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED,
PROP_MAX_APDU_LENGTH_ACCEPTED,
PROP_SEGMENTATION_SUPPORTED,
PROP_LOCAL_TIME,
PROP_LOCAL_DATE,
PROP_UTC_OFFSET,
PROP_DAYLIGHT_SAVINGS_STATUS,
PROP_APDU_SEGMENT_TIMEOUT,
PROP_APDU_TIMEOUT,
PROP_NUMBER_OF_APDU_RETRIES,
PROP_TIME_SYNCHRONIZATION_RECIPIENTS,
PROP_MAX_MASTER,
PROP_MAX_INFO_FRAMES,
PROP_DEVICE_ADDRESS_BINDING,
/* note: PROP_OBJECT_LIST is missing because
the result can be very large. Read index 0
which gives us the number of objects in the list,
and then we can read index 1, 2.. n one by one,
rather than trying to read the entire object
list in one message. */
/* some proprietary properties */
514, 515,
/* end of list */
-1
};
if (address_count()) {
if (address_get_by_index(index, &device_id, &max_apdu, &src)) {
if (object_props[property] < 0)
next_device = true;
else {
/* note: if we wanted to do this synchronously, we would get the
invoke ID from the sending of the request, and wait until we
got the reply with matching invoke ID or the TSM of the
invoke ID expired. This demo is doing things asynchronously. */
status = Send_Read_Property_Request(device_id, /* destination device */
OBJECT_DEVICE,
device_id, object_props[property], BACNET_ARRAY_ALL);
if (status)
property++;
}
} else
next_device = true;
if (next_device) {
next_device = false;
index++;
if (index >= MAX_ADDRESS_CACHE)
index = 0;
property = 0;
}
}
return;
}
static void Init_Service_Handlers(void)
{
/* we need to handle who-is to support dynamic device binding */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
handler_who_is);
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM,
LocalIAmHandler);
/* set the handler for all the services we don't implement */
/* It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler
(handler_unrecognized_service);
/* Set the handlers for any confirmed services that we support. */
/* We must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
handler_write_property);
apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE,
handler_atomic_read_file);
/* handle the data coming back from confirmed requests */
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property_ack);
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE,
handler_atomic_read_file_ack);
}
static void print_address_cache(void)
{
unsigned i, j;
BACNET_ADDRESS address;
uint32_t device_id = 0;
unsigned max_apdu = 0;
fprintf(stderr, "Device\tMAC\tMaxAPDU\tNet\n");
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (address_get_by_index(i, &device_id, &max_apdu, &address)) {
fprintf(stderr, "%u\t", device_id);
for (j = 0; j < address.mac_len; j++) {
fprintf(stderr, "%02X", address.mac[j]);
}
fprintf(stderr, "\t");
fprintf(stderr, "%hu\t", max_apdu);
fprintf(stderr, "%hu\n", address.net);
}
}
}
static void print_tsm_stats(void)
{
int idle = 0;
int total = 0;
idle = tsm_transaction_idle_count();
total = MAX_TSM_TRANSACTIONS;
fprintf(stderr, "TSM: %d idle of %d transactions\n", idle, total);
}
static void sig_handler(int signo)
{
datalink_cleanup();
print_address_cache();
print_tsm_stats();
exit(0);
}
int main(int argc, char *argv[])
{
BACNET_ADDRESS src = { 0 }; /* address where message came from */
uint16_t pdu_len = 0;
unsigned timeout = 100; /* milliseconds */
unsigned count = 0; /* milliseconds */
time_t start_time;
time_t new_time = 0;
start_time = time(NULL); /* get current time */
/* Linux specials */
signal(SIGINT, sig_handler);
signal(SIGHUP, sig_handler);
signal(SIGTERM, sig_handler);
/* setup this BACnet Server device */
Device_Set_Object_Instance_Number(111);
Init_Service_Handlers();
#ifdef BACDL_ETHERNET
/* init the physical layer */
if (!ethernet_init("eth0"))
return 1;
#endif
#ifdef BACDL_BIP
bip_set_interface("eth0");
bip_set_port(0xBAC0);
if (!bip_init())
return 1;
#endif
#ifdef BACDL_ARCNET
if (!arcnet_init("arc0"))
return 1;
#endif
/* loop forever */
for (;;) {
/* input */
new_time = time(NULL);
/* returns 0 bytes on timeout */
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
/* process */
if (pdu_len) {
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
if (new_time > start_time) {
tsm_timer_milliseconds(new_time - start_time * 1000);
start_time = new_time;
}
if (I_Am_Request) {
I_Am_Request = false;
iam_send(&Handler_Transmit_Buffer[0]);
} else if (Who_Is_Request) {
Who_Is_Request = false;
Send_WhoIs(-1, -1);
}
/* output */
/* some round robin task switching */
count++;
switch (count) {
case 1:
/* used for testing, but kind of noisy on the network */
/*Read_Properties(); */
break;
case 2:
break;
default:
count = 0;
break;
}
/* blink LEDs, Turn on or off outputs, etc */
}
return 0;
}
+99
View File
@@ -0,0 +1,99 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
#ifndef NET_H
#define NET_H
/* common unix sockets headers needed */
#include <sys/types.h> /* basic system data types */
#include <sys/time.h> /* timeval{} for select() */
#include <time.h> /* timespec{} for pselect() */
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <arpa/inet.h> /* inet(3) functions */
#include <fcntl.h> /* for nonblocking */
#include <netdb.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h> /* for S_xxx file mode constants */
#include <sys/uio.h> /* for iovec{} and readv/writev */
#include <unistd.h>
#include <sys/wait.h>
#include <sys/un.h> /* for Unix domain sockets */
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h> /* for convenience */
#endif
#ifdef HAVE_POLL_H
# include <poll.h> /* for convenience */
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h> /* for convenience */
#endif
/* Three headers are normally needed for socket/file ioctl's:
* <sys/ioctl.h>, <sys/filio.h>, and <sys/sockio.h>.
*/
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#ifdef HAVE_SYS_SOCKIO_H
# include <sys/sockio.h>
#endif
#ifdef HAVE_PTHREAD_H
# include <pthread.h>
#endif
#define ENUMS
#include <sys/socket.h>
#include <net/route.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <features.h> /* for the glibc version number */
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h> /* the L2 protocols */
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_arcnet.h>
#include <linux/if_ether.h>
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <netdb.h>
#endif
@@ -0,0 +1,2 @@
This is a port to Linux for testing.
The unit tests can be run via the test.sh script.
+93
View File
@@ -0,0 +1,93 @@
/*####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####*/
/* The module handles sending data out the RS-485 port */
/* and handles receiving data from the RS-485 port. */
/* Customize this file for your specific hardware */
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "mstp.h"
/* 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) */
/* in order to avoid line contention */
while (mstp_port->Turn_Around_Waiting) {
/* wait, yield, or whatever */
}
/* Disable the receiver, and enable the transmit line driver. */
while (nbytes) {
putc(*buffer, stderr);
buffer++;
nbytes--;
}
/* 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)
{
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; */
}
}
+29
View File
@@ -0,0 +1,29 @@
// $Id$
// File: 18f252.lkr
// Sample linker script for the PIC18F252 processor
LIBPATH .
FILES c018i.o
FILES clib.lib
FILES p18f252.lib
CODEPAGE NAME=vectors START=0x0 END=0x29 PROTECTED
CODEPAGE NAME=page START=0x2A END=0x7FFF
CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED
CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED
CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED
CODEPAGE NAME=eedata START=0xF00000 END=0xF000FF PROTECTED
ACCESSBANK NAME=accessram START=0x0 END=0x7F
DATABANK NAME=gpr0 START=0x80 END=0xFF
DATABANK NAME=gpr1 START=0x100 END=0x1FF
DATABANK NAME=gpr2 START=0x200 END=0x2FF
DATABANK NAME=gpr3 START=0x300 END=0x3FF
DATABANK NAME=gpr4 START=0x400 END=0x4FF
DATABANK NAME=gpr5 START=0x500 END=0x5FF
ACCESSBANK NAME=accesssfr START=0xF80 END=0xFFF PROTECTED
SECTION NAME=CONFIG ROM=config
STACK SIZE=0x100 RAM=gpr5
@@ -0,0 +1,131 @@
[HEADER]
magic_cookie={66E99B07-E706-4689-9E80-9B2582898A13}
file_version=1.0
[PATH_INFO]
dir_src=
dir_bin=
dir_tmp=
dir_sin=
dir_inc=C:\code\bacnet-stack;C:\code\bacnet-stack\demo\handler;C:\code\bacnet-stack\demo\object;C:\code\bacnet-stack\ports\pic18
dir_lib=C:\mcc18\lib
dir_lkr=
[CAT_FILTERS]
filter_src=*.asm;*.c
filter_inc=*.h;*.inc
filter_obj=*.o
filter_lib=*.lib
filter_lkr=*.lkr
[OTHER_FILES]
file_000=no
file_001=no
file_002=no
file_003=no
file_004=no
file_005=no
file_006=no
file_007=no
file_008=no
file_009=no
file_010=no
file_011=no
file_012=no
file_013=no
file_014=no
file_015=no
file_016=no
file_017=no
file_018=no
file_019=no
file_020=no
file_021=no
file_022=no
file_023=no
file_024=no
file_025=no
file_026=no
file_027=no
file_028=no
file_029=no
file_030=no
file_031=no
file_032=no
file_033=no
file_034=no
file_035=no
file_036=no
file_037=no
file_038=no
file_039=no
file_040=no
file_041=no
file_042=no
file_043=no
file_044=no
file_045=no
file_046=no
file_047=no
file_048=no
file_049=no
file_050=no
file_051=no
[FILE_INFO]
file_000=C:\code\bacnet-stack\abort.c
file_001=C:\code\bacnet-stack\apdu.c
file_002=C:\code\bacnet-stack\bacapp.c
file_003=C:\code\bacnet-stack\bacdcode.c
file_004=C:\code\bacnet-stack\bacerror.c
file_005=C:\code\bacnet-stack\bacstr.c
file_006=C:\code\bacnet-stack\crc.c
file_007=C:\code\bacnet-stack\datalink.c
file_008=C:\code\bacnet-stack\dcc.c
file_009=C:\code\bacnet-stack\iam.c
file_010=C:\code\bacnet-stack\mstp.c
file_011=C:\code\bacnet-stack\npdu.c
file_012=C:\code\bacnet-stack\rd.c
file_013=C:\code\bacnet-stack\reject.c
file_014=C:\code\bacnet-stack\rp.c
file_015=C:\code\bacnet-stack\whois.c
file_016=C:\code\bacnet-stack\demo\handler\h_dcc.c
file_017=C:\code\bacnet-stack\demo\handler\h_rd.c
file_018=main.c
file_019=dlmstp.c
file_020=rs485.c
file_021=device.c
file_022=C:\code\bacnet-stack\wp.h
file_023=C:\code\bacnet-stack\abort.h
file_024=C:\code\bacnet-stack\apdu.h
file_025=C:\code\bacnet-stack\bacapp.h
file_026=C:\code\bacnet-stack\bacdcode.h
file_027=C:\code\bacnet-stack\bacdef.h
file_028=C:\code\bacnet-stack\bacenum.h
file_029=C:\code\bacnet-stack\bacerror.h
file_030=C:\code\bacnet-stack\bacstr.h
file_031=C:\code\bacnet-stack\config.h
file_032=C:\code\bacnet-stack\crc.h
file_033=C:\code\bacnet-stack\datalink.h
file_034=C:\code\bacnet-stack\dcc.h
file_035=C:\code\bacnet-stack\dlmstp.h
file_036=C:\code\bacnet-stack\iam.h
file_037=C:\code\bacnet-stack\mstp.h
file_038=C:\code\bacnet-stack\npdu.h
file_039=C:\code\bacnet-stack\rd.h
file_040=C:\code\bacnet-stack\reject.h
file_041=C:\code\bacnet-stack\rp.h
file_042=C:\code\bacnet-stack\rs485.h
file_043=C:\code\bacnet-stack\whois.h
file_044=C:\code\bacnet-stack\demo\handler\client.h
file_045=C:\code\bacnet-stack\demo\handler\handlers.h
file_046=C:\code\bacnet-stack\demo\object\ai.h
file_047=C:\code\bacnet-stack\demo\object\ao.h
file_048=C:\code\bacnet-stack\demo\object\device.h
file_049=stdbool.h
file_050=stdint.h
file_051=hardware.h
[SUITE_INFO]
suite_guid={5B7D72DD-9861-47BD-9F60-2BE967BF8416}
suite_state=
[TOOL_SETTINGS]
TS{DD2213A8-6310-47B1-8376-9430CDFC013F}=
TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}=/o"$(TARGETBASE).cof" /M"$(BINDIR_)$(TARGETBASE).map"
TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}=-DPRINT_ENABLED=0 -DBACDL_MSTP -DBIG_ENDIAN=1 -mL -Ls -Ou- -Ot- -Ob- -Op- -Or- -Od- -Opa-
TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}=
Binary file not shown.
+151
View File
@@ -0,0 +1,151 @@
[HEADER]
magic_cookie={66E99B07-E706-4689-9E80-9B2582898A13}
file_version=1.0
[PATH_INFO]
dir_src=
dir_bin=
dir_tmp=
dir_sin=
dir_inc=c:\mcc18\h;c:\code\bacnet-stack;c:\code\bacnet-stack\ports\pic18;c:\code\bacnet-stack\demo\object;c:\code\bacnet-stack\demo\handler
dir_lib=c:\mcc18\lib
dir_lkr=
[CAT_FILTERS]
filter_src=*.asm;*.c
filter_inc=*.h;*.inc
filter_obj=*.o
filter_lib=*.lib
filter_lkr=*.lkr
[OTHER_FILES]
file_000=no
file_001=no
file_002=no
file_003=no
file_004=no
file_005=no
file_006=no
file_007=no
file_008=no
file_009=no
file_010=no
file_011=no
file_012=no
file_013=no
file_014=no
file_015=no
file_016=no
file_017=no
file_018=no
file_019=no
file_020=no
file_021=no
file_022=no
file_023=no
file_024=no
file_025=no
file_026=no
file_027=no
file_028=no
file_029=no
file_030=no
file_031=no
file_032=no
file_033=no
file_034=no
file_035=no
file_036=no
file_037=no
file_038=no
file_039=no
file_040=no
file_041=no
file_042=no
file_043=no
file_044=no
file_045=no
file_046=no
file_047=no
file_048=no
file_049=no
file_050=no
file_051=no
file_052=no
file_053=no
file_054=no
file_055=no
file_056=no
file_057=no
file_058=no
[FILE_INFO]
file_000=rs485.c
file_001=main.c
file_002=init.c
file_003=isr.c
file_004=timer.c
file_005=C:\code\bacnet-stack\rp.c
file_006=C:\code\bacnet-stack\whois.c
file_007=C:\code\bacnet-stack\abort.c
file_008=C:\code\bacnet-stack\apdu.c
file_009=C:\code\bacnet-stack\bacdcode.c
file_010=C:\code\bacnet-stack\bacerror.c
file_011=C:\code\bacnet-stack\crc.c
file_012=C:\code\bacnet-stack\datalink.c
file_013=C:\code\bacnet-stack\iam.c
file_014=C:\code\bacnet-stack\mstp.c
file_015=C:\code\bacnet-stack\npdu.c
file_016=C:\code\bacnet-stack\reject.c
file_017=C:\code\bacnet-stack\dcc.c
file_018=C:\code\bacnet-stack\bacstr.c
file_019=C:\code\bacnet-stack\bacapp.c
file_020=device.c
file_021=dlmstp.c
file_022=C:\code\bacnet-stack\demo\handler\h_rd.c
file_023=C:\code\bacnet-stack\demo\handler\h_dcc.c
file_024=C:\code\bacnet-stack\rd.c
file_025=C:\code\bacnet-stack\demo\handler\txbuf.c
file_026=C:\code\bacnet-stack\demo\handler\h_whois.c
file_027=C:\code\bacnet-stack\demo\handler\noserv.c
file_028=stdbool.h
file_029=stdint.h
file_030=init.h
file_031=timer.h
file_032=C:\code\bacnet-stack\whois.h
file_033=C:\code\bacnet-stack\abort.h
file_034=C:\code\bacnet-stack\apdu.h
file_035=C:\code\bacnet-stack\bacdcode.h
file_036=C:\code\bacnet-stack\bacdef.h
file_037=C:\code\bacnet-stack\bacenum.h
file_038=C:\code\bacnet-stack\bacerror.h
file_039=C:\code\bacnet-stack\bigend.h
file_040=C:\code\bacnet-stack\bits.h
file_041=C:\code\bacnet-stack\bytes.h
file_042=C:\code\bacnet-stack\config.h
file_043=C:\code\bacnet-stack\crc.h
file_044=C:\code\bacnet-stack\datalink.h
file_045=C:\code\bacnet-stack\dlmstp.h
file_046=C:\code\bacnet-stack\mstp.h
file_047=C:\code\bacnet-stack\npdu.h
file_048=C:\code\bacnet-stack\reject.h
file_049=C:\code\bacnet-stack\ringbuf.h
file_050=C:\code\bacnet-stack\rs485.h
file_051=C:\code\bacnet-stack\demo\object\ai.h
file_052=C:\code\bacnet-stack\demo\object\ao.h
file_053=C:\code\bacnet-stack\demo\object\device.h
file_054=C:\code\bacnet-stack\dcc.h
file_055=C:\code\bacnet-stack\bacstr.h
file_056=C:\code\bacnet-stack\bacapp.h
file_057=C:\code\bacnet-stack\rd.h
file_058=18f252.lkr
[SUITE_INFO]
suite_guid={5B7D72DD-9861-47BD-9F60-2BE967BF8416}
suite_state=
[TOOL_SETTINGS]
TS{DD2213A8-6310-47B1-8376-9430CDFC013F}=
TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}=/m"$(BINDIR_)$(TARGETBASE).map" /o"$(TARGETBASE).cof"
TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}=-DTSM_ENABLED=0 -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -mL -pa=1
TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}=
TS{DD2213A8-6310-47B1-8376-9430CDFC013F}001=
TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}001=/o"$(TARGETBASE).cof"
TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}001=-Ou- -Ot- -Ob- -Op- -Or- -Od- -Opa-
TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}001=
[ACTIVE_FILE_SETTINGS]
TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}001_active=yes
Binary file not shown.
+408
View File
@@ -0,0 +1,408 @@
/**************************************************************************
*
* Copyright (C) 2005,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 <string.h> /* for memmove */
#include "bacdef.h"
#include "bacdcode.h"
#include "bacenum.h"
#include "config.h" /* the custom stuff */
#include "apdu.h"
#include "device.h" /* me */
/* note: you really only need to define variables for
properties that are writable or that may change.
The properties that are constant can be hard coded
into the read-property encoding. */
static uint32_t Object_Instance_Number = 0;
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
static uint16_t APDU_Timeout = 3000;
static uint8_t Number_Of_APDU_Retries = 3;
/* methods to manipulate the data */
uint32_t Device_Object_Instance_Number(void)
{
return Object_Instance_Number;
}
bool Device_Set_Object_Instance_Number(uint32_t object_id)
{
bool status = true; /* return value */
if (object_id <= BACNET_MAX_INSTANCE)
Object_Instance_Number = object_id;
else
status = false;
return status;
}
bool Device_Valid_Object_Instance_Number(uint32_t object_id)
{
/* BACnet allows for a wildcard instance number */
return ((Object_Instance_Number == object_id) ||
(object_id == BACNET_MAX_INSTANCE));
}
BACNET_DEVICE_STATUS Device_System_Status(void)
{
return System_Status;
}
void Device_Set_System_Status(BACNET_DEVICE_STATUS status)
{
/* FIXME: bounds check? */
System_Status = status;
}
/* FIXME: put your vendor ID here! */
uint16_t Device_Vendor_Identifier(void)
{
return 0;
}
uint8_t Device_Protocol_Version(void)
{
return 1;
}
uint8_t Device_Protocol_Revision(void)
{
return 5;
}
/* FIXME: MAX_APDU is defined in config.ini - set it! */
uint16_t Device_Max_APDU_Length_Accepted(void)
{
return MAX_APDU;
}
BACNET_SEGMENTATION Device_Segmentation_Supported(void)
{
return SEGMENTATION_NONE;
}
uint16_t Device_APDU_Timeout(void)
{
return APDU_Timeout;
}
/* in milliseconds */
void Device_Set_APDU_Timeout(uint16_t timeout)
{
APDU_Timeout = timeout;
}
uint8_t Device_Number_Of_APDU_Retries(void)
{
return Number_Of_APDU_Retries;
}
void Device_Set_Number_Of_APDU_Retries(uint8_t retries)
{
Number_Of_APDU_Retries = retries;
}
uint8_t Device_Database_Revision(void)
{
return 0;
}
/* Since many network clients depend on the object list */
/* for discovery, it must be consistent! */
unsigned Device_Object_List_Count(void)
{
unsigned count = 1;
return count;
}
bool Device_Object_List_Identifier(unsigned array_index,
int *object_type, uint32_t * instance)
{
bool status = false;
unsigned object_index = 0;
unsigned object_count = 0;
/* device object */
if (array_index == 1) {
*object_type = OBJECT_DEVICE;
*instance = Object_Instance_Number;
status = true;
}
return status;
}
/* return the length of the apdu encoded or -1 for error or
-2 for abort */
int Device_Encode_Property_APDU(uint8_t * apdu,
BACNET_PROPERTY_ID property,
int32_t array_index,
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
{
int apdu_len = 0; /* return value */
int len = 0; /* apdu len intermediate value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned i = 0;
int object_type = 0;
uint32_t instance = 0;
unsigned count = 0;
/* FIXME: change the hardcoded names to suit your application */
switch (property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE,
Object_Instance_Number);
break;
case PROP_OBJECT_NAME:
characterstring_init_ansi(&char_string, (char *) "TD");
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_DEVICE);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(&char_string, (char *) "Tiny");
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_SYSTEM_STATUS:
apdu_len =
encode_tagged_enumerated(&apdu[0], Device_System_Status());
break;
case PROP_VENDOR_NAME:
characterstring_init_ansi(&char_string, (char *) "ASHRAE");
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_VENDOR_IDENTIFIER:
apdu_len =
encode_tagged_unsigned(&apdu[0], Device_Vendor_Identifier());
break;
case PROP_MODEL_NAME:
characterstring_init_ansi(&char_string, (char *) "GNU");
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_FIRMWARE_REVISION:
characterstring_init_ansi(&char_string, (char *) "1.0");
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_APPLICATION_SOFTWARE_VERSION:
characterstring_init_ansi(&char_string, (char *) "1.0");
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_PROTOCOL_VERSION:
apdu_len =
encode_tagged_unsigned(&apdu[0], Device_Protocol_Version());
break;
case PROP_PROTOCOL_REVISION:
apdu_len =
encode_tagged_unsigned(&apdu[0], Device_Protocol_Revision());
break;
/* BACnet Legacy Support */
case PROP_PROTOCOL_CONFORMANCE_CLASS:
apdu_len = encode_tagged_unsigned(&apdu[0], 1);
break;
case PROP_PROTOCOL_SERVICES_SUPPORTED:
/* Note: list of services that are executed, not initiated. */
bitstring_init(&bit_string);
for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) {
/* automatic lookup based on handlers set */
bitstring_set_bit(&bit_string, (uint8_t) i,
apdu_service_supported(i));
}
apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string);
break;
case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
/* Note: this is the list of objects that can be in this device,
not a list of objects that this device can access */
bitstring_init(&bit_string);
for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) {
/* initialize all the object types to not-supported */
bitstring_set_bit(&bit_string, (uint8_t) i, false);
}
/* FIXME: indicate the objects that YOU support */
bitstring_set_bit(&bit_string, OBJECT_DEVICE, true);
apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string);
break;
case PROP_OBJECT_LIST:
count = Device_Object_List_Count();
/* Array element zero is the number of objects in the list */
if (array_index == 0)
apdu_len = encode_tagged_unsigned(&apdu[0], count);
/* if no index was specified, then try to encode the entire list */
/* into one packet. Note that more than likely you will have */
/* to return an error if the number of encoded objects exceeds */
/* your maximum APDU size. */
else if (array_index == BACNET_ARRAY_ALL) {
for (i = 1; i <= count; i++) {
if (Device_Object_List_Identifier(i, &object_type,
&instance)) {
len =
encode_tagged_object_id(&apdu[apdu_len],
object_type, instance);
apdu_len += len;
/* assume next one is the same size as this one */
/* can we all fit into the APDU? */
if ((apdu_len + len) >= MAX_APDU) {
apdu_len = -2;
break;
}
} else {
/* error: internal error? */
*error_class = ERROR_CLASS_SERVICES;
*error_code = ERROR_CODE_OTHER;
apdu_len = -1;
break;
}
}
} else {
if (Device_Object_List_Identifier(array_index, &object_type,
&instance))
apdu_len =
encode_tagged_object_id(&apdu[0], object_type,
instance);
else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = -1;
}
}
break;
case PROP_MAX_APDU_LENGTH_ACCEPTED:
apdu_len = encode_tagged_unsigned(&apdu[0],
Device_Max_APDU_Length_Accepted());
break;
case PROP_SEGMENTATION_SUPPORTED:
apdu_len = encode_tagged_enumerated(&apdu[0],
Device_Segmentation_Supported());
break;
case PROP_APDU_TIMEOUT:
apdu_len = encode_tagged_unsigned(&apdu[0], APDU_Timeout);
break;
case PROP_NUMBER_OF_APDU_RETRIES:
apdu_len =
encode_tagged_unsigned(&apdu[0],
Device_Number_Of_APDU_Retries());
break;
case PROP_DEVICE_ADDRESS_BINDING:
/* FIXME: encode the list here, if it exists */
break;
case PROP_DATABASE_REVISION:
apdu_len =
encode_tagged_unsigned(&apdu[0], Device_Database_Revision());
break;
default:
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = -1;
break;
}
return apdu_len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testDevice(Test * pTest)
{
bool status = false;
const char *name = "Patricia";
status = Device_Set_Object_Instance_Number(0);
ct_test(pTest, Device_Object_Instance_Number() == 0);
ct_test(pTest, status == true);
status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
ct_test(pTest, Device_Object_Instance_Number() == BACNET_MAX_INSTANCE);
ct_test(pTest, status == true);
status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE / 2);
ct_test(pTest,
Device_Object_Instance_Number() == (BACNET_MAX_INSTANCE / 2));
ct_test(pTest, status == true);
status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE + 1);
ct_test(pTest,
Device_Object_Instance_Number() != (BACNET_MAX_INSTANCE + 1));
ct_test(pTest, status == false);
return;
}
#ifdef TEST_DEVICE
/* stubs to dependencies to keep unit test simple */
char *Analog_Input_Name(uint32_t object_instance)
{
(void) object_instance;
return "";
}
unsigned Analog_Input_Count(void)
{
return 0;
}
uint32_t Analog_Input_Index_To_Instance(unsigned index)
{
return index;
}
char *Analog_Output_Name(uint32_t object_instance)
{
(void) object_instance;
return "";
}
unsigned Analog_Output_Count(void)
{
return 0;
}
uint32_t Analog_Output_Index_To_Instance(unsigned index)
{
return index;
}
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Tiny Device", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testDevice);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_DEVICE */
#endif /* TEST */
+328
View File
@@ -0,0 +1,328 @@
/**************************************************************************
*
* 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"
#include "eeprom.h"
/* Number of MS/TP Packets Rx/Tx
*/
uint16_t MSTP_Packets = 0;
/* receive buffer */
#pragma udata MSTP_RxData
static DLMSTP_PACKET Receive_Buffer;
/* temp buffer for NPDU insertion */
/* local MS/TP port data - shared with RS-485 */
#pragma udata MSTP_PortData
volatile struct mstp_port_struct_t MSTP_Port;
#pragma udata
#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;}
/* This defines the number of edit fields for this module
*/
#define MAX_EDIT_FIELD 1
static uint8_t EditField = 0;
/* *************************************************************************
DESCRIPTION: This function handles incrementing or decrementing our
EditField
RETURN: none
ALGORITHM: none
NOTES: Pass a #>0 to increment #<0 to decrement
*************************************************************************** */
void dlmstp_SetEditField(signed char state)
{ /* direction our editfield is moving */
if (state > 0) {
if (++EditField > MAX_EDIT_FIELD)
EditField = 0;
} else if (state < 0) {
if (EditField)
EditField--;
else
EditField = MAX_EDIT_FIELD;
} else
EditField = 0;
}
/* *************************************************************************
DESCRIPTION: Gets the current edit field for this module
RETURN: the current edit field
ALGORITHM: none
NOTES: none
*************************************************************************** */
uint8_t dlmstp_GetEditField(void)
{
return (EditField);
}
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);
data = 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);
data = I2C_Read_Byte(EEPROM_DEVICE_ADDRESS,
EEPROM_MSTP_MAX_MASTER_ADDR);
if (data <= 127)
MSTP_Port.Nmax_master = data;
else
dlmstp_set_max_master(DEFAULT_MAX_MASTER);
MSTP_Port.Nmax_info_frames =
I2C_Read_Byte(EEPROM_DEVICE_ADDRESS,
EEPROM_MSTP_MAX_INFO_FRAMES_ADDR);
}
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;
unsigned npdu_len = 0;
uint8_t frame_type = 0;
uint8_t destination = 0; /* destination address */
BACNET_ADDRESS src;
unsigned i = 0; /* loop counter */
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;
}
void dlmstp_task(void)
{
uint8_t bytes_remaining;
bool received_frame;
/* 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);
}
/* 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)) {
};
/*MSTP_Master_Node_FSM(&MSTP_Port);
*/
}
/* 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;
}
return;
}
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;
}
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;
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(unsigned max_info_frames)
{
MSTP_Port.Nmax_info_frames = max_info_frames;
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)
MSTP_Port.Nmax_master = max_master;
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; /* len=0 denotes broadcast address */
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
+250
View File
@@ -0,0 +1,250 @@
/**************************************************************************
*
* Copyright (C) 2003 Mark Norton and Steve Karg
*
* 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.
*
* Functional
* Description: Defines the hardware implementation for the Microchip
* microprocessor used in the Synergy lighting control project.
*
*********************************************************************/
#ifndef HARDWARE_H
#define HARDWARE_H
#include <p18F452.h>
#include <portb.h>
#include <timers.h>
/****************************************************************************
* Card IO *
****************************************************************************/
/*
TRIS masks are:
0 = OUTPUT
1 = INPUT
The IO on this card is as follows:
RA0 - SDA - SEEPROM (input)
RA1 - SCL - SEEPROM (input)
RA2 - not used (input)
RA3 - not used (input)
RA4 - LK2a - jumper (input)
RA5 - LK2b - jumper (input)
TRISA - 0011 1111 - 3Fh
RB0 - INT - Zero Cross Interrupt (input)
RB1 - LED - I2C Bus Indication (output)
RB2 - LED - Labeled 'DATA' (output)
RB3 - not used (input)
RB4 - CTS input for RS-232 (not used unless LT1180A chip is there)
RB5 - RTS output for RS-232 (not used unless LT1180A chip is there)
RB6 - PGC - in circuit programming (input)
RB7 - PGD - in circuit programming (input)
TRISB - 1101 1001 - D9h
RC0 - QH of 74165 shift register (input)
RC1 - SHIFTREG_CKL of 74165 shift register (output)
RC2 - SHIFTREG_LOAD of 74165 shift register (output)
RC3 - SCL for I2C bus (input)
RC4 - SDA for I2C bus (input)
RC5 - RS-485 TXEN (or RS232) (output)
RC6 - RS-485 TXD (or RS232) (output)
RC7 - RS-485 RXD (or RS232) (input)
TRISC - 1001 1001 - 99h
*/
#define PORT_A_TRIS_MASK 0x3F
#define PORT_B_TRIS_MASK 0xD9
#define PORT_C_TRIS_MASK 0x99
/* hardware mapping of functionality */
#define DATA_LED_ON() PORTBbits.RB2=0
#define DATA_LED_OFF() PORTBbits.RB2=1
#define ABUS_LED_ON() PORTBbits.RB1=0
#define ABUS_LED_OFF() PORTBbits.RB1=1
#define RS485_TRANSMIT_DISABLE() PORTCbits.RC5=0
#define RS485_TRANSMIT_ENABLE() PORTCbits.RC5=1
/* note: board is inverted logic */
#define JUMPER_LK2_TOP_OFF() PORTAbits.RA4
#define JUMPER_LK2_TOP_ON() (!PORTAbits.RA4)
#define JUMPER_LK2_BOTTOM_OFF() PORTAbits.RA5
#define JUMPER_LK2_BOTTOM_ON() (!PORTAbits.RA5)
#define ZERO_CROSS PORTBbits.RB0
#define I2C_CLK_LATCH LATCbits.LATC3
#define I2C_DATA_LATCH LATCbits.LATC4
#define I2C_CLK PORTCbits.RC3
#define I2C_DATA PORTCbits.RC4
#define I2C_CLK_HI_Z TRISCbits.TRISC3
#define I2C_SDA_HI_Z TRISCbits.TRISC4
#define EEPROM_DATA_LATCH LATAbits.LATA0
#define EEPROM_CLK_LATCH LATAbits.LATA1
#define EEPROM_SDA PORTAbits.RA0
#define EEPROM_CLK PORTAbits.RA1
#define EEPROM_SDA_HI_Z TRISAbits.TRISA0
#define EEPROM_CLK_HI_Z TRISAbits.TRISA1
#define SHIFTREG_LOAD PORTCbits.RC2
#define SHIFTREG_CLK PORTCbits.RC1
#define SHIFTREG_DATA PORTCbits.RC0
#define NO_ANALOGS 0x06 /* None */
#define ALL_ANALOG 0x00 /* RA0 RA1 RA2 RA3 RA5 RE0 RE1 RE2 Ref=Vdd */
#define ANALOG_RA3_REF 0x01 /* RA0 RA1 RA2 RA5 RE0 RE1 RE2 Ref=RA3 */
#define A_ANALOG 0x02 /* RA0 RA1 RA2 RA3 RA5 Ref=Vdd */
#define A_ANALOG_RA3_REF 0x03 /* RA0 RA1 RA2 RA5 Ref=RA3 */
#define RA0_RA1_RA3_ANALOG 0x04 /* RA0 RA1 RA3 Ref=Vdd */
#define RA0_RA1_ANALOG_RA3_REF 0x05 /* RA0 RA1 Ref=RA3 */
#define ANALOG_RA3_RA2_REF 0x08
#define ANALOG_NOT_RE1_RE2 0x09
#define ANALOG_NOT_RE1_RE2_REF_RA3 0x0A
#define ANALOG_NOT_RE1_RE2_REF_RA3_RA2 0x0B
#define A_ANALOG_RA3_RA2_REF 0x0C
#define RA0_RA1_ANALOG_RA3_RA2_REF 0x0D
#define RA0_ANALOG 0x0E
#define RA0_ANALOG_RA3_RA2_REF 0x0F
/* Constants used for SETUP_ADC() are: */
#define ADC_OFF 0
#define ADC_START 4
#define ADC_CLOCK_DIV_2 1
#define ADC_CLOCK_DIV_4 0x101
#define ADC_CLOCK_DIV_8 0x41
#define ADC_CLOCK_DIV_16 0x141
#define ADC_CLOCK_DIV_32 0x81
#define ADC_CLOCK_DIV_64 0x181
#define ADC_CLOCK_INTERNAL 0xc1
#define ADC_DONE_MASK 0x04
#define SET_ADC_CHAN(x) ADCON0 = (ADC_CLOCK_DIV_32 | ((x) << 3))
#define T1_DISABLED 0
#define T1_INTERNAL 0x85
#define T1_EXTERNAL 0x87
#define T1_EXTERNAL_SYNC 0x83
#define T1_CLK_OUT 8
#define T1_DIV_BY_1 0
#define T1_DIV_BY_2 0x10
#define T1_DIV_BY_4 0x20
#define T1_DIV_BY_8 0x30
#define SETUP_TIMER1(mode) T1CON = (mode)
#define T2_DISABLED 0
#define T2_DIV_BY_1 4
#define T2_DIV_BY_4 5
#define T2_DIV_BY_16 6
#define SETUP_TIMER2(mode, period, postscale) \
{ \
T2CON = ((mode) | ((postscale)-1)<<3); \
PR2 = (period); \
}
#define T3_DISABLED 0
#define T3_INTERNAL 0x85
#define T3_EXTERNAL 0x87
#define T3_EXTERNAL_SYNC 0x83
#define T3_DIV_BY_1 0
#define T3_DIV_BY_2 0x10
#define T3_DIV_BY_4 0x20
#define T3_DIV_BY_8 0x30
#define SETUP_TIMER3(mode) T3CON = (mode)
#define CCP_OFF 0
#define CCP_CAPTURE_FE 4
#define CCP_CAPTURE_RE 5
#define CCP_CAPTURE_DIV_4 6
#define CCP_CAPTURE_DIV_16 7
#define CCP_COMPARE_SET_ON_MATCH 8
#define CCP_COMPARE_CLR_ON_MATCH 9
#define CCP_COMPARE_INT 0xA
#define CCP_COMPARE_RESET_TIMER 0xB
#define CCP_PWM 0xC
#define CCP_PWM_PLUS_1 0x1c
#define CCP_PWM_PLUS_2 0x2c
#define CCP_PWM_PLUS_3 0x3c
#define SETUP_CCP1(mode) CCP1CON = (mode)
#define SETUP_CCP2(mode) CCP2CON = (mode)
#define WATCHDOG_TIMER() \
{ \
_asm \
CLRWDT \
_endasm \
}
#define GLOBAL_INT_ENABLE() INTCONbits.GIE = 1
#define GLOBAL_INT_DISABLE() INTCONbits.GIE = 0
#define PERIPHERAL_INT_ENABLE() INTCONbits.PEIE = 1
#define PERIPHERAL_INT_DISABLE() INTCONbits.PEIE = 0
#define TIMER0_INT_ENABLE() INTCONbits.TMR0IE = 1
#define TIMER0_INT_DISABLE() INTCONbits.TMR0IE = 0
#define TIMER2_INT_ENABLE() PIE1bits.TMR2IE = 1
#define TIMER2_INT_DISABLE() PIE1bits.TMR2IE = 0
#define CCP2_INT_ENABLE() PIE2bits.CCP2IE = 1
#define CCP2_INT_DISABLE() PIE2bits.CCP2IE = 0
#define CCP1_INT_ENABLE() PIE1bits.CCP1IE = 1
#define CCP1_INT_DISABLE() PIE1bits.CCP1IE = 0
#define ABUS_INT_ENABLE() PIE1bits.SSPIE = 1
#define ABUS_INT_DISABLE() PIE1bits.SSPIE = 0
#define ABUS_INT_FLAG_CLEAR() PIR1bits.SSPIF = 0
#define USART_RX_INT_DISABLE() PIE1bits.RCIE = 0
#define USART_RX_INT_ENABLE() PIE1bits.RCIE = 1
#define USART_TX_INTERRUPT() PIE1bits.TXIE
#define USART_TX_INT_DISABLE() PIE1bits.TXIE = 0
#define USART_TX_INT_ENABLE() PIE1bits.TXIE = 1
#define USART_TX_ENABLE() TXSTAbits.TXEN = 1
#define USART_TX_INT_FLAG_CLEAR() PIR1bits.TXIF = 0
#define USART_TX_EMPTY() TXSTAbits.TRMT
#define USART_CONTINUOUS_RX_ENABLE() RCSTAbits.CREN = 1
#define USART_CONTINUOUS_RX_DISABLE() RCSTAbits.CREN = 0
#define USART_RX_COMPLETE() PIR1bits.RCIF
#define USART_RX_STATUS() RCSTAbits
#define USART_RX_STATUS() RCSTAbits
#define USART_TRANSMIT(x) TXREG = (x)
#define USART_RECEIVE() RCREG
#define USART_RX_FRAME_ERROR() rcstabits.FERR
/* combine the sequence correctly */
#define USART_RX_SETUP() PIE1bits.RCIE = 1; RCSTAbits.CREN = 1
#define USART_TX_SETUP() PIE1bits.TXIE = 1; TXSTAbits.TXEN = 1
#endif /* HARDWARE_H */
+130
View File
@@ -0,0 +1,130 @@
/**************************************************************************
*
* Copyright (C) 2003 Mark Norton
*
* 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.
*
* Functional
* Description: Handles the init code for the Microchip microprocessor
*
*********************************************************************/
#include <string.h>
#include <stdint.h>
#include "hardware.h"
/* define this to enable ICD */
/*#define USE_ICD */
/* Configuration Bits */
#pragma config OSC = HS
#pragma config PWRT = ON
#pragma config BOR = ON, BORV = 42
#pragma config CCP2MUX = ON
#pragma config STVR = ON
#pragma config LVP = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CP2 = OFF
#pragma config CP3 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRT2 = OFF
#pragma config WRT3 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTR2 = OFF
#pragma config EBTR3 = OFF
#pragma config EBTRB = OFF
#ifdef USE_ICD
#pragma config WDT = OFF, WDTPS = 128
#pragma config DEBUG = ON
#else
#pragma config WDT = ON, WDTPS = 128
#pragma config DEBUG = OFF
#endif /* USE_ICD */
#pragma romdata
/****************************************************************************
* DESCRIPTION: Initializes the PIC, its timers, WDT, etc.
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void init_hardware(void)
{
/* If the processor gets a power on reset then we can do something. */
/* We should not get a reset unless there has been */
/* some kind of power line disturbance. */
if (RCONbits.POR) {
/*do something special! */
}
GLOBAL_INT_DISABLE();
/* Setup PORT A */
TRISA = PORT_A_TRIS_MASK;
/* PORT A can have analog inputs or digital IO */
ADCON1 = NO_ANALOGS;
/* Setup PORT B */
TRISB = PORT_B_TRIS_MASK;
/* Setup PORT C */
TRISC = PORT_C_TRIS_MASK;
/* setup zero cross interrupt to trigger on a low to high edge */
INTCON2bits.INTEDG0 = 1;
/* setup ABUS */
/*ABUS_LED_OFF(); */
/*SSPADD = ABUS_DEFAULT_ADDR; */
/*SSPCON1 = (ABUS_SLAVE_MASK | ABUS_CLK_ENABLE | ABUS_MODE_SETUP); */
/*ABUS_Clear_SSPBUF(); */
/* setup timer2 to reset every 1ms */
CloseTimer2();
PR2 = 250;
OpenTimer2(T2_PS_1_4 & T2_POST_1_5 & 0x7F);
/* Setup our interrupt priorities ---------> all low priority */
RCONbits.IPEN = 1;
IPR1 = 0;
IPR2 = 0;
INTCON2bits.TMR0IP = 0;
INTCON2bits.RBIP = 0;
INTCON3 = 0;
/* Enable interrupts */
TIMER2_INT_ENABLE();
PERIPHERAL_INT_ENABLE();
GLOBAL_INT_ENABLE();
/* Turn on the Zero cross interrupt */
INTCONbits.INT0F = 0;
INTCONbits.INT0E = 1;
}
+33
View File
@@ -0,0 +1,33 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
#ifndef INIT_H
#define INIT_H
void init_hardware(void);
#endif
+165
View File
@@ -0,0 +1,165 @@
/**************************************************************************
*
* Copyright (C) 2003 Mark Norton and Steve Karg
*
* 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.
*
* Functional
* Description: Defines the interrupt service routines (ISR) for the
* Microchip microprocessor used in the Synergy lighting
* control project.
*
*********************************************************************/
#include <stdint.h>
#include "hardware.h"
#include "timer.h"
/* interrupt service routines */
extern void RS485_Receive_Interrupt(void);
extern void RS485_Transmit_Interrupt(void);
/****************************************************************************
* DESCRIPTION: High priority interrupt routine
* PARAMETERS: none
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
#pragma interruptlow InterruptHandlerLow save=PROD, section(".tmpdata")
void InterruptHandlerLow(void)
{
/* check for timer0 interrupt */
if ((INTCONbits.TMR0IF) && (INTCONbits.TMR0IE)) {
/* clear interrupt flag */
INTCONbits.TMR0IF = 0;
/* call interrupt handler */
}
/*check for timer1 interrupt */
if ((PIR1bits.TMR1IF) && (PIE1bits.TMR1IE)) {
PIR1bits.TMR1IF = 0;
/* call interrupt handler */
}
/*check for timer2 interrupt */
if ((PIR1bits.TMR2IF) && (PIE1bits.TMR2IE)) {
PIR1bits.TMR2IF = 0;
Timer_Millisecond_Interrupt();
}
/*check for timer3 interrupt */
if ((PIR2bits.TMR3IF) && (PIE2bits.TMR3IE)) {
PIR2bits.TMR3IF = 0;
/* call interrupt handler */
}
/*check for compare 1 int */
if ((PIR1bits.CCP1IF) && (PIE1bits.CCP1IE)) {
PIR1bits.CCP1IF = 0;
/* call interrupt handler */
}
/*check for compare 2 int */
if ((PIR2bits.CCP2IF) && (PIE2bits.CCP2IE)) {
PIR2bits.CCP2IF = 0;
/* call interrupt handler */
}
/*check for USART Rx int */
if ((PIR2bits.EEIF) && (PIE2bits.EEIE)) {
PIR2bits.EEIF = 0; /*clear interrupt flag */
EECON1bits.WREN = 0; /* disable writes */
/* call interrupt handler */
}
/*check for USART Tx int */
if ((PIR1bits.TXIF) && (PIE1bits.TXIE)) {
/* call interrupt handler */
RS485_Transmit_Interrupt();
}
/*check for USART Rx int */
if ((PIR1bits.RCIF) && (PIE1bits.RCIE)) {
/* call interrupt handler */
RS485_Receive_Interrupt();
}
/*check for AD int */
if ((PIR1bits.ADIF) && (PIE1bits.ADIE)) {
/* call interrupt handler */
PIR1bits.ADIF = 0;
}
/*check for I2C receive int (MSSP int) */
if ((PIR1bits.SSPIF) && (PIE1bits.SSPIE)) {
PIR1bits.SSPIF = 0;
/* call interrupt handler */
}
return;
}
/****************************************************************************
* DESCRIPTION: High priority interrupt routine
* PARAMETERS: none
* RETURN: none
* ALGORITHM: none
* NOTES: don't call functions from this function because registers are
* not saved, and saving registers is slower.
*****************************************************************************/
#pragma interrupt InterruptHandlerHigh
void InterruptHandlerHigh(void)
{
/*check for external int */
if ((INTCONbits.INT0IF) && (INTCONbits.INT0IE)) {
/* Test to ensure that we are not getting a false trigger on the
falling edge. Only trigger on Rising edge. */
if (ZERO_CROSS) {
/* timer used to determine when power is gone (no zero crosses) */
/* Power_Timeout = 30; */
/* if (ABUS_Current_Status.Zerox_Fail) */
/* { */
/* ABUS_Flags.SendStatus = TRUE; */
/* ABUS_Current_Status.Zerox_Fail = FALSE; */
/* } */
/* if we get here it means power is good */
/* System_Flags.PowerFail = FALSE; */
}
INTCONbits.INT0IF = 0;
}
return;
}
/****************************************************************************
* DESCRIPTION: High priority interrupt vector
* PARAMETERS: none
* RETURN: none
* ALGORITHM: none
* NOTES: ISRs not here because we would only have 0x10 bytes for code
*****************************************************************************/
#pragma code InterruptVectorHigh = 0x08
void InterruptVectorHigh(void)
{
_asm goto InterruptHandlerHigh /*jump to interrupt routine */
_endasm}
#pragma code
/****************************************************************************
* DESCRIPTION: Low priority interrupt vector
* PARAMETERS: none
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
#pragma code InterruptVectorLow = 0x18
void InterruptVectorLow(void)
{
_asm goto InterruptHandlerLow /*jump to interrupt routine */
_endasm}
#pragma code
+192
View File
@@ -0,0 +1,192 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stddef.h>
#include <stdint.h>
#include "mstp.h"
#include "bytes.h"
#include "crc.h"
#include "rs485.h"
#include "init.h"
#include "timer.h"
#include "datalink.h"
#include "handlers.h"
#include "device.h"
#include "hardware.h"
#include "iam.h"
/* for readproperty handler */
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacerror.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "rp.h"
/* buffer used for encoding RP apdu */
static uint8_t Temp_Buf[MAX_APDU];
/* buffer used for receiving */
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
/* address where message came from */
static BACNET_ADDRESS src;
/* address used to send */
static BACNET_ADDRESS my_address;
/* see demo/handler/h_rp.c for a more complete example */
void My_Read_Property_Handler(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_READ_PROPERTY_DATA data;
int len = 0;
int pdu_len = 0;
bool send = false;
bool error = false;
int bytes_sent = 0;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
len = rp_decode_service_request(service_request, service_len, &data);
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
&my_address, &npdu_data);
/* bad decoding - send an abort */
if (len < 0) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
} else if (service_data->segmented_message) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
} else {
switch (data.object_type) {
case OBJECT_DEVICE:
/* FIXME: probably need a length limitation sent with encode */
if (data.object_instance == Device_Object_Instance_Number()) {
len = Device_Encode_Property_APDU(&Temp_Buf[0],
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
} else
error = true;
} else
error = true;
break;
default:
error = true;
break;
}
}
if (error) {
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_READ_PROPERTY, error_class, error_code);
}
pdu_len += len;
bytes_sent = datalink_send_pdu(src, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
return;
}
/****************************************************************************
* DESCRIPTION: Handles our calling our module level milisecond counters
* PARAMETERS: none
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
static void Check_Timer_Milliseconds(void)
{
/* We might have missed some so keep doing it until we have got them all */
while (Milliseconds) {
dlmstp_millisecond_timer();
Milliseconds--;
}
}
void main(void)
{
unsigned timeout = 100; /* milliseconds */
uint16_t pdu_len = 0;
/* we need to handle who-is
to support dynamic device binding to us */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
handler_who_is);
/* set the handler for all the services we don't implement
It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler
(handler_unrecognized_service);
/* we must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
My_Read_Property_Handler);
/* handle communication so we can shutup when asked */
apdu_set_confirmed_handler
(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
handler_device_communication_control);
Device_Set_Object_Instance_Number(5);
dlmstp_set_my_address(0x05);
dlmstp_init();
init_hardware();
/* broadcast an I-Am on startup */
iam_send(&Handler_Transmit_Buffer[0]);
/* loop forever */
for (;;) {
WATCHDOG_TIMER();
/* input */
Check_Timer_Milliseconds();
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
/* process */
if (pdu_len) {
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
/* output */
}
return;
}
File diff suppressed because it is too large Load Diff
+246
View File
@@ -0,0 +1,246 @@
/*####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) */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+30
View File
@@ -0,0 +1,30 @@
BACnet Stack - SourceForge.net
Build for MPLAB IDE
These are some settings that are important when building
the BACnet Stack using MPLAB IDE and MCC18 Compiler,
1. Add the files to the project that you need:
abort.c, apdu.c, bacapp.c, bacdcode.c, bacerror.c,
bacstr.c, bigend.c, crc.c, datalink.c, dcc.c, dlmstp.c,
ima.c, mstp.c, npdu.c, rd.c, reject.c, reject.c,
ringbuf.c, rp.c, whois.c, wp.c
From demo/object/: device.c or dev_tiny.c, ai.c, ao.c, etc.
From demo/handler/: h_dcc.c, h_rd.c, h_rp.c, h_wp.c
2. Project->Options->Project
General Tab: Include Path:
C:\code\bacnet-stack\;C:\code\bacnet-stack\demo\handler\;C:\code\bacnet-stack\demo\object\;C:\code\bacnet-stack\ports\pic18\
MPLAB C18 Tab: Memory Model:
Code: Large Code Model
Data: Large Data Model
Stack: Multi-bank Model
MPLAB C18 Tab: General: Macro Definitions:
PRINT_ENABLED=0
BACDL_MSTP=1
BIG_ENDIAN=0
+365
View File
@@ -0,0 +1,365 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
/* The module handles sending data out the RS-485 port */
/* and handles receiving data from the RS-485 port. */
/* Customize this file for your specific hardware */
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "hardware.h"
#include "mstp.h"
#include "comm.h"
#include "eeprom.h"
/* public port info */
extern volatile struct mstp_port_struct_t MSTP_Port;
/* the baud rate is adjustable */
uint32_t RS485_Baud_Rate = 9600;
/* the ISR and other use this for status and control */
COMSTAT RS485_Comstat;
/*#pragma udata MSTPPortData */
/* the buffer for receiving characters */
volatile uint8_t RS485_Rx_Buffer[MAX_MPDU];
/* UART transmission buffer and index */
volatile uint8_t RS485_Tx_Buffer[MAX_MPDU];
/****************************************************************************
* DESCRIPTION: Transmits a frame using the UART
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
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) */
uint16_t i = 0; /* loop counter */
uint8_t turnaround_time;
if (!buffer)
return;
/* bounds check */
if (nbytes >= sizeof(RS485_Tx_Buffer))
return;
/* buffer is full. Wait for ISR to transmit. */
while (RS485_Comstat.Tx_Bytes) {
};
/* wait 40 bit times since reception */
if (RS485_Baud_Rate == 9600)
turnaround_time = 4;
else if (RS485_Baud_Rate == 19200)
turnaround_time = 2;
else
turnaround_time = 1;
while (mstp_port->SilenceTimer < turnaround_time) {
};
RS485_Comstat.TxHead = 0;
memcpy((void *) &RS485_Tx_Buffer[0], (void *) buffer, nbytes);
/*for (i = 0; i < nbytes; i++) { */
/* /* put the data into the buffer */ */
/* RS485_Tx_Buffer[i] = *buffer; */
/* buffer++; */
/*} */
RS485_Comstat.Tx_Bytes = nbytes;
/* disable the receiver */
PIE3bits.RC2IE = 0;
RCSTA2bits.CREN = 0;
/* enable the transceiver */
RS485_TX_ENABLE = 1;
RS485_RX_DISABLE = 1;
/* enable the transmitter */
TXSTA2bits.TXEN = 1;
PIE3bits.TX2IE = 1;
/* per MSTP spec, sort of */
mstp_port->SilenceTimer = 0;
return;
}
/****************************************************************************
* DESCRIPTION: Checks for data on the receive UART, and handles errors
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
uint8_t RS485_Check_UART_Data(volatile struct mstp_port_struct_t *
mstp_port)
{
/* check for data */
if (RS485_Comstat.Rx_Bytes) {
mstp_port->DataRegister = RS485_Rx_Buffer[RS485_Comstat.RxTail];
if (RS485_Comstat.RxTail >= (sizeof(RS485_Rx_Buffer) - 1))
RS485_Comstat.RxTail = 0;
else
RS485_Comstat.RxTail++;
RS485_Comstat.Rx_Bytes--;
/* errors? let the state machine know */
if (RS485_Comstat.Rx_Bufferoverrun) {
RS485_Comstat.Rx_Bufferoverrun = FALSE;
mstp_port->ReceiveError = TRUE;
}
/* We read a good byte */
else
mstp_port->DataAvailable = TRUE;
}
return RS485_Comstat.Rx_Bytes;
}
/* *************************************************************************
DESCRIPTION: Receives RS485 data stream
RETURN: none
ALGORITHM: none
NOTES: none
*************************************************************************** */
void RS485_Interrupt_Rx(void)
{
char dummy;
if ((RCSTA2bits.FERR) || (RCSTA2bits.OERR)) {
/* Clear the error */
RCSTA2bits.CREN = 0;
RCSTA2bits.CREN = 1;
RS485_Comstat.Rx_Bufferoverrun = TRUE;
dummy = RCREG2;
} else if (RS485_Comstat.Rx_Bytes < sizeof(RS485_Rx_Buffer)) {
RS485_Rx_Buffer[RS485_Comstat.RxHead] = RCREG2;
if (RS485_Comstat.RxHead >= (sizeof(RS485_Rx_Buffer) - 1))
RS485_Comstat.RxHead = 0;
else
RS485_Comstat.RxHead++;
RS485_Comstat.Rx_Bytes++;
} else {
RS485_Comstat.Rx_Bufferoverrun = TRUE;
dummy = RCREG2;
(void) dummy;
}
}
/* *************************************************************************
DESCRIPTION: Transmits a byte using the UART out the RS485 port
RETURN: none
ALGORITHM: none
NOTES: none
*************************************************************************** */
void RS485_Interrupt_Tx(void)
{
if (RS485_Comstat.Tx_Bytes) {
/* Get the data byte */
TXREG2 = RS485_Tx_Buffer[RS485_Comstat.TxHead];
/* point to the next byte */
RS485_Comstat.TxHead++;
/* reduce the buffer size */
RS485_Comstat.Tx_Bytes--;
} else {
/* wait for the USART to be empty */
while (!TXSTA2bits.TRMT);
/* disable this interrupt */
PIE3bits.TX2IE = 0;
/* enable the receiver */
RS485_TX_ENABLE = 0;
RS485_RX_DISABLE = 0;
/* FIXME: might not be necessary */
PIE3bits.RC2IE = 1;
RCSTA2bits.CREN = 1;
}
}
/****************************************************************************
* DESCRIPTION: Returns the baud rate that we are currently running at
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
uint32_t RS485_Get_Baud_Rate(void)
{
return RS485_Baud_Rate;
}
/****************************************************************************
* DESCRIPTION: Sets the baud rate for the chip USART
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Set_Baud_Rate(uint32_t baud)
{
if (baud < 19200)
RS485_Baud_Rate = 9600;
else if (baud < 38400)
RS485_Baud_Rate = 19200;
else if (baud < 57600)
RS485_Baud_Rate = 38400;
else if (baud < 57600)
RS485_Baud_Rate = 57600;
else if (baud < 115200)
RS485_Baud_Rate = 76800;
else
RS485_Baud_Rate = 115200;
I2C_Write_Block(EEPROM_DEVICE_ADDRESS,
(char *) &RS485_Baud_Rate,
sizeof(RS485_Baud_Rate), EEPROM_MSTP_BAUD_RATE_ADDR);
}
/****************************************************************************
* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in
* receive mode.
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Initialize_Port(void)
{
/* Reset USART registers to POR state */
TXSTA2 = 0;
RCSTA2 = 0;
/* configure USART for receiving */
/* since the TX will handle setting up for transmit */
RCSTA2bits.CREN = 1;
/* Interrupt on receipt */
PIE3bits.RC2IE = 1;
/* enable the transmitter, disable its interrupt */
TXSTA2bits.TXEN = 1;
PIE3bits.TX2IE = 0;
/* setup USART Baud Rate Generator */
/* see BAUD RATES FOR ASYNCHRONOUS MODE in Data Book */
/* Fosc=20MHz
BRGH=1 BRGH=0
Rate SPBRG Rate SPBRG
------- ----- ------- -----
9615 129 9469 32
19230 64 19530 15
37878 32 78130 3
56818 21 104200 2
113630 10 312500 0
250000 4
625000 1
1250000 0
*/
switch (RS485_Baud_Rate) {
case 19200:
SPBRG2 = 64;
TXSTA2bits.BRGH = 1;
break;
case 38400:
SPBRG2 = 32;
TXSTA2bits.BRGH = 1;
break;
case 57600:
SPBRG2 = 21;
TXSTA2bits.BRGH = 1;
break;
case 76800:
SPBRG2 = 3;
TXSTA2bits.BRGH = 0;
break;
case 115200:
SPBRG2 = 10;
TXSTA2bits.BRGH = 1;
break;
case 9600:
SPBRG2 = 129;
TXSTA2bits.BRGH = 1;
break;
default:
SPBRG2 = 129;
TXSTA2bits.BRGH = 1;
RS485_Set_Baud_Rate(9600);
break;
}
/* select async mode */
TXSTA2bits.SYNC = 0;
/* enable transmitter */
TXSTA2bits.TXEN = 1;
/* serial port enable */
RCSTA2bits.SPEN = 1;
/* since we are using RS485,
we need to explicitly say
transmit enable or not */
RS485_RX_DISABLE = 0;
RS485_TX_ENABLE = 0;
}
/****************************************************************************
* DESCRIPTION: Disables the RS485 hardware
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Disable_Port(void)
{
RCSTA2 &= 0x4F; /* Disable the receiver */
TXSTA2bits.TXEN = 0; /* and transmitter */
PIE3 &= 0xCF; /* Disable both interrupts */
}
void RS485_Reinit(void)
{
RS485_Set_Baud_Rate(9600);
}
/****************************************************************************
* DESCRIPTION: Initializes the data and the port
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Initialize(void)
{
/* Init the Rs485 buffers */
RS485_Comstat.RxHead = 0;
RS485_Comstat.RxTail = 0;
RS485_Comstat.Rx_Bytes = 0;
RS485_Comstat.Rx_Bufferoverrun = FALSE;
RS485_Comstat.TxHead = 0;
RS485_Comstat.TxTail = 0;
RS485_Comstat.Tx_Bytes = 0;
I2C_Read_Block(EEPROM_DEVICE_ADDRESS,
(char *) &RS485_Baud_Rate,
sizeof(RS485_Baud_Rate), EEPROM_MSTP_BAUD_RATE_ADDR);
RS485_Initialize_Port();
}
+74
View File
@@ -0,0 +1,74 @@
/*####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"
#include "comm.h"
extern COMSTAT RS485_Comstat;
extern volatile uint8_t RS485_Rx_Buffer[MAX_MPDU];
extern volatile uint8_t RS485_Tx_Buffer[MAX_MPDU];
extern uint32_t RS485_Baud_Rate;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void RS485_Reinit(void);
void RS485_Initialize(void);
void RS485_Disable(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 */
void RS485_Interrupt_Rx(void);
void RS485_Interrupt_Tx(void);
uint32_t RS485_Get_Baud_Rate(void);
void RS485_Set_Baud_Rate(uint32_t baud);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+28
View File
@@ -0,0 +1,28 @@
#ifndef STDBOOL_H
#define STDBOOL_H
/* C99 Boolean types for compilers without C99 support */
#ifndef __cplusplus
typedef char _Bool;
#ifndef bool
#define bool _Bool
#endif
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#define __bool_true_false_are_defined 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#endif
+18
View File
@@ -0,0 +1,18 @@
/* Defines the standard integer types that are used in code */
#ifndef STDINT_H
#define STDINT_H 1
#include <stddef.h>
typedef unsigned char uint8_t; /* 1 byte 0 to 255 */
typedef signed char int8_t; /* 1 byte -127 to 127 */
typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */
typedef signed short int16_t; /* 2 bytes -32767 to 32767 */
/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */
typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */
typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */
/* typedef signed long long int64_t; */
/* typedef unsigned long long uint64_t; */
#endif /* STDINT_H */
+45
View File
@@ -0,0 +1,45 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg
*
* 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 <stdint.h>
volatile uint8_t Milliseconds; /* used for timing stuff - counts up to 0xFF. */
/****************************************************************************
* DESCRIPTION: Timer is set to go off every 1ms. We increment the counter,
* and the main task will decrement the counter.
* PARAMETERS: none
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void Timer_Millisecond_Interrupt(void)
{
/* Global Milisecond timer */
if (Milliseconds < 0xFF)
Milliseconds++;
return;
}
+34
View File
@@ -0,0 +1,34 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
#ifndef TIMER_H
#define TIMER_H
/* used for timing stuff - counts up to 0xFF. */
extern volatile uint8_t Milliseconds;
void Timer_Millisecond_Interrupt(void);
#endif
@@ -0,0 +1,41 @@
// $Id: 18f6720.lkr,v 1.1 2003/12/16 14:53:08 GrosbaJ Exp $
// File: 18f6720.lkr
// Sample linker script for the PIC18F6720 processor
LIBPATH .
FILES c018i.o
FILES clib.lib
FILES p18f6720.lib
CODEPAGE NAME=vectors START=0x0 END=0x29 PROTECTED
CODEPAGE NAME=page START=0x2A END=0x1FFFF
CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED
CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED
CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED
CODEPAGE NAME=eedata START=0xF00000 END=0xF003FF PROTECTED
ACCESSBANK NAME=accessram START=0x0 END=0x5F
DATABANK NAME=gpr0 START=0x60 END=0xFF
DATABANK NAME=gpr1 START=0x100 END=0x1FF
DATABANK NAME=gpr2 START=0x200 END=0x2FF
DATABANK NAME=gpr3 START=0x300 END=0x3FF
DATABANK NAME=gpr4 START=0x400 END=0x4FF
DATABANK NAME=gpr5 START=0x500 END=0x5FF
DATABANK NAME=gpr6 START=0x600 END=0x6FF
DATABANK NAME=gpr7 START=0x700 END=0x7FF
DATABANK NAME=gpr8 START=0x800 END=0x8FF
DATABANK NAME=gpr9 START=0x900 END=0x9FF
DATABANK NAME=gpr10 START=0xA00 END=0xAFF
DATABANK NAME=gpr11 START=0xB00 END=0xBFF
//DATABANK NAME=gpr12 START=0xC00 END=0xCFF
//DATABANK NAME=gpr13 START=0xD00 END=0xDFF
DATABANK NAME=stackreg START=0xC00 END=0xDFF PROTECTED
DATABANK NAME=gpr14 START=0xE00 END=0xEF3
DATABANK NAME=dbgspr START=0xEF4 END=0xEFF PROTECTED
ACCESSBANK NAME=accesssfr START=0xF60 END=0xFFF PROTECTED
SECTION NAME=CONFIG ROM=config
//STACK SIZE=0x100 RAM=gpr13
STACK SIZE=0x200 RAM=stackreg
@@ -0,0 +1,147 @@
[HEADER]
magic_cookie={66E99B07-E706-4689-9E80-9B2582898A13}
file_version=1.0
[PATH_INFO]
dir_src=
dir_bin=
dir_tmp=
dir_sin=
dir_inc=C:\code\bacnet-stack;C:\code\bacnet-stack\demo\handler;C:\code\bacnet-stack\demo\object;C:\code\bacnet-stack\ports\pic18
dir_lib=C:\mcc18\lib
dir_lkr=
[CAT_FILTERS]
filter_src=*.asm;*.c
filter_inc=*.h;*.inc
filter_obj=*.o
filter_lib=*.lib
filter_lkr=*.lkr
[OTHER_FILES]
file_000=no
file_001=no
file_002=no
file_003=no
file_004=no
file_005=no
file_006=no
file_007=no
file_008=no
file_009=no
file_010=no
file_011=no
file_012=no
file_013=no
file_014=no
file_015=no
file_016=no
file_017=no
file_018=no
file_019=no
file_020=no
file_021=no
file_022=no
file_023=no
file_024=no
file_025=no
file_026=no
file_027=no
file_028=no
file_029=no
file_030=no
file_031=no
file_032=no
file_033=no
file_034=no
file_035=no
file_036=no
file_037=no
file_038=no
file_039=no
file_040=no
file_041=no
file_042=no
file_043=no
file_044=no
file_045=no
file_046=no
file_047=no
file_048=no
file_049=no
file_050=no
file_051=no
file_052=no
file_053=no
file_054=no
file_055=no
file_056=no
file_057=no
file_058=no
file_059=no
[FILE_INFO]
file_000=C:\code\bacnet-stack\abort.c
file_001=C:\code\bacnet-stack\apdu.c
file_002=C:\code\bacnet-stack\bacapp.c
file_003=C:\code\bacnet-stack\bacdcode.c
file_004=C:\code\bacnet-stack\bacerror.c
file_005=C:\code\bacnet-stack\bacstr.c
file_006=C:\code\bacnet-stack\crc.c
file_007=C:\code\bacnet-stack\dcc.c
file_008=C:\code\bacnet-stack\iam.c
file_009=C:\code\bacnet-stack\npdu.c
file_010=C:\code\bacnet-stack\rd.c
file_011=C:\code\bacnet-stack\reject.c
file_012=C:\code\bacnet-stack\rp.c
file_013=C:\code\bacnet-stack\whois.c
file_014=C:\code\bacnet-stack\demo\handler\h_dcc.c
file_015=C:\code\bacnet-stack\demo\handler\h_rd.c
file_016=main.c
file_017=dlmstp.c
file_018=device.c
file_019=rs485.c
file_020=isr.c
file_021=C:\code\bacnet-stack\datetime.c
file_022=C:\code\bacnet-stack\demo\handler\txbuf.c
file_023=C:\code\bacnet-stack\demo\handler\h_whois.c
file_024=mstp.c
file_025=C:\code\bacnet-stack\demo\handler\h_rp_tiny.c
file_026=C:\code\bacnet-stack\wp.h
file_027=C:\code\bacnet-stack\abort.h
file_028=C:\code\bacnet-stack\apdu.h
file_029=C:\code\bacnet-stack\bacapp.h
file_030=C:\code\bacnet-stack\bacdcode.h
file_031=C:\code\bacnet-stack\bacdef.h
file_032=C:\code\bacnet-stack\bacenum.h
file_033=C:\code\bacnet-stack\bacerror.h
file_034=C:\code\bacnet-stack\bacstr.h
file_035=C:\code\bacnet-stack\config.h
file_036=C:\code\bacnet-stack\crc.h
file_037=C:\code\bacnet-stack\dcc.h
file_038=C:\code\bacnet-stack\dlmstp.h
file_039=C:\code\bacnet-stack\iam.h
file_040=C:\code\bacnet-stack\npdu.h
file_041=C:\code\bacnet-stack\rd.h
file_042=C:\code\bacnet-stack\reject.h
file_043=C:\code\bacnet-stack\rp.h
file_044=C:\code\bacnet-stack\whois.h
file_045=C:\code\bacnet-stack\demo\handler\client.h
file_046=C:\code\bacnet-stack\demo\handler\handlers.h
file_047=C:\code\bacnet-stack\demo\object\ai.h
file_048=C:\code\bacnet-stack\demo\object\ao.h
file_049=C:\code\bacnet-stack\demo\object\device.h
file_050=stdbool.h
file_051=stdint.h
file_052=hardware.h
file_053=rs485.h
file_054=C:\code\bacnet-stack\datetime.h
file_055=C:\code\bacnet-stack\demo\handler\txbuf.h
file_056=mstp.h
file_057=C:\code\bacnet-stack\datalink.h
file_058=C:\mcc18\h\p18f6720.h
file_059=18F6720.lkr
[SUITE_INFO]
suite_guid={5B7D72DD-9861-47BD-9F60-2BE967BF8416}
suite_state=
[TOOL_SETTINGS]
TS{DD2213A8-6310-47B1-8376-9430CDFC013F}=
TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}=/m"$(BINDIR_)$(TARGETBASE).map" /o"$(TARGETBASE).cof"
TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}=-DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DTSM_ENABLED=0 -DBIG_ENDIAN=0 -mL -Ls -Ou- -Ot- -Ob- -Op- -Or- -Od- -Opa-
TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}=
@@ -0,0 +1,567 @@
/**************************************************************************
*
* Copyright (C) 2007 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 <string.h> /* for memmove */
#include "bacdef.h"
#include "bacdcode.h"
#include "bacstr.h"
#include "bacenum.h"
#include "config.h" /* the custom stuff */
#include "apdu.h"
#include "device.h" /* me */
#include "dlmstp.h"
#include "rs485.h"
#include "ai.h"
#include "bi.h"
#include "bv.h"
#include "wp.h"
#include "dcc.h"
/* note: you really only need to define variables for
properties that are writable or that may change.
The properties that are constant can be hard coded
into the read-property encoding. */
static uint32_t Object_Instance_Number = 12345;
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
BACNET_REINITIALIZED_STATE_OF_DEVICE Reinitialize_State =
REINITIALIZED_STATE_IDLE;
void Device_Reinit(void)
{
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
}
void Device_Init(void)
{
Reinitialize_State = REINITIALIZED_STATE_IDLE;
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
/* FIXME: Get the data from the eeprom */
/* I2C_Read_Block(EEPROM_DEVICE_ADDRESS,
(char *)&Object_Instance_Number,
sizeof(Object_Instance_Number),
EEPROM_BACNET_ID_ADDR); */
}
/* methods to manipulate the data */
uint32_t Device_Object_Instance_Number(void)
{
return Object_Instance_Number;
}
bool Device_Set_Object_Instance_Number(uint32_t object_id)
{
bool status = true; /* return value */
if (object_id <= BACNET_MAX_INSTANCE) {
Object_Instance_Number = object_id;
/* FIXME: Write the data to the eeprom */
/* I2C_Write_Block(
EEPROM_DEVICE_ADDRESS,
(char *)&Object_Instance_Number,
sizeof(Object_Instance_Number),
EEPROM_BACNET_ID_ADDR); */
} else
status = false;
return status;
}
bool Device_Valid_Object_Instance_Number(uint32_t object_id)
{
/* BACnet allows for a wildcard instance number */
return ((Object_Instance_Number == object_id) ||
(object_id == BACNET_MAX_INSTANCE));
}
BACNET_DEVICE_STATUS Device_System_Status(void)
{
return System_Status;
}
void Device_Set_System_Status(BACNET_DEVICE_STATUS status)
{
if (status < MAX_DEVICE_STATUS)
System_Status = status;
}
/* FIXME: put your vendor ID here! */
uint16_t Device_Vendor_Identifier(void)
{
return 0;
}
uint8_t Device_Protocol_Version(void)
{
return 1;
}
uint8_t Device_Protocol_Revision(void)
{
return 5;
}
/* FIXME: MAX_APDU is defined in config.ini - set it! */
uint16_t Device_Max_APDU_Length_Accepted(void)
{
return MAX_APDU;
}
BACNET_SEGMENTATION Device_Segmentation_Supported(void)
{
return SEGMENTATION_NONE;
}
uint16_t Device_APDU_Timeout(void)
{
return 60000;
}
uint8_t Device_Number_Of_APDU_Retries(void)
{
return 0;
}
uint8_t Device_Database_Revision(void)
{
return 0;
}
/* Since many network clients depend on the object list */
/* for discovery, it must be consistent! */
unsigned Device_Object_List_Count(void)
{
unsigned count = 1; /* at least 1 for device object */
/* FIXME: add objects as needed */
#if 0
count += Binary_Value_Count();
count += Analog_Input_Count();
count += Binary_Input_Count();
#endif
return count;
}
/* Since many network clients depend on the object list */
/* for discovery, it must be consistent! */
bool Device_Object_List_Identifier(unsigned array_index,
int *object_type, uint32_t * instance)
{
bool status = false;
unsigned object_index = 0;
unsigned object_count = 0;
/* device object */
if (array_index == 1) {
*object_type = OBJECT_DEVICE;
*instance = Object_Instance_Number;
status = true;
}
#if 0
/* FIXME: add objects as needed */
/* binary input objects */
if (!status) {
/* normalize the index since
we know it is not the previous objects */
/* array index starts at 1, and 1 for the device object */
object_index = array_index - 2;
object_count = Binary_Value_Count();
/* is it a valid index for this object? */
if (object_index < object_count) {
*object_type = OBJECT_BINARY_VALUE;
*instance = Binary_Value_Index_To_Instance(object_index);
status = true;
}
}
/* analog input objects */
if (!status) {
/* array index starts at 1, and 1 for the device object */
object_index -= object_count;
object_count = Analog_Input_Count();
if (object_index < object_count) {
*object_type = OBJECT_ANALOG_INPUT;
*instance = Analog_Input_Index_To_Instance(object_index);
status = true;
}
}
/* binary input objects */
if (!status) {
/* normalize the index since
we know it is not the previous objects */
object_index -= object_count;
object_count = Binary_Input_Count();
/* is it a valid index for this object? */
if (object_index < object_count) {
*object_type = OBJECT_BINARY_INPUT;
*instance = Binary_Input_Index_To_Instance(object_index);
status = true;
}
}
#endif
return status;
}
/* return the length of the apdu encoded or -1 for error */
int Device_Encode_Property_APDU(uint8_t * apdu,
BACNET_PROPERTY_ID property,
int32_t array_index,
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
{
int apdu_len = 0; /* return value */
int len = 0; /* apdu len intermediate value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned i = 0;
int object_type = 0;
uint32_t instance = 0;
unsigned count = 0;
BACNET_TIME local_time;
BACNET_DATE local_date;
uint8_t year = 0;
char string_buffer[24];
int16_t TimeZone = 0;
/* FIXME: change the hardcoded names to suit your application */
switch (property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE,
Object_Instance_Number);
break;
case PROP_OBJECT_NAME:
(void) strcpypgm2ram(&string_buffer[0], "PIC18F6720 Device");
characterstring_init_ansi(&char_string, string_buffer);
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_DEVICE);
break;
case PROP_DESCRIPTION:
(void) strcpypgm2ram(&string_buffer[0], "BACnet Demo");
characterstring_init_ansi(&char_string, string_buffer);
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_SYSTEM_STATUS:
apdu_len =
encode_tagged_enumerated(&apdu[0], Device_System_Status());
break;
case PROP_VENDOR_NAME:
(void) strcpypgm2ram(&string_buffer[0], "ASHRAE");
characterstring_init_ansi(&char_string, string_buffer);
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_VENDOR_IDENTIFIER:
apdu_len =
encode_tagged_unsigned(&apdu[0], Device_Vendor_Identifier());
break;
case PROP_MODEL_NAME:
(void) strcpypgm2ram(&string_buffer[0], "GNU Demo");
characterstring_init_ansi(&char_string, string_buffer);
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_FIRMWARE_REVISION:
(void) strcpypgm2ram(&string_buffer[0], "1.00");
characterstring_init_ansi(&char_string, string_buffer);
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_APPLICATION_SOFTWARE_VERSION:
(void) strcpypgm2ram(&string_buffer[0], "1.00");
characterstring_init_ansi(&char_string, string_buffer);
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_LOCATION:
(void) strcpypgm2ram(&string_buffer[0], "USA");
characterstring_init_ansi(&char_string, string_buffer);
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_PROTOCOL_VERSION:
apdu_len =
encode_tagged_unsigned(&apdu[0], Device_Protocol_Version());
break;
case PROP_PROTOCOL_REVISION:
apdu_len =
encode_tagged_unsigned(&apdu[0], Device_Protocol_Revision());
break;
/* BACnet Legacy Support */
case PROP_PROTOCOL_CONFORMANCE_CLASS:
apdu_len = encode_tagged_unsigned(&apdu[0], 1);
break;
case PROP_PROTOCOL_SERVICES_SUPPORTED:
/* Note: list of services that are executed, not initiated. */
bitstring_init(&bit_string);
for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) {
/* automatic lookup based on handlers set */
bitstring_set_bit(&bit_string, (uint8_t) i,
apdu_service_supported(i));
}
apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string);
break;
case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
/* Note: this is the list of objects that can be in this device,
not a list of objects that this device can access */
bitstring_init(&bit_string);
for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) {
/* initialize all the object types to not-supported */
bitstring_set_bit(&bit_string, (uint8_t) i, false);
}
/* FIXME: indicate the objects that YOU support */
bitstring_set_bit(&bit_string, OBJECT_DEVICE, true);
#if 0
bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true);
bitstring_set_bit(&bit_string, OBJECT_ANALOG_INPUT, true);
bitstring_set_bit(&bit_string, OBJECT_BINARY_INPUT, true);
#endif
apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string);
break;
case PROP_OBJECT_LIST:
count = Device_Object_List_Count();
/* Array element zero is the number of objects in the list */
if (array_index == 0)
apdu_len = encode_tagged_unsigned(&apdu[0], count);
/* if no index was specified, then try to encode the entire list */
/* into one packet. Note that more than likely you will have */
/* to return an error if the number of encoded objects exceeds */
/* your maximum APDU size. */
else if (array_index == BACNET_ARRAY_ALL) {
for (i = 1; i <= count; i++) {
if (Device_Object_List_Identifier(i, &object_type,
&instance)) {
len =
encode_tagged_object_id(&apdu[apdu_len],
object_type, instance);
apdu_len += len;
/* assume next one is the same size as this one */
/* can we all fit into the APDU? */
if ((apdu_len + len) >= MAX_APDU) {
*error_class = ERROR_CLASS_SERVICES;
*error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;
apdu_len = -1;
break;
}
} else {
/* error: internal error? */
*error_class = ERROR_CLASS_SERVICES;
*error_code = ERROR_CODE_OTHER;
apdu_len = -1;
break;
}
}
} else {
if (Device_Object_List_Identifier(array_index, &object_type,
&instance))
apdu_len =
encode_tagged_object_id(&apdu[0], object_type,
instance);
else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = -1;
}
}
break;
case PROP_MAX_APDU_LENGTH_ACCEPTED:
apdu_len = encode_tagged_unsigned(&apdu[0],
Device_Max_APDU_Length_Accepted());
break;
case PROP_SEGMENTATION_SUPPORTED:
apdu_len = encode_tagged_enumerated(&apdu[0],
Device_Segmentation_Supported());
break;
case PROP_APDU_TIMEOUT:
apdu_len = encode_tagged_unsigned(&apdu[0], Device_APDU_Timeout());
break;
case PROP_NUMBER_OF_APDU_RETRIES:
apdu_len =
encode_tagged_unsigned(&apdu[0],
Device_Number_Of_APDU_Retries());
break;
case PROP_DEVICE_ADDRESS_BINDING:
/* FIXME: encode the list here, if it exists */
break;
case PROP_DATABASE_REVISION:
apdu_len =
encode_tagged_unsigned(&apdu[0], Device_Database_Revision());
break;
case PROP_MAX_INFO_FRAMES:
apdu_len =
encode_tagged_unsigned(&apdu[0], dlmstp_max_info_frames());
break;
case PROP_MAX_MASTER:
apdu_len = encode_tagged_unsigned(&apdu[0], dlmstp_max_master());
break;
case PROP_LOCAL_TIME:
/* FIXME: if you support time */
local_time.hour = 0;
local_time.min = 0;
local_time.sec = 0;
local_time.hundredths = 0;
apdu_len = encode_tagged_time(&apdu[0], &local_time);
break;
case PROP_UTC_OFFSET:
/* Note: BACnet Time Zone is inverse of everybody else */
apdu_len = encode_tagged_signed(&apdu[0], 5 /* EST */ );
break;
case PROP_LOCAL_DATE:
/* FIXME: if you support date */
local_date.year = 2006; /* AD */
local_date.month = 4; /* Jan=1..Dec=12 */
local_date.day = 11; /* 1..31 */
local_date.wday = 0; /* 1=Mon..7=Sun */
apdu_len = encode_tagged_date(&apdu[0], &local_date);
break;
case PROP_DAYLIGHT_SAVINGS_STATUS:
/* FIXME: if you support time/date */
apdu_len = encode_tagged_boolean(&apdu[0], false);
break;
case 9600:
apdu_len = encode_tagged_unsigned(&apdu[0], RS485_Get_Baud_Rate());
break;
default:
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = -1;
break;
}
return apdu_len;
}
bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data,
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) {
*error_class = ERROR_CLASS_OBJECT;
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
return false;
}
/* decode the some of the request */
len = bacapp_decode_application_data(wp_data->application_data,
wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
/* FIXME: len == 0: unable to decode? */
switch (wp_data->object_property) {
case PROP_OBJECT_IDENTIFIER:
if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) {
if ((value.type.Object_Id.type == OBJECT_DEVICE) &&
(Device_Set_Object_Instance_Number(value.type.
Object_Id.instance))) {
/* we could send an I-Am broadcast to let the world know */
status = true;
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
break;
case PROP_MAX_INFO_FRAMES:
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
if (value.type.Unsigned_Int <= 255) {
dlmstp_set_max_info_frames(value.type.Unsigned_Int);
status = true;
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
break;
case PROP_MAX_MASTER:
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
if ((value.type.Unsigned_Int > 0) &&
(value.type.Unsigned_Int <= 127)) {
dlmstp_set_max_master(value.type.Unsigned_Int);
status = true;
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
break;
case PROP_OBJECT_NAME:
if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
uint8_t encoding;
size_t len;
encoding =
characterstring_encoding(&value.type.Character_String);
len = characterstring_length(&value.type.Character_String);
if (encoding == CHARACTER_ANSI_X34) {
if (len <= 20) {
/* FIXME: set the name */
/* Display_Set_Name(
characterstring_value(&value.type.Character_String)); */
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
break;
case 9600:
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
if (value.type.Unsigned_Int > 115200) {
RS485_Set_Baud_Rate(value.type.Unsigned_Int);
status = true;
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
break;
default:
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
}
return status;
}
@@ -0,0 +1,325 @@
/**************************************************************************
*
* 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"
/* Number of MS/TP Packets Rx/Tx */
uint16_t MSTP_Packets = 0;
/* receive buffer */
#pragma udata MSTP_RxData
static DLMSTP_PACKET Receive_Buffer;
/* temp buffer for NPDU insertion */
/* local MS/TP port data - shared with RS-485 */
#pragma udata MSTP_PortData
volatile struct mstp_port_struct_t MSTP_Port;
#pragma udata
#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;
unsigned npdu_len = 0;
uint8_t frame_type = 0;
uint8_t destination = 0; /* destination address */
BACNET_ADDRESS src;
unsigned i = 0; /* loop counter */
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;
}
void dlmstp_task(void)
{
uint8_t bytes_remaining;
bool received_frame;
/* 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);
}
/* 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)) {
};
}
/* 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;
}
return;
}
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;
}
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; /* len=0 denotes broadcast address */
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
@@ -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
@@ -0,0 +1,271 @@
/**************************************************************************
*
* Copyright (C) 2007 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.
*
*********************************************************************/
#ifndef HARDWARE_H
#define HARDWARE_H
#include <p18F6720.h>
#include <stdint.h>
#include <stdbool.h>
/* PORTA.0 Photocell Input PORTA.1 LED Row6 PORTA.2 LED Row5 PORTA.3 LED
* Row4 PORTA.4 Square Wave input from RTC PORTA.5 LCD RW PORTB.0 Zero
* Cross PORTB.1 USB RXF# PORTB.2 USB TXE# PORTB.3 Keypad Row Enable
* (74HC373 Output Control) PORTB.4 Keypad Row Gate (74HC373 Gate)
* PORTB.5 Switch Input Latch & Keypad Column Gate (74HC373 Gate) PORTB.6
* ICD connection PORTB.7 ICD connection PORTC.0 Pilot Latch PORTC.1
* Pilot Output Enable (low) PORTC.2 Piezo PORTC.3 I2C clock PORTC.4 I2C
* data PORTC.5 RS232 enable (low) PORTC.6 RS232 Tx PORTC.7 RS232 Rx
* PORTD.0 Data bus PORTD.1 Data bus PORTD.2 Data bus PORTD.3 Data bus
* PORTD.4 Data bus PORTD.5 Data bus PORTD.6 Data bus PORTD.7 Data bus
* PORTE.0 USB RD PORTE.1 USB WR PORTE.2 LCD RS PORTE.3 485 transmit
* enable PORTE.4 Relay data latch PORTE.5 Switch Input Clock PORTE.6
* Switch Input High/Low PORTE.7 Switch Input Data PORTF.0 LED Row2
* PORTF.1 LED Row1 PORTF.2 LED Col5 PORTF.3 LED Col4 PORTF.4 LED Col3
* PORTF.5 LED Col2 PORTF.6 LED Col1 PORTF.7 LED Col0 PORTG.0 485 receive
* enable PORTG.1 485 Tx PORTG.2 485 Rx PORTG.3 LCD E PORTG.4 LED Row0 */
#define LCD_BUSY PORTDbits.RD7
#define LCD_E PORTGbits.RG3
#define LCD_RW PORTAbits.RA5
#define LCD_RS PORTEbits.RE2
#define LCD_DATA PORTD
#define LCD_TRIS TRISD
#define PILOT_LATCH PORTCbits.RC0
#define PILOT_ENABLE PORTCbits.RC1
#define PILOT_PORT PORTD
#define PILOT_PORT_TRIS TRISD
#define PIEZO PORTCbits.RC2
#define PIEZO_ON() TRISCbits.TRISC2 = 0
#define PIEZO_OFF() TRISCbits.TRISC2 = 1
#define RS232_ENABLE PORTCbits.RC5
#define RS485_TX_ENABLE PORTEbits.RE3
#define RS485_RX_DISABLE PORTGbits.RG0
#define SWITCH_LOAD PORTBbits.RB5
#define SWITCH_CLK PORTEbits.RE5
#define SWITCH_COM PORTEbits.RE6
#define SWITCH_DATA PORTEbits.RE7
#define LEDPORT PORTF
#define LEDTRIS TRISF
#define LED_ROW1 PORTGbits.RG4
#define LED_ROW2 PORTFbits.RF1
#define LED_ROW3 PORTFbits.RF0
#define LED_ROW4 PORTAbits.RA3
#define LED_ROW5 PORTAbits.RA2
#define LED_ROW6 PORTAbits.RA1
#define RELAY_PORT PORTD
#define RELAY_PORT_TRIS TRISD
#define RELAY_LATCH PORTEbits.RE4
#define KEYPAD_DATA PORTD
#define KEYPAD_TRIS TRISD
#define KEYPAD_COL_LATCH PORTBbits.RB5
#define KEYPAD_ROW_ENABLE PORTBbits.RB3
#define KEYPAD_ROW_LATCH PORTBbits.RB4
#define KEYPAD_ROW1 0 b00000001
#define KEYPAD_ROW2 0 b00000010
#define KEYPAD_ROW3 0 b00000100
#define KEYPAD_ROW4 0 b00001000
#define KEYPAD_ROW5 0 b00010000
#define KEYPAD_ROW6 0 b00100000
#define USB_RD_EMPTY PORTBbits.RB1
#define USB_WR_FULL PORTBbits.RB2
#define USB_RD PORTEbits.RE0
#define USB_WR PORTEbits.RE1
#define USB_PORT PORTD
#define USB_PORT_TRIS TRISD
#define ZERO_CROSS PORTBbits.RB0
#define PORT_A_TRIS_MASK 0x11 /* 0b00010001 */
#define PORT_B_TRIS_MASK 0xC7 /* 0b11000111 */
#define PORT_C_TRIS_MASK 0x9C /* 0b10011100 */
#define PORT_D_TRIS_MASK 0xFF /* 0b11111111 */
#define PORT_E_TRIS_MASK 0x88 /* 0b10001000 */
#define PORT_F_TRIS_MASK 0x00 /* 0b00000000 */
#define PORT_G_TRIS_MASK 0x04 /* 0b00000100 */
#define TURN_OFF_COMPARATORS() CMCON = 0x07
#define SHORT_BEEP 50
#define LONG_BEEP 250
#define CLICK() Hardware_Sound_Piezo(SHORT_BEEP);
#define BEEP() Hardware_Sound_Piezo(LONG_BEEP);
typedef union {
struct {
uint8_t:1;
uint8_t:1;
uint8_t Thursday:1;
uint8_t Wednesday:1;
uint8_t Tuesday:1;
uint8_t Monday:1;
uint8_t Program:1;
uint8_t Run:1;
uint8_t:1;
uint8_t:1;
uint8_t Input1:1;
uint8_t Input2:1;
uint8_t Input3:1;
uint8_t Input4:1;
uint8_t Input5:1;
uint8_t Input6:1;
uint8_t:1;
uint8_t:1;
uint8_t:1;
uint8_t Input7:1;
uint8_t:1;
uint8_t:1;
uint8_t Input8:1;
uint8_t Photocell:1;
uint8_t:1;
uint8_t:1;
uint8_t:1;
uint8_t:1;
uint8_t:1;
uint8_t:1;
uint8_t Remote:1;
uint8_t Relay8:1;
uint8_t:1;
uint8_t:1;
uint8_t:1;
uint8_t Relay7:1;
uint8_t Relay6:1;
uint8_t Relay5:1;
uint8_t Relay4:1;
uint8_t Relay3:1;
uint8_t:1;
uint8_t:1;
uint8_t Relay2:1;
uint8_t Relay1:1;
uint8_t Holiday:1;
uint8_t Sunday:1;
uint8_t Saturday:1;
uint8_t Friday:1;
};
struct {
uint8_t row1;
uint8_t row2;
uint8_t row3;
uint8_t row4;
uint8_t row5;
uint8_t row6;
};
} LED_REGS;
union SWITCH_REGS {
struct {
uint8_t All_On:1;
uint8_t All_Off:1;
uint8_t Addr:4;
uint8_t Pilot_Fault:1;
uint8_t Master:1;
};
uint8_t Sw_Byte;
};
enum INT_STATE { INT_DISABLED, INT_ENABLED, INT_RESTORE };
#define RESTART_WDT() { _asm CLRWDT _endasm }
/* *************************************************************************
define ENABLE_GLOBAL_INT() INTCONbits.GIE = 1 £
#define DISABLE_GLOBAL_INT() INTCONbits.GIE = 0 £
#define ENABLE_PERIPHERAL_INT() INTCONbits.PEIE = 1 £
#define DISABLE_PERIPHERAL_INT() INTCONbits.PEIE = 0
*************************************************************************** */
#define ENABLE_HIGH_INT() INTCONbits.GIE = 1
#define DISABLE_HIGH_INT() INTCONbits.GIE = 0
#define ENABLE_LOW_INT() INTCONbits.PEIE = 1
#define DISABLE_LOW_INT() INTCONbits.PEIE = 0
#define ENABLE_TIMER0_INT() INTCONbits.TMR0IE = 1
#define DISABLE_TIMER0_INT() INTCONbits.TMR0IE = 0
#define ENABLE_TIMER2_INT() PIE1bits.TMR2IE = 1
#define DISABLE_TIMER2_INT() PIE1bits.TMR2IE = 0
#define ENABLE_TIMER4_INT() PIE3bits.TMR4IE = 1
#define DISABLE_TIMER4_INT() PIE3bits.TMR4IE = 0
#define ENABLE_CCP2_INT() PIE2bits.CCP2IE = 1
#define DISABLE_CCP2_INT() PIE2bits.CCP2IE = 0
#define ENABLE_CCP1_INT() PIE1bits.CCP1IE = 1
#define DISABLE_CCP1_INT() PIE1bits.CCP1IE = 0
#define ENABLE_ABUS_INT() PIE1bits.SSPIE = 1
#define DISABLE_ABUS_INT() PIE1bits.SSPIE = 0
#define CLEAR_ABUS_FLAG() PIR1bits.SSPIF = 0
#define SETUP_CCP1(x) CCP1CON = x
#define SETUP_CCP2(x) CCP2CON = x
#define DISABLE_RX_INT() PIE1bits.RCIE = 0
#define ENABLE_RX_INT() PIE1bits.RCIE = 1
#define DISABLE_TX_INT() PIE1bits.TXIE = 0
#define ENABLE_TX_INT() PIE1bits.TXIE = 1
#if CLOCKSPEED == 20
#define DELAY_US(x) { _asm \
MOVLW x \
LOOP: \
NOP \
NOP \
DECFSZ WREG, 1, 0 \
BRA LOOP \
_endasm }
#endif
#define setup_timer4(mode, period, postscale) \
T4CON = (mode | (postscale - 1) << 3); \
PR4 = period
#define setup_timer2(mode, period, postscale) \
T2CON = (mode | (postscale - 1) << 3); \
PR2 = period
/* Global Vars */
extern volatile LED_REGS LEDS;
extern volatile LED_REGS Blink;
extern uint8_t Piezo_Timer;
extern volatile bool DataPortLocked;
#endif /* HARDWARE_H */
+190
View File
@@ -0,0 +1,190 @@
/**************************************************************************
*
* Copyright (C) 2007 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 <p18F6720.h>
#include "stdint.h"
#include "hardware.h"
#include "rs485.h"
#include "dlmstp.h"
/* from main.c */
extern volatile uint8_t Milliseconds;
void InterruptHandlerHigh(void);
void InterruptHandlerLow(void);
void Interrupt_Timer2(void);
void Interrupt_Timer3(void);
void Interrupt_Timer4(void);
void Interrupt_USART_Rx(void);
void Interrupt_USART_Tx(void);
void Interrupt_CCP2(void);
void INT0_Interrupt(void);
#pragma code InterruptVectorHigh = 0x08
void InterruptVectorHigh(void)
{
/* jump to interrupt routine */
_asm goto InterruptHandlerHigh _endasm}
#pragma code
#pragma code InterruptVectorLow = 0x18
void InterruptVectorLow(void)
{
/* jump to interrupt routine */
_asm goto InterruptHandlerLow _endasm}
#pragma code
#pragma interrupt InterruptHandlerHigh
void InterruptHandlerHigh(void)
{
#if 0
/* check for USART Rx int */
if ((PIR1bits.RCIF) && (PIE1bits.RCIE)) {
if ((RCSTA1bits.FERR) || (RCSTA1bits.OERR)) {
Comstat.Rx_Bufferoverrun = TRUE;
PIE1bits.RC1IE = 0; /* Disable Interrupt on receipt */
} else if (Comstat.Rx_Bytes++ < RX_BUFFER_SIZE - 1) {
Rx_Buffer[Comstat.RxHead++] = RCREG1;
/* Stick a Null on the end to let us use str functions on our
* buffer */
Rx_Buffer[Comstat.RxHead] = 0;
} else {
Comstat.Rx_Bufferoverrun = TRUE;
PIE1bits.RC1IE = 0; /* Disable Interrupt on receipt */
}
}
#endif
/* check for timer0 int */
if ((INTCONbits.TMR0IF) && (INTCONbits.TMR0IE)) {
INTCONbits.TMR0IF = 0;
}
}
#pragma interruptlow InterruptHandlerLow save = PROD, section(".tmpdata"), TABLAT, TBLPTR, section \
("MATH_DATA")
void InterruptHandlerLow(void)
{
/* check for timer2 int */
if ((PIR1bits.TMR2IF) && (PIE1bits.TMR2IE)) {
PIR1bits.TMR2IF = 0;
Interrupt_Timer2();
}
/* check for timer3 int */
if ((PIR2bits.TMR3IF) && (PIE2bits.TMR3IE)) {
PIR2bits.TMR3IF = 0;
Interrupt_Timer3();
}
/* check for timer4 int */
if ((PIR3bits.TMR4IF) && (PIE3bits.TMR4IE)) {
PIR3bits.TMR4IF = 0;
dlmstp_millisecond_timer();
Interrupt_Timer4();
}
/* check for compare int */
if ((PIR2bits.CCP2IF) && (PIE2bits.CCP2IE)) {
PIR2bits.CCP2IF = 0;
Interrupt_CCP2();
}
/* check for USART Tx int */
if ((PIR3bits.TX2IF) && (PIE3bits.TX2IE)) {
RS485_Interrupt_Tx();
}
/* check for USART Rx int */
if ((PIR3bits.RC2IF) && (PIE3bits.RC2IE)) {
RS485_Interrupt_Rx();
}
/* Unused Interrupts
//check for timer1 int
if ((PIR1bits.TMR1IF) && (PIE1bits.TMR1IE))
{
PIR1bits.TMR1IF = 0;
Interrupt_Timer1();
}
//check for compare int
if ((PIR1bits.CCP1IF) && (PIE1bits.CCP1IE))
{
PIR1bits.CCP1IF = 0;
Interrupt_CCP1();
}
//check for compare int
if ((PIR3bits.CCP3IF) && (PIE3bits.CCP3IE))
{
PIR3bits.CCP3IF = 0;
Interrupt_CCP3();
}
//check for compare int
if ((PIR3bits.CCP4IF) && (PIE3bits.CCP4IE))
{
PIR3bits.CCP4IF = 0;
Interrupt_CCP4();
}
//check for AD int
if ((PIR1bits.ADIF) && (PIE1bits.ADIE))
{
PIR1bits.ADIF = 0;
Interrupt_ADC();
}
//check for MSSP int
if ((PIR1bits.SSPIF) && (PIE1bits.SSPIE))
{
PIR1bits.SSPIF = 0;
Interrupt_SSP();
}
*/
}
void Interrupt_Timer2(void)
{
}
void Interrupt_Timer3(void)
{
}
/* Timer4 is set to go off every 1ms. This is our system tick */
void Interrupt_Timer4(void)
{
/* Milisecond is our system tick */
if (Milliseconds < 0xFF)
++Milliseconds;
}
void Interrupt_CCP2(void)
{
}
+294
View File
@@ -0,0 +1,294 @@
/**************************************************************************
*
* Copyright (C) 2007 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 <string.h> /* for memmove */
#include <p18F6720.h>
#include <stdlib.h>
#include <string.h>
#include "stdint.h"
#include "hardware.h"
/* BACnet */
#include "apdu.h"
#include "datalink.h"
#include "dcc.h"
#include "handlers.h"
#include "iam.h"
#include "txbuf.h"
/* chip configuration data */
/* define this to enable ICD */
/* #define USE_ICD */
/* Configuration Bits */
#pragma config OSC = HS, OSCS = OFF
#pragma config PWRT = ON
#pragma config BOR = ON, BORV = 27
#pragma config CCP2MUX = ON
#pragma config STVR = ON
#pragma config LVP = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CP2 = OFF
#pragma config CP3 = OFF
#pragma config CP4 = OFF
#pragma config CP5 = OFF
#pragma config CP6 = OFF
#pragma config CP7 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRT2 = OFF
#pragma config WRT3 = OFF
#pragma config WRT4 = OFF
#pragma config WRT5 = OFF
#pragma config WRT6 = OFF
#pragma config WRT7 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTR2 = OFF
#pragma config EBTR3 = OFF
#pragma config EBTR4 = OFF
#pragma config EBTR5 = OFF
#pragma config EBTR6 = OFF
#pragma config EBTR7 = OFF
#pragma config EBTRB = OFF
#ifdef USE_ICD
#pragma config WDT = OFF, WDTPS = 128
#pragma config DEBUG = ON
#else
#pragma config WDT = ON, WDTPS = 128
#pragma config DEBUG = OFF
#endif /* USE_ICD */
volatile uint8_t Milliseconds = 0;
volatile uint8_t Zero_Cross_Timeout = 0;
static void BACnet_Service_Handlers_Init(void)
{
/* we need to handle who-is to support dynamic device binding */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
handler_who_is);
/* Set the handlers for any confirmed services that we support. */
/* We must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
handler_reinitialize_device);
#if 0
apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
handler_write_property);
apdu_set_unconfirmed_handler
(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION,
handler_timesync_utc);
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION,
handler_timesync);
#endif
/* handle communication so we can shutup when asked */
apdu_set_confirmed_handler
(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
handler_device_communication_control);
}
void Reinitialize(void)
{
uint8_t i;
char name = 0;
_asm reset _endasm return;
}
void Global_Int(enum INT_STATE state)
{
static uint8_t intstate = 0;
switch (state) {
case INT_DISABLED:
intstate >>= 2;
intstate |= (INTCON & 0xC0);
break;
case INT_ENABLED:
INTCONbits.GIE = 1;
INTCONbits.PEIE = 1;
intstate <<= 2;
break;
case INT_RESTORE:
INTCON |= (intstate & 0xC0);
intstate <<= 2;
break;
default:
break;
}
}
void Hardware_Initialize(void)
{
/* PORTA.0 Input - Photocell PORTA.1 Output - LED Row6 PORTA.2 Output
* - LED Row5 PORTA.3 Output - LED Row4 PORTA.4 Input - Square Wave
* input from RTC PORTA.5 Output - LCD RW */
TRISA = 0xD1;
/* PORTB.0 Input - Zero Cross PORTB.1 Input - USB RXF# PORTB.2 Input
* USB TXE# PORTB.3 Output - Keypad Row Enable (74HC373 Output Control)
* PORTB.4 Output Keypad Row Gate (74HC373 Gate) PORTB.5 Output Switch
* Input Latch & Keypad Column Gate (74HC373 Gate) PORTB.6 Input - ICD
* connection PORTB.7 Input - ICD connection */
TRISB = 0xC7;
/* PORTC.0 Output - Pilot Latch PORTC.1 Output - Pilot Output Enable
* (low) PORTC.2 I/O - Piezo PORTC.3 Input - I2C clock PORTC.4 Input
* I2C data PORTC.5 Output RS232 enable (low) PORTC.6 Output - RS232 Tx
* PORTC.7 Input - RS232 Rx */
TRISC = 0x9C;
/* PORTD.0 I/O - Data bus PORTD.1 I/O - Data bus PORTD.2 I/O - Data
* bus PORTD.3 I/O - Data bus PORTD.4 I/O - Data bus PORTD.5 I/O - Data
* bus PORTD.6 I/O - Data bus PORTD.7 I/O - Data bus */
TRISD = 0xFF;
/* PORTE.0 Input - USB RD PORTE.1 Input - USB WR PORTE.2 Output - LCD
* RS PORTE.3 Output - 485 transmit enable PORTE.4 Output - Relay data
* latch PORTE.5 Output Switch Input Clock PORTE.6 Output - Switch
* Input High/Low PORTE.7 Input Switch Input Data */
TRISE = 0x83;
/* PORTF.0 Output - LED Row2 PORTF.1 Output - LED Row1 PORTF.2 Output
* - LED Col5 PORTF.3 Output - LED Col4 PORTF.4 Output - LED Col3
* PORTF.5 Output - LED Col2 PORTF.6 Output - LED Col1 PORTF.7 Output
* LED Col0 */
TRISF = 0x00;
/* PORTG.0 Output - 485 receive enable PORTG.1 Output - 485 Tx PORTG.2
* Input 485 Rx PORTG.3 Output - LCD E PORTG.4 Output - LED Row0 */
TRISG = 0xE6;
/* The initial state of the keypad enables and latches */
KEYPAD_ROW_ENABLE = 1;
KEYPAD_ROW_LATCH = 0;
KEYPAD_COL_LATCH = 1;
RELAY_LATCH = 0;
/* Setup to read the switch inputs */
SWITCH_COM = 1;
/* Enable the RS232 transmitter */
RS232_ENABLE = 0;
/* Turn all leds off. These are the hardware pins */
LED_ROW1 = 1;
LED_ROW2 = 1;
LED_ROW3 = 1;
LED_ROW4 = 1;
LED_ROW5 = 1;
LED_ROW6 = 1;
LEDPORT = 0x03;
/* The initial values for the signals to the LCD */
LCD_E = 1;
LCD_RW = 1;
LCD_RS = 1;
/* The following gives us a PWM frequency of 1.990KHz with a 50% duty
* cycle It also serves to multiplex the LEDs. */
PIEZO_OFF();
CCPR1L = 0x4E;
CCP1CON = 0x2F;
setup_timer2(6, 156, 2);
PIE1bits.TMR2IE = 1;
/* We will use Timer4 as our system tick timer. Our system tick is set
* to 1ms. Hold off on enabling the int. */
setup_timer4(5, 250, 5);
/* Setup our interrupt priorities */
RCONbits.IPEN = 1;
IPR1 = 0;
IPR2 = 0;
IPR3 = 0;
/* Setup TMR0 to be high priority */
INTCON2 = 0xFC;
INTCON3 = 0;
/* USART 1 high priority */
IPR1bits.RC1IP = 1;
IPR1bits.TX1IP = 1;
/* Finally enable our ints */
Global_Int(INT_ENABLED);
}
void Initialize_Variables(void)
{
/* Check to see if we need to initialize our eeproms */
ENABLE_TIMER4_INT();
/* interrupts must be enabled before we read our inputs */
Global_Int(INT_ENABLED);
BACnet_Service_Handlers_Init();
dlmstp_init();
/* Start our time from now */
Milliseconds = 0;
}
void MainTasks(void)
{
static uint16_t millisecond_counter = 0;
/* Handle our millisecond counters */
while (Milliseconds) {
millisecond_counter++;
--Milliseconds;
}
/* Handle our seconds counters */
if (millisecond_counter > 1000) {
millisecond_counter -= 1000;
dcc_timer_seconds(1);
}
}
void main(void)
{
RCONbits.NOT_POR = 1;
RCONbits.NOT_RI = 1;
Hardware_Initialize();
Initialize_Variables();
/* Handle anything that needs to be done on powerup */
/* Greet the BACnet world! */
iam_send(&Handler_Transmit_Buffer[0]);
/* Main loop */
while (TRUE) {
RESTART_WDT();
dlmstp_task();
MainTasks();
Global_Int(INT_ENABLED);
ENABLE_TIMER4_INT();
}
}
File diff suppressed because it is too large Load Diff
+246
View File
@@ -0,0 +1,246 @@
/*####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) */
#ifdef __cplusplus
}
#endif /* __cplusplus */
+380
View File
@@ -0,0 +1,380 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
/* The module handles sending data out the RS-485 port */
/* and handles receiving data from the RS-485 port. */
/* Customize this file for your specific hardware */
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "hardware.h"
#include "mstp.h"
#include "rs485.h"
/* public port info */
extern volatile struct mstp_port_struct_t MSTP_Port;
/* the baud rate is adjustable */
uint32_t RS485_Baud_Rate = 38400;
/* the ISR and other use this for status and control */
COMSTAT RS485_Comstat;
/*#pragma udata MSTPPortData
*/
/* the buffer for receiving characters */
volatile uint8_t RS485_Rx_Buffer[MAX_MPDU];
/* UART transmission buffer and index */
volatile uint8_t RS485_Tx_Buffer[MAX_MPDU];
/****************************************************************************
* DESCRIPTION: Transmits a frame using the UART
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
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) */
uint16_t i = 0; /* loop counter */
uint8_t turnaround_time;
if (!buffer)
return;
/* bounds check */
if (nbytes >= sizeof(RS485_Tx_Buffer))
return;
/* buffer is full. Wait for ISR to transmit. */
while (RS485_Comstat.Tx_Bytes) {
};
/* wait 40 bit times since reception */
if (RS485_Baud_Rate == 9600)
turnaround_time = 4;
else if (RS485_Baud_Rate == 19200)
turnaround_time = 2;
else
turnaround_time = 1;
while (mstp_port->SilenceTimer < turnaround_time) {
};
RS485_Comstat.TxHead = 0;
memcpy((void *) &RS485_Tx_Buffer[0], (void *) buffer, nbytes);
#if 0
for (i = 0; i < nbytes; i++) {
/* put the data into the buffer */
RS485_Tx_Buffer[i] = *buffer;
buffer++;
}
#endif
RS485_Comstat.Tx_Bytes = nbytes;
/* disable the receiver */
PIE3bits.RC2IE = 0;
RCSTA2bits.CREN = 0;
/* enable the transceiver */
RS485_TX_ENABLE = 1;
RS485_RX_DISABLE = 1;
/* enable the transmitter */
TXSTA2bits.TXEN = 1;
PIE3bits.TX2IE = 1;
/* per MSTP spec, sort of */
mstp_port->SilenceTimer = 0;
return;
}
/****************************************************************************
* DESCRIPTION: Checks for data on the receive UART, and handles errors
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
uint8_t RS485_Check_UART_Data(volatile struct mstp_port_struct_t *
mstp_port)
{
/* check for data */
if (RS485_Comstat.Rx_Bytes) {
mstp_port->DataRegister = RS485_Rx_Buffer[RS485_Comstat.RxTail];
if (RS485_Comstat.RxTail >= (sizeof(RS485_Rx_Buffer) - 1))
RS485_Comstat.RxTail = 0;
else
RS485_Comstat.RxTail++;
RS485_Comstat.Rx_Bytes--;
/* errors? let the state machine know */
if (RS485_Comstat.Rx_Bufferoverrun) {
RS485_Comstat.Rx_Bufferoverrun = FALSE;
mstp_port->ReceiveError = TRUE;
}
/* We read a good byte */
else
mstp_port->DataAvailable = TRUE;
}
return RS485_Comstat.Rx_Bytes;
}
/* *************************************************************************
DESCRIPTION: Receives RS485 data stream
RETURN: none
ALGORITHM: none
NOTES: none
*************************************************************************** */
void RS485_Interrupt_Rx(void)
{
char dummy;
if ((RCSTA2bits.FERR) || (RCSTA2bits.OERR)) {
/* Clear the error */
RCSTA2bits.CREN = 0;
RCSTA2bits.CREN = 1;
RS485_Comstat.Rx_Bufferoverrun = TRUE;
dummy = RCREG2;
} else if (RS485_Comstat.Rx_Bytes < sizeof(RS485_Rx_Buffer)) {
RS485_Rx_Buffer[RS485_Comstat.RxHead] = RCREG2;
if (RS485_Comstat.RxHead >= (sizeof(RS485_Rx_Buffer) - 1))
RS485_Comstat.RxHead = 0;
else
RS485_Comstat.RxHead++;
RS485_Comstat.Rx_Bytes++;
} else {
RS485_Comstat.Rx_Bufferoverrun = TRUE;
dummy = RCREG2;
(void) dummy;
}
}
/* *************************************************************************
DESCRIPTION: Transmits a byte using the UART out the RS485 port
RETURN: none
ALGORITHM: none
NOTES: none
*************************************************************************** */
void RS485_Interrupt_Tx(void)
{
if (RS485_Comstat.Tx_Bytes) {
/* Get the data byte */
TXREG2 = RS485_Tx_Buffer[RS485_Comstat.TxHead];
/* point to the next byte */
RS485_Comstat.TxHead++;
/* reduce the buffer size */
RS485_Comstat.Tx_Bytes--;
} else {
/* wait for the USART to be empty */
while (!TXSTA2bits.TRMT);
/* disable this interrupt */
PIE3bits.TX2IE = 0;
/* enable the receiver */
RS485_TX_ENABLE = 0;
RS485_RX_DISABLE = 0;
/* FIXME: might not be necessary
*/
PIE3bits.RC2IE = 1;
RCSTA2bits.CREN = 1;
}
}
/****************************************************************************
* DESCRIPTION: Returns the baud rate that we are currently running at
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
uint32_t RS485_Get_Baud_Rate(void)
{
return RS485_Baud_Rate;
}
/****************************************************************************
* DESCRIPTION: Sets the baud rate for the chip USART
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
bool RS485_Set_Baud_Rate(uint32_t baud)
{
bool valid = true;
switch (baud) {
case 9600:
case 19200:
case 38400:
case 57600:
case 76800:
case 115200:
RS485_Baud_Rate = baud;
break;
default:
valid = false;
break;
}
if (valid) {
/* FIXME: store the baud rate */
/* I2C_Write_Block(
EEPROM_DEVICE_ADDRESS,
(char *)&RS485_Baud_Rate,
sizeof(RS485_Baud_Rate),
EEPROM_MSTP_BAUD_RATE_ADDR); */
}
return valid;
}
/****************************************************************************
* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in
* receive mode.
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Initialize_Port(void)
{
/* Reset USART registers to POR state */
TXSTA2 = 0;
RCSTA2 = 0;
/* configure USART for receiving */
/* since the TX will handle setting up for transmit */
RCSTA2bits.CREN = 1;
/* Interrupt on receipt */
PIE3bits.RC2IE = 1;
/* enable the transmitter, disable its interrupt */
TXSTA2bits.TXEN = 1;
PIE3bits.TX2IE = 0;
/* setup USART Baud Rate Generator */
/* see BAUD RATES FOR ASYNCHRONOUS MODE in Data Book */
/* Fosc=20MHz
BRGH=1 BRGH=0
Rate SPBRG Rate SPBRG
------- ----- ------- -----
9615 129 9469 32
19230 64 19530 15
37878 32 78130 3
56818 21 104200 2
113630 10 312500 0
250000 4
625000 1
1250000 0
*/
switch (RS485_Baud_Rate) {
case 19200:
SPBRG2 = 64;
TXSTA2bits.BRGH = 1;
break;
case 38400:
SPBRG2 = 32;
TXSTA2bits.BRGH = 1;
break;
case 57600:
SPBRG2 = 21;
TXSTA2bits.BRGH = 1;
break;
case 76800:
SPBRG2 = 3;
TXSTA2bits.BRGH = 0;
break;
case 115200:
SPBRG2 = 10;
TXSTA2bits.BRGH = 1;
break;
case 9600:
SPBRG2 = 129;
TXSTA2bits.BRGH = 1;
break;
default:
SPBRG2 = 129;
TXSTA2bits.BRGH = 1;
RS485_Set_Baud_Rate(9600);
break;
}
/* select async mode */
TXSTA2bits.SYNC = 0;
/* enable transmitter */
TXSTA2bits.TXEN = 1;
/* serial port enable */
RCSTA2bits.SPEN = 1;
/* since we are using RS485,
we need to explicitly say
transmit enable or not */
RS485_RX_DISABLE = 0;
RS485_TX_ENABLE = 0;
}
/****************************************************************************
* DESCRIPTION: Disables the RS485 hardware
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Disable_Port(void)
{
RCSTA2 &= 0x4F; /* Disable the receiver */
TXSTA2bits.TXEN = 0; /* and transmitter */
PIE3 &= 0xCF; /* Disable both interrupts */
}
void RS485_Reinit(void)
{
RS485_Set_Baud_Rate(38400);
}
/****************************************************************************
* DESCRIPTION: Initializes the data and the port
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Initialize(void)
{
/* Init the Rs485 buffers */
RS485_Comstat.RxHead = 0;
RS485_Comstat.RxTail = 0;
RS485_Comstat.Rx_Bytes = 0;
RS485_Comstat.Rx_Bufferoverrun = FALSE;
RS485_Comstat.TxHead = 0;
RS485_Comstat.TxTail = 0;
RS485_Comstat.Tx_Bytes = 0;
/* FIXME: read the data from storage */
/* I2C_Read_Block(
EEPROM_DEVICE_ADDRESS,
(char *)&RS485_Baud_Rate,
sizeof(RS485_Baud_Rate),
EEPROM_MSTP_BAUD_RATE_ADDR); */
RS485_Initialize_Port();
}
@@ -0,0 +1,84 @@
/*####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"
typedef struct {
uint8_t RxHead;
uint8_t RxTail;
uint8_t Rx_Bytes;
uint8_t TxHead;
uint8_t TxTail;
uint8_t Tx_Bytes;
uint8_t Rx_Bufferoverrun:1;
uint8_t Tx_Bufferoverrun:1;
} COMSTAT;
extern COMSTAT RS485_Comstat;
extern volatile uint8_t RS485_Rx_Buffer[MAX_MPDU];
extern volatile uint8_t RS485_Tx_Buffer[MAX_MPDU];
extern uint32_t RS485_Baud_Rate;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void RS485_Reinit(void);
void RS485_Initialize(void);
void RS485_Disable(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 */
void RS485_Interrupt_Rx(void);
void RS485_Interrupt_Tx(void);
uint32_t RS485_Get_Baud_Rate(void);
bool RS485_Set_Baud_Rate(uint32_t baud);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
@@ -0,0 +1,28 @@
#ifndef STDBOOL_H
#define STDBOOL_H
/* C99 Boolean types for compilers without C99 support */
#ifndef __cplusplus
typedef char _Bool;
#ifndef bool
#define bool _Bool
#endif
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#define __bool_true_false_are_defined 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#endif
@@ -0,0 +1,18 @@
/* Defines the standard integer types that are used in code */
#ifndef STDINT_H
#define STDINT_H 1
#include <stddef.h>
typedef unsigned char uint8_t; /* 1 byte 0 to 255 */
typedef signed char int8_t; /* 1 byte -127 to 127 */
typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */
typedef signed short int16_t; /* 2 bytes -32767 to 32767 */
/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */
typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */
typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */
/* typedef signed long long int64_t; */
/* typedef unsigned long long uint64_t; */
#endif /* STDINT_H */
+282
View File
@@ -0,0 +1,282 @@
/*####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####*/
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include "bacdcode.h"
#include "bip.h"
static int interface = SOCKET_ERROR; /* SOCKET_ERROR means no open interface */
void bip_set_interface(char *ifname)
{
/*dummy function - to make the demos compile easier */
(void) ifname;
}
/*-----------------------------------*/
static void Error(const char *Msg)
{
int Code = WSAGetLastError();
#ifdef HOST
printf("%s, error code: %i\n", Msg, Code);
#else
printf("%s, error code: %s\n", Msg, xn_geterror_string(Code));
#endif
exit(1);
}
#ifndef HOST
/*-----------------------------------*/
void InterfaceCleanup(void)
{
if (interface != SOCKET_ERROR) {
xn_interface_close(interface);
interface = SOCKET_ERROR;
#if DEVICE_ID == PRISM_PCMCIA_DEVICE
RTPCShutDown();
#endif
}
}
static void NetInitialize(void)
/* initialize the TCP/IP stack */
{
int Result;
#ifndef HOST
if (!RTKDebugVersion()) /* switch of all diagnostics and error messages of RTIP-32 */
xn_callbacks()->cb_wr_screen_string_fnc = NULL;
#ifdef RTUSB_VER
RTURegisterCallback(USBAX172); /* ax172 and ax772 drivers */
RTURegisterCallback(USBAX772);
RTURegisterCallback(USBKeyboard); /* support USB keyboards */
FindUSBControllers(); /* install USB host controllers */
Sleep(2000); /* give the USB stack time to enumerate devices */
#endif
#ifdef DHCP
XN_REGISTER_DHCP_CLI() /* and optionally the DHCP client */
#endif
Result = xn_rtip_init(); /* Initialize the RTIP stack */
if (Result != 0)
Error("xn_rtip_init failed");
atexit(InterfaceCleanup); /* make sure the driver is shut down properly */
RTCallDebugger(RT_DBG_CALLRESET, (DWORD) exit, 0); /* even if we get restarted by the debugger */
Result = BIND_DRIVER(MINOR_0); /* tell RTIP what Ethernet driver we want (see netcfg.h) */
if (Result != 0)
Error("driver initialization failed");
#if DEVICE_ID == PRISM_PCMCIA_DEVICE
/* if this is a PCMCIA device, start the PCMCIA driver */
if (RTPCInit(-1, 0, 2, NULL) == 0)
Error("No PCMCIA controller found");
#endif
/* Open the interface */
interface =
xn_interface_open_config(DEVICE_ID, MINOR_0, ED_IO_ADD, ED_IRQ,
ED_MEM_ADD);
if (interface == SOCKET_ERROR)
Error("xn_interface_open_config failed");
else {
struct _iface_info ii;
#ifdef BACDL_ETHERNET
BACNET_ADDRESS my_address;
unsigned i;
#endif
xn_interface_info(interface, &ii);
printf
("Interface opened, MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n",
ii.my_ethernet_address[0], ii.my_ethernet_address[1],
ii.my_ethernet_address[2], ii.my_ethernet_address[3],
ii.my_ethernet_address[4], ii.my_ethernet_address[5]);
#ifdef BACDL_ETHERNET
for (i = 0; i < 6; i++) {
my_address.mac[i] = ii.my_ethernet_address[i];
}
ethernet_set_my_address(&my_address);
#endif
}
#if DEVICE_ID == PRISM_PCMCIA_DEVICE || DEVICE_ID == PRISM_DEVICE
xn_wlan_setup(interface, /* iface_no: value returned by xn_interface_open_config() */
"network name", /* SSID : network name set in the access point */
"station name", /* Name : name of this node */
0, /* Channel : 0 for access points, 1..14 for ad-hoc */
0, /* KeyIndex: 0 .. 3 */
"12345", /* WEP Key : key to use (5 or 13 bytes) */
0); /* Flags : see manual and Wlanapi.h for details */
Sleep(1000); /* wireless devices need a little time before they can be used */
#endif /* WLAN device */
#if defined(AUTO_IP) /* use xn_autoip() to get an IP address */
Result = xn_autoip(interface, MinIP, MaxIP, NetMask, TargetIP);
if (Result == SOCKET_ERROR)
Error("xn_autoip failed");
else {
printf("Auto-assigned IP address %i.%i.%i.%i\n", TargetIP[0],
TargetIP[1], TargetIP[2], TargetIP[3]);
/* define default gateway and DNS server */
xn_rt_add(RT_DEFAULT, ip_ffaddr, DefaultGateway, 1, interface,
RT_INF);
xn_set_server_list((DWORD *) DNSServer, 1);
}
#elif defined(DHCP) /* use DHCP */
{
DHCP_param param[] = { {SUBNET_MASK, 1}
, {DNS_OP, 1}
, {ROUTER_OPTION, 1}
};
DHCP_session DS;
DHCP_conf DC;
xn_init_dhcp_conf(&DC); /* load default DHCP options */
DC.plist = param; /* add MASK, DNS, and gateway options */
DC.plist_entries = sizeof(param) / sizeof(param[0]);
printf("Contacting DHCP server, please wait...\n");
Result = xn_dhcp(interface, &DS, &DC); /* contact DHCP server */
if (Result == SOCKET_ERROR)
Error("xn_dhcp failed");
memcpy(TargetIP, DS.client_ip, 4);
printf("My IP address is: %i.%i.%i.%i\n", TargetIP[0], TargetIP[1],
TargetIP[2], TargetIP[3]);
}
#else
/* Set the IP address and interface */
printf("Using static IP address %i.%i.%i.%i\n", TargetIP[0],
TargetIP[1], TargetIP[2], TargetIP[3]);
Result = xn_set_ip(interface, TargetIP, NetMask);
/* define default gateway and DNS server */
xn_rt_add(RT_DEFAULT, ip_ffaddr, DefaultGateway, 1, interface, RT_INF);
xn_set_server_list((DWORD *) DNSServer, 1);
#endif
#else /* HOST defined, run on Windows */
WSADATA wd;
Result = WSAStartup(0x0101, &wd);
#endif
if (Result != 0)
Error("TCP/IP stack initialization failed");
}
#endif
/******************************************************************
* DESCRIPTION: Converts the byte stored address to an inet address
* RETURN: none
* ALGORITHM: none
* NOTES: none
******************************************************************/
static void RTIP_To_Network_Address(BYTE * octet_address,
struct in_addr *addr)
{
uint32_t ip_address = 0; /* for decoding the subnet mask */
decode_unsigned32(octet_address, &ip_address);
addr->s_addr = htonl(ip_address);
return;
}
static void set_broadcast_address(uint32_t net_address)
{
long broadcast_address = 0;
long mask = 0;
/* Note: sometimes INADDR_BROADCAST does not let me get
any unicast messages. Not sure why... */
#if USE_INADDR
(void) net_address;
bip_set_broadcast_addr(INADDR_BROADCAST);
#else
if (IN_CLASSA(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSA_HOST) | IN_CLASSA_HOST;
else if (IN_CLASSB(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSB_HOST) | IN_CLASSB_HOST;
else if (IN_CLASSC(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSC_HOST) | IN_CLASSC_HOST;
else if (IN_CLASSD(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSD_HOST) | IN_CLASSD_HOST;
else
broadcast_address = INADDR_BROADCAST;
bip_set_broadcast_addr(htonl(broadcast_address));
#endif
}
bool bip_init(void)
{
int rv = 0; /* return from socket lib calls */
struct sockaddr_in sin = { -1 };
int value = 1;
int sock_fd = -1;
struct in_addr my_addr;
NetInitialize();
RTIP_To_Network_Address(TargetIP, &my_addr);
bip_set_addr(my_addr.s_addr);
set_broadcast_address(my_addr.s_addr);
bip_set_port(0xBAC0);
/* assumes that the driver has already been initialized */
sock_fd = socket(AF_INET, SOCK_DGRAM, IPROTO_UDP);
bip_set_socket(sock_fd);
if (sock_fd < 0)
return false;
/* bind the socket to the local port number and IP address */
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(bip_get_port());
memset(&(sin.sin_zero), '\0', 8);
rv = bind(sock_fd,
(const struct sockaddr *) &sin, sizeof(struct sockaddr));
if (rv < 0) {
close(sock_fd);
bip_set_socket(-1);
return false;
}
return true;
}
+267
View File
@@ -0,0 +1,267 @@
/**************************************************************************
*
* 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"
/* receive buffer */
static DLMSTP_PACKET Receive_Buffer;
/* temp buffer for NPDU insertion */
static uint8_t PDU_Buffer[MAX_MPDU];
/* local MS/TP port data */
static volatile struct mstp_port_struct_t MSTP_Port;
void dlmstp_init(void)
{
/* initialize buffer */
Receive_Buffer.ready = false;
Receive_Buffer.pdu_len = 0;
/* initialize hardware */
RS485_Initialize();
MSTP_Init(&MSTP_Port, MSTP_Port.This_Station);
}
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 frame_type = 0;
uint8_t destination = 0; /* destination address */
BACNET_ADDRESS src;
unsigned mtu_len = 0;
if (MSTP_Port.TxReady == false) {
if (npdu_data->confirmed_message)
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 {
#if PRINT_ENABLED
fprintf(stderr, "mstp: invalid destination MAC address!\n");
#endif
return -2;
}
/* header len */
mtu_len = 8;
if ((mtu_len + pdu_len) > MAX_MPDU) {
#if PRINT_ENABLED
fprintf(stderr, "mstp: PDU is too big to send!\n");
#endif
return -4;
}
memmove(&PDU_Buffer[mtu_len], pdu, pdu_len);
mtu_len += pdu_len;
bytes_sent = MSTP_Create_Frame(
(uint8_t *) & MSTP_Port.TxBuffer[0],
sizeof(MSTP_Port.TxBuffer),
MSTP_Port.TxFrameType,
destination, MSTP_Port.This_Station, &PDU_Buffer[0], mtu_len);
MSTP_Port.TxLength = bytes_sent;
MSTP_Port.TxReady = true;
}
return bytes_sent;
}
/* called about once a millisecond */
void dlmstp_millisecond_timer(void)
{
MSTP_Millisecond_Timer(&MSTP_Port);
}
/* returns the number of octets in the PDU, or zero on failure */
/* This function is expecting to be polled. */
uint16_t dlmstp_receive(BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t max_pdu, /* amount of space available in the PDU */
unsigned timeout)
{
uint16_t pdu_len = 0;
(void) timeout;
/* only do receive state machine while we don't have a frame */
if ((MSTP_Port.ReceivedValidFrame == false) &&
(MSTP_Port.ReceivedInvalidFrame == false)) {
RS485_Check_UART_Data(&MSTP_Port);
MSTP_Receive_Frame_FSM(&MSTP_Port);
}
/* 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)) {
};
}
/* see if there is a packet available */
if (Receive_Buffer.ready) {
memmove(src, &Receive_Buffer.address,
sizeof(Receive_Buffer.address));
pdu_len = Receive_Buffer.pdu_len;
memmove(&pdu[0], &Receive_Buffer.pdu[0], max_pdu);
Receive_Buffer.ready = false;
}
return pdu_len;
}
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)
{
if (Receive_Buffer.ready) {
/* FIXME: what to do when we miss a message? */
pdu_len = 0;
} else if (pdu_len < sizeof(Receive_Buffer.pdu)) {
dlmstp_fill_bacnet_address(&Receive_Buffer.address, src);
Receive_Buffer.pdu_len = pdu_len;
memmove(Receive_Buffer.pdu, pdu, pdu_len);
Receive_Buffer.ready = true;
} else {
/* FIXME: message too large? */
pdu_len = 0;
}
return pdu_len;
}
void dlmstp_set_my_address(uint8_t mac_address)
{
/* FIXME: Master Nodes can only have address 1-127 */
MSTP_Port.This_Station = mac_address;
return;
}
/* 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(unsigned max_info_frames)
{
MSTP_Port.Nmax_info_frames = max_info_frames;
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)
{
MSTP_Port.Nmax_master = max_master;
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; /* len=0 denotes broadcast address */
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
+337
View File
@@ -0,0 +1,337 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include <stdio.h> /* for the standard bool type. */
#include <stdlib.h> /* for the standard bool type. */
#include <rttarget.h>
#include <rtk32.h>
#include <clock.h>
#include <socket.h>
#include <windows.h>
#include "ethernet.h"
#include "bacdcode.h"
#include "npdu.h"
/* commonly used comparison address for ethernet */
uint8_t Ethernet_Broadcast[MAX_MAC_LEN] =
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/* commonly used empty address for ethernet quick compare */
uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 };
/* my local device data - MAC address */
uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 };
static SOCKET Ethernet_Socket = -1;
/* used for binding 802.2 */
static struct sockaddr Ethernet_Address = { 0 };
bool ethernet_valid(void)
{
return (Ethernet_Socket != -1);
}
void ethernet_cleanup(void)
{
if (ethernet_valid())
closesocket(Ethernet_Socket);
Ethernet_Socket = -1;
return;
}
bool ethernet_init(char *interface_name)
{
int value = 1;
(void) interface_name;
/* setup the socket */
Ethernet_Socket = socket(AF_INET, SOCK_RAW, 0);
/*Ethernet_Socket = socket(AF_INET, SOCK_STREAM, 0); */
if (Ethernet_Socket < 0)
fprintf(stderr, "ethernet: failed to bind to socket!\r\n");
Ethernet_Address.sa_family = AF_INET;
memset(Ethernet_Address.sa_data, 0, sizeof(Ethernet_Address.sa_data));
if (bind(Ethernet_Socket,
&Ethernet_Address, sizeof(Ethernet_Address)) == SOCKET_ERROR)
fprintf(stderr, "ethernet: failed to bind to socket!\r\n");
/*setsockopt(Ethernet_Socket,SOL_SOCKET,SO_802_2,(char *)&value,sizeof(value)); */
return ethernet_valid();
}
/* function to send a packet out the 802.2 socket */
/* returns bytes sent on success, negative number on failure */
int ethernet_send(BACNET_ADDRESS * dest, /* destination address */
BACNET_ADDRESS * src, /* source 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 = 0;
uint8_t mtu[MAX_MPDU] = { 0 };
int mtu_len = 0;
int i = 0;
(void) npdu_data;
/* don't waste time if the socket is not valid */
if (Ethernet_Socket < 0) {
fprintf(stderr, "ethernet: 802.2 socket is invalid!\n");
return -1;
}
/* load destination ethernet MAC address */
if (dest->mac_len == 6) {
for (i = 0; i < 6; i++) {
mtu[mtu_len] = dest->mac[i];
mtu_len++;
}
} else {
fprintf(stderr, "ethernet: invalid destination MAC address!\n");
return -2;
}
/* load source ethernet MAC address */
if (src->mac_len == 6) {
for (i = 0; i < 6; i++) {
mtu[mtu_len] = src->mac[i];
mtu_len++;
}
} else {
fprintf(stderr, "ethernet: invalid source MAC address!\n");
return -3;
}
if ((14 + 3 + pdu_len) > MAX_MPDU) {
fprintf(stderr, "ethernet: PDU is too big to send!\n");
return -4;
}
/* packet length */
mtu_len += encode_unsigned16(&mtu[12],
3 /*DSAP,SSAP,LLC */ + pdu_len);
/* Logical PDU portion */
mtu[mtu_len++] = 0x82; /* DSAP for BACnet */
mtu[mtu_len++] = 0x82; /* SSAP for BACnet */
mtu[mtu_len++] = 0x03; /* Control byte in header */
mtu_len = 17;
if ((mtu_len + pdu_len) > MAX_MPDU) {
fprintf(stderr, "ethernet: PDU is too big to send!\n");
return -4;
}
memcpy(&mtu[mtu_len], pdu, pdu_len);
mtu_len += pdu_len;
/* packet length - only the logical portion, not the address */
encode_unsigned16(&mtu[12], 3 + pdu_len);
/* Send the packet */
bytes = send(Ethernet_Socket, (const char *) &mtu, mtu_len, 0);
/* did it get sent? */
if (bytes < 0)
fprintf(stderr, "ethernet: Error sending packet: %s\n",
strerror(errno));
return bytes;
}
/* function to send a packet out the 802.2 socket */
/* returns bytes sent on success, negative number on failure */
int ethernet_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 i = 0; /* counter */
BACNET_ADDRESS src = { 0 }; /* source address */
for (i = 0; i < 6; i++) {
src.mac[i] = Ethernet_MAC_Address[i];
src.mac_len++;
}
/* FIXME: npdu_data? */
/* function to send a packet out the 802.2 socket */
/* returns 1 on success, 0 on failure */
return ethernet_send(dest, /* destination address */
&src, /* source address */
npdu_data, pdu, /* any data to be sent - may be null */
pdu_len); /* number of bytes of data */
}
/* receives an 802.2 framed packet */
/* returns the number of octets in the PDU, or zero on failure */
uint16_t ethernet_receive(BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t max_pdu, /* amount of space available in the PDU */
unsigned timeout)
{ /* number of milliseconds to wait for a packet */
int received_bytes;
uint8_t buf[MAX_MPDU] = { 0 }; /* data */
uint16_t pdu_len = 0; /* return value */
fd_set read_fds;
int max;
struct timeval select_timeout;
/* Make sure the socket is open */
if (Ethernet_Socket <= 0)
return 0;
/* we could just use a non-blocking socket, but that consumes all
the CPU time. We can use a timeout; it is only supported as
a select. */
if (timeout >= 1000) {
select_timeout.tv_sec = timeout / 1000;
select_timeout.tv_usec =
1000 * (timeout - select_timeout.tv_sec * 1000);
} else {
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 1000 * timeout;
}
FD_ZERO(&read_fds);
FD_SET(Ethernet_Socket, &read_fds);
max = Ethernet_Socket;
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
received_bytes =
recv(Ethernet_Socket, (char *) &buf[0], MAX_MPDU, 0);
else
return 0;
/* See if there is a problem */
if (received_bytes < 0) {
/* EAGAIN Non-blocking I/O has been selected */
/* using O_NONBLOCK and no data */
/* was immediately available for reading. */
if (errno != EAGAIN)
fprintf(stderr,
"ethernet: Read error in receiving packet: %s\n",
strerror(errno));
return 0;
}
if (received_bytes == 0)
return 0;
/* the signature of an 802.2 BACnet packet */
if ((buf[14] != 0x82) && (buf[15] != 0x82)) {
/*fprintf(stderr,"ethernet: Non-BACnet packet\n"); */
return 0;
}
/* copy the source address */
src->mac_len = 6;
memmove(src->mac, &buf[6], 6);
/* check destination address for when */
/* the Ethernet card is in promiscious mode */
if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0)
&& (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) {
/*fprintf(stderr, "ethernet: This packet isn't for us\n"); */
return 0;
}
(void) decode_unsigned16(&buf[12], &pdu_len);
pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ;
/* copy the buffer into the PDU */
if (pdu_len < max_pdu)
memmove(&pdu[0], &buf[17], pdu_len);
/* ignore packets that are too large */
/* client should check my max apdu first */
else
pdu_len = 0;
return pdu_len;
}
void ethernet_get_my_address(BACNET_ADDRESS * my_address)
{
int i = 0;
my_address->mac_len = 0;
for (i = 0; i < 6; i++) {
my_address->mac[i] = Ethernet_MAC_Address[i];
my_address->mac_len++;
}
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 ethernet_set_my_address(BACNET_ADDRESS * my_address)
{
int i = 0;
for (i = 0; i < 6; i++) {
Ethernet_MAC_Address[i] = my_address->mac[i];
}
return;
}
void ethernet_get_broadcast_address(BACNET_ADDRESS * dest)
{ /* destination address */
int i = 0; /* counter */
if (dest) {
for (i = 0; i < 6; i++) {
dest->mac[i] = Ethernet_Broadcast[i];
}
dest->mac_len = 6;
dest->net = BACNET_BROADCAST_NETWORK;
dest->len = 0; /* denotes broadcast address */
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
void ethernet_debug_address(const char *info, BACNET_ADDRESS * dest)
{
int i = 0; /* counter */
if (info)
fprintf(stderr, "%s", info);
if (dest) {
fprintf(stderr, "Address:\n");
fprintf(stderr, " MAC Length=%d\n", dest->mac_len);
fprintf(stderr, " MAC Address=");
for (i = 0; i < MAX_MAC_LEN; i++) {
fprintf(stderr, "%02X ", (unsigned) dest->mac[i]);
}
fprintf(stderr, "\n");
fprintf(stderr, " Net=%hu\n", dest->net);
fprintf(stderr, " Len=%d\n", dest->len);
fprintf(stderr, " Adr=");
for (i = 0; i < MAX_MAC_LEN; i++) {
fprintf(stderr, "%02X ", (unsigned) dest->adr[i]);
}
fprintf(stderr, "\n");
}
return;
}
@@ -0,0 +1,24 @@
// * The target computer is IBM-PC-AT compatible.
// * There is a minimum of 4 MB of RAM installed.
#ifdef DEBUGDOS
Region RealModeVectors 0 1k RAM NoAccess // interrupt vectors
Region BIOSDataArea 1k 3k RAM ReadOnly // BIOS data area
Region DOSMem 4k 252k RAM
Region LowMem 256k 256k RAM
#else
Region = RealModeVectors 0, 4k, RAM, NoAccess // interrupt vectors
// note: locate only has 4k granularity so the 0-1k will be readonly
// Region = RealModeVectors 0, 1k, RAM, NoAccess // interrupt vectors
// Region = BIOSDataArea 1k, 3k, RAM, ReadOnly // BIOS data area
Region = LowMem 4k, 508k, RAM, Assign // Conventional memory
#endif
Region = MoreLowMem 512k, 128k, RAM, Assign // Reserved BIOS ext
Region = MonoText B0000h 4k, Device, ReadWrite // Mono text video mem
Region = ColorText B8000h, 4k, Device, ReadWrite // Text mode video ram
Region = DiskOnChip D0000h, 8k, Device, ReadWrite // Driver Ampro Card
Region = DiskOnChip1 E8000h, 32k, Device, ReadWrite // Driver WinSys Card
Region = HighMem 1M, 3M, RAM, Assign // 3mb ext mem on target
Virtual HeapMem 1G
Virtual StackMem 2G
Virtual ProgMem 3G
+124
View File
@@ -0,0 +1,124 @@
/**************************************************************************
*
* Copyright (C) 2005 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 <rttarget.h>
#include <rtkernel.h>
#include <windows.h>
extern void RTEmuInit(void);
#ifdef _MSC_VER
#define VOIDEXPORT _declspec(dllexport) void __cdecl
#else
#define VOIDEXPORT void __export __cdecl
#endif
/* DISK SYSTEM */
#ifdef DOC /* include DiskOnChip driver */
#include <rtfiles.h>
#define RTF_MAX_FILES 16 /* support for more open files (default is 8) */
#define RTF_BUFFERS_IN_BSS /* we do not need file I/O before the run-time */
#include <rtfdata.c> /* system is initialized */
/*#define READ_HEAD_BUFFER_SIZE 2048+4 */
/*static BYTE ReadAheadBuffer[READ_HEAD_BUFFER_SIZE]; */
static RTFDrvFLPYData FLPYDriveAData = { 0 };
static RTFDrvDOCData DOCDriveData = { 0 };
static RTFDrvIDEData IDEDriveData = { 0 };
RTFDevice RTFDeviceList[] = {
/* type,number,flags,driver,driverdata */
{RTF_DEVICE_FLOPPY, 0, 0, &RTFDrvFloppy, &FLPYDriveAData},
{RTF_DEVICE_FDISK, 0, 0, &RTFDrvDOC, &DOCDriveData},
{RTF_DEVICE_FDISK, 0, 0, &RTFDrvIDE, &IDEDriveData},
{0, 0, 0, NULL, NULL}
};
#endif
/* END OF DISK SYSTEM */
/* RTTarget only defines 64 Win32 handles, which are not enough for BACstac */
/* the following code is from the RTTarget manual, page 106 */
#define MAXHANDLES 1024
#define MAXOBJECTS 1024
#define MAXTYPES 32
RTW32Handle RTHandleTable[MAXHANDLES] = { {0} };
int RTHandleCount = MAXHANDLES;
RTW32Object RTObjectTable[MAXOBJECTS] = { {0} };
int RTObjectCount = MAXOBJECTS;
RTW32Types RTTypeTable[MAXTYPES] = { {0} };
int RTTypeCount = MAXTYPES;
#if 0
/* We can embed some files in the RTB file, like a binary
file used for configuring a remote device, using
'Locate File filename HighMem' in the config (.CFG) file.
However, the default setup for RTFiles and RTTarget
doesn't include the RAM files, so we need to specify
that here, as well as the LPT, console, and FAT.
From RTFiles-32 manual, ch. 7, "Using RTFiles-32 with
RTTarget-32" */
RTFileSystem Console = { RT_FS_CONSOLE, 0, 0, &RTConsoleFileSystem };
RTFileSystem LPTFiles = { RT_FS_LPT_DEVICE, 0, 0, &RTLPTFileSystem };
/* logical drive Z: can be used to access the RAM drive */
RTFileSystem RAMFiles =
{ RT_FS_FILE, 1 << ('Z' - 'A'), 0, &RTRAMFileSystem };
/* logical drive A: through D: are reserved for FAT */
RTFileSystem FATFiles =
{ RT_FS_FILE | RT_FS_IS_DEFAULT, 0x0F, 0x03, &RTFilesFileSystem };
RTFileSystem *RTFileSystemList[] = {
&Console,
&LPTFiles,
&RAMFiles,
&FATFiles,
NULL,
};
#endif
/*-----------------------------------*/
VOIDEXPORT Init(void)
{
(void) RTSetFlags(RT_MM_VIRTUAL, 1); /* this is the better method */
(void) RTCMOSExtendHeap(); /* get as much memory as we can */
RTCMOSSetSystemTime(); /* get the right date and time */
RTEmuInit(); /* set up floating point emulation */
/* pizza - RTHaltCPL3 appears to cause problems with file handling */
/*RTIdleHandler = (void RTTAPI *)RTHaltCPL3; // low power when idle */
/* not needed with pre-emptive */
/*RTKTimeSlice(2); // allow same priority task switch */
RTKConfig.Flags |= RF_PREEMPTIVE; /* preemptive multitasking */
RTKConfig.Flags |= RF_WIN32MUTEX_MUTEX; /* Win32 mutexes are RTK32 mutexes */
RTKConfig.Flags |= RF_FPCONTEXT; /* saves floating point context for tasks */
RTKConfig.HookedIRQs |= 1 << 1; /* hook the keyboard IRQ */
RTKConfig.DefaultTaskStackSize = 1024 * 8; /* for Win32 task stacks req = 0 */
}
+172
View File
@@ -0,0 +1,172 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
/* This is one way to use the embedded BACnet stack under RTOS-32 */
/* compiled with Borland C++ 5.02 */
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <conio.h> /* for kbhit */
#include "config.h"
#include "bacdef.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "handlers.h"
#include "datalink.h"
#include "iam.h"
#include "txbuf.h"
/* RTOS-32 */
#include "rtkernel.h"
#if defined(RTK32_VER)
#define _USER32_
#define _KERNEL32_
#include <windows.h>
#include <rttarget.h> /* for RTCMOSSetSystemTime */
#include <rtfiles.h> /* file system */
#include <rtfsys.h> /* file system */
#include <Rttbios.h>
#endif
#include <rtcom.h> /* serial port driver */
#include <itimer.h> /* time measurement & timer interrupt rate control */
#include <rtkeybrd.h> /* interrupt handler for the keyboard */
/* buffers used for transmit and receive */
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
static void Init_Service_Handlers(void)
{
/* we need to handle who-is to support dynamic device binding */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
handler_who_is);
/* set the handler for all the services we don't implement */
/* It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler
(handler_unrecognized_service);
/* we must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
handler_write_property);
}
void millisecond_task(void)
{
Time ticks = 0; /* task cycle */
int i = 0; /* loop counter */
ticks = RTKGetTime() + MilliSecsToTicks(1);
while (TRUE) {
RTKDelayUntil(ticks);
dlmstp_millisecond_timer();
ticks += MilliSecsToTicks(1);
}
}
void RTOS_Initialize(void)
{
/* allow OS to setup IRQ 1 by using a dummy call */
(void) kbhit();
RTKernelInit(5); /* get the kernel going */
RTKeybrdInit();
/*(void)CPUMoniInit(); /* not needed - just monitor idle task */ */
RTComInit();
ITimerInit();
if (RTCallDebugger(RT_DBG_MONITOR, 0, 0) != -1) {
/* Win32 structured exception - if no handler is
installed, TerminateProcess() will be called,
which will reboot - a good thing in our case. */
RTRaiseCPUException(0); /* Divide Error DIV and IDIV instructions. */
RTRaiseCPUException(1); /* Debug Any code or data reference. */
RTRaiseCPUException(2); /* NMI */
RTRaiseCPUException(3); /* Breakpoint INT 3 instruction. */
RTRaiseCPUException(4); /* Overflow INTO instruction. */
RTRaiseCPUException(5); /* BOUND Range Exceeded BOUND instruction. */
RTRaiseCPUException(6); /* Invalid Opcode (Undefined Opcode) */
/* RTRaiseCPUException(7); // Device Not Available (No Math Coprocessor) */
RTRaiseCPUException(8); /* Double Fault any exception instruction,NMI,INTR. */
RTRaiseCPUException(9); /* Co-Processor overrun */
RTRaiseCPUException(10); /* Invalid TSS Task switch or TSS access. */
RTRaiseCPUException(11); /* Segment Not Present Loading segment registers */
RTRaiseCPUException(12); /* Stack Seg Fault Stack ops /SS reg loads. */
RTRaiseCPUException(13); /* General Protection Any memory reference */
RTRaiseCPUException(14); /* Page Fault Any memory reference. */
RTRaiseCPUException(15); /* reserved */
RTRaiseCPUException(16); /* Floating-Point Error (Math Fault) */
}
/* setup 1ms timer tick */
SetTimerIntVal(1000);
/* per recommendation in manual */
RTKDelay(1);
RTCMOSSetSystemTime(); /* get the right time-of-day */
/* create timer tick task */
RTKCreateTask(millisecond_task, 16, 1024 * 8, "millisec task");
}
int main(int argc, char *argv[])
{
BACNET_ADDRESS src = { 0 }; /* address where message came from */
uint16_t pdu_len = 0;
unsigned timeout = 100; /* milliseconds */
(void) argc;
(void) argv;
Device_Set_Object_Instance_Number(126);
Init_Service_Handlers();
RTOS_Initialize();
/* init the physical layer */
#ifdef BACDL_BIP
if (!bip_init())
return 1;
#endif
#ifdef BACDL_ETHERNET
if (!ethernet_init(NULL))
return 1;
#endif
#ifdef BACDL_MSTP
dlmstp_set_my_address(0x05);
dlmstp_init();
#endif
iam_send(&Handler_Transmit_Buffer[0]);
/* loop forever */
for (;;) {
/* input */
/* returns 0 bytes on timeout */
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
/* process */
if (pdu_len) {
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
/* output */
/* blink LEDs, Turn on or off outputs, etc */
}
}
@@ -0,0 +1,197 @@
#
# Simple makefile to build an RTB executable for RTOS-32
#
# This makefile assumes Borland bcc32 development environment
# on Windows NT/9x/2000/XP
#
!ifndef RTOS32_DIR
RTOS32_DIR_Not_Defined:
@echo .
@echo You must define environment variable RTOS32_DIR to compile.
!endif
!ifndef BORLAND_DIR
BORLAND_DIR_Not_Defined:
@echo .
@echo You must define environment variable BORLAND_DIR to compile.
!endif
PRODUCT = bacnet
PRODUCT_RTB = $(PRODUCT).rtb
PRODUCT_EXE = $(PRODUCT).exe
# Choose the Data Link Layer to Enable
#DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=1;BACDL_BIP=1
#DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=1;BACDL_ETHERNET=1
#DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=1;BACDL_ARCNET=1
DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=0;BACDL_MSTP=1
SRCS = main.c \
ethernet.c \
bip-init.c \
dlmstp.c \
rs485.c \
init.c \
..\..\bip.c \
..\..\mstp.c \
..\..\crc.c \
..\..\demo\handler\h_iam.c \
..\..\demo\handler\h_whois.c \
..\..\demo\handler\h_wp.c \
..\..\demo\handler\h_rp.c \
..\..\demo\handler\noserv.c \
..\..\demo\handler\txbuf.c \
..\..\demo\handler\s_rp.c \
..\..\demo\handler\s_whois.c \
..\..\bacdcode.c \
..\..\bacstr.c \
..\..\bactext.c \
..\..\indtext.c \
..\..\bacapp.c \
..\..\bigend.c \
..\..\whois.c \
..\..\dcc.c \
..\..\iam.c \
..\..\rp.c \
..\..\wp.c \
..\..\arf.c \
..\..\awf.c \
..\..\demo\object\bacfile.c \
..\..\demo\object\device.c \
..\..\demo\object\ai.c \
..\..\demo\object\ao.c \
..\..\demo\object\av.c \
..\..\demo\object\bi.c \
..\..\demo\object\bo.c \
..\..\demo\object\bv.c \
..\..\demo\object\lsp.c \
..\..\demo\object\mso.c \
..\..\datalink.c \
..\..\tsm.c \
..\..\address.c \
..\..\abort.c \
..\..\reject.c \
..\..\bacerror.c \
..\..\apdu.c \
..\..\npdu.c
OBJS = $(SRCS:.c=.obj)
# Compiler definitions
#
CC = $(BORLAND_DIR)\bin\bcc32 +bcc32.cfg
LINK = $(BORLAND_DIR)\bin\tlink32
#LINK = $(BORLAND_DIR)\bin\ilink32
TLIB = $(BORLAND_DIR)\bin\tlib
LOCATE = $(RTOS32_DIR)\bin\rtloc
#
# Include directories
#
CC_DIR = $(BORLAND_DIR)\BIN
INCL_DIRS = -I$(BORLAND_DIR)\include;$(RTOS32_DIR)\include;..\..\;..\..\demo\handler\;..\..\demo\object\;.
CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES)
# Libraries
#
RTOS32_LIB_DIR = $(RTOS32_DIR)\libbc
C_LIB_DIR = $(BORLAND_DIR)\lib
LIBDIR = $(RTOS32_LIB_DIR);$(C_LIB_DIR)
LIBS = $(RTOS32_LIB_DIR)\RTFILES.LIB \
$(RTOS32_LIB_DIR)\RTFSK32.LIB \
$(RTOS32_LIB_DIR)\DRVDOC.LIB \
$(RTOS32_LIB_DIR)\RTIP.LIB \
$(RTOS32_LIB_DIR)\RTK32.LIB \
$(RTOS32_LIB_DIR)\FLTEMUMT.LIB \
$(RTOS32_LIB_DIR)\DRVRT32.LIB \
$(RTOS32_LIB_DIR)\RTEMUMT.LIB \
$(RTOS32_LIB_DIR)\RTT32.LIB \
$(RTOS32_LIB_DIR)\RTTHEAP.LIB \
#$(C_LIB_DIR)\DPMI32.lib \
$(C_LIB_DIR)\IMPORT32.lib \
$(C_LIB_DIR)\CW32MT.lib
#
# Main target
#
# This should be the first one in the makefile
all : $(PRODUCT_RTB) monitor.rtb
monitor.rtb: monitor.cfg hardware.cfg
$(LOCATE) monitor
# debug using COM3 (ISA Card) as the debug port
# boot from floppy
debugcom3: hardware.cfg software.cfg $(PRODUCT_RTB) monitor.rtb
$(LOCATE) -DDEBUGCOM3 monitor
$(LOCATE) -d- -DMONITOR -DDEBUGCOM3 $(PRODUCT) software.cfg
$(PRODUCT_RTB): bcc32.cfg hardware.cfg software.cfg $(PRODUCT_EXE)
@echo Running Locate on $(PRODUCT)
$(LOCATE) $(PRODUCT) software.cfg
# 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$(LINKER_LIB) -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), $(PRODUCT_RTB) and map files.
del *.obj
del ..\..\*.obj
del $(PRODUCT_EXE)
del $(PRODUCT_RTB)
del *.map
del bcc32.cfg
install : $(PRODUCT)
copy $(PRODUCT) ..\bin
#
# Generic rules
#
.SUFFIXES: .cpp .c .sbr .obj
#
# cc generic rule
#
.c.obj:
$(CC) -o$@ $<
# Compiler configuration file
bcc32.cfg :
Copy &&|
$(CFLAGS)
-c
#-g2 #stop after gN warnings
-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
@@ -0,0 +1,47 @@
// Configuration files for the RTTarget-32 Debug Monitor and Borland C/C++.
// Some general parameters for this file are:
//
// * The default disk boot code is used to boot the system from
// a floppy disk, hard disk, or ROM disk.
// * Pageing is enabled.
// * The program privilege level is set to 3 for maximum protection.
// * Boot code and the Monitor are placed in low (conventional) memory.
// * The target PC is assumed to have a color display.
// * The target PC uses COM1 to communicated with the host.
// * 115200 baud is used for host - target communication.
@HARDWARE.CFG // pull in hardware definitions
Locate BootCode BIOSBOOT.EXE LowMem // boot from disk
Locate BootData BootData LowMem 0 16 // boot stuff must be in conventional memory
Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code
CPL = 0
Locate Section CODE LowMem 1 // Monitor's code section
Locate Header Monitor LowMem 0 4 // and header
Locate Section DATA LowMem 2 // data section
Locate Stack Stack LowMem 1k 4 // and a small stack, no heap
Locate PageTable Pages LowMem
Locate DecompCode Expand LowMem // include decompression stuff
Locate DecompData ExBuffer LowMem
Locate Copy CODE LowMem // compress everything
Locate Copy DATA LowMem // ditto
Locate Copy Pages LowMem // ditto
#ifdef DEBUGCOM1
COMPort COM1 115200 // use COM1 with 115200 baud
VideoRAM = None // program output sent to debugger - clrscr() crashes it.
#elifdef DEBUGCOM3
COMPort COM3 115200 9 // use COM3 IRQ9 115200 baud - Everex EV170 serial card
//VideoRAM = ColorText // program output sent to Graphic Card
VideoRAM = None // program output sent to debugger - clrscr() crashes it.
#else
COMPort COM3 115200 9 // use COM3 IRQ9 115200 baud - Everex EV170 serial card
VideoRAM = ColorText // program output sent to Graphic Card
#endif
IgnoreMsg "No heap" // the monitor does not need a heap
File diff suppressed because it is too large Load Diff
+244
View File
@@ -0,0 +1,244 @@
/*####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,
MSTP_RECEIVE_STATE_PREAMBLE,
MSTP_RECEIVE_STATE_HEADER,
MSTP_RECEIVE_STATE_HEADER_CRC,
MSTP_RECEIVE_STATE_DATA
} MSTP_RECEIVE_STATE;
/* master node FSM states */
typedef enum {
MSTP_MASTER_STATE_INITIALIZE,
MSTP_MASTER_STATE_IDLE,
MSTP_MASTER_STATE_USE_TOKEN,
MSTP_MASTER_STATE_WAIT_FOR_REPLY,
MSTP_MASTER_STATE_DONE_WITH_TOKEN,
MSTP_MASTER_STATE_PASS_TOKEN,
MSTP_MASTER_STATE_NO_TOKEN,
MSTP_MASTER_STATE_POLL_FOR_MASTER,
MSTP_MASTER_STATE_ANSWER_DATA_REQUEST
} 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 FramingError:1; /* TRUE if we got a framing error */
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;
/* After receiving a frame this value will be TRUE until Tturnaround */
/* has expired */
unsigned Turn_Around_Waiting: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[MAX_MPDU];
/* "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. */
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
/* 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 */
#define Tturnaround 40;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port,
uint8_t this_station_mac);
void MSTP_Millisecond_Timer(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) */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+80
View File
@@ -0,0 +1,80 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
#ifndef NET_H
#define NET_H
/* network file for RTOS-32 from On-Time */
#define WIN32_LEAN_AND_MEAN
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include "bip.h"
#ifndef HOST
#include <rtipapi.h>
#include "netcfg.h"
#include <rttarget.h>
#include <rtk32.h>
#include <clock.h>
#include <socket.h>
/*
* Definitions of bits in internet address integers.
* On subnets, the decomposition of addresses to host and net parts
* is done according to subnet mask, not the masks here.
*/
#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0)
#define IN_CLASSA_NET 0xff000000
#define IN_CLASSA_NSHIFT 24
#define IN_CLASSA_HOST 0x00ffffff
#define IN_CLASSA_MAX 128
#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000)
#define IN_CLASSB_NET 0xffff0000
#define IN_CLASSB_NSHIFT 16
#define IN_CLASSB_HOST 0x0000ffff
#define IN_CLASSB_MAX 65536
#define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000)
#define IN_CLASSC_NET 0xffffff00
#define IN_CLASSC_NSHIFT 8
#define IN_CLASSC_HOST 0x000000ff
#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000)
#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */
#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */
#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */
#define IN_MULTICAST(i) IN_CLASSD(i)
#else
#include <winsock.h>
#endif
#define close closesocket
typedef size_t socklen_t;
#endif
+104
View File
@@ -0,0 +1,104 @@
/**************************************************************************/
/* */
/* File: NetCfg.h Copyright (c) 1996,2004 */
/* Version: 4.0 On Time Informatik GmbH */
/* */
/* */
/* On Time /////////////----- */
/* Informatik GmbH ///////////// */
/* --------------------------------------------------///////////// */
/* Real-Time and System Software */
/* */
/**************************************************************************/
/* Network environment configuration file for the On Time RTOS-32 RTIP-32
demos.
By default, the RTIP-32 demos use static IP address assignment. If this is
not what you want, uncomment either #define AUTO_IP or #define DHCP. In all
cases, make sure the IP addresses (NetMask, TargetIP, DefaultGateway,
DNSServer, etc) given in the respective section below are all correct for
your select and LAN configuration. If you choose to use DHCP, the library
Dhcpc.lib must also be linked.
Please define symbol DEVICE_ID to match your target's ethernet
card and make sure that the card's hardware resource assigments are
correct (for PCI cards, the drivers will determine this information
automatically).
*/
/* #define AUTO_IP // use xn_autoip() to get an IP address */
/* #define DHCP // if you enable this, you must also link library dhcpc.lib */
#if defined(AUTO_IP) /* use xn_autoip() to get an IP address */
static BYTE TargetIP[] = { 0, 0, 0, 0 }; /* will be filled at run-time */
static BYTE NetMask[] = { 255, 255, 255, 0 };
static BYTE MinIP[] = { 192, 168, 0, 128 };
static BYTE MaxIP[] = { 192, 168, 0, 255 };
static BYTE DefaultGateway[] = { 192, 168, 0, 1 }; /* set to zero if not available or required */
static BYTE DNSServer[] = { 192, 168, 0, 1 }; /* ditto */
#elif defined(DHCP) /* use DHCP */
#include <dhcpcapi.h>
static BYTE TargetIP[] = { 0, 0, 0, 0 }; /* will be filled at run-time */
#else /* static IP address assignment (default) */
static BYTE TargetIP[] = { 192, 168, 0, 50 };
static BYTE NetMask[] = { 255, 255, 255, 0 };
static BYTE DefaultGateway[] = { 192, 168, 0, 1 }; /* set to zero if not available or required */
static BYTE DNSServer[] = { 192, 168, 0, 1 }; /* ditto */
#endif
#define DEVICE_ID DAVICOM_DEVICE /* define your device type here */
#ifndef DEVICE_ID
#error You must define Ethernet driver/resources and IP address/net mask here
#endif
/* The following values are ignored for PCI devices (the BIOS supplies */
/* them), but they must be set correctly for ISA/PCMCIA systems and for */
/* PCI devices if you do not have a BIOS */
#define ED_IO_ADD 0x300 /* I/O address of the device */
#define ED_IRQ 5 /* IRQ of the device */
#define ED_MEM_ADD 0 /* Memory Window (only some devices) */
/* Define function to pull in the required driver */
#if DEVICE_ID == NE2000_DEVICE
#define BIND_DRIVER xn_bind_ne2000
#elif DEVICE_ID == N83815_DEVICE
#define BIND_DRIVER xn_bind_n83815
#elif DEVICE_ID == TC90X_DEVICE
#define BIND_DRIVER xn_bind_tc90x
#elif DEVICE_ID == SMC91C9X_DEVICE
#define BIND_DRIVER xn_bind_smc91c9x
#elif DEVICE_ID == LANCE_DEVICE
#define BIND_DRIVER xn_bind_rtlance
#elif DEVICE_ID == LANCE_ISA_DEVICE
#define BIND_DRIVER xn_bind_lance_isa
#elif DEVICE_ID == LAN_CS89X0_DEVICE
#define BIND_DRIVER xn_bind_cs
#elif DEVICE_ID == I82559_DEVICE
#define BIND_DRIVER xn_bind_i82559
#elif DEVICE_ID == R8139_DEVICE
#define BIND_DRIVER xn_bind_r8139
#elif DEVICE_ID == DAVICOM_DEVICE
#define BIND_DRIVER xn_bind_davicom
#elif DEVICE_ID == RHINE_DEVICE
#define BIND_DRIVER xn_bind_rhine
#elif DEVICE_ID == AX172_DEVICE
#include <rtusb.h> /* must also link Rtusb.lib and UsbInit.cpp */
#define BIND_DRIVER xn_bind_ax172
#elif DEVICE_ID == AX772_DEVICE
#include <rtusb.h> /* must also link Rtusb.lib and UsbInit.cpp */
#define BIND_DRIVER xn_bind_ax772
#elif DEVICE_ID == PRISM_DEVICE
#include <wlanapi.h> /* must also link Wlan.lib */
#define BIND_DRIVER xn_bind_prism
#elif DEVICE_ID == PRISM_PCMCIA_DEVICE
#include <rtpcmcia.h>
#include <wlanapi.h> /* must also link Wlan.lib */
#define BIND_DRIVER xn_bind_prism_pcmcia
#else
#error Invalid DEVICE_ID value
#endif
+218
View File
@@ -0,0 +1,218 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
#if PRINT_ENABLED
#define PRINT_ENABLED_RS485 1
#else
#define PRINT_ENABLED_RS485 0
#endif
#include <stdint.h>
#include <rtkernel.h>
#include <rtcom.h>
#include <itimer.h>
#if PRINT_ENABLED_RS485
#include <stdio.h>
#endif
#include "mstp.h"
/* note: use the RTKernel-C API so that it can use this library */
#define RS485_IO_ENABLE(p) ModemControl(p,0,DTR);
#define RS485_TRANSMIT_ENABLE(p) ModemControl(p,1,RTS);
#define RS485_RECEIVE_ENABLE(p) ModemControl(p,0,RTS);
/* COM port number - COM1 = 0 */
static int RS485_Port = COM2;
/* baud rate */
static long RS485_Baud = 9600;
/* io base address */
static long RS485_Base = 0;
/* hardware IRQ number */
static long RS485_IRQ_Number = 0;
#if PRINT_ENABLED_RS485
static FineTime RS485_Debug_Transmit_Timer;
#endif
#if PRINT_ENABLED_RS485
void RS485_Print_Frame(int port, FineTime timer, uint8_t * buffer, /* frame to send (up to 501 bytes of data) */
uint16_t nbytes)
{
uint16_t i; /* byte counter */
unsigned long duration; /* measures the time from last output to this one */
unsigned long seconds;
unsigned long milliseconds;
duration = ElapsedMilliSecs(timer);
seconds = duration / 1000U;
milliseconds = duration - (seconds * 1000U);
fprintf(stderr, "%0lu.%03lu: COM%d:", seconds, milliseconds, port + 1);
for (i = 0; i < nbytes; i++) {
unsigned value;
value = buffer[i];
fprintf(stderr, " %02X", value);
}
fprintf(stderr, "\n");
fflush(stderr);
}
#endif
static void RS485_Standard_Port_Settings(long port, long *pIRQ,
long *pBase)
{
switch (port) {
case COM1:
*pBase = (long) 0x3F8;
*pIRQ = 4L;
break;
case COM2:
*pBase = (long) 0x2F8;
*pIRQ = 3L;
break;
case COM3:
*pBase = (long) 0x3E8;
*pIRQ = 4L;
break;
case COM4:
*pBase = (long) 0x2E8;
*pIRQ = 3L;
break;
default:
break;
}
}
static int TestCOMPort(int Base)
{ /* base address of UART */
int i;
for (i = 0; i < 256; i++) {
RTOut(Base + 7, (BYTE) i); /* write scratch register */
RTOut(Base + 1, RTIn(Base + 1)); /* read/write IER */
if (RTIn(Base + 7) != i) /* check scratch register */
return FALSE;
}
return TRUE;
}
static RS485_Open_Port(int port, /* COM port number - COM1 = 0 */
long baud, /* baud rate */
unsigned base, /* io base address */
int irq)
{ /* hardware IRQ number */
if (!TestCOMPort(base))
return;
/* setup the COM IO */
SetIOBase(port, base);
SetIRQ(port, irq);
if (irq < 8)
RTKIRQTopPriority(irq, 9);
InitPort(port, baud, PARITY_NONE, 1, 8);
if (HasFIFO(port))
EnableFIFO(port, 8);
EnableCOMInterrupt(port, 1024 * 4);
/* enable the 485 via the DTR pin */
RS485_IO_ENABLE(port);
RS485_RECEIVE_ENABLE(port);
#if PRINT_ENABLED_RS485
fprintf(stderr, "RS485: COM%d Enabled\r\n", port + 1);
#endif
return;
}
void RS485_Initialize(void)
{
#if PRINT_ENABLED_RS485
MarkTime(&RS485_Debug_Transmit_Timer);
#endif
RS485_Standard_Port_Settings(RS485_Port, &RS485_IRQ_Number,
&RS485_Base);
RS485_Open_Port(RS485_Port, RS485_Baud, RS485_Base, RS485_IRQ_Number);
}
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) */
bool status = true; /* return value */
RS485_TRANSMIT_ENABLE(RS485_Port);
SendBlock(RS485_Port, (char *) buffer, nbytes);
/* need to wait at least 9600 baud * 512 bytes = 54mS */
(void) WaitSendBufferEmpty(RS485_Port, MilliSecsToTicks(200));
while (!(LineStatus(RS485_Port) & TX_SHIFT_EMPTY))
RTKScheduler();
RS485_RECEIVE_ENABLE(RS485_Port);
/* SilenceTimer is cleared by the Receive State Machine when
activity is detected and by the SendFrame procedure as each
octet is transmitted. */
mstp_port->SilenceTimer = 0;
#if PRINT_ENABLED_RS485
RS485_Print_Frame(RS485_Port, RS485_Debug_Transmit_Timer, buffer, /* frame to send (up to 501 bytes of data) */
nbytes);
MarkTime(&RS485_Debug_Transmit_Timer);
#endif
return;
}
void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port)
{ /* port specific data */
COMData com_data = 0; /* byte from COM driver */
unsigned timeout = 1; /* milliseconds to wait for a character */
static Duration ticks = 0; /* duration to wait for data */
if (mstp_port->ReceiveError) {
/* wait for state machine to clear this */
RTKScheduler();
}
/* wait for state machine to read from the DataRegister */
else if (!mstp_port->DataAvailable) {
if (!ticks) {
ticks = MilliSecsToTicks(timeout);
if (!ticks)
ticks = 1;
}
/* check for data */
if (RTKGetTimed(ReceiveBuffer[RS485_Port], &com_data, ticks)) {
/* if error, */
if (com_data & (COM_OVERRUN << 8))
mstp_port->ReceiveError = true;
else if (com_data & (COM_FRAME << 8))
mstp_port->ReceiveError = true;
else {
mstp_port->DataRegister = com_data & 0x00FF;
mstp_port->DataAvailable = true;
}
}
} else
RTKScheduler();
}
+57
View File
@@ -0,0 +1,57 @@
/*####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_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) */
void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port); /* port specific data */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
@@ -0,0 +1,3 @@
set BORLAND_DIR=\bc5
set RTOS32_DIR=\code\rtos32
@@ -0,0 +1,61 @@
// Configuration files for the application and Borland C/C++.
// Some general parameters for this file are:
// * The program will run under the control of the debugger or is
// downloaded using RTRun.
// * Paging is enabled.
// * The program privilege level is set to 3 for maximum protection.
// * Boot code and the Monitor are placed in low (conventional) memory.
// * The program is placed in high (extended) memory.
// * Unused low memory is remapped and appended to the high memory area.
// * The Turbo Debugger symbol tables are pulled in to support
// task positions at source level.
@HARDWARE.CFG
// Either use the monitor, or create bootable code.
#ifdef MONITOR
Reserve Monitor // leave room for Debug Monitor
#elifdef DEBUGDOS
Locate BootCode BIOSBOOT.EXE LowMem // boot from disk
Locate BootData BootData LowMem // must be in conventional mem
Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code
NoFPU=0 // Check FPU
CPL = 3 // normal priveleges
VideoRAM ColorText // program output sent to Graphic Card
#else
Locate BootCode BIOSBOOT.EXE LowMem // boot from disk
Locate BootData BootData LowMem 0 16 // must be in conventional mem
Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code
NoFPU=0 // Check FPU
CPL = 3 // normal priveleges
// VideoRAM ColorText // program output sent to Graphic Card
VideoRAM None // program output sent to file and host
#endif
FillRAM HeapMem // remap unused RAM
Locate Header Header LowMem // application header
Locate PageTable Paging LowMem 20k // paging to use this
Locate NTSection CODE ProgMem->HighMem // code section
Locate NTSection DATA ProgMem->HighMem // data section
Locate NTSection .tls ProgMem->HighMem // TLS data section
Locate NTSection .rdata ProgMem->HighMem // TLS directory
Locate Stack Stack StackMem->LowMem 6k // stack space for main()
Locate Heap Heap HeapMem // and the rest for the heap
// Compression needed if we are short on disk space - but shortens download
// Note that this is discardable, unless we use -d- option of RTLoc
Locate DecompCode Expand LowMem // include decompression stuff
Locate DecompData ExBuffer LowMem
Locate Copy Paging LowMem // compress Paging
Locate Copy CODE HighMem // compress CODE
Locate Copy DATA HighMem // compress DATA
Locate Nothing FloppyDMA MoreLowMem 18k 64k ReadWrite // floppy driver
Init _Init // do some standard initializations (see init.c)
CommandLine "bacnet.exe"
+28
View File
@@ -0,0 +1,28 @@
#ifndef STDBOOL_H
#define STDBOOL_H
/* C99 Boolean types for compilers without C99 support */
#ifndef __cplusplus
typedef int _Bool;
#ifndef bool
#define bool _Bool
#endif
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#define __bool_true_false_are_defined 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#endif
+19
View File
@@ -0,0 +1,19 @@
/* Defines the standard integer types that are used in code */
/* for the x86 processor and Borland Compiler */
#ifndef STDINT_H
#define STDINT_H
#include <stddef.h>
typedef unsigned char uint8_t; /* 1 byte 0 to 255 */
typedef signed char int8_t; /* 1 byte -127 to 127 */
typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */
typedef signed short int16_t; /* 2 bytes -32767 to 32767 */
/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */
typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */
typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */
/* typedef signed long long int64_t; */
/* typedef unsigned long long uint64_t; */
#endif /* STDINT_H */
+151
View File
@@ -0,0 +1,151 @@
#
# Simple makefile to build an RTB executable for RTOS-32
#
# This makefile assumes Borland bcc32 development environment
# on Windows NT/9x/2000/XP
#
!ifndef BORLAND_DIR
BORLAND_DIR_Not_Defined:
@echo .
@echo You must define environment variable BORLAND_DIR to compile.
!endif
PRODUCT = bacnet
PRODUCT_EXE = $(PRODUCT).exe
# Choose the Data Link Layer to Enable
DEFINES = -DBACDL_BIP=1;TSM_ENABLED=1;BIG_ENDIAN=0
SRCS = main.c bip-init.c \
..\..\bip.c \
..\..\demo\handler\h_iam.c \
..\..\demo\handler\h_whois.c \
..\..\demo\handler\h_wp.c \
..\..\demo\handler\h_rp.c \
..\..\demo\handler\h_rp_a.c \
..\..\demo\handler\noserv.c \
..\..\demo\handler\txbuf.c \
..\..\demo\handler\s_rp.c \
..\..\demo\handler\s_whois.c \
..\..\bacdcode.c \
..\..\bacapp.c \
..\..\bacstr.c \
..\..\bactext.c \
..\..\indtext.c \
..\..\bigend.c \
..\..\whois.c \
..\..\iam.c \
..\..\dcc.c \
..\..\rp.c \
..\..\wp.c \
..\..\arf.c \
..\..\awf.c \
..\..\demo\object\bacfile.c \
..\..\demo\object\device.c \
..\..\demo\object\ai.c \
..\..\demo\object\ao.c \
..\..\demo\object\av.c \
..\..\demo\object\bi.c \
..\..\demo\object\bo.c \
..\..\demo\object\bv.c \
..\..\demo\object\lsp.c \
..\..\demo\object\mso.c \
..\..\datalink.c \
..\..\tsm.c \
..\..\address.c \
..\..\abort.c \
..\..\reject.c \
..\..\bacerror.c \
..\..\apdu.c \
..\..\npdu.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
INCL_DIRS = -I$(BORLAND_DIR)\include;..\..\;..\..\demo\handler\;..\..\demo\object\;.
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
Binary file not shown.
@@ -0,0 +1,460 @@
# Microsoft Developer Studio Project File - Name="bacnet" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=bacnet - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "bacnet.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "bacnet.mak" CFG="bacnet - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "bacnet - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "bacnet - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "bacnet - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /I "..\..\.." /I ".." /I "..\..\..\demo\object\\" /I "..\..\..\demo\handler\\" /D "NDEBUG" /D "BACDL_BIP" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D TSM_ENABLED=1 /D BACAPP_PRINT_ENABLED=1 /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "bacnet - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\.." /I ".." /I "..\..\..\demo\object\\" /I "..\..\..\demo\handler\\" /D "_DEBUG" /D BACDL_BIP=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D TSM_ENABLED=1 /D PRINT_ENABLED=1 /D BIG_ENDIAN=0 /D USE_INADDR=0 /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "bacnet - Win32 Release"
# Name "bacnet - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=..\..\..\abort.c
# End Source File
# Begin Source File
SOURCE=..\..\..\address.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\ai.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\ao.c
# End Source File
# Begin Source File
SOURCE=..\..\..\apdu.c
# End Source File
# Begin Source File
SOURCE=..\..\..\arf.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\av.c
# End Source File
# Begin Source File
SOURCE=..\..\..\bacapp.c
# End Source File
# Begin Source File
SOURCE=..\..\..\bacdcode.c
# End Source File
# Begin Source File
SOURCE=..\..\..\bacerror.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\bacfile.c
# End Source File
# Begin Source File
SOURCE=..\..\..\bacstr.c
# End Source File
# Begin Source File
SOURCE=..\..\..\bactext.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\bi.c
# End Source File
# Begin Source File
SOURCE=..\..\..\bigend.c
# End Source File
# Begin Source File
SOURCE="..\bip-init.c"
# End Source File
# Begin Source File
SOURCE=..\..\..\bip.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\bo.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\bv.c
# End Source File
# Begin Source File
SOURCE=..\..\..\crc.c
# End Source File
# Begin Source File
SOURCE=..\..\..\datetime.c
# End Source File
# Begin Source File
SOURCE=..\..\..\dcc.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\device.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\h_arf.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\h_arf_a.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\h_iam.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\h_rp.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\h_rp_a.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\h_whois.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\h_wp.c
# End Source File
# Begin Source File
SOURCE=..\..\..\iam.c
# End Source File
# Begin Source File
SOURCE=..\..\..\indtext.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\lc.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\lc.h
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\lsp.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\lsp.h
# End Source File
# Begin Source File
SOURCE=..\main.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\mso.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\noserv.c
# End Source File
# Begin Source File
SOURCE=..\..\..\npdu.c
# End Source File
# Begin Source File
SOURCE=..\..\..\reject.c
# End Source File
# Begin Source File
SOURCE=..\..\..\ringbuf.c
# End Source File
# Begin Source File
SOURCE=..\..\..\rp.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\s_rp.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\s_whois.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\s_wp.c
# End Source File
# Begin Source File
SOURCE=..\..\..\tsm.c
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\handler\txbuf.c
# End Source File
# Begin Source File
SOURCE=..\..\..\whois.c
# End Source File
# Begin Source File
SOURCE=..\..\..\wp.c
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=..\..\..\abort.h
# End Source File
# Begin Source File
SOURCE=..\..\..\address.h
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\ai.h
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\ao.h
# End Source File
# Begin Source File
SOURCE=..\..\..\apdu.h
# End Source File
# Begin Source File
SOURCE=..\..\..\arcnet.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bacapp.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bacdcode.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bacdef.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bacenum.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bacerror.h
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\bacfile.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bacstr.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bactext.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bigend.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bip.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bits.h
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\bo.h
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\bv.h
# End Source File
# Begin Source File
SOURCE=..\..\..\bytes.h
# End Source File
# Begin Source File
SOURCE=..\..\..\config.h
# End Source File
# Begin Source File
SOURCE=..\..\..\crc.h
# End Source File
# Begin Source File
SOURCE=..\..\..\datalink.h
# End Source File
# Begin Source File
SOURCE=..\..\..\datetime.h
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\device.h
# End Source File
# Begin Source File
SOURCE=..\..\..\ethernet.h
# End Source File
# Begin Source File
SOURCE=..\..\..\handlers.h
# End Source File
# Begin Source File
SOURCE=..\..\..\iam.h
# End Source File
# Begin Source File
SOURCE=..\..\..\demo\object\mso.h
# End Source File
# Begin Source File
SOURCE=..\..\..\mstp.h
# End Source File
# Begin Source File
SOURCE=..\..\..\npdu.h
# End Source File
# Begin Source File
SOURCE=..\..\..\reject.h
# End Source File
# Begin Source File
SOURCE=..\..\..\ringbuf.h
# End Source File
# Begin Source File
SOURCE=..\..\..\rp.h
# End Source File
# Begin Source File
SOURCE=..\..\..\rs485.h
# End Source File
# Begin Source File
SOURCE=..\stdbool.h
# End Source File
# Begin Source File
SOURCE=..\stdint.h
# End Source File
# Begin Source File
SOURCE=..\..\..\tsm.h
# End Source File
# Begin Source File
SOURCE=..\..\..\whois.h
# End Source File
# Begin Source File
SOURCE=..\..\..\wp.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project
@@ -0,0 +1,29 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "bacnet"=".\bacnet.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,136 @@
<html>
<body>
<pre>
<h1>Build Log</h1>
<h3>
--------------------Configuration: bacnet - Win32 Debug--------------------
</h3>
<h3>Command Lines</h3>
Creating temporary file "C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP862.tmp" with contents
[
/nologo /MLd /W3 /Gm /GX /ZI /Od /I "..\..\.." /I ".." /I "..\..\..\demo\object\\" /I "..\..\..\demo\handler\\" /D "_DEBUG" /D BACDL_BIP=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D TSM_ENABLED=1 /D PRINT_ENABLED=1 /D BIG_ENDIAN=0 /D USE_INADDR=0 /FR"Debug/" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
"C:\code\bacnet-stack\bacapp.c"
"C:\code\bacnet-stack\bacdcode.c"
]
Creating command line "cl.exe @C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP862.tmp"
Creating temporary file "C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP863.tmp" with contents
[
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/bacnet.pdb" /debug /machine:I386 /out:"Debug/bacnet.exe" /pdbtype:sept
".\Debug\abort.obj"
".\Debug\address.obj"
".\Debug\ai.obj"
".\Debug\ao.obj"
".\Debug\apdu.obj"
".\Debug\arf.obj"
".\Debug\av.obj"
".\Debug\bacapp.obj"
".\Debug\bacdcode.obj"
".\Debug\bacerror.obj"
".\Debug\bacfile.obj"
".\Debug\bacstr.obj"
".\Debug\bactext.obj"
".\Debug\bi.obj"
".\Debug\bigend.obj"
".\Debug\bip-init.obj"
".\Debug\bip.obj"
".\Debug\bo.obj"
".\Debug\bv.obj"
".\Debug\crc.obj"
".\Debug\datetime.obj"
".\Debug\dcc.obj"
".\Debug\device.obj"
".\Debug\h_arf.obj"
".\Debug\h_arf_a.obj"
".\Debug\h_iam.obj"
".\Debug\h_rp.obj"
".\Debug\h_rp_a.obj"
".\Debug\h_whois.obj"
".\Debug\h_wp.obj"
".\Debug\iam.obj"
".\Debug\indtext.obj"
".\Debug\lc.obj"
".\Debug\lsp.obj"
".\Debug\main.obj"
".\Debug\mso.obj"
".\Debug\noserv.obj"
".\Debug\npdu.obj"
".\Debug\reject.obj"
".\Debug\ringbuf.obj"
".\Debug\rp.obj"
".\Debug\s_rp.obj"
".\Debug\s_whois.obj"
".\Debug\s_wp.obj"
".\Debug\tsm.obj"
".\Debug\txbuf.obj"
".\Debug\whois.obj"
".\Debug\wp.obj"
]
Creating command line "link.exe @C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP863.tmp"
<h3>Output Window</h3>
Compiling...
bacapp.c
bacdcode.c
Generating Code...
Linking...
Creating temporary file "C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP864.tmp" with contents
[
/nologo /o"Debug/bacnet.bsc"
".\Debug\abort.sbr"
".\Debug\address.sbr"
".\Debug\ai.sbr"
".\Debug\ao.sbr"
".\Debug\apdu.sbr"
".\Debug\arf.sbr"
".\Debug\av.sbr"
".\Debug\bacapp.sbr"
".\Debug\bacdcode.sbr"
".\Debug\bacerror.sbr"
".\Debug\bacfile.sbr"
".\Debug\bacstr.sbr"
".\Debug\bactext.sbr"
".\Debug\bi.sbr"
".\Debug\bigend.sbr"
".\Debug\bip-init.sbr"
".\Debug\bip.sbr"
".\Debug\bo.sbr"
".\Debug\bv.sbr"
".\Debug\crc.sbr"
".\Debug\datetime.sbr"
".\Debug\dcc.sbr"
".\Debug\device.sbr"
".\Debug\h_arf.sbr"
".\Debug\h_arf_a.sbr"
".\Debug\h_iam.sbr"
".\Debug\h_rp.sbr"
".\Debug\h_rp_a.sbr"
".\Debug\h_whois.sbr"
".\Debug\h_wp.sbr"
".\Debug\iam.sbr"
".\Debug\indtext.sbr"
".\Debug\lc.sbr"
".\Debug\lsp.sbr"
".\Debug\main.sbr"
".\Debug\mso.sbr"
".\Debug\noserv.sbr"
".\Debug\npdu.sbr"
".\Debug\reject.sbr"
".\Debug\ringbuf.sbr"
".\Debug\rp.sbr"
".\Debug\s_rp.sbr"
".\Debug\s_whois.sbr"
".\Debug\s_wp.sbr"
".\Debug\tsm.sbr"
".\Debug\txbuf.sbr"
".\Debug\whois.sbr"
".\Debug\wp.sbr"]
Creating command line "bscmake.exe @C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP864.tmp"
Creating browse info file...
<h3>Output Window</h3>
<h3>Results</h3>
bacnet.exe - 0 error(s), 0 warning(s)
</pre>
</body>
</html>
@@ -0,0 +1,113 @@
BACnet Stack - SourceForge.net
Build for Visual C++ 6.0
When building the BACnet stack using Visual C++ compiler,
there are some settings that are important.
Q. Are there some global configuration options for this BACnet stack?
A. The BACnet stack uses some preprocessor defines to configure
a number of subtle personalities.
PRINT_ENABLED=1 - enables printing to stdio
BIG_ENDIAN=0 - chooses the BACnet encoding and decoding order
BACDL_BIP=1 - chooses BACnet/IP for the datalink layer
BACDL_ETHERNET=0 - chooses BACnet Ethernet for the datalink layer
BACDL_ARCNET=0 - chooses BACnet ARCNET for the datalink layer
BACDL_MSTP=0 - chooses BACnet MS/TP for the datalink layer
USE_INADDR=1 - uses INADDR_BROADCAST for broadcast rather than CLASSx
TSM_ENABLED=1 - enables the Transaction State Machine for clients
BIP_DEBUG=1 - enables print statements for debugging
In Visual C++, add a Preprocessor Definition by:
1. Select "Project" menu
2. Select "Settings..."
3. Select the "C/C++" tab (3rd Tab)
4. Select the Category: General
5. You can see the "Preprocessor Definitions:" box
6. Type OPTION_NAME=1 or OPTION_NAME=0 in that edit box
using a comma to separate multiple options.
7. Press OK
8. Compile the entire project again...
Q. MSVC refuses to open bacnet.dsw and bacnet.dsp.
A. bacnet.dsw and bacnet.dsp are text files that were retrieved
from CVS on a unix client and are now in unix text file format since
they end with a "\r\n" rather than "\n". Use the unix2dos commandline
tool to convert them back to dos:
unix2dos bacnet.dsw
unix2dos bacnet.dsp
Q. error LNK2001: unresolved external symbol _WinMain@16
A. The demo ports/win32/main.c was designed as a Win32 Console
Application. If you want to change it to a Windows GUI application,
you will have to add all the Windows GUI code, including WinMain().
I recommend that you use a framework, such as WxWidgets/WxWindows,
but this has not been done yet.
Q. error C1083: Cannot open include file: 'stdint.h': No such file
A. The BACnet stack uses some header files, and Visual C++ needs to know
where they are:
1. Select "Project" menu
2. Select "Settings..."
3. Select the "C/C++" tab (3rd Tab)
4. Select the Category: Preprocessor
5. You can see the "Additional include directories:" box
6. Type the path to stdint.h in that edit box (using a comma if necessary)
7. Type the path to bacdcode.h in that edit box (using a comma if necessary)
In my system, the paths look like:
c:\code\bacnet-stack\,c:\code\bacnet-stack\ports\win32\,
c:\code\bacnet-stack\demo\handler\,c:\code\bacnet-stack\demo\object\
8. Press OK
9. Compile the project again...
Q. error C2065: 'MAX_MPDU' : undeclared identifier
A. The BACnet stack uses a preprocessor define to configure
its datalink layer. In Visual C++, add a Preprocessor Definition by:
1. Select "Project" menu
2. Select "Settings..."
3. Select the "C/C++" tab (3rd Tab)
4. Select the Category: General
5. You can see the "Preprocessor Definitions:" box
6. Type BACDL_BIP=1 in that edit box (using a comma if necessary)
7. Press OK
8. Compile the entire project again...
Q. error LNK2001: unresolved external symbol _bacapp_print
A. The BACnet stack uses a preprocessor define to configure
printing to stdio. In Visual C++, add a Preprocessor Definition by:
1. Select "Project" menu
2. Select "Settings..."
3. Select the "C/C++" tab (3rd Tab)
4. Select the Category: General
5. You can see the "Preprocessor Definitions:" box
6. Type PRINT_ENABLED=1 in that edit box (using a comma if necessary)
7. Press OK
8. Compile the entire project again...
Q. error LNK2001: unresolved external symbol __imp__closesocket@4
A. Visual C++ needs to have the Winsock library to be happy:
1. Select "Project" menu
2. Select "Settings..."
3. Select the "Link" tab (4th Tab)
4. You can see "Object/library modules:" edit box
5. Type Wsock32.LIB in that edit box
6. Press OK
7. Compile the entire project again...
Q. error C2061: in file tsm.c
A. The BACnet stack uses a preprocessor define to configure
client functionality in the Transaction State Machine (TSM).
In Visual C++, add a Preprocessor Definition by:
1. Select "Project" menu
2. Select "Settings..."
3. Select the "C/C++" tab (3rd Tab)
4. Select the Category: General
5. You can see the "Preprocessor Definitions:" box
6. Type TSM_ENABLED=1 in that edit box (using a comma if necessary)
7. Press OK
8. Compile the entire project again...
+212
View File
@@ -0,0 +1,212 @@
/*####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####*/
#define WIN32_LEAN_AND_MEAN
#define STRICT 1
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include "bacdcode.h"
#include "bip.h"
#include "net.h"
/* To fill a need, we invent the gethostaddr() function. */
static long gethostaddr(void)
{
struct hostent *host_ent;
char host_name[255];
if (gethostname(host_name, sizeof(host_name)) != 0)
return -1;
#ifdef BIP_DEBUG
printf("host name: %s\n", host_name);
#endif
if ((host_ent = gethostbyname(host_name)) == NULL)
return -1;
return *(long *) host_ent->h_addr;
}
static void set_broadcast_address(uint32_t net_address)
{
long broadcast_address = 0;
long mask = 0;
/* Note: sometimes INADDR_BROADCAST does not let me get
any unicast messages. Not sure why... */
#if USE_INADDR
(void) net_address;
bip_set_broadcast_addr(INADDR_BROADCAST);
#else
if (IN_CLASSA(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSA_HOST) | IN_CLASSA_HOST;
else if (IN_CLASSB(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSB_HOST) | IN_CLASSB_HOST;
else if (IN_CLASSC(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSC_HOST) | IN_CLASSC_HOST;
else if (IN_CLASSD(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSD_HOST) | IN_CLASSD_HOST;
else
broadcast_address = INADDR_BROADCAST;
bip_set_broadcast_addr(htonl(broadcast_address));
#endif
}
static void cleanup(void)
{
WSACleanup();
}
void bip_set_interface(char *ifname)
{
(void) ifname;
/* dummy function */
}
bool bip_init(void)
{
int rv = 0; /* return from socket lib calls */
struct sockaddr_in sin = { -1 };
int value = 1;
int sock_fd = -1;
int Result;
int Code;
WSADATA wd;
struct in_addr address;
Result = WSAStartup((1 << 8) | 1, &wd);
/*Result = WSAStartup(MAKEWORD(2,2), &wd); */
if (Result != 0) {
Code = WSAGetLastError();
printf("TCP/IP stack initialization failed, error code: %i\n",
Code);
exit(1);
}
atexit(cleanup);
address.s_addr = gethostaddr();
if (address.s_addr == (unsigned) -1) {
Code = WSAGetLastError();
printf("Get host address failed, error code: %i\n", Code);
exit(1);
}
#ifdef BIP_DEBUG
printf("host address: %s\n", inet_ntoa(address));
#endif
bip_set_addr(address.s_addr);
set_broadcast_address(address.s_addr);
/* assumes that the driver has already been initialized */
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
bip_set_socket(sock_fd);
if (sock_fd < 0) {
fprintf(stderr, "bip: failed to allocate a socket.\n");
return false;
}
/* Allow us to use the same socket for sending and receiving */
/* This makes sure that the src port is correct when sending */
rv = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &value, sizeof(value));
if (rv < 0) {
fprintf(stderr, "bip: failed to set REUSEADDR socket option.\n");
close(sock_fd);
bip_set_socket(-1);
return false;
}
/* allow us to send a broadcast */
rv = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST,
(char *) &value, sizeof(value));
if (rv < 0) {
fprintf(stderr, "bip: failed to set BROADCAST socket option.\n");
close(sock_fd);
bip_set_socket(-1);
return false;
}
#if 0
/* probably only for Apple... */
/* rebind a port that is already in use.
Note: all users of the port must specify this flag */
rv = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT,
(char *) &value, sizeof(value));
if (rv < 0) {
fprintf(stderr, "bip: failed to set REUSEPORT socket option.\n");
close(sock_fd);
bip_set_socket(-1);
return false;
}
#endif
/* bind the socket to the local port number and IP address */
sin.sin_family = AF_INET;
#if USE_INADDR
/* by setting sin.sin_addr.s_addr to INADDR_ANY,
I am telling the IP stack to automatically fill
in the IP address of the machine the process
is running on.
Some server computers have multiple IP addresses.
A socket bound to one of these will not accept
connections to another address. Frequently you prefer
to allow any one of the computer's IP addresses
to be used for connections. Use INADDR_ANY (0L) to
allow clients to connect using any one of the host's
IP addresses.
Note: sometimes INADDR_ANY does not let me get
any unicast messages. Not sure why... */
sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
/* or we could use the specific adapter address
note: already in network byte order */
sin.sin_addr.s_addr = address.s_addr;
#endif
sin.sin_port = htons(bip_get_port());
memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero));
rv = bind(sock_fd,
(const struct sockaddr *) &sin, sizeof(struct sockaddr));
if (rv < 0) {
fprintf(stderr, "bip: failed to bind to %s port %hd\n",
inet_ntoa(sin.sin_addr), bip_get_port());
close(sock_fd);
bip_set_socket(-1);
return false;
}
return true;
}
+455
View File
@@ -0,0 +1,455 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg, modified by Kevin Liao
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####*/
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include <assert.h>
#include <direct.h>
#include <io.h>
#include "bacdef.h"
#include "ethernet.h"
#include "bacdcode.h"
/* Uses WinPCap to access raw ethernet */
/* Notes: */
/* To make ethernet.c work under win32, you have to: */
/* 1. install winpcap 3.1 development pack; */
/* 2. install Microsoft Platform SDK Feb 2003. */
/* 3. remove or modify functions used for log such as */
/* "LogError()", "LogInfo()", which were implemented */
/* as a wrapper of Log4cpp. */
/* -- Kevin Liao */
/* includes for accessing ethernet by using winpcap */
#include "pcap.h"
#include "packet32.h"
#include "ntddndis.h"
#include "remote-ext.h"
/* commonly used comparison address for ethernet */
uint8_t Ethernet_Broadcast[MAX_MAC_LEN] =
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/* commonly used empty address for ethernet quick compare */
uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 };
/* my local device data - MAC address */
uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0 };
/* couple of var for using winpcap */
static char pcap_errbuf[PCAP_ERRBUF_SIZE + 1];
static pcap_t *pcap_eth802_fp = NULL; /* 802.2 file handle, from winpcap */
static unsigned eth_timeout = 100;
/* couple of external func for runtime error logging, you can simply */
/* replace them with standard "printf(...)" */
/* Logging extern functions: Info level */
extern void LogInfo(const char *msg);
/* Logging extern functions: Error level*/
extern void LogError(const char *msg);
/* Logging extern functions: Debug level*/
extern void LogDebug(const char *msg);
bool ethernet_valid(void)
{
return (pcap_eth802_fp != NULL);
}
void ethernet_cleanup(void)
{
if (pcap_eth802_fp) {
pcap_close(pcap_eth802_fp);
pcap_eth802_fp = NULL;
}
LogInfo("ethernet.c: ethernet_cleanup() ok.\n");
}
void ethernet_set_timeout(unsigned timeout)
{
eth_timeout = timeout;
}
/*----------------------------------------------------------------------
Portable function to set a socket into nonblocking mode.
Calling this on a socket causes all future read() and write() calls on
that socket to do only as much as they can immediately, and return
without waiting.
If no data can be read or written, they return -1 and set errno
to EAGAIN (or EWOULDBLOCK).
Thanks to Bjorn Reese for this code.
----------------------------------------------------------------------*/
/**
* We don't need to use this function since WinPCap has provided one
* named "pcap_setnonblock()".
* Kevin, 2006.08.15
*/
/*
int setNonblocking(int fd)
{
int flags;
if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
flags = 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
*/
bool ethernet_init(char *if_name)
{
PPACKET_OID_DATA pOidData;
LPADAPTER lpAdapter;
pcap_if_t *pcap_all_if;
pcap_if_t *dev;
BOOLEAN result;
CHAR str[sizeof(PACKET_OID_DATA) + 128];
int i;
char msgBuf[200];
if (ethernet_valid())
ethernet_cleanup();
/**
* Find the interface user specified
*/
/* Retrieve the device list */
if (pcap_findalldevs(&pcap_all_if, pcap_errbuf) == -1) {
sprintf(msgBuf,
"ethernet.c: error in pcap_findalldevs: %s\n", pcap_errbuf);
LogError(msgBuf);
return false;
}
/* Scan the list printing every entry */
for (dev = pcap_all_if; dev; dev = dev->next) {
if (strcmp(if_name, dev->name) == 0)
break;
}
pcap_freealldevs(pcap_all_if); /* we don't need it anymore */
if (dev == NULL) {
sprintf(msgBuf,
"ethernet.c: specified interface not found: %s\n", if_name);
LogError(msgBuf);
return false;
}
/**
* Get local MAC address
*/
ZeroMemory(str, sizeof(PACKET_OID_DATA) + 128);
lpAdapter = PacketOpenAdapter(if_name);
if (lpAdapter == NULL) {
ethernet_cleanup();
sprintf(msgBuf,
"ethernet.c: error in PacketOpenAdapter(\"%s\")\n", if_name);
LogError(msgBuf);
return false;
}
pOidData = (PPACKET_OID_DATA) str;
pOidData->Oid = OID_802_3_CURRENT_ADDRESS;
pOidData->Length = 6;
result = PacketRequest(lpAdapter, FALSE, pOidData);
if (!result) {
PacketCloseAdapter(lpAdapter);
ethernet_cleanup();
LogError("ethernet.c: error in PacketRequest()\n");
return false;
}
for (i = 0; i < 6; ++i)
Ethernet_MAC_Address[i] = pOidData->Data[i];
PacketCloseAdapter(lpAdapter);
/**
* Open interface for subsequent sending and receiving
*/
/* Open the output device */
pcap_eth802_fp = pcap_open(if_name, /* name of the device */
MAX_MPDU, /* portion of the packet to capture */
PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */
eth_timeout, /* read timeout */
NULL, /* authentication on the remote machine */
pcap_errbuf /* error buffer */
);
if (pcap_eth802_fp == NULL) {
PacketCloseAdapter(lpAdapter);
ethernet_cleanup();
sprintf(msgBuf,
"ethernet.c: unable to open the adapter. %s is not supported by WinPcap\n",
if_name);
LogError(msgBuf);
return false;
}
LogInfo("ethernet.c: ethernet_init() ok.\n");
atexit(ethernet_cleanup);
return ethernet_valid();
}
/* function to send a packet out the 802.2 socket */
/* returns bytes sent success, negative on failure */
int ethernet_send(BACNET_ADDRESS * dest, /* destination address */
BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* any data to be sent - may be null */
unsigned pdu_len /* number of bytes of data */
)
{
int bytes = 0;
uint8_t mtu[MAX_MPDU] = { 0 };
int mtu_len = 0;
int i = 0;
/* don't waste time if the socket is not valid */
if (!ethernet_valid()) {
LogError
("ethernet.c: invalid 802.2 ethernet interface descriptor!\n");
return -1;
}
/* load destination ethernet MAC address */
if (dest->mac_len == 6) {
for (i = 0; i < 6; i++) {
mtu[mtu_len] = dest->mac[i];
mtu_len++;
}
} else {
LogError("ethernet.c: invalid destination MAC address!\n");
return -2;
}
/* load source ethernet MAC address */
if (src->mac_len == 6) {
for (i = 0; i < 6; i++) {
mtu[mtu_len] = src->mac[i];
mtu_len++;
}
} else {
LogError("ethernet.c: invalid source MAC address!\n");
return -3;
}
if ((14 + 3 + pdu_len) > MAX_MPDU) {
LogError("ethernet.c: PDU is too big to send!\n");
return -4;
}
/* packet length */
mtu_len +=
encode_unsigned16(&mtu[12], 3 /*DSAP,SSAP,LLC */ + pdu_len);
/* Logical PDU portion */
mtu[mtu_len++] = 0x82; /* DSAP for BACnet */
mtu[mtu_len++] = 0x82; /* SSAP for BACnet */
mtu[mtu_len++] = 0x03; /* Control byte in header */
memcpy(&mtu[mtu_len], pdu, pdu_len);
mtu_len += pdu_len;
/* Send the packet */
if (pcap_sendpacket(pcap_eth802_fp, mtu, mtu_len) != 0) {
/* did it get sent? */
char msgBuf[200];
sprintf(msgBuf,
"ethernet.c: error sending packet: %s\n",
pcap_geterr(pcap_eth802_fp));
LogError(msgBuf);
return -5;
}
return mtu_len;
}
/* function to send a packet out the 802.2 socket */
/* returns number of bytes sent on success, negative on failure */
int ethernet_send_pdu(BACNET_ADDRESS * dest, /* destination address */
uint8_t * pdu, /* any data to be sent - may be null */
unsigned pdu_len /* number of bytes of data */
)
{
int i = 0; /* counter */
BACNET_ADDRESS src = { 0 }; /* source address */
for (i = 0; i < 6; i++) {
src.mac[i] = Ethernet_MAC_Address[i];
src.mac_len++;
}
/* function to send a packet out the 802.2 socket */
/* returns 1 on success, 0 on failure */
return ethernet_send(dest, /* destination address */
&src, /* source address */
pdu, /* any data to be sent - may be null */
pdu_len /* number of bytes of data */
);
}
/* receives an 802.2 framed packet */
/* returns the number of octets in the PDU, or zero on failure */
uint16_t ethernet_receive(BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t max_pdu, /* amount of space available in the PDU */
unsigned timeout /* number of milliseconds to wait for a packet. we ommit it due to winpcap API. */
)
{
struct pcap_pkthdr *header;
int res;
u_char *pkt_data;
uint16_t pdu_len = 0; /* return value */
/* Make sure the socket is open */
if (!ethernet_valid()) {
LogError
("ethernet.c: invalid 802.2 ethernet interface descriptor!\n");
return 0;
}
/* Capture a packet */
res = pcap_next_ex(pcap_eth802_fp, &header, &pkt_data);
if (res < 0) {
char msgBuf[200];
sprintf(msgBuf,
"ethernet.c: error in receiving packet: %s\n",
pcap_geterr(pcap_eth802_fp));
return 0;
} else if (res == 0)
return 0;
if (header->len == 0 || header->caplen == 0)
return 0;
/* the signature of an 802.2 BACnet packet */
if ((pkt_data[14] != 0x82) && (pkt_data[15] != 0x82)) {
/*eth_log_error("ethernet.c: Non-BACnet packet\n"); */
return 0;
}
/* copy the source address */
src->mac_len = 6;
memmove(src->mac, &pkt_data[6], 6);
/* check destination address for when */
/* the Ethernet card is in promiscious mode */
if ((memcmp(&pkt_data[0], Ethernet_MAC_Address, 6) != 0)
&& (memcmp(&pkt_data[0], Ethernet_Broadcast, 6) != 0)) {
/*eth_log_error( "ethernet.c: This packet isn't for us\n"); */
return 0;
}
(void) decode_unsigned16(&pkt_data[12], &pdu_len);
pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ;
/* copy the buffer into the PDU */
if (pdu_len < max_pdu)
memmove(&pdu[0], &pkt_data[17], pdu_len);
/* ignore packets that are too large */
else
pdu_len = 0;
return pdu_len;
}
void ethernet_set_my_address(BACNET_ADDRESS * my_address)
{
int i = 0;
for (i = 0; i < 6; i++) {
Ethernet_MAC_Address[i] = my_address->mac[i];
}
return;
}
void ethernet_get_my_address(BACNET_ADDRESS * my_address)
{
int i = 0;
my_address->mac_len = 0;
for (i = 0; i < 6; i++) {
my_address->mac[i] = Ethernet_MAC_Address[i];
my_address->mac_len++;
}
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 ethernet_get_broadcast_address(BACNET_ADDRESS * dest)
{ /* destination address */
int i = 0; /* counter */
if (dest) {
for (i = 0; i < 6; i++) {
dest->mac[i] = Ethernet_Broadcast[i];
}
dest->mac_len = 6;
dest->net = BACNET_BROADCAST_NETWORK;
dest->len = 0; /* denotes broadcast address */
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
return;
}
void ethernet_debug_address(const char *info, BACNET_ADDRESS * dest)
{
int i = 0; /* counter */
char msgBuf[200];
if (info) {
sprintf(msgBuf, "%s", info);
LogError(msgBuf);
}
/* if */
if (dest) {
sprintf(msgBuf,
"Address:\n MAC Length=%d\n MAC Address=", dest->mac_len);
LogInfo(msgBuf);
for (i = 0; i < MAX_MAC_LEN; i++) {
sprintf(msgBuf, "%02X ", (unsigned) dest->mac[i]);
LogInfo(msgBuf);
} /* for */
LogInfo("\n");
sprintf(msgBuf,
" Net=%hu\n Len=%d\n Adr=", dest->net, dest->len);
LogInfo(msgBuf);
for (i = 0; i < MAX_MAC_LEN; i++) {
sprintf(msgBuf, "%02X ", (unsigned) dest->adr[i]);
LogInfo(msgBuf);
} /* for */
LogInfo("\n");
}
/* if ( dest ) */
return;
}
+262
View File
@@ -0,0 +1,262 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
/* This is one way to use the embedded BACnet stack under Win32 */
/* compiled with Borland C++ 5.02 or Visual C++ 6.0 */
#include <winsock2.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <conio.h> /* for kbhit and getch */
#include "iam.h"
#include "address.h"
#include "config.h"
#include "bacdef.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "handlers.h"
#include "client.h"
#include "datalink.h"
#include "txbuf.h"
/* buffer used for receive */
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
/* send a whois to see who is on the network */
static bool Who_Is_Request = true;
bool I_Am_Request = true;
static void Read_Properties(void)
{
uint32_t device_id = 0;
bool status = false;
unsigned max_apdu = 0;
BACNET_ADDRESS src;
bool next_device = false;
static unsigned index = 0;
static unsigned property = 0;
/* list of required (and some optional) properties in the
Device Object
note: you could just loop through
all the properties in all the objects. */
const int object_props[] = {
PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME,
PROP_OBJECT_TYPE,
PROP_SYSTEM_STATUS,
PROP_VENDOR_NAME,
PROP_VENDOR_IDENTIFIER,
PROP_MODEL_NAME,
PROP_FIRMWARE_REVISION,
PROP_APPLICATION_SOFTWARE_VERSION,
PROP_PROTOCOL_VERSION,
PROP_PROTOCOL_CONFORMANCE_CLASS,
PROP_PROTOCOL_SERVICES_SUPPORTED,
PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED,
PROP_MAX_APDU_LENGTH_ACCEPTED,
PROP_SEGMENTATION_SUPPORTED,
PROP_LOCAL_TIME,
PROP_LOCAL_DATE,
PROP_UTC_OFFSET,
PROP_DAYLIGHT_SAVINGS_STATUS,
PROP_APDU_SEGMENT_TIMEOUT,
PROP_APDU_TIMEOUT,
PROP_NUMBER_OF_APDU_RETRIES,
PROP_TIME_SYNCHRONIZATION_RECIPIENTS,
PROP_MAX_MASTER,
PROP_MAX_INFO_FRAMES,
PROP_DEVICE_ADDRESS_BINDING,
/* note: PROP_OBJECT_LIST is missing cause
we need to get it with an index method since
the list could be very large */
/* some proprietary properties */
514, 515,
/* end of list */
-1
};
if (address_count()) {
if (address_get_by_index(index, &device_id, &max_apdu, &src)) {
if (object_props[property] < 0)
next_device = true;
else {
status = Send_Read_Property_Request(device_id, /* destination device */
OBJECT_DEVICE,
device_id, object_props[property], BACNET_ARRAY_ALL);
if (status)
property++;
}
} else
next_device = true;
if (next_device) {
next_device = false;
index++;
if (index >= MAX_ADDRESS_CACHE)
index = 0;
property = 0;
}
}
return;
}
static void LocalIAmHandler(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src)
{
int len = 0;
uint32_t device_id = 0;
unsigned max_apdu = 0;
int segmentation = 0;
uint16_t vendor_id = 0;
(void) src;
(void) service_len;
len = iam_decode_service_request(service_request,
&device_id, &max_apdu, &segmentation, &vendor_id);
fprintf(stderr, "Received I-Am Request");
if (len != -1) {
fprintf(stderr, " from %u!\n", device_id);
address_add(device_id, max_apdu, src);
} else
fprintf(stderr, "!\n");
return;
}
static void Init_Service_Handlers(void)
{
/* we need to handle who-is to support dynamic device binding */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
handler_who_is);
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM,
LocalIAmHandler);
/* set the handler for all the services we don't implement */
/* It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler
(handler_unrecognized_service);
/* we must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
handler_write_property);
/* handle the data coming back from confirmed requests */
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property_ack);
}
static void print_address(char *name, BACNET_ADDRESS * dest)
{ /* destination address */
int i = 0; /* counter */
if (dest) {
printf("%s: ", name);
for (i = 0; i < dest->mac_len; i++) {
printf("%02X", dest->mac[i]);
}
printf("\n");
}
}
static void print_address_cache(void)
{
int i, j;
BACNET_ADDRESS address;
uint32_t device_id = 0;
unsigned max_apdu = 0;
fprintf(stderr, "Device\tMAC\tMaxAPDU\tNet\n");
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (address_get_by_index(i, &device_id, &max_apdu, &address)) {
fprintf(stderr, "%u\t", device_id);
for (j = 0; j < address.mac_len; j++) {
fprintf(stderr, "%02X", address.mac[j]);
}
fprintf(stderr, "\t");
fprintf(stderr, "%hu\t", max_apdu);
fprintf(stderr, "%hu\n", address.net);
}
}
}
int main(int argc, char *argv[])
{
BACNET_ADDRESS src = { 0 }; /* address where message came from */
uint16_t pdu_len = 0;
unsigned timeout = 100; /* milliseconds */
BACNET_ADDRESS my_address, broadcast_address;
(void) argc;
(void) argv;
Device_Set_Object_Instance_Number(124);
Init_Service_Handlers();
/* init the data link layer */
/* configure standard BACnet/IP port */
bip_set_port(0xBAC0);
if (!bip_init())
return 1;
datalink_get_broadcast_address(&broadcast_address);
print_address("Broadcast", &broadcast_address);
datalink_get_my_address(&my_address);
print_address("Address", &my_address);
printf("BACnet stack running...\n");
/* loop forever */
for (;;) {
/* input */
/* returns 0 bytes on timeout */
pdu_len = bip_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
/* process */
if (pdu_len) {
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
if (I_Am_Request) {
I_Am_Request = false;
iam_send(&Handler_Transmit_Buffer[0]);
} else if (Who_Is_Request) {
Who_Is_Request = false;
Send_WhoIs(-1, -1);
} else {
Read_Properties();
}
/* output */
/* blink LEDs, Turn on or off outputs, etc */
/* wait for ESC from keyboard before quitting */
if (kbhit() && (getch() == 0x1B))
break;
}
print_address_cache();
return 0;
}
+38
View File
@@ -0,0 +1,38 @@
/**************************************************************************
*
* Copyright (C) 2005 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.
*
*********************************************************************/
#ifndef NET_H
#define NET_H
#define WIN32_LEAN_AND_MEAN
#define STRICT 1
#include <winsock2.h>
#define close closesocket
typedef int socklen_t;
#endif
+15
View File
@@ -0,0 +1,15 @@
BACnet Stack - Win32
This directory contains a demo program that compiles with a Win32 compiler.
It was tested with the freely downloadable Borland C++ 5.5, as well as
Borland C++ 5 and Visual C++ 6.0.
The makefile.mak file is used with the Borland command line tools.
Run setvars.bat to configure the environment for the Borland tools.
Edit it if necessary to set the correct location of your tools.
The bacnet.ide file is used with the Borland IDE.
The bacnet directory is used with Visual C++ 6 tools, and there is a
workspace file bacnet.dsw that is used to compile the demo program.
@@ -0,0 +1,2 @@
set BORLAND_DIR=\bcc55
+28
View File
@@ -0,0 +1,28 @@
#ifndef STDBOOL_H
#define STDBOOL_H
/* C99 Boolean types for compilers without C99 support */
#ifndef __cplusplus
typedef int _Bool;
#ifndef bool
#define bool _Bool
#endif
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#define __bool_true_false_are_defined 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#endif
+19
View File
@@ -0,0 +1,19 @@
/* Defines the standard integer types that are used in code */
/* for the x86 processor and Borland Compiler */
#ifndef STDINT_H
#define STDINT_H
#include <stddef.h>
typedef unsigned char uint8_t; /* 1 byte 0 to 255 */
typedef signed char int8_t; /* 1 byte -127 to 127 */
typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */
typedef signed short int16_t; /* 2 bytes -32767 to 32767 */
/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */
typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */
typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */
/* typedef signed long long int64_t; */
/* typedef unsigned long long uint64_t; */
#endif /* STDINT_H */