UART(Universal Asynchronous Reciver/Transmister)

Introduction to UART

UART (Universal Asynchronous Reciver/Transmister) một chuẩn giao tiếp không đồng bộ cho MCU và các thiết bị ngoại vi.

Chuẩn UART là chuẩn giao tiếp điểm và điểm, nghĩa là trong mạng chỉ có hai thiết bị đóng vai trò là transmister hoặc reciver.

  • Truyền thông cao cấp được sử dụng: LIN, CAN, ETH, FR,...

  • Truyền thông trên mạch điện(onboard) : UART, SPI, I2C,...

💡
Giao tiếp không đồng bộ (asynchronous communication) là một loại giao tiếp trong đó các bên không cần phải đồng bộ hóa thời gian khi truyền thông tin. Trong môi trường không đồng bộ, dữ liệu có thể được truyền đi và nhận về mà không cần phải có một đồng hồ chung hoặc bất kỳ thời gian cố định nào giữa các bước truyền thông tin.

VD: Khi bạn gửi một email, bạn không cần phải đợi người nhận đọc nó ngay lập tức. Người nhận có thể đọc email khi nào họ muốn và trả lời khi họ có thời gian. Cả hai đều hoạt động không đồng bộ và không cần phải truyền thông tin theo một lịch trình cố định.

How it works?

UART (Universal Asynchronous Receiver/Transmitter) là giao thức truyền thông không đồng bộ, không yêu cầu sự đồng bộ hóa thời gian bằng xung Clock chung. Các thiết bị có thể truyền và nhận đồng thời, tạo nên truyền thông song công (Full Duplex).

Quan trọng nhất là tốc độ truyền thông, được đo bằng Baud rate, là thời gian cần thiết để truyền một bit. Cả hai thiết bị cần được cài đặt với cùng một Baud rate để đảm bảo đồng bộ trong truyền thông.

💡
"Full Duplex" là một thuật ngữ để mô tả khả năng của một kênh truyền thông cho phép truyền và nhận dữ liệu đồng thời, tức là có thể thực hiện cả hai hành động một cách độc lập và cùng một lúc.

Ký hiệu:

  • Truyền thông tin (TX).

  • Nhận thông tin (RX).

  • Tham số truyền [(Baudrate,tần số truyền 1 bit, đơn vị: bps).

  • Số bit mỗi khung truyền (Bitdata, bao gồm 8,9 -->UART gửi thông tin theo dạng từng khung một)

How to configure UART?

  • Về cách cấu hình UART khá là đơn giản, tôi sẽ làm trên con FRDM - KL46Z. Trên IDE là MCUXpresso. Link tải: https://www.nxp.com/design/design-center/software/development-software/mcuxpresso-software-and-tools-/mcuxpresso-integrated-development-environment-ide:MCUXpresso-IDE

    💡
    FRDM-KL46Z là một bo mạch phát triển của NXP Semiconductors, thuộc dòng Freedom Development Platform, được thiết kế để hỗ trợ việc phát triển ứng dụng nhúng trên vi điều khiển ARM Cortex-M0+ trong dòng Kinetis KL46Z. Bo mạch này cung cấp nhiều tính năng như giao tiếp USB, nhiều giao diện I/O, giao tiếp SPI, I2C, UART, và cổng debug OpenSDA, giúp phát triển và kiểm thử phần cứng và phần mềm một cách thuận lợi.
  • FRDM-KL46Z #1: Nền tảng phát triển hệ thống máy tính nhúng ARM - STEM  Education

    Về cách cấu hình thì gồm có 3 bước:

  • Cấu hình clock ( có thể là clock nội, FLL, PLL, Clock thạch anh)

  • Cấu hình chân A1 RX, A2 TX

  • Cấu hình tham số UART

Cấu hình clock

  • Bạn phải tìm đến thanh ghi SIM_SOPT2 ở phần UART0SRC

  • Nếu nó ở 00(0) thì clock k bật, 01(1) bật clock FLL( tối đa là 21Mhz), 10(2) bật clock thạch anh(OSC) ( Tối đa là 8Mhz), 11(3) là bật clock nội ( 4Mhz)

  • Ở đây tôi sẽ sử dụng clock nội ( 4Mhz)

    /*cấu hình clock nội 4MHz*/
    MCG->SC &= ~MCG_SC_FCRDIV_MASK; /* không chia */
    MCG->C2 |= MCG_C2_IRCS_MASK; /* chọn fast IRC */
    MCG->C1 |= MCG_C1_IRCLKEN_MASK; /* bật clock internal reference clock */
    SIM->SOPT2 |= SIM_SOPT2_UART0SRC(3); /* chọn nguồn cho uart là MCGIRCLK */

Cấu hình chân A1 RX, A2 TX

    /*cấu hình chân A1 RX  A2 TX*/
    SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK;
    PORTA->PCR[1] &= ~ PORT_PCR_MUX_MASK;
    PORTA->PCR[1] |= PORT_PCR_MUX(2);
    PORTA->PCR[2] &= ~ PORT_PCR_MUX_MASK;
    PORTA->PCR[2] |= PORT_PCR_MUX(2);

Cấu hình tham số UART

  • Để cấu hình được tham số UART thì chúng ta cần biết về baudrate là gì?

  • Baudrate là một thuật ngữ thường được sử dụng trong việc truyền thông dữ liệu, đặc biệt là trong ngữ cảnh của truyền thông serial (giao tiếp nối tiếp). Baudrate thường được đo bằng số lượng các biểu tượng (bits) dữ liệu được truyền đi trong một giây.

  • VD: Baudrate thường được đặc tả bằng số bit dữ liệu được truyền đi mỗi giây (bps - bits per second). Ví dụ, nếu bạn nghe nói về một baudrate là 9600 bps, điều này có nghĩa là 9600 bit dữ liệu được truyền đi mỗi giây trong quá trình truyền thông.

  • Công thức tính Baudrate

  • UART ASYNCH Module Clock: Chính là clock nội (4Mhz) ta vừa cấu hình hoặc là FLL, PLL,...

  • SBR là (thanh ghi Baudrate). SBR được phân chia thành hai phần: BDH (Baudrate Register High) và BDL (Baudrate Register Low)

  • OSR là viết tắt của "Over Sampling Ratio" (Tỷ lệ Lấy Mẫu Quá Khả Năng). Tỷ lệ này ám chỉ số lượng lần lấy mẫu trên mỗi bit dữ liệu khi truyền thông serial. Giá trị OSR chạy trong khoảng 3 đến 31

  • 💡
    Với giá trị OSR mà nhỏ hơn 8 thì sẽ bị lỗi với baudrate quá cao.
  • Thì cách cấu hình UART

  • Start Bit (bat dau tu 0)

  • Data bit

  • Bit Parity (có thể chẵn hoặc lẻ hoặc không dùng)

  • Stop bit

  • OSR = 31

  • SBR = 13

  • Mục đích là để chạy được baudrate 9600

    /* cấu hình tham số UART */
    SIM->SCGC4 |= SIM_SCGC4_UART0_MASK;
    UART0->C1 &= ~UART_C1_M_MASK; /* 8 bit mode */
    UART0->C1 &= ~UART_C1_PE_MASK; /* none parity */
    UART0->BDH &= ~ UART_BDH_SBNS_MASK; /* 1 stop bit */
    /* baudrate OSR = 31, SBR = 13, mục đích là để chạy baudrate đầu là 9600*/
    UART0->C4 = 31;
    UART0->BDL = 13;
    /*bật mode TE*/
    UART0->C2 |= UART_C2_TE_MASK;
    /*bật mode RE*/
    UART0->C2 |= UART_C2_RE_MASK;

Vậy là chúng ta đã xong cấu hình cho UART rồi. Để test thì chúng ta cần làm thêm 1 hàm là UART_SendString và UART_Sendchar để nó đọc được chuỗi mình gửi. Và đồng thời cài thêm terminal ngoài là Tera Term để có thể chạy được

void UART_SendChar (char c)
{
    UART0->D = c;
    while(((UART0->S1)&UART_S1_TDRE_MASK) != UART_S1_TDRE_MASK);

}
void UART_SendString(const char *str)
{
    uint32_t i;
    for(i = 0; str[i] != '\0'; i++)
    {
        UART_SendChar(str[i]);
    }
}

Code hoàn chỉnh:

#include "MKL46Z4.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/


/*******************************************************************************
 * Prototypes
 ******************************************************************************/
void UART_SendString(const char *str);
void UART_SendChar (char c);
void main()
{

    /*cấu hình clock nội 4MHz*/
    MCG->SC &= ~MCG_SC_FCRDIV_MASK; /* không chia */
    MCG->C2 |= MCG_C2_IRCS_MASK; /* chọn fast IRC */
    MCG->C1 |= MCG_C1_IRCLKEN_MASK; /* bật clock internal reference clock */
    SIM->SOPT2 |= SIM_SOPT2_UART0SRC(3); /* chọn nguồn cho uart là MCGIRCLK */

    /*cấu hình chân A1 RX  A2 TX*/
    SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK;
    PORTA->PCR[1] &= ~ PORT_PCR_MUX_MASK;
    PORTA->PCR[1] |= PORT_PCR_MUX(2);
    PORTA->PCR[2] &= ~ PORT_PCR_MUX_MASK;
    PORTA->PCR[2] |= PORT_PCR_MUX(2);

    /* cấu hình tham số UART */
    SIM->SCGC4 |= SIM_SCGC4_UART0_MASK;
    UART0->C1 &= ~UART_C1_M_MASK; /* 8 bit mode */
    UART0->C1 &= ~UART_C1_PE_MASK; /* none parity */
    UART0->BDH &= ~ UART_BDH_SBNS_MASK; /* 1 stop bit */
    /* baudrate OSR = 31, SBR = 13, mục đích là để chạy baudrate đầu là 9600*/
    UART0->C4 = 31;
    UART0->BDL = 13;
    /*bật mode TE*/
    UART0->C2 |= UART_C2_TE_MASK;
    /*bật mode RE*/
    UART0->C2 |= UART_C2_RE_MASK;

    while(1)
    {

        UART_SendString(" MU Vo Doi !\r\n");

    }

}

void UART_SendString(const char *str)
{
    uint32_t i;
    for(i = 0; str[i] != '\0'; i++)
    {
        UART_SendChar(str[i]);
    }
}

void UART_SendChar (char c)
{
    UART0->D = c;
    while(((UART0->S1)&UART_S1_TDRE_MASK) != UART_S1_TDRE_MASK);

}

Và thiết lập trên Tera Term ở speed 9600

Và kết quả chạy được là: