UART

通信模式
CPU与外设之间的通信模式;
并行通信:一次传输多个比特;
串行通信:是将传输数据的每个字符一位接一位(例如先低位、后高位)地传送;
图1 通信模式
RS232 Recommended Standard
UART Universal Asynchronous Receiver/Transmitter
JTAG Joint Test Action Group
Morse Code 莫尔斯电码
SPI Serial Peripheral Interface
MIDI Musical Instrument Digital Interface
USB Universal Serial Bus
表1 常见串口通信协议
UART
通用异步收发器,是一种通用的串行 [1]、异步[2]通信总线;
实现全双工[3]的发送和接收;
广泛应用于嵌入式系统中主机和外设之间的通信;
[1]串行通信
信号线少:分时使用同一传输通道,最少用一对线即可[收发];全双工通信;
实现较容易:允许双方使用各自的时钟信号,且时钟频率可以有一定误差;
效率较低:每个字符都要独立确定起始位和结束位;
起始位为低电平;结束位为高电平;数据线空闲时默认是高电平;
[2]异步通信
同步:所有设备使用同一个时钟信号;以数据块为单位传送数据,包括同步字符、数据块和校验字符。同步字符位于数据块的开头,用于确认数据字符的开始;接收时,接收设备连续不断地对传输线采样,并把接收到的字符与双方约定的同步字符进行比较,只有比较成功后才会把后面接收到的字符加以存储;同步通信的优点是数据传输速率高,缺点是要求发送时钟和接收时钟保持严格同步。对硬件结构要求较高;
异步:每个设备使用自己的时钟信号;以字符为单位进行数据传送,每一个字符均按照固定的格式传送,又被称为帧,即异步串行通信一次传送一个帧;
[3]全双工通信
单工通信 半双工通信 全双工通信
单向通信 双向通信,但是不能同时通信 双向同时通信
一根线 二根线 二根线
表2 通信方式
图2 通信方式
图3 全双工通信过程
要求
接收方对于同一根线上一连串的数字信号,首先要分割成位,再按位组成字符;
为了恢复发送的信息,双方必须协调工作[使用时钟];
逐位发送,消除累计误差;
帧格式 Frame
起始位 start bit
数据位 data bit:可以是5bit、6bit、7bit、8bit
可选的奇偶校验位 optional parity bit
终止位 stop bit
波特率 Baud
串口通信的速率,单位是每秒传输符号的个数;
用来正确区分/截断字符;
双方提前约定;不能随便指定,而应从规定的数字当中选择-programmable baud rates;
常用波特率:1200、2400、4800、9600、19200、38400、57600、115200;
由专门的寄存器通过设置不同的分频实现波特率的选择;
图4 帧格式和比特率
奇偶校验 parity check
通过增加冗余位使得代码中1的个数恒为奇数或偶数的编码方法,以确定代码传输的正确性,是一种检错码,不能纠错;现在网络环境比较好,一般不需要校验;
若1的个数是奇数,称为奇校验odd parity ;反之,称为偶校验even parity 需专门设置一个奇偶校验位;采用何种校验应事先约定好;
图5 奇偶检验
RS-232C串行接口
美国电子工业协会EIA制定,全称是:数据终端设备( DTE)和数据通信设备(DCE)之间串行二进制数据交换接口技术标准;提供全双工通信;速率20Kbps;通信线长度15m;标准EIA电平, PC使用的是TTL电平:需电平转换;
图6 电平转换
RS-232C接口物理实现:早期使用DB25针连接;现在都是DB9针连接;实际只用了其中的GEN、TX、RX共3根线,其它为流控AFC–Auto Flow Control; 早期用来通信,现在多为调试使用,所以会禁掉流控;
图7 DB9接口和串口配置
Pin Name Desc
1 CD-Carrier Detect 载波检出,用以确认是否收到 Modem 的载波
2 RXD-Received Data 数据输入线
3 TXD-Transmitted Data 数据输出线
4 DTR-Data Terminal Ready 告知数据终端处于待命状态
5 SG/GND-Signal Ground 信号线的接地线(严格的说是信号线的零标准线)
6 DSR-Data Set Ready 告知本机在待命状态
7 RTS-Request to Send 要求发送数据
8 CTS-Clear to Send 回应对方发送的 RTS 的发送许可,告诉对方可以发送
9 RI-Ring Indicator 响铃指示
表3 DB9各接口定义
几点考虑
根据通信速率确定采用的接口标准,如通信距离、通信速率;
确定通信的波特率:通信双方一定要一致;
确定通信协议:是否需要校验;
寄存器
CC2530的USART串口通信,涉及到的寄存器有:
UxCSR:USARTx控制和状态寄存器
UxUCR:USARTx UART控制寄存器
UxGCR:USARTx 通用控制寄存器
UxBAUD:USARTx 波特率控制寄存器
UxBUF:USARTx 接收/发送数据缓冲寄存器
[1]UxCSR 控制状态寄存器
位号 位名 复位值 操作性 说明
7 MODE 0 R 串口模式选择:0 SPI;1 UART
6 RE 0 R/W 接收使能:0 关闭;1 允许接收
5 SLAVE 0 R/W SPI主从选择:0 SPI主;1 SPI从
4 FE 0 R/W 串口帧错误状态:0 没有错误;1 出现错误
3 ERR 0 R/W 串口校验结果:0 没有校验错误;1 字节校验出错
2 RX_BYTE 0 R/W 接收状态:0 没有收到数据;1 收到1字节数据
1 TX_BYTE 0 R/W 发送状态:0 没有发送;1 最后一次写入U0BUF的数据已经发
0 ACTIVE 0 R/W 串口忙标记:0 闲;1 忙
接收数据时,U0CSR |= 0X40,允许接收;
发送数据时,U0CSR &= 0X40,禁止接收;
[配置示例]使用串口0并准备接收
方法1
U0CSR |= 0x80;//1000 0000;打开串口
U0CSR |= 0x40;//0000 0100;接收
方法2
U0CSR |= (1<<7);
U0CSR |= (1<<6);
方法3
U0CSR |= (0x80 | 0x40);
方法4
U0CSR |= (0x3<<6);
方法5
U0CSR |= 0xC0;
[2]UxUCR UART控制寄存器
通常无需设置该寄存器,采用默认值即可;
U0UCR |= 0x80;//1000 0000;无流控、8位数据位;清空缓冲区
位号 位名 复位值 操作性 说明
7 FLUSH 0 R/W 冲刷单位。当设置将停止当前操作,返回空闲状态
6 FLOW 0 R/W 流控制使能。硬件通过RTS和CTS控制流; 0:禁止; 1:使能
5 D9 0 R/W 奇偶位;0: 奇校验;1: 偶校验
4 BIT9 0 R/W 9-bit使能;0:8位传输;1:9位传输
3 PARITY 0 R/W 奇偶使能;0:禁止; 1:允许
2 SPB 0 R/W 停止位数; 0: 1位停止位; 1: 2位停止位
1 STOP 0 R/W 停止位等级必须不同起始位等级; 0: 低停止位; 1: 高停止位
0 START 0 R USART起始位等级; 0: 低起始位; 1: 高起始位
[3]UxGCR 通用控制寄存器
位号 位名 复位值 操作性 说明
7 CPOL 0 R/W SPI 时钟极性:0:低电平空闲;1:高电平空闲
6 CPHA 0 R/W SPI 时钟相位
0:由 CPOL 跳向非 CPOL 时采样,由非 CPOL跳向 CPOL 时输出
1:由非 CPOL 跳向 CPOL 时采样,由 CPOL跳向非 CPOL 时输出
5 ORDER 0 R/W 传输位序:0:低位在先;1: 高位在先
4-0 BAUD_E[4:0] 0x00 R/W 波特率指数值,BAUD_M 决定波特率
[4]UxBAUD 波特率控制寄存器
位号 位名 复位值 操作性 说明
7-0 BAUD_M[7:0] 0x00 R/W 波特率尾数,与 BAUD_E 决定波特率
注意:设置波特率,需要同时操作UxGCR和UxBAUD;
[配置示例]波特率设为57600
U0GCR |= 10
U0BAUD |= 216
[配置示例]波特率设为115200
U0GCR |= 11
U0BAUD |= 216
[5]UxDBUF 收发缓冲寄存器
通过UxDBUF来发送和接收数据
位号 位名 复位值 操作性 说明
7-0 DATA[7:0] 0x00 R/W UARTx收发缓存寄存器
PERCFG 外设配置寄存器
两个USART接口具有相同的功能,通过PERCFG寄存器可以设置两个USART接口对应外部I/O引脚的映射关系;0为默认位置1,1为默认位置2;
位号 位名 复位值 操作性 说明
1 U1CFG 0 R/W UART1的映射位置:0->alt 1;1->alt 2
0 U0CFG 0 R/W UART0的映射位置:0->alt 1;1->alt 2
位置映射
UART0 UART1
alt 1 TX:P0_3 RX:P0_2 alt 1 TX:P0_5 RX:P0_4
alt 2 TX:P1_5 RX:P1_4 alt 2 TX:P1_6 RX:P1_7
[配置实例]PERCFG = 0x00;//使用UART0的备用位置1(即P0_2,P0_3)
[配置实例]PERCFG |= 1;//使用UART0的备用位置2(即P1_4,P1_5)
CLKCONCMD 时钟控制命令寄存器
位号 位名 复位值 操作性 说明
7 OSC32K 1 R/W 32Khz时钟源选择:
0 32Khz的晶体振荡器;
1 32Khz的RC振荡器
6 OSC 1 R/W 系统主时钟源选择:
0 32Mhz的晶体振荡器;
1 16Mhz的RC振荡器
5-3 TICKSPD[2:0] 001 R/W 计数时钟:不能超过系统时钟
000 32Mhz; 100 2Mhz
001 16Mhz; 101 1Mhz
010 8Mhz; 110 500Khz
011 4Mhz; 111 250Khz
2-0 CLKSPD 001 R/W 时钟速度:不能超过系统时钟;取值同TICKSPD
SLEEPSTA 睡眠模式控制状态寄存器
位号 位名 复位值 操作性 说明
7 OSC32K_CALDIS 0 R 32Khz RC振荡器校准状态
6:5 - 00 R 保留
4-3 RST[1:0] XX R 状态位,表示上一次复位的原因;只保留最新的复位事件
00 上电复位和掉电探测;01 外部复位
10 看门狗定时器复位;11 时钟丢失复位
2-1 - 00 R 保留
0 CLK32K 0 R 32Khz时钟信号(与系统时钟同步)
注意:时钟切换必须等待稳定;
[配置示例]选择32Mhz
CLKCONCMD &= ~0x40; //或CLKCONCMD &= ~(1<<6)
while(!(SLEEPSTA & 0x40)); //等待 XSOC稳定
CLKCONCMD = 0xb8; //1 0 111 000;TICHSPD[128分频],CLKSPD[不分频]
IENx 中断使能寄存器
位号 位名 复位值 操作性 说明
7 EA 0 R 中断系统使能控制,即:总中断;0 禁止所有中断;1 允许所有中断
6 - 0 R 保留
5 STIE 0 R/W 睡眠定时器中断使能:0 中断禁止;1 中断使能
4 ENCIE 0 R/W AES加密/解密中断使能:0 中断禁止;1 中断使能
3 URX1IE 0 R/W USART1接收中断使能:0 中断禁止;1 中断使能
2 URX0IE 0 R/W USART0接收中断使能:0 中断禁止;1 中断使能
1 ADCIE 0 R/W ADC中断使能:0 中断禁止;1 中断使能
0 RFERRIE 0 R/W RF收发中断使能:0 中断禁止;1 中断使能
中断寄存器
UTX0IF来判断是否发送完成 ,为1则发送成功;
URX0IF来判断是否接受完成,为1则接收成功;
更多中断请点击访问 Interrupt
实操1
发送字符串

    #include "ioCC2530.h"
    #include "string.h"
    #include "../lib/delay.h"
    typedef unsigned char uchar;
    typedef unsigned int  uint;
    #define TX_SIZE    20
    #define TX_STRING  "Hello Zigbee... "
    
    void Init32M( void )
    {
        CLKCONCMD &= ~0x40;               //设置系统时钟源为32MHZ晶振
        while( CLKCONSTA & 0x40 );        //等待晶振稳定为32M
        CLKCONCMD &= ~0x47;               //设置系统主时钟频率为32MHZ
        SLEEPCMD |= 0x04;                   //关闭不用的RC振荡器
    }
    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
    }
    void UartSendString( char *Data, int len )
    {
        uint i;
        for( i = 0; i < len; i++ )
        {
            U0DBUF = *Data++;
            while( UTX0IF == 0 );
            UTX0IF = 0;
        }
    }
    void main( void )
    {
        char TxData[TX_SIZE];
        Init32M();
        InitUart();                       //调置串口相关寄存器
        memset( TxData, 0, TX_SIZE );     //数据清0
        memcpy( TxData, TX_STRING, sizeof( TX_STRING ) ); //复制发送字符串到TxData
    
        while( 1 )
        {
            UartSendString( TxData, sizeof( TX_STRING ) ); //串口发送数据
            delay( 1000 );
        }
    }
                
实操2
利用IO发送字符串

    #include "ioCC2530.h"
    #include "../lib/delay.h"
    char uart_buffer;

    void InitIO( void );
    void InitUART( void );
    void Init32M( void );
    void UartTX_Send_String( unsigned char *Data, int len );
    void UartTX_Send_Data( unsigned char Data, int len );

    #pragma vector=URX0_VECTOR
    __interrupt void uart0( void )
    {
        URX0IF = 0;//清中断标志
        P0_0 = ~P0_0;
        uart_buffer = U0DBUF;
        //UartTX_Send_String("welcome",10);
        UartTX_Send_Data( uart_buffer, 1 );
    }

    void main( void )
    {
        unsigned char buf[4];

        //InitIO();[NOT MUST]
        Init32M();
        InitUART();

        buf[0] = '\t';
        while( 1 )
        {
            P2_0 = ~P2_0;
            delay( 1000 );
            if( uart_buffer == 0x01 )               //按照16进制输入[cnplaman]
            {
                UartTX_Send_String( "welcome", 7 ); //串口接收要发送的数据
                UartTX_Send_String( &buf[0], 1 );   //串口发送接收的数据
            }
        }
    }
    void InitIO( void )
    {
        P0DIR |= 0x01;  //设置P0.0为输出方式[P0.4和P0.5为输入方式???]
        P2DIR |= 0x01;  //设置P2.0为输出方式
        P0_0 = 1;     //高电平截至
        P2_0 = 1;     //高电平截至
    }
    void InitUART( void )
    {
        PERCFG = 0x00;              //外设控制寄存器UART0:P0口位置1:RX0 - P0_2;TX0 - P0_3
        P0SEL = 0x0C;               //0011 1100:P0_2,P0_3 用作串口(外设功能);[0x3c]
        P2DIR &= ~0xC0;             //设置P0口优先为UART0,即串口0优先级最高;其次是串口1,再是计时器1;P2DIR[7-6]PRIP0:00

        U0CSR |= 0x80;              //1000 0000;UART方式

        U0GCR |= 10;                //baud_e = 10;
        U0BAUD |= 216;              //波特率设为57600

        UTX0IF = 0;                 //清除UART0 TX中断标志:置0
        U0CSR |= 0x40;              //允许接收
        IEN0 |= 0x84;               //1000 0100开总中断,允许接收中断
    }
    void Init32M( void )
    {
        CLKCONCMD &= ~0x40;                 //选择32M晶振
        while( !( SLEEPSTA & 0x40 ) );      //等待XSOC稳定
        CLKCONCMD = 0xb8;                   //TICHSPD 128分频,CLKSPD 不分频
        SLEEPCMD |= 0x04;                   //关闭不用的RC 振荡器[cnplaman:not MUST]}
    }
    void UartTX_Send_String( unsigned char *Data, int len )
    {
        for( int j = 0; j < len; j++ )
        {
            U0DBUF = *Data++;
            while( UTX0IF == 0 );
            UTX0IF = 0;
        }
    }
    void UartTX_Send_Data( unsigned char Data, int len )
    {
        for( int j = 0; j < len; j++ )
        {
            U0DBUF = Data;
            while( UTX0IF == 0 );
            UTX0IF = 0;
        }
    }
                
实操3
利用IO控制LED

    #include “ioCC2530.h”
    #define uint unsigned int
    #define uchar unsigned char
    
    #define LED1 P0_0       //定义P0_0为LED1的控制引脚
    #define LED2 P2_0       //定义P2_0为LED2的控制引脚
    
    void InitIO();        //声明LED初始化函数
    void InitUART();      //声明串口0初始化函数
    void Init32M();    //声明初始化32M时钟初始化函数
    void UR0SendByte( unsigned char Byte ); //声明发送一个字节初始化函数
    void UR0SendString( unsigned char *str ); //声明发送字符串初始化函数
    void Execute_CMD();     //声明执行上位机命令初始化函数
    
    char RxBuf;             //定义接收缓冲区
    char Rx_flag;           //定义串口接收标志位
    #pragma vector = URX0_VECTOR
    __interrupt void URX0_ISR()
    {
        URX0IF = 0;           //清中断标志位
        RxBuf = U0DBUF;       //将缓冲寄存器的数据给读出来
        Rx_flag = 1;          //接收标志位置1
    }
    void main()
    {
        InitIO();           //初始化LED端口
        InitUART();         //初始化串口0
        Init32M();       //初始化32M晶振
    
        UR0SendString( "Hello ZigBee!\r\n" );
    
        while( 1 )
        {
            if( Rx_flag == 1 )  //是否接收到上位机指令
            {
                Execute_CMD();    //判断并执行上位机指令
            }
        }
    }
    /*===================LED初始化函数==================*/
    void InitIO()
    {
        P0SEL &= ~0x01;       //将P1_0和P1_1设置为通用I/O端口功能
        P0DIR |= 0x01;        //将P1_0和P1_1的端口设置为输出
        P2SEL &= ~0x01;
        P2DIR |= 0x01;
        LED1 = 1;             //关闭LED1灯
        LED2 = 1;             //关闭LED2灯
    }
    void Init32M()
    {
        CLKCONCMD &= ~0x40;       //系统时钟源选择:外部32MHz 。
        while( !( SLEEPSTA & 0x40 ) ); //等待晶振稳定
        CLKCONCMD &= ~0x47;       //128分频 CLKSPD不发分频
        SLEEPCMD |= 0x04;         //关闭不用的RC振荡器
    }
    void InitUART()
    {
        PERCFG = 0x00;      //外设控制寄存器USART 0 的IO位置:
        P0SEL  = 0x0C;      //设置P0_2,P_3为外设功能
        P2DIR &= ~0xC0;     //设置P0口优先为UART0,即串口0优先级最高
    
        U0CSR |= 0xC0;      //设置为UART模式
    
        U0GCR |= 10;        //查表
        U0BAUD |= 216;      //设置波特率为57600
    
        U0UCR |= 0x80;      //无流控,8位数据位,清空缓冲区
    
        UTX0IF = 0;         //串口0TX发送中断标志位清0:清除中断标记
        URX0IF = 0;         //串口0RX接收中断标志位清0:清除中断标记
    
        URX0IE = 1;         //开串口0接收中断:使能串口0中断
        EA = 1;             //开中断总开关
    }
    /*================串口0发送一个字节函数==============*/
    void UR0SendByte( unsigned char Byte )
    {
        U0DBUF = Byte;        //将要发送的一个字节数据写入U0DBUF
        while( !UTX0IF );     //等待TX中断标志,即数据发送完成
        UTX0IF = 0;           //清除TX中断标志,准备下一次发送
    }
    /*================串口0发送字符串函数================*/
    void UR0SendString( unsigned char *str )
    {
        while( *str != '\0' )
        {
            UR0SendByte( *str++ ); //逐个发送字符串中的字节
        }
    }
    /*================执行上位机指令函数=================*/
    void Execute_CMD()
    {
        Rx_flag = 0;      //清0接收标志位
        switch( RxBuf )   //通过上位机发送的命令,判断并执行
        {
        case 0xF1:        //如果PC发送00xF1 则点亮LED1 并串口发送字符串
            LED1 = 0;
            UR0SendString( "The LED1 is Open!\r\n" );
            break;
        case 0xF2:        //如果PC发送0xF2 则熄灭LED1 并串口发送字符串
            LED1 = 1;
            UR0SendString( "The LED1 is Closed!\r\n" );
            break;
        case 0xF3:        //如果PC发送0xF3 则点亮LED2 并串口发送字符串
            LED2 = 0;
            UR0SendString( "The LED2 is Open!\r\n" );
            break;
        case 0xF4:        //如果PC发送0xF4 则熄灭LED2 并串口发送字符串
            LED2 = 1;
            UR0SendString( "The LED2 is Closed!\r\n" );
            break;
        default:
            UR0SendString( "F1 F2 F3 F4!\r\n" );
            break;
        }
    }
                
实验目的
1. 掌握CC 2530 UART串口寄存器设置;
2. 掌握UART串口中断函数程序的编程方法;
实验内容
在IAR集成开发环境中编写定时器中断程序;通过按键以串行方式发送指定数据;即:上位机PC通过串口助手向CC2530发送一个数据命令; CC2530先接收,然后判断并执行相应的命令;
预备知识
1. 串口通信;
2. 了解C语言的基本知识;
3. 了解IAR中编写和调试程序的方法;
实验需求
安装IAR和相关驱动的个人PC1台
迷你USB线mini USB1根
仿真器CC Debugger1根
串并转换线USB to UART1根
ZigBee通用节点板1块
实验步骤
1.创建IAR工作空间;
2.创建基于C的IAR工程;
3.编辑代码并调试;可参考提供的源码,调试应无差错、无告警;
4.试验箱上电;使用mini USB分别连接CC Debugger和PC;并将仿真器CC Debugger的另外一端和ZigBee通用节点板上的JTAG插座相连;
5.按下仿真器CC Debugger的复位按钮RESET,绿灯常亮表示模块连接成功,可以往芯片中烧写程序;每次烧写程序都需要复位,确保仿真器是绿灯状态;
6.在IAR中点击下载并调试按钮download and debug(CTRL+D)烧写程序;
7.在IAR的调试窗口中,点击[全速]运行;
8.用串并转换线USB to UART线连接节点板串口和PC机某USB接口,在PC设备管理器中确认串口端口;
9.打开PC机上的串口调试助手,选择对应的串口、波特率及8位数据、1位停止、无校验、无流控;
10.利用串口调试助手操作串口发送数据;
开发过程中,请注意CTRL+S保存项目!
项目提升
1.修改波特率
2.修改控制字符
3.修改发送内容
4.修改时钟
5.按键发送消息
6.控制LED灯
7.完善项目
实验报告
1. 根据实操部分的内容,完成项目[三选一];
2. 以纸质的形式提交实验报告;
3. 论文格式请参照范文[点击下载]