×
单片机 > 单片机程序设计 > 详情

S3C2440—5.UART的使用

发布时间:2021-11-25 发布时间:
|

一.S3C2440中的UART介绍

UART(universal asynchronous receive transmitter)通用异步收发器,用来收发串行数据,以全双工的形式进行通信,UART使用的电平标准是TTL/CMOS,一帧数据通常包含开始位、数据位、校验位、停止位,UART传输的双方要统一波特率。


S3C2440中有三个UART独立通道,功能类似,下面分块介绍一下UART比较重要的部分。


UART的用途一般有俩种:


1.作为与上位机的通信接口,打印调试信息


2.作为外设模块的驱动接口,连接驱动外设,比如:蓝牙、GPS等


1.1 电平匹配

ARM串口电平为TTL,要根据PC的匹配电平来选择电平转换芯片。


UART直接输出的电平是TTL电平,以前上位机PC端都有RS-232电平(九针接口),所以以前ARM与上位机连接需要TTL转RS-232电平的芯片。RS-232适合长距离传输。


现在,PC上基本没有RS-232接口了,取而代之的是USB接口,所以现在ARM与PC的UART通信都使用TTL转USB芯片了。


1.2 UART数据帧与波特率

一帧数据通常包含开始位、数据位、校验位、停止位。


开始位:UART空闲时,TxD数据线是高电平的(因此需要给相应引脚上拉引脚),将要发送数据时,TxD数据线会以拉低电平作为起始信号,所以“0”电平就相当于开始位。


数据位:数据位包含了要发送的信息,数据位的大小可以通过配置寄存器来确定,通常是8bit。


校验位:为了保证数据传输的准确性,有时会在数据位后面加上一个校验位,分为奇、偶校验,校验规则:数据位+校验位中为1的个数是奇数或者偶数。一般不用。


停止位:会给出一段持续的高电平作为停止信号,持续时间可以通过配置寄存器来设置,一般设置停止位的持续时间为1位长度。


最常用的数据帧格式为:8n1(意为:8位数据位,不使用校验位,停止位长度为一位)


波特率:每秒发送的bit(位)


1.3UART框图

框图如下:

UART发送数据的流程:CPU从内存中将数据取到FIFO,FIFO中的数据发送到移位器,由移位器逐位发送数据。


UART接收数据的流程:移位器逐位接收数据,将接收到的数据放在FIFO中,CPU将数据从FIFO中取到内存。


UART中FIFO深度为64Byte,不使用FIFO时,将数据放在接收/保持寄存器中(1Byte)。


数据发送、接收完成,可以利用中断进行处理,也可以不断查询寄存器标志位。


波特率由波特率发生器产生,涉及到时钟源和分频因子的选择


二.UART的配置

这里介绍一下最基本的UART配置,即:使用UART0、满足收发数据功能、不使用FIFO(使用1Byte的寄存器)、收发状态可以通过中断或查询标志位获知。


2.1 UART引脚的配置

S3C2440中UART0的引脚对应关系为:TxD:GPH2 RxD:GPH3


首先要将俩个引脚的模式设置为UART模式,寄存器配置如下:

/* 设置引脚 */

//TxD0:GPH2 RxD0:GPH3

GPHCON &= ~((3<<4)|(3<<6));

GPHCON |= ((2<<4)|(2<<6));


由于数据线平时是高电平,所以要设置引脚的内部上拉。


我们要将内部上拉电阻与引脚连接,通过配置寄存器可以控制,上拉电阻与引脚的连接状态由GPHUP寄存器控制:

我们要使能GPH2、GPH3的内部上拉,将2、3位复位


/* 使能引脚的内部上拉 */

GPHUP &= ~( (1<<2)|(1<<3) );


2.2 波特率的配置

波特率计算如下:

通过设置分频系数DIV、选择时钟源来配置波特率


UCON0寄存器可以选择时钟源:


一般默认PCLK为时钟源,所以不必专门配置时钟源。

UBRDIV寄存器负责分频因子设置:

直接将分频因子写入寄存器即可:


/* 设置波特率 */

/* 波特率设置格公式:UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1 */

/* 实现波特率:115200 b/s 时钟源采用PCLK:50MHz 分频因子:26 (本来26.127,忽略误差可以) */

/* 默认PCLK:UCON0 &= ~(3<<10)*/

UBRDIV0 = 26;


2.3 数据帧的配置

数据帧的配置主要包括对:校验位、停止位、数据位的配置


ULCON0寄存器配置:

/* 设置数据格式 */

/* 数据帧格式:8n1 */

ULCON0 = 0x00000003;


2.4 收发模式配置

UCON0寄存器也用来配置RxD、TxD的模式:可以通过中断和查询寄存器标志位来获取收发的状态


默认选择时钟(PLCK=50MHz)、Rx、Tx模式(中断和查询)

/* 收发模式:中断+查询 */

UCON0 = 0x00000005;


收发状态的查询通过UTRSTAT0寄存器来确定:

可以通过如下程序配合寄存器查询获取收发的状态:


/* 输出数据 */

int putchar( int c )

{

/* 等待传输数据寄存器空 */

while( !(UTRSTAT0 & (1<<2)) );


/* 不使用FIFO,对数据传输寄存器UTXH0直接写 */

UTXH0 = (unsigned char)c;

}



/* 接收数据 */

int getchar( void )

{

while( !(UTRSTAT0 & (1<<0)));

/* 接收数据寄存器URXH0 */

return URXH0;

}


2.5 收发数据寄存器

直接对寄存器进行读写就可以。


发送数据寄存器:

接收数据寄存器:

三.代码

话不多说,直接上代码,程序的框架是:main.c+uart.h+start.S


main.c:


#include "s3c2440_soc.h"

#include "uart.h"


int main(void)

{

unsigned char c;

uart0_init();

puts("Hello, world!nr");

while(1)

{

c = getchar();

if (c == 'r')

{

putchar('n');

}


if (c == 'n')

{

putchar('r');

}


putchar(c);

}

return 0;

}

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

uart.c:


#include "s3c2440_soc.h"

#include "uart.h"



void uart0_init( void )

{

/* 设置引脚 */

//TxD0:GPH2 RxD0:GPH3

GPHCON &= ~((3<<4)|(3<<6));

GPHCON |= ((2<<4)|(2<<6));


/* 使能引脚的内部上拉 */

GPHUP &= ~( (1<<2)|(1<<3) );

/* 设置波特率 */

/* 波特率设置格公式:UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1 */

/* 实现波特率:115200 b/s 时钟源采用PCLK:50MHz 分频因子:26 (本来26.127,忽略误差可以) */

/* 默认PCLK:UCON0 &= ~(3<<10)*/

UBRDIV0 = 26;

/* 设置数据格式 */

/* 数据帧格式:8n1 */

ULCON0 = 0x00000003;


/* 收发模式:中断+查询 */

UCON0 = 0x00000005;

}


/* 输出数据 */

int putchar( int c )

{

/* 等待传输数据寄存器空 */

while( !(UTRSTAT0 & (1<<2)) );


/* 不使用FIFO,对数据传输寄存器UTXH0直接写 */

UTXH0 = (unsigned char)c;

}


/* 接收数据 */

int getchar( void )

{

while( !(UTRSTAT0 & (1<<0)));

/* 接收数据寄存器URXH0 */

return URXH0;

}


/* 连续发送字符串 */

int puts( const char *s )

{

while( *s )

{

putchar( *s );

s++;

}

}


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

start.S:


.text

.global _start


_start:


/* 关闭看门狗 */

ldr r0, =0x53000000

ldr r1, =0

str r1, [r0]


/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */

/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */

ldr r0, =0x4C000000

ldr r1, =0xFFFFFFFF

str r1, [r0]


/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */

ldr r0, =0x4C000014

ldr r1, =0x5

str r1, [r0]


/* 设置CPU工作于异步模式 */

mrc p15,0,r0,c1,c0,0

orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA

mcr p15,0,r0,c1,c0,0


/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)

* m = MDIV+8 = 92+8=100

* p = PDIV+2 = 1+2 = 3

* s = SDIV = 1

* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M

*/

ldr r0, =0x4C000004

ldr r1, =(92<<12)|(1<<4)|(1<<0)

str r1, [r0]


/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定

* 然后CPU工作于新的频率FCLK

*/


/* 设置内存: sp 栈 */

/* 分辨是nor/nand启动

* 写0到0地址, 再读出来

* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动

* 否则就是nor启动

*/

mov r1, #0

ldr r0, [r1] /* 读出原来的值备份 */

str r1, [r1] /* 0->[0] */

ldr r2, [r1] /* r2=[0] */

cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */

ldr sp, =0x40000000+4096 /* 先假设是nor启动 */

moveq sp, #4096 /* nand启动 */

streq r0, [r1] /* 恢复原来的值 */


bl main


halt:

b halt


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
ARM 汇编的必知必会