Files
bacnet_stack/ports/pico/rs485.c
T
Testimony 17e94092a1 Add Raspberry Pi Pico port (#1232)
* Add Raspberry Pi Pico port
- Adds BACnet/IP with abstracted network layer
- Adds BACnet MS/TP support using RS485/UART
- Includes README with examples

* Add Pico 2 MS/TP demo, CI, and docs
2026-03-25 08:22:09 -05:00

230 lines
6.0 KiB
C

/**************************************************************************
*
* Copyright (C) 2025 Testimony Adams <adamstestimony@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*
*********************************************************************/
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/basic/sys/mstimer.h"
#include "bacnet/datalink/dlmstp.h"
#include "bacnet/basic/sys/fifo.h"
#include "rs485.h"
#include "mstimer_init.h"
#include <stdio.h>
// --- Static State Variables ---
static bool Rs485_RTS_Enabled = false;
static uint32_t Rs485_Baud_Rate = RS485_BAUD_RATE;
static volatile uint32_t Rs485_Bytes_Tx = 0;
static volatile uint32_t Rs485_Bytes_Rx = 0;
/* amount of silence on the wire */
static struct mstimer Silence_Timer;
/**
* @brief Initialize the RS-485 UART and GPIO pins.
*/
void rs485_init(void)
{
// 1. Initialize GPIO pins
gpio_set_function(RS485_TX_PIN, GPIO_FUNC_UART);
gpio_set_function(RS485_RX_PIN, GPIO_FUNC_UART);
// Initialize DE/RE pin (RTS) as an output
gpio_init(RS485_DE_PIN);
gpio_set_dir(RS485_DE_PIN, GPIO_OUT);
rs485_rts_enable(false); // Set to receive (DE/RE low)
// 2. Initialize UART
uart_init(RS485_UART_ID, Rs485_Baud_Rate);
uart_set_format(RS485_UART_ID, DATA_BIT, STOP_BIT, UART_PARITY_NONE);
uart_set_hw_flow(RS485_UART_ID, false, false);
uart_set_fifo_enabled(RS485_UART_ID, false);
// 3. Reset silence timer
rs485_silence_reset();
// 4. Flush any junk data in the RX buffer from startup
while (uart_is_readable(RS485_UART_ID)) {
uart_getc(RS485_UART_ID);
}
}
/**
* @brief Enables or disables the Request To Send (RTS) / Driver Enable (DE/RE)
* pin.
* @param enable True to enable transmitter (TX), False to enable receiver (RX).
*/
void rs485_rts_enable(bool enable)
{
// DE=1: Driver Enable (TX mode)
// DE=0: Receiver Enable (RX mode)
gpio_put(RS485_DE_PIN, enable ? 1 : 0);
Rs485_RTS_Enabled = enable;
}
/**
* @brief Returns the current state of the RTS/DE/RE pin.
* @return True if transmitter is enabled, False if receiver is enabled.
*/
bool rs485_rts_enabled(void)
{
return Rs485_RTS_Enabled;
}
/**
* @brief Attempts to read a single byte from the UART.
* @param data_register Pointer to store the received byte.
* @return True if a byte was received, False otherwise.
*/
bool rs485_byte_available(uint8_t *data_register)
{
if (!uart_is_readable(RS485_UART_ID)) {
return false;
}
if (!data_register) {
// only checking availability — DO NOT CONSUME
// Edit to consume if needed
return true;
}
*data_register = uart_getc(RS485_UART_ID);
Rs485_Bytes_Rx++;
rs485_silence_reset();
return true;
}
/**
* @brief Checks the UART hardware for a receive error (Framing, Parity,
* Overrun).
* @return True if a receive error is present, False otherwise.
*/
bool rs485_receive_error(void)
{
// The RSR (Receive Status Register) holds error flags.
// It is part of the raw hardware registers, accessed via uart_get_hw().
uart_hw_t *uart_hw = uart_get_hw(RS485_UART_ID);
// RSR bits: 0: FE (Framing Error), 1: PE (Parity Error), 2: BE (Break
// Error), 3: OE (Overrun Error)
uint32_t rsr = uart_hw->rsr;
// Clearing the RSR register is crucial after reading errors.
// Writing anything to it clears all error flags.
uart_hw->rsr = 0;
// Check if any error bit is set
return (rsr != 0);
}
/**
* @brief Sends a buffer of bytes over the UART.
* @param buffer Pointer to the data buffer.
* @param nbytes Number of bytes to send.
*/
void rs485_bytes_send(const uint8_t *buffer, uint16_t nbytes)
{
// The BACnet stack will call rs485_rts_enable(true) before this function,
// and wait T_prop before sending.
// printf("TX %u bytes: ", nbytes);
// for (uint16_t i = 0; i < nbytes; i++) {
// printf("%02X ", buffer[i]);
// }
// printf("\r\n");
rs485_rts_enable(true);
// TO BE IMPROVED (Implement custom_sleep)
sleep_us(200);
uart_write_blocking(RS485_UART_ID, buffer, nbytes);
uart_tx_wait_blocking(RS485_UART_ID);
sleep_us(200);
// Update count and silence timer.
Rs485_Bytes_Tx += nbytes;
rs485_rts_enable(false);
rs485_silence_reset();
// The BACnet stack handles the post-transmit delay (T_prop + T_frame_gap)
// and calls rs485_rts_enable(false) to turn off the driver.
}
/**
* @brief Returns the currently configured baud rate.
* @return The current baud rate.
*/
uint32_t rs485_baud_rate(void)
{
return Rs485_Baud_Rate;
}
/**
* @brief Sets a new baud rate for the UART.
* @param baud The new baud rate to set.
* @return True if the baud rate was successfully set, False otherwise.
*/
bool rs485_baud_rate_set(uint32_t baud)
{
if (baud == 0) {
return false;
}
// uart_set_baudrate returns the actual configured rate, which may be
// slightly off. We only care that the function executed successfully and we
// store the requested rate.
switch (baud) {
case 9600:
case 19200:
case 38400:
case 57600:
case 76800:
case 115200:
Rs485_Baud_Rate = baud;
uart_set_baudrate(RS485_UART_ID, Rs485_Baud_Rate);
break;
default:
return false;
break;
}
return true;
}
/**
* @brief Measures the duration of silence on the bus since the last byte (Tx or
* Rx).
* @return The duration of silence in milliseconds.
*/
uint32_t rs485_silence_milliseconds(void)
{
return mstimer_elapsed(&Silence_Timer);
}
/**
* @brief Resets the silence timer to the current time.
*/
void rs485_silence_reset(void)
{
mstimer_set(&Silence_Timer, 0);
}
/**
* @brief Gets the total number of bytes transmitted.
* @return The byte count.
*/
uint32_t rs485_bytes_transmitted(void)
{
return Rs485_Bytes_Tx;
}
/**
* @brief Gets the total number of bytes received.
* @return The byte count.
*/
uint32_t rs485_bytes_received(void)
{
return Rs485_Bytes_Rx;
}