본 글은 MSP430G2553 MCU를 기준으로 작성되었습니다.
세부적인 내용은 제품군마다 조금씩 다를 수 있습니다.
USCI란 Universal Serial Communication Interface의 줄임말로, 모드 설정에 따라 여러 가지 통신 모드로 전환하여 사용할 수 있는 시리얼 통신 모듈이다.
쉽게 설명하기 위해 그림을 가져왔다. 위 그림의 오른쪽 아래를 보면 MSP430에는 UART 모듈, SPI모듈, I2C 모듈이 있는 것이 아니라 USCI란 하나의 모듈만 있는 것을 볼 수 있다. USCI 모듈 내에서 모드를 UART모드, SPI모드 I2C모드로 설정하여 해당 통신 모듈로써 사용할 수 있다.
USCI는 USCI_A와 USCI_B로 나뉜다. 간단히 설명하면 USCI_A는 UART와 SPI를 지원하며, USCI_B는 SPI와 I2C를 지원한다.
자세한 내용은 아래 그림을 참고한다.
UART란 Universal Asynchromous Receiver/Transmitter의 약자로, 두 칩 간에 데이터를 주고 받기 위한 시리얼 통신 프로토콜 중 하나이다.
USCI-UART의 특징은 아래와 같다.
- 7- or 8-bit data with odd, even, or non-parity
- Independent transmit and receive shift registers
- Separate transmit and receive buffer registers
- LSB-first or MSB-first data transmit and receive
- Programmable baud rate with modulation for fractional baud rate support
- Status flags for error detection and suppression
- Status flags for address detection
- Built-in idle-line and address-bit communication protocols for multiprocessor systems
- Receiver start-edge detection for auto-wake up from LPMx modes
- Independent interrupt capability for receive and transmit
대부분은 우리가 알고 있는 UART에 대한 설명과 동일하다. 맨 아래 세 줄만 눈여겨 보면 될 것 같다.
USCI-UART의 다이어그램은 아래와 같다.
선택된 입력 클럭으로부터 승산/제산을 거쳐 보레이트가 생성되어 각각 Tx, Rx 모듈로 들어가고, Rx 모듈은 보레이트에 맞춰 받아들인 비트를 결합하여 버퍼에 기록하고 상황에 따른 처리를, Tx 모듈은 보내고자 하는 데이터를 비트 단위로 쪼개어 보레이트에 맞춰 내보내고 상황에 따른 처리를 하는 것을 다이어그램을 통해 알 수 있다.
USCI-UART 관련 레지스터는 아래와 같다.
- UCAxCTLy - USCI_Ax Control Register
- UCAxBRy - USCI_Ax Baud Rate Control Register
- UCAxMCTL - USCI_Ax Modulation Control Register
- UCAxSTAT - USCI_Ax Status Register
- UCAxRXBUF - USCI_Ax Receive Buffer Register
- UCAxTXBUF - USCI_Ax Transmit Buffer Register
- UCAxIRTCTL - USCI_Ax IrDA Transmit Control Register
- UCAxIRRCTL - USCI_Ax IrDA Receive Control Register
- UCAxABCTL - USCI_Ax Auto Baud Rate Control Register
- IE2 - Interrupt Enable Register 2
- IFG2 - Interrupt Flag Register 2
- UC1IE - USCI_A1 Interrupt Enable Register
- UC1IFG - USCI_A1 Interrupt Flag Register
USCI는 다른 모듈들보다 레지스터 수가 훨씬 많아서 일일이 설명하기엔 글이 복잡해질 것 같다는 생각이 든다. 필자도 명세를 다 모르기 때문에, 6번에서 설명할 예제를 바탕으로 하며, 추가적으로 필요한 부분이 있으면 아래의 MSP430x2xx User Guide를 참고하는 것을 권한다.
https://www.ti.com.cn/cn/lit/ug/slau144j/slau144j.pdf (p.429)
USCI_A 설정을 위한 레지스터이다.
UART 보레이트 설정을 위한 레지스터이다.
UART 오버샘플링을 위한 레지스터이다.
UART 상태 관리를 위한 레지스터이다.
UART Rx 버퍼 레지스터이다.
UART Tx 버퍼 레지스터이다.
IrDA 송신 설정을 위한 레지스터이다.
IrDA 수신 설정을 위한 레지스터이다.
보레이트를 자동으로 감지하는 기능을 제공한다고 하는 것 같다.
인터럽트 활성화 레지스터이다. 일부 비트가 USCI_A0용으로 쓰인다.
인터럽트 플래그 레지스터이다. 마찬가지로 일부 비트가 USCI_A0용으로 쓰인다.
USCI_A1용 인터럽트 활성화 레지스터이다.
USCI_A1용 인터럽트 플래그 레지스터이다.
본 글에서는 PC와 MCU 사이의 UART 통신를 예제로 다루며, PC와 개발 보드를 연결하기 위한 TTL-to-USB 케이블과 터미널 프로그램을 이용한다.
준비물
- TTL-to-USB 케이블 + 드라이버
- 터미널 프로그램
본 글에서는 PL2303TA TTL-to-USB 케이블을 사용한다.
PC와 MCU 사이에 UART 통신을 할 수 있다면 꼭 위의 케이블이 아니어도 상관 없다.
TTL-to-USB 케이블을 OS에서 인식하여 사용하기 위해서는 케이블에 맞는 드라이버가 필요하다. 필자는 아래 페이지에서 PL2303TA 드라이버를 다운로드 받아 설치하였다.
PL23XX Prolific Driver for Windows Vista/7/8.1/10
http://www.prolific.com.tw/US/ShowProduct.aspx?p_id=225&pcid=41
본 글에서는 터미널 프로그램으로 Tera Term을 사용한다.
다만 Tera Term은 설치를 해야 하기 때문에, 번거롭다면 PuTTY 무설치판을 쓰는 것을 추천한다.
PuTTY: https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
가장 대표적인 두 가지 예제를 다루어 보자.
- 루프백 Baudrate 9600
- 루프백 Baudrate 115200
9600의 보레이트를 이용하여 PC와 MCU 간의 루프백 UART 통신을 하는 예제이다. 메인 클럭은 1MHz이며, UART 설정 후 CPU는 LPM0 모드로 진입, USCI 모듈은 Rx 인터럽트 수신 시 받은 데이터를 고스란히 Tx로 다시 내보내는 동작을 수행한다.
#include <msp430.h>
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop WDT
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 DCO
DCOCTL = CALDCO_1MHZ;
P1SEL = BIT1 + BIT2 ; // P1.1 = RXD, P1.2 = TXD
P1SEL2 = BIT1 + BIT2 ; // P1.1 = RXD, P1.2 = TXD
UCA0CTL1 |= UCSSEL_2; // SMCLK
UCA0BR0 = 104; // 1MHz 9600
UCA0BR1 = 0; // 1MHz 9600
UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0, interrupts enabled
}
#pragma vector=USCIAB0RX_VECTOR // Echo back RXed character, confirm TX buffer is ready first
__interrupt void USCI0RX_ISR(void)
{
while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?
UCA0TXBUF = UCA0RXBUF; // TX -> RXed character
}
예제 실행 후 터미널의 보레이트를 9600으로 설정한다.
키보드 입력 시, 입력한 값이 터미널에 정상적으로 출력되는 것을 볼 수 있다.
위의 예제에서 보레이트를 115200으로 늘린 예제이다.
#include <msp430.h>
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop WDT
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 DCO
DCOCTL = CALDCO_1MHZ;
P1SEL = BIT1 + BIT2 ; // P1.1 = RXD, P1.2 = TXD
P1SEL2 = BIT1 + BIT2;
UCA0CTL1 |= UCSSEL_2; // SMCLK
UCA0BR0 = 8; // 1MHz 115200
UCA0BR1 = 0; // 1MHz 115200
UCA0MCTL = UCBRS2 + UCBRS0; // Modulation UCBRSx = 5
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0, interrupts enabled
}
#pragma vector=USCIAB0RX_VECTOR // Echo back RXed character, confirm TX buffer is ready first
__interrupt void USCI0RX_ISR(void)
{
while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?
UCA0TXBUF = UCA0RXBUF; // TX -> RXed character
}
예제 실행 후 터미널의 보레이트를 115200으로 설정한다.
키보드 입력 시, 입력한 값이 터미널에 정상적으로 출력되는 것을 볼 수 있다.
루프백 예제로는 통신 속도가 늘어난 것을 확인하기 어렵지만, UART를 통해 대량의 데이터를 전송하거나 무수한 로그 프린트를 출력하도록 한다면 속도 차이가 확연히 나는 것을 알 수 있다.