嵌入式Linux应用开发完全手册(四)UART

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/guanchunsheng/article/details/77170634

11. 通用异步收发器 UART

11.1 UART原理,部件使用方法

11.1.1UART原理

UART是Universal Asynchronous Receiver Transmitter的缩写,即通用异步收发器
UART用来传输串行数据:
- 发送时,CPU将并行数据写入UART,UART按照一定格式在一根电线上串行发出
- 接收时,UART检测另一根电线上的信号,收集串行数据存放在缓冲区,供CPU读取

连线图

图中收发各有一条线,有的设备比如SIM卡,只有一条线,收发共用。
- 2条线可以实现全双工
- 1条线可以实现半双工

电平逻辑

  • TTL/CMOS
    • 5V,3.3V,2.5V/1.8V 表示1
    • 0V,表示0
  • RS-232
    • 3~12V 表示1
    • -3~-12V表示0

传输结构

  • 位,最小单元
  • 帧,不可分割的若干位
    • 开始位
    • 数据位
    • 校验位(可选)
    • 停止位

波特率
每一位所需时间的倒数,即每秒可以传输的位数。

波形
Markdown
上图是一个7位数据位的帧波形。

  • 空闲状态,高电平
  • 拉低1位,表示帧开始
  • 然后按照次序,按照约定的每帧数据位数,传输数据
  • 数据位全部传输完成后,传输校验位,校验方式提前约定,可以是奇校验,也可以是偶校验
  • 下一位是停止位,拉高电平
  • 停止位长度可以设置,1,1.5,2,为了保护各个帧,防止错乱

11.1.2 2440的UART

3个通道

  • UART0
  • UART1
  • UART2

工作模式

  • 中断模式
  • DMA模式

收发过程
发数据

  • 2440的UART由深度64的FIFO控制
  • CPU写入数据到FIFO
  • UART将FIFO的数据复制到“发送移位寄存器”Transmit Shifter
  • Shifter将数据发送的TxDn数据线上

收数据

  • 数据线RxDn上的数据进入接收移位器,Receive Shifter
  • Shifter复制数据到FIFO中
  • CPU从FIFO中读取数据
    Markdown

11.1.3 2440的UART使用

使用前的设置

  • 波特率
  • 传输格式
    • 数据位宽度
    • 是否使用校验位
    • 奇校验还是偶校验
    • 多少个停止位
    • 是否使用流量控制
    • 设置管脚
      • 对应管脚设置位UART管脚
    • UART通道工作模式
      • 中断模式
      • DMA模式

设置好以后的使用

  • 往某寄存器写入数据即发送
  • 读取某个寄存器即获得接收到的数据
  • 发送完毕,接收到数据的信息获取
    • 查询状态寄存器
    • 设置中断

1. UART管脚设置

  • UART通道0
    • TxD0 GPH2
    • RxD0 GPH3

所以GPH2和GPH3需要设置成TxD0和RxD0管脚。

2. 波特率 UBRDIVn寄存器
根据芯片手册,时钟频率,波特率和UBVRDIVn寄存器的数学关系如下

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

那么如果给定时钟频率是40MHz,波特率要求是115200,那么可以计算出寄存器UBRDIVn的设置值应该是

UBRDIVn = int(40000000/(115200 x 16)) - 1
        = int(21.7) - 1        /*取最近接的整数*/
        = 22 - 1
        = 21

3. 传输格式 ULCONn寄存器
Markdown
传输格式包括这几个方面的设置

  • 红外模式开关
  • 校验位设置
  • 停止位设置
  • 数据位宽度

其他几项的含义非常直观,不赘述。

红外模式的含义这里按照芯片手册复述一下,来龙去脉并不清楚。
下图是正常的串口波形,高电平是1,低电平是0。
Markdown

下图是红外模式的波形:
Markdown
从图中可以看出,在红外模式中:

  • 发送时,通过宽度是3/16位的脉冲,判断当前位是0,如果没有脉冲,当前位是1
  • 接收时,通过宽度是3/16位的脉冲,发出信号0,如果是1,不发出脉冲

4. UART控制寄存器 UCONn
Markdown

从低位往高位分析:
Markdown
接收/接收模式

  • 00 关闭,禁止接收
  • 01 中断或者查询模式
  • 10 ,11 DMA模式

Markdown
break信号
自环模式
接收错误状态中断

  • 0 正常模式
  • 1 打开/启用

Markdown
接收超时中断

  • 0 关闭
  • 1 打开

发送/接收中断类型

  • 0 脉冲
  • 1 电平

Markdown
时钟选择

  • 00 10 PCLK
  • 01 UEXTCLK
  • 11 FCLK/n

PCLK是用于串口等速度较慢的外设的时钟(用于APB 总线设备),FCLK是内核晶振的频率(用于CPU),HCLK用于液晶,内存等高速设备(用于AHB总线设备),UEXTCLK是外接的时钟,用于UART
在2440中,这几个时钟的频率如下

  • FCLK <= 400MHz
  • HCLK <= 136MHz
  • PCLK <= 68MHz

Markdown
最后如果选择了FCLK/n,那么这个n由FCLK Divider确定。
规则比较复杂:

  • UCON2[15] 是使能位,决定是否允许FCLK/n作为时钟源
  • n的确定
    • n = 7 ~ 21
      • n = divider + 6, divider = UCON0[15:12]
    • n = 22 ~ 36
      • n = divider + 21, divider = UCON1[15:12]
    • n = 37 ~ 43
      • n = divider + 36, divider = UCON2[14:12]
    • n = 44
      • UCON0[15:12], UCON1[15:12], UCON2[14:12]都是0

5. FIFO配置(UFCONn寄存器),FIFO状态(UFSTATn)
见下图,含义比较明显,不赘述。可以使用FIFIO队列,也可以不使用。本篇的实例就没有使用FIFO
Markdown
Markdown

6. 流量控制(UMCONn),流量状态(UMSTATn)
本篇不涉及。

7. 发送/接收状态(UTRSTATn)
Markdown
记录这3个状态信息

  • 接收缓冲数据就绪,接收到数据时,自动被设置为1
    • 0 空
    • 1 接收到数据
  • 发送缓冲为空, 发送缓冲区内没有数据时,自动设为1
    • 0 缓冲不为空
    • 1 空
  • 发送器空, 发送缓冲区中没有数据,并且最后一个数据也发送出去了,自动设为1
    • 0 非空
    • 1 空

8. 错误状态(UERSTATn)
Markdown
4种错误,见表格。
读取这个寄存器时,会自动清0。

9. 发送缓冲寄存器(UTXHn)
Markdown
CPU将数据写入这个寄存器,UART会立即将它保存到缓冲区中,并自动发送。

10. 接收缓冲寄存器(URXHn)
Markdown
UART接收到数据时,CPU读取这个寄存器,就可以获得数据。

11.2 UART操作实例

在串口上接收一个字符,然后ASCII + 1,从串口输出

1. UART初始化

#include "s3c24xx.h"
#include "serial.h"

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

#define PCLK            50000000    // init.c中的clock_init函数设置PCLK为50MHz
#define UART_CLK        PCLK        //  UART0的时钟源设为PCLK
#define UART_BAUD_RATE  115200      // 波特率
#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)

/*
 * 初始化UART0
 * 115200,8N1,无流控
 */
void uart0_init(void)
{
    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 = UART_BRD; // 波特率为115200
}

2. 发送字符函数

/*
 * 发送一个字符
 */
void putc(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(UTRSTAT0 & TXD0READY));

    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
    UTXH0 = c;
}

3. 接收字符函数

/*
 * 接收字符
 */
unsigned char getc(void)
{
    /* 等待,直到接收缓冲区中的有数据 */
    while (!(UTRSTAT0 & RXD0READY));

    /* 直接读取URXH0寄存器,即可获得接收到的数据 */
    return URXH0;
}

4. 主函数

#include "serial.h"

int main()
{
    unsigned char c;
    uart0_init();   // 波特率115200,8N1(8个数据位,无校验位,1个停止位)

    while(1)
    {
        // 从串口接收数据后,判断其是否数字或子母,若是则加1后输出
        c = getc();
        if (isDigit(c) || isLetter(c))
            putc(c+1);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/guanchunsheng/article/details/77170634