[经验] MSP430的UART串口通信详解

Jacktang   2016-10-31 20:36 楼主
MSP430的串口通信由一个称为USCI(Universal Serial Communications Interface)的片上外设处理。USCI外设可以处理多种串口通信格式,包含同步的和异步的,如SPI、I2C、IrDA、UART等。MSP430G2553上有两个USCI模块,分别是USCI_A0和USCI_B0,前者可以配置用于处理LIN、IrDA、SPI和UART通信,而后者可以用于处理SPI和I2C通信。

         本文主要介绍配置MSP430G2553的USCI_A0模块,实现异步串行通信即UART模式。UART异步串行通信模式被广泛的用于与外部设备通信,如与PC、笔记本、GSM模块、GPS模块等通信,而且其也是很多标准协议(RS232,RS422,RS485等)的基石。UART模式使用两个引脚来传输(UCA0TXD)和接受(UCA0RXD)数据。USCI_A0处于UART模式的简单框图如下:

360截图20161031203413905.jpg

         从上图可知,USCI的时钟源可以是来自SMCLK或者ACLK,生成的BRCLK用于产生需要的计时。UC0CLK是一个外部时钟源。

         本文的例子主要是配置MSP430 UART,传输和接受数据的波特率为9600bps,8 databits,no Parity和1 stop bit。



1. 系统时钟配置

         下面一段代码配置DCO产生1MHz,使用子系统时钟SMCLK作为UART时钟:

-------------------------------------------code start------------------------------------------------------------------------

         if (CALBC1_1MHZ == 0xFF) // If calibration constant erased

         {

                  while(1);            // do not load, trap CPU!!

         }

         DCOCTL  = 0;             // Select lowest DCOx and MODx settings

         BCSCTL1 = CALBC1_1MHZ;   // Set range

         DCOCTL  = CALDCO_1MHZ;   // Set DCO step + modulation

-------------------------------------------code end------------------------------------------------------------------------

为了产生精确的1MHz时钟,需要将校准常量加载到基本时钟系统寄存器(BCSCTL1)和DCO控制寄存器(DCOCTL)中。CALBC1_1MHZ是头文件msp430g2553.h中定义的常量,头文件中还定义有其它常量,可以配置DCO操作于1MHz、8MHz、12MHz和16MHz,即CALBC1_1MHZ,CALBC1_8MHZ,CALBC1_12MHZ,CALBC1_16MHZ,这些常量值在出厂时已经校准好,写在芯片上的保护存储区域,如果不小心把这些常量值擦除了,则必须重新校准。



2. UART需要的寄存器

         UCAxCTL0寄存器,由于我们使用的是USC1_A0模块,因此需要使用UCA0CTL0寄存器,该寄存器控制着Parity选择、数据传输方向、LSB或者MSB优先、字符长度、stop bit、传输模式等的设置。我们的选择中,UCA0CTL0寄存器的所有比特位都为0,因此程序中不必配置。

         UCAxCTL1寄存器,需要使用UCA0CTL1中的两个重要比特位,UCSSELx(用来选择时钟源,我们需要选择为SMCLK @1MHz)和UCSWRST(用于将USCI模块置于reset状态,推荐对寄存器进行任何改变之前将USCI置于reset状态)。

         UCAxSTAT寄存器,需要使用UCA0STAT中的两个重要比特位,UCLISTEN(可以用于选择内部循环模式Loop Back mode,这样UCAxTXD会内部反馈给receiver),UCBUSY(指示是否一个传输或者接受操作正在进行中)。

         UCAxBR0寄存器和UCAxBR1寄存器,两个8位寄存器,用于为波特率生成器设置时钟预置分频器值。

         UCAxTXBUF寄存器,一个8位数据寄存器,相当于一个传输缓冲区,即MSP430 UART将要传输的一个字节。

         UCAxRXBUF寄存器,也是一个8位数据寄存器,相当于一个接受缓冲区,用于存储接收到的字节。

         注意,由于使用的是USC1_A0模块,上述寄存器中的x都要变成0.



3. 波特率配置

         在MSP430的用户手册中给出了一个波特率选择表,根据该表可以很容易的对波特率进行配置,该表如下:

360截图20161031203459841.jpg

通过该表找到对应的值填入UCA0BR0 和UCA0BR1两个寄存器中,就可以配置波特率。由于我们使用SMCLK @ 1MHz作为BRCLK,9600bps作为波特率,表中其对应的UCBRx值为104,将该值放入寄存器UCA0BR0和UCA0BR1中即可。

       配置波特率时另一个需要考虑的寄存器是UCAxMCTL (UCA0MCTL),该寄存器用于选择调制级别(modulation stage),将表中对应的UCBRSx值(这里为1)放到UCA0MCTL寄存器中可以配置modulation stage。



4. MSP430 UART的中断

         MSP430G2553拥有一个数据传输中断和一个数据接收中断,在使用前必须现在IE2寄存器中将这两个终端开启(enabled)。IFG2寄存器中的两个flags位用于指示数据在传输还是在接收。

         IE2寄存器:UCA0TXIE用于开启/关闭传输中断,UCA0RXIE用于开启/关闭接收中断。

         IFG2寄存器:UCA0TXIFG是USCI_A0传输中断flag,当UCA0TXBUF为空时设置该位;UCA0RXIFG是USCI_A0接收中断flag,当UCA0RXBUF接收到一个完整的字符时设置该位,应该记得中对应的中断处理器程序ISR中手动清除掉该flag。

5. 代码-UART传输:采用内部循环模式

         两个设备之间进行通信的一个困难时,出现问题很难定位,可能是接受设备代码的问题,也可能是通信连接的问题,还可能是发送设备代码的问题等等。为了使得开发UART代码更加容易,MSP430提供了内部循环模式,即Loop Back mode,通过UCAxSTAT寄存器可以进行配置。该模式下,UART的传输口的输出会自动连接到接受口的输入,即MSP430设备自己传输自己接受。

         下面的代码(在CCS下编译通过)通过UART传输一个ASCII字符‘A’,UART配置为内部循环模式,接受的字符会存储在设备的UCA0RXBUF寄存器中。通过两个LED灯来指示传输和接受操作。

-------------------------------------------code start------------------------------------------------------------------------

#include



/*

* main.c

*/

int main(void) {

    WDTCTL = WDTPW | WDTHOLD;    // Stop watchdog timer



    //------------------- 配置时钟 -------------------//

    if (CALBC1_1MHZ==0xFF)   // If calibration constant erased

     {

        while(1);          // do not load, trap CPU!!

     }

    DCOCTL  = 0;             // Select lowest DCOx and MODx settings

    BCSCTL1 = CALBC1_1MHZ;   // Set range

    DCOCTL  = CALDCO_1MHZ;   // Set DCO step + modulation

      //---------------- 配置LED灯,P1.0和P1.6分别对应LED1和LED2 ----------------------//


    P1DIR  |=  BIT0 + BIT6;  // P1.0 and P1.6 output

    P1OUT  &= ~BIT0 + BIT6;  // P1.0 and P1.6 = 0



      //--------- 配置UART,P1.1和P1.2两个引脚分别对应UART的输入和输出,通过PXSEL寄存器可以配置哪个用作接受RXD,哪个用作传输TXD --------//


    P1SEL  |=  BIT1 + BIT2;  // P1.1 UCA0RXD input

    P1SEL2 |=  BIT1 + BIT2;  // P1.2 UCA0TXD output

      //------------UART(USCI_A0) 的寄存器和波特率配置----------------//

    UCA0CTL1 |=  UCSSEL_2 + UCSWRST;  // USCI Clock = SMCLK,USCI_A0 disabled

    UCA0BR0   =  104;                 // 104 From datasheet table-

    UCA0BR1   =  0;                   // -selects baudrate =9600,clk = SMCLK

    UCA0MCTL  =  UCBRS_1;             // Modulation value = 1 from datasheet

    UCA0STAT |=  UCLISTEN;            // loop back mode enabled,配置为内部循环模式,即自己的TXD传输给自己的RXD接受

    UCA0CTL1 &= ~UCSWRST;             // Clear UCSWRST to enable USCI_A0



      //---------------- 开启传输中断和接受中断,同时开启全局中断,然后在UCA0TXBUF寄存器中写入一个A ------------------//

   IE2 |= UCA0TXIE;                  // Enable the Transmit interrupt

    IE2 |= UCA0RXIE;                  // Enable the Receive  interrupt

    _BIS_SR(GIE);                     // Enable the global interrupt


    UCA0TXBUF = 'A';                  // Transmit a byte


    _BIS_SR(LPM0_bits + GIE);         // Going to LPM0


    return 0;

}

//-----------------------------------------------------------------------//

  //                Transmit and Receive interrupts                        //

  //-----------------------------------------------------------------------//

#pragma vector = USCIAB0TX_VECTOR

  __interrupt void TransmitInterrupt(void)

  {//当UCA0TXBUF寄存器的A传输后,传输中断会点亮LED 1

    P1OUT  ^= BIT0;//light up P1.0 Led on Tx

  }


#pragma vector = USCIAB0RX_VECTOR

  __interrupt void ReceiveInterrupt(void)

  {//传输的字符A会被接受到UCA0RXBUF寄存器中,接受中断会点亮LED 2,同时记住在此ISR中手动清除掉UCA0RXIFG标记

    P1OUT  ^= BIT6;     // light up P1.6 LED on RX

    IFG2 &= ~UCA0RXIFG; // Clear RX flag

  }


-------------------------------------------code end ------------------------------------------------------------------------


测试该程序的结果如下:

         UCA0STAT |=  UCLISTEN这句代码是通过内部寄存器自动配置为内部循环模式,不用将P1.1和P1.2连接起来,代码就可以正常执行,传输和接受操作都正常,LED1和LED2都会亮。

         注释掉UCA0STAT |=  UCLISTEN代码,再次执行时,会发现只有LED1亮,而LED2不会亮,说明传输出去了,但是没有在本设备上接受。

         注释掉UCA0STAT |=  UCLISTEN代码后,用引脚线将P1.1和P1.2连接起来,再次看看执行效果,会发现LED1和LED2都会亮,说明传输和接受都正常,连接这两个引脚后,数据也在该设备上接受了。



6. 与实际机器通过UART通信

         继续使用上面的代码,不过将内部循环模式取消掉,即注释掉UCA0STAT |=  UCLISTEN。将J3配置为HW UART,如下图:

360截图20161031203625971.jpg
通过USB线连接MSP430 Launchpad后,在设备管理器中可以看到端口处多了一个“MSP430 Application UART COMxx”设备,在PC上通过串口终端工具(PuTTY、Tera等)连接上对应的COMxx,可以看到接收到的A。




回复评论 (5)

360截图20161031203731631.jpg
连接上USB线,并配置好Tera后,不会出现A。按一下MSP430上的reset按钮,就会出现一个A,图中出现多个A,是因为按了多次reset按钮,每次reset后,默认都会执行我们上面的程序,即发送一个A。然后PC上的终端工具Tera就接收到了该字符并显示。在Tera中随便按Enter键,相当于是PC发送一个字符;MSP430的接受中断会接受这个字符,最终会看到按下PC的Enter键后,MSP430上面的LED 2也亮了,说明其接受到了。

         再次强调,必须将MSP430上的跳脚设置为HW UART,否则接受和发送都不会成功。至于HW UART和SW UART的区别,有其历史原因,这里不再叙述。

点赞  2016-10-31 20:38
很详细的资料,谢谢楼主分享
点赞  2016-11-1 00:41
楼主分享的资料非常实用,真心感谢楼主
点赞  2016-11-1 08:41
感谢楼主,对我很有用
点赞  2017-3-27 11:37
如何通过UART传输多个ASCII字符?
点赞  2020-8-14 12:45
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复