• JZ2440 裸机驱动 第11章 通用异步收发器UART


    本章目标:

    了解UART原理;
    掌握S3C2410/S3C2440中UART的使用

    11.1 UART原理及UART内部使用方法

    11.1.1 UART原理说明

        UART用于传输串行数据:
        发送数据时,CPU将并行数据写入UART,UART按照一定的格式在一根电线上串行发出;
        接收数据时,UART检测另一根电线上的信号,将串行收集放在缓冲区中,CPU即可读取
    UART获得这些数据。
        UART之间以全双工方式传输数据,最精简的连接方式只有3根线:TxD、RxD、Gnd。
        UART使用标准的TTL/CMOS逻辑电平(0~5V、0~3.3V、0~2.5V或0~1.8V)来表示数据,
    高电平表示1,低电平表示0.为了增强数据抗干扰能力、提高传输长度,通常将TTL/CMOS
    逻辑电平转换为RS-232逻辑电平,3~12V表示0,-3~-12V表示1。

    11.1.2 S3C2410/S3C2440 UART的特性

        S3C2410/S3C2440中UART功能类似,有3个独立的通道,每个通道都可以工作于中断
    或DMA模式。即UART可以发出中断或DMA请求以便在UART、CPU间传输数据。
        UART由波特率发生器、发送器、接收器和控制逻辑组成。
        使用系统时钟时,S3C2410的UART波特率可以达到230.4Kbit/s,S3C2440则可以达到
    115.2Kbit/s;如果使用UEXTCLK引脚提供的外部时钟,则可以达到更高的波特率。波特率
    可以通过编程进行控制。
        S3C2410 UART的每个通道都有16字节的发送FIFO和16字节的接收FIFO,S3C2440 UART
    的FIFO深度为64。
        S3C2410/S3C2440 UART的每个通道支持的停止位有1、2位,数据位有5、6、7或8位,
    支持校验功能,另外还有红外发送/接收功能。

    11.1.3 S3C2410/S3C2440 UART的使用

        在使用UART之前需要设置波特率、传输格式(多少数据位、是否使用校验位、是奇校验
    还是偶校验、有多少停止位、是否使用流量控制);对于S3C2410/S3C2440,还要选择所涉及
    管脚为UART功能、选择UART通道的工作模式为中断模式或DMA模式。设置好之后,向某个寄存器
    中写入数据即可发送,读取某个寄存器即可得到接收到的数据。可以通过查询状态寄存器或
    设置中断来获知数据是否已经发送完毕、是否已经接收到数据。
        针对上述过程,下面一一说明。
    1.将所涉及的UART通道管脚设为UART功能
    2.UBRDIVn寄存器(UART BAUD RATE DIVISOR):设置波特率
        S3C2410 UART 的时钟源有两种选择:PCLK、UEXTCLK;S3C2440 UART的时钟源有
    三种选择:PCLK、UEXTCLK、FCLK/n,其中n的值通过UCON0~UCON2联合设置。
        根据给定的波特率、所选择的时钟源的频率,可以通过如下公式计算UBRDIVn寄
    存器(n为0~2,对应3个UART通道)的值。
        UBRDIVn = (int)(UART clock / (buad rate * 16)) - 1
        上面计算出来的UBRDIVn寄存器不一定是整数,只要其误差在1.87%之内即可。误
    差计算公式如下:
        tUPCLK = (UBRDIVn + 1) * 16 * 1Frame/(UART clock)    //实际的UART时钟
        tUEXACT = 1Frame / baud-rate                  //理论的UART时钟
        UART error = (tUPCLK - tUEXACT) / tUEXACT * 100%     //误差
    3.ULCONn寄存器(n:0~2)(UART LINE CONTROL):设置传输格式
    4.UCONn寄存器(UART CONTROL)
        UCONn寄存器用于选择UART时钟源、设置UART中断方式等。由于S3C2410的UART
    只有两个时钟源,S3C2440有3个时钟源,所以在选择和设置时钟源时有所不同。
        S3C2410的UCONn寄存器格式如下表11.2所示。
        S3C2440的UCONn寄存器在UART时钟的选择方面与S3C2410有所不同,从位[10]往上
    的位含义不一样,并且原来的位[4]用于徐娜则是否发出“break”信号,这些位的含义
    如表11.3所示。
        UCON0、UCON1、UCON2这三个寄存器的位[15:12]一起用来确定n的值,它们的意义如下。
        (1)UCON2[15]:“FCLK/n”使能位。
        它等于0时,禁止使用“FCLK/n”作为UART时钟源;等于1时,可以用作UART时钟源。
        (2)n的值设置。
        UCON0[15:12]、UCON1[15:12]、UCON2[14:12]三者用于设置n的值,当其中一个被射程非
    0时,其他两个必须为0.
        ① n的值处于7~21处时,UART时钟 = FCLK/(divider + 6),divider 为UCON0[15:12]的值,大于0。 
        ② n的值处于22~36时,UART时钟 = FCLK/(divider + 21),divider为UCON1[15:12]的值,大于0。
        ③ n的值处于37~43时,UART时钟 = FCLK/(divider + 36),divider为UCON2[14:12]的值,大于0.
        ④ UCON0[15:12]、UCON1[15:12]、UCON2[14:12]都等于0时,UART时钟:FCLK/44。
    5.UFCONn寄存器(UART FIFO CONTROL)、UFSTATn寄存器(UART FIFO STATUS)
        UFCONn寄存器用于设置是否使用FIFO,设置个FIFO的触发值。并可以通过设置UFCONn
    寄存器来复位个寄存器。
        读取UFSTATn寄存器可以知道各个FIFO是否已经满、其中有多少个数据。
        不使用FIFO时,可以认为FIFO的深度为1,使用FIFO时,S3C2410的FIFO深度为16,
    S3C2440的FIFO深度为64.这两类寄存器各位的含义请参考数据手册,后面的实例部分没有
    使用FIFO。
    6.UMCONn寄存器(UART MODEM CONTROL)、UMSTATn寄存器(UART MODEM STATUS)
        这两类寄存器用于流控,这里不介绍。
    7.UTRSTATn寄存器(UART TX/RX STATUS)
        UTRSTATn寄存器用于表明数据是否已经发送完毕、是否已经接收到数据,格式如表11.4
    所示。缓冲区起始就是图11.3中的FIFO,只不过不使用FIFO功能时可以认为其深度为1。
    8.UERSTATn寄存器(UART ERROR STATUS)
        用于表示各种错误是否发生,位[0]~位[3]为1时分别表示溢出错误、校验错误、帧错误、
    检测到“break”信号。读取这个寄存器时,它会自动清零。
        注意:接收数据时,如果使用FIFO,则UART内部会使用一个“错误FIFO”来表明接收FIFO
    中哪个数据在接收过程中发生了错误。CPU只有在读出这个错误的数据时,才会察觉到发生了错
    误。要想清除“错误FIFO”,则必须读出错误的数据,并读出UERSTATn寄存器。
    9.UTXHn寄存器(UART TRANSMIT BUFFER REGISTER)
        CPU将数据写入这个寄存器,UART即将它保存到缓冲区中,并自动发送出去。
    10.URXHn寄存器(UART RECEIVE BUFFER REGISTER)
        当UART接收到数据时,CPU读取这个寄存器,即可获得数据。

    11.2 UART 操作实例

    11.2.1 代码详解

        本实例代码在/work/hardware/uart目录下。目的是在串口上输入一个字符,单板收到后将
    它的ASCII值加1后,从串口输出。
        首先设置MPLL提高系统时钟,令PCLK为50MHz,UART将选择PCLK作为时钟源。将代码复制到
    SDRAM之后,调用main函数。这些代码与第10章相似。重点在于main函数对UART0的初始化、收发
    数据,这由3个函数实现:usat0_init、getc和putc,它们在serial.c文件中。
    1.UART初始化
        uart0_init函数代码如下:
     1 #define PCLK              50000000    //init.c中的clock_init函数设置PCLK为50MHz
     2 #define UART_CLK          PCLK        //UART0的时钟源设为PCLK
     3 #define UART_BAUD_RATE    115200      
     4 #define UART_BRD          ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)
     5 
     6 /*
     7 *初始化UART0
     8 *115200, 8N1, 无流控
     9 */
    10 void uart0_init(void)
    11 {
    12     GPHCON |= 0xa0;    //GPH2、GPH3用作TXD0、RXD0
    13     GPHUP   = 0x0c;    //GPH2、GPH3内部上拉
    14     
    15     ULCON0 = 0x03;    //波特率:115200,8N1
    16     UCON0  = 0x05;    //查询方式,UART时钟源为PCLK
    17     UFCON0 = 0x00;    //不使用FIFO
    18     UMCON0 = 0x00;    //不使用流控
    19     UBRDIV0 = UART_BRD;    //波特率:115200
    20 }
    serial.c->uart0_init()
    2.发送字符的函数
        本实例不使用FIFO,发送字符前,先判断上一个字符是否已经发送出去。如果没有,
    则不断查询UTRSTAT0寄存器的位[2],当它为1时表示已经发送完毕。于是,即可向UTXH0寄
    存器中写入要发送的字符。代码如下(宏TXD0READY被定义为“(1 << 2)”):
     1 /*
     2 *发送一个字符
     3 */
     4 void putc(unaigned char c)
     5 {
     6     /*等待,直到发送缓冲区中的数据已经全部发送出去*/
     7     while(!(UTRSTAT0 & TXD0READY));
     8     
     9     /*向UTXH0寄存器中写数据,UART自动将其发送出去*/
    10     UTXH0 = c;
    11 }
    serial.c->putc()
    3.接收字符的函数
        试图读取数据前,先查询UTRSTAT0寄存器的位[1],当它为1时,表示接收缓冲区中有数据。
    于是,即可读取URXH0得到数据。代码如下(宏RXD0READY被定义为“(1)”):
     1 /*
     2 *接收字符
     3 */
     4 unsigned char getc(void)
     5 {
     6     /*等待,知道接收缓冲区中有数据*/
     7     while(!(UTRSTAT0 & RXD0READY));
     8     
     9     /*直接读取URXH0寄存器,即可获得接收到的数据*/
    10     return URXH0;
    11 }
    serial.c->getc()
    4.主函数
        初始化完UART0之后,不断地读取串口数据,并判断它是否是数据或字符。如果是的话,
    将它加1后从串口输出。代码如下:
     1 #include "serial.h"
     2 
     3 int main()
     4 {
     5     unsigned char c;
     6     uart0_init();    //波特率:115200,8N1
     7     
     8     while(1)
     9     {
    10         //从串口接收数据后,判断其是否为数字或字母,若是则加1后输出
    11         c = getc();
    12         if(isDigit(c) || (isLetter(c)))
    13             putc(c+1);
    14     }
    15     
    16     return 0;
    17 }
    main.c

    11.2.2 实例测试

    1.PC上的串口工具推荐
        Windows下推荐使用SecureCRT工具,Linux下推荐使用kermit。
        下面介绍在Linux下安装、使用kermit的方法。
        确保Linux能上网,然后使用下面命令安装,会安装一个kermit命令。
    $ sudo apt-get install ckermit
        在使用kermit之前,先建立一个配置文件,在/home/book(假设用户名为book)目录下创
    建名为.kermrc的文件,内容如下:
     1 set line /dev/ttyS0
     2 set speed 115200
     3 set carrier-watch off
     4 set handshake none
     5 set flow-control none
     6 robust
     7 set file type bin
     8 set file name lit
     9 set rec  pack 1000
    10 set send pack 1000
    11 set window 5
    配置kermrc
        然后,运行“$ sudo kermit -c”命令即可开启串口:要关闭串口,可以先输入
    “Ctrl+”,然后按住“C”键,最后输入“exit”后回车。
    2. 测试方法
        首先使用串口线将开发板的COM0和PC的串口连接,打开PC上的串口工具并设置其波
    特率为115200、8N1。
        然后,在uart目录下运行make命令生成可执行文件uart.bin,将它烧入NAND Flash
    中运行。
        最后,在PC上串口工具中输入数字或字母,可以看到输出另一个字符(加了1):如果
    输入其他字符,则无输出。
        /work/hardware/stdio目录下的程序在串口0上实现printf、scanf等函数,它使用
    scanf、sscanf和printf等函数从串口接收一个十进制数字序列,然后将它转换为十六进制
    输出。步骤与UART实例相似,读者可自行操作。
    附:代码:
    链接: https://pan.baidu.com/s/1kV24a9L 密码: tfab
  • 相关阅读:
    poj 3243 Clever Y(BabyStep GiantStep)
    poj 2417 Discrete Logging
    poj 3481 Double Queue
    hdu 4046 Panda
    hdu 2896 病毒侵袭
    poj 1442 Black Box
    hdu 2815 Mod Tree
    hdu 3065 病毒侵袭持续中
    hdu 1576 A/B
    所有控件
  • 原文地址:https://www.cnblogs.com/sz189981/p/7712409.html
Copyright © 2020-2023  润新知