CH582m串口透傳程序


目錄

先前在582上實現了“上位機通過串口1發送指令規定串口2和3的波特率,實現串口2和3之間的數據透傳”的功能,其實更普遍的用法就是串口間的透傳,沒有前提條件,故筆者在573上又用結構體封了一下相關變量,看上去更規整些,並加上了查詢發送與中斷發送切換的宏。接收方向上,都是中斷接收的沒有區別。

復制代碼后,編譯若報錯找不到UART3相關的函數,需要將相關庫添加到工程中一起編譯。

582也是一樣的代碼,將下面代碼的頭文件引用改成CH58x_common.h,即可。

#include "CH57x_common.h"  //使用582需要講這里改成包含CH58x_common.h

#define INQUIER_SEND  0      //查詢方式從另一串口發出,波特率不一致也適用
#define INTERRUPT_SEND  1    //中斷方式從另一串口發出。注意:在兩透傳串口波特率不一致時,
                             //由於低波特率的輸出占用時間長,會影響到高波特率串口中斷數據的接收,造成丟包

#define HOW_2_SEND INQUIER_SEND

uint32_t irq_status;    //用於保留中斷值

uint8_t u1_rx_Buff[64];
uint8_t u3_rx_Buff[64];

struct _uart_rx_str_{
    uint8_t *rx_buff;       //接收緩存數組
    uint8_t max_buff_size;  //接收緩存數組最大長度,越界報錯
    uint8_t rx_index;       //已緩存數組下標
    uint8_t rx_done;        //在接收超時中斷中置位,表示接收完成
    uint8_t trig_num;       //接收FIFO觸發字節數
};

typedef struct  _uart_rx_str_  uart_rx_str;

uart_rx_str u1_rx_str = {.rx_buff = u1_rx_Buff, .max_buff_size = sizeof u1_rx_Buff};    //定義接收結構體並部分初始化
uart_rx_str u3_rx_str = {.rx_buff = u3_rx_Buff, .max_buff_size = sizeof u3_rx_Buff};


void u1_init(uint32_t btl)
{
    uint32_t x;

    /* 配置串口1:先配置IO口模式,再配置串口 */
    GPIOA_SetBits(GPIO_Pin_9); //先將PA9置位為高
    GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉輸入
    GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽輸出,注意要先讓IO口輸出高電平

    x = 10 * GetSysClock() / 8 / btl;
    x = (x + 5) / 10; //整型運算中四舍五入
    R16_UART1_DL = (uint16_t)x; //給波特率寄存器賦值

    R8_UART1_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN;
    //FIFO控制寄存器配置,10h左移6位:觸發點4字節;收發FIFO數據都清空;FIFO使能
    R8_UART1_LCR = RB_LCR_WORD_SZ; //線路控制寄存器配置,選擇串口數據長度
    R8_UART1_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT; //中斷使能控制器配置,TX引腳輸出使能;接收中斷使能;接收線路狀態中斷使能
    R8_UART1_MCR |= RB_MCR_INT_OE; //調制解調器控制寄存器配置,允許串口的中斷請求輸出
    R8_UART1_DIV = 1; //用於波特率配置,參考手冊中的公式

    PFIC_EnableIRQ(UART1_IRQn); //中斷注冊

    u1_rx_str.trig_num = 4; //根據R8_UART1_FCR配置設置觸發字節數

    UART1_SendByte('O');
    UART1_SendByte('K');
}

void u3_init(uint32_t btl) //波特率數字較大時需要用32位的整型存放,比如115200
{
    uint32_t x;

    GPIOA_SetBits(GPIO_Pin_5);
    GPIOA_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_PU);
    GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeOut_PP_5mA);

    x = 10 * GetSysClock() / 8 / btl;
    x = (x + 5) / 10;
    R16_UART3_DL = (uint16_t)x;

    R8_UART3_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN;
    R8_UART3_LCR = RB_LCR_WORD_SZ;
    R8_UART3_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT;
    R8_UART3_MCR |= RB_MCR_INT_OE;
    R8_UART3_DIV = 1;

    PFIC_EnableIRQ(UART3_IRQn);

    u3_rx_str.trig_num = 4; //根據R8_UART1_FCR配置設置觸發字節數

    UART3_SendByte('O');
    UART3_SendByte('K');
}

/*********************************************************************
 * @fn      main
 *
 * @brief   主函數
 *
 * @return  none
 */
int main()
{
    SetSysClock(CLK_SOURCE_PLL_60MHz);

    u1_init(115200);
    u3_init(9600);

    while(1)
    {
#if HOW_2_SEND == INQUIER_SEND
        while(u1_rx_str.rx_done)
        {
            SYS_DisableAllIrq(&irq_status);
            for(uint8_t i=0; i<u1_rx_str.rx_index; i++)
            {
                uint8_t j = R8_UART3_TFC; //查詢 發FIFO計數寄存器的數值,還有數據時不發送
                while(j>4)    //_TFC寄存器中的值最多為8,不讓FIFO溢出即可
                    j = R8_UART3_TFC;
                UART3_SendByte(u1_rx_str.rx_buff[i]); //串口3收到的數據從串口1發出
            }
            u1_rx_str.rx_index = 0;     //for循環中發完數據
            u1_rx_str.rx_done = 0;      //清超時發送標志
            SYS_RecoverIrq(irq_status);
        }

        while(u3_rx_str.rx_done)
        {
            SYS_DisableAllIrq(&irq_status);
            for(uint8_t i=0; i<u3_rx_str.rx_index; i++)
            {
                uint8_t j = R8_UART1_TFC; //查詢 發FIFO計數寄存器的數值,還有數據時不發送
                while(j>4)    //_TFC寄存器中的值最多為8,不讓FIFO溢出即可
                    j = R8_UART1_TFC;
                UART1_SendByte(u3_rx_str.rx_buff[i]); //串口3收到的數據從串口1發出
            }
            u3_rx_str.rx_index = 0;     //for循環中發完數據
            u3_rx_str.rx_done = 0;      //清超時發送標志
            SYS_RecoverIrq(irq_status);
        }
#endif
        ;
    }
}

/*********************************************************************
 * @fn      UART1_IRQHandler
 *
 * @brief   UART1中斷函數
 *
 * @return  none
 */
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void UART1_IRQHandler(void)
{
    volatile uint8_t i;

    switch(UART1_GetITFlag())
    {
        case UART_II_LINE_STAT: // 線路狀態錯誤
        {
            UART1_GetLinSTA();
            break;
        }

        case UART_II_RECV_RDY: // 數據達到設置觸發點
            for(i = 0; i != u1_rx_str.trig_num -1; i++) //-1保證每次接收都能進超時
            {
#if HOW_2_SEND == INQUIER_SEND
                u1_rx_str.rx_buff[u1_rx_str.rx_index] = UART1_RecvByte();
                u1_rx_str.rx_index++;

                if(u1_rx_str.rx_index > u1_rx_str.max_buff_size)
                {
                    UART1_SendByte('E');    //緩存數組越界提示,可以改良使用環形緩存,或加大緩存數組大小
                    return;
                }
#endif
#if HOW_2_SEND == INTERRUPT_SEND
                u1_rx_str.rx_buff[i] = UART1_RecvByte();
                UART3_SendByte(u1_rx_str.rx_buff[i]); //從串口3轉發出去
#endif
            }
            break;

        case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成
#if HOW_2_SEND == INQUIER_SEND
            i = UART1_RecvString(u1_rx_str.rx_buff + u1_rx_str.rx_index);

            if((u1_rx_str.rx_index +i) > u1_rx_str.max_buff_size)   //數組越界訪問,可能走不到這里就代碼錯誤
            {
                UART1_SendByte('E');    //緩存數組越界提示,可以改良使用環形緩存,或加大緩存數組大小
                u1_rx_str.rx_done = 1;
                return;
            }

            u1_rx_str.rx_index +=i ;
            u1_rx_str.rx_done = 1;

#endif
#if HOW_2_SEND == INTERRUPT_SEND
            i = UART1_RecvString(u1_rx_str.rx_buff);
            UART3_SendString(u1_rx_str.rx_buff, i);
#endif
            break;

        case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送
            break;

        case UART_II_MODEM_CHG: // 只支持串口0
            break;

        default:
            break;
    }
}

__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void UART3_IRQHandler(void)
{
    volatile uint8_t i;

    switch(UART3_GetITFlag())
    {
        case UART_II_LINE_STAT: // 線路狀態錯誤
        {
            UART3_GetLinSTA();
            break;
        }

        case UART_II_RECV_RDY: // 數據達到設置觸發點
            for(i = 0; i != u3_rx_str.trig_num -1; i++) //-1保證每次接收都能進超時
            {
#if HOW_2_SEND == INQUIER_SEND
                u3_rx_str.rx_buff[u3_rx_str.rx_index] = UART3_RecvByte();
                u3_rx_str.rx_index++;

                if(u3_rx_str.rx_index > u3_rx_str.max_buff_size)
                {
                    UART3_SendByte('E');    //緩存數組越界提示,可以改良使用環形緩存,或加大緩存數組大小
                    return;
                }
#endif
#if HOW_2_SEND == INTERRUPT_SEND
                u3_rx_str.rx_buff[i] = UART3_RecvByte();
                UART1_SendByte(u3_rx_str.rx_buff[i]); //從串口1轉發出去
#endif
            }
            break;

        case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成
#if HOW_2_SEND == INQUIER_SEND
            i = UART3_RecvString(u3_rx_str.rx_buff + u3_rx_str.rx_index);

            if((u3_rx_str.rx_index +i) > u3_rx_str.max_buff_size)   //數組越界訪問,可能走不到這里就代碼錯誤
            {
                UART3_SendByte('E');    //緩存數組越界提示,可以改良使用環形緩存,或加大緩存數組大小
                u3_rx_str.rx_done = 1;
                return;
            }

            u3_rx_str.rx_index +=i ;
            u3_rx_str.rx_done = 1;
#endif
#if HOW_2_SEND == INTERRUPT_SEND
            i = UART3_RecvString(u3_rx_str.rx_buff);
            UART1_SendString(u3_rx_str.rx_buff, i);
#endif
            break;

        case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送
            break;

        case UART_II_MODEM_CHG: // 只支持串口0
            break;

        default:
            break;
    }
}

下面舊的例子代碼,不知為何直接復制到MounRiver編譯器中會多出來很多空格,用上方的新代碼不會多出空格。

----------更新線  新↑   舊↓  更新線--------------

參考了沁恆官網22年1月更新的CH583EVT包中的UART1例程

功能:上位機通過串口1發送指令規定串口2和3的波特率,實現串口2和3之間的數據透傳

擔心串口3的接收中斷會被串口2的發送中斷打斷而導致錯誤,故不采用在串口3的接收中斷中,立即於串口2轉發的方式(即串口3的中斷服務函數中注釋掉的兩句),而采用緩存串口3接收的數據,在主函數中轉發的方式。

實測串口2和3全部采用中斷收發不緩存數據,不會影響串口透傳,前提是兩個串口的波特率一致。如果出現某個串口波特率高過另一串口的情況,比如說一個115200,一個9600的波特率,那么在高波特率的串口接收數據,由低波特率串口轉發的過程中,會造成占用串口中斷時間過長,丟失高波特率串口包的問題。全部用中斷收發,可以減小串口3的緩存,但是在中斷使用較多的情況下不保證不會出問題。代碼相似度很高,就不貼了。

#include "CH58x_common.h"

uint8_t RxBuff1[50];
uint8_t u1_i = 0; //用於判斷字符長度
uint8_t RxBuff2[50];
uint8_t RxBuff3[100]; //串口3的接收緩存,串口2向串口3透傳的數據不能溢出該數組
uint8_t trigB = 4; //FIFO的觸發字節數,可以設置00b,01b,10b,11b。本程序中用了10b,這里直接賦了4
uint8_t RxBuff1_get = 0; //判斷串口1是否接收完了一串字符,在串口1中斷中的FIFO超時判斷中置1
uint8_t u3_get_flag = 0; //判斷串口3是否接收完一串字符串,在串口3中斷中的FIFO超時判斷中置1
uint8_t baud_get = 0; //判斷是否接收到了串口1收到的波特率信息,格式 baud:XXXXX (9600/57600/115200等)
uint8_t u1_get[13] = {}; //接收存放串口1收到的波特率信息,方便仿真觀察
uint8_t u3_i = 0; //記錄串口3接收到的字節數,以便后續轉發
uint32_t baud_num = 0; //存放波特率,用於初始化u2u3的函數

void u1_init()
{
  uint32_t x;

  /* 配置串口1:先配置IO口模式,再配置串口 */
  GPIOA_SetBits(GPIO_Pin_9); //先將PA9置位為高
  GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉輸入
  GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽輸出,注意要先讓IO口輸出高電平

  x = 10 * GetSysClock() / 8 / 115200; //串口1定為115200波特率
  x = (x + 5) / 10; //整型運算中四舍五入
  R16_UART1_DL = (uint16_t)x; //給波特率寄存器賦值

  R8_UART1_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN;
      //FIFO控制寄存器配置,10左移6位:觸發點4字節;收發FIFO數據都清空;FIFO使能
  R8_UART1_LCR = RB_LCR_WORD_SZ; //線路控制寄存器配置,選擇串口數據長度
  R8_UART1_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT; //中斷使能控制器配置,TX引腳輸出使能;接收中斷使能;接收線路狀態中斷使能
  R8_UART1_MCR |= RB_MCR_INT_OE; //調制解調器控制寄存器配置,允許串口的中斷請求輸出
  R8_UART1_DIV = 1; //用於波特率配置,參考手冊中的公式

  PFIC_EnableIRQ(UART1_IRQn); //中斷注冊

  UART1_SendByte('O');
  UART1_SendByte('K');
}

void u2u3_init(uint32_t btl) //波特率數字較大時需要用32位的整型存放,比如115200
{
  uint32_t x;

  /* 配置串口2、3的IO口模式 */
  GPIOA_SetBits(GPIO_Pin_7);
  GPIOA_ModeCfg(GPIO_Pin_6, GPIO_ModeIN_PU);
  GPIOA_ModeCfg(GPIO_Pin_7, GPIO_ModeOut_PP_5mA);
  GPIOA_SetBits(GPIO_Pin_5);
  GPIOA_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_PU);
  GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeOut_PP_5mA);

  x = 10 * GetSysClock() / 8 / btl;
  x = (x + 5) / 10;
  R16_UART2_DL = (uint16_t)x;
  R16_UART3_DL = (uint16_t)x; //寄存器為16位的

  R8_UART2_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN;
  R8_UART2_LCR = RB_LCR_WORD_SZ;
  R8_UART2_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT;
  R8_UART2_MCR |= RB_MCR_INT_OE;
  R8_UART2_DIV = 1;

  R8_UART3_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN;
  R8_UART3_LCR = RB_LCR_WORD_SZ;
  R8_UART3_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT;
  R8_UART3_MCR |= RB_MCR_INT_OE;
  R8_UART3_DIV = 1;

  PFIC_EnableIRQ(UART2_IRQn);
  PFIC_EnableIRQ(UART3_IRQn);

  baud_get = 1; //波特率已由串口1獲得的標識

  UART2_SendByte('O');
  UART2_SendByte('K');
  UART3_SendByte('O');
  UART3_SendByte('K');
}

void main()
{
  uint8_t i,j;

  SetSysClock(CLK_SOURCE_PLL_60MHz);
  u1_init(); //串口1默認115200波特率

  while(!RxBuff1_get)
    __nop(); //等待串口1接收指令。串口1接收到一串數據之后,開始處理數據
  while(RxBuff1_get)
  {
    for(i=0; i<u1_i; i++)
    {
      j = R8_UART1_TFC; //查詢 發FIFO計數寄存器的數值,數據過多時不發送數據給FIFO
      while(j > 4)
      {
        j = R8_UART1_TFC; //再讀取一遍,等待FIFO有空間存放即將發送的數據。發送數據過快將導致與波特率不匹配而亂碼
      }
    UART1_SendByte(RxBuff1[i]); //反饋上位機
    u1_get[i] = RxBuff1[i]; //將RxBuff1中的指令搬到u1_get中,方便仿真觀察
    }
    RxBuff1_get = 0;
  }

  baud_num = u1_get[5]-'0'; //將"baud:"之后的字符轉換為數字
  for (i=6; i<u1_i; i++)
  {
  baud_num = baud_num *10;
  baud_num += u1_get[i]-'0';
  }

  u2u3_init(baud_num); //初始化u2u3

  while(baud_get) //獲得波特率,並且u2u3初始化完成之后
  {
    __nop();
    while(u3_get_flag)
    {
      for(i=0; i<u3_i; i++)
      {
        j = R8_UART2_TFC; //查詢 發FIFO計數寄存器的數值,還有數據時不發送
        while(j) //作用和上面判斷FIFO數據是否過多類似,_TFC寄存器中的值最多為8,不讓FIFO溢出即可
        j = R8_UART2_TFC;
        UART2_SendByte(RxBuff3[i]); //串口3收到的數據從串口2發出
      }
      if(i == u3_i)
      {
        u3_i = 0; //RxBuff3的數組指針歸零
        u3_get_flag = 0; //清空串口3接收到數據的標志
      }
    }
  }
}

__INTERRUPT //硬件壓棧
__HIGH_CODE //放在ram里跑,更快
void UART1_IRQHandler(void)
{
  volatile uint8_t i;

  switch(R8_UART1_IIR & RB_IIR_INT_MASK) //獲取中斷標志
  {
    case UART_II_LINE_STAT: // 線路狀態錯誤
    {
      UART1_GetLinSTA(); //讀取線路狀態寄存器,讀取該寄存器將自動清除中斷
      break;
    }

    case UART_II_RECV_RDY: // 數據達到設置觸發點
      for(i = 0; i != trigB-1; i++)
      {  
        RxBuff1[u1_i] = UART1_RecvByte();
      u1_i++;
      }
      break;

    case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成。注意FIFO里要留有至少一個字符用來比較,判斷超時
      i = UART1_RecvString(RxBuff1+u1_i);
      u1_i += i;
      RxBuff1_get = 1;
      break;

    case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送
      break;

    default:
      break;
  }
}


__INTERRUPT //硬件壓棧
__HIGH_CODE //下方程序放在ram里跑,更快
void UART2_IRQHandler(void)
{
  volatile uint8_t i;

  switch(R8_UART2_IIR & RB_IIR_INT_MASK) //獲取中斷標志
  {
    case UART_II_LINE_STAT: // 線路狀態錯誤
    {
      UART2_GetLinSTA(); //讀取線路狀態寄存器,讀取該寄存器將自動清除中斷
      break;
    }

    case UART_II_RECV_RDY: // 數據達到設置觸發點
      for(i = 0; i != trigB; i++)
      {
        RxBuff2[i] = UART2_RecvByte();
        UART3_SendByte(RxBuff2[i]); //從串口3轉發出去
      }
      break;

    case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成
      i = UART2_RecvString(RxBuff2);
      UART3_SendString(RxBuff2, i);
      break;

    case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送
      break;

    default:
      break;
  }
}


__INTERRUPT //硬件壓棧
__HIGH_CODE //放在ram里跑,更快
void UART3_IRQHandler(void)
{
  volatile uint8_t i;
  switch(R8_UART3_IIR & RB_IIR_INT_MASK) //獲取中斷標志
  {
    case UART_II_LINE_STAT: //線路狀態錯誤
    {
    UART3_GetLinSTA(); //讀取線路狀態寄存器,讀取該寄存器將自動清除中斷
    break;
    }

    case UART_II_RECV_RDY: // 數據達到設置觸發點
    for(i = 0; i != trigB-1; i++) //FIFO中留一個字節的數據,用於觸發_TOUT標識
    {
      RxBuff3[u3_i++] = UART3_RecvByte();
      //UART2_SendByte(RxBuff3[i]); //先不從串口2轉發,防止串口2的發送中斷打斷串口3的中斷服務函數
    }
    break;

    case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成
      i = UART3_RecvString(RxBuff3+u3_i);
      u3_i += i;
      //UART2_SendString(RxBuff3, i); //先不從串口2轉發,防止串口2的發送中斷打斷串口3的中斷服務函數
      u3_get_flag = 1;
      break;

    case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送
      break;

    default:
      break;
  }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM