#include "bsp_usart.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_rcc.h"
#include "misc.h"

#include "stdio.h"
#include "public_diy.h"
#include "bsp_usart.h"

/* 串口设备结构体 */
typedef struct
{
	USART_TypeDef *uart;		/* STM32内部串口设备指针 */
	uint8_t *pTxBuf;			/* 发送缓冲区 */
	uint8_t *pRxBuf;			/* 接收缓冲区 */
	uint16_t usTxBufSize;		/* 发送缓冲区大小 */
	uint16_t usRxBufSize;		/* 接收缓冲区大小 */
	uint16_t usTxWrite;			/* 发送缓冲区写指针 */
	uint16_t usTxRead;			/* 发送缓冲区读指针 */
	uint16_t usTxCount;			/* 等待发送的数据个数 */

	uint16_t usRxWrite;			/* 接收缓冲区写指针 */
	uint16_t usRxRead;			/* 接收缓冲区读指针 */
	uint16_t usRxCount;			/* 还未读取的新数据个数 */

	void (*SendBefor)(void); 	/* 开始发送之前的回调函数指针（主要用于RS485切换到发送模式） */
	void (*SendOver)(void); 	/* 发送完毕的回调函数指针（主要用于RS485将发送模式切换为接收模式） */
	void (*ReciveNew)(uint8_t _byte);	/* 串口收到数据的回调函数指针 */
}UART_T;

/* 定义每个串口结构体变量 */
#if UART1_FIFO_EN == 1
	static UART_T g_tUart1;
	static uint8_t g_TxBuf1[UART1_TX_BUF_SIZE];		/* 发送缓冲区 */
	static uint8_t g_RxBuf1[UART1_RX_BUF_SIZE];		/* 接收缓冲区 */
#endif

#if UART2_FIFO_EN == 1
	static UART_T g_tUart2;
	static uint8_t g_TxBuf2[UART2_TX_BUF_SIZE];		/* 发送缓冲区 */
	static uint8_t g_RxBuf2[UART2_RX_BUF_SIZE];		/* 接收缓冲区 */
#endif

#if UART3_FIFO_EN == 1
	static UART_T g_tUart3;
	static uint8_t g_TxBuf3[UART3_TX_BUF_SIZE];		/* 发送缓冲区 */
	static uint8_t g_RxBuf3[UART3_RX_BUF_SIZE];		/* 接收缓冲区 */
#endif

/* debug log专用的usart外设 */
#define USART_DEBUG_TYPE    (USART3)   
#define USART_DEBUG_PERIPH_RCC  (RCC_APB1Periph_USART3)
#define USART_DEBUG_GPIO_RCC  (RCC_APB2Periph_GPIOB)
#define USART_DEBUG_TX_PORT (GPIOB)
#define USART_DEBUG_TX_PIN  (GPIO_Pin_10)
#define USART_DEBUG_RX_PORT (GPIOB)
#define USART_DEBUG_RX_PIN  (GPIO_Pin_11)
#define USART_DEBUG_BAUD    (115200u)
/* ir controller 专用 usart外设 */
#define USART_IRCONTROLLER_TYPE    (USART2)   
#define USART_IRCONTROLLER_PERIPH_RCC  (RCC_APB1Periph_USART2)
#define USART_IRCONTROLLER_GPIO_RCC  (RCC_APB2Periph_GPIOB)
#define USART_IRCONTROLLER_TX_PORT (GPIOA)
#define USART_IRCONTROLLER_TX_PIN  (GPIO_Pin_2)
#define USART_IRCONTROLLER_RX_PORT (GPIOA)
#define USART_IRCONTROLLER_RX_PIN  (GPIO_Pin_3)
#define USART_IRCONTROLLER_BAUD    (9600u)






#define EN_USART1_RX        (0)


/*************************************************************************************
 * @brief    usart全局的变量初始化，必须第一个先使用这个注册一下
 * 
 * @warning  【不可重入,阻塞等警告】
 * @note     【重大修改】
  *************************************************************************************/
void bsp_usartTotalInit(void)
{
#if UART1_FIFO_EN == 1
	g_tUart1.uart = USART1;						/* STM32 串口设备 */
	g_tUart1.pTxBuf = g_TxBuf1;					/* 发送缓冲区指针 */
	g_tUart1.pRxBuf = g_RxBuf1;					/* 接收缓冲区指针 */
	g_tUart1.usTxBufSize = UART1_TX_BUF_SIZE;	/* 发送缓冲区大小 */
	g_tUart1.usRxBufSize = UART1_RX_BUF_SIZE;	/* 接收缓冲区大小 */
	g_tUart1.usTxWrite = 0;						/* 发送FIFO写索引 */
	g_tUart1.usTxRead = 0;						/* 发送FIFO读索引 */
	g_tUart1.usRxWrite = 0;						/* 接收FIFO写索引 */
	g_tUart1.usRxRead = 0;						/* 接收FIFO读索引 */
	g_tUart1.usRxCount = 0;						/* 接收到的新数据个数 */
	g_tUart1.usTxCount = 0;						/* 待发送的数据个数 */
	g_tUart1.SendBefor = 0;						/* 发送数据前的回调函数 */
	g_tUart1.SendOver = 0;						/* 发送完毕后的回调函数 */
	g_tUart1.ReciveNew = 0;						/* 接收到新数据后的回调函数 */
#endif

#if UART2_FIFO_EN == 1
	g_tUart2.uart = USART2;						/* STM32 串口设备 */
	g_tUart2.pTxBuf = g_TxBuf2;					/* 发送缓冲区指针 */
	g_tUart2.pRxBuf = g_RxBuf2;					/* 接收缓冲区指针 */
	g_tUart2.usTxBufSize = UART2_TX_BUF_SIZE;	/* 发送缓冲区大小 */
	g_tUart2.usRxBufSize = UART2_RX_BUF_SIZE;	/* 接收缓冲区大小 */
	g_tUart2.usTxWrite = 0;						/* 发送FIFO写索引 */
	g_tUart2.usTxRead = 0;						/* 发送FIFO读索引 */
	g_tUart2.usRxWrite = 0;						/* 接收FIFO写索引 */
	g_tUart2.usRxRead = 0;						/* 接收FIFO读索引 */
	g_tUart2.usRxCount = 0;						/* 接收到的新数据个数 */
	g_tUart2.usTxCount = 0;						/* 待发送的数据个数 */
	g_tUart2.SendBefor = 0;						/* 发送数据前的回调函数 */
	g_tUart2.SendOver = 0;						/* 发送完毕后的回调函数 */
	g_tUart2.ReciveNew = 0;						/* 接收到新数据后的回调函数 */
#endif

#if UART3_FIFO_EN == 1
	g_tUart3.uart = USART3;						/* STM32 串口设备 */
	g_tUart3.pTxBuf = g_TxBuf3;					/* 发送缓冲区指针 */
	g_tUart3.pRxBuf = g_RxBuf3;					/* 接收缓冲区指针 */
	g_tUart3.usTxBufSize = UART3_TX_BUF_SIZE;	/* 发送缓冲区大小 */
	g_tUart3.usRxBufSize = UART3_RX_BUF_SIZE;	/* 接收缓冲区大小 */
	g_tUart3.usTxWrite = 0;						/* 发送FIFO写索引 */
	g_tUart3.usTxRead = 0;						/* 发送FIFO读索引 */
	g_tUart3.usRxWrite = 0;						/* 接收FIFO写索引 */
	g_tUart3.usRxRead = 0;						/* 接收FIFO读索引 */
	g_tUart3.usRxCount = 0;						/* 接收到的新数据个数 */
	g_tUart3.usTxCount = 0;						/* 待发送的数据个数 */
	g_tUart3.SendBefor = RS485_SendBefor;		/* 发送数据前的回调函数 */
	g_tUart3.SendOver = RS485_SendOver;			/* 发送完毕后的回调函数 */
	g_tUart3.ReciveNew = RS485_ReciveNew;		/* 接收到新数据后的回调函数 */
#endif
}

/**********************************************************************************
  * @brief  用于debug的串口初始化
  * @param  baud
  * @note  
  * @retval None
  *********************************************************************************
*/
void bsp_usart_debug_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
//    NVIC_InitTypeDef NVIC_InitStructure;

    /* 1. 串口时钟使能，GPIO 时钟使能，复用时钟使能 */
    //使能 USART的GPIO 时钟
    RCC_APB2PeriphClockCmd(USART_DEBUG_GPIO_RCC, ENABLE);
    // usart1在APB2上 ，而usart2和usart3在APB1上
    RCC_APB1PeriphClockCmd(USART_DEBUG_PERIPH_RCC, ENABLE);
    /* 2. 串口复位 */
    USART_DeInit(USART_DEBUG_TYPE);
    /* 3. GPIO 端口模式设置*/
    // Usart TX
    GPIO_InitStructure.GPIO_Pin = USART_DEBUG_TX_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     // 复用推挽输出
    GPIO_Init(USART_DEBUG_TX_PORT, &GPIO_InitStructure);
    // Usart RX
    GPIO_InitStructure.GPIO_Pin = USART_DEBUG_RX_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
    GPIO_Init(USART_DEBUG_RX_PORT, &GPIO_InitStructure);
    /* 4. 串口参数初始化 */
    USART_InitStructure.USART_BaudRate = USART_DEBUG_BAUD;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
    USART_Init(USART_DEBUG_TYPE, &USART_InitStructure);

    /* 5. 使能串口 */
    USART_Cmd(USART_DEBUG_TYPE, ENABLE);
}
/*************************************************************************************
 * @brief    ir controller 红外遥控器专用初始化程序
 * 
 * @warning  【不可重入,阻塞等警告】
 * @note     【重大修改】
  *************************************************************************************/
void bsp_usart_IrController_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* 1. 串口时钟使能，GPIO 时钟使能，复用时钟使能 */
    //使能 USART的GPIO 时钟
    RCC_APB2PeriphClockCmd(USART_IRCONTROLLER_GPIO_RCC, ENABLE);
    // usart1在APB2上 ，而usart2和usart3在APB1上
    RCC_APB1PeriphClockCmd(USART_IRCONTROLLER_PERIPH_RCC, ENABLE);
    /* 2. 串口复位 */
    USART_DeInit(USART_IRCONTROLLER_TYPE);
    /* 3. GPIO 端口模式设置*/
    // Usart TX
    GPIO_InitStructure.GPIO_Pin = USART_IRCONTROLLER_TX_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     // 复用推挽输出
    GPIO_Init(USART_IRCONTROLLER_TX_PORT, &GPIO_InitStructure);
    // Usart RX
    GPIO_InitStructure.GPIO_Pin = USART_IRCONTROLLER_RX_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
    GPIO_Init(USART_IRCONTROLLER_RX_PORT, &GPIO_InitStructure);
    /* 4. 串口参数初始化 */
    USART_InitStructure.USART_BaudRate = USART_IRCONTROLLER_BAUD;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
    USART_Init(USART_IRCONTROLLER_TYPE, &USART_InitStructure);
    /* 5. 使能接收中断 */
    USART_ITConfig(USART_IRCONTROLLER_TYPE, USART_IT_RXNE, ENABLE);	

    /* Enable the USARTx Interrupt */
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
    /* 5. 使能串口 */
    USART_Cmd(USART_IRCONTROLLER_TYPE, ENABLE);
}

// void bsp_usart

/*************************************************************************************
 * @brief    enum转换为内部的uart指针 并返回指针
 * @param[in/out] e_com【参数注释】
 * @return UART_T*     【返回值注释】
 * 
 * @warning  【不可重入,阻塞等警告】
 * @note     【重大修改】
  *************************************************************************************/
static UART_T * bsp_usart_ComToUART(com_type_ENUM e_com)
{
	UART_T* retVal = 0;
	
	if(e_com == COM_DEBUG)
	{
		#if UART1_FIFO_EN == 1
			retVal = &g_tUart1;
		#endif
	}
	else if(e_com == COM_IR_CONTROLLER)
	{
		#if UART2_FIFO_EN == 1
			retVal = &g_tUart2;
		#endif
	}
	else if(e_com == COM_RECV1)
	{
		#if UART3_FIFO_EN == 1 
			retVal = &g_tUart3;
		#endif
	}
	return retVal;
}
/*************************************************************************************
 * @brief    获取特定串口所收到的值
 * @param[in/out] e_com【参数注释】
 * @return uint8_t     【返回值注释】
 * 
 * @warning  【不可重入,阻塞等警告】
 * @note     【重大修改】
  *************************************************************************************/
uint8_t bsp_usart_GetComChar(com_type_ENUM e_com, uint8_t *recv_uc)
{
	UART_T * tmp_ptr_uart;
	uint8_t retVal = RET_ERROR;
	uint16_t usCount;

	tmp_ptr_uart = bsp_usart_ComToUART(e_com);
	// 非空指针
	if(tmp_ptr_uart != 0)
	{
		/* usRxCount 变量在中断函数中被改写，主程序读取该变量时，必须进行临界区保护 */
		DISABLE_INT();
		usCount = tmp_ptr_uart->usRxCount;
		ENABLE_INT();

		// 有收到数据
		if(usCount != 0)
		{
			// 取数据
			*recv_uc = tmp_ptr_uart->pRxBuf[tmp_ptr_uart->usRxRead];
			// 更新队列
			/* 改写FIFO读索引 */
			DISABLE_INT();
			if (++tmp_ptr_uart->usRxRead >= tmp_ptr_uart->usRxBufSize)
			{
				tmp_ptr_uart->usRxRead = 0;
			}
			tmp_ptr_uart->usRxCount--;
			ENABLE_INT();

			retVal = RET_OK;
		}
		else
		{
			retVal = RET_ERROR;
		}
	}
	else
	{
		retVal = RET_ERROR;
	}

	// 返回值
	return retVal;
}

/*************************************************************************************
 * @brief    判断串口是否接收到数据
 * @param[in/out] e_com【参数注释】
 * @return uint8_t     【返回值注释】
 * 
 * @warning  【不可重入,阻塞等警告】
 * @note     【重大修改】
  *************************************************************************************/
uint8_t bsp_usart_IsComRecvChar(com_type_ENUM e_com)
{
	UART_T * tmp_ptr_uart;
	uint8_t retVal = FALSE;
	uint16_t usCount;
	tmp_ptr_uart = bsp_usart_ComToUART(e_com);

	/* usRxCount 变量在中断函数中被改写，主程序读取该变量时，必须进行临界区保护 */
	DISABLE_INT();
	usCount = tmp_ptr_uart->usRxCount;
	ENABLE_INT();
	if(tmp_ptr_uart != NULL)
	{
		
		if(usCount != 0)
		{
			retVal = TRUE;
		}
	}
	return retVal;
}
/*
*********************************************************************************************************
*	函 数 名: UartIRQ
*	功能说明: 供中断服务程序调用，通用串口中断处理函数
*	形    参: _pUart : 串口设备
*	返 回 值: 无
*********************************************************************************************************
*/
static void UartIRQ(UART_T *_pUart)
{
	/* 处理接收中断  */
	if (USART_GetITStatus(_pUart->uart, USART_IT_RXNE) != RESET)
	{
		/* 从串口接收数据寄存器读取数据存放到接收FIFO */
		uint8_t ch;

		ch = USART_ReceiveData(_pUart->uart);
		_pUart->pRxBuf[_pUart->usRxWrite] = ch;
		if (++_pUart->usRxWrite >= _pUart->usRxBufSize)
		{
			_pUart->usRxWrite = 0;
		}
		if (_pUart->usRxCount < _pUart->usRxBufSize)
		{
			_pUart->usRxCount++;
		}

		/* 回调函数,通知应用程序收到新数据,一般是发送1个消息或者设置一个标记 */
		//if (_pUart->usRxWrite == _pUart->usRxRead)
		//if (_pUart->usRxCount == 1)
		{
			if (_pUart->ReciveNew)
			{
				_pUart->ReciveNew(ch);
			}
		}
	}

	/* 处理发送缓冲区空中断 */
	if (USART_GetITStatus(_pUart->uart, USART_IT_TXE) != RESET)
	{
		//if (_pUart->usTxRead == _pUart->usTxWrite)
		if (_pUart->usTxCount == 0)
		{
			/* 发送缓冲区的数据已取完时， 禁止发送缓冲区空中断 （注意：此时最后1个数据还未真正发送完毕）*/
			USART_ITConfig(_pUart->uart, USART_IT_TXE, DISABLE);

			/* 使能数据发送完毕中断 */
			USART_ITConfig(_pUart->uart, USART_IT_TC, ENABLE);
		}
		else
		{
			/* 从发送FIFO取1个字节写入串口发送数据寄存器 */
			USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);
			if (++_pUart->usTxRead >= _pUart->usTxBufSize)
			{
				_pUart->usTxRead = 0;
			}
			_pUart->usTxCount--;
		}

	}
	/* 数据bit位全部发送完毕的中断 */
	else if (USART_GetITStatus(_pUart->uart, USART_IT_TC) != RESET)
	{
		//if (_pUart->usTxRead == _pUart->usTxWrite)
		if (_pUart->usTxCount == 0)
		{
			/* 如果发送FIFO的数据全部发送完毕，禁止数据发送完毕中断 */
			USART_ITConfig(_pUart->uart, USART_IT_TC, DISABLE);

			/* 回调函数, 一般用来处理RS485通信，将RS485芯片设置为接收模式，避免抢占总线 */
			if (_pUart->SendOver)
			{
				_pUart->SendOver();
			}
		}
		else
		{
			/* 正常情况下，不会进入此分支 */

			/* 如果发送FIFO的数据还未完毕，则从发送FIFO取1个数据写入发送数据寄存器 */
			USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);
			if (++_pUart->usTxRead >= _pUart->usTxBufSize)
			{
				_pUart->usTxRead = 0;
			}
			_pUart->usTxCount--;
		}
	}
}


// uint8_t bsp_usart_GetComChar(com_type_ENUM e_com, uint8_t *recv_uc)
// uint8_t 

// //加入以下代码,支持 printf 函数,而不需要选择 use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数 
struct __FILE 
{ 
    int handle;
}; 
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式 
void _sys_exit(int x) 
{ 
    x = x; 
}
/*************************************************************************************
 * @brief    重定义 fputc 函数
 * @param[in/out] ch   【参数注释】
 * @param[in/out] f    【参数注释】
 * @return int         【返回值注释】
 * 
 * @warning  【不可重入,阻塞等警告】
 * @note     【重大修改】
  *************************************************************************************/
int fputc(int ch, FILE *f)
{ 
    while(USART_GetFlagStatus(USART_DEBUG_TYPE,USART_FLAG_TC)==RESET); 
    USART_SendData(USART_DEBUG_TYPE,(uint8_t)ch); 
    return ch;
}

#endif


#if UART2_FIFO_EN == 1
void USART2_IRQHandler(void)
{
	UartIRQ(&g_tUart2);
}

#endif
