----------------------------------------------------------------------------
-- |
-- Module      :  STM32.USART
-- Copyright   :  (c) Marc Fontaine 2017
-- License     :  BSD3
-- 
-- Maintainer  :  Marc.Fontaine@gmx.de
-- Stability   :  experimental
-- Portability :  GHC-only
--
-- USART (Serial Port)

{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
module STM32.USART
where

import Device
import STM32.MachineInterface
import STM32.Utils
import STM32.GPIO as GPIO
import qualified STM32.RCC as RCC

import Control.Monad
import Data.Word
       
data Config = Config  {
     _baudRate   :: BaudRate
   , _wordLength :: WordLength
   , _stopBits   :: StopBits
   , _parity     :: Parity
   , _mode       :: Mode
   , _hardwareFlowControl :: HardwareFlowControl
   } deriving (Show)

{-
Only USART1 is clocked with PCLK2 (72 MHz Max). Other USARTs are clocked with
PCLK1 (36 MHz Max).

todo :: fix the baudrate stuff--
baudrate depends on clock

-}


defaultConfig :: Config
defaultConfig = Config  {
     _baudRate   = BaudRateRegisterValue 625 -- 115200 @ 72Mhz
   , _wordLength = Eight
   , _stopBits   = One
   , _parity     = No
   , _mode       = RxTx
   , _hardwareFlowControl = None
   }
   
data WordLength = Eight | Nine deriving Show
instance ToBit WordLength where
  toBit Eight = False
  toBit Nine  = True

data StopBits = Zero5 | One | One5 | Two deriving Show
instance RegisterField StopBits where
  toBits b = case b of
     One   -> "00"
     Zero5 -> "01"
     Two   -> "10"
     One5  -> "11"
  toField = const CR2_STOP
  
data Parity   = No | Even | Odd deriving Show
data Mode = Rx | Tx | RxTx deriving Show
data HardwareFlowControl = None | RTS | CTS | RTS_CTS | NA deriving (Eq,Show)
data BaudRate = BaudRateRegisterValue {getBRR :: Word16} deriving Show 

deInit :: Peripheral -> MI ()
deInit = RCC.peripheralResetToggle

init :: Peripheral -> Config -> MI ()
init p conf = do
  let write field rs = bitWrite p field rs
  
  fieldWrite p $ _stopBits conf

  write CR1_PCE $ case _parity conf of
        No -> False
        Even -> False        
        Odd  -> True

  write CR1_PS $ case _parity conf of
        Even -> False
        _ -> True

  write CR1_M $ _wordLength conf

  write CR1_TE $ case _mode conf of
    Tx   -> True 
    Rx   -> False
    RxTx -> True

  write CR1_RE $ case _mode conf of
    Tx   -> False
    Rx   -> True
    RxTx -> True

  when (_hardwareFlowControl conf /= NA) $ do
      write CR3_RTSE $ case _hardwareFlowControl conf of
        RTS     -> True
        RTS_CTS -> True
        _       -> False
      write CR3_CTSE $ case _hardwareFlowControl conf of
        CTS     -> True
        RTS_CTS -> True
        _ -> False

  pokeReg p BRR $ fromIntegral $ getBRR $ _baudRate conf

sendWord8 :: Peripheral -> Word8 -> MI ()
sendWord8 p b = pokeReg p DR $ fromIntegral b

enable :: Peripheral -> MI ()
enable p = bitSet p CR1_UE

disable :: Peripheral -> MI ()
disable p = bitReset p CR1_UE 

data UartPort = UartPort {
   _UartPeripheral  :: Peripheral
  ,_UartTXWire :: GPIO.Wire
  ,_UartRXWire  :: GPIO.Wire
  ,_UartIsAlternativeMapping :: Bool
  }

stm32F103_UartPort1 :: UartPort
stm32F103_UartPort1 = UartPort {
   _UartPeripheral  = USART1
  ,_UartTXWire = (GPIOA,Pin_9)
  ,_UartRXWire  = (GPIOA,Pin_10)
  ,_UartIsAlternativeMapping = False
  }

stm32F103_UartPort2 :: UartPort
stm32F103_UartPort2 = UartPort {
   _UartPeripheral  = USART2
  ,_UartTXWire = (GPIOA,Pin_2)
  ,_UartRXWire = (GPIOA,Pin_3)
  ,_UartIsAlternativeMapping = False
  }

stm32F103_UartPort3 :: UartPort
stm32F103_UartPort3 = UartPort {
   _UartPeripheral  = USART3
  ,_UartTXWire = (GPIOB,Pin_10)
  ,_UartRXWire  = (GPIOB,Pin_11)
  ,_UartIsAlternativeMapping = False
  }


configure :: UartPort -> Config -> MI () 
configure UartPort {..} config = do
  STM32.USART.deInit _UartPeripheral
  RCC.peripheralClockOn _UartPeripheral
  RCC.peripheralClockOn $ fst _UartRXWire
  RCC.peripheralClockOn AFIO

  GPIO.pinMode _UartTXWire (AlternateOutPushPull Mhz_2)
  GPIO.pinMode _UartRXWire InputFloating

  STM32.USART.enable _UartPeripheral
  STM32.USART.init _UartPeripheral config