[Zigbee]

7、ZigBee之UART剖析(ONLY串口发送)

[复制链接]
579|33
手机看帖
扫描二维码
随时随地手机跟帖
programmable|  楼主 | 2020-1-11 17:04 | 显示全部楼层 |阅读模式
综述:USART0和USART1是串行通信接口,它们能够分别运行于异步UART模式或者同步SPI 模式。两个USART具有同样的功能,可以设置在单独的I/O 引脚。

1、UART 模式
  UART 模式提供异步串行接口。在UART 模式中,接口使用2 线或者含有引脚RXD、TXD、可选RTS 和CTS 的4 线。
  UART 模式的操作具有下列特点:
● 8 位或者9 位负载数据
● 奇校验、偶校验或者无奇偶校验
● 配置起始位和停止位电平
● 配置LSB 或者MSB 首先传送
● 独立收发中断
● 独立收发DMA 触发
● 奇偶校验和帧校验出错状态
  UART 模式提供全双工传送,接收器中的位同步不影响发送功能。传送一个UART 字节包含1 个起始位、8个数据位、1 个作为可选项的第9 位数据或者奇偶校验位再加上1 个或2 个停止位。注意,虽然真实的数据包含8 位或者9 位,但是,数据传送只涉及一个字节。
  UART操作由USART控制寄存器UxUCR和状态寄存器UxCSR来控制。这里的x 是USART的编号,其数值为0 或者1。
  当UxCSR.MODE 设置为1 时,就选择了UART 模式。

使用特权

评论回复

相关帖子

programmable|  楼主 | 2020-1-11 17:04 | 显示全部楼层
1.1、UART 发送

  当USART 收/发数据缓冲器、寄存器UxBUF 写入数据时,该字节发送到输出引脚TXDx。UxBUF 寄存器是双缓冲的。

  当字节传送开始时, UxCSR.ACTIVE 位变为高电平,而当字节传送结束时为低。当传送结束时,UxCSR.TX_BYTE 位设置为1。当USART 收/发数据缓冲寄存器就绪,准备接收新的发送数据时,就产生了一个中断请求。该中断在传送开始之后立刻发生,因此,当字节正在发送时,新的字节能够装入数据缓冲器。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:04 | 显示全部楼层
1.2、UART 接收

  当1 写入UxCSR.RE 位时,在UART 上数据接收就开始了。然后UART 会在输入引脚RXDx 中寻找有效起始位,并且设置UxCSR.ACTIVE 位为1。当检测出有效起始位时,收到的字节就传入到接收寄存器,UxCSR.RX_BYTE 位设置为1。该操作完成时,产生接收中断。同时UxCSR.ACTIVE 变为低电平。

  通过寄存器UxBUF 提供收到的数据字节。当UxBUF 读出时,UxCSR.RX_BYTE 位由硬件清0。

    注意:当应用程序读UxDBUF,很重要的一点是不清除UxCSR.RX_BYTE。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:05 | 显示全部楼层
1.3、UART 硬件流控制

  当UxUCR.FLOW 位设置为1,硬件流控制使能。然后,当接收寄存器为空而且接收使能时,RTS 输出变低。在CTS 输入变低之前,不会发生字节传送。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:05 | 显示全部楼层
1.4、UART 特征格式

  如果寄存器UxUCR 中的BIT9 和奇偶校验位设置为1,那么奇偶校验产生而且检测使能。奇偶校验计算出来,作为第9 位来传送。在接收期间,奇偶校验位计算出来而且与收到的第9 位进行比较。如果奇偶校验出错,则UxCSR.ERR 位设置为高电平。当读取UxCSR 时,UxC-SR.ERR 位清除。

  要传送的停止位的数量设置为1 或者2,这取决于寄存器位UxUCR. SPB。接收器总是要核对一个停止位。如果在接收期间收到的第一个停止位不是期望的停止位电平,就通过设置寄存器位UxCSR.FE 为高电平,发出帧出错信号。当读取UxCSR 时,UxCSR.FE 位清除,当UxCSR.SPB 设置为1 时,接收器将核对两个停止位。

  注意:当检测到第一个停止位正确时,将设置RX 中断。如果第二个停止位不正确,设置帧误码时将有一个延迟。这个延迟与波特率有关(位持续时间)。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:05 | 显示全部楼层
2、SPI 模式

  本节描述了同步通信的SPI 模式。在SPI 模式中,USART 通过3 线接口或者4 线接口与外部系统通信。接口包含引脚MOSI、MISO、SCK 和SS_N。参见GPIO节中有关如何将USART 引脚指派到I/O 引脚的描述。

  SPI 模式包含下列特征:

· 3 线(主要)或者4 线SPI 接口
· 主和从模式
· 可配置的SCK 极性和相位
· 可配置的LSB 或MSB 传送

当UxCSR.MODE 设置为0 时,选中SPI 模式。在SPI 模式中,USART 可以通过写UxCSR.SLAVE 位来配置SPI 为主模式或者从模式。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:06 | 显示全部楼层
2.1、SPI 主模式操作

  当寄存器UxBUF 写入字节后,SPI 主模式字节传送就开始了。USART 使用波特率发生器生成SCK 串行时钟,而且传送发送寄存器提供的字节到输出引脚MOSI。与此同时,接收寄存器从输入引脚MISO获取收到的字节。

  当传送开始UxCSR.ACTIVE 位变高,而当传送结束后, UxCSR.ACTIVE 位变低。当传送结束时,UxCSR.TX_BYTE 位设置为1。

  串行时钟SCK 的极性由UxGCR.CPOL 位选择,其相位由UxCSR.CPHA 位选择。字节传送的顺序由UxCSR.ORDER 位选择。

  传送结束时,收到的数据字节由UxBUF 提供读取。当这个新的数据在UxDBUF USART 接收/发送数据寄存器中准备好,就产生一个接收中断。当单元就绪接收另一个字节用来发送时,发送中断产生。由于UxBUF 是双缓冲,这个操作刚好在发送开始时就发生了。注意数据不应写入UxDBUF,直到UxCSR.TX_BYTE 是1。对于DMA 传输这是自动处理的。

  对于使用DMA 的背对背传输,如果传输字节不能被损坏,UxGDR.CPHA 位必须设置为0。对于需要设置UxGDR.CPHA 的系统,需要轮询UxCSR.TX_BYTE。还要注意发送中断和接收中断的区别,因为前者比后者大约提前8 位周期到达。

  如上所述的SPI 主模式操作是一个3 线接口。不选择输入用于使能主模式。如果外部从模式需要一个从模式选择信号,这可以使用一个通用I/O 引脚通过软件实现。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:07 | 显示全部楼层
2.2、SPI 从模式操作

  SPI 从模式字节传送由外部系统控制。输入引脚MISO 上的数据传送到接收寄存器,该寄存器由串行时钟SCK 控制。SCK 为从模式输入。与此同时,发送寄存器中的字节传送到输出引脚MOSI。

  当传送开始时UxCSR.ACTIVE 位变高,而当传送结束后,UxCSR.ACTIVE 位变低。当传送结束时,UxCSR.RX_BYTE 位设置为1,接收中断产生。

  串行时钟SCK 的极性由UxCSR.CPOL 位选择,其相位由UxGCR.CPHA 位选择。字节传送的顺序由UxGCR.ORDER 位选择。

  传送结束时,收到的数据字节由UxBUF 提供读取。当SPI 从模式操作开始时,发送中断。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:07 | 显示全部楼层
2.3、SSN 从模式选择引脚

  当USART 运行在SPI 模式,配置为SPI 从模式,从模式选择(SSN)引脚使用一个4 线接口来作为SPI的输入(边沿控制)。SSN 的下降沿,SPI 从模式活跃,在MOSI 输入上接收数据,在MOSI 输出上输出数据。SSN 的上升沿,SPI 从模式不活跃,不接收数据。还要注意SSN 上升沿之后MISO 输出不是三态。还要注意释放SSN(SSN 变为高电平)必须在字节接收或发送结束。如果在字节中间释放,下一个要接收的字节将不能正确接收,因为关于之前字节的信息在SPI 系统中。USART 清除能用于删除这个信息。在SPI 主模式中,不使用SSN 引脚。当USART 运行在SPI 主模式,外部SPI 从模式设备需要提供一个从模式选择信号,然后一个通用I/O 引脚应在软件方面作为从模式选择信号功能。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:08 | 显示全部楼层
2.4、波特率的产生

  当运行在UART 模式时,内部的波特率发生器设置UART 波特率。当运行在SPI 模式时,内部的波特率发生器设置SPI 主时钟频率。

  由寄存器UxBAUD.BAUD_M[7:0]和UxGCR.BAUD_E[4:0]定义波特率。该波特率用于UART 传送,也用于SPI 传送的串行时钟速率。波特率由下式给出:

930325e1990681aaad.png

  式中:f 是系统时钟频率,等于16 MHz RCOSC 或者32 MHz XOSC。

  标准波特率所需的寄存器值如表16-1 所列。该表适用于典型的32 MHz 系统时钟。真实波特率与标准波特率之间的误差,用百分数表示。

  当BAUD_E 等于16 且BAUD_M 等于0 时,UART 模式的最大波特率是f/16 且f 是系统时钟频率。SPI 模式下的最大波特率见设备数据手册。

  注意:波特率必须通过UxBAUD 和寄存器UxGCR 在任何其他UART 和SPI 操作发生之前设置。这意味着使用这个信息的定时器不会更新,直到它完成它的起始条件,因此改变波特率是需要时间的。

34845e19906fc94b6.png

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:08 | 显示全部楼层
2.5、清除USART

  通过设置寄存器位UxUCR.FLUSH 可以取消当前的操作。这一事件会立即停止当前操作并且清除全部数据缓冲器。应注意在TX/RX 位中间设置清除位,清除将不会发生,直到这个位结束(缓冲将被立即清除但是知道位持续时间的定时器不会被清除)。因此使用清除位应符合USART 中断,或在USART 可以接收更新的数据或配置之前,使用当前波特率的等待时间位。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:08 | 显示全部楼层
2.6 USART 中断

  每个USART 都有两个中断:RX 完成中断(URXx)和TX 完成中断(UTXx)。当传输开始触发TX 中断,且数据缓冲区被卸载。

  USART 的中断使能位在寄存器IEN0 和寄存器IEN2 中,中断标志位在寄存器TCON 和寄存器IRCON2 中,中断使能和标志总结如下。

中断使能:

● USART0 RX:IEN0.URX0IE
● USARTl RX:IEN0.URXlIE
● USART0 TX:IEN2.UTX0IE
● USARTl TX:IEN2.UTXlIE

中断标志:

● USART0 RX:TCON.URX0IF
● USARTl RX:TCON.URXlIF
● USART0 TX:IRCON2.UTX0IF
● USARTl TX:IRCON2.UTX1IF

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:08 | 显示全部楼层
2.7、USART DMA 触发

  有两个DMA 触发与每个USART 相关。DMA 触发由事件RX 或者TX 完成激活,也就是说,该事件作为DMA 中断请求。可以配置DMA 通道使用USART 收/发缓冲器(即UxBUF)作为它的源地址或者目标地址。

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:09 | 显示全部楼层
2.8、USART 寄存器

  本节描述了USART 的寄存器。对于每个USART,有5 个如下的寄存器(x 是USART 的编号,为0 或者1):

● UxCSR:USARTx 控制和状态;
● UxUCR:USARTx UART 控制;
● UxGCR:USARTx 通用控制
● UxBUF:USART x 接收/发送数据缓冲
● UxBAUD:USART x 波特率控制

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:09 | 显示全部楼层
3、代码分析

3.1、发送代码分析
/****************************************************************************
* 文 件 名: main.c
* 描    述: 设置串口调试助手波特率:115200bps 8N1
*           会收到CC2530发过来的:Hello Zigbee
****************************************************************************/
#include <ioCC2530.h>
#include <string.h>

typedef unsigned char uchar;
typedef unsigned int  uint;
#define TX_SIZE    20

#define TX_STRING  "Hello Zigbee  "

char TxData[TX_SIZE];        //存储发送字符串

/****************************************************************************
* 名    称: DelayMS()
* 功    能: 以毫秒为单位延时 16M时约为535,32M时要调整,系统时钟不修改默认为16M
* 入口参数: msec 延时参数,值越大延时越久
* 出口参数: 无
****************************************************************************/
void DelayMS(uint msec)
{  
    uint i,j;
   
    for (i=0; i<msec; i++)
        for (j=0; j<1070; j++);
}

/****************************************************************************
* 名    称: InitUart()
* 功    能: 串口初始化函数
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitUart(void)
{
    PERCFG = 0x00;           //外设控制寄存器 USART 0的IO位置:0为P0口位置1
    P0SEL = 0x0c;            //P0_2,P0_3用作串口(外设功能)
    P2DIR &= ~0XC0;          //P0优先作为UART0
   
    U0CSR |= 0x80;           //设置为UART方式
    U0GCR |= 11;                       
    U0BAUD |= 216;           //波特率设为115200
    UTX0IF = 0;              //UART0 TX中断标志初始置位0
}

/****************************************************************************
* 名    称: UartSendString()
* 功    能: 串口发送函数
* 入口参数: Data:发送缓冲区   len:发送长度
* 出口参数: 无
****************************************************************************/
void UartSendString(char *Data, int len)
{
    uint i;
   
    for(i=0; i<len; i++)
    {
        U0DBUF = *Data++;
        while(UTX0IF == 0);
        UTX0IF = 0;
    }
}

/****************************************************************************
* 程序入口函数
****************************************************************************/
void main(void)
{   
    CLKCONCMD &= ~0x40;               //设置系统时钟源为32MHZ晶振
    while(CLKCONSTA & 0x40);          //等待晶振稳定为32M
    CLKCONCMD &= ~0x47;               //设置系统主时钟频率为32MHZ   
      
    InitUart();                       //调置串口相关寄存器
    memset(TxData, 0, TX_SIZE);       //数据清0
    memcpy(TxData, TX_STRING, sizeof(TX_STRING)); //复制发送字符串到TxData
        
    while(1)
    {
        UartSendString(TxData, sizeof(TX_STRING)); //串口发送数据
        DelayMS(1000);                 //延时
    }
}

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:09 | 显示全部楼层
第39~41行是串口的IO设置相关,这个要回到IO节进行了解。

首先我们先看下面外设IO映射表:发现USART0和USART1都各自对应着两种端口映射关系:

以USART0为例,第一种映射关系是RT-P05\CT-P04\TX-P03\RX-P02;另一种映射关系是TX-P15\RX-P14\RT-P13\CT-P12。

858255e1990e509ee8.png

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:10 | 显示全部楼层
那么在使用过程中,一种模式对应两种映射肯定会出现矛盾!因此代码中第39行:

PERCFG = 0x00;
就是实现选中USART0和USART1的各自的第一种映射!(这样我们就能找到对应的引脚了!)这里的SFR 寄存器位PERCFG.U0CFG的功能就是选择是否使用备用位置1 或备用位置2。

676735e1990f9ca313.png

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:10 | 显示全部楼层
对于USART 和定时器I/O,在一个数字I/O 引脚上选择外设I/O 功能,需要设置对应的PxSEL 位为1。因此,代码中第40行0x0c=1100

P0SEL = 0x0c;
是设置P02、P03用在串口外设IO

716585e199108273c9.png

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:10 | 显示全部楼层
以上两个设置导致USART0的4个串口IO和USART1的四个IO冲突了,如果同时发生USART事件就会导致问题。因此可以通过指派这些冲突外设的优先级:

P2DIR &= ~0XC0;
P2DIR.PRIP0 选择为端口0 指派一些外设的优先顺序。当设置为00 时,USART 0 优先。注意如果选择了UART 模式,且硬件流量控制禁用,UART 1 或定时器1 将优先使用端口P0.4 和P0.5。2SEL.PRI3P1 和P2SEL.PRI0P1 选择为端口1 指派一些外设的优先顺序。当它们两个都设置为0 时,USART0 优先。注意如果选择了UART 模式,且硬件流量控制禁用,定时器1 或定时器3 将优先使用端口P1.2 和P1.3。这里设置的是00,因此UART0优先于UART1!

265885e199116a3293.png

使用特权

评论回复
programmable|  楼主 | 2020-1-11 17:11 | 显示全部楼层
上面这么长的分析我们才知道39~41行是设置UART1和UART0对应的引脚及他们的优先级关系!

接下来的几行代码主要和串口设置有关,比如波特率、奇偶校验等...

其中第43行:

U0CSR |= 0x80;
用来设置UART0模式:UART模式,不接收数据(仅仅发送)等

708945e1991374c474.png

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

28

主题

394

帖子

0

粉丝