这一节我们将会看到如何在 MSP430 Launchpad 上使 用 UART 进行通信。我们的程序将会实现通过 UART 从 PC 读入一个字节的数据,然后发送相应的反馈字节给 PC。通信 模式为 115200 波特率,全双工,8 位数据,无奇偶校验,1 位停止位。
MSP430 G2553(Launchpad 使用的芯片)是 MSP430 家 族中比较给力的芯片,它集成了硬件 UART 模块。
UART 通信在处理/调试传感器的时候尤为有用,举一个简单的 例子,我们可以用温度传感器采集数据,经过 AD 转换之后通过 UART 发送给 PC,传送的方式可以是有线,也可以使用无线例如蓝牙。
让我们直接进入正题吧。
首先,你应该已经熟悉了这样的开头:
#include "msp430g2553.h"
#define TXLED BIT0
#define RXLED BIT6
#define TXD BIT2
#define RXD BIT1
const char string[] = { "Hello World\n" };
unsigned int i; //Counter
像前两节中所做的那样,程序开头包含 MSP430G2553 的头文 件,然后宏定义一些常数使得程序更具可读性。
接着我们定义了一个 char 类型数组(即一个 C String),存了 我们将要反馈给 PC 的信息(经典的 Hello World)。最后定义一个 计数变量,辅助反馈字符串的发送。
int main(void) {
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
DCOCTL = 0; // Select lowest DCOx and MODx settings
BCSCTL1 = CALBC1_1MHZ; // Set DCO
DCOCTL = CALDCO_1MHZ;
这依然是大家熟悉的 main 函数的开始方法,首先关闭看门狗定 时器,之后的三行将单片机的内部时钟设置为 1MHz。接下来 UART 和板上所有其他外设都将使用 SMCLK 时钟(sub-main clock)。
P2DIR |= 0xFF; // All P2.x outputs
P2OUT &= 0x00; // All P2.x reset
P1SEL |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
P1SEL2 |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
P1DIR |= RXLED + TXLED;
P1OUT &= 0x00;
这里是配置输入输出引脚:
最后两行是初始化板载 LED。
UCA0CTL1 |= UCSSEL_2; // SMCLK
UCA0BR0 = 0x08; // 1MHz 115200
UCA0BR1 = 0x00; // 1MHz 115200
UCA0MCTL = UCBRS2 + UCBRS0; // Modulation UCBRSx = 5
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UC0IE |= UCA0RXIE; // Enable USCI_A0 RX interrupt
_EINT();
while(1)
{}
}
这是 UART 的配置。第 1 行,如同前面所说的,我们选择 SMCLK 作为 UART 模块的时钟源,用来产生需要的波特率(当然, 你也可以选择其他的时钟源)。调节器差不多会这样工作:
位数 | 分频因子 | 误差(%) | 累积误差(%) |
---|---|---|---|
1 | 8 | +8.5 | +8.5 |
2 | 9 | -3.6 | +4.9 |
3 | 9 | -3.6 | +1.3 |
4 | 9 | -3.6 | -2.3 |
5 | 8 | +8.5 | +6.2 |
…… | …… | …… | …… |
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
P1OUT |= RXLED;
if (UCA0RXBUF == 'a') // 'a' received?
{
i = 0;
UC0IE |= UCA0TXIE; // Enable USCI_A0 TX interrupt
UCA0TXBUF = string[i++];
}
P1OUT &= ~RXLED;
}
这是当 UART 接收到数据时的中断处理程序。如果你看过了 指导书的第二部分,你应该对中断的概率比较熟悉了。
在这个中断处理程序的最后,记得关闭 LED,表示接收中断 处理完毕。
#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void)
{
P1OUT |= TXLED;
UCA0TXBUF = string[i++]; // TX next character
if (i == sizeof(string) - 1) // TX over?
{
UC0IE &= ~UCA0TXIE; // Disable USCI_A0 TX interrupt
}
P1OUT &= ~TXLED;
}
这是UART的发送中断处理程序。和刚才一样,我们点亮一个 LED表示进入发送中断。
烧代码看效果吧!
提示:通信时PC与单片机的互动可以在PC端下载一个串口助手, 比较经典是sscom。串口助手是调试单片机程序的利器!
以下是这一节的完整代码:
#include "msp430g2553.h"
#define TXLED BIT0
#define RXLED BIT6
#define TXD BIT2
#define RXD BIT1
const char string[] = { "Hello World\n" };
unsigned int i; //Counter
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
DCOCTL = 0; // Select lowest DCOx and MODx settings
BCSCTL1 = CALBC1_1MHZ; // Set DCO
DCOCTL = CALDCO_1MHZ;
P2DIR |= 0xFF; // All P2.x outputs
P2OUT &= 0x00; // All P2.x reset
P1SEL |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
P1SEL2 |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
P1DIR |= RXLED + TXLED;
P1OUT &= 0x00;
UCA0CTL1 |= UCSSEL_2; // SMCLK
UCA0BR0 = 0x08; // 1MHz 115200
UCA0BR1 = 0x00; // 1MHz 115200
UCA0MCTL = UCBRS2 + UCBRS0; // Modulation UCBRSx = 5
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UC0IE |= UCA0RXIE; // Enable USCI_A0 RX interrupt
_EINT();
while (1)
{}
}
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
P1OUT |= RXLED;
if (UCA0RXBUF == 'a') // 'a' received?
{
i = 0;
UC0IE |= UCA0TXIE; // Enable USCI_A0 TX interrupt
UCA0TXBUF = string[i++];
}
P1OUT &= ~RXLED;
}
#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void)
{
P1OUT |= TXLED;
UCA0TXBUF = string[i++]; // TX next character
if (i == sizeof(string) - 1) // TX over?
{
UC0IE &= ~UCA0TXIE; // Disable USCI_A0 TX interrupt
}
P1OUT &= ~TXLED;
}