RS485Driver/RTApp/main.c (234 lines of code) (raw):
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "lib/mt3620/gpt.h"
#include "lib/GPT.h"
#include "lib/CPUFreq.h"
#include "lib/VectorTable.h"
#include "lib/NVIC.h"
#include "lib/Print.h"
#include "../common_defs.h"
#include "Socket.h"
#include "ringBuffer.h"
#include "rs485_driver.h"
#define DEBUG_INFO
static UART *debug = NULL;
static Socket *socket = NULL;
static GPT *sendTimer = NULL;
static const Component_Id A7ID =
{
.seg_0 = 0x96ACA524,
.seg_1 = 0x8113,
.seg_2 = 0x4171,
.seg_3_4 = {0x9C, 0x76, 0x6F, 0xBD, 0xBB, 0x44, 0x11, 0x31}
};
typedef struct CallbackNode {
bool enqueued;
struct CallbackNode *next;
void *data;
void (*cb_void)(void);
void (*cb_void_ptr)(void *);
} CallbackNode;
static CallbackNode *volatile callbacks = NULL;
// Function prototypes
static void HandleUartRxIrq(void);
// Callback handlers
static void EnqueueCallback(CallbackNode *node)
{
uint32_t prevBasePri = NVIC_BlockIRQs();
if (!node->enqueued) {
CallbackNode *prevHead = callbacks;
node->enqueued = true;
callbacks = node;
node->next = prevHead;
}
NVIC_RestoreIRQs(prevBasePri);
}
static void InvokeCallbacks(void)
{
CallbackNode *node = NULL;
do {
uint32_t prevBasePri = NVIC_BlockIRQs();
node = callbacks;
if (node) {
node->enqueued = false;
callbacks = node->next;
}
NVIC_RestoreIRQs(prevBasePri);
if (node) {
if (NULL != node->cb_void)
{
(*node->cb_void)();
}
else if (NULL != node->cb_void_ptr)
{
(*node->cb_void_ptr)(node->data);
}
}
} while (node);
}
// Handlers for messages received from the HLApp
static void handleRecvMsg(void *handle)
{
Socket *socket = (Socket *)handle;
Component_Id senderId;
static uint8_t msg[MAX_HLAPP_MESSAGE_SIZE];
if (Socket_NegotiationPending(socket)) {
UART_Printf(debug, "Negotiation pending, attempting renegotiation\n");
// NB: this is blocking, if you want to protect against hanging, add a timeout!
if (Socket_Negotiate(socket) != ERROR_NONE) {
UART_Printf(debug, "ERROR: renegotiating socket connection\n");
}
}
// Read the message from the HLApp mailbox socket
uint32_t bytesRead = sizeof(msg);
int32_t error = Socket_Read(socket, &senderId, &msg, &bytesRead);
if (error != ERROR_NONE) {
UART_Printf(debug, "ERROR: receiving message from HLApp - %ld\r\n", error);
}
else if (bytesRead > sizeof(msg)) {
UART_Printf(debug, "ERROR: message from HLApp too long - %ld\r\n", bytesRead);
}
// Is this a special command?
if (*((uint32_t *)&msg[0]) == 0xffffffffUL)
{
bool bRes = Rs485_Init(*((uint16_t *)&msg[4]), NULL);
if (bRes)
{
memcpy(msg, "\xff\xff\xff\xff\x00\x00\x00\x00", 8);
}
else
{
memcpy(msg, "\xff\xff\xff\xff\xff\xff\xff\xff", 8);
}
#ifdef DEBUG_INFO
UART_Printf(debug, "Changing baud rate to %d --> %s\r\n", *((uint16_t *)&msg[4]), bRes ? "OK" : "FAILED!!");
#endif
if (ring_buffer_push_bytes(&rs485_rxRingBuffer, msg, 8) == -1)
{
UART_Print(debug, "Message to HLApp LOST (rs485_rxRingBuffer overflow)!! ");
}
}
else
{
#ifdef DEBUG_INFO
UART_Printf(debug, "Received %ld bytes from HLApp: ", bytesRead);
for (uint32_t i = 0; i < bytesRead; ++i) {
UART_Printf(debug, "%02x", msg[i]);
if (i != bytesRead - 1) {
UART_Print(debug, ":");
}
}
UART_Print(debug, " --> sending to RS-485 field bus\r\n");
#endif
if ((error = Rs485_Write(msg, bytesRead)) != ERROR_NONE)
{
UART_Printf(debug, "Message from HLApp LOST (error: %ld)!!\r\n", error);
}
}
}
static void handleRecvMsgWrapper(Socket *handle)
{
static CallbackNode cbn = { .enqueued = false, .cb_void = NULL, .cb_void_ptr = handleRecvMsg, .data = NULL };
if (!cbn.data) {
cbn.data = handle;
}
EnqueueCallback(&cbn);
}
// Handler for messages to be sent to the HLApp
static void handleSendMsgTimer(void *data)
{
// Dequeue the bytes to be sent to the HLApp
int bytesBuffered = ring_buffer_count(&rs485_rxRingBuffer);
uint8_t buffer[bytesBuffered];
int32_t error;
if ((error = ring_buffer_pop_bytes(&rs485_rxRingBuffer, buffer, bytesBuffered)) == -1)
{
UART_Printf(debug, "Message from HLApp LOST (error: %ld)!!\r\n", error);
}
else
{
#ifdef DEBUG_INFO
UART_Printf(debug, "Sending %d bytes to HLApp: ", bytesBuffered);
for (uint32_t i = 0; i < bytesBuffered; ++i) {
UART_Printf(debug, "%02x", buffer[i]);
if (i != bytesBuffered - 1) {
UART_Print(debug, ":");
}
}
UART_Print(debug, "\r\n");
#endif
error = Socket_Write(socket, &A7ID, buffer, bytesBuffered);
if (error != ERROR_NONE) {
UART_Printf(debug, "ERROR: sending message - %ld\r\n", error);
}
}
Socket_Reset(socket); // Simulate reboot
}
static void handleSendMsgTimerWrapper(GPT *timer)
{
if (NULL != timer)
(void)(timer);
static CallbackNode cbn = { .enqueued = false, .cb_void = NULL, .cb_void_ptr = handleSendMsgTimer, .data = NULL };
EnqueueCallback(&cbn);
}
// IRQ Handlers for the RS-485 UART
static void HandleUartRxIrqDeferred(void)
{
uintptr_t avail = Rs485_ReadAvailable();
if (avail == 0) {
UART_Print(debug, "ERROR: UART received interrupt for zero bytes.\r\n");
return;
}
uint8_t buffer[avail];
if (Rs485_Read(buffer, avail) != ERROR_NONE) {
UART_Printf(debug, "ERROR: Failed to read %zu bytes from UART.\r\n", avail);
return;
}
#ifdef DEBUG_INFO
UART_Printf(debug, "Received %zu bytes from RS-485 bus: ", avail);
for (uint32_t i = 0; i < avail; ++i) {
UART_Printf(debug, "%02x", buffer[i]);
if (i != avail - 1) {
UART_Print(debug, ":");
}
}
UART_Printf(debug, "\r\n");
#endif
// If the RX buffer overflows the desired limit, immediately send the bytes to the HLApp
// so to lower chances of losing bytes from the serial port in between GPT interrupts.
if (ring_buffer_count(&rs485_rxRingBuffer) > DRIVER_MAX_RX_BUFFER_FILL_SIZE)
{
handleSendMsgTimerWrapper(NULL);
}
// Enqueue the received bytes in the ring buffer, to be sent to the HLApp upon GPT interrupts.
if (ring_buffer_push_bytes(&rs485_rxRingBuffer, buffer, avail) == -1)
{
UART_Print(debug, "Message from UART LOST (rs485_rxRingBuffer overflow)!! ");
}
}
static void HandleUartRxIrq(void) {
static CallbackNode cbn = { .enqueued = false, .cb_void = HandleUartRxIrqDeferred, .cb_void_ptr = NULL, .data = NULL };
EnqueueCallback(&cbn);
}
_Noreturn void RTCoreMain(void)
{
VectorTableInit();
//CPUFreq_Set(26000000);
// Initialize the debug UART
debug = UART_Open(MT3620_UNIT_UART_DEBUG, 115200, UART_PARITY_NONE, 1, NULL);
UART_Print(debug, "RS-485 real-time driver\r\n");
UART_Print(debug, "Built on: " __DATE__ " " __TIME__ "\r\n");
// Initialize the RS-485 driver
Rs485_Init(9600, HandleUartRxIrq);
// Setup GPT1 as "Write to HLApp" timer
sendTimer = GPT_Open(MT3620_UNIT_GPT0, MT3620_GPT_012_HIGH_SPEED, GPT_MODE_REPEAT);
if (!sendTimer) {
UART_Printf(debug, "ERROR: GPT_Open failed\r\n");
}
else
{
int32_t error;
if ((error = GPT_SetMode(sendTimer, GPT_MODE_REPEAT)) != ERROR_NONE) {
UART_Printf(debug, "ERROR: GPT_SetMode failed %ld\r\n", error);
}
if ((error = GPT_StartTimeout(
sendTimer, RTDRV_SEND_DELAY_MSEC, GPT_UNITS_MILLISEC,
handleSendMsgTimerWrapper)) != ERROR_NONE) {
UART_Printf(debug, "ERROR: GPT_StartTimeout failed %ld\r\n", error);
}
}
// Setup the receive socket for the HLApp
socket = Socket_Open(handleRecvMsgWrapper);
if (!socket) {
UART_Printf(debug, "ERROR: Socket_Open failed\r\n");
}
for (;;) {
__asm__("wfi");
InvokeCallbacks();
}
}