/**
 * @file   ctrlUART4.c
 * @author StepTechnica
 * @date   May 16, 2023
 * 
 * @brief Evaluation Board 用ソースファイル「UART(4)制御」
 * 
 * @copyright (C) 2022- StepTechnica Co.,Ltd.
 * 
 * このソフトウェアは、コピー利用、配布、変更の追加、変更を加えたもの再配布、商用利用、有料販売など、どなたも自由にお使いいただくことができます。
 * このソフトウェアには保証はついていません。
 * このソフトウェアを利用したことで問題が起きた際に、ソフトウェアの製作者は一切の責任を負いません。
 * 
 */

/* -----------------------------------------------------------------------------
 * #include
 */

#include "stm32f4xx_hal.h"
#include "ctrlUART4.h"

/* -----------------------------------------------------------------------------
 * 変数定義
 */
unsigned char	txBuf[SZ_SIOBUF];
unsigned char	rxBuf[SZ_SIOBUF];
unsigned char	reBuf[SZ_SIOBUF];
unsigned short	wp_txBuf;
unsigned short	rp_txBuf;
unsigned short	wp_rxBuf;
unsigned short	rp_rxBuf;
char         	gLFcode;
unsigned char	gLFbuf[4];
char         	gTxBusy;

/* -----------------------------------------------------------------------------
 * 関数プロトタイプ宣言
 */
void  dbUART4_IRQHandler(void);
void  init_GPIO_UART4(void);
short init_UART(SCI_CONFIG *sciParam);
short print_char(char c);
short print_string(char *str);
static void sci_txdone(void);
static void sci_rxdone(void);
static void sci_putc(unsigned char);

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： UART4 Interrupt Handller
 * 
 * @note： ./Core/Startup/startup_stm32f446zetx.s のベクターテーブルに登録
 * 
 * @param  none
 * @retval none
 * 
 */
void dbUART4_IRQHandler(void)
{
	__IO uint32_t	u4SR;
	unsigned char	err;
	unsigned short	wp;

	u4SR = UART4->SR;
	// UART4.SR(USART4_BASE + 0x00):
	//     Bit-31:10 :
	//     Bit- 9    : CTS   : CTS flag
	//     Bit- 8    : LBDF  : LIN break detection flag
	//     Bit- 7    : TXE   : Transmit data register empty
	//     Bit- 6    : TC    : Transmission complete
	//     Bit- 5    : RXNE  : Read data register not empty
	//     Bit- 4    : IDLE  : Idle line detected
	//     Bit- 3    : ORE   : Overrun error
	//     Bit- 2    : NF    : START bit Noise detection flag
	//     Bit- 1    : FE    : Framing error
	//     Bit- 0    : PE    : Parity error

	//if ( (u4SR & USART_SR_TXE ) == USART_SR_TXE ) {
	if ( (u4SR & USART_SR_TC ) == USART_SR_TC ) {
		sci_txdone();
	}
	if ( (u4SR & USART_SR_RXNE) == USART_SR_RXNE) {
		sci_rxdone();
	}

	err = 0x00;
	if ( (u4SR & USART_SR_ORE ) == USART_SR_ORE ) 	err |= BIT_ORERR;
	if ( (u4SR & USART_SR_NE  ) == USART_SR_NE  ) 	err |= BIT_NzERR;
	if ( (u4SR & USART_SR_FE  ) == USART_SR_FE  ) 	err |= BIT_FrERR;
	if ( (u4SR & USART_SR_PE  ) == USART_SR_PE  ) 	err |= BIT_PaERR;
	if (err != 0x00) {
		// check Buffer FUll
		wp  = (wp_rxBuf + 1);
		wp &= MSK_POINTER;
		if (wp == rp_rxBuf) err |= BIT_BfERR;

		rxBuf[wp_rxBuf] = 0x00;
		reBuf[wp_rxBuf] = err;
		wp_rxBuf = wp;
	}

	// clear interrupt

}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： UART4 Port Initialization
 * 
 * @note： UART4 の端子設定
 * 
 * @param  none
 * @retval none
 * 
 */
void init_GPIO_UART4(void)
{
	GPIO_InitTypeDef	GPIO_InitStruct = {0};

	// Enable Port Clock
	__HAL_RCC_UART4_CLK_ENABLE();
	__HAL_RCC_GPIOC_CLK_ENABLE();

	/* Configure GPIO pins : Port-C -------------------------
		PC0  : CN60-38 = N.C.      : Input
		PC1  : CN60-36 = N.C.      : Input
		PC2  : CN60-35 = SPI2_MISO : Alernate Function-5
		PC3  : CN60-37 = SPI2_MOSI : Alernate Function-5
		PC4  : CN60-34 = N.C.      : Input
		PC5  : CN61-6  = N.C.      : Input
		PC6  : CN61-4  = MKY_RESET : Output
		PC7  : CN60-19 = N.C.      : Input
		PC8  : CN60-2  = N.C.      : Input
		PC9  : CN60-1  = N.C.      : Input
		PC10 : CN60-1  = UART_TX   : Alernate Function-8
		PC11 : CN60-2  = UART_RX   : Alernate Function-8
		PC12 : CN60-3  = N.C.      : Input
		PC13 : CN60-23 = USER_Btn(Nucleo)  : Input
		PC14 : CN60-25 = OSC32_IN(Nucleo)  : Alernate Function-0(SYS)
		PC15 : CN60-27 = OSC32_OUT(Nucleo) : Alernate Function-0(SYS)
	 */
	GPIO_InitStruct.Pin   = (GPIO_PIN_10 | GPIO_PIN_11);
	GPIO_InitStruct.Mode  = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull  = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
	HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： UART4 Port Initialization
 * 
 * @note： UART4 通信パラメータ設定
 * 
 * @param  SCI_CONFIG *sciParam ：
 *                         Baud   = ボーレート
 *                         DtLen  = データ長
 *                         StLen  = ストップビット
 *                         Parity = パリティ
 *                         LFCode = 改行コード
 * @retval 0 = 正常終了
 * 
 */
short init_UART(SCI_CONFIG *sciParam)
{
	int          	i;
	__IO uint32_t	tmp;

	// check parameter
	if ( (sciParam->Baud < 0)             || (Baud230400 < sciParam->Baud)   )	return(ERR_CHKBARD);
	if ( (sciParam->DtLen != DATALen8)    && (sciParam->DtLen != DATALen9)   )	return(ERR_CHKDTLEN);
	if ( (sciParam->StLen != STOPLEN1)    && (sciParam->StLen != STOPLEN2)   )	return(ERR_CHKSPLEN);
	if ( (sciParam->Parity != PARITYNONE) && (sciParam->Parity != PARITYODD) && (sciParam->Parity != PARITYEVEN) )	return(ERR_CHKPARTY);
	if ( (sciParam->LFCode != LFC_LF)     && (sciParam->LFCode != LFC_CRLF)  && (sciParam->LFCode != LFC_CR)     )	return(ERR_CHKLFCOD);

	// clear buffer
	for (i=0; i<SZ_SIOBUF; i++) {
		txBuf[i] = 0x00;
		rxBuf[i] = 0x00;
	}
	wp_txBuf = 0x0001;
	rp_txBuf = 0x0000;
	wp_rxBuf = 0x0001;
	rp_rxBuf = 0x0000;
	gTxBusy  = 0x00;

	// 改行コード設定
	if      (sciParam->LFCode == LFC_LF)    {gLFcode = CHR_LF; gLFbuf[0] = CHR_LF; gLFbuf[1] = 0x00  ; gLFbuf[2] = 0x00 ; gLFbuf[3] = 0x00 ;}
	else if (sciParam->LFCode == LFC_CRLF)  {gLFcode = CHR_LF; gLFbuf[0] = CHR_CR; gLFbuf[1] = CHR_LF; gLFbuf[2] = 0x00 ; gLFbuf[3] = 0x00 ;}
	else if (sciParam->LFCode == LFC_CR)    {gLFcode = CHR_CR; gLFbuf[0] = CHR_CR; gLFbuf[1] = 0x00  ; gLFbuf[2] = 0x00 ; gLFbuf[3] = 0x00 ;}

	// Stop USART4
	UART4->CR1 = 0;

	/*Configure GPIO pin : PC10=Tx, PC11=Rx */
	{
		//
		SET_BIT(  RCC->APB1RSTR, RCC_APB1RSTR_UART4RST);	// Reset
		CLEAR_BIT(RCC->APB1RSTR, RCC_APB1RSTR_UART4RST);	// Release Reset

		// GPIO Ports Clock Enable
		SET_BIT(RCC->APB1ENR,   RCC_APB1ENR_UART4EN);
		SET_BIT(RCC->APB1LPENR, RCC_APB1LPENR_UART4LPEN);
//		CLEAR_BIT(RCC->DCKCFGR1, RCC_DCKCFGR1_UART4SEL);	// 00 -> Use APB2Clock(PCLK2)


		// set Interrupt Priority
		HAL_NVIC_SetPriority(UART4_IRQn, 1, 0);
		// Enable Interrupt
		HAL_NVIC_EnableIRQ(UART4_IRQn);
	}

	// Set Baudrate
	// UART4.BRR(USART4_BASE + 0x08):RW
	//     Bit-31:16 :
	//     Bit-15: 4 : DIV_Manyissal[11:0]
	//     Bit- 3: 0 : DIV_Fraction[3:0]
	//  Baud = fck / ( 8 * (2 - OVER8) * USARTDIV)
	//    OVER8 = 0
	{
		uint32_t	freq1;

		// get current PCLK1 (= 42MHz)
		freq1 = HAL_RCC_GetPCLK1Freq();
		UART4->BRR = UART_BRR_SAMPLING16(freq1, sciParam->Baud);

	}

	// Setup CR3
	//  USART4.CR3(USART4_BASE + 0x14):RW
	//      Bit-31:12 :
	//      Bit-11    : ONEBIT: One sample bit method enable
	//      Bit-10    : CTSIE : CTS interrupt enable
	//      Bit- 9    : CTSE  : CTS enable
	//      Bit- 8    : RTSE  : RTS enable
	//      Bit- 7    : DMAT  : DMA enable transmitter
	//      Bit- 6    : DMAR  : DMA enable receiver
	//      Bit- 5    : SCEN  : Smartcard mode enable
	//      Bit- 4    : NACK  : Smartcard NACK enable
	//      Bit- 3    : HDSEL : Half-duplex selection
	//      Bit- 2    : IRLP  : IrDA low-power
	//      Bit- 1    : IREN  : IrDA mode enable
	//      Bit- 0    : EIE   : Error interrupt enable
	tmp =  0x0UL
	    // | USART_CR3_ONEBIT // One sample bit method enable    -> 3bit Sample
	    // | USART_CR3_CTSIE  // CTS Interrupt Enable            -> CTS interrupt disable
	    // | USART_CR3_CTSE   // CTS Enable                      -> CTS disable
	    // | USART_CR3_RTSE   // RTS Enable                      -> RTS disable
	    // | USART_CR3_DMAT   // DMA Enable Transmitter          -> DMA sisable
	    // | USART_CR3_DMAR   // DMA Enable Receiver             -> DMA sisable
	    // | USART_CR3_SCEN   // SmartCard mode enable           -> No Smart Card Mode
	    // | USART_CR3_NACK   // SmartCard NACK enable           ->
	    // | USART_CR3_HDSEL  // Half-Duplex Selection           -> No Single Wire
	    // | USART_CR3_IRLP   // IrDA Low-Power                  ->
	    // | USART_CR3_IREN   // IrDA mode Enable                -> No IrDA
	    | USART_CR3_EIE    // Error Interrupt Enable             -> Enable Error interrupt
	;
	UART4->CR3 = tmp;

	// Setup CR2
	//   USART4.CR2(USART4_BASE + 0x10):RW
	//       Bit-31:15 :
	//       Bit-14    : LINEN : LIN mode enable
	//       Bit-13:12 : STOP  : STOP bits[1:0]
	//       Bit-11    : CLKEN : Clock enable
	//       Bit-10    : CPOL  : Clock polarity
	//       Bit- 9    : CPHA  : Clock phase
	//       Bit- 8    : LBCL  : Last bit clock pulse
	//       Bit- 7    :
	//       Bit- 6    : LBDIE : LIN break detection interrupt enable
	//       Bit- 5    : LBDL  : LIN break detection length
	//       Bit- 4    : ADDM7 : 7-bit Address Detection/4-bit Address Detection
	//       Bit- 3: 0 :
	if      (sciParam->StLen == STOPLEN1)    tmp = (0x0UL  << USART_CR2_STOP_Pos);
	else if (sciParam->StLen == STOPLEN2)    tmp = (0x2UL  << USART_CR2_STOP_Pos);
	tmp |=  0x0UL
	     // | USART_CR2_LINEN    // LIN mode enable                        -> Disabke LIN mode
	     // | (0x0UL  << USART_CR2_STOP_Pos)     // STOP[1:0] bits (STOP bits)
	     // | USART_CR2_CLKEN    // Clock Enable                           -> not use CKE
	     // | USART_CR2_CPOL     // Clock Polarity                         -> not use CKE
	     // | USART_CR2_CPHA     // Clock Phase                            -> not use CKE
	     // | USART_CR2_LBCL     // Last Bit Clock pulse                   -> not use CKE
	     // | USART_CR2_LBDIE    // LIN Break Detection Interrupt Enable   -> Disabke LIN mode
	     // | USART_CR2_LBDL     // LIN Break Detection Length             -> Disabke LIN mode
	     // | USART_CR2_ADDM7    // 7-bit or 4-bit Address Detection
	;
	UART4->CR2 = tmp;

	// Setup CR1
	//    USART4.CR1(USART4_BASE + 0x0C):RW
	//        Bit-31:16 :
	//        Bit-15    : OVER8 : Oversampling mode
	//        Bit-14    :
	//        Bit-13    : MME   : Mute mode enable
	//        Bit-12    : M0    : Word length[0]
	//        Bit-11    : WAKE  : Receiver wakeup method
	//        Bit-10    : PCE   : Parity control enable
	//        Bit- 9    : PS    : Parity selection
	//        Bit- 8    : PEIE  : PE interrupt enable
	//        Bit- 7    : TXEIE : interrupt enable
	//        Bit- 6    : TCIE  : Transmission complete interrupt enable
	//        Bit- 5    : RXNEIE: RXNE interrupt enable
	//        Bit- 4    : IDLEIE: IDLE interrupt enable
	//        Bit- 3    : TE    : Transmitter enable
	//        Bit- 2    : RE    : Receiver enable
	//        Bit- 1    :
	//        Bit- 0    : UE    : USART enable
	tmp = 0x0UL
	     // | USART_CR1_OVER8    // Oversampling by 8-bit or 16-bit mode        -> 16 over sampling
	     // | USART_CR1_MME      // Mute Mode Enable                            -> no mute mode
	     // | USART_CR1_WAKE     // Receiver Wakeup method                      -> idle line
	     | USART_CR1_PEIE     // PE Interrupt Enable                            -> PE interrupt
	     // | USART_CR1_TXEIE    // TXE Interrupt Enable                           -> TXE interrupt
	     // | USART_CR1_TCIE     // Transmission Complete Interrupt Enable      -> no TC interrupt
	     | USART_CR1_RXNEIE   // RXNE Interrupt Enable                          -> RXE interrupt
	     // | USART_CR1_IDLEIE   // IDLE Interrupt Enable                       -> no IDLE interrupt
	     // | USART_CR1_TE       // Transmitter Enable                             -> Tx Start
	     | USART_CR1_RE       // Receiver Enable                                -> Rx Start
	     | USART_CR1_UE       // USART Enable                                   -> Module Enable
	;

	// M1 M0
	//  0  0   DataBit = 8
	//  0  1   DataBit = 9
	//  1  0   DataBit = 7
	//  1  1
	if      (sciParam->DtLen == DATALen9)    tmp |= USART_CR1_M;
	else if (sciParam->DtLen == DATALen8)    tmp |= 0x0UL;

	if      (sciParam->Parity == PARITYNONE) tmp |= (0x0UL         );
	else if (sciParam->Parity == PARITYODD)  tmp |= (USART_CR1_PCE | USART_CR1_PS);
	else if (sciParam->Parity == PARITYEVEN) tmp |= (USART_CR1_PCE );

	UART4->CR1 = tmp;


	return(0);
}


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： Print 1 charactor
 * 
 * @note： １文字送信
 * 
 * @param  char c ： 送信文字
 * @retval 0 = 正常終了
 * 
 */
short print_char(char c)
{
	unsigned short	wp;
	unsigned short	rp;
	unsigned char	wd;
	__IO uint32_t	u4SR;

	do {
		// if ( (wp_txBuf+1) == rp_txBuf) return(ERR_FULLTXBUF);
		wp  = (wp_txBuf + 1);
		wp &= MSK_POINTER;

	} while (wp == rp_txBuf);

	txBuf[wp_txBuf] = c;
	wp_txBuf++;
	wp_txBuf &= MSK_POINTER;

	u4SR = UART4->SR;
	if ((u4SR & USART_SR_TXE) == USART_SR_TXE) {
		// ready
		if (gTxBusy == 0){
			// load send data
			rp  = (rp_txBuf + 1);
			rp &= MSK_POINTER;
			wd = txBuf[rp];
			rp_txBuf = rp;

			// send
			// sci_putc(wd);
			UART4->DR = (__IO uint32_t)wd;
			UART4->CR1 |= (USART_CR1_TXEIE | USART_CR1_TE);
			gTxBusy = 1;
		}
	} else {
		//busy
	}

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： Print strings
 * 
 * @note： 文字列送信
 * 
 * @param  char *str ： 送信文字列
 * @retval 0 = 正常終了
 * 
 */
short print_string(char *str)
{
	unsigned short	wp;
	unsigned short	rp;
	unsigned char	wd;
	__IO uint32_t	u4SR;


	// output data
	while(*str) {
		// if ( (wp_txBuf+1) == rp_txBuf) return(ERR_FULLTXBUF);
		do {
			wp  = (wp_txBuf + 1);
			wp &= MSK_POINTER;

		} while (wp == rp_txBuf);
		txBuf[wp_txBuf] = *str++;
		wp_txBuf++;
		wp_txBuf &= MSK_POINTER;

	}

	u4SR = UART4->SR;
	if ((u4SR & USART_SR_TXE) == USART_SR_TXE) {
		// ready
		if (gTxBusy == 0){
			// load send data
			rp  = (rp_txBuf + 1);
			rp &= MSK_POINTER;
			wd = txBuf[rp];
			rp_txBuf = rp;

			// send
			// sci_putc(wd);
			UART4->DR = (__IO uint32_t)wd;
			UART4->CR1 |= (USART_CR1_TXEIE | USART_CR1_TE);
			gTxBusy = 1;
		}
	} else {
		//busy
	}

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： TxDone
 * 
 * @note： 割込み処理中、送信完了時の処理
 * 
 * @param  none
 * @retval none
 * 
 */
static void sci_txdone(void)
{
	unsigned char	wd;
	unsigned short	rp;

	// check pointer
	rp  = (rp_txBuf + 1);
	rp &= MSK_POINTER;
	if ( rp == wp_txBuf ) {
		gTxBusy = 0;
		UART4->CR1 &= ~(USART_CR1_TXEIE | USART_CR1_TE);
		return;
	}

	// load send data
	wd = txBuf[rp];
	rp_txBuf = rp;

	// send
	sci_putc(wd);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： RxDone
 * 
 * @note： 割込み処理中、受信完了時の処理
 * 
 * @param  none
 * @retval none
 * 
 */
static void sci_rxdone(void)
{
	unsigned char	rd;
	unsigned short	wp;

	// Read received data
	rd = (unsigned char)UART4->DR;

	rxBuf[wp_rxBuf] = rd;

	// check Buffer FUll
	wp  = (wp_rxBuf + 1);
	wp &= MSK_POINTER;
	if (wp == rp_rxBuf) {
		reBuf[wp_rxBuf] = BIT_BfERR;
	} else {
		reBuf[wp_rxBuf] = 0x00;
	}
	wp_rxBuf = wp;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： Data set to TxBuffer
 * 
 * @note： 送信バッファへ書き込み
 * 
 * @param  unsigned char data ： 送信文字
 * @retval none
 * 
 */
static void sci_putc(unsigned char data)
{
	__IO uint32_t	u4SR;

	// check TxEmpty
	do {
		u4SR = UART4->SR;
	} while ( (u4SR & USART_SR_TXE) != USART_SR_TXE);

	// write data to Tx
	// USART4.TDR(USART4_BASE + 0x28):RW
	//     Bit-31: 9 :
	//     Bit- 8: 0 : TDR   : Transmit data value[8:0]
	//         SC3TB				Bit[7:0]
	UART4->DR = (__IO uint32_t)data;

}


