CH9121串口轉以太網模塊STM32驅動
0. 簡介
CH9121 集成TCP/IP 協議棧,可實現網絡數據包和串口數據的雙向透明傳輸,具有TCPCLIENT、TCP SERVER、UDP 3 種工作模式,串口波特率最高可支持到921600bps,可通過上位機軟件輕松配置,方便快捷。
下圖為CH9121 應用框圖:

1. 特性:
內部自帶以太網介質傳輸層(MAC)和物理層(PHY)。
實現串口數據和網絡數據的雙向透明傳輸。
支持10/100M,全雙工/半雙工自適應以太網接口,兼容IEEE 802.3 協議。
支持MDI/MDIX 線路自動轉換。
工作模式支持TCP CLIENT、TCP SERVER 和UDP 模式。
串口波特率支持300bps ~ 921600bps。
串口TTL 電平,兼容3.3V 和5V。
串口支持全雙工和半雙工串口通訊,支持RS485 收發自動切換。
工作模式、端口、IP 等網絡參數,串口波特率等參數可通過上位機配置。
支持虛擬串口。
2. 應用場合

3. 示例平台
STM32F407
4. 代碼
bsp_ch9121.h
// 定義了與網絡CH9121通信的基本數據結構,和配置結構,參數等
#ifndef __BSP_CH9121_H__
#define __BSP_CH9121_H__
#include <bsp.h>
// 本程序中默認模塊做為客戶端
#define M_UARTX UART4
#define WRITE_CMD {Usartx_SendByte(M_UARTX, 0x57);Usartx_SendByte(M_UARTX, 0xab);}
// 定義接收和發送緩沖區,用動態申請大小,打包長度小於1024byte
extern uint8_t net_tx_buf[1024];
extern uint8_t net_rx_buf[1024];
extern uint8_t overflow_flag;
// 網絡連接狀態類型
typedef enum _ConnectStatus
{
disconnect = 0,
connect_ok
} ConnectStatus;
// 校驗方式數據類型
typedef enum _Serial_Check
{
Even_Check = 0,
Odd_Check,
Mark,
Space,
None
} Serial_Check;
// 工作模式類型
typedef enum _Net_Mode
{
TCP_Server_mode = 0,
TCP_Client_mode,
UDP_Server_mode,
UDP_Client_mode
} Net_Mode;
// 服務器/客戶端通信相關的類型
typedef struct _Net_CommunitionType
{
uint16_t port_num; // 端口號
uint8_t ip_address[4]; // IP地址
uint8_t subnet_mask[4]; // 子網掩碼
uint8_t gateway[4]; // 默認網關
uint16_t tcp_retry_count; // TCP重試次數
uint8_t mac_address[4]; // MAC地址
ConnectStatus status; // 連接狀態
Net_Mode net_mode;
} Net_CommunitionType;
extern Net_CommunitionType net_communition_Client;
extern Net_CommunitionType net_communition_DstServer;
extern Net_CommunitionType net_communition_Server1;
// 串口相關數據類型
typedef struct _Serial
{
uint32_t bound; // 波特率
Serial_Check serial_check; // 校驗類型
uint8_t data_bit; // 數據位數
uint8_t stop_bit; // 停止位
uint32_t timeout; // 超時時間
} Serial;
extern Serial serial;
// 寫命令碼,格式(0x57 0xab + 命令碼 + 數據)
#define RESET_CHIP 0x02 // 復位命令,芯片重新運行
#define UPDATE_CONFIG 0x0d // 更新配置參數至 EEPROM
#define CMD_EXECUTION 0x0e // 命令執行
#define SET_CHIP_MODE 0x10 // 設置模式
#define SET_CHIP_IP 0x11 // 設置芯片 IP 地址
#define SET_CHIP_SUBNET_MASK 0x12 // 設置芯片掩碼
#define SET_CHIP_GATEWAY 0x13 // 設置芯片網關
#define SET_LOCAL_PORT 0x14 // 設置芯片本地端口
#define SET_DESTINATION_IP 0x15 // 設置芯片目的 IP 地址
#define SET_DESTINATION_PORT 0x16 // 設置芯片目的端口
#define SET_SERIAL_BOUND 0x21 // 設置串口波特率
#define SET_SERIAL_DATA_FORMAT 0x22 // 設置串口校驗位數據位停止位
#define EXIT_SERIAL_CONFIG 0x5e // 退出串口配置模式
// 讀命令碼,格式(0x57 0xab + 命令碼)
#define READ_CHIP_MODE 0x60 // 讀取芯片工作模式,返回 1 字節
#define READ_CHIP_IP 0x61 // 讀取芯片 IP 地址,返回 4 字節
#define READ_CHIP_SUBNET_MASK 0x62 // 讀取芯片掩碼,返回 4 字節
#define READ_CHIP_GATEWAY 0X63 // 讀取芯片網關,返回 4 字節
#define READ_SOURCE_PORT 0x64 // 讀取芯片源端口號,返回 2 字節
#define READ_DESTINATION_IP 0x65 // 讀取芯片目的 IP 地址,返回 4 字節
#define READ_DESTINATION_PORT 0x66 // 讀取芯片目的端口號,返回 2 字節
#define READ_RETRY_TIMES 0x67 // 讀取 TCP 重試次數,返回 1 字節
#define READ_SERIAL_BOUND 0x71 // 讀取串口波特率,返回 4 字節
#define READ_SERIAL_DATA_FORMAT 0x72 // 讀取串口校驗位數據位停止位,返回 3 字節
#define READ_SERIAL_OVERTIME 0x73 // 讀取串口超時時間,返回 1 字節
#define READ_MAC_ADDRESS 0x81 // 讀取 MAC 地址,返回 6 字節
#define READ_TCP_STATUS 0x03 // 讀取 TCP 連接狀態(TCP CLIENT 模式下),返回 1 字節,1:連接,0:斷開。
// 公有函數區域
void init_ch9121(void);
void send_netdata(uint8_t * t_buf);
uint8_t read_WorkMode(void);
uint16_t read_source_port(void);
void read_chip_ip(uint8_t *ip_addr);
uint16_t read_distination_port(void);
void read_distination_ip(uint8_t *ip_addr);
void reset_chip(void);
#endif
bsp_ch9121.c
#include <bsp_ch9121.h>
// 備注:兩個讀函數之間間隔510ms
// 定義接收和發送緩沖區,用動態申請大小
uint8_t net_tx_buf[1024];
uint8_t net_rx_buf[1024];
// 溢出標志
uint16_t overflow = 0;
uint8_t overflow_flag = 0;
// 模塊串口參數初始化
Serial serial =
{
921600,
None,
0x08,
0x01,
0
};
// 模塊客戶端結構體成員初始化
Net_CommunitionType net_communition_Client =
{
2000, // 客戶端端口號
{192, 168, 1, 200}, // 客戶端ip地址
{255, 255, 255, 0}, // 客戶端子網掩碼
{192, 168, 1, 1}, // 客戶端默認網關
};
// 模塊做服務器端結構體成員
Net_CommunitionType net_communition_Server1 =
{
2001, // 服務器端口號
{192, 168, 1, 202}, // 服務器ip地址
{255, 255, 255, 0}, // 服務器子網掩碼
{192, 168, 1, 1}, // 服霧器默認網關
};
// 如果模塊用TCP client模式,這里的參數自行修改為目的服務器對應參數
Net_CommunitionType net_communition_DstServer =
{
1000, // 服務器端口號
{192, 168, 1, 125}, // 服務器ip地址
{255, 255, 255, 0}, // 服務器子網掩碼
{192, 168, 1, 1}, // 服霧器默認網關
};
// 進入配置模式
// 對於內部讀寫網口需要先調用此函數進入配置模式
static void ch9121_config_mode(void)
{
// 發送命令字進入配置模式
Usartx_SendByte(M_UARTX, 0x55);
Usartx_SendByte(M_UARTX, 0xaa);
Usartx_SendByte(M_UARTX, 0x5a);
delay_ms(100);
M_UARTX->DR = 0XA5;
}
static void ch9121_exitconfige(void)
{
// 發送命令字進入配置模式
WRITE_CMD;
Usartx_SendByte(M_UARTX, EXIT_SERIAL_CONFIG);
}
// 更新配置參數到EEPROM
static void update_config(void)
{
WRITE_CMD;
Usartx_SendByte(M_UARTX, UPDATE_CONFIG);
}
// 執行配置
static void execution_config(void)
{
WRITE_CMD;
Usartx_SendByte(M_UARTX, CMD_EXECUTION);
}
// 配置為芯片為TCP Server模式
static void ch9121_TCP_Server(void)
{
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 配置為服務器模式
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_MODE);
Usartx_SendByte(M_UARTX, TCP_Server_mode);
delay_ms(100);
// 設置芯片IP地址 192.168.1.202
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_IP);
Usartx_SendByte(M_UARTX, net_communition_Server1.ip_address[0]);
Usartx_SendByte(M_UARTX, net_communition_Server1.ip_address[1]);
Usartx_SendByte(M_UARTX, net_communition_Server1.ip_address[2]);
Usartx_SendByte(M_UARTX, net_communition_Server1.ip_address[3]);
delay_ms(100);
// 設置芯片子網掩碼 255.255.255.0
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_SUBNET_MASK);
Usartx_SendByte(M_UARTX, net_communition_Server1.subnet_mask[0]);
Usartx_SendByte(M_UARTX, net_communition_Server1.subnet_mask[1]);
Usartx_SendByte(M_UARTX, net_communition_Server1.subnet_mask[2]);
Usartx_SendByte(M_UARTX, net_communition_Server1.subnet_mask[2]);
delay_ms(100);
// 設置芯片網關 192.168.1.1
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_GATEWAY);
Usartx_SendByte(M_UARTX, net_communition_Server1.gateway[0]);
Usartx_SendByte(M_UARTX, net_communition_Server1.gateway[1]);
Usartx_SendByte(M_UARTX, net_communition_Server1.gateway[2]);
Usartx_SendByte(M_UARTX, net_communition_Server1.gateway[3]);
delay_ms(100);
// 設置服務器端口2001,端口號發送字節序是先發低字節,再發高字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_LOCAL_PORT);
Usartx_SendByte(M_UARTX, net_communition_Server1.port_num & 0x0ff);
Usartx_SendByte(M_UARTX, (uint8_t)(net_communition_Server1.port_num>>8));
delay_ms(100);
// 更新配置到EEPROM
update_config();
delay_ms(100);
// 執行配置
execution_config();
delay_ms(100);
// 退出配置模式
ch9121_exitconfige();
}
// 配置為TCP Client模式
static void ch9121_TCP_Client(void)
{
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 配置為客戶端模式
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_MODE);
Usartx_SendByte(M_UARTX, TCP_Client_mode);
delay_ms(100);
/*
// 已從上位機軟件配置為DHCP模式,不需要配置IP、網關和子網掩碼,端口號配置為了隨機
// 設置芯片IP地址 192.168.1.202
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_IP);
Usartx_SendByte(M_UARTX, net_communition_Client.ip_address[0]);
Usartx_SendByte(M_UARTX, net_communition_Client.ip_address[1]);
Usartx_SendByte(M_UARTX, net_communition_Client.ip_address[2]);
Usartx_SendByte(M_UARTX, net_communition_Client.ip_address[3]);
// 設置芯片子網掩碼 255.255.255.0
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_SUBNET_MASK);
Usartx_SendByte(M_UARTX, net_communition_Client.subnet_mask[0]);
Usartx_SendByte(M_UARTX, net_communition_Client.subnet_mask[1]);
Usartx_SendByte(M_UARTX, net_communition_Client.subnet_mask[2]);
Usartx_SendByte(M_UARTX, net_communition_Client.subnet_mask[2]);
// 設置芯片網關 192.168.1.1
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_GATEWAY);
Usartx_SendByte(M_UARTX, net_communition_Client.gateway[0]);
Usartx_SendByte(M_UARTX, net_communition_Client.gateway[1]);
Usartx_SendByte(M_UARTX, net_communition_Client.gateway[2]);
Usartx_SendByte(M_UARTX, net_communition_Client.gateway[3]);
// 設置服務器端口2001,端口號發送字節序是先發低字節,再發高字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_LOCAL_PORT);
Usartx_SendByte(M_UARTX, net_communition_Client.port_num & 0x0ff);
Usartx_SendByte(M_UARTX, (uint8_t)net_communition_Client.port_num>>8);
*/
// 設置目的服務器IP地址
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_DESTINATION_IP);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[0]);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[1]);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[2]);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[3]);
delay_ms(100);
// 設置目的服務器端口1000,端口號發送字節序是先發低字節,再發高字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_DESTINATION_PORT);
Usartx_SendByte(M_UARTX, net_communition_DstServer.port_num & 0x0ff);
Usartx_SendByte(M_UARTX, (uint8_t)(net_communition_DstServer.port_num>>8));
delay_ms(100);
// 更新配置到EEPROM
update_config();
delay_ms(100);
// 執行配置
execution_config();
delay_ms(100);
// 退出配置模式
ch9121_exitconfige();
}
// 配置為UDP Client模式
static void ch9121_UDP(void)
{
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 配置為UDP客戶端模式
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_MODE);
Usartx_SendByte(M_UARTX, UDP_Client_mode);
delay_ms(100);
// 設置目的服務器IP地址
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_DESTINATION_IP);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[0]);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[1]);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[2]);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[3]);
delay_ms(100);
// 設置目的服務器端口1000,端口號發送字節序是先發低字節,再發高字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_DESTINATION_PORT);
Usartx_SendByte(M_UARTX, net_communition_DstServer.port_num & 0x0ff);
Usartx_SendByte(M_UARTX, (uint8_t)(net_communition_DstServer.port_num>>8));
delay_ms(100);
// 更新配置到EEPROM
update_config();
delay_ms(100);
// 執行配置
execution_config();
delay_ms(100);
// 退出配置模式
ch9121_exitconfige();
}
// 配置為UDP Server模式
static void ch9121_UDP_Server(void)
{
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 配置為UDP服務器端模式
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_CHIP_MODE);
Usartx_SendByte(M_UARTX, UDP_Server_mode);
delay_ms(100);
// 設置目的服務器IP地址
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_DESTINATION_IP);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[0]);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[1]);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[2]);
Usartx_SendByte(M_UARTX, net_communition_DstServer.ip_address[3]);
delay_ms(100);
// 設置目的服務器端口1000,端口號發送字節序是先發低字節,再發高字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_DESTINATION_PORT);
Usartx_SendByte(M_UARTX, net_communition_DstServer.port_num & 0x0ff);
Usartx_SendByte(M_UARTX, (uint8_t)(net_communition_DstServer.port_num>>8));
// 更新配置到EEPROM
update_config();
delay_ms(100);
// 執行配置
execution_config();
delay_ms(100);
// 退出配置模式
ch9121_exitconfige();
}
// 選擇ch9121的工作模式
// mode:0->TCP Server
// 1->TCP Client
// 2->UDP
// 3->UDP Server
static void ch9121_mode_select(uint8_t mode)
{
switch(mode)
{
case 0:
ch9121_TCP_Server();
break;
case 1:
ch9121_TCP_Client();
break;
case 2:
ch9121_UDP();
break;
case 3:
ch9121_UDP_Server();
break;
}
}
// 配置串口相關餐數
static void serial_config(void)
{
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 設置串口波特率926100,需要籌夠4字節,發送字節序是先發低字節,再發高字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_SERIAL_BOUND);
Usartx_SendByte(M_UARTX, serial.bound&0xff);
Usartx_SendByte(M_UARTX, (serial.bound>>8)&0xff);
Usartx_SendByte(M_UARTX, (serial.bound>>16)&0xff);
Usartx_SendByte(M_UARTX, (serial.bound>>24)&0xff);
delay_ms(100);
// 設置串口數據幀格式,1位停止位,無校驗,8位數據位
WRITE_CMD;
Usartx_SendByte(M_UARTX, SET_SERIAL_DATA_FORMAT);
Usartx_SendByte(M_UARTX, serial.stop_bit);
Usartx_SendByte(M_UARTX, serial.serial_check);
Usartx_SendByte(M_UARTX, serial.data_bit);
delay_ms(100);
// 更新配置到EEPROM
update_config();
delay_ms(100);
// 執行配置
execution_config();
delay_ms(100);
// 退出配置模式
ch9121_exitconfige();
}
// 發送數據
void send_netdata(uint8_t * t_buf)
{
while(*t_buf != '\0')
{
Usartx_SendByte(M_UARTX, *t_buf);
t_buf++;
}
}
/*************************************************************************************************************************************************
讀取相關函數
**************************************************************************************************************************************************/
// 讀取芯片工作模式,返回一個字節
uint8_t read_WorkMode(void)
{
uint8_t netmode;
// 每次進來先把緩沖數組下標清零,便於取數
overflow = 0;
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 讀取芯片工作模式,返回一個字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, READ_CHIP_MODE);
delay_ms(100);
// 退出配置模式
ch9121_exitconfige();
netmode = net_rx_buf[2];
printf("%d\r\n", netmode);
switch(netmode)
{
case 0:
return TCP_Server_mode;
case 1:
return TCP_Client_mode;
case 2:
return UDP_Server_mode;
case 3:
return UDP_Client_mode;
}
return 0xff;
}
// 讀取芯片IP
void read_chip_ip(uint8_t *ip_addr)
{
uint8_t i;
// 每次進來先把緩沖數組下標清零,便於取數
overflow = 0;
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 讀取芯片工作模式,返回一個字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, READ_CHIP_IP);
delay_ms(100);
for(i = 0; i < 4; i++)
{
ip_addr[i] = net_rx_buf[i+2];
}
// 退出配置模式
ch9121_exitconfige();
printf("%d.%d.%d.%d\r\n", ip_addr[0],ip_addr[1],ip_addr[2],ip_addr[3]);
}
// 讀取源端口
uint16_t read_source_port(void)
{
uint16_t _port;
// 每次進來先把緩沖數組下標清零,便於取數
overflow = 0;
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 讀取芯片工作模式,返回一個字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, READ_SOURCE_PORT);
delay_ms(100);
// 退出配置模式
ch9121_exitconfige();
_port = net_rx_buf[2];
_port |= (net_rx_buf[3]<<8);
printf("%d\r\n", _port);
return _port;
}
// 讀取目的IP
void read_distination_ip(uint8_t *ip_addr)
{
uint8_t i;
// 每次進來先把緩沖數組下標清零,便於取數
overflow = 0;
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 讀取芯片工作模式,返回一個字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, READ_DESTINATION_IP);
delay_ms(100);
for(i = 0; i < 4; i++)
{
ip_addr[i] = net_rx_buf[i+2];
}
// 退出配置模式
ch9121_exitconfige();
printf("%d.%d.%d.%d\r\n", ip_addr[0],ip_addr[1],ip_addr[2],ip_addr[3]);
}
// 讀取目的端口
uint16_t read_distination_port(void)
{
uint16_t _port;
// 每次進來先把緩沖數組下標清零,便於取數
overflow = 0;
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
// 讀取芯片工作模式,返回一個字節
WRITE_CMD;
Usartx_SendByte(M_UARTX, READ_DESTINATION_PORT);
delay_ms(100);
// 退出配置模式
ch9121_exitconfige();
_port = net_rx_buf[2];
_port |= (net_rx_buf[3]<<8);
printf("%d\r\n", _port);
return _port;
}
// 復位芯片
void reset_chip(void)
{
// 芯片進入配置模式
ch9121_config_mode();
delay_ms(100);
WRITE_CMD;
Usartx_SendByte(M_UARTX, RESET_CHIP);
delay_ms(100);
// 退出配置模式
ch9121_exitconfige();
}
// 初始化配置函數
void init_ch9121(void)
{
// 設置工作模式
ch9121_mode_select(TCP_Client_mode);
}
/**
* @name UART4_IRQHandler
* @brief 串口4中斷服務函數,目前用於CH9121網絡通信模塊
* @param 空
* @return 空
* @DateTime 2019-7-20
*/
void UART4_IRQHandler(void)
{
uint8_t return_val = 0;
if(USART_GetITStatus( M_UARTX, USART_IT_RXNE ) != RESET)
{
return_val = USART_ReceiveData(M_UARTX);
if(overflow > 1022)
{
overflow = 0;
overflow_flag = 1;
net_rx_buf[1023] = '\0';
}
else
{
net_rx_buf[overflow++] = return_val;
}
}
}
