
---------------------------------------------------
IDE: TI CCS 20.3.0
--------------------------------------------------
UART是MCU與外部溝通的常見的協定,
本來是TX RX RTS CTS 火 地 6條線,
但近年來RTS CTS很少用,
通常直接TX RX 火 地就好了,
常見的傳輸速率為11520bps 和 9600bps🏃➡️🏃➡️🏃➡️
程式碼
先看程式碼吧! 直接貼應該是要能build的過😳#include <msp430.h>
#include <stdint.h>
//================================Define區=============================
//Uart Part
//協定常數
#define RX_HEAD (0xAA)
#define RX_MAX (254)
//接收狀態
typedef enum {
RX_WAIT_HEAD = 0,
RX_WAIT_LEN,
RX_RECV_DATA,
RX_WAIT_CHK
} rx_state_t;
//================================變數區================================
//Uart Part
static volatile rx_state_t rx_state = RX_WAIT_HEAD;
static volatile uint8_t rx_len = 0;
static volatile uint8_t rx_idx = 0;
static volatile uint8_t rx_chk = 0;
//接收完成後可由主迴圈讀取
static volatile uint8_t frame_ready = 0;
static volatile uint8_t frame_len = 0;
static volatile uint8_t frame_buf[RX_MAX]; // 保存 payload
//可選:統計錯誤
static volatile uint16_t rx_err_chk = 0;
static volatile uint16_t rx_err_ovf = 0;
//================================函式區=================================
//UART Part---------------------------------------------------------------------
void setupUART_p16_p17(void)
{
// P1.6 = RXD, P1.7 = TXD
P1DIR &= ~BIT6; // RXD 輸入
P1DIR |= BIT7; // TXD 輸出
P1SEL0 |= (BIT6 | BIT7); // P1SELx = 01 → UCA0RXD/UCA0TXD
P1SEL1 &= ~(BIT6 | BIT7);
}
// 波特率: 9600 @ SMCLK=1MHz (UCBRx=104, UCBRS?1)
static void uart_init_1M_9600(void)
{
PM5CTL0 &= ~LOCKLPM5; // 保險再解一次
// 腳位你已經在 setupUART_p16_p17() 設好了
UCA0CTLW0 = UCSWRST; // 進 reset
UCA0CTLW0 |= UCSSEL__SMCLK; // BRCLK = SMCLK (1 MHz)
UCA0BRW = 6; // BRW
UCA0MCTLW = UCOS16 // 開 oversampling
| (13 << 4) // UCBRF = 13
| (0x00 << 8); // UCBRS = 0
UCA0CTLW0 &= ~UCSWRST; // 出 reset
UCA0IE |= UCRXIE; // 要 RX 中斷再開
}
/* ===== 基礎傳送 ===== */
static inline void uart_putc(uint8_t c)
{
while (!(UCA0IFG & UCTXIFG));
UCA0TXBUF = c;
}
static void uart_send_bytes(const uint8_t *p, uint16_t n)
{
while (n--) {
uart_putc(*p++);
}
}
/* 一次組好並送出整包(長度上限 255 byte 資料) */
static void uart_send_frame(const uint8_t *data, uint8_t len)
{
uint8_t buf[255]; // Head + Len + Data
uint8_t n = 0;
buf[n++] = 0xAA; // Head
buf[n++] = len; // Length
uint8_t i;
for (i = 0; i < len; i++) {
buf[n++] = data[i];
}
uart_send_bytes(buf, n); // 一次送出去
// 小延遲,避免連續封包黏在一起
__delay_cycles(16000); // 約 16ms @1MHz
}
// 取走一包資料(回傳 1=OK, 0=尚未有新包)
uint8_t uart_frame_read(uint8_t *dst, uint8_t *len)
{
uint8_t i;
if (!frame_ready) return 0;
uint8_t n = frame_len;
for (i=0; i<n; ++i) dst[i] = frame_buf[i];
*len = n;
frame_ready = 0; // 清旗標,允許下一包
return 1;
}
// RX 中斷(eUSCI_A0)
#pragma vector=USCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void)
{
switch (__even_in_range(UCA0IV, 0x08)) {
case 0x02: { // UCRXIFG
uint8_t b = UCA0RXBUF;
switch (rx_state) {
case RX_WAIT_HEAD:
if (b == RX_HEAD) rx_state = RX_WAIT_LEN;
break;
case RX_WAIT_LEN:
rx_len = b;
rx_idx = 0;
if (rx_len > RX_MAX) {
rx_err_ovf++;
rx_state = RX_WAIT_HEAD;
} else {
rx_state = RX_RECV_DATA;
}
break;
case RX_RECV_DATA:
{
// 先取出 volatile,避免 ++ 與比較衝突
uint8_t idx = rx_idx;
uint8_t len = rx_len;
if (idx < RX_MAX) {
frame_buf[idx] = b;
idx++;
rx_idx = idx; // 回存
} else {
rx_err_ovf++;
rx_state = RX_WAIT_HEAD;
break;
}
if (idx >= len) {
frame_len = len;
frame_ready = 1;
rx_state = RX_WAIT_HEAD;
__no_operation(); // debug 斷點
}
}break;
}
} break;
default: break;
}
}
void parser_uartdata(uint8_t *payload, uint8_t len)
{
if (len < 1) return;
uint8_t cmd = payload[0];
if (cmd == 0x01) {
uint8_t message[5];
message[0] = 0x55;
message[1] = 0x44;
message[2] = 0x33;
message[3] = 0x22;
message[4] = 0x11;
uart_send_frame(message, 5);
}
}
//GPIO Part---------------------------------------------------------------------
static void gpio_input_init(void)
{
// P1.1 當按鈕:內部上拉、下降緣中斷
P1DIR &= ~BIT1; // Input
P1REN |= BIT1; // 啟用上/下拉
P1OUT |= BIT1; // 上拉
P1IES |= BIT1; // 高->低(按下)觸發
P1IFG &= ~BIT1; // 清旗標
P1IE |= BIT1; // 開中斷
}
//=================== 中斷 ===================
#pragma vector=PORT1_VECTOR
__interrupt void PORT1_ISR(void)
{
if (P1IFG & BIT1) {
P1IFG &= ~BIT1;
P1IE &= ~BIT1; // 先關中斷避免彈跳重入
uint8_t message[5];
message[0] = 0x11;
message[1] = 0x22;
message[2] = 0x33;
message[3] = 0x44;
message[4] = 0x55;
uart_send_frame(message, 5);
// 重新設定下降緣(避免鬆開的上升緣誤觸)
P1IES |= BIT1; // 高->低
P1IFG &= ~BIT1;
P1IE |= BIT1; // 再次開啟按鈕中斷
}
}
//================================主程式=================================
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
PM5CTL0 &= ~LOCKLPM5; // 解除高阻態,讓 GPIO 可用(即使本例沒用到腳位,建議保留)
//GPIO init
gpio_input_init(); // P1.1 按鈕 input
//Uart Init
setupUART_p16_p17(); // ★ 腳位切到 UCA0
uart_init_1M_9600(); // ★ 設定 9600bps
__enable_interrupt(); // 開啟中斷
while(1){
uint8_t buf[10], n;
if (uart_frame_read(buf, &n)) {
parser_uartdata(buf, n);// 這裡 buf[0..n-1] 就是解出的 payload
__no_operation(); // (IAR 會產生一條 NOP 指令)
}
}
}
這次分享的程式碼有幾點要注意一下:
- 傳輸速率的部分AI跟網路上可能會建議用下圖數據

因為G蛋自己環境實測出來,我用上面數據量到的速率都不是9600,會偏一點💣💣
也不知道為啥,上面G蛋的數據是調出來的🫣🫣,
若一開始您也收不到,可以往這部分調整。
(有遇到的也請留言下唷😁)
------------------
心得: 韌體就是這樣,千奇百怪的問題,不合理的邏輯也蠻常出現的,習慣就好🤣🤣 所有Bug ,都要盡量懷疑盡量試,有時也是要憑運氣跟靈感😐
------------------
- 程式碼裡含按鈕的部分,主要是驗證Tx用。
- 軟體沒有做CRC等偵錯機制,可依需求自己加。
- 驗證Tx Rx 有沒有通,還是透過外部裝置來驗證比較簡單,
本來我想直接一條線 TX 接 RX 就好,自己發自己收,
但會遇到時序問題,
例如:你發送的時候還沒開始收,可能就會收不到。 - 驗證會需要用以下
4.1 UART 轉 USB Dongle 。
4.2 對接軟體 - AccessPort137 。
(google一下應該很好找,找不到在留言跟G蛋說)。
(其他COM Port軟體也行)。

UART 轉 USB Dongle (範例)
驗證
連接架構:

接法:
記得
😈😈Dongle 的Tx要接開發板的Rx😈😈。
😈😈Dongle 的Rx要接開發板的Tx😈😈。
這提醒很蠢,但還是有人常常Tx接Tx,Rx接Rx 🫣🫣🫣。

- 通道連線設定
1.在電腦上開啟 AccessPort137 選擇 Dongle所對應的COM Port。
2.設定(確認)連線參數。
3.連線成功。

選擇埠號 & 設定參數

確認參數
- 驗證Tx
1.連上軟體後,按下開發板的按鈕(P1.1)。
2.軟體應該要收到
0x11 0x22 0x33 0x44 0x55 。
0xAA : 為傳輸標頭。
0x05 : 資料長度 。

Tx 驗證
- 驗證Rx
1.在軟體下方輸入 AA 05 01 02 03 04 05
2.按下發送👇
3.應該要收到 AA 05 55 44 33 22 11
(表示Rx有收到資料 並又發送了新資料給軟體)

驗Rx
有些用文字的可能也說不清楚👀👀
有不清楚的在留言討論吧🙋♀️🙋♀️🙋♀️🙋♂️🙋♂️🙋♂️
或是之後拍隻影片講解一下🙇♂️🙇♂️🤣🤣













