[单片机芯片] 【CH32V317W-R0开发板】串口服务器技术储备篇

[复制链接]
 楼主| abner_ma 发表于 2025-7-23 17:56 | 显示全部楼层 |阅读模式
  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才可以。

    D2.png


  用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基础上提高中断响应速度,确保串口通信的实时性和可靠性。





D3.png



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


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

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

  14. int main(void)
  15. {
  16.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  17.         SystemCoreClockUpdate();
  18.         Delay_Init();
  19.         USART_Printf_Init(115200);       
  20.         printf("SystemClk:%d\r\n",SystemCoreClock);
  21.         printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
  22.         printf("This is printf example\r\n");

  23.         while(1)
  24.     {
  25.                 printf("SystemClk:%d\r\n",SystemCoreClock);
  26.                 printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
  27.                 printf("This is printf example\r\n");
  28.                 Delay_ms(1000);


  29.         }
  30. }

  1. #include "debug.h"

  2. static uint8_t  p_us = 0;
  3. static uint16_t p_ms = 0;

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

  6. /*********************************************************************
  7. * @fn      Delay_Init
  8. *
  9. * [url=home.php?mod=space&uid=247401]@brief[/url]   Initializes Delay Funcation.
  10. *
  11. * [url=home.php?mod=space&uid=266161]@return[/url]  none
  12. */
  13. void Delay_Init(void)
  14. {
  15.     p_us = SystemCoreClock / 8000000;
  16.     p_ms = (uint16_t)p_us * 1000;
  17. }

  18. /*********************************************************************
  19. * @fn      Delay_Us
  20. *
  21. * @brief   Microsecond Delay Time.
  22. *
  23. * @param   n - Microsecond number.
  24. *
  25. * @return  None
  26. */
  27. void Delay_Us(uint32_t n)
  28. {
  29.     uint32_t i;

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

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

  35.     while((SysTick->SR & (1 << 0)) != (1 << 0))
  36.         ;
  37.     SysTick->CTLR &= ~(1 << 0);
  38. }

  39. /*********************************************************************
  40. * @fn      Delay_Ms
  41. *
  42. * @brief   Millisecond Delay Time.
  43. *
  44. * @param   n - Millisecond number.
  45. *
  46. * @return  None
  47. */
  48. void Delay_Ms(uint32_t n)
  49. {
  50.     uint32_t i;

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

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

  56.     while((SysTick->SR & (1 << 0)) != (1 << 0))
  57.         ;
  58.     SysTick->CTLR &= ~(1 << 0);
  59. }

  60. /*********************************************************************
  61. * @fn      USART_Printf_Init
  62. *
  63. * @brief   Initializes the USARTx peripheral.
  64. *
  65. * @param   baudrate - USART communication baud rate.
  66. *
  67. * @return  None
  68. */
  69. void USART_Printf_Init(uint32_t baudrate)
  70. {
  71.     GPIO_InitTypeDef  GPIO_InitStructure;
  72.     USART_InitTypeDef USART_InitStructure;

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

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

  79. #elif(DEBUG == DEBUG_UART2)
  80.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  81.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

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

  86. #elif(DEBUG == DEBUG_UART3)
  87.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
  88.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

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

  93. #endif

  94.     USART_InitStructure.USART_BaudRate = baudrate;
  95.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  96.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
  97.     USART_InitStructure.USART_Parity = USART_Parity_No;
  98.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  99.     USART_InitStructure.USART_Mode = USART_Mode_Tx;

  100. #if(DEBUG == DEBUG_UART1)
  101.     USART_Init(USART1, &USART_InitStructure);
  102.     USART_Cmd(USART1, ENABLE);

  103. #elif(DEBUG == DEBUG_UART2)
  104.     USART_Init(USART2, &USART_InitStructure);
  105.     USART_Cmd(USART2, ENABLE);

  106. #elif(DEBUG == DEBUG_UART3)
  107.     USART_Init(USART3, &USART_InitStructure);
  108.     USART_Cmd(USART3, ENABLE);

  109. #endif
  110. }

  111. /*********************************************************************
  112. * @fn      SDI_Printf_Enable
  113. *
  114. * @brief   Initializes the SDI printf Function.
  115. *
  116. * @param   None
  117. *
  118. * @return  None
  119. */
  120. void SDI_Printf_Enable(void)
  121. {
  122.     *(DEBUG_DATA0_ADDRESS) = 0;
  123.     Delay_Init();
  124.     Delay_Ms(1);
  125. }

  126. /*********************************************************************
  127. * @fn      _write
  128. *
  129. * @brief   Support Printf Function
  130. *
  131. * @param   *buf - UART send Data.
  132. *          size - Data length
  133. *
  134. * @return  size: Data length
  135. */
  136. __attribute__((used)) int _write(int fd, char *buf, int size)
  137. {
  138.     int i = 0;

  139. #if (SDI_PRINT == SDI_PR_OPEN)
  140.     int writeSize = size;

  141.     do
  142.     {

  143.         /**
  144.          * data0  data1 8 bytes
  145.          * data0 The lowest byte storage length, the maximum is 7
  146.          *
  147.          */

  148.         while( (*(DEBUG_DATA0_ADDRESS) != 0u))
  149.         {

  150.         }

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

  155.             i += 7;
  156.             writeSize -= 7;
  157.         }
  158.         else
  159.         {
  160.             *(DEBUG_DATA1_ADDRESS) = (*(buf+i+3)) | (*(buf+i+4)<<8) | (*(buf+i+5)<<16) | (*(buf+i+6)<<24);
  161.             *(DEBUG_DATA0_ADDRESS) = (writeSize) | (*(buf+i)<<8) | (*(buf+i+1)<<16) | (*(buf+i+2)<<24);

  162.             writeSize = 0;
  163.         }

  164.     } while (writeSize);


  165. #else
  166.     for(i = 0; i < size; i++)
  167.     {
  168. #if(DEBUG == DEBUG_UART1)
  169.         while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
  170.         USART_SendData(USART1, *buf++);
  171. #elif(DEBUG == DEBUG_UART2)
  172.         while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
  173.         USART_SendData(USART2, *buf++);
  174. #elif(DEBUG == DEBUG_UART3)
  175.         while(USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
  176.         USART_SendData(USART3, *buf++);
  177. #endif
  178.     }
  179. #endif
  180.     return size;
  181. }

  182. /*********************************************************************
  183. * @fn      _sbrk
  184. *
  185. * @brief   Change the spatial position of data segment.
  186. *
  187. * @return  size: Data length
  188. */
  189. __attribute__((used)) void *_sbrk(ptrdiff_t incr)
  190. {
  191.     extern char _end[];
  192.     extern char _heap_end[];
  193.     static char *curbrk = _end;

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

  196.     curbrk += incr;
  197.     return curbrk - incr;
  198. }



测试结果:nice!!
  1. SystemClk:96000000
  2. ChipID:3173b568
  3. This is printf example
  4. SystemClk:96000000
  5. ChipID:3173b568
  6. This is printf example
  7. SystemClk:96000000
  8. ChipID:3173b568
  9. This is printf example
  10. SystemClk:96000000
  11. ChipID:3173b568
  12. This is printf example
  13. SystemClk:96000000
  14. ChipID:3173b568
  15. This is printf example
  16. SystemClk:96000000
  17. ChipID:3173b568
  18. This is printf example
  19. SystemClk:96000000
  20. ChipID:3173b568
  21. This is printf example


D1.png


彩虹捕手 发表于 2025-7-28 09:39 | 显示全部楼层
CH32V30x系列的MCU确实性能强大,144MHz的主频和丰富的外设接口,非常适合做串口服务器。
二月寒风 发表于 2025-7-29 11:33 | 显示全部楼层
8路RS485的TXEN 怎么做比较效率高?

评论

将8路RS485的TXEN引脚通过逻辑门(如与门、或门)或GPIO扩展芯片(如74HC595)连接至单一控制信号,实现同步切换。减少MCU引脚占用:仅需1个GPIO即可控制8路收发状态,节省资源。 时序一致性:避免多路信号独立控制导致的时延差异,确保数据同步性。  发表于 2025-7-29 18:08
彩虹彼岸 发表于 2025-8-7 16:16 | 显示全部楼层
串口服务器是一种网络设备,是把以太网数据变成串口数据对吧
AuroraWaltz 发表于 2025-8-15 11:54 | 显示全部楼层
144MHz的主频和丰富的外设接口,非常适合做串口服务器。好像是这么回事哈
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

95

主题

181

帖子

3

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

95

主题

181

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部