blob: 785ea402168a2c974ada42e70e3fd202b263e2b8 [file] [log] [blame]
/******************************************************************************
* @file uart.c
*
* @brief for TLSR chips
*
* @author public@telink-semi.com;
* @date Sep. 30, 2010
*
* @attention
*
* Copyright (C) 2019-2020 Telink Semiconductor (Shanghai) Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*****************************************************************************/
#include "uart.h"
#include "gpio.h"
#include "compiler.h"
/**
* @brief This function is used to look for the prime.if the prime is finded,it will
* return 1, or return 0.
* @param[in] the value to judge
* @return none
*/
static unsigned char IsPrime(unsigned int n)
{
unsigned int i = 5;
if(n <= 3){
return 1; //although n is prime, but the bwpc must be larger than 2.
}
else if((n %2 == 0) || (n % 3 == 0)){
return 0;
}
else{
for(i=5;i*i<n;i+=6){
if((n % i == 0)||(n %(i+2))==0){
return 0;
}
}
return 1;
}
}
/**
* @brief This function serves to calculate the best bwpc(bit width) .i.e reg0x96
* @param[in] baut_rate:The value of the baut rate to set.
* @param[in] tmp_sysclk:The system clock
* algorithm: BaudRate*(div+1)*(bwpc+1)=system clock
* simplify the expression: div*bwpc = constant(z)
* bwpc range from 3 to 15.so loop and
* get the minimum one decimal point
* @return the position of getting the minimum value
*/
static unsigned char g_bwpc = 0;
static unsigned int g_uart_div = 0;
static void GetBetterBwpc(unsigned int baut_rate,unsigned int tmp_sysclk )
{
unsigned char i = 0, j= 0;
unsigned int primeInt = 0;
unsigned char primeDec = 0;
unsigned int D_intdec[13],D_int[13];
unsigned char D_dec[13];
primeInt = tmp_sysclk/baut_rate;
primeDec = 10*tmp_sysclk/baut_rate - 10*primeInt;
/************************************************************
* calculate the primeInt and check whether primeInt is prime.
*/
if(IsPrime(primeInt)){ // primeInt is prime
primeInt += 1; //+1 must be not prime. and primeInt must be larger than 2.
}
else{
if(primeDec > 5){ // >5
primeInt += 1;
if(IsPrime(primeInt)){
primeInt -= 1;
}
}
}
/*******************************************
* get the best division value and bit width
*/
for(i=3;i<=15;i++){
D_intdec[i-3] = (10*primeInt)/(i+1);////get the LSB
D_dec[i-3] = D_intdec[i-3] - 10*(D_intdec[i-3]/10);///get the decimal section
D_int[i-3] = D_intdec[i-3]/10;///get the integer section
}
//find the max and min one decimation point
unsigned char position_min = 0,position_max = 0;
unsigned int min = 0xffffffff,max = 0x00;
for(j=0;j<13;j++){
if((D_dec[j] <= min)&&(D_int[j] != 0x01)){
min = D_dec[j];
position_min = j;
}
if(D_dec[j]>=max){
max = D_dec[j];
position_max = j;
}
}
if((D_dec[position_min]<5) && (D_dec[position_max]>=5)){
if(D_dec[position_min]<(10-D_dec[position_max])){
g_bwpc = position_min + 3;
g_uart_div = D_int[position_min]-1;
}
else{
g_bwpc = position_max + 3;
g_uart_div = D_int[position_max];
}
}
else if((D_dec[position_min]<5) && (D_dec[position_max]<5)){
g_bwpc = position_min + 3;
g_uart_div = D_int[position_min] - 1;
}
else{
g_bwpc = position_max + 3;
g_uart_div = D_int[position_max];
}
}
/**
* @brief This function initializes the UART module.
* @param[in] g_uart_div - uart clock divider
* @param[in] g_bwpc - bitwidth, should be set to larger than
*
* sys_clk baud rate g_uart_div g_bwpc
*
* 16Mhz 9600 118 13
* 19200 118 6
* 115200 9 13
*
* 24Mhz 9600 249 9
* 19200 124 9
* 115200 12 15
*
* 32Mhz 9600 235 13
* 19200 235 6
* 115200 17 13
*
* 48Mhz 9600 499 9
* 19200 249 9
* 115200 25 15
*
* @param[in] Parity - selected parity type for UART interface
* @param[in] StopBit - selected length of stop bit for UART interface
* @return none
*/
void uart_init(unsigned short g_uart_div, unsigned char g_bwpc, UART_ParityTypeDef Parity, UART_StopBitTypeDef StopBit)
{
//GetBetterBwpc(BaudRate); //get the best bwpc and uart_div
reg_uart_ctrl0 = g_bwpc; //set bwpc
reg_uart_clk_div = (g_uart_div | FLD_UART_CLK_DIV_EN); //set div_clock
reg_uart_rx_timeout0 = (g_bwpc+1) * 12; //one byte includes 12 bits at most
reg_uart_rx_timeout1 = UART_BW_MUL2; //if over 2*(tmp_bwpc+1),one transaction end.
//parity config
if (Parity) {
reg_uart_ctrl1 |= FLD_UART_CTRL1_PARITY_EN; //enable parity function
if (PARITY_EVEN == Parity) {
reg_uart_ctrl1 &= (~FLD_UART_CTRL1_PARITY_POLARITY); //enable even parity
}
else if (PARITY_ODD == Parity) {
reg_uart_ctrl1 |= FLD_UART_CTRL1_PARITY_POLARITY; //enable odd parity
}
}
else {
reg_uart_ctrl1 &= (~FLD_UART_CTRL1_PARITY_EN); //disable parity function
}
//stop bit config
reg_uart_ctrl1 &= (~FLD_UART_CTRL1_STOP_BIT);
reg_uart_ctrl1 |= StopBit;
}
/**
* @brief This function initializes the UART module.
* @param[in] Baudrate - uart baud rate
* @param[in] System_clock - clock of system
* @param[in] Parity - selected parity type for UART interface
* @param[in] StopBit - selected length of stop bit for UART interface
* @return none
*/
void uart_init_baudrate(unsigned int Baudrate,unsigned int System_clock , UART_ParityTypeDef Parity, UART_StopBitTypeDef StopBit)
{
GetBetterBwpc(Baudrate,System_clock); //get the best bwpc and uart_div
reg_uart_ctrl0 = g_bwpc; //set bwpc
reg_uart_clk_div = (g_uart_div | FLD_UART_CLK_DIV_EN); //set div_clock
reg_uart_rx_timeout0 = (g_bwpc+1) * 12; //one byte includes 12 bits at most
reg_uart_rx_timeout1 = UART_BW_MUL2; //if over 2*(tmp_bwpc+1),one transaction end.
//parity config
if (Parity) {
reg_uart_ctrl1 |= FLD_UART_CTRL1_PARITY_EN; //enable parity function
if (PARITY_EVEN == Parity) {
reg_uart_ctrl1 &= (~FLD_UART_CTRL1_PARITY_POLARITY); //enable even parity
}
else if (PARITY_ODD == Parity) {
reg_uart_ctrl1 |= FLD_UART_CTRL1_PARITY_POLARITY; //enable odd parity
}
}
else {
reg_uart_ctrl1 &= (~FLD_UART_CTRL1_PARITY_EN); //disable parity function
}
//stop bit config
reg_uart_ctrl1 &= (~FLD_UART_CTRL1_STOP_BIT);
reg_uart_ctrl1 |= StopBit;
}
/**
* @brief enable uart DMA mode
* @param[in] none
* @return none
*/
void uart_dma_enable(unsigned char rx_dma_en, unsigned char tx_dma_en)
{
//enable DMA function of tx and rx
if(rx_dma_en){
reg_uart_ctrl0 |= FLD_UART_RX_DMA_EN ;
}else{
reg_uart_ctrl0 &= (~FLD_UART_RX_DMA_EN );
}
if(tx_dma_en){
reg_uart_ctrl0 |= FLD_UART_TX_DMA_EN;
}else{
reg_uart_ctrl0 &= (~FLD_UART_TX_DMA_EN);
}
}
/**
* @brief config the irq of uart tx and rx
* @param[in] rx_irq_en - 1:enable rx irq. 0:disable rx irq
* @param[in] tx_irq_en - 1:enable tx irq. 0:disable tx irq
* @return none
*/
void uart_irq_enable(unsigned char rx_irq_en, unsigned char tx_irq_en)
{
if(rx_irq_en){
reg_uart_ctrl0 |= FLD_UART_RX_IRQ_EN ;
}else{
reg_uart_ctrl0 &= (~FLD_UART_RX_IRQ_EN );
}
if(tx_irq_en){
reg_uart_ctrl0 |= FLD_UART_TX_IRQ_EN;
}else{
reg_uart_ctrl0 &= (~FLD_UART_TX_IRQ_EN);
}
if(tx_irq_en||rx_irq_en)
{
reg_irq_mask |= FLD_IRQ_UART_EN;
}
else
{
reg_irq_mask &= ~FLD_IRQ_UART_EN;
}
}
/**
* @brief config the number level setting the irq bit of status register 0x9d
* ie 0x9d[3].
* If the cnt register value(0x9c[0,3]) larger or equal than the value of 0x99[0,3]
* or the cnt register value(0x9c[4,7]) less or equal than the value of 0x99[4,7],
* it will set the irq bit of status register 0x9d, ie 0x9d[3]
* @param[in] rx_level - receive level value. ie 0x99[0,3]
* @param[in] tx_level - transmit level value.ie 0x99[4,7]
* @return none
*/
void uart_ndma_irq_triglevel(unsigned char rx_level, unsigned char tx_level)
{
reg_uart_ctrl3 = rx_level | (tx_level<<4);
}
/**
* @brief get the status of uart irq.
* @param[in] none
* @return 0: not uart irq ;
* not 0: indicate tx or rx irq
*/
unsigned char uart_ndmairq_get(void)
{
return (reg_uart_status0&FLD_UART_IRQ_FLAG );
}
unsigned char uart_TxIndex = 0;
/**
* @brief uart send data function with not DMA method.
* variable uart_TxIndex,it must cycle the four registers 0x90 0x91 0x92 0x93 for the design of SOC.
* so we need variable to remember the index.
* @param[in] uartData - the data to be send.
* @return none
*/
void uart_ndma_send_byte(unsigned char uartData)
{
while((reg_uart_buf_cnt>>4)>7);
reg_uart_data_buf(uart_TxIndex) = uartData;
uart_TxIndex++;
uart_TxIndex &= 0x03;// cycle the four register 0x90 0x91 0x92 0x93.
}
/**
* @brief uart send data function, this function tell the DMA to get data from the RAM and start
* the DMA transmission
* @param[in] Addr - pointer to the buffer containing data need to send
* @return 1: send success ;
* 0: DMA busy
*/
volatile unsigned char uart_dma_send(unsigned char* Addr)
{
if (reg_uart_status1 & FLD_UART_TX_DONE )
{
reg_dma1_addr = (unsigned short)((unsigned int)Addr); //packet data, start address is sendBuff+1
reg_dma_tx_rdy0 |= FLD_DMA_CHN_UART_TX;
return 1;
}
return 0;
}
/**
* @brief uart send data function, this function tell the DMA to get data from the RAM and start
* the DMA transmission
* @param[in] byte - single byte data need to send
* @return 1: send success ;
* 0: DMA busy
*/
volatile unsigned char uart_send_byte(unsigned char byte)
{
unsigned int addr;
unsigned char b[5] = {1, 0,0,0,0};
addr = (unsigned int)b;
b[4] = byte;
if (reg_uart_status1 & FLD_UART_TX_DONE ) {
reg_dma1_addr = addr; //packet data, start address is sendBuff+1
reg_dma1_addrHi = ((addr>>16)&0xff);
reg_dma_tx_rdy0 = FLD_DMA_CHN1;
return 1;
}
return 0;
}
/**
* @brief data receive buffer initiate function. DMA would move received uart data to the address space,
* uart packet length needs to be no larger than (recBuffLen - 4).
* @param[in] RecvAddr - pointer to the receiving buffer
* @param[in] RecvBufLen - length in byte of the receiving buffer
* @return none
*/
void uart_recbuff_init(unsigned char *RecvAddr, unsigned short RecvBufLen)
{
unsigned char bufLen;
unsigned int addr;
addr = (unsigned int) RecvAddr;
bufLen = RecvBufLen / 16;
reg_dma0_addr = addr; //set receive buffer address
reg_dma0_addrHi = 0x04;
reg_dma0_size = bufLen; //set receive buffer size
reg_dma0_mode = FLD_DMA_WR_MEM; //set DMA 0 mode to 0x01 for receive
}
/**
* @brief This function determines whether parity error occurs once a packet arrives.
* @param[in] none
* @return 1: parity error ;
* 0: no parity error
*/
unsigned char uart_is_parity_error(void)
{
return (reg_uart_status0 & FLD_UART_RX_ERR_FLAG);
}
/**
* @brief This function clears parity error status once when it occurs.
* @param[in] none
* @return none
*
* Note:
*(1)DMA mode
* RX FIFO will also be cleared when parity error flag is cleared .
*(2)NON-DMA mode
* When parity error occurs, clear parity error flag after UART receives the data.
* Cycle the four registers (0x90 0x91 0x92 0x93) from register "0x90" to get data when UART receives the data next time.
*/
void uart_clear_parity_error(void)
{
reg_uart_status0|= FLD_UART_CLEAR_RX_FLAG; //write 1 to clear
}
/**
* @brief UART hardware flow control configuration. Configure RTS pin.
* @param[in] Enable - enable or disable RTS function.
* @param[in] Mode - set the mode of RTS(auto or manual).
* @param[in] Thresh - threshold of trig RTS pin's level toggle(only for auto mode),
* it means the number of bytes that has arrived in Rx buf.
* @param[in] Invert - whether invert the output of RTS pin(only for auto mode)
* @param[in] pin - RTS pin select,it can be GPIO_PA4/GPIO_PB3/GPIO_PB6/GPIO_PC0.
* @return none
*/
void uart_set_rts(unsigned char Enable, UART_RTSModeTypeDef Mode, unsigned char Thresh, unsigned char Invert, UART_RtsPinDef pin)
{
if (Enable)
{
gpio_set_func(pin,AS_UART);//enable rts pin
gpio_set_input_en(pin, 1);//enable input
gpio_set_output_en(pin, 1);//enable output
reg_uart_ctrl2 |= FLD_UART_CTRL2_RTS_EN; //enable RTS function
}
else
{
reg_uart_ctrl2 &= (~FLD_UART_CTRL2_RTS_EN); //disable RTS function
}
if (Mode)
{
reg_uart_ctrl2 |= FLD_UART_CTRL2_RTS_MANUAL_EN;
}
else {
reg_uart_ctrl2 &= (~FLD_UART_CTRL2_RTS_MANUAL_EN);
}
if (Invert) {
reg_uart_ctrl2 |= FLD_UART_CTRL2_RTS_PARITY;
}
else {
reg_uart_ctrl2 &= (~FLD_UART_CTRL2_RTS_PARITY);
}
//set threshold
reg_uart_ctrl2 &= (~FLD_UART_CTRL2_RTS_TRIG_LVL);
reg_uart_ctrl2 |= (Thresh & FLD_UART_CTRL2_RTS_TRIG_LVL);
}
/**
* @brief This function sets the RTS pin's level manually
* @param[in] Polarity - set the output of RTS pin(only for manual mode)
* @return none
*/
void uart_set_rts_level(unsigned char Polarity)
{
if (Polarity) {
reg_uart_ctrl2 |= FLD_UART_CTRL2_RTS_MANUAL_VAL;
}
else {
reg_uart_ctrl2 &= (~FLD_UART_CTRL2_RTS_MANUAL_VAL);
}
}
/**
* @brief UART hardware flow control configuration. Configure CTS pin.
* @param[in] Enable - enable or disable CTS function.
* @param[in] Select - when CTS's input equals to select, tx will be stopped
* @param[in] pin - CTS pin select,it can be GPIO_PA3/GPIO_PB2/GPIO_PC4/GPIO_PD1.
* @return none
*/
void uart_set_cts(unsigned char Enable, unsigned char Select,UART_CtsPinDef pin)
{
if (Enable)
{
gpio_set_func(pin,AS_UART);//enable cts pin
gpio_set_input_en(pin, 1);//enable input
reg_uart_ctrl1|= FLD_UART_CTRL1_CTS_EN; //enable CTS function
}
else
{
reg_uart_ctrl1 &= (~FLD_UART_CTRL1_CTS_EN); //disable CTS function
}
//Select Trigger Voltage
if (Select)
{
reg_uart_ctrl1 |= FLD_UART_CTRL1_CTS_SELECT;
}
else
{
reg_uart_ctrl1 &= (~FLD_UART_CTRL1_CTS_SELECT);
}
}
/**
* @brief This function serves to select pin for UART module.
* @param[in] tx_pin - the pin to send data.
* @param[in] rx_pin - the pin to receive data.
* @return none
*/
void uart_gpio_set(UART_TxPinDef tx_pin,UART_RxPinDef rx_pin)
{
//note: pullup setting must before uart gpio config, cause it will lead to ERR data to uart RX buffer(confirmed by sihui&sunpeng)
//PM_PIN_PULLUP_1M PM_PIN_PULLUP_10K
gpio_setup_up_down_resistor(tx_pin, PM_PIN_PULLUP_10K); //must, for stability and prevent from current leakage
gpio_setup_up_down_resistor(rx_pin, PM_PIN_PULLUP_10K); //must for stability and prevent from current leakage
gpio_set_func(tx_pin,AS_UART); // set tx pin
gpio_set_func(rx_pin,AS_UART); // set rx pin
gpio_set_input_en(tx_pin, 1); //experiment shows that tx_pin should open input en(confirmed by qiuwei)
gpio_set_input_en(rx_pin, 1); //
}
/**
* @brief This function enables the irq when UART module receives error data.
* @param[in] none
* @return none
*/
void uart_mask_error_irq_enable(void)
{
reg_uart_rx_timeout1|= FLD_UART_MASK_ERR_IRQ;
reg_irq_mask |= FLD_IRQ_UART_EN;
}