cb243c36a8
* Change MIT license texts to SPDX-License-Identifier SPDX-License-Identifier is much easier to understand and grep than license text so use that instead. * Change GPL exception license texts to SPDX-License-Identifier SPDX-License-Identifier is much easier to understand and grep than license text so use that instead. * Change misc license texts to SPDX-License-Identifier There are some external code in repo which are not licenses as most of the stuff in this repo. We still want every file to have SPDX identifier to easily grep licenses. * Add currently used license files Even though Bacnet-Stack is using SPDX identifiers we still need to give those license files with source. For this reason add all license files to license/ folder. SPDX has also files which would make same thing but this is style which example Linux kernel is using and it is quite clear so I choose that one for now. I choosed not yet bring CC-PDDC as that is not right license for those files. --------- Co-authored-by: Kari Argillander <kari.argillander@fidelix.com>
328 lines
9.4 KiB
C
328 lines
9.4 KiB
C
/**************************************************************************
|
|
*
|
|
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
*********************************************************************/
|
|
|
|
/* 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 "bacnet/datalink/mstp.h"
|
|
#include "rs485.h"
|
|
#include "bacnet/basic/sys/fifo.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 FIFO structures for sending and receiving */
|
|
FIFO_BUFFER FIFO_Rx;
|
|
FIFO_BUFFER FIFO_Tx;
|
|
#pragma udata MSTPPortData
|
|
/* the buffer for receiving data (size must be a power of 2) */
|
|
volatile uint8_t RS485_Rx_Buffer[NEXT_POWER_OF_2(DLMSTP_MPDU_MAX)];
|
|
/* the buffer for sending data (size must be a power of 2) */
|
|
volatile uint8_t RS485_Tx_Buffer[NEXT_POWER_OF_2(DLMSTP_MPDU_MAX)];
|
|
#pragma udata
|
|
|
|
/****************************************************************************
|
|
* 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;
|
|
|
|
while (!FIFO_Empty(&FIFO_Tx)) {
|
|
/* buffer is not empty. Wait for ISR to transmit. */
|
|
};
|
|
|
|
/* 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 = 2;
|
|
|
|
while (mstp_port->SilenceTimer < turnaround_time) {
|
|
/* The line has not been silent long enough, so wait. */
|
|
};
|
|
|
|
if (FIFO_Add(&FIFO_Tx, buffer, 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;
|
|
/* reset the silence timer 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
|
|
*****************************************************************************/
|
|
bool RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
/* check for data */
|
|
if (!FIFO_Empty(&FIFO_Rx)) {
|
|
mstp_port->DataRegister = FIFO_Get(&FIFO_Rx);
|
|
mstp_port->DataAvailable = TRUE;
|
|
}
|
|
|
|
return (!FIFO_Empty(&FIFO_Rx));
|
|
}
|
|
|
|
/* *************************************************************************
|
|
DESCRIPTION: Receives RS485 data stream
|
|
|
|
RETURN: none
|
|
|
|
ALGORITHM: none
|
|
|
|
NOTES: none
|
|
*************************************************************************** */
|
|
void RS485_Interrupt_Rx(void)
|
|
{
|
|
uint8_t data_byte;
|
|
|
|
if ((RCSTA2bits.FERR) || (RCSTA2bits.OERR)) {
|
|
/* Clear the error */
|
|
RCSTA2bits.CREN = 0;
|
|
RCSTA2bits.CREN = 1;
|
|
/* FIXME: flag the MS/TP state machine on buffer overrun */
|
|
data_byte = RCREG2;
|
|
} else {
|
|
data_byte = RCREG2;
|
|
FIFO_Put(&FIFO_Rx, data_byte);
|
|
}
|
|
}
|
|
|
|
/* *************************************************************************
|
|
DESCRIPTION: Transmits a byte using the UART out the RS485 port
|
|
|
|
RETURN: none
|
|
|
|
ALGORITHM: none
|
|
|
|
NOTES: none
|
|
*************************************************************************** */
|
|
void RS485_Interrupt_Tx(void)
|
|
{
|
|
if (!FIFO_Empty(&FIFO_Tx)) {
|
|
TXREG2 = FIFO_Get(&FIFO_Tx);
|
|
} 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;
|
|
/* enable the this interrupt */
|
|
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 */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* DESCRIPTION: Reinitializes the port
|
|
* RETURN: none
|
|
* ALGORITHM: none
|
|
* NOTES: none
|
|
*****************************************************************************/
|
|
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 */
|
|
FIFO_Init(&FIFO_Rx, RS485_Rx_Buffer, sizeof(RS485_Rx_Buffer));
|
|
FIFO_Init(&FIFO_Tx, RS485_Tx_Buffer, sizeof(RS485_Tx_Buffer));
|
|
|
|
/* FIXME: read the stored baud rate */
|
|
/* I2C_Read_Block(
|
|
EEPROM_DEVICE_ADDRESS,
|
|
(char *)&RS485_Baud_Rate,
|
|
sizeof(RS485_Baud_Rate),
|
|
EEPROM_MSTP_BAUD_RATE_ADDR); */
|
|
|
|
RS485_Initialize_Port();
|
|
}
|