#include "rs485.h"

#include "ch32v003fun.h"

#define RS485_TX_EN (1 << 14)  // PB14
#define RS485_RX_EN (1 << 15)  // PB15

void rs485_init(int uart_brr) {
  RCC->APB2PCENR |= RCC_APB2Periph_GPIOA |  // Enable GPIOA
                    RCC_APB2Periph_GPIOB |  // Enable GPIOB
                    RCC_APB2Periph_USART1;  // Enable USART1

  // configure uart1 pins (PA9=TX, PA10=RX)
  GPIOA->CFGHR &=
      ~(0xf << (4 * 1) | 0xf << (4 * 2));  // Clear PA9 & PA10 config
  GPIOA->CFGHR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)
                  << (4 * 1);                       // PA9 (TX)
  GPIOA->CFGHR |= GPIO_CNF_IN_FLOATING << (4 * 2);  // PA10 (RX)

  // configure rs485 direction control pins (PB14=TX_EN, PB15=RX_EN)
  GPIOB->CFGHR &=
      ~(0xf << (4 * 6) | 0xf << (4 * 7));  // Clear PB14 & PB15 config
  GPIOB->CFGHR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)
                  << (4 * 6);  // PB14 (TX_EN)
  GPIOB->CFGHR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)
                  << (4 * 7);  // PB15 (RX_EN)

  // initial state: rx mode
  GPIOB->BCR = RS485_TX_EN;   // TX_EN low
  GPIOB->BSHR = RS485_RX_EN;  // RX_EN high

  // uart1 configuration
  USART1->CTLR1 =
      USART_WordLength_8b | USART_Parity_No | USART_Mode_Tx | USART_Mode_Rx;
  USART1->CTLR2 = USART_StopBits_1;
  USART1->CTLR3 = USART_HardwareFlowControl_None;
  USART1->BRR = uart_brr;
  USART1->CTLR1 |= CTLR1_UE_Set;
}

void rs485_send(uint8_t *buf, uint16_t len) {
  // enable tx mode
  GPIOB->BSHR = RS485_TX_EN;  // TX_EN high
  GPIOB->BSHR = RS485_RX_EN;  // RX_EN low

  for (uint16_t i = 0; i < len; i++) {
    while (!(USART1->STATR & USART_FLAG_TXE));
    USART1->DATAR = buf[i];
  }

  while (!(USART1->STATR & USART_FLAG_TC));

  // switch back to rx mode
  GPIOB->BCR = RS485_TX_EN;  // TX_EN low
  GPIOB->BCR = RS485_RX_EN;  // RX_EN high
}

uint8_t rs485_available(void) { return (USART1->STATR & USART_FLAG_RXNE) != 0; }

uint8_t rs485_read(void) { return USART1->DATAR & 0xFF; }
