# RISC-V MCU 基于 CH32V307 工业物联网平台系统demo **Repository Path**: slick-zhang/CH32V307demo ## Basic Information - **Project Name**: RISC-V MCU 基于 CH32V307 工业物联网平台系统demo - **Description**: 全国大学生嵌入式芯片与系统设计竞赛开源代码 - **Primary Language**: Unknown - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 5 - **Forks**: 2 - **Created**: 2022-08-16 - **Last Updated**: 2025-04-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # RISC-V MCU 基于 CH32V307 工业物联网平台系统 ## 第一部分 设计概述 ### 1.1 设计目的     在智能工业生产现场中,生产设备的控制依赖于传感器读数以及相关算法 等。而传感器起着至关重要的作用,传感器的驱动和数据传输依赖于微控制器, 系统基于沁恒 CH32V307 微控制器进行数据收集、服务器进行数据处理和,中 间基于服务器通过串口与微控制器进行数据传输处理并通过以太网与服务器交 互。服务器端,可实现数据库、数据可视化显示和设备交互;微控制器端通过 操作系统执行传感器驱动进程、以太网传输进程、工业现场数据显示等进程; 工业控制器端。 ### 1.2 应用领域     在现代智能工厂等环境下,传感器代替了人工读数操作,但是信号的传输 多基于以太网连接。本作品应用于智能工厂环境通过以太网传输数据将数据发 生至上位机,经过 Python Socket 简单数据接收处理。 ### 1.3 主要技术特点     微控制器使用了显示屏、温湿度传感器以及以太网模块。控制器驱动温湿 度模块测量温湿度;显示屏显示传感器名称温湿度名称以及数值;以太网采用 芯片自带 10M 处理模块,外接以太网接口,采用官方驱动库进行数据传输。数 据传输至服务器 python socket 进行以太网数据接收处理。 ### 1.4 关键性能指标   (1) MCU:     CH32V307VCT6,主频 144MHz,FLASH 和 RAM 四种配置     288KB FLASH ,32KB RAM     256KB FLASH ,64KB RAM     224KB FLASH ,96KB RAM     192KB FLASH ,128KB RAM     CH32V307V-R0 是 南京沁恒微电子(WCH) 推出的一款基于 RISC-V 内核的开发板,最高主频为 120Mhz,该开发板芯片为 CH32V307-R0。 ![主控片上资源](pict/1660612807666.png)   (2) AHT10:     温度范围为: 测量范围为 -40 ℃ ~+ 85 ℃ 精度±0.5℃;     湿度范围为:测量范围为 0% - 100% 精度为±3%RH;   (3) 以太网:ETH 支持 10M ### 1.5 主要创新点     (1) MCU 自带以太网处理模块     (2) 采用 TCP 连接,数据传输准确 ## 第二部分 系统组成及功能说明 ### 2.1 整体介绍 ![系统结构](pict/1660612912265.png) ### 2.2 各模块介绍 #### (一)开发板   a) MCU     MCU 作为核心处理器,驱动周围模块并与外界进行通信。   b) 温湿度模块     温湿度传感器测量物理环境的温湿度,由 MCU 驱动,通过 IIC 协议与 MCU 进行数据传输。 ![温湿度模块](pict/1660612963629.png) c) 显示屏     显示屏作为人机交互界面有 MCU 驱动显示。显示内容包括温湿度传感器信 号;温湿度以及数值等内容 d) 以太网模块     以太网模块为 MCU 外界以太网接口,无相关转接芯片。 #### (二)服务器     以 PC 作为服务器接口,使用 Python Socket 建立服务器。通过绑定 IP 以及 端口进行输出传输以及相关数据的处理等内容。 ``` import socket,os server = socket.socket() server.bind(("192.168.1.100",1000)) server.listen(5) #数据暂存器 TEMP = [b'H',b'2'] while True: conn,addr = server.accept() print("Connected to:",addr) while True: #接收两个字节数据 data = conn.recv(2) #判断接收的字节是否为空,为空则退出 if not data: print("Connected break") break #将两个字节切片 info = [data[i:i+1] for i in range(0, len(data), 1)] #print(len(info)) if len(info) == 2 : TEMP[0] = info[0] TEMP[1] = info[1] #字节判断转换输出 if (TEMP[0] == b'H') : print('H',ord(TEMP[1])) if (TEMP[0] == b'T') : print('T',ord(TEMP[1])) server.close() ``` ## 第三部分 完成情况及性能参数 #### (一)显示屏模块     显示屏温湿度显示 ![显示屏温湿度显示](pict/1660613052919.png) #### (二)服务器模块 ![Python Socket TCP连接](pict/1660613087708.png) ## 第四部分 主控关键代码
点击查看代码 ``` #include "debug.h" #include "WCHNET.h" #include "string.h" #include "lcd.h" #include "AHT_10.h" int32_t temperature, humidity; char temp[2]; __attribute__((__aligned__(4))) ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB]; /* MAC接收描述符 ,4字节对齐*/ __attribute__((__aligned__(4))) ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB]; /* MAC发送描述符,4字节对齐 */ __attribute__((__aligned__(4))) u8 MACRxBuf[ETH_RXBUFNB*ETH_MAX_PACKET_SIZE]; /* MAC接收缓冲区,4字节对齐 */ __attribute__((__aligned__(4))) u8 MACTxBuf[ETH_TXBUFNB*ETH_MAX_PACKET_SIZE]; /* MAC发送缓冲区,4字节对齐 */ __attribute__((__aligned__(4))) SOCK_INF SocketInf[WCHNET_MAX_SOCKET_NUM]; /* Socket信息表,4字节对齐 */ const u16 MemNum[8] = {WCHNET_NUM_IPRAW, WCHNET_NUM_UDP, WCHNET_NUM_TCP, WCHNET_NUM_TCP_LISTEN, WCHNET_NUM_TCP_SEG, WCHNET_NUM_IP_REASSDATA, WCHNET_NUM_PBUF, WCHNET_NUM_POOL_BUF }; const u16 MemSize[8] = {WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_IPRAW_PCB), WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_UDP_PCB), WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_TCP_PCB), WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_TCP_PCB_LISTEN), WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_TCP_SEG), WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_IP_REASSDATA), WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_PBUF) + WCHNET_MEM_ALIGN_SIZE(0), WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_PBUF) + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_POOL_BUF) }; __attribute__((__aligned__(4)))u8 Memp_Memory[WCHNET_MEMP_SIZE]; __attribute__((__aligned__(4)))u8 Mem_Heap_Memory[WCHNET_RAM_HEAP_SIZE]; __attribute__((__aligned__(4)))u8 Mem_ArpTable[WCHNET_RAM_ARP_TABLE_SIZE]; #define RECE_BUF_LEN WCHNET_TCP_MSS*2 /*socket接收缓冲区的长度,最小为TCP MSS*/ u8 MACAddr[6]; /*Mac地址*/ u8 IPAddr[4] = {192,168,1,10}; /*IP地址*/ u8 GWIPAddr[4] = {192,168,1,1}; /*网关*/ u8 IPMask[4] = {255,255,255,0}; /*子网掩码*/ u8 DESIP[4] = {192,168,1,100}; /*目的IP地址*/ u8 connectStatus=0; u8 SocketId; /*socket id号*/ u8 SocketRecvBuf[WCHNET_MAX_SOCKET_NUM][RECE_BUF_LEN]; /*socket缓冲区*/ u8 MyBuf[RECE_BUF_LEN]; u16 desport=1000; /*目的端口号*/ u16 srcport=1000; /*源端口号*/ /********************************************************************* * @fn Ethernet_LED_Configuration * * @brief set eth data and link led pin * * @return none */ void Ethernet_LED_Configuration(void) { GPIO_InitTypeDef GPIO; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9; GPIO.GPIO_Mode = GPIO_Mode_Out_PP; GPIO.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO); Ethernet_LED_LINKSET(1); Ethernet_LED_DATASET(1); } /********************************************************************* * @fn Ethernet_LED_LINKSET * * @brief set eth link led,setbit 0 or 1,the link led turn on or turn off * * @return none */ void Ethernet_LED_LINKSET(u8 setbit) { if(setbit){ GPIO_SetBits(GPIOB, GPIO_Pin_8); } else { GPIO_ResetBits(GPIOB, GPIO_Pin_8); } } /********************************************************************* * @fn Ethernet_LED_DATASET * * @brief set eth data led,setbit 0 or 1,the data led turn on or turn off * * @return none */ void Ethernet_LED_DATASET(u8 setbit) { if(setbit){ GPIO_SetBits(GPIOB, GPIO_Pin_9); } else { GPIO_ResetBits(GPIOB, GPIO_Pin_9); } } /********************************************************************* * @fn mStopIfError * * @brief check if error. * * @return none */ void mStopIfError(u8 iError) { if (iError == WCHNET_ERR_SUCCESS) return; /* 操作成功 */ printf("Error: %02X\r\n", (u16)iError); /* 显示错误 */ } /********************************************************************* * @fn WCHNET_LibInit * * @brief Initializes NET. * * @return command status */ u8 WCHNET_LibInit(const u8 *ip,const u8 *gwip,const u8 *mask,const u8 *macaddr) { u8 i; struct _WCH_CFG cfg; cfg.RxBufSize = RX_BUF_SIZE; cfg.TCPMss = WCHNET_TCP_MSS; cfg.HeapSize = WCH_MEM_HEAP_SIZE; cfg.ARPTableNum = WCHNET_NUM_ARP_TABLE; cfg.MiscConfig0 = WCHNET_MISC_CONFIG0; WCHNET_ConfigLIB(&cfg); i = WCHNET_Init(ip,gwip,mask,macaddr); return (i); } /********************************************************************* * @fn SET_MCO * * @brief Set ETH Clock. * * @return none */ void SET_MCO(void) { RCC_PLL3Cmd(DISABLE); RCC_PREDIV2Config(RCC_PREDIV2_Div2); RCC_PLL3Config(RCC_PLL3Mul_15); RCC_MCOConfig(RCC_MCO_PLL3CLK); RCC_PLL3Cmd(ENABLE); Delay_Ms(100); while(RESET == RCC_GetFlagStatus(RCC_FLAG_PLL3RDY)) { Delay_Ms(500); } RCC_AHBPeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); } /********************************************************************* * @fn TIM2_Init * * @brief Initializes TIM2. * * @return none */ void TIM2_Init( void ) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure={0}; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period = 200-1; TIM_TimeBaseStructure.TIM_Prescaler =7200-1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ITConfig(TIM2, TIM_IT_Update ,ENABLE); TIM_Cmd(TIM2, ENABLE); TIM_ClearITPendingBit(TIM2, TIM_IT_Update ); NVIC_SetPriority(TIM2_IRQn, 0x80); NVIC_EnableIRQ(TIM2_IRQn); } /********************************************************************* * @fn WCHNET_CreatTcpSocket * * @brief Creat Tcp Socket * * @return none */ void WCHNET_CreatTcpSocket(void) { u8 i; SOCK_INF TmpSocketInf; /* 创建临时socket变量 */ memset((void *)&TmpSocketInf,0,sizeof(SOCK_INF)); /* 库内部会将此变量复制,所以最好将临时变量先全部清零 */ memcpy((void *)TmpSocketInf.IPAddr,DESIP,4); /* 设置目的IP地址 */ TmpSocketInf.DesPort = desport; /* 设置目的端口 */ TmpSocketInf.SourPort = srcport++; /* 设置源端口 */ TmpSocketInf.ProtoType = PROTO_TYPE_TCP; /* 设置socekt类型 */ TmpSocketInf.RecvStartPoint = (u32)SocketRecvBuf[SocketId]; /* 设置接收缓冲区的接收缓冲区 */ TmpSocketInf.RecvBufLen = RECE_BUF_LEN ; /* 设置接收缓冲区的接收长度 */ i = WCHNET_SocketCreat(&SocketId,&TmpSocketInf); /* 创建socket,将返回的socket索引保存在SocketId中 */ printf("WCHNET_SocketCreat %d\r\n",SocketId); mStopIfError(i); /* 检查错误 */ i = WCHNET_SocketConnect(SocketId); /* TCP连接 */ mStopIfError(i); /* 检查错误 */ printf("Error= %d\r\n",i); } /********************************************************************* * @fn WCHNET_HandleSockInt * * @brief Socket Interrupt Handle * * @return none */ void WCHNET_HandleSockInt(u8 sockeid,u8 initstat) { u32 len; if(initstat & SINT_STAT_RECV) /* socket接收中断*/ { len = WCHNET_SocketRecvLen(sockeid,NULL); /* 获取socket缓冲区数据长度 */ printf("WCHNET_SocketRecvLen %d %d\r\n",len,sockeid); WCHNET_SocketRecv(sockeid,MyBuf,&len); /* 获取socket缓冲区数据 */ } if(initstat & SINT_STAT_CONNECT) /* socket连接成功中断*/ { printf("TCP Connect Success\r\n"); WCHNET_ModifyRecvBuf(sockeid, (u32)SocketRecvBuf[sockeid], RECE_BUF_LEN); } if(initstat & SINT_STAT_DISCONNECT) /* socket连接断开中断*/ { printf("TCP Disconnect\r\n"); } if(initstat & SINT_STAT_TIM_OUT) /* socket连接超时中断*/ { printf("TCP Timout\r\n"); /* 延时200ms,重连*/ } } /********************************************************************* * @fn WCHNET_HandleGlobalInt * * @brief Global Interrupt Handle * * @return none */ void WCHNET_HandleGlobalInt(void) { u8 initstat; u16 i; u8 socketinit; initstat = WCHNET_GetGlobalInt(); /* 获取全局中断标志*/ if(initstat & GINT_STAT_UNREACH) /* 不可达中断 */ { printf("GINT_STAT_UNREACH\r\n"); } if(initstat & GINT_STAT_IP_CONFLI) /* IP冲突中断 */ { printf("GINT_STAT_IP_CONFLI\r\n"); } if(initstat & GINT_STAT_PHY_CHANGE) /* PHY状态变化中断 */ { i = WCHNET_GetPHYStatus(); /* 获取PHY连接状态*/ if(i&PHY_Linked_Status) printf("PHY Link Success\r\n"); } if(initstat & GINT_STAT_SOCKET) /* Socket中断*/ { for(i = 0; i < WCHNET_MAX_SOCKET_NUM; i ++) { socketinit = WCHNET_GetSocketInt(i); /* 获取socket中断并清零 */ if(socketinit)WCHNET_HandleSockInt(i,socketinit); /* Èç¹ûÓÐÖжÏÔòÇåÁã */ } } } void led_gpio(){ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStructure); } void Refresh_TIM_Init( u16 arr, u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM7, ENABLE ); TIM_TimeBaseInitStructure.TIM_Period = arr; TIM_TimeBaseInitStructure.TIM_Prescaler = psc; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Down; TIM_TimeBaseInit( TIM7, &TIM_TimeBaseInitStructure); TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE); TIM_ARRPreloadConfig( TIM7, ENABLE ); TIM_Cmd( TIM7, ENABLE ); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void TIM7_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); void TIM7_IRQHandler(void) { uint32_t len = 1; char old1 = temperature, old2 = humidity ; if(old1 != temp[0]) { old1 = temp[0]; WCHNET_SocketSend(SocketId,"T",&len); WCHNET_SocketSend(SocketId,temp,&len); } if(old2 != temp[1]) { old2 = temp[1]; WCHNET_SocketSend(SocketId,"H",&len); WCHNET_SocketSend(SocketId,(temp+1),&len); } TIM_ClearFlag(TIM7, TIM_FLAG_Update);//清除标志位 } int main(void) { u8 i; Delay_Init(); USART_Printf_Init(115200); lcd_init(); printf("width:%02d height:%02d \n", LCD_W, LCD_H); lcd_clear(BLACK); lcd_show_string(25, 10, 32, " Logstic"); lcd_set_color(BLACK, RED); delay_ms(100); Refresh_TIM_Init( 200-1, (SystemCoreClock/10000)-1 ); lcd_set_color(BLACK,WHITE); lcd_show_string(0, 70, 16,"AHT10"); while(AHT10_Init()) //初始化AHT10 { printf("AHT10 Error"); lcd_set_color(BLACK,RED); lcd_show_string(180, 176, 16,"Error"); Delay_Ms(200); lcd_show_string(180, 176, 16," "); Delay_Ms(200); } /*串口打印初始化*/ printf("TcpClient Test\r\n"); led_gpio(); SET_MCO(); TIM2_Init(); WCH_GetMac(MACAddr); /*获取芯片Mac地址*/ i=WCHNET_LibInit(IPAddr,GWIPAddr,IPMask,MACAddr); /*以太网库初始化*/ mStopIfError(i); if(i==WCHNET_ERR_SUCCESS) printf("WCHNET_LibInit Success\r\n"); while(!(WCHNET_GetPHYStatus()&PHY_LINK_SUCCESS)) /*等待PHY连接成功*/ { Delay_Ms(100); } WCHNET_CreatTcpSocket(); /*创建Tcp socket*/ while(1) { temperature = AHT10_Read_Temperature(); humidity = AHT10_Read_Humidity(); temp[0] = (char)(temperature); temp[1] = (char)(humidity); WCHNET_MainTask(); /*以太网库主任务函数,需要循环调用*/ if(WCHNET_QueryGlobalInt()) /*查询以太网全局中断,如果有中断,调用全局中断处理函数*/ { WCHNET_HandleGlobalInt(); } lcd_set_color(BLACK,WHITE); lcd_show_string(0, 70, 16,"AHT10"); while(AHT10_Init()) //初始化AHT10 { printf("AHT10 Error"); lcd_set_color(BLACK,RED); lcd_show_string(180, 176, 16,"Error"); Delay_Ms(200); lcd_show_string(180, 176, 16," "); Delay_Ms(200); } temperature = AHT10_Read_Temperature(); humidity = AHT10_Read_Humidity(); lcd_set_color(BLACK,GREEN); lcd_show_string(30, 92, 16,"Temperature : %5d", (int)(temperature)); lcd_show_string(30, 108, 16,"Humidity : %5d", (int)(humidity)); } } ```