zoukankan      html  css  js  c++  java
  • 05-笔记:LPC1788-串口

    概述

    LPC178x/177x 系列 Cortex-M3 具有 5 个符合 16C550 工业标准的异步串行口 UART0、UART1、UART2、UART3 和 UART4。
    其中,各串行口的区别是:UART1 比 UART0、2、3 增加了 Modem 接口;
    UART4 较 UART0、2、3 增加了 IrDA 接口和符合 ISO7816-3 的智能卡接口,其余都基本相同。
    注:默认情况下, UART2 、 UART3 和 UART4 都被关闭,以节省功耗。若需使用 UART2 、 UART3 和 UART4 ,请设置 PCONP 寄存器的相关位。此外,在不启用 Modem 、 IrDA 等模式时, UART1 和 UART4 也可以和 UART0 、2 、 3 一样,当普通 UART 来用。
     

    特性

     数据大小为 5、6、7、8 位;
     奇偶发生和校验:奇、偶标记,空格或没有;
     1 个或 2 个停止位;
     16 字节收发 FIFO;
     内置波特率发生器,包括小数波特率分频器用于各种功能;
     支持 DMA 发送和接收;
     自动波特率功能;
     间隔发生和检测;
     多处理器寻址模式;
     UART1 包含标准 Modem 接口信号(CTS、DCD、DTS、DTR、RI、RTS);
     UART4 包含支持红外通信的 IrDA 模式;
     UART0/2/3/4 支持软件流控制;
     UART0/2/3/4 支持 RS-458/EIA-485,其中 UART4 支持 9-位模式和输出使能;
     UART4 支持可选择的同步发送或接收模式;
     UART4 支持可选择的遵循 ISO 7816-3 规范的智能卡接口。
     

    引脚描述

    [1] 当 UARTn 的两根或两根以上 RXDn 脚启用时,仅引脚编号最小的那根 RXD 脚有效,而成为 UARTn的数据输入引脚。此时,其它被复用为 RXD 的引脚对 UARTn 输入没有影响。例如 RXD3 对应的四根引脚 P0.1 、 P0.3 、 P0.26 、 P4.29 若同时启用为 UART 数据接收脚,那么,仅 P0.1 脚能有效接收数据。
    [2] 当 UARTn 的 TXDn 脚全部启用时,所有 TXDn 引脚都会彼此独立地输出相同信号。
    只有 UART1 才具有 Modem 接口,因此 UART0、UART2、UART3 和 UART4 不具有 CTS1、
    DCD1、DSR1、RI1、DTR1 和 RTS1 引脚。
    只有 UART4 支持同步模式,因此 UART0、UART1、UART2 和 UART3 不具有 SCLK 引脚。
     
     
    UART0 和 UART1、UART2、UART3、UART4 各有一个独立波特率发生器,它们的功能
    都是相同的,我们以 UART0 波特率发生器(U0BRG)为例进行说明。
    U0BRG 产生 UART0 发送模块所需的时钟。UART0 波特率发生器时钟源为 APB 时钟
    (PCLK)。时钟源与 U0DLL 和 U0DLM 寄存器所确定的除数相除得到 UART0 Tx 模块所需时
    钟,该时钟必须为目标波特率的 16 倍。
     
    LPC178x/177x 系列 Cortex-M3 UART 部分的寄存器结构如图 5.16 所示。其中,UART1 具
    有 Modem 模块,UART4 具有 IrDA 模块和智能卡接口模块,UART0/2/3/4 具有 485 模块。
     
     
    LPC178x/177x 系列 Cortex-M3 的五个 UART,具有 16 字节的收发 FIFO,内置波特率发
    生器,五个串口具有基本相同的寄存器,其中 UART1 带有完全的调制解调器控制握手接口。
    在大多数异步串行通讯的应用中,并不需要完整的 Modem 接口信号(辅助控制信号),而只使用
    TXD、RXD 和 GND 信号即可。
     

    UART 中断

    LPC178x/177x 系列 Cortex-M3 UART 接口具有中断功能,而且由嵌套向量中断控制器
    (NVIC)管理,UART0、UART1、UART2、UART3 和 UART4 中断分别位于 NVIC 中断通道
    21、通道 22、通道 23、通道 24 和通道 51。
    以 UART0 为例,UART0 接口中断与嵌套向量中断控制器(NVIC)的关系如图 5.24 所示。
    UART0 中断占用 NVIC 的通道 21,中断使能寄存器 ISER 用来控制 NIVC 通道的中断使能。
    当 ISER0[5]=1 时,通道 21 中断使能,即 UART0 中断使能。
    中断优先级寄存器 IPR 用来设定 NIVC 通道中断的优先级。IPR1[15:11]用来设定通道 21
    的优先级,即 UART0 中断的优先级。具体的设定方法可参考“嵌套向量中断控制器(NVIC)”
    一节。
    当 UART0 接口的优先级设定且中断使能后,若触发条件满足时,则会触发中断。当处理
    器响应中断后将自动定位到中断向量表,并根据中断号从向量表中找出 UART0 中断处理的入
    口地址,然后 PC 指针跳转到该地址处执行中断服务函数。因此,用户需要在中断发生前将
    UART0 的中断服务函数地址(UART0_IRQHandler)保存到向量表中。
    UART 中断主要分为 5 类:接收中断、发送中断、接收线状态中断、Modem 中断和自动波
    特率中断,如图 5.25 所示。其中,接收线状态中断指接收过程中发生了错误,即接收错误中断。
    只有 UART1 接口具有 Modem 中断,其它 UART 接口由于没有 Modem 功能,所以没有 Modem
    中断。自动波特率中断包括自动波特率结束中断和超时中断。

    UARTn 中断标志寄存器(UnIIR,n = 0~4)

    UnIIR 提供状态码用于指示挂起中断 [1] 的中断源和优先级,5 个 UART 的 IIR 寄存器之概况如表 5.29
    所列。其中,只有 UART1 具有 Modem 中断。[1] “挂起中断”是指产生了但是未被响应的中断请求。
    在访问 UnIIR 过程中,中断被冻结。若访问 UnIIR时产生了中断,该中断将被记录,下次访问 UnIIR 时
    便可将其读出。UART 中断标志寄存器描述如表 5.30所列。
    中断的处理见表 5.31。给定了 UnIIR[3:0]的状态,中断处理程序就能确定中断源以及如何清除激活的中断
    (1)UART 接收线状态中断(RLS 中断)
    接收线状态(UnIIR[3:1]=011)是最高优先级中断。只要 UARTn 在接收数据时产生下面 4
    个错误中的任意一个,UnIIR 将产生相应的中断标志。
     溢出错误(OE);
     奇偶错误(PE);
     帧错误(FE);
     间隔中断(BI)。
    具体错误类型可通过查看 UnLSR[4:1]得到。当读取 UnLSR 寄存器时,自动清除该中断标志。
    (2)UART 接收数据可用中断(RDA 中断)
    接收数据可用中断(UnIIR[3:1]=010)与字符超时中断(UnIIR[3:1]=110)共用第二优先级。
    当 UARTn 接收 FIFO 达到 UnFCR[7:6]所定义触发点时,RDA 中断被激活。当 UARTn Rx FIFO
    的深度低于触发点时,RDA 中断复位。当接收数据可用中断激活时,CPU 可读出由触发点所
    定义长度的数据块。
    (3)UART 字符超时中断(CTI 中断)
    字符超时中断(UnIIR[3:1]=110)为第二优先级中断。当接收 FIFO 中的有效数据个数少于
    触发个数时(至少有一个),如果经过了一段时间 [1] 没有数据到达,将触发字符超时中断,此时
    CPU 就认为一个完整的字符串已经结束,然后将接收 FIFO 中的剩余数据。
    [1] 这个触发时间为:接收 3.5 到 4.5 个字符的时间。“ 3.5 ~ 4.5 个字符的时间”,其意思是在当前波特率下,发送 3.5 ~ 4.5 个字节所需要的时间。
    产生字符超时中断后,对接收 FIFO 的以下操作都会清除该中断标志:
     从接收 FIFO 中读取数据,即,读取 UnRBR 寄存器;
     有新的数据送入接收 FIFO,即,接收到新数据。
    需要注意的是:当接收 FIFO 中存在多个数据,从 UnRBR 读取数据,但是没有读完所有数
    据,那么在经过 3.5~4.5 个字节的时间后将再次触发字符超时中断;
    例如,一个外设向 LPC178x/177x 系列 Cortex-M3 发送 85 个字符,而接收触发值为 8 个字
    符,那么前 80 个字符将使 CPU 接收 10 个接收数据可用中断,而剩下的 5 个字符使 CPU 接收
    1~5 个字符超时中断(取决于服务程序)。
     
     

    接收中断

    对于 UART 接口来说,有两种情况可以触发 UART 接收中断:
    接收字节数达到接收 FIFO的触发点(RDA)、接收超时(CTI)。
     
    接收字节数达到接收 FIFO 中的触发点(RDA)
    LPC178x/177x系列Cortex-M3 UART接口具有 16 字节的接收 FIFO,接收触发点可以设
    置为 1、4、8、14 字节,当接收到的字节数达到接收触发点时,便会触发中断
    如图 5.26 所示,通过 UART FIFO 控制寄存器 UnFCR,将接收触发点设置为“8 字节触
    发”。那么当 UART 接收 8 个字节时,便会触发 RDA 中断(注:在接收中断使能的前提下)。
     
    接收超时
    当接收 FIFO 中的有效数据个数少于触发个数时(注:接收 FIFO 中至少有一个字节),如果长时
    间没有数据到达,将触发 CTI 中断。这个时间为:3.5 到 4.5 个字符的时间。

    接收线状态中断

    在 UART 接收数据时,如果出现溢出错误(OE)、奇偶错误(PE)、帧错误(FE)和间隔中断(BI)中的任意一个错误时,都会触发接收线状态中断。具体的错误标志可以通
    过读取 UART 状态寄存器 UnLSR[4:1]得到。当读取 UnLSR寄存器时,会清除该中断标志。
     
     
     
     

    初始化

    设置 UART 通信波特率,就是设置寄存器 UnDLL 和 UnDLM 的值,UnDLL 和 UnDLM 寄
    存器是波特率发生器的除数锁存寄存器,用于设置合适的串口波特率。
    上面已经讲过,寄存器
    UnDLL 与 UnRBR/UnTHR、UnDLM 与 UnIER 具有同样的地址,如果要访问 UnDLL、UnDLM,
    除数访问位 DLAB 必须为 1。在不使用小数分频器时,寄存器 UnDLL 和 UnDLM 的计算如下:
    设置 UART 的工作模式,如:字长度选择、停止位个数、奇偶校验位等。此外,还要根据实际情况设置中断。
    UART0 初始化示例,程序将串口波特率设置为 UART_BPS(如 115200),8 位数据长度,1 位停止位,无奇偶校验。
    #define UART_BPS 115200  /*  定义通讯波特率 */
    /**********************************************************************************************
    **  函数名称: UART0_Ini
    **  函数功能:初始化串口 0 。设置为 8 位数据位, 1 位停止位,无奇偶校验,波特率为 115200
    **********************************************************************************************/
    void UART0_Ini(void)
    {
    	uint32_t Fdiv = 0;
    	U0LCR = 0x83;  /* DLAB = 1 ,可设置波特率 */
    	Fdiv = (Fpclk / 16) / UART_BPS; /*  设置波特率 */
    	U0DLM = Fdiv / 256;
    	U0DLL = Fdiv % 256; 
    	U0LCR = 0x03;  /* 锁定除数访问 */
    	U0FCR  = 0x07;  /* 使能并复位 FIFO */
    }
     

    UART 发送数据

    LPC178x/177x 系列 Cortex-M3 含有一个 16 字节的发送 FIFO,在发送数据的过程中,发送
    FIFO 是一直使能的,即,UART 发送的数据首先保存到发送 FIFO 中,发送移位寄存器会从发
    送 FIFO 中获取数据,并通过 TXD 引脚发送出去,如图 5.36 所示。
    上面讲过,寄存器 UnRBR 与 UnTHR 是同一地址,但物理上是分开的,读操作时为 UnRBR,
    而写操作时为 UnTHR。
    在寄存器 UnLSR 中,有两个位可以用在 UART 发送过程中,UnLSR[5]和 UnLSR[6]
    (1)UnLSR[5]——THRE
    当发送 FIFO 为空时,THRE 置位。从上面的描述可知,当发送 FIFO 变空时,发送 FIFO
    中的数据已经保存到了发送移位寄存器中,因此,移位寄存器此时正开始传输一个新的数据。
    当再次向 UnTHR 寄存器中写入数据时,THRE 位会自动清零。
    (2)UnLSR[6]——TEMT
    当发送 FIFO 和移位寄存器都为空时,TEMT 置位。由于所有发送的数据都是从移位寄存
    器中发送出去的,因此,当 TEMT 置位时,表示 UART 数据已经发送完毕,而且,此时发送
    FIFO 也已经没有数据了。当再次向 UnTHR 寄存器中写入数据时,TEMT 位会自动清零。
    只要 TEMT 位置位,则 THRE 位也一定会置位的。
    UART 接口发送操作可以采用两种方式:中断方式和查询方式。如表 5.66 所示。
    采用查询方式发送一字节数据
    /**********************************************************************************************
    **  函数名称: UART0_SendByte
    **  函数功能:向串口发送字节数据,并等待发送完毕
    **  入口参数: data  要发送的数据
    **  出口参数:无
    **********************************************************************************************/
    void UART0_SendByte(uint8 data)
    {
    U0THR = data; /*  发送数据 */
    while ( (U0LSR&0x40)==0 );  /*  等待数据发送完毕 */
    }
     
     
     

    UART 接收数据

    寄存器 UnRBR 与 UnTHR 是同一地址,但物理上是分开的,读操作时为 UnRBR,
    而写操作时为 UnTHR。
    LPC178x/177x 系列 Cortex-M3 的四个 UART,各含有一个 16 字节的 FIFO,用来作为接收
    缓冲区,缓冲区中的数据只能够通过寄存器 UnRBR 来获取。UnRBR 是 UARTn 接收 FIFO 的
    最高字节,它包含了最早接收到的字符。每读取一次 UnRBR,接收 FIFO 便丢掉一个字符。
    可见,只要接收 FIFO 中含有数据,则寄存器 UnRBR 便不会为空,就会包含有效数据,即,
    UnLSR[0] = 1。
    UART 接收数据时,可以使用查询方式接收,也可以使用中断方式接收,如表 5.67 所示。
    采用查询方式接收一字节数据
    /**********************************************************************************************
    **  函数名称: UART0_RcvByte
    **  函数功能:从串口接收一个字节的数据。使用查询方式
    **  入口参数:无
    **  出口参数:返回接收到的数据
    **********************************************************************************************/
    uint8 UART0_RcvByte(void)
    {
    uint8 rcv_data;
    while ((U0LSR&0x01) == 0); /*  查询数据是否接收完毕 */
    rcv_data = U0RBR;
    return (rcv_data);
    }
     
     
    使用中断方式接收数据时,如果发生 RDA 中断,则循环从 UnRBR 中读取数据即可。如果
    发生了字符超时中断——CTI,可以通过 UnLSR[0]来判断 FIFO 中是否含有有效数据,如图 5.43
    所示。
    /**********************************************************************************************
    **  函数名称: UART_Exception
    **  函数功能:串口中断服务程序
    **********************************************************************************************/
    void UART_Exception(void)
    {
    ……
    switch(U0IIR & 0x0f)
    {
    case 0x04 : /*  发生 RDA 中断 */
    /*
    **  从接收 FIFO 中读取数据
    */
    break;
    case 0x0c : /*  发生字符超时中断 ——CTI  */
    while((U0LSR & 0x01) == 1) { 
    /*
    **  如果接收 FIFO 中含有有效数据,就读取 UnRBR 寄存器
    */
    RcvData[i++] = U0RBR;
    break;
        ……
    default :
    break;
    }
    ……
    }
     
     
    注:彻底清除 UART 中断标志后才可退出中断服务程序,否则会导致处理器反复陷入中断。
     
    综上所述,UARTn 的基本操作方法:
     设置 I/O 连接到 UARTn;
     设置串口波特率(UnDLM、UnDLL);
     设置串口工作模式(UnLCR、UnFCR);
     发送或接收数据(UnTHR、UnRBR);
     检查串口状态字或等待串口中断(UnLSR)。
     
     
     
     
    #ifndef __DEBUGSERIAL_H_
    #define __DEBUGSERIAL_H_
    
    #include "sys.h"
    #include "stdio.h"
    
    extern u8 serialBuffer[256];
    extern u16 serialStatus;
    
    void Debug_Serial_Init(u32 baud);
    void Debug_Serial_Send_Byte(u8 dat);
    void Debug_Serial_Send_Buffer(u8 length,u8* buffer);
    
    #endif
    
     
    
     
     
    #include "debugSerial.h"
    
    //加入printf支持
    #pragma import(__use_no_semihosting)                            
    struct __FILE
    {
        int handle;
        /* Whatever you require here. If the only file you are using is */
        /* standard output using printf() for debugging, no file handling */
        /* is required. */
    };
    
    FILE __stdout;      
    
    _sys_exit(int x)
    {
        x = x;
    } 
    
    int fputc(int ch, FILE *f)
    {     
        while(!((LPC_UART0->LSR) & 0x20));      //等待判断LSR[5](即THRE)是否是1,1时表示THR中为空     
        LPC_UART0->THR = (u8)ch;                    //发送数据    
        return ch;
    }
    
    
    
    //定义一个256字节的缓冲区用于存放接收到的串口数据信息
    //定义一个16位数据同时保存接收数据长度以及接收数据的状态
    u8 serialBuffer[256] = {0};
    u16 serialStatus = 0;
    
    //16字节的状态
    //低八位为当前存储的有效数据长度
    //15位为接收完成等待处理标志
    //8位表示当前已经接受到回车符
    //第9到十四位表示在等待处理期间系统冗余发送的数据量
    //用于后期通讯系统的负载自适应
     
    void TransSerialsCommand(u8 res)
    {
        u8 lostCount;
        u8 receiveCount;
    
        //接收数据处理
        if(serialStatus & (1<<15))//已经接收完成,这个数据被抛弃
        {
            lostCount = ((u8)(serialStatus>>9))&0x3f;//漏掉的数据计数
            if(lostCount < 0x3f)lostCount++;
            serialStatus &= ~(0x3f<<9);
            serialStatus |= (lostCount<<9);
        }
        else//上一个命令没有接收完
        {
            if(serialStatus & (1<<8))//接收到
            {
                //等待接收N
                if(res == '
    ')
                {
                    //接收完成
                    serialStatus |= 0x8000;
                }
                else//不是
    ,这一次命令作废
                {
                    serialStatus = 0;
                }
            }
            else//没收到
            {
                if(res == '
    ')
                {
                    serialStatus |= 0x0100;
                }
                else
                {
                    receiveCount = (u8)(serialStatus&0xff);
                    if(receiveCount < 255)
                    {
                        serialBuffer[receiveCount] = res;
                        receiveCount++;
                        serialStatus &= 0xff00;
                        serialStatus |= receiveCount;
                    }
                    else
                    {
                        //数据溢出,清空
                        serialStatus = 0;
                    }
                }
            }
        }
    }
    
    
    void UART0_IRQHandler(void)
    {
        u8 status = 0;
        u8 res = 0;
    	
        //清除串口中断挂起
        NVIC_ClearPendingIRQ(GPIO_IRQn);
        //清除串口接收中断
        if(!(LPC_UART0->IIR & 0x01))//确认有中断发生
        {
            status = LPC_UART0->IIR & 0x0e;
            if(status == 0x04)//确认是RDA中断
            {
                //读取串口接收值
                res = (LPC_UART0->RBR&0xff);
                //处理串口接收值
                TransSerialsCommand(res);
            }
        }
    }
    
    
    void Debug_Serial_Init(u32 baud)
    {
        LPC_SC->PCONP |= (1<<3)|(1<<15);                //打开时钟
    	
        //配置io口
        LPC_IOCON->P0_2 = 0x00;                         //选择TXD功能,禁止迟滞 不反向 正常推挽
        LPC_IOCON->P0_2 |= (1<<0)|(2<<3);               //上拉
        LPC_IOCON->P0_3 = 0x00;                         //选择RXD功能,禁止迟滞 不反向 正常推挽
        LPC_IOCON->P0_3 |= (1<<0)|(2<<3);               //上拉
        LPC_UART0->LCR = 0x83;                          //设置串口数据格式,8位字符长度,1个停止位,无校验,使能除数访问
        LPC_UART0->DLM = ((ApbClock/16)/baud) / 256;    //除数高八位  , 没有小数情况
        LPC_UART0->DLL = ((ApbClock/16)/baud) % 256;    //除数第八位
        LPC_UART0->LCR = 0x03;                          //禁止访问除数锁存器,锁定波特率
        LPC_UART0->FCR  = 0x00;                         //禁止FIFO
        LPC_UART0->IER = 0x01;                          //使能接收中断RDA
    
        NVIC_EnableIRQ(UART0_IRQn);                     //打开IRQ中断
    }
    
    void Debug_Serial_Send_Byte(u8 dat)
    {
        //当检测到UARTn THR已空时,THRE就会立即被设置。写UnTHR会清零THRE
        //0  -  UnTHR包含有效字符
        //1  -  UnTHR为空
        while(!((LPC_UART0->LSR) & 0x20));      //等待判断LSR[5](即THRE)是否是1,1时表示THR中为空     
        LPC_UART0->THR = dat;                   //发送数据
    }
    
    void Debug_Serial_Send_Buffer(u8 length,u8* buffer)
    {
        u8 i = 0;
    	
        for(i = 0; i < length; i++)
        {
            Debug_Serial_Send_Byte(buffer[i]);
        }
        printf("
    ");
    }
    
    
    
    
    
  • 相关阅读:
    ASCII码对照表 And HTML字符实体
    操作系统自带命令查看文件的哈希
    HMAC简介及HMAC-SHA256实现Demo
    CSV文件注入漏洞简析
    Kubernetes集群的安全机制
    Kubernetes -- Horizontal Pod Autoscaler
    获取两坐标之间距离
    在CentOS 7中搭建Git服务器
    centos7 搭建svn服务器
    node.js依赖express解析post请求四种数据格式()
  • 原文地址:https://www.cnblogs.com/bog-box/p/LPC1788-UART.html
Copyright © 2011-2022 走看看