s3c2440串口使用

一、串口通信原理

UART:Universal Asynchronous Receiver/Transmitter(通用异步收发送器),用来传输串行数据,发送数据时,CPU将并行数据写入UART,UART按照一定格式在TxD线上串行发出;接收数据时,UART检测到RxD线上的信号,将串行收集放到缓冲区中,CPU即可读取UART获得的这些数据。


UART最精简的连线形式只有3根线,TXD用于发送,RXD用于接收,GND用于提供参考电平。UART之间以帧作为数据传输单位,帧由具有完整意义的若干位组成,它包含开始位、数据位、校验位和停止位。发送数据之前,互相通信的UART之间要约定好数据传输速率(波特率的倒数)、数据的传输格式(多少个数据位、是否使用校验位、奇校验还是偶校验、多少个停止位)。

二、S3C2440串口介绍

s3c2440提供了三个UART端口,它们都可以通过查询、中断和DMA方式传输数据,而且每个UART都分别有一个64个字节的接收FIFO和一个64个字节的发送FIFO。如下图所示:每个UART包含一个波特率发生器、发送器、接收器和一个控制单元。波特率发生器可以由PCLK、FCLK/n或UEXTCLK(外部输入时钟)时钟驱动。UART通过使用系统时钟可以支持最高115.2Kbps的比特率。如果是使用外部器件提供UEXTCLK的UART,则UART可以运行在更高的速度。发送器和接收器各包含一个64字节的FIFO和数据移位器。要发送数据时,先将数据写入到FIFO接着在发送前复制到发送移位器中,随后将数据从发送数据引脚(TXDn)移出;接收数据时,从接收数据引脚(RXDn)移入收到的数据,接着从移位器复制到FIFO。

三、S3C2440的串口寄存器配置

3.1 串行数据格式设置



例子:ULCON0=0x03    //8N1(正常模式 8个数据位,无较验,1个停止位)

3.2 UART控制寄存器





例子:UCON=0x05 //时钟使用PLCK,中断为边沿触发,禁止接受和发送超时,不产生接受错误中断,不采用RX、TX直接相连的调节模式,rx和tx处于中断或者查询模式。

3.3 UART发送接受状态寄存器

当程序使用的是查询方式收发数据时,必须通过读取UART寄存器的值来正确控制UART数据的收发。



#define TXD0READY   (1<<2)
#define RXD0READY   (1)

//发送字符
void putc(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(UTRSTAT0 & TXD0READY));
    
    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
    UTXH0 = c;
}

//接受字符
unsigned char getc(void)
{
    /* 等待,直到接收缓冲区中的有数据 */
    while (!(UTRSTAT0 & RXD0READY));
    
    /* 直接读取URXH0寄存器,即可获得接收到的数据 */
    return URXH0;
}

3.4  发送和接受缓冲寄存器

当串口有数据可读时,程序直接读取URXHn,当用户想发送数据时直接将数据写入到UTXHn即可。


3.5 波特率寄存器

UBRDIVn由下列表达式确定:
UBRDIVn=(int)(UART时钟/(波特率*16))-1


假如uart0采用中断模式来收发数据我们必须要做三件事:

        1.在程序的启动代码中开中断

        2.设置INTMSK和INTSUBMSK,打开uart0中断

        3.配置GPH2,GPH3用作TXD0,RXD0

四、串口中断控制led灯实例(tq2440)

启动代码:初始化时钟、led引脚配置、uart0初始化以及中断相关设定

.extern     main
.text 
.global _start 
_start:
@******************************************************************************       
@ 异常向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
@******************************************************************************       
    b   Reset

@ 0x04: 未定义指令中止模式的向量地址
HandleUndef:
    b   HandleUndef 
 
@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
    b   HandleSWI

@ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
    b   HandlePrefetchAbort

@ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:
    b   HandleDataAbort

@ 0x14: 保留
HandleNotUsed:
    b   HandleNotUsed

@ 0x18: 中断模式的向量地址
    b   HandleIRQ

@ 0x1c: 快中断模式的向量地址
HandleFIQ:
    b   HandleFIQ

Reset:                  
    ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈
    bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启
    
    msr cpsr_c, #0xd2       @ 进入中断模式
    ldr sp, =3072           @ 设置中断模式栈指针

    msr cpsr_c, #0xd3       @ 进入管理模式
    ldr sp, =4096           @ 设置管理模式栈指针,
                            @ 其实复位之后,CPU就处于管理模式,
                            @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略
                           
    bl  clock_init
    bl  init_led            @ 初始化LED的GPIO管脚
    bl  init_irq            @ 调用中断初始化函数,在init.c中
    msr cpsr_c, #0x53       @ 设置I-bit=0,开IRQ中断
    
    ldr lr, =halt_loop      @ 设置返回地址
    ldr pc, =main           @ 调用main函数
halt_loop:
    b   halt_loop

HandleIRQ:
    sub lr, lr, #4                  @ 计算返回地址
    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器
                                    @ 注意,此时的sp是中断模式的sp
                                    @ 初始值是上面设置的3072
    
    ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址  
    ldr pc, =EINT_Handle            @ 调用中断服务函数,在interrupt.c中
int_return:
    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr

启动代码中使用到的初始化代码

#define	GPB5_out	(1<<(5*2))
#define	GPB6_out	(1<<(6*2))
#define	GPB7_out	(1<<(7*2))
#define	GPB8_out	(1<<(8*2))

#define	GPB5_msk	(3<<(5*2))
#define	GPB6_msk	(3<<(6*2))
#define	GPB7_msk	(3<<(7*2))
#define	GPB8_msk	(3<<(8*2))

/*
 * K1,K2,K3,K4对应GPF1、GPF4、GPF2、GPF0
 */
#define GPF0_int     (0x2<<(0*2))
#define GPF1_int     (0x2<<(1*2))
#define GPF2_int     (0x2<<(2*2))
#define GPF4_int     (0x2<<(4*2))

#define GPF0_msk    (3<<(0*2))
#define GPF1_msk    (3<<(1*2))
#define GPF2_msk    (3<<(2*2))
#define GPF4_msk    (3<<(4*2))

/*
 * 关闭WATCHDOG,否则CPU会不断重启
 */
void disable_watch_dog(void)
{
    WTCON = 0;  // 关闭WATCHDOG很简单,往这个寄存器写0即可
}

void init_led(void)
{
	// LED1,LED2,LED3,LED4对应的4根引脚设为输出
	GPBCON &= ~(GPB5_msk | GPB6_msk | GPB7_msk | GPB8_msk);
	GPBCON |= GPB5_out | GPB6_out | GPB7_out | GPB8_out;
}

/*
 * 初始化GPIO引脚为外部中断
 * GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD)
 */ 
void init_irq( )
{

     INTMSK &=~(1<<28);  //开启uart0中断
     INTSUBMSK &=~(3<<0);//开启uart0的接收和发送中断
     
     GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
     GPHUP   = 0x0c;     // GPH2,GPH3内部上拉

     ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)
     UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK
     UFCON0  = 0x00;     // 不使用FIFO
     UMCON0  = 0x00;     // 不使用流控
     UBRDIV0 = 0x1a;     // 波特率为115200
            
    /*
     * 设定优先级:
     * ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ2 > REQ3,即EINT0 > EINT1 > EINT2
     * 仲裁器1、6无需设置
     * 最终:
     * EINT0 > EINT1> EINT2 > EINT4 即K4 > K1 > K3 > K2
     */
    PRIORITY = (PRIORITY & ((~0x01) | ~(0x3<<7)));

    // EINT0、EINT1、EINT2、EINT4_7使能
    INTMSK   &= (~(1<<0)) & (~(1<<1)) & (~(1<<2)) & (~(1<<4));
}

/*
 * 对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV
 * 有如下计算公式:
 *  S3C2410: MPLL(FCLK) = (m * Fin)/(p * 2^s)
 *  S3C2440: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s)
 *  其中: m = MDIV + 8, p = PDIV + 2, s = SDIV
 * 对于本开发板,Fin = 12MHz
 * 设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK=1:2:4,
 * FCLK=200MHz,HCLK=100MHz,PCLK=50MHz
 */
void clock_init(void)
{
    CLKDIVN  = 0x03;            // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1

    /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
__asm__(
    "mrc    p15, 0, r1, c1, c0, 0\n"        /* 读出控制寄存器 */ 
    "orr    r1, r1, #0xc0000000\n"          /* 设置为“asynchronous bus mode” */
    "mcr    p15, 0, r1, c1, c0, 0\n"        /* 写入控制寄存器 */
    );
    MPLLCON =((0x5c<<12)|(0x01<<4)|(0x02));  /* 现在,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz */
}
中断处理函数

#include "s3c24xx.h"

void EINT_Handle()
{
	/*
	 * K1,K2,K3,K4对应GPF1、GPF4、GPF2、GPF0
	 *            即 EINT1, ETIN4, EINT2, EINT0
	 *            oft为 1, 4, 2, 0 (对应INTMSK寄存器)
	 */
    
    switch(URXH0)
    {
        // K1被按下
        case '1': 
        {   
            GPBDAT |= (0xF<<5);   // 所有LED熄灭
            GPBDAT &= ~(1<<5);      // LED1点亮
            break;
        }       
        // K2被按下
        case '2':
        {   
            GPBDAT |= (0xF<<5);   // 所有LED熄灭
            GPBDAT &= ~(1<<6);      // LED2点亮
            break;
        }
        // K3被按下
        case '3':
        {   
            GPBDAT |= (0xF<<5);   // 所有LED熄灭
            GPBDAT &= ~(1<<7);      // LED3点亮
            break;
        }
       // K4被按下
        case '4':
        {   
            GPBDAT |= (0xF<<5);   // 所有LED熄灭
            GPBDAT &= ~(1<<8);      // LED4点亮
            break;
        }
        default:
            break;
    }
    //清中断
    SUBSRCPND = 3<<0;
    SRCPND    = 1<<28;
    INTPND    = 1<<28;
}
主函数

int main()
{
    while(1);
    return 0;
}
完整源码地址: http://pan.baidu.com/share/link?shareid=3888282204&uk=101680913

参考文档:http://www.cnblogs.com/idle_man/archive/2010/12/19/1910548.html


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值