185 lines
4.4 KiB
C
185 lines
4.4 KiB
C
/**
|
|
* @file
|
|
* @brief RS485 UART driver used by BACnet MS/TP on ESP32
|
|
* @author Kato Gangstad
|
|
*/
|
|
|
|
#include "rs485.h"
|
|
|
|
#include <driver/gpio.h>
|
|
#include <driver/uart.h>
|
|
|
|
/* UART_SCLK_DEFAULT was introduced in ESP-IDF v5.0.
|
|
Fall back to UART_SCLK_APB for older IDF/Arduino-ESP32 toolchains. */
|
|
#ifndef UART_SCLK_DEFAULT
|
|
#define UART_SCLK_DEFAULT UART_SCLK_APB
|
|
#endif
|
|
|
|
#include "bacnet/basic/sys/mstimer.h"
|
|
|
|
static bool Rs485_RTS_Enabled;
|
|
static uint32_t Rs485_Baud_Rate = RS485_BAUD_RATE;
|
|
static volatile uint32_t Rs485_Bytes_Tx;
|
|
static volatile uint32_t Rs485_Bytes_Rx;
|
|
static struct mstimer Silence_Timer;
|
|
|
|
/**
|
|
* @brief Initialize the RS485 UART peripheral for BACnet MS/TP
|
|
*/
|
|
void rs485_init(void)
|
|
{
|
|
uart_config_t cfg = { .baud_rate = (int)Rs485_Baud_Rate,
|
|
.data_bits = UART_DATA_8_BITS,
|
|
.parity = UART_PARITY_DISABLE,
|
|
.stop_bits = UART_STOP_BITS_1,
|
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
|
.source_clk = UART_SCLK_DEFAULT };
|
|
|
|
(void)uart_driver_delete(RS485_UART_NUM);
|
|
(void)uart_param_config(RS485_UART_NUM, &cfg);
|
|
(void)uart_set_pin(
|
|
RS485_UART_NUM, RS485_TX_PIN, RS485_RX_PIN, RS485_DIR_PIN,
|
|
UART_PIN_NO_CHANGE);
|
|
(void)uart_driver_install(RS485_UART_NUM, 1024, 0, 0, NULL, 0);
|
|
(void)uart_set_mode(RS485_UART_NUM, UART_MODE_RS485_HALF_DUPLEX);
|
|
|
|
Rs485_RTS_Enabled = false;
|
|
Rs485_Bytes_Tx = 0;
|
|
Rs485_Bytes_Rx = 0;
|
|
rs485_silence_reset();
|
|
}
|
|
|
|
/**
|
|
* @brief Update the cached RS485 transmit-enable state
|
|
* @param enable true when transmit is active
|
|
*/
|
|
void rs485_rts_enable(bool enable)
|
|
{
|
|
/* Hardware RS485 half-duplex mode drives DIR. Keep state for stack queries.
|
|
*/
|
|
Rs485_RTS_Enabled = enable;
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether the RS485 transmit-enable state is active
|
|
* @return true if transmit is active
|
|
*/
|
|
bool rs485_rts_enabled(void)
|
|
{
|
|
return Rs485_RTS_Enabled;
|
|
}
|
|
|
|
/**
|
|
* @brief Read one byte from the RS485 receive FIFO when available
|
|
* @param data_register destination for the received byte, or NULL to probe only
|
|
* @return true if at least one byte is available
|
|
*/
|
|
bool rs485_byte_available(uint8_t *data_register)
|
|
{
|
|
size_t buffered = 0;
|
|
(void)uart_get_buffered_data_len(RS485_UART_NUM, &buffered);
|
|
if (buffered == 0U) {
|
|
return false;
|
|
}
|
|
if (!data_register) {
|
|
return true;
|
|
}
|
|
|
|
if (uart_read_bytes(RS485_UART_NUM, data_register, 1, 0) == 1) {
|
|
Rs485_Bytes_Rx++;
|
|
rs485_silence_reset();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Report whether an RS485 receive error is pending
|
|
* @return true if a receive error is detected
|
|
*/
|
|
bool rs485_receive_error(void)
|
|
{
|
|
/* In Arduino-ESP32 builds, low-level UART error queues are not always
|
|
* exposed. */
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Send a block of RS485 bytes
|
|
* @param buffer transmit buffer
|
|
* @param nbytes number of bytes to send
|
|
*/
|
|
void rs485_bytes_send(const uint8_t *buffer, uint16_t nbytes)
|
|
{
|
|
if (!buffer || (nbytes == 0U)) {
|
|
return;
|
|
}
|
|
|
|
rs485_rts_enable(true);
|
|
(void)uart_write_bytes(RS485_UART_NUM, (const char *)buffer, nbytes);
|
|
(void)uart_wait_tx_done(RS485_UART_NUM, pdMS_TO_TICKS(20));
|
|
Rs485_Bytes_Tx += nbytes;
|
|
rs485_rts_enable(false);
|
|
rs485_silence_reset();
|
|
}
|
|
|
|
/**
|
|
* @brief Get the configured RS485 baud rate
|
|
* @return baud rate in bits per second
|
|
*/
|
|
uint32_t rs485_baud_rate(void)
|
|
{
|
|
return Rs485_Baud_Rate;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the RS485 baud rate
|
|
* @param baud baud rate in bits per second
|
|
* @return true if the baud rate was applied
|
|
*/
|
|
bool rs485_baud_rate_set(uint32_t baud)
|
|
{
|
|
if (baud == 0U) {
|
|
return false;
|
|
}
|
|
|
|
Rs485_Baud_Rate = baud;
|
|
return (uart_set_baudrate(RS485_UART_NUM, (int)Rs485_Baud_Rate) == ESP_OK);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the elapsed silent time on the RS485 bus
|
|
* @return milliseconds since the last bus activity
|
|
*/
|
|
uint32_t rs485_silence_milliseconds(void)
|
|
{
|
|
return mstimer_elapsed(&Silence_Timer);
|
|
}
|
|
|
|
/**
|
|
* @brief Reset the RS485 silence timer
|
|
*/
|
|
void rs485_silence_reset(void)
|
|
{
|
|
mstimer_set(&Silence_Timer, 0);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the number of transmitted RS485 bytes
|
|
* @return transmitted byte count
|
|
*/
|
|
uint32_t rs485_bytes_transmitted(void)
|
|
{
|
|
return Rs485_Bytes_Tx;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the number of received RS485 bytes
|
|
* @return received byte count
|
|
*/
|
|
uint32_t rs485_bytes_received(void)
|
|
{
|
|
return Rs485_Bytes_Rx;
|
|
}
|