打印
[单片机芯片]

【CH32V317W-R0开发板】串口服务器技术储备篇

[复制链接]
878|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
  CH32V30x和CH32V31x系列是基于青稞V4F微处理器设计的32位RISC-V内核MCU,工作频率144MHz,内置高速存储器,系统结构中多条总线同步工作,提供了丰富的外设功能和增强型 I/O 端口。内置 2 个 12 位 ADC 模块、2 个 12 位 DAC 模块、多组定时器、多通道触摸按键电容检测(TKey)等功能,还包含了标准和专用通讯接口:I2C、I2S、SPI、USART、SDIO、CAN 控制器、USB2.0 全速主机/设备控制器、USB2.0 高速主机/设备控制器(内置 480Mbps 收发器)、数字图像接口、千兆以太网控制器等。产品工作额定电压为 3.3V,工作温度范围为-40℃~85℃工业级。支持多种省电工作模式来满足产低功耗应用要求。系列产品中各型号在资源分配、外设数量、外设功能等方面有所差异,按需选择。

  CH32V303/305/307 产品资源分配:307有8个串口,是8个,没错!!如果用STM32,上H7才可以。

   


  用CH32V3**做串口服务器是理想选择,串口服务器是一种网络设备,想在将8个独立的TTL,RS-232、RS-485或RS-422串口设备连接到以太网,实现串口与网络之间的双向透明数据传输。其核心功能包括:
1,多串口支持:提供8个独立的串口接口,可同时连接多个串口设备。
2,协议转换:将串行数据封装为TCP/IP数据包进行传输,接收端还原为串行信号。
3,双向透明传输:确保数据在串口与网络之间无协议变化地传输,保持原始数据的完整性。
4,远程管理:支持通过Web界面、Telnet或专用配置工具进行远程参数设置和状态监控。
5,多协议支持:内部集成ARP、IP、TCP、UDP、HTTP、ICMP等协议,支持动态IP(DHCP)和静态IP配置。
  灵活的工作模式:TCP Server模式:作为服务器端,等待客户端连接。
  TCP Client模式:作为客户端,主动向服务器发起连接。
  UDP模式:支持快速、低延迟的数据广播和接收。
虚拟串口模式:在计算机上创建虚拟串口,远程设备被识别为本地串口。
6,Modbus网关功能:支持Modbus TCP转Modbus RTU,实现上位机使用Modbus TCP协议对RS-485的Modbus RTU设备的数据采集。
7,多主机功能:支持多个计算机主站同时访问同一个串口设备,适用于一问一答的查询方式。

CH32V307/317串口特点:
  • 多串口配置:CH32V307扩展了8组串口,包括3个通用同步异步收发器(USART1/2/3)和5个通用异步收发器(UART4/5/6/7/8),可同时连接多个串口设备,满足复杂系统需求。
  • 高速通信能力:支持最高9Mbps的波特率传输,满足高速数据传输需求,适用于实时性要求高的应用场景。
  • 灵活通信模式:
    • 全双工/半双工:支持同步或异步通信,适应不同协议要求。
    • 同步模式:USART模块可输出时钟信号,通过CK引脚同步数据传输,适用于需要严格时序控制的场景。
    • 异步模式:采用起始位、数据位、停止位和校验位的组合传输数据,无需同步时钟信号,实现简单灵活。
  • 协议兼容性:
    • 支持LIN(局部互连网)、IrDA(红外数据协会)编码器、智能卡协议。
    • 兼容调制解调器(CTS/RTS硬件流控)操作,满足多样化通信需求。
  • DMA支持:支持DMA操作连续通讯,减少CPU干预,提升数据传输效率,适用于大数据量场景。
  • 中断功能:提供多种中断源,包括接收缓冲区非空中断(RXNE)和空闲线中断(IDLE),便于实时处理数据。空闲中断触发流程中,每接收到一个字节,RXNE标志置1,可通过检测IDLE标志实现帧接收。
  • 低功耗设计:在保持高性能的同时,支持多种低功耗模式(睡眠、停止、待机),适用于电池供电或对功耗敏感的应用场景。
  • 硬件加速:配备硬件堆栈区和快速中断入口,在标准RISC-V基础上提高中断响应速度,确保串口通信的实时性和可靠性。









先编写一个串口打印测试:
main.c
#include "debug.h"


// 简单延时函数(单位:微秒)
void Delay_us(uint32_t us) {
    for (uint32_t i = 0; i < us; i++) {
        for (volatile uint32_t j = 0; j < 7; j++); // 调整循环次数以适配你的时钟频率
    }
}

// 简单延时函数(单位:毫秒)
void Delay_ms(uint32_t ms) {
    for (uint32_t i = 0; i < ms; i++) {
        for (volatile uint32_t j = 0; j < 7200; j++); // 调整循环次数以适配你的时钟频率
    }
}

int main(void)
{
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
        SystemCoreClockUpdate();
        Delay_Init();
        USART_Printf_Init(115200);       
        printf("SystemClk:%d\r\n",SystemCoreClock);
        printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
        printf("This is printf example\r\n");

        while(1)
    {
                printf("SystemClk:%d\r\n",SystemCoreClock);
                printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
                printf("This is printf example\r\n");
                Delay_ms(1000);


        }
}

#include "debug.h"

static uint8_t  p_us = 0;
static uint16_t p_ms = 0;

#define DEBUG_DATA0_ADDRESS  ((volatile uint32_t*)0xE0000380)
#define DEBUG_DATA1_ADDRESS  ((volatile uint32_t*)0xE0000384)

/*********************************************************************
* @fn      Delay_Init
*
* [url=home.php?mod=space&uid=247401]@brief[/url]   Initializes Delay Funcation.
*
* [url=home.php?mod=space&uid=266161]@return[/url]  none
*/
void Delay_Init(void)
{
    p_us = SystemCoreClock / 8000000;
    p_ms = (uint16_t)p_us * 1000;
}

/*********************************************************************
* @fn      Delay_Us
*
* @brief   Microsecond Delay Time.
*
* @param   n - Microsecond number.
*
* @return  None
*/
void Delay_Us(uint32_t n)
{
    uint32_t i;

    SysTick->SR &= ~(1 << 0);
    i = (uint32_t)n * p_us;

    SysTick->CMP = i;
    SysTick->CTLR |= (1 << 4);
    SysTick->CTLR |= (1 << 5) | (1 << 0);

    while((SysTick->SR & (1 << 0)) != (1 << 0))
        ;
    SysTick->CTLR &= ~(1 << 0);
}

/*********************************************************************
* @fn      Delay_Ms
*
* @brief   Millisecond Delay Time.
*
* @param   n - Millisecond number.
*
* @return  None
*/
void Delay_Ms(uint32_t n)
{
    uint32_t i;

    SysTick->SR &= ~(1 << 0);
    i = (uint32_t)n * p_ms;

    SysTick->CMP = i;
    SysTick->CTLR |= (1 << 4);
    SysTick->CTLR |= (1 << 5) | (1 << 0);

    while((SysTick->SR & (1 << 0)) != (1 << 0))
        ;
    SysTick->CTLR &= ~(1 << 0);
}

/*********************************************************************
* @fn      USART_Printf_Init
*
* @brief   Initializes the USARTx peripheral.
*
* @param   baudrate - USART communication baud rate.
*
* @return  None
*/
void USART_Printf_Init(uint32_t baudrate)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

#if(DEBUG == DEBUG_UART1)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

#elif(DEBUG == DEBUG_UART2)
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

#elif(DEBUG == DEBUG_UART3)
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

#endif

    USART_InitStructure.USART_BaudRate = baudrate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx;

#if(DEBUG == DEBUG_UART1)
    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);

#elif(DEBUG == DEBUG_UART2)
    USART_Init(USART2, &USART_InitStructure);
    USART_Cmd(USART2, ENABLE);

#elif(DEBUG == DEBUG_UART3)
    USART_Init(USART3, &USART_InitStructure);
    USART_Cmd(USART3, ENABLE);

#endif
}

/*********************************************************************
* @fn      SDI_Printf_Enable
*
* @brief   Initializes the SDI printf Function.
*
* @param   None
*
* @return  None
*/
void SDI_Printf_Enable(void)
{
    *(DEBUG_DATA0_ADDRESS) = 0;
    Delay_Init();
    Delay_Ms(1);
}

/*********************************************************************
* @fn      _write
*
* @brief   Support Printf Function
*
* @param   *buf - UART send Data.
*          size - Data length
*
* @return  size: Data length
*/
__attribute__((used)) int _write(int fd, char *buf, int size)
{
    int i = 0;

#if (SDI_PRINT == SDI_PR_OPEN)
    int writeSize = size;

    do
    {

        /**
         * data0  data1 8 bytes
         * data0 The lowest byte storage length, the maximum is 7
         *
         */

        while( (*(DEBUG_DATA0_ADDRESS) != 0u))
        {

        }

        if(writeSize>7)
        {
            *(DEBUG_DATA1_ADDRESS) = (*(buf+i+3)) | (*(buf+i+4)<<8) | (*(buf+i+5)<<16) | (*(buf+i+6)<<24);
            *(DEBUG_DATA0_ADDRESS) = (7u) | (*(buf+i)<<8) | (*(buf+i+1)<<16) | (*(buf+i+2)<<24);

            i += 7;
            writeSize -= 7;
        }
        else
        {
            *(DEBUG_DATA1_ADDRESS) = (*(buf+i+3)) | (*(buf+i+4)<<8) | (*(buf+i+5)<<16) | (*(buf+i+6)<<24);
            *(DEBUG_DATA0_ADDRESS) = (writeSize) | (*(buf+i)<<8) | (*(buf+i+1)<<16) | (*(buf+i+2)<<24);

            writeSize = 0;
        }

    } while (writeSize);


#else
    for(i = 0; i < size; i++)
    {
#if(DEBUG == DEBUG_UART1)
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
        USART_SendData(USART1, *buf++);
#elif(DEBUG == DEBUG_UART2)
        while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
        USART_SendData(USART2, *buf++);
#elif(DEBUG == DEBUG_UART3)
        while(USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
        USART_SendData(USART3, *buf++);
#endif
    }
#endif
    return size;
}

/*********************************************************************
* @fn      _sbrk
*
* @brief   Change the spatial position of data segment.
*
* @return  size: Data length
*/
__attribute__((used)) void *_sbrk(ptrdiff_t incr)
{
    extern char _end[];
    extern char _heap_end[];
    static char *curbrk = _end;

    if ((curbrk + incr < _end) || (curbrk + incr > _heap_end))
    return NULL - 1;

    curbrk += incr;
    return curbrk - incr;
}



测试结果:nice!!
SystemClk:96000000
ChipID:3173b568
This is printf example
SystemClk:96000000
ChipID:3173b568
This is printf example
SystemClk:96000000
ChipID:3173b568
This is printf example
SystemClk:96000000
ChipID:3173b568
This is printf example
SystemClk:96000000
ChipID:3173b568
This is printf example
SystemClk:96000000
ChipID:3173b568
This is printf example
SystemClk:96000000
ChipID:3173b568
This is printf example





使用特权

评论回复
沙发
彩虹捕手| | 2025-7-28 09:39 | 只看该作者
CH32V30x系列的MCU确实性能强大,144MHz的主频和丰富的外设接口,非常适合做串口服务器。

使用特权

评论回复
板凳
二月寒风| | 2025-7-29 11:33 | 只看该作者
8路RS485的TXEN 怎么做比较效率高?

使用特权

评论回复
评论
abner_ma 2025-7-29 18:08 回复TA
将8路RS485的TXEN引脚通过逻辑门(如与门、或门)或GPIO扩展芯片(如74HC595)连接至单一控制信号,实现同步切换。减少MCU引脚占用:仅需1个GPIO即可控制8路收发状态,节省资源。 时序一致性:避免多路信号独立控制导致的时延差异,确保数据同步性。 
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:项目经理
简介:资深嵌入式开发工程师

95

主题

181

帖子

3

粉丝