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;
  }
}

 

posted @ 2022-03-01 15:54  JayWell  阅读(1886)  评论(0编辑  收藏  举报