嵌入式Linux之UART串口通信

UART串口通信

    • UART 通信格式
    • UART电平标准
    • I.MX6U UART 简介
    • 自动设置波特率

参考文档
一、【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.2
二、IMX6ULL参考手册

UART 通信格式

串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。使用两条线即可实现双向通信,一条用于发送,一条用于接收。串口通信距离远,但是速度相对会低,串口是一种很常用的工业接口。 I.MX6U 自带的 UART 外设就是串口的一种, UART 全称是 Universal Asynchronous Receiver/Trasmitter,也就是异步串行收发器。既然有异步串行收发器,那肯定也有同步串行收发器,学过 STM32 的同学应该知道, STM32除 了 有 UART 外 ,还有 另 外一 个 叫 做 USART 的 东 西。 USART 的全 称 是 Universal Synchronous/Asynchronous Receiver/Transmitter,也就是同步/异步串行收发器。 相比 UART 多了一个同步的功能,在硬件上体现出来的就是多了一条时钟线。 一般 USART 是可以作为 UART使用的,也就是不使用其同步的功能。

UART 作为串口的一种,其工作原理也是将数据一位一位的进行传输,发送和接收各用一条线,因此通过 UART 接口与外界相连最少只需要三条线: TXD(发送)、 RXD(接收)和 GND(地线),下图就是 UART 的通信格式:

嵌入式Linux之UART串口通信_第1张图片
各位的含义如下:

空闲位:数据线在空闲状态的时候为逻辑“1”状态,也就是高电平,表示没有数据线空闲,没有数据传输。

起始位:当要传输数据的时候先传输一个逻辑“0”,也就是将数据线拉低,表示开始数据传输。

数据位: 数据位就是实际要传输的数据,数据位数可选择 5~8 位,我们一般都是按照字节传输数据的,一个字节 8 位,因此数据位通常是 8 位的。低位在前,先传输,高位最后传输。

奇偶校验位: 这是对数据中“1”的位数进行奇偶校验用的,可以不使用奇偶校验功能。

停止位:数据传输完成标志位,停止位的位数可以选择 1 位、 1.5 位或 2 位高电平,一般都选择 1 位停止位。

波特率:波特率就是 UART 数据传输的速率,也就是每秒传输的数据位数,一般选择 9600、19200、 115200 等。

UART电平标准

UART 一般的接口电平有 TTL 和 RS-232,一般开发板上都有 TXD 和 RXD 这样的引脚,这些引脚低电平表示逻辑 0,高电平表示逻辑 1,这个就是 TTL 电平。 RS-232 采用差分线, -3~ -15V 表示逻辑 1, +3~ +15V 表示逻辑 0。
下图中的接口就是 TTL 电平:
嵌入式Linux之UART串口通信_第2张图片
上图中的模块就是 USB 转 TTL 模块, TTL 接口部分有 VCC、 GND、 RXD、 TXD、RTS 和 CTS。 RTS 和 CTS 基本用不到,使用的时候通过杜邦线和其他模块的 TTL 接口相连即可。

RS-232 电平需要 DB9 接口, I.MX6U-ALPHA 开发板上的 COM3(UART3)口就是 RS-232 接口的,如下图所示:
嵌入式Linux之UART串口通信_第3张图片
由于现在的电脑都没有 DB9 接口了,取而代之的是 USB 接口,所以就催生出了很多 USB
转串口 TTL 芯片,比如 CH340、PL2303 等。通过这些芯片就可以实现串口 TTL 转 USB。I.MX6UALPHA开发板就使用CH340 芯片来完成UART1 和电脑之间的连接,只需要一条USB 线即可。

I.MX6U UART 简介

I.MX6U 一共有 8 个 UART,其主要特性如下:

①、兼容 TIA/EIA-232F 标准,速度最高可到 5Mbit/S。
②、支持串行 IR 接口,兼容 IrDA,最高可到 115.2Kbit/s。
③、支持 9 位或者多节点模式(RS-485)。
④、 1 或 2 位停止位。
⑥、可编程的奇偶校验(奇校验和偶校验)。
⑦、自动波特率检测(最高支持 115.2Kbit/S)。

I.MX6U 的 UART 功能很多,但是我们本次就只介绍其最基本的串口功能,关于 UART 其它功能的介绍请参考《I.MX6ULL 参考手册》第 3561 页的“Chapter 55 Universal Asynchronous Receiver/Transmitter(UART)”章节。
UART 的时钟源是由寄存器 CCM_CSCDR1 的 UART_CLK_SEL(bit)位来选择的,当为 0 的时候 UART 的时钟源为 pll3_80m(80MHz),如果为 1 的时候 UART 的时钟源为 osc_clk(24M),一般选择 pll3_80m 作为 UART 的时钟源。寄存器 CCM_CSCDR1 的 UART_CLK_PODF(bit5:0)位是 UART 的时钟分频值,可设置 0~ 63,分别对应 1 ~64 分频,一般设置为 1 分频,因此最终
进入 UART 的时钟为 80MHz。接下来看一下 UART 几个重要的寄存器,第一个就是 UART 的控制寄存器 1,即UARTx_UCR1(x=1~8),此寄存器的结构如下图所示:
嵌入式Linux之UART串口通信_第4张图片
寄存器 UARTx_UCR1 我们用到的重要位如下:

ADBR(bit14):自动波特率检测使能位,为 0 的时候关闭自动波特率检测,为 1 的时候使能自动波特率检测。

UARTEN(bit0): UART 使能位,为 0 的时候关闭 UART,为 1 的时候使能 UART。

接下来看一下 UART 的控制寄存器 2,即: UARTx_UCR2,此寄存器结构如下图所示:

嵌入式Linux之UART串口通信_第5张图片
寄存器 UARTx_UCR2 用到的重要位如下:

IRTS(bit14):为 0 的时候使用 RTS 引脚功能,为 1 的时候忽略 RTS 引脚。

PREN(bit8):奇偶校验使能位,为 0 的时候关闭奇偶校验,为 1 的时候使能奇偶校验。

PROE(bit7):奇偶校验模式选择位,开启奇偶校验以后此位如果为 0 的话就使用偶校验,此位为 1 的话就使能奇校验。

STOP(bit6):停止位数量,为 0 的话 1 位停止位,为 1 的话 2 位停止位。

WS(bit5):数据位长度,为 0 的时候选择 7 位数据位,为 1 的时候选择 8 位数据位。

TXEN(bit2):发送使能位,为 0 的时候关闭 UART 的发送功能,为 1 的时候打开 UART的发送功能。

RXEN(bit1):接收使能位,为 0 的时候关闭 UART 的接收功能,为 1 的时候打开 UART的接收功能。

SRST(bit0):软件复位,为 0 的是时候软件复位 UART,为 1 的时候表示复位完成。复位完成以后此位会自动置 1, 表示复位完成。此位只能写 0,写 1 会被忽略掉。

接下来看一下 UARTx_UCR3 寄存器,此寄存器结构如下图所示:
嵌入式Linux之UART串口通信_第6张图片
寄存器 UARTx_UCR3 中的位 RXDMUXSEL(bit2),这个位应该始终为 1,这个在《I.MX6ULL 参考手册》第 3624 页有说明。

接下来看一下寄存器 UARTx_USR2,这个是 UART 的状态寄存器 2,此寄存器结构如下图所示:
嵌入式Linux之UART串口通信_第7张图片
寄存器 UARTx_USR2 用到的重要位如下:

TXDC(bit3):发送完成标志位,为 1 的时候表明发送缓冲(TxFIFO)和移位寄存器为空,也就是发送完成,向 TxFIFO 写入数据此位就会自动清零。

RDR(bit0):数据接收标志位,为 1 的时候表明至少接收到一个数据,从寄存器UARTx_URXD 读取数据接收到的数据以后此为会自动清零。

接 下 来 看 一 下 寄 存 器 UARTx_UFCR 、 UARTx_UBIR 和 UARTx_UBMR

寄 存 器UARTx_UFCR 中我们要用到的是位 RFDIV(bit9:7),用来设置参考时钟分频,设置如下表所示:
嵌入式Linux之UART串口通信_第8张图片
通过这三个寄存器可以设置 UART 的波特率,波特率的计算公式如下:
在这里插入图片描述
Ref Freq:经过分频以后进入 UART 的最终时钟频率。

UBMR:寄存器 UARTx_UBMR 中的值。

UBIR:寄存器 UARTx_UBIR 中的值。

通过 UARTx_UFCR 的 RFDIV 位、 UARTx_UBMR 和UARTx_UBIR 这三者的配合即可得到我们想要的波特率。
比如现在要设置 UART 波特率为 115200,那么可以设置 RFDIV 为5(0b101),也就是 1 分频,因此 Ref Freq=80MHz。设置 UBIR=71, UBMR=3124,根据上面的公式可以得到:
在这里插入图片描述
最后来看一下寄存器 UARTx_URXD 和 UARTx_UTXD,这两个寄存器分别为 UART 的接收和发送数据寄存器,这两个寄存器的低八位为接收到的和要发送的数据。读取寄存器UARTx_URXD 即可获取到接收到的数据,如果要通过 UART 发送数据,直接将数据写入到寄存器 UARTx_UTXD 即可。

自动设置波特率

uart_setbaudrate,这个函数是从NXP 官方的 SDK 包里面移植过来的,用于设置波特率。我们只需将要设置的波特率告诉此函数,此函数就会使用逐次逼近方式来计算出寄存器 UART1_UFCR 的 FRDIV 位、寄存器UART1_UBIR 和寄存器 UART1_UBMR 这三个的值。

函数已贴出,有兴趣自行研究

注:在此函数中使用到了除法运算,因此在链接的时候需要将编译器的数学库也链接进来。

/*
 * @description 		: 波特率计算公式,
 *    			  	  	  可以用此函数计算出指定串口对应的UFCR,
 * 				          UBIR和UBMR这三个寄存器的值
 * @param - base		: 要计算的串口。
 * @param - baudrate	: 要使用的波特率。
 * @param - srcclock_hz	:串口时钟源频率,单位Hz
 * @return		: 无
 */
void uart_setbaudrate(UART_Type *base, unsigned int baudrate, unsigned int srcclock_hz)
{
    uint32_t numerator = 0u;		//分子
    uint32_t denominator = 0U;		//分母
    uint32_t divisor = 0U;
    uint32_t refFreqDiv = 0U;
    uint32_t divider = 1U;
    uint64_t baudDiff = 0U;
    uint64_t tempNumerator = 0U;
    uint32_t tempDenominator = 0u;

    /* get the approximately maximum divisor */
    numerator = srcclock_hz;
    denominator = baudrate << 4;
    divisor = 1;

    while (denominator != 0)
    {
        divisor = denominator;
        denominator = numerator % denominator;
        numerator = divisor;
    }

    numerator = srcclock_hz / divisor;
    denominator = (baudrate << 4) / divisor;

    /* numerator ranges from 1 ~ 7 * 64k */
    /* denominator ranges from 1 ~ 64k */
    if ((numerator > (UART_UBIR_INC_MASK * 7)) || (denominator > UART_UBIR_INC_MASK))
    {
        uint32_t m = (numerator - 1) / (UART_UBIR_INC_MASK * 7) + 1;
        uint32_t n = (denominator - 1) / UART_UBIR_INC_MASK + 1;
        uint32_t max = m > n ? m : n;
        numerator /= max;
        denominator /= max;
        if (0 == numerator)
        {
            numerator = 1;
        }
        if (0 == denominator)
        {
            denominator = 1;
        }
    }
    divider = (numerator - 1) / UART_UBIR_INC_MASK + 1;

    switch (divider)
    {
        case 1:
            refFreqDiv = 0x05;
            break;
        case 2:
            refFreqDiv = 0x04;
            break;
        case 3:
            refFreqDiv = 0x03;
            break;
        case 4:
            refFreqDiv = 0x02;
            break;
        case 5:
            refFreqDiv = 0x01;
            break;
        case 6:
            refFreqDiv = 0x00;
            break;
        case 7:
            refFreqDiv = 0x06;
            break;
        default:
            refFreqDiv = 0x05;
            break;
    }
    /* Compare the difference between baudRate_Bps and calculated baud rate.
     * Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)).
     * baudDiff = (srcClock_Hz/divider)/( 16 * ((numerator / divider)/ denominator).
     */
    tempNumerator = srcclock_hz;
    tempDenominator = (numerator << 4);
    divisor = 1;
    /* get the approximately maximum divisor */
    while (tempDenominator != 0)
    {
        divisor = tempDenominator;
        tempDenominator = tempNumerator % tempDenominator;
        tempNumerator = divisor;
    }
    tempNumerator = srcclock_hz / divisor;
    tempDenominator = (numerator << 4) / divisor;
    baudDiff = (tempNumerator * denominator) / tempDenominator;
    baudDiff = (baudDiff >= baudrate) ? (baudDiff - baudrate) : (baudrate - baudDiff);

    if (baudDiff < (baudrate / 100) * 3)
    {
        base->UFCR &= ~UART_UFCR_RFDIV_MASK;
        base->UFCR |= UART_UFCR_RFDIV(refFreqDiv);
        base->UBIR = UART_UBIR_INC(denominator - 1); //要先写UBIR寄存器,然后在写UBMR寄存器,3592页 
        base->UBMR = UART_UBMR_MOD(numerator / divider - 1);
    }
}

你可能感兴趣的:(Linux)