关于设计,生活和电子的二三事

走进MSP430(5)——MSP430G2串口使用

虽然现在USB越来越常见,但作为工控领域最常见的接口之一,和最通用的端口之一,串口仍然有很大用处。基本上所有单片机都有串口,并且使用简单,可以作为单片机和电脑、单片机和外设或单片机之间通信使用,在程序调试时如果使用得当会带来很大方便。
和其他外设一样,串口使用首先需要初始化,然后是接收和发送数据函数,然后是数据处理部分。这一节的主要内容是串口初始化和收发函数,因为数据处理部分和应用密切相关,这一节不会讲到这一部分。下面结合代码讲解。代码来源于MSPWare,TI提供的MSP430例程库,在运行此部分程序时需要将开发板附带的32.768KHz晶振焊上。

#include <msp430.h>

const char string1[] = { "Hello World\r\n" };
unsigned int i;

int main(void)
{
    WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
    P1DIR = 0xFF;                             // All P1.x outputs
    P1OUT = 0;                                // All P1.x reset
    P2DIR = 0xFF;                             // All P2.x outputs
    P2OUT = 0;                                // All P2.x reset
    P1SEL = BIT1 + BIT2 ;                     // P1.1 = RXD, P1.2=TXD
    P1SEL2 = BIT1 + BIT2 ;                     // P1.1 = RXD, P1.2=TXD
    P3DIR = 0xFF;                             // All P3.x outputs
    P3OUT = 0;                                // All P3.x reset
    UCA0CTL1 |= UCSSEL_1;                     // CLK = ACLK
    UCA0BR0 = 0x03;                           // 32kHz/9600 = 3.41
    UCA0BR1 = 0x00;                           //
    UCA0MCTL = UCBRS1 + UCBRS0;               // Modulation UCBRSx = 3
    UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
    IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt

    __bis_SR_register(LPM3_bits + GIE);       // Enter LPM3 w/ int until Byte RXed
}

#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void)
{
    UCA0TXBUF = string1[i++];                 // TX next character

    if (i == sizeof string1 - 1)              // TX over?
        IE2 &= ~UCA0TXIE;                       // Disable USCI_A0 TX interrupt
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
    if (UCA0RXBUF == 'u')                     // 'u' received?
    {
        i = 0;
        IE2 |= UCA0TXIE;                        // Enable USCI_A0 TX interrupt
        UCA0TXBUF = string1[i++];
    }
}

首先是串口初始化。在main函数中首先将串口对应的端口配置为外设功能。因为MSP430所有端口上电默认为GPIO,输入,因此需要先将对应端口分配给串口模块使用。在MSP430G2553中,串口对应的端口为P1.1和P1.2,因此将P1SEL和P1SEL2对应位设为1即为将P1.1和P1.2分配给串口模块。MSP430G2553芯片有几种不同的封装,最多有32个引脚,而LAUNCHPAD上的是20引脚的型号,其中P3和P2的一部分引脚没有引出,但芯片中存在对应寄存器,因此引脚初始化中将没有用到的P2和P3组引脚配置为输出低电平状态,避免引脚处于不确定状态。此部分代码如下:

    P1DIR = 0xFF;                             // All P1.x outputs
    P1OUT = 0;                                // All P1.x reset
    P2DIR = 0xFF;                             // All P2.x outputs
    P2OUT = 0;                                // All P2.x reset
    P1SEL = BIT1 + BIT2 ;                     // P1.1 = RXD, P1.2=TXD
    P1SEL2 = BIT1 + BIT2 ;                     // P1.1 = RXD, P1.2=TXD
    P3DIR = 0xFF;                             // All P3.x outputs
    P3OUT = 0;                                // All P3.x reset

然后是配置串口外设功能。首先配置串口时钟,例程使用ACLK作为串口时钟,时钟速率为32.768KHz。由于MSP430G2系列内部带有校准过的DCO,也可使用DCO作为串口时钟源,以达到更高通信速率。UCA0BR0和UCA0BR1用于配置串口通信速率,UCA0BR0为低8位,UCA0BR1为高8位。串口速率和串口外设时钟速率关系如下:
串口速率 = 时钟速率 / (UCA0BR0 + UCA0BR0 * 256)
UCA0MCTL用于配置串口时钟产生和检测方式。由于串口为异步接口,接口中不包含时钟信号,因此时钟信号需要单机自己产生,并通过产生的时钟信号检测串口数据。MSP430有两种串口时钟产生方式,即Modulation和Oversampling两种方式,通过UCOS16寄存器位选择。在串口时钟使用低频时钟时采用Modulation方式,在串口时钟使用高频时钟时采用Oversampling方式。在Modulation方式下,串口速率最快可以达到1/3时钟速率。此方式下串口数据检测方式如下:

在每个串口数据位周期内,在对应的串口时钟的第N/2,N/2 + 1和N/2 – 1位采样。如上图BRCLK为串口时钟,BITCLK为串口数据流,图上Bit Period为一个串口位,一个串口位对应N个串口时钟。在对应串口时钟的N/2 – 1,N/2,N/2 + 1的时刻进行采样,对采样结果进行多数判决确定数据对应位为0还是1。采样图案可以通过UCBRSx寄存器设置,最多可在一个串口位进行7次采样,即在串口位对应的N/2 – 4,N/2 – 3,N/2 – 2……N/2 + 3,N/2 + 4个串口时钟位采样。这样的好处是在较低的串口速率下可以将串口配置为低速时钟,降低电源消耗。
另一种方式Oversampling在串口时钟使用高频时钟时使用。这种方式下串口速率可以达到串口时钟速率的1/16。这种方法首先通过预分频器产生一个频率是串口速率16倍的时钟,然后依据这个时钟和采样图形进行采样,采样图形如下图所示:

因为需要产生串口采样时钟,这个时钟是通过串口时钟分频得到,因此这种方式串口速率不能超过串口时钟速率的1/16。
配置完成完成时钟后,通过UCA0CTL1 &= ~UCSWRST;启用串口模块,然后打开接收中断。到此串口配置部分完成。下面是完整的串口配置部分代码

    UCA0CTL1 |= UCSSEL_1;                     // CLK = ACLK
    UCA0BR0 = 0x03;                           // 32kHz/9600 = 3.41
    UCA0BR1 = 0x00;                           //
    UCA0MCTL = UCBRS1 + UCBRS0;               // Modulation UCBRSx = 3
    UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
    IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt

然后是串口收发部分。这部分都是在串口中断中进行的。串口中断分为两个中断,接收中断和发送中断。(顺便说一下,因为MSP430G2553通信部分不同通信方式共用这两个中断,因此不同通信方式,如IIC,SPI,串口等不能同时使用。这是一个大坑=。 =之前我打算用G2553做一个串口转IIC,但死活不成功,表现是串口能正常工作,但没法转发到IIC。)
首先是接收中断。注意看串口配置部分最后,只打开了接收中断,没有打开发送中断。因为发送中断会在UCA0TXBUF为空的时候产生,而UCA0TXBUF通常会在串口配置完成后很短时间内变为空,如果没有数据写入UCA0TXBUF,就会一直产生发送中断,因此发送中断只在需要时候打开。因为这个只是演示程序,所以数据处理部分写的比较简单。在判断到接受的数据是字母u的时候回复一串字符串。因为MSP430会在UCA0RXBUF被读取之后自动清除接收中断,因此接收中断函数中没有清除中断标志位的代码。

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
    if (UCA0RXBUF == 'u')                     // 'u' received?
    {
        i = 0;
        IE2 |= UCA0TXIE;                        // Enable USCI_A0 TX interrupt
        UCA0TXBUF = string1[i++];
    }
}

然后是发送中断。发送中断标志位会在向UCA0TXBUF写入数据后自动清除,因此函数里没有清除中断标志位的代码。当发送完成后,关掉发送中断,然后等待下一次收到数据。

#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void)
{
    UCA0TXBUF = string1[i++];                 // TX next character

    if (i == sizeof string1 - 1)              // TX over?
        IE2 &= ~UCA0TXIE;                       // Disable USCI_A0 TX interrupt
}

主函数中有一行代码如下:

__bis_SR_register(LPM3_bits + GIE);       // Enter LPM3 w/ int until Byte RXed

因为MSP430外设可以在CPU处于低功耗模式下工作,通过中断唤醒CPU,因此这一行的作用就是让CPU进入LPM3低功耗模式下,并打开全局中断,以降低芯片功耗。
另外还有一点,MSP430 LAUNCHPAD带的串口似乎只支持9600波特率,如果使用大于9600波特率的串口的话可能会无法正确收发,这时候需要断开LAUNCHPAD上的TXD,RXD两个跳线,然后从P1.1,P1.2两个引脚引线接到另外的USB转串口上。
除此之外,有兴趣的话可以看看USER GUIDE中串口部分的框图,了解串口部分模块结构,可以更好理解串口工作流程。
串口部分到此结束,高级功能,如自动检测波特率,校验等功能自己尝试使用。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据