1 /**************************************************************************//**
2  * @file     scuart.c
3  * @version  V3.00
4  * @brief    Smartcard UART mode (SCUART) driver source file
5  *
6  * @copyright SPDX-License-Identifier: Apache-2.0
7  * @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved.
8  *****************************************************************************/
9 #include "NuMicro.h"
10 
11 
12 /** @addtogroup Standard_Driver Standard Driver
13   @{
14 */
15 
16 /** @addtogroup SCUART_Driver SCUART Driver
17   @{
18 */
19 
20 /** @addtogroup SCUART_EXPORTED_FUNCTIONS SCUART Exported Functions
21   @{
22 */
23 
24 /**
25   * @brief      Disable smartcard interface
26   *
27   * @param      sc      The pointer of smartcard module.
28   *
29   * @return     None
30   *
31   * @details    The function is used to disable smartcard interface UART mode.
32   */
SCUART_Close(SC_T * sc)33 void SCUART_Close(SC_T* sc)
34 {
35     sc->INTEN = 0UL;
36     sc->UARTCTL = 0UL;
37     sc->CTL = 0UL;
38 }
39 
40 /** @cond HIDDEN_SYMBOLS */
41 /**
42   * @brief      Returns module clock of specified SC interface
43   *
44   * @param[in]  sc      The pointer of smartcard module.
45   *
46   * @return     Module clock of specified SC interface.
47   */
SCUART_GetClock(SC_T * sc)48 static uint32_t SCUART_GetClock(SC_T *sc)
49 {
50     uint32_t u32ClkSrc = 0, u32Num = 0, u32ClkFreq = __HIRC, u32Div = 0;
51 
52     /* Get smartcard module clock source and divider */
53     if(sc == SC0)
54     {
55         u32Num = 0UL;
56         u32ClkSrc = CLK_GetModuleClockSource(SC0_MODULE);
57         u32Div = CLK_GetModuleClockDivider(SC0_MODULE);
58     }
59     else if(sc == SC1)
60     {
61         u32Num = 1UL;
62         u32ClkSrc = CLK_GetModuleClockSource(SC1_MODULE);
63         u32Div = CLK_GetModuleClockDivider(SC1_MODULE);
64     }
65     else if(sc == SC2)
66     {
67         u32Num = 2UL;
68         u32ClkSrc = CLK_GetModuleClockSource(SC2_MODULE);
69         u32Div = CLK_GetModuleClockDivider(SC2_MODULE);
70     }
71     else
72     {
73         u32ClkFreq = 0UL;
74     }
75 
76     if(u32ClkFreq != 0UL)
77     {
78         /* Get smartcard module clock */
79         if(u32ClkSrc == 0UL)
80         {
81             u32ClkFreq = __HXT;
82         }
83         else if(u32ClkSrc == 1UL)
84         {
85             u32ClkFreq = CLK_GetPLLClockFreq();
86         }
87         else if(u32ClkSrc == 2UL)
88         {
89             if(u32Num == 1UL)
90             {
91                 u32ClkFreq = CLK_GetPCLK1Freq();
92             }
93             else
94             {
95                 u32ClkFreq = CLK_GetPCLK0Freq();
96             }
97         }
98         else
99         {
100             u32ClkFreq = __HIRC;
101         }
102 
103         u32ClkFreq /= (u32Div + 1UL);
104     }
105 
106     return u32ClkFreq;
107 }
108 /** @endcond HIDDEN_SYMBOLS */
109 
110 /**
111   * @brief      Enable smartcard module UART mode and set baudrate
112   *
113   * @param[in]  sc          The pointer of smartcard module.
114   * @param[in]  u32Baudrate Target baudrate of smartcard UART module.
115   *
116   * @return     Actual baudrate of smartcard UART mode
117   *
118   * @details    This function use to enable smartcard module UART mode and set baudrate.
119   *
120   * @note       This function configures character width to 8 bits, 1 stop bit, and no parity.
121   *             And can use \ref SCUART_SetLineConfig function to update these settings.
122   *             The baudrate clock source comes from SC_CLK/SC_DIV, where SC_CLK is controlled
123   *             by SCxSEL in CLKSEL3 register, SC_DIV is controlled by SCxDIV in CLKDIV1
124   *             register. Since the baudrate divider is 12-bit wide and must be larger than 4,
125   *             (clock source / baudrate) must be larger or equal to 5 and smaller or equal to
126   *             4096. Otherwise this function cannot configure SCUART to work with target baudrate.
127   */
SCUART_Open(SC_T * sc,uint32_t u32Baudrate)128 uint32_t SCUART_Open(SC_T* sc, uint32_t u32Baudrate)
129 {
130     uint32_t u32ClkFreq = SCUART_GetClock(sc), u32Div;
131 
132     /* Calculate divider for target baudrate */
133     u32Div = (u32ClkFreq + (u32Baudrate >> 1) - 1UL) / u32Baudrate - 1UL;
134 
135     sc->CTL = SC_CTL_SCEN_Msk | SC_CTL_NSB_Msk;  /* Enable smartcard interface and stop bit = 1 */
136     sc->UARTCTL = SCUART_CHAR_LEN_8 | SCUART_PARITY_NONE | SC_UARTCTL_UARTEN_Msk; /* Enable UART mode, disable parity and 8 bit per character */
137     sc->ETUCTL = u32Div;
138 
139     return (u32ClkFreq / (u32Div + 1UL));
140 }
141 
142 /**
143   * @brief      Read Rx data from Rx FIFO
144   *
145   * @param[in]  sc              The pointer of smartcard module.
146   * @param[in]  pu8RxBuf        The buffer to store receive the data.
147   * @param[in] u32ReadBytes     Target number of characters to receive
148   *
149   * @return     Actual character number reads to buffer
150   *
151   * @details    The function is used to read data from Rx FIFO.
152   *
153   * @note       This function does not block and return immediately if there's no data available.
154   */
SCUART_Read(SC_T * sc,uint8_t pu8RxBuf[],uint32_t u32ReadBytes)155 uint32_t SCUART_Read(SC_T* sc, uint8_t pu8RxBuf[], uint32_t u32ReadBytes)
156 {
157     uint32_t u32Count;
158 
159     for(u32Count = 0UL; u32Count < u32ReadBytes; u32Count++)
160     {
161         if(SCUART_GET_RX_EMPTY(sc) == SC_STATUS_RXEMPTY_Msk)
162         {
163             /* No data available */
164             break;
165         }
166         /* Get data from FIFO */
167         pu8RxBuf[u32Count] = (uint8_t)SCUART_READ(sc);
168     }
169 
170     return u32Count;
171 }
172 
173 /**
174   * @brief      Configure smartcard UART mode line setting
175   *
176   * @param[in]  sc              The pointer of smartcard module.
177   * @param[in]  u32Baudrate     Target baudrate of smartcard UART mode. If this value is 0, SC UART baudrate will not change.
178   * @param[in]  u32DataWidth    The data length, could be:
179   *                                 - \ref SCUART_CHAR_LEN_5
180   *                                 - \ref SCUART_CHAR_LEN_6
181   *                                 - \ref SCUART_CHAR_LEN_7
182   *                                 - \ref SCUART_CHAR_LEN_8
183   * @param[in]  u32Parity       The parity setting, could be:
184   *                                 - \ref SCUART_PARITY_NONE
185   *                                 - \ref SCUART_PARITY_ODD
186   *                                  - \ref SCUART_PARITY_EVEN
187   * @param[in]  u32StopBits     The stop bit length, could be:
188   *                                 - \ref SCUART_STOP_BIT_1
189   *                                 - \ref SCUART_STOP_BIT_2
190   *
191   * @return     Actual baudrate of smartcard UART mode
192   *
193   * @details    The baudrate clock source comes from SC_CLK/SC_DIV, where SC_CLK is controlled
194   *             by SCxSEL in CLKSEL3 register, SC_DIV is controlled by SCxDIV in CLKDIV1
195   *             register. Since the baudrate divider is 12-bit wide and must be larger than 4,
196   *             (clock source / baudrate) must be larger or equal to 5 and smaller or equal to
197   *             4096. Otherwise this function cannot configure SCUART to work with target baudrate.
198   */
SCUART_SetLineConfig(SC_T * sc,uint32_t u32Baudrate,uint32_t u32DataWidth,uint32_t u32Parity,uint32_t u32StopBits)199 uint32_t SCUART_SetLineConfig(SC_T* sc, uint32_t u32Baudrate, uint32_t u32DataWidth, uint32_t u32Parity, uint32_t u32StopBits)
200 {
201     uint32_t u32ClkFreq = SCUART_GetClock(sc), u32Div;
202 
203     if(u32Baudrate == 0UL)
204     {
205         /* Keep original baudrate setting */
206         u32Div = sc->ETUCTL & SC_ETUCTL_ETURDIV_Msk;
207     }
208     else
209     {
210         /* Calculate divider for target baudrate */
211         u32Div = ((u32ClkFreq + (u32Baudrate >> 1) - 1UL) / u32Baudrate) - 1UL;
212         sc->ETUCTL = u32Div;
213     }
214 
215     sc->CTL = u32StopBits | SC_CTL_SCEN_Msk;  /* Set stop bit */
216     sc->UARTCTL = u32Parity | u32DataWidth | SC_UARTCTL_UARTEN_Msk;  /* Set character width and parity */
217 
218     return (u32ClkFreq / (u32Div + 1UL));
219 }
220 
221 /**
222   * @brief      Set receive timeout count
223   *
224   * @param[in]  sc      The pointer of smartcard module.
225   * @param[in]  u32TOC  Rx time-out counter, using baudrate as counter unit. Valid range are 0~0x1FF,
226   *                     set this value to 0 will disable time-out counter.
227   *
228   * @return     None
229   *
230   * @details    The time-out counter resets and starts counting whenever the Rx buffer received a
231   *             new data word. Once the counter decrease to 1 and no new data is received or CPU
232   *             does not read any data from FIFO, a receiver time-out interrupt will be generated.
233   */
SCUART_SetTimeoutCnt(SC_T * sc,uint32_t u32TOC)234 void SCUART_SetTimeoutCnt(SC_T* sc, uint32_t u32TOC)
235 {
236     sc->RXTOUT = u32TOC;
237 }
238 
239 /**
240   * @brief      Write data into transmit FIFO to send data out
241   *
242   * @param[in]  sc              The pointer of smartcard module.
243   * @param[in]  pu8TxBuf        The buffer containing data to send to transmit FIFO.
244   * @param[in]  u32WriteBytes   Number of data to send.
245   *
246   * @return     Actual number of data put into SCUART Tx FIFO
247   *
248   * @details    This function is used to write data into Tx FIFO to send data out.
249   */
SCUART_Write(SC_T * sc,uint8_t pu8TxBuf[],uint32_t u32WriteBytes)250 uint32_t SCUART_Write(SC_T* sc, uint8_t pu8TxBuf[], uint32_t u32WriteBytes)
251 {
252     uint32_t u32Count;
253     /* Baudrate * (start bit + 8-bit data + 1-bit parity + 2-bit stop) */
254     uint32_t u32Delay = (SystemCoreClock / SCUART_GetClock(sc)) * sc->ETUCTL * 12, i;
255 
256     for(u32Count = 0UL; u32Count != u32WriteBytes; u32Count++)
257     {
258         i = 0;
259         /* Wait 'til FIFO not full */
260         while(SCUART_GET_TX_FULL(sc))
261         {
262             /* Block longer than expected. Maybe some interrupt disable SCUART clock. */
263             if(i++ > u32Delay) return u32Count;
264         }
265         /* Write 1 byte to FIFO */
266         sc->DAT = pu8TxBuf[u32Count];
267     }
268     return u32Count;
269 }
270 
271 
272 /**@}*/ /* end of group SCUART_EXPORTED_FUNCTIONS */
273 
274 /**@}*/ /* end of group SCUART_Driver */
275 
276 /**@}*/ /* end of group Standard_Driver */
277