ILD

UART
作者:Herbert Yuan 邮箱:yuanjp89@163.com
发布时间:2017-11-10 站点:Inside Linux Development

在嵌入式中,UART是最常用的东西,作为串口输出和输入,是调试必不可少的东西。


1 Overview

S3C2440A Universal Asynchronous Receiver and Transmitter (UART) 提供3个独立的异步串口,每个均可运行在中断模式或者DMA模式。使用系统时钟,能支持最高921.6Kbps的波特率。如果外部设备提供EXTCLK时钟,则能运行在更高的速度。每个UART channel包含2个64字节的FIFO用于接收和发送。


包括可编程波特率,红外收发,一个或2个停止位,5/6/7/8位数据宽度,校验位。


每个UART包含一个baud-rate generator, transmitter, receiver和一个控制单元。波特率产生器可使用PCLK,FLCK/n 或者UEXTCLK。接收器和发送器包含64字节FIFO和data shifters。


2 UART operation

Data Transmission

发送数据帧格式可编程。包含一个开始位,5-8位数据位,可选的校验位,和1-2位停止位。发送器可以产生break condition。


Data Reception

接收数据帧格式也是可编程的。接收器能检测overrun error, parity error, fram error, 和 break condtion,每一种都设置一个错误标志。

overrun error意味着新数据覆盖旧数据,在旧数据被读走之前。

parity error 意味着接收器检测到一个不期望的校验条件。

fram error 意味着接收数据没有一个合法的停止位。

break condition 意味着RxDn输入保持逻辑0状态超过一个传输帧时间。

当超过3个字时间没收到任何数据,或者FIFO模式下,Rx FIFO不为空时,接收超时条件发生。


Auto Flow Control (AFC)

UART0和UART1支持自动流控,通过nRTS和nCTS信号。发送者的nCTS接到接收者的nRTS,当nCTS由nRTS激活时,发送者才能发送数据。


Interrupt / DMA 请求产生

每个UART有7种状态信号:Overrun error, parity error, frame error, brea, receive buffer data ready, transmit buffer emty 和 transmit shifter empty。每一种都有对应的寄存器位。

overrun error, parity error, fram error, break condition 被当做是接收错误状态,每一个都产生接收错误状态中断请求。

当接收器将数据从receive shifter传送到Rx FIFO寄存器时,而且数量达到Rx FIFO触发水平,产生Rx中断。在非FIFO模式,将数据传送到接收保持寄存器引起Rx中断。

当发送器将数据从Tx FIFO寄存器传送到transmit shifter,而且FIFO中的数量少于Tx FIFO触发水平时,触发Tx中断。在非FIFO模式,从transmit holding register传送数据到transmit shifter将导致Tx中断。

如果是DMA模式,上述情况产生对应的DMA请求。


UART Error status FIFO

除了Rx FIFO,错误也有FIFO。error status FIFO表示FIFO寄存器中的哪个数据,以一个错误接收。错误中断只有在数据可以被读出时才产生。为了清除错误FIFO,必须读出UERSTATn和有错误的URXHn。


Buad-rate generation

波特率产生器位发送器和接收器提供串口时钟。波特率产生器的时钟源可以选择内部系统时钟,或者UEXTCLK。通过设置寄存器来获取对应的波特率:

UBRDIVn = (UART clock / (buad rate x 16)) - 1


3 UART special register

UART Line control register

3个UART line控制寄存器,对应3个通道。

RegisteraddressR/Wdescreset value
ULCON00x50000000R/WUART ch0 line control register0x0
ULCON10x50004000R/WUART ch1 line control register0x0
ULCON20x50008000R/WUART ch2 line control register0x0


ULCONnBitDescInisital state
Reserved[7]


0
Infrared Mode[6]

Determine wether or not to use the Infrared mode.

0 = Normal mode operation

1 = Infrared Rx/Rx mode


Parity Mode[5:3]

Specify the type of parity generation and checking during UART transmit and receive operation.

0xx = No parity

100 = Odd parity

101 = Even parity

110 = Parity forced / checked as 1

111 = Parity forced / checked as 0


Number of stop bit[2]

Specify how may stop bits are to be used for end-of-frame signal.

0 = One stop bit per frame

1 = Two stop bit per frame


Word length[1:0]

Indiciate the number of data bits to be tx or rx per frame.

00 = 5-bits 01  = 6-bits

10 = 7-bits 11 = 8-bits



UART control register

RegisteraddressR/Wdescreset value
UCON00x50000004R/WUART ch0 control register0x0
UCON10x50004004R/WUART ch1 control register0x0
UCON20x50008004R/WUART ch2 control register0x0


UCONnBitDescInitial state
FCLK Divider[15:12]

设置是否使用FCLK/n,以及n的值。由3个通道的该字段共同决定。

UCON2[15] 是FCLK/n的开关。

n从7-21,使用UCON0[15:12]

n从22-36,使用UCON1[15:12]

n从37-43,使用UCON2[14:12]

使用一个时,其它两个必须为0.


Clock Selection[11:10]

00,10 = PCLK, 01 = UEXTCLK, 11 = FLCK/n

如果使用FCLK/n应当添加note中的代码。


Tx Interrupt type9

0 = Pulse

1 = Level


Rx Interrupt type8

0 = Pulse

1 = Level


Rx Time out enable7

Enable / disable Rx time out interrupt when UART FIFO is enable. The interrupt is a receive interrupt.

0 = disable 1 = enable


Rx error status 

interrupt enable

6

0 = Do not generate

1 = generate


Loopback mode5

0 = NOrmal operation

1 = loopback mode


send break signal4

0 = Normal transmit

1 = send break signal


Transmit mode3:2

00 = Disable

01 = Interrupt request or polling mode

10 = DMA0 request for UART0

        DMA3 request for UART2

11 = DMA1  request for UART1


Receive Mode1:0

00 = Disable

01 = Interrupt request or polling mode

10 = DMA0 request for UART0

        DMA3 request for UART2

11 = DMA1  request for UART1



UART FIFO control register

RegisteraddressR/Wdescreset value
UFCON00x50000008R/WUART ch0 FIFO control register0x0
UFCON10x50004008R/WUART ch1 FIFO control register0x0
UFCON20x50008008R/WUART ch2 FIFO control register0x0


UFCONnBitDescInitial state
Tx FIFO Trigger Level[7:6]

00 = Empty 01 = 16Byte

10 = 32Byte 11 = 48Byte


Rx FIFO Trigger Level[5:4]

00 = 1Byte 01 = 8Byte

10 = 16Byte 11 = 32Byte


Reserved3

Tx FIFO reset2

Auot-cleared after resetting FIFO

0 = Normal 1 = Tx FIFO Reset


Rx FIFO reset1

Auot-cleared after resetting FIFO

0 = Normal 1 = Rx FIFO Reset


FIFO Enable00 = Disable 1 = Enable


UART Modem control register

UART 2没有AFC功能。

RegisteraddressR/Wdescreset value
UMCON00x5000000CR/WUART ch0 FIFO control register0x0
UMCON10x5000400CR/WUART ch1 FIFO control register0x0
Reserved0x5000800C



UMCONnBitDescInitial State
Reserved[7:5]Must be 00
Auto Flow Control (AFC)[4]0 = disable, 1 = enable0
Reserved[3:1]must be 00
Request to send[0]

If AFC bit is enabled, this value will be ignored. In this case the S3C2440A will control nRTS automatically.

If AFC bit is disabled. nRTS must be controlled by software.

0 = H level (Inactivate nRTS)

1 = L level (Activate  nRTS)

0


UART TX/RX Status Register

RegisteraddressR/Wdescreset value
UTRSTAT00x50000010RUART ch0 Tx/Rx status register0x6
UTRSTAT10x50004010RUART ch1 Tx/Rx status register0x6
UTRSTAT20x50008010RUART ch2 Tx/Rx status register0x6


UTRSTATnBitDescInitial state
Transmitter empty[2]

Set to 1 automatically when the transmit buffer register has no valid data to transmit and the transmit shift register is empty.

0 = Not empty

1 = Transmitter (transmit buffer & shifter register) empty

1
Transmit buffer empty[1]

Set to 1 automatically when transmit buffer register is empty.

0 = The buffer register is not empty.

1 = Empty

      (In Non-FIFO mode, Interrupt or DMA is requested. In FIFO mode, Interrupt or DMA is requested, when Tx FIFO Trigger Level is set to 0)


If the UART uses the FIFO, users should check Tx FIFO COunt bits and Tx FIFO Full bit in the UFSTAT register instead of this bit.

1
Receive buffer data ready[0]

Set to 1 automatically whenever receive buffer register contains valid data, received over the RXDn port.

0 = Empty

1 = The buffer resiger has a received data

      (In Non-FIFO mode, interrupt or DMA is requested)


If the UART uses the FIFO, users should check Rx Fifo Count bits and Rx Fifo full bi in the UFSTAT register instead of this bit.

0


UART Error Status register

RegisteraddressR/Wdescreset value
UERSTAT00x50000014RUART ch0 Rx error register0x0
UERSTAT10x50004014RUART ch1 Rx error register0x0
UERSTAT20x50008014RUART ch2 Rx error register0x0


UERSTATnBitDescInitial state
Break Detect[3]1 = Bread receive (Interrupt is requested)0
Frame Error21 = Frame error (Interrupt is requested)0
Parity Error11 = Parity error  (Interrupt is requested)0
Overrun Error01 = Overrun error  (Interrupt is requested)0


UART FIFO STATUS register

RegisteraddressR/Wdescreset value
UFSTAT00x50000018RUART ch0 FIFO status register0x0
UFSTAT10x50004018RUART ch1 FIFO status register0x0
UFSTAT20x50008018RUART ch2 FIFO status register0x0


UFSTATnBitDescInitial state
Reserved[15]
0
TX FIFO full[14]

0 = 0 <= Tx fifo data <= 63

1 = Full

0
TX FIFO count[13:8]Number of data in Tx FIFO0
Reserved7
0
RX FIFO FULL60 = 0 <= Tx fifo data <= 63

1 = Full


RX FIFO COUNT[5:0]Number of data in Rx FIFO


UART modem status register

RegisteraddressR/Wdescreset value
UMSTAT00x5000001CRUART ch0 modem status register0x0
UMSTAT10x5000401CRUART ch1 modem status register0x0
Reserved0x5000801C



UMSTATnBitDescInitial state
Delta CTS[4]

Indicate that the nCTS input to the S3C2440A has changed state since that last time it was read by CPU.

0 = has not changed

1 = has changed

0
reserved[3:1]



Clear to send[0]

0 = CTS signal is not activated (nCTS pin is high)

1 = CTS signal is actived (nCTS pin is low)



UART transmit buffer register (Holading register & FIFO register)

RegisteraddressR/Wdescreset value
UTXH0

0x50000020(L)

0x50000023(H)

W (by byte)UART ch0 transmit buffer register
UTXH1

0x50004020

0x50004023

W (by byte)UART ch1 transmit buffer  register
UTXH2

0x50008020

0x50008023

W (by byte)UART ch2 transmit buffer  register

TXDATAn [7:0] Transmit data for UARTn


UART receive buffer register (Holding register & FIFO register)

RegisteraddressR/Wdescreset value
URXH0

0x50000024(L)

0x50000027(H)

W (by byte)UART ch0 receive buffer register
URXH1

0x50004024

0x50004027

W (by byte)UART ch1 receive buffer  register
URXH2

0x50008024

0x50008027

W (by byte)UART ch2 receive buffer  register

RXDATAn [7:0] receive data for UARTn


UART buad rate divisor register

RegisteraddressR/Wdescreset value
UBRDIV00x50000028R/WBuad rate divisor register 00x0
UBRDIV10x50004028R/WBuad rate divisor register 10x0
UBRDIV20x50008028R/WBuad rate divisor register 2

UBRDIV [15:0] Baud rate division value UBRDIVn > 0

UBRDIVn = (clock / (baud rate x 16)) - 1

必须大于0,当时钟为UEXTCLK时,可以等于0.


4 实战

使用中断,输入什么原封的显示到输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// watchdog
#define     WTCON           (*(volatile unsigned long *)0x53000000)
#define     WTDAT           (*(volatile unsigned long *)0x53000004)
#define     WTCNT           (*(volatile unsigned long *)0x53000008)
 
#define     GPFCON           (*(volatile unsigned long *)0x56000050)
#define     GPFDAT           (*(volatile unsigned char *)0x56000054)
#define     GPGCON          (*(volatile unsigned long *)0x56000060)
 
#define     GPHCON           (*(volatile unsigned long *)0x56000070)
#define     GPHUP          (*(volatile unsigned long *)0x56000078)
 
#define     EINTMSK        (*(volatile unsigned long *)0x560000a4)
#define     EINTPND        (*(volatile unsigned long *)0x560000a8)
#define     EXTINT0         (*(volatile unsigned long *)0x56000088)
#define     EXTINT1         (*(volatile unsigned long *)0x5600008c)
 
typedef unsigned long uint32_t;
typedef unsigned char uint8_t;
 
struct S3C2440_INT_CONT
{
    uint32_t SRCPND;
    uint32_t INTMOD;
    uint32_t INTMSK;
    uint32_t PRIO;
    uint32_t INTPND;
    uint32_t INTOFFSET;
    uint32_t SUBSRCPND;
    uint32_t INTSUBMSK;
};
 
volatile struct S3C2440_INT_CONT *intcont = (volatile struct S3C2440_INT_CONT *)0x4a000000;
 
struct S3C2440_CLK
{
    uint32_t LOCKTIME;
    uint32_t MPLLCON;
    uint32_t UPLLCON;
    uint32_t CLKCON;
    uint32_t CLKSLOW;
    uint32_t CLKDIVN;
    uint32_t CAMDIVN;
};
 
volatile struct S3C2440_CLK * clk = (volatile struct S3C2440_CLK *)0x4c000000;
 
struct S3C2440_UART
{
    uint32_t ULCON;
    uint32_t UCON;
    uint32_t UFCON;
    uint32_t UMCON;
    uint32_t UTRSTAT;
    uint32_t UERSTAT;
    uint32_t UFSTAT;
    uint32_t UMSTAT;
    uint8_t  UTXH;
    uint8_t  unused1[3];
    uint8_t  URXH;
    uint8_t  unused2[3];
    uint32_t UBRDIV;
};
 
volatile struct S3C2440_UART *uart_ch0 = (volatile struct S3C2440_UART *)0x50000000;
volatile struct S3C2440_UART *uart_ch1 = (volatile struct S3C2440_UART *)0x50004000;
volatile struct S3C2440_UART *uart_ch2 = (volatile struct S3C2440_UART *)0x50008000;
 
void disable_watchdog()
{
    WTCON = 0; 
}
 
void uart_init()
{
    GPHCON = (2 << 4) | (2 << 6);      // set gpio to RXD0/TXD0
    GPHUP = (1 << 2) | (1 << 3);        // SET PULL-up
 
    uart_ch0->ULCON = 0x3;              // NO Parity | 1 Stop bit | 8N1
    uart_ch0->UCON = (1<<7)|(1<<6)|(1<<2)|(1<<0);         // Enable RX timeout, enable rx erro intr, 
    uart_ch0->UFCON = (2<<6)|(2<<4)|(1<<0);
    uart_ch0->UMCON = 0;
    uart_ch0->UBRDIV = 400*1000*1000/6/16/115200;
}
 
void clk_init()
{
    clk->CLKDIVN = (0x3 << 1) | 0x1;    // HDIVN:11b, PDIVN, 1 
 
    asm volatile (
        "mrc p15, 0, r1, c1, c0, 0\n\t"
        "orr r1, r1, #0xc0000000\n\t"
        "mcr p15, 0, r1, c1, c0, 0"
        :
        :
        "r1");
    
    // according to datasheet, should set UPLLCON firstly.
    clk->UPLLCON = (56 << 12) | (2 << 4) | 2;   // 48MHz
    clk->MPLLCON = (92 << 12) | (1 << 4) | 1;   // 400MHz
}
 
void gpio_init()
{
#define set_gpio(r,v,n) r &= ~(3 << 2*n); r |= (v << 2*n)
     
    unsigned long r1 = GPFCON;
    unsigned long r2 = GPGCON;
 
    // 4,5,6 LED output mode
    set_gpio(r1,1,4);
    set_gpio(r1,1,5);
    set_gpio(r1,1,6);
 
    // 0(EINT0),2(EINT2) Button INT Mode
    set_gpio(r1,2,0);
    set_gpio(r1,2,2);
 
    // 3(EINT11) BUtton INT Mode
    set_gpio(r2,2,3);
 
    GPFCON = r1;
    GPGCON = r2;
}
 
 
int ledon[8];
 
void REVERT_LED(int led)
{
    uint32_t reg = GPFDAT;
    if (ledon[led])
    {
        ledon[led] = 0;
        reg |= (1 << led);
    }
    else
    {
        ledon[led] = 1;
        reg &= ~ (1 << led);
    }
    GPFDAT = reg;
}
 
void init_interrupt_controller()
{
    // bit 28: INT_UART0
    intcont->INTMSK = ~ (1 << 28);
 
    // INT_RXD0 : INT_TXD0 : INT_ERR0
    intcont->INTSUBMSK = ~((1<<0)|(1<<1)|(1<<2));
}
 
void init()
{
    disable_watchdog();
    clk_init();
    init_interrupt_controller();
    gpio_init();
    uart_init();
 
    GPFDAT = -1;
 
    asm volatile (
        "msr cpsr_c, #0xd2\n\t"     // enter IRQ mode
        "ldr sp, =3072\n\t"         // set IRQ mode stack
        "msr cpsr_c, #0x53");       // re-enter svc mode and enable IRQ and FIQ
 
}
 
#define DISABLE_IRQ()   
#define ENABLE_IRQ()    
 
char txbuf[100];
int tx_head = 0;
int tx_tail = 0;
 
char rxbuf[100];
int rx_head = 0;
int rx_tail = 0;
 
void schedule_w()
{
    while ( tx_head != tx_tail
        && !(uart_ch0->UFSTAT & (1 << 14)))
    {
        uart_ch0->UTXH = txbuf[tx_tail];
        tx_tail++;
        if (tx_tail == sizeof(txbuf))
            tx_tail = 0;
 
        REVERT_LED(4);
    }
}
 
void schedule_r()
{
    while ( ((rx_head == sizeof(rxbuf) -1 && rx_tail != 0) 
                || (rx_head + 1 != rx_tail))
        && (uart_ch0->UFSTAT & 0x1f))
    {
        rxbuf[rx_head] = uart_ch0->URXH;
        rx_head++;
        if (rx_head == sizeof(rxbuf))
            rx_head = 0;
 
        REVERT_LED(5);
    }
}
 
int read(char *buf, int len)
{
    int n = 0;
 
    if (len <= 0)
        return len;
 
    DISABLE_IRQ();
    while (n < len && rx_tail != rx_head)
    {
        buf[n] = rxbuf[rx_tail];
        n++;
        rx_tail ++;
        if (rx_tail == sizeof(rxbuf))
            rx_tail = 0;
    }
    schedule_r();
    ENABLE_IRQ();
     
    return n;
}
 
int write(char *buf, int len)
{
    int n = 0;
 
    if (len <= 0)
        return len;
 
    DISABLE_IRQ();
    while(n < len && 
        ! ((tx_head == sizeof(txbuf)-1 && tx_tail == 0)
            || (tx_head + 1 == tx_tail)))
    {
        txbuf[tx_head] = buf[n];
        n++;
        tx_head++;
        if (tx_head == sizeof(txbuf))
            tx_head = 0;
    }
    schedule_w();
    ENABLE_IRQ();
 
    return n;
}
 
void main()
{
    char buf[100];
    write("hello\r\n", 6);
    for (;;)
    {
        int i = 0;
        int n = read(buf, sizeof(buf));
        for (i = 0; i < n; i++)
        {
            if (buf [i] == '\n' || buf[i] == '\r')
                write("\r\n# ", 4);
            else
                write(&buf[i], 1);
        }
    }
}
 
void irq_handler()
{
    unsigned long off = intcont->INTOFFSET;
     
    if (off == 28)
    {
        if (intcont->SUBSRCPND & 1)
        {
            schedule_r();
        }
        if (intcont->SUBSRCPND & 2){
            schedule_w();
        }
        if (intcont->SUBSRCPND & 4)
            ;
    }
 
    if (off == 28)
        intcont->SUBSRCPND = 7;
    intcont->SRCPND = 1 << off;
    intcont->INTPND = 1 << off;
}


效果如下:

Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.