中科藍訊530X、532X模組之硬體UART

文章轉載請註明來源  作者:Zeroer

一、選擇IO

想要使用硬體的UART必須先確定要mapping的pin腳

注意:用作TX的腳位可以分時復用成單線雙工

因為晶片默認的調試串口用的是UART0,所以我們在做功能的時候使用UART1和UART2可以不用重新banding我們的調試介面

首先選擇好我們的腳位後,對我們的IO進行初始化,我們以UART2的PB1、PB2為例

GPIOBDE  |= (BIT(2) | BIT(1));    //As Digital IO
GPIOBPU  |= (BIT(2) | BIT(1));    //pull-up 10K
GPIOBDIR &= ~BIT(2);          //output
GPIOBDIR |= BIT(1);           //input
GPIOBFEN |= (BIT(2) | BIT(1));    //set function io

因為UART是屬於我們的功能io,所以在初始化的時候需要將io配置為功能io,且一個腳同時只能有一個功能

二、配置IO映射

接下來對我們的IO映射到我們的UART功能上

FUNCMCON1 = (0xff<<4);         //Clear mapping
//two line mode
FUNCMCON1 = ((0x2<<4)|(0x2<<8));    //Map to Group 2
//single mode
FUNCMCON1 = ((0x2<<4)|(0x3<<8));   //Map to Group 2 and map RX pin by TX pin

↑↑↑這個是UART2的map暫存器以及佔位

FUNCMCON0 = (0xff<<24);         //Clear mapping
//two line mode
FUNCMCON0 = ((0x2<<24)|(0x2<<28));   //Map to Group 2
//single mode
FUNCMCON0 = ((0x2<<24)|(0x3<<28));   //Map to Group 2 and map RX pin by TX pin

↑↑↑這個是UART1的map暫存器以及佔位

FUNCMCON0 = (0xff<<8);         //Clear mapping
//two line mode
FUNCMCON0 = ((0x2<<8)|(0x2<<12));   //Map to Group 2
//single mode
FUNCMCON0 = ((0x2<<8)|(0x3<<12));   //Map to Group 2 and map RX pin by TX pin

↑↑↑這個是UART0的map暫存器以及佔位

選擇我們使用的暫存器通道以及單線或者雙線模式

三、UART參數配置

繼續對UART功能進行配置,先對我們UART的參數進行設置

UART2CON = 0;
UART2CON |= (BIT(5)|BIT(7)|BIT(2));

我們選擇打開了Rx的中斷接收,然後將時鐘源切換到uart的獨立時鐘,然後我們對時鐘進行設置

CLKCON1 |= BIT(14);    //select 26M div2
CLKGAT1 |= BIT(11);    //clk enble
u32 baud = (26000000 / 2 + 115200 / 2) / 115200 - 1; //baud: 115200
UART2BAUD = ((baud<<16) | baud);

最後配置完所有的東西後,我們打開我們的UART,然後註冊我們的中斷函數

UART2CON |= BIT(0);
sys_irq_init(IRQ_UART_VECTOR, 0, uart2_isr_func);

這時候我們需要提供一個名為uart2_isr_func(也可以任意命名)的函數處理中斷

u8 data = 0;

AT(.com_text.isr)
void uart2_isr_func(void)
{
    if (UART2CON & BIT(9)){
        UART2CPND |= BIT(9);
        data = UART2DATA;
    }
}

中斷函數必須要用AT宏放到ram區域去,接下來我們寫一個發送函數就完成了

void uart2_send(char *data, u8 len)
{
    for(int i=0; i<len; i++)
    {
        while(!(UART2CON & BIT(8)));
        UART2DATA = data[i];
    }
}

四、總結

如果我們需要使用到UART0,我們就需要給調試介面重新寫一個UART然後通過binding將print通過UART輸出。

void my_printf_init(void (*putchar)(char));

最後我將完整的程式碼貼在後面提供給大家測試調試,會和前面的程式碼有一些差異,但是功能是一樣的

char recv_buf[128];
u8 recv_len = 0;
AT(.com_rodata.isr)
const char ptr_t3[] = "RX:%lx\n";
AT(.com_rodata.isr)
const char ptr_r3[] = "TX:%lx\n";
AT(.com_text.isr)
void uart2_isr_func(void) //FIQ
{
    if (UART2CON & BIT(9)){
        UART2CPND |= BIT(9);
        if (recv_len < 128) {
            recv_buf[recv_len] = UART2DATA;
            recv_len++;
        } else {
            memset(recv_buf, 0, sizeof(recv_buf));
            recv_len = 0;
        }
    }
}

void uart2_send(char *data, u8 len)
{
    printf(ptr_r3,data[0]);
    for(int i=0; i<len; i++)
    {
        while(!(UART2CON & BIT(8)));
        UART2DATA = data[i];
    }
}
#define BAUD_UART2          ((26000000/2+2000000/2)/2000000-1)
void uart2_init(void)
{
    printf("%s\n",__func__);

    GPIOBDE  |= (BIT(2) | BIT(1));
    GPIOBPU  |= (BIT(2) | BIT(1));
    GPIOBDIR &= ~BIT(2);
    GPIOBDIR |= BIT(1);
    GPIOBFEN |= (BIT(2) | BIT(1));

    FUNCMCON1 = (0xff<<4);
    FUNCMCON1 = ((0x02<<4)|(0x02<<8));

    UART2CON = 0;
    UART2CON |= (BIT(5)|BIT(7)|BIT(2));

    CLKCON1 |= BIT(14);
    CLKGAT1 |= BIT(11);
    
    UART2BAUD = ((BAUD_UART2<<16) | BAUD_UART2);

    UART2CON |= BIT(0);
    sys_irq_init(IRQ_UART_VECTOR, 0, uart2_isr_func);
}