S3C2440中的UART

一.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框图
框图如下:

S3C2440中的UART_第1张图片

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模式,寄存器配置如下:

S3C2440中的UART_第2张图片

    /* 设置引脚 */
	//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 波特率的配置
波特率计算如下:
S3C2440中的UART_第3张图片

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

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

一般默认PCLK为时钟源,所以不必专门配置时钟源。
S3C2440中的UART_第4张图片

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

S3C2440中的UART_第5张图片

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

    /* 设置波特率 */
	/* 波特率设置格公式: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寄存器配置:
S3C2440中的UART_第6张图片

  /* 设置数据格式 */
	/* 数据帧格式:8n1 */
	ULCON0 = 0x00000003;  

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

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

S3C2440中的UART_第7张图片

S3C2440中的UART_第8张图片

/* 收发模式:中断+查询 */
	UCON0 = 0x00000005;

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

S3C2440中的UART_第9张图片

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

/* 输出数据 */
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 收发数据寄存器
直接对寄存器进行读写就可以。

发送数据寄存器:
S3C2440中的UART_第10张图片

接收数据寄存器:
S3C2440中的UART_第11张图片

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

main.c

#include "s3c2440_soc.h"
#include "uart.h"

int main(void)
{
	unsigned char c;

	uart0_init();
	puts("Hello,word!\n\r");

	while(1)
	{
		c = getchar();
		if (c == '\r')
		{
			putchar('\n');
		}

		if (c == '\n')
		{
			putchar('\r');
		}
		
		putchar(c);
	}
	return 0;
}

uart.h


#ifndef	_UART_H
#define	_UART_H

void uart0_init();
int putchar(int c);
int getchar(void);
int puts(const char *s);

#endif

uart.c

#include "s3c2440_soc.h"

void uart0_init()
{
	/*设置引脚用于串口*/

	/*GPH2,3用于TxD0,RxD0*/
	GPHCON &= ~((3<<4) | (3<<6));
	GPHCON |= ((2<<4) | (2<<6));

	GPFUP &= ~((1<<2) | (1<<3));	/*使能内部上拉*/
	/*设置波特率*/
	/*UBRDIVn = (int)(UART clock / (buad rate x 16)) - 1
	 *UART clock = 50M
	 *UBRDIVn = (int)(50000000 / (115200 x 16)) - 1
	 */
	UCON0 = 0x00000005;/*PCLK,中断/查询模式*/
    UBRDIV0 = 26;
	
	/*设置数据格式*/
	
	ULCON0 = 0x00000003;/* 8n1:8个数据位,无校验位,1个停止位*/

	
	/**/

}

int putchar(int c)
{
	/*UTRSTAT0*/
	/*UTXH0*/

	while (!(UTRSTAT0 & (1<<2)));
	UTXH0 = (unsigned char)c;

}

int getchar(void)
{
	while (!(UTRSTAT0 & (1<<0)));
	return URXH0;

}

int puts(const char *s)
{
	while(*s)
	{
		putchar(*s);
		s++;
	}	

}

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, FCLK : HCLK :PCLK = 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地址上的内容被修改了,它对应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]	/*恢复原来的值*/

	/*调用main*/
	bl main

halt:
	b halt

Makefile

all:
	arm-linux-gcc -c -o uart.o uart.c 
	arm-linux-gcc -c -o main.o main.c 
	arm-linux-gcc -c -o start.o start.S
	arm-linux-ld -Ttext 0 start.o uart.o main.o -o uart.elf
	arm-linux-objcopy -O binary -S uart.elf uart.bin
	arm-linux-objdump -D uart.elf > uart.dis

clean:
	rm *.o *.elf *.bin *.dis

你可能感兴趣的:(JZ2440,linux)