/********************************* (C) РОТЕК ***********************************
 * @module  onewire
 * @file    onewire.c
 * @version 1.0.0
 * @date    XX.XX.XXXX
 * $brief   DS18B20 temperature sensor driver
 *******************************************************************************
 * @history     Version  Author         Comment
 * XX.XX.XXXX   1.0.0    Telenkov D.A.  First release.
 *******************************************************************************
 */

#include "common_config.h"
#include "stm32f4xx.h"
#include "stm32f4xx_dma.h"
#include "usart.h"
#include "gpio.h"
#include "onewire.h"
#ifdef PRINTF_STDLIB
#include <stdio.h>
#endif
#ifdef PRINTF_CUSTOM
#include "tinystdio.h"
#endif

/* Force remove debug output */
#undef DBG
#define DBG if(0)

#ifdef DALLAS_SENSOR_ENABLE
/**
  * @brief  Размер буфера для отправки данных датчикам
  */
#define DALLAS_BUF_SIZE   8

#define DALLAS_TIME_OUT   1500000

#define DALLAS_0    0x00
#define DALLAS_1    0xff
#define DALLAS_R_1  0xff

typedef struct {
  gpio_t ow_pin;
  USART_TypeDef *uart_addr;
  DMA_InitTypeDef DMA_InitStructureTx;
  DMA_InitTypeDef DMA_InitStructureRx;
  uint8_t sensorBuf[DALLAS_BUF_SIZE];
}ow_sensor_t;

ow_sensor_t ow_sensor[MAX_T_SENSORS];

#define T_SENSOR_ADD(sensor_num, ow_line, uart) \
  do { \
    ow_sensor[sensor_num].uart_addr = uart; \
    ow_sensor[sensor_num].ow_pin = ow_line; \
  } while (0);

/**
  * @brief  Настройка USART
  * @retval
  */
void DALLAS_Init()
{
#define XTSENSOR(sensor_num, ow_line, uart) T_SENSOR_ADD(sensor_num, ow_line, uart);
      T_SENSOR_TABLE
#undef XTSENSOR
  for (uint8_t i = 0; i < MAX_T_SENSORS; i ++) {
    uart_hw_init(ow_sensor[i].uart_addr, DALLAS_SENSOR_UART_BAUD, DALLAS_SENSOR_UART_WORD_LEN,
        DALLAS_SENSOR_UART_PARITY, DALLAS_SENSOR_UART_STOP_BIT);
  }
  DALLAS_DmaInit();
}

/**
  * @brief  Настройка DMA
  * @retval
  */
void DALLAS_DmaInit(void)
{
  DMA_Stream_TypeDef *RxDMA_Stream = NULL;
  DMA_Stream_TypeDef *TxDMA_Stream = NULL;
  uint32_t RxDMA_Channel = 0;
  uint32_t TxDMA_Channel = 0;

  for (uint8_t i = 0; i < MAX_T_SENSORS; i ++) {
    if (ow_sensor[i].uart_addr == USART1) {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
      RxDMA_Stream = DMA1_Stream2;
      TxDMA_Stream = DMA1_Stream7;
      RxDMA_Channel = DMA_Channel_4;
      TxDMA_Channel = DMA_Channel_4;
    } else if (ow_sensor[i].uart_addr == USART2) {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
      RxDMA_Stream = DMA1_Stream5;
      TxDMA_Stream = DMA1_Stream6;
      RxDMA_Channel = DMA_Channel_4;
      TxDMA_Channel = DMA_Channel_4;
    } else if (ow_sensor[i].uart_addr == USART3) {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
      RxDMA_Stream = DMA1_Stream1;
      TxDMA_Stream = DMA1_Stream3;
      RxDMA_Channel = DMA_Channel_4;
      TxDMA_Channel = DMA_Channel_4;
    } else if (ow_sensor[i].uart_addr == UART4) {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
      RxDMA_Stream = DMA1_Stream2;
      TxDMA_Stream = DMA1_Stream4;
      RxDMA_Channel = DMA_Channel_4;
      TxDMA_Channel = DMA_Channel_4;
    } else if (ow_sensor[i].uart_addr == UART5) {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
      RxDMA_Stream = DMA1_Stream0;
      TxDMA_Stream = DMA1_Stream7;
      RxDMA_Channel = DMA_Channel_4;
      TxDMA_Channel = DMA_Channel_4;
    } else if (ow_sensor[i].uart_addr == USART6) {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
      RxDMA_Stream = DMA2_Stream1;
      TxDMA_Stream = DMA2_Stream6;
      RxDMA_Channel = DMA_Channel_5;
      TxDMA_Channel = DMA_Channel_5;
    }
  
      /* DMA на чтение */
      DMA_DeInit(RxDMA_Stream);
      ow_sensor[i].DMA_InitStructureRx.DMA_Channel = RxDMA_Channel;
      ow_sensor[i].DMA_InitStructureRx.DMA_PeripheralBaseAddr = (uint32_t)&ow_sensor[i].uart_addr->DR;
      ow_sensor[i].DMA_InitStructureRx.DMA_Memory0BaseAddr = (uint32_t)&ow_sensor[i].sensorBuf;
      ow_sensor[i].DMA_InitStructureRx.DMA_DIR = DMA_DIR_PeripheralToMemory;
      ow_sensor[i].DMA_InitStructureRx.DMA_BufferSize = DALLAS_BUF_SIZE;
      ow_sensor[i].DMA_InitStructureRx.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
      ow_sensor[i].DMA_InitStructureRx.DMA_MemoryInc = DMA_MemoryInc_Enable;
      ow_sensor[i].DMA_InitStructureRx.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
      ow_sensor[i].DMA_InitStructureRx.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
      ow_sensor[i].DMA_InitStructureRx.DMA_Mode = DMA_Mode_Normal;
      ow_sensor[i].DMA_InitStructureRx.DMA_Priority = DMA_Priority_Medium;

      DMA_Init(RxDMA_Stream, &ow_sensor[i].DMA_InitStructureRx);

      /* DMA на запись */
      DMA_DeInit(TxDMA_Stream);
      ow_sensor[i].DMA_InitStructureTx.DMA_Channel = TxDMA_Channel;
      ow_sensor[i].DMA_InitStructureTx.DMA_PeripheralBaseAddr = &ow_sensor[i].uart_addr->DR;
      ow_sensor[i].DMA_InitStructureTx.DMA_Memory0BaseAddr = (uint32_t)&ow_sensor[i].sensorBuf;
      ow_sensor[i].DMA_InitStructureTx.DMA_DIR = DMA_DIR_MemoryToPeripheral;
      ow_sensor[i].DMA_InitStructureTx.DMA_BufferSize = DALLAS_BUF_SIZE;
      ow_sensor[i].DMA_InitStructureTx.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
      ow_sensor[i].DMA_InitStructureTx.DMA_MemoryInc = DMA_MemoryInc_Enable;
      ow_sensor[i].DMA_InitStructureTx.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
      ow_sensor[i].DMA_InitStructureTx.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
      ow_sensor[i].DMA_InitStructureTx.DMA_Mode = DMA_Mode_Normal;
      ow_sensor[i].DMA_InitStructureTx.DMA_Priority = DMA_Priority_Medium;

      DMA_Init(TxDMA_Stream, &ow_sensor[i].DMA_InitStructureTx);
  }
}

/**
  * @brief  Настроить TX пин как питающий выход
  * @retval
  */
void DALLAS_OutSetAsPower(t_sensor_list_t sensor)
{
    gpio_pindef_t *pin = &gpio_pins[ow_sensor[sensor].ow_pin];
    gpio_hw_config_pin(pin->port, pin->pin, (GPIO_MODE_OUT_CFG | GPIO_TYPE_PP_CFG |
    GPIO_SPEED_HIGH_CFG));
    gpio_set(ow_sensor[sensor].ow_pin, GPIO_SET);
}

/**
  * @brief  Настроить TX пин как передатчик
  * @retval
  */
void DALLAS_OutSetAsTX(t_sensor_list_t sensor)
{
    gpio_pindef_t *pin = &gpio_pins[ow_sensor[sensor].ow_pin];
    uint8_t af_n = (uint8_t)(pin->flags >> _GPIO_AF_SHIFT);
    gpio_connect_af(ow_sensor[sensor].ow_pin, af_n);
    gpio_hw_config_pin(pin->port, pin->pin, GPIO_MODE_AF_CFG |
        GPIO_SPEED_HIGH_CFG);
}

/**
  * @brief  Сброс и проверка наличия устройства на шине 1-wire
  * @retval
  */
uint8_t DALLAS_SensorReset(t_sensor_list_t sensor)
{
    uint8_t presence;

    uart_config_reinit(ow_sensor[sensor].uart_addr, DALLAS_SENSOR_RST_UART_BAUD,
        DALLAS_SENSOR_UART_WORD_LEN, DALLAS_SENSOR_UART_PARITY, DALLAS_SENSOR_UART_STOP_BIT);

    // отправляем 0xf0 на скорости 9600
    USART_ClearFlag(ow_sensor[sensor].uart_addr, USART_FLAG_TC);
    USART_SendData(ow_sensor[sensor].uart_addr, 0xf0);

    while (USART_GetFlagStatus(ow_sensor[sensor].uart_addr, USART_FLAG_TC) == RESET) { }

    presence = USART_ReceiveData(ow_sensor[sensor].uart_addr);

    uart_config_reinit(ow_sensor[sensor].uart_addr, DALLAS_SENSOR_UART_BAUD, DALLAS_SENSOR_UART_WORD_LEN,
              DALLAS_SENSOR_UART_PARITY, DALLAS_SENSOR_UART_STOP_BIT);

    if (presence != 0xf0) {
        DBG printf("DALLAS_SensorReset: DALLAS_OK\r\n");
        return DALLAS_OK;
    }

    DBG printf("DALLAS_SensorReset: DALLAS_NO_DEVICE\r\n");
    return DALLAS_NO_DEVICE;
}

/**
  * @brief   Отправка и прием данных
  * @param   sendReset посылать RESET в начале общения.
  *          OW_SEND_RESET
  *          OW_NO_RESET
  * @param   *command - массив байт, отсылаемых в шину.
  *          Если нужно чтение - отправляем OW_READ_SLOTH
  * @param   cLen - длина буфера команд, столько байт отошлется в шину
  * @param   *data - если требуется чтение, то ссылка на буфер для чтения
  * @param   dLen - длина буфера для чтения. Прочитается не более этой длины
  * @param   readStart - с какого символа передачи начинать чтение (нумеруются с 0)
  *          можно указать OW_NO_READ, тогда можно не задавать data и dLen
  * @retval
  */
uint8_t DALLAS_Send(t_sensor_list_t sensor, uint8_t sendReset, uint8_t *command,
    uint8_t cLen, uint8_t *data, uint8_t dLen, uint8_t readStart)
{
    uint32_t timeOutCounter = 0;
    uint32_t DMA_FLAG_TCIF = 0;
    DMA_Stream_TypeDef *RxDMA_Stream = NULL;
    DMA_Stream_TypeDef *TxDMA_Stream = NULL;

    if (ow_sensor[sensor].uart_addr == USART1) {
      DMA_FLAG_TCIF = DMA_FLAG_TCIF2;
      RxDMA_Stream = DMA1_Stream2;
      TxDMA_Stream = DMA1_Stream7;
    } else if (ow_sensor[sensor].uart_addr == USART2) {
      DMA_FLAG_TCIF = DMA_FLAG_TCIF5;
      RxDMA_Stream = DMA1_Stream5;
      TxDMA_Stream = DMA1_Stream6;
    } else if (ow_sensor[sensor].uart_addr == USART3) {
      DMA_FLAG_TCIF = DMA_FLAG_TCIF1;
      RxDMA_Stream = DMA1_Stream1;
      TxDMA_Stream = DMA1_Stream3;
    } else if (ow_sensor[sensor].uart_addr == UART4) {
      DMA_FLAG_TCIF = DMA_FLAG_TCIF2;
      RxDMA_Stream = DMA1_Stream2;
      TxDMA_Stream = DMA1_Stream4;
    } else if (ow_sensor[sensor].uart_addr == UART5) {
      DMA_FLAG_TCIF = DMA_FLAG_TCIF0;
      RxDMA_Stream = DMA1_Stream0;
      TxDMA_Stream = DMA1_Stream7;
    } else if (ow_sensor[sensor].uart_addr == USART6) {
      DMA_FLAG_TCIF = DMA_FLAG_TCIF1;
      RxDMA_Stream = DMA2_Stream1;
      TxDMA_Stream = DMA2_Stream6;
    }

    /* если требуется сброс - сбрасываем и проверяем на наличие устройств */
    if (sendReset == DALLAS_SEND_RESET) {
        DBG printf("_Send: DALLAS_SEND_RESET\r\n");
        if (DALLAS_SensorReset(sensor) == DALLAS_NO_DEVICE) {
            DBG printf("_Send: DALLAS_NO_DEVICE\r\n");
            return DALLAS_NO_DEVICE;
        }
    }

    while (cLen > 0) {
        DALLAS_ToBits(*command, ow_sensor[sensor].sensorBuf);
        command++;
        cLen--;

        /* DMA на чтение */
        DMA_DeInit(RxDMA_Stream);
        DMA_Init(RxDMA_Stream, &ow_sensor[sensor].DMA_InitStructureRx);

        /* DMA на запись */
        DMA_DeInit(TxDMA_Stream);
        DMA_Init(TxDMA_Stream, &ow_sensor[sensor].DMA_InitStructureTx);

        /* старт цикла отправки */
        USART_ClearFlag(ow_sensor[sensor].uart_addr, USART_FLAG_RXNE | USART_FLAG_TC );
        USART_DMACmd(ow_sensor[sensor].uart_addr, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);
        DMA_Cmd(RxDMA_Stream, ENABLE);
        DMA_Cmd(TxDMA_Stream, ENABLE);


        while (DMA_GetFlagStatus(RxDMA_Stream, DMA_FLAG_TCIF) == RESET) {

            timeOutCounter++;
            if (timeOutCounter > DALLAS_TIME_OUT) {
                DBG printf("_Send: return DALLAS_ERROR\r\n");
                return DALLAS_ERROR;
            }
        }
        DBG printf("timeOutCounter: %u\r\n", (unsigned int)timeOutCounter);


        /* отключаем DMA */
        DMA_Cmd(RxDMA_Stream, DISABLE);
        DMA_Cmd(TxDMA_Stream, DISABLE);
        USART_DMACmd(ow_sensor[sensor].uart_addr, USART_DMAReq_Tx | USART_DMAReq_Rx, DISABLE);

        /* если прочитанные данные кому-то нужны - выкинем их в буфер */
        DBG printf("_Send: readStart = %d, dLen = %d\r\n", readStart, dLen);
        if (readStart == 0 && dLen > 0) {
            *data = DALLAS_ToByte(ow_sensor[sensor].sensorBuf);
            data++;
            dLen--;
            DBG printf("_Send: Copy\r\n");
        } else {
            if (readStart != DALLAS_NO_READ) {
                readStart--;
                DBG printf("_Send: readStart--\r\n");
            }
            DBG printf("_Send: else state\r\n");
        }
    }

    DBG printf("_Send: return DALLAS_OK\r\n");

    return DALLAS_OK;
}

/**
  * @brief  Функция преобразует один байт в восемь, для передачи через USART
  * @param  ow_byte - байт, который надо преобразовать
  * @param  ow_bits - ссылка на буфер, размером не менее 8 байт
  * @retval
  */
void DALLAS_ToBits(uint8_t ow_byte, uint8_t *ow_bits)
{
    uint8_t i;

    for (i = 0; i < 8; i++) {
        if (ow_byte & 0x01) {
            *ow_bits = DALLAS_1;
        } else {
            *ow_bits = DALLAS_0;
        }

        ow_bits++;
        ow_byte = ow_byte >> 1;
    }
}

/**
  * @brief  Обратное преобразование - из того, что получено через USART опять собирается байт
  * @param  ow_bits - ссылка на буфер, размером не менее 8 байт
  * @param  ow_byte - собранный байт данных
  * @retval
  */
uint8_t DALLAS_ToByte(uint8_t *ow_bits)
{
    uint8_t ow_byte, i;
    ow_byte = 0;

    for (i = 0; i < 8; i++) {
        ow_byte = ow_byte >> 1;
        if (*ow_bits == DALLAS_R_1) {
            ow_byte |= 0x80;
        }

        ow_bits++;
    }
    return ow_byte;
}

#endif
