/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id$
 */

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "main.h"
#include "usart.h"

#include "elog.h"

/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR(void);
static void prvvUARTRxISR(void);

/* -----------------------    variables     ---------------------------------*/

#define Modbus_USE_485   1
#define Modbus_UART_IRQHandler   USART2_IRQHandler
UART_HandleTypeDef *pMobusHuart = &huart2;
 
#if Modbus_USE_485
#define MODBUS_485_Receive_Enable() HAL_GPIO_WritePin( NULL, 0, RESET );
#define MODBUS_485_Send_Enable()  HAL_GPIO_WritePin( NULL, 0, SET );
#endif

volatile uint32_t it_num = 0;
volatile uint32_t aa = 0;
volatile uint32_t bb = 0;
volatile uint32_t cc = 0;

#ifndef FREERTOS_USED

/* ----------------------- Start implementation -----------------------------*/
void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
    if (xRxEnable)
    {
#if Modbus_USE_485
        for (size_t i = 0; i < 22000; i++);  // 485 拉低准备接收 延时以免漏掉发送的最后字节
        MODBUS_485_Receive_Enable();
#endif
        __HAL_UART_ENABLE_IT(pMobusHuart, UART_IT_RXNE);
    }
    else
    {
#if Modbus_USE_485
        MODBUS_485_Send_Enable();
#endif
        __HAL_UART_DISABLE_IT(pMobusHuart, UART_IT_RXNE);
    }

    if (xTxEnable)
    {
#if Modbus_USE_485
        MODBUS_485_Send_Enable();
#endif
        __HAL_UART_ENABLE_IT(pMobusHuart, UART_IT_TXE);
    }
    else
    {
#if Modbus_USE_485
        MODBUS_485_Send_Enable();
#endif
        __HAL_UART_DISABLE_IT(pMobusHuart, UART_IT_TXE);
    }
}

BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity)
{
    // pMobusHuart->Instance = USART2;
    pMobusHuart->Init.BaudRate = ulBaudRate;
    pMobusHuart->Init.StopBits = UART_STOPBITS_1;
    pMobusHuart->Init.Mode = UART_MODE_TX_RX;
    pMobusHuart->Init.HwFlowCtl = UART_HWCONTROL_NONE;
    pMobusHuart->Init.OverSampling = UART_OVERSAMPLING_16;

    switch (eParity)
    {
    // 奇校验
    case MB_PAR_ODD:
        pMobusHuart->Init.Parity = UART_PARITY_ODD;
        pMobusHuart->Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
        break;

    // 偶校验
    case MB_PAR_EVEN:
        pMobusHuart->Init.Parity = UART_PARITY_EVEN;
        pMobusHuart->Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
        break;

    // 无校验
    default:
        pMobusHuart->Init.Parity = UART_PARITY_NONE;
        pMobusHuart->Init.WordLength = UART_WORDLENGTH_8B; // 无奇偶校验数据位为8bits
        break;
    }

    /* 防止启用时进入中断*/
    __HAL_UART_CLEAR_FLAG(pMobusHuart, UART_FLAG_RXNE);
    __HAL_UART_CLEAR_FLAG(pMobusHuart, UART_FLAG_TXE);
    __HAL_UART_DISABLE_IT(pMobusHuart, UART_IT_RXNE);
    __HAL_UART_DISABLE_IT(pMobusHuart, UART_IT_TXE);

    return HAL_UART_Init(pMobusHuart) == HAL_OK ? TRUE : FALSE;
}

BOOL xMBPortSerialPutByte(CHAR ucByte)
{
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
    pMobusHuart->Instance->DR = ucByte;
    return TRUE;
    // return (HAL_OK == HAL_UART_Transmit(pMobusHuart, (uint8_t *)&ucByte, 1, 10));
}

BOOL xMBPortSerialGetByte(CHAR *pucByte)
{
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */
    *pucByte = (uint8_t)(pMobusHuart->Instance->DR & (uint8_t)0x00FF);
    return TRUE;
}

/* Create an interrupt handler for the transmit buffer empty interrupt
 * (or an equivalent) for your target processor. This function should then
 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
 * a new character can be sent. The protocol stack will then call
 * xMBPortSerialPutByte( ) to send the character.
 */
static void prvvUARTTxReadyISR(void)
{
    pxMBFrameCBTransmitterEmpty();
}

/* Create an interrupt handler for the receive interrupt for your target
 * processor. This function should then call pxMBFrameCBByteReceived( ). The
 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
 * character.
 */
static void prvvUARTRxISR(void)
{
    // log_i( " modbus rcv one  byte....... ");
    pxMBFrameCBByteReceived();
}

void USART2_IRQHandler(void)
{   
    aa++;
    // if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE))			// 接收非空中断标记被置位
    // {
    //     bb++;
    //     __HAL_UART_CLEAR_FLAG(pMobusHuart, UART_FLAG_RXNE); // 清除中断标记
    //     prvvUARTRxISR();                                    // 通知modbus有数据到达
    // }
    // if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE))				// 发送为空中断标记被置位
    // {
    //     cc++;
    //     __HAL_UART_CLEAR_FLAG(pMobusHuart, UART_FLAG_TXE); // 清除中断标记
    //     prvvUARTTxReadyISR();                              // 通知modbus数据可以发松
    // }

    if (__HAL_UART_GET_IT_SOURCE(pMobusHuart, UART_IT_RXNE) != RESET 
            && __HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE)  != RESET )
    {
        bb++;
        __HAL_UART_CLEAR_FLAG(pMobusHuart, UART_FLAG_RXNE); // 清除中断标记
        prvvUARTRxISR();                                    // 通知freemodbus有数据到达
    }

    if (__HAL_UART_GET_IT_SOURCE(pMobusHuart, UART_IT_TXE) != RESET
            && __HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE)  != RESET )
    {
        cc++;
        __HAL_UART_CLEAR_FLAG(pMobusHuart, UART_FLAG_TXE); // 清除中断标记
        prvvUARTTxReadyISR();                              // 通知modbus数据可以发松
    }

    if (__HAL_UART_GET_FLAG(pMobusHuart, UART_FLAG_ORE))
    {
        /*  deal with uart rcv buffer ORE*/
        // uint16_t pucByte = (uint16_t)((pMobusHuart)->Instance->DR & (uint16_t)0x01FF);
        __HAL_UART_CLEAR_OREFLAG(pMobusHuart);
    }

    HAL_UART_IRQHandler(pMobusHuart);
}

#else

/* ----------------------- System includes ----------------------------------*/
#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>

/* ----------------------- Static variables ---------------------------------*/
/* software simulation serial transmit IRQ handler thread */
static TaskHandle_t thread_serial_soft_trans_irq = NULL;
/* serial event */
static EventGroupHandle_t event_serial; /* 消息队列取代 */
/* modbus slave serial device */
static UART_HandleTypeDef *serial;
/*
 * Serial FIFO mode
 */

static volatile uint8_t rx_buff[FIFO_SIZE_MAX];
static Serial_fifo Slave_serial_rx_fifo;
/* ----------------------- Defines ------------------------------------------*/
/* serial transmit event */
#define EVENT_SERIAL_TRANS_START (1 << 0)

/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR(void);
static void prvvUARTRxISR(void);
static void serial_soft_trans_irq(void *parameter);
static void Slave_TxCpltCallback(struct __UART_HandleTypeDef *huart);
static void Slave_RxCpltCallback(struct __UART_HandleTypeDef *huart);
static int stm32_getc(void);
static int stm32_putc(CHAR c);
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
                       eMBParity eParity)
{
    /**
     * set 485 mode receive and transmit control IO
     * @note MODBUS_SLAVE_RT_CONTROL_PIN_INDEX need be defined by user
     */
    // rt_pin_mode(MODBUS_SLAVE_RT_CONTROL_PIN_INDEX, PIN_MODE_OUTPUT);

    /* set serial name */
    if (ucPORT == 1)
    {
#if defined(USING_UART1)
        extern UART_HandleTypeDef huart1;
        serial = &huart1;
        MODBUS_DEBUG("Slave using uart1!\r\n");

#endif
    }
    else if (ucPORT == 2)
    {
#if defined(USING_UART2)
        extern UART_HandleTypeDef huart2;
        serial = &huart2;
        MODBUS_DEBUG("Slave using uart2!\r\n");

#endif
    }
    else if (ucPORT == 3)
    {
#if defined(USING_UART3)
        extern UART_HandleTypeDef huart3;
        serial = &huart3;
        MODBUS_DEBUG("Slave using uart3!\r\n");
#endif
    }
    /* set serial configure */

    serial->Init.StopBits = UART_STOPBITS_1;
    serial->Init.BaudRate = ulBaudRate;
    switch (eParity)
    {
    case MB_PAR_NONE:
    {
        serial->Init.WordLength = UART_WORDLENGTH_8B;
        serial->Init.Parity = UART_PARITY_NONE;
        break;
    }
    case MB_PAR_ODD:
    {
        serial->Init.WordLength = UART_WORDLENGTH_9B;
        serial->Init.Parity = UART_PARITY_ODD;
        break;
    }
    case MB_PAR_EVEN:
    {
        serial->Init.WordLength = UART_WORDLENGTH_9B;
        serial->Init.Parity = UART_PARITY_EVEN;
        break;
    }
    }
    if (HAL_UART_Init(serial) != HAL_OK)
    {
        Error_Handler();
    }
    __HAL_UART_DISABLE_IT(serial, UART_IT_RXNE);
    __HAL_UART_DISABLE_IT(serial, UART_IT_TC);
    /*registe recieve callback*/
    HAL_UART_RegisterCallback(serial, HAL_UART_RX_COMPLETE_CB_ID,
                              Slave_RxCpltCallback);
    /* software initialize */
    Slave_serial_rx_fifo.buffer = rx_buff;
    Slave_serial_rx_fifo.get_index = 0;
    Slave_serial_rx_fifo.put_index = 0;

    /* 创建串口发送线程*/
    event_serial = xEventGroupCreate(); // 创建事件
    if (NULL != event_serial)
    {
        MODBUS_DEBUG("Create Slave event_serial Event success!\r\n");
    }
    else
    {
        MODBUS_DEBUG("Create Slave event_serial Event  Faild!\r\n");
    }
    BaseType_t xReturn =
        xTaskCreate((TaskFunction_t)serial_soft_trans_irq,          /* 任务函数*/
                    (const char *)"slave trans",                    /* 任务名称*/
                    (uint16_t)128,                                  /* 栈*/
                    (void *)NULL,                                   /* 入口参数 */
                    (UBaseType_t)12,                                /* 优先级*/
                    (TaskHandle_t *)&thread_serial_soft_trans_irq); /*任务句柄*/

    if (xReturn == pdPASS)
    {
        MODBUS_DEBUG("xTaskCreate slave trans success\r\n");
    }
    return TRUE;
}

void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
    /*清除中断标志，这一步不要省略*/
    __HAL_UART_CLEAR_FLAG(serial, UART_FLAG_RXNE);
    __HAL_UART_CLEAR_FLAG(serial, UART_FLAG_TC);
    if (xRxEnable)
    {
        /* enable RX interrupt */
        __HAL_UART_ENABLE_IT(serial, UART_IT_RXNE);
        /* switch 485 to receive mode */
        MODBUS_DEBUG("RS485_RX_MODE\r\n");
        SLAVE_RS485_RX_MODE;
    }
    else
    {
        /* switch 485 to transmit mode */
        MODBUS_DEBUG("RS485_TX_MODE\r\n");
        SLAVE_RS485_TX_MODE;
        /* disable RX interrupt */
        __HAL_UART_DISABLE_IT(serial, UART_IT_RXNE);
    }
    if (xTxEnable)
    {
        /* start serial transmit */
        xEventGroupSetBits(event_serial, EVENT_SERIAL_TRANS_START);
    }
    else
    {
        /* stop serial transmit */
        xEventGroupClearBits(event_serial, EVENT_SERIAL_TRANS_START);
        /*测试帧数*/
        // printf("ms=%.2f,fps=%.2f\r\n", __HAL_TIM_GetCounter(&htim7) / 100.f,
        // 1000.f / (__HAL_TIM_GetCounter(&htim7) / 100.f));
    }
}

void vMBPortClose(void) { __HAL_UART_DISABLE(serial); }
/*Send a byte*/
BOOL xMBPortSerialPutByte(CHAR ucByte)
{
    stm32_putc(ucByte);   
    return TRUE;
}
/*Get a byte from fifo*/
BOOL xMBPortSerialGetByte(CHAR *pucByte)
{
    Get_from_fifo(&Slave_serial_rx_fifo, (uint8_t *)pucByte, 1);
    return TRUE;
}

void prvvUARTTxReadyISR(void) { pxMBFrameCBTransmitterEmpty(); }

void prvvUARTRxISR(void) { pxMBFrameCBByteReceived(); }

/**
 * Software simulation serial transmit IRQ handler.
 *
 * @param parameter parameter
 */
static void serial_soft_trans_irq(void *parameter)
{
    uint32_t recved_event;
    while (1)
    {
        /* waiting for serial transmit start */
        xEventGroupWaitBits(event_serial,             /* 事件对象句柄 */
                            EVENT_SERIAL_TRANS_START, /* 接收任务感兴趣的事件 */
                            pdFALSE,                  /* 退出时清除事件?? */
                            pdFALSE,                  /* 满足感兴趣的所有事?? */
                            portMAX_DELAY);           /* 指定超时事件,无限等待 */
        /* execute modbus callback */
        prvvUARTTxReadyISR();
    }
}

/**
 * @brief  Rx Transfer completed callbacks.
 * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
 *                the configuration information for the specified UART module.
 * @retval None
 */
void Slave_RxCpltCallback(UART_HandleTypeDef *huart)
{
    int ch = -1;
    while (1)
    {
        ch = stm32_getc();
        if (ch == -1)
            break;
        Put_in_fifo(&Slave_serial_rx_fifo, (uint8_t *)&ch, 1);
    }
    prvvUARTRxISR();
}
/*UART发送一个字节*/
static int stm32_putc(CHAR c)
{
    serial->Instance->DR = c;
    while (!(serial->Instance->SR & UART_FLAG_TC))
        ;
    return TRUE;
}
/*UART接收一个字节*/
static int stm32_getc(void)
{
    int ch;
    ch = -1;
    if (serial->Instance->SR & UART_FLAG_RXNE)
    {
        ch = serial->Instance->DR & 0xff;
    }
    return ch;
}

/*put  bytes in buff*/
void Put_in_fifo(Serial_fifo *buff, uint8_t *putdata, int length)
{
    portDISABLE_INTERRUPTS();
    while (length--)
    {
        buff->buffer[buff->put_index] = *putdata;
        buff->put_index += 1;
        if (buff->put_index >= MB_SIZE_MAX)
            buff->put_index = 0;
        /* if the next position is read index, discard this 'read char' */
        if (buff->put_index == buff->get_index)
        {
            buff->get_index += 1;
            if (buff->get_index >= MB_SIZE_MAX)
                buff->get_index = 0;
        }
    }
    portENABLE_INTERRUPTS();
}
/*get  bytes from buff*/
int Get_from_fifo(Serial_fifo *buff, uint8_t *getdata, int length)
{
    int size = length;
    /* read from software FIFO */
    while (length)
    {
        int ch;
        /* disable interrupt */
        portDISABLE_INTERRUPTS();
        if (buff->get_index != buff->put_index)
        {
            ch = buff->buffer[buff->get_index];
            buff->get_index += 1;
            if (buff->get_index >= MB_SIZE_MAX)
                buff->get_index = 0;
        }
        else
        {
            /* no data, enable interrupt and break out */
            portENABLE_INTERRUPTS();
            break;
        }
        *getdata = ch & 0xff;
        getdata++;
        length--;
        /* enable interrupt */
        portENABLE_INTERRUPTS();
    }
    return size - length;
}

/**
 * @brief This function handles USART2 global interrupt.
 */
void USART2_IRQHandler(void)
{
    /* USER CODE BEGIN USART2_IRQn 0 */
    if (__HAL_UART_GET_FLAG(pMobusHuart, UART_FLAG_RXNE))
    {
        huart2.RxCpltCallback(pMobusHuart);
        __HAL_UART_CLEAR_FLAG(pMobusHuart, UART_FLAG_RXNE);
    }
    if (__HAL_UART_GET_FLAG(pMobusHuart, UART_FLAG_ORE))
    {
        uint16_t pucByte = (uint16_t)((pMobusHuart)->Instance->DR & (uint16_t)0x01FF);
        __HAL_UART_CLEAR_OREFLAG(pMobusHuart);
    }
    if (__HAL_UART_GET_FLAG(pMobusHuart, UART_FLAG_TC))
    {
        __HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_TC);
    }

    /* USER CODE END USART2_IRQn 0 */
    /* USER CODE BEGIN USART2_IRQn 1 */
    /* USER CODE END USART2_IRQn 1 */
}

#endif