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模式的简单框图如下:
从上图可知,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的用户手册中给出了一个波特率选择表,根据该表可以很容易的对波特率进行配置,该表如下:
通过该表找到对应的值填入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,如下图:
通过USB线连接MSP430 Launchpad后,在设备管理器中可以看到端口处多了一个“MSP430 Application UART COMxx”设备,在PC上通过串口终端工具(PuTTY、Tera等)连接上对应的COMxx,可以看到接收到的A。