第一步:資料下載
以太網協議棧芯片 CH395 - 南京沁恒微電子股份有限公司
第二步:準備工程
(1) 首先準備一個編譯無報錯、可以正常打印和延時的工程文件,官方例程采用STM32F1芯片,但本文采用GD32F470芯片
(2)將例程代碼中的PUB文件夾加入,keil工程添加CH395CMD.c文件和CH395SPI_HW.c文件
(3)將例程代碼中的main.c內容全部加入到自己的main.c文件中,如果覺得函數較多,可以自己放置到CH398CMD.c函數中
(4)例程代碼使用SPI_DMA,看個人需要,使用則配置,不使用則屏蔽
(5)此時編譯有很多錯誤,繼續下一步
第三步:引腳配置
ch395Q支持SPI和串口通信,本文采用SPI通信,我們需要配置6個GPIO口,分別如下
一、SPI通信基礎引腳
- CS(片選引腳)
- 功能:用于使能CH395的SPI通信,低電平有效。
- 配置:需連接到STM32的GPIO引腳,并在代碼中控制其電平12。
- SCK(時鐘引腳)
- 功能:提供SPI通信的時鐘信號,由主設備(如STM32)控制。
- 配置:需連接到STM32的SPI外設時鐘線(如SPI1_SCK),并設置時鐘頻率(如36MHz)13。
- MOSI(主出從入引腳)
- 功能:主設備向CH395發送數據。
- 配置:需連接到STM32的SPI外設MOSI線(如SPI1_MOSI)
- MISO(主入從出引腳)
- 功能:CH395向主設備返回數據。
- 配置:需連接到STM32的SPI外設MISO線(如SPI1_MISO)
二、輔助控制引腳
- INT(中斷引腳)
- 功能:用于CH395通知主設備事件(如數據接收完成、錯誤狀態)。
- 配置:需連接到STM32的外部中斷引腳(如PA0),并設置為輸入模式,同時啟用中斷服務程序14。
- RST(復位引腳)
- 功能:硬件復位CH395,低電平有效。
- 配置:需連接到STM32的GPIO引腳,初始化時拉低再拉高以完成復位
CH395_PORT_INIT();//SPI初始化
CH395_GPIO_INIT();//RST和INT引腳初始化
CH395Reset();//修改RST對應引腳
xCH395CmdStart();//修改CS對應引腳,CMD_START_HANDEL();可以刪除,無作用
xEndCH395Cmd();//修改CS對應引腳,CMD_END_HANDEL();可以刪除,無作用
第四步:延時函數配置
例程代碼使用debug.c文件中配置的延時函數,本文采用自定義函數,或者直接全文替換?
void Delay_Us(uint32_t us)
{us *= 168; // 72MHz下1us≈72個周期(每條循環指令約3周期)while (us--) {__NOP(); // 內聯匯編NOP指令 }
}void Delay_Ms(uint32_t ms)
{delay_1ms(ms);
}
第五步:編譯
此處編譯后肯定會多處報錯,比如UINT8沒有定義,stm32f10x_dma.h文件找不到等等,這些都是正常的,按照自己的經驗進行修改即可,直到編譯無警告,無報錯
第六步:ip修改
InitCH395InfParam()?/* CH395 Related definition */
const uint8_t CH395IPAddr[4] = {192, 168, 1, 101}; /* CH395 IP */
const uint8_t CH395GWIPAddr[4] = {192, 168, 1, 1}; /* CH395 gateway */
const uint8_t CH395IPMask[4] = {255, 255, 255, 0}; /* CH395 mask *//* Socket definitions */
const uint8_t Socket0DesIP[4] = {192, 168, 1, 123}; /* Destination IP address for Socket 0 */
const uint16_t Socket0DesPort = 1000; /* Destination port for Socket 0 */
const uint16_t Socket0SourPort = 5000; /* Source port for Socket 0 */
第七步:查看保活機制
keeplive_set()#define DEF_KEEP_LIVE_IDLE (15 * 1000) /* Idle time */
#define DEF_KEEP_LIVE_PERIOD (20 * 1000) /* Send a KEEPALIVE packet every 20 seconds */
#define DEF_KEEP_LIVE_CNT 200 /* Number of retry attempts */DEF_KEEP_LIVE_IDLE:空閑時間,可能指的是在TCP連接建立后,如果在15秒內沒有數據傳輸,則開始發送保活包。這與TCP的保活機制中的空閑時間類似,用于確定何時開始檢測連接是否有效。
DEF_KEEP_LIVE_PERIOD:每隔20秒發送一次保活包。這個參數可能控制保活包的發送頻率,確保在空閑期間定期檢測連接狀態。
DEF_KEEP_LIVE_CNT:重試次數200次。當保活包發送后未收到響應時,會進行重試,這個參數指定最大重試次數,超過后認為連接已斷開。
- 保活機制主要用于維持TCP連接的活躍狀態,防止因網絡中斷或空閑導致連接被中間設備(如路由器、防火墻)主動斷開
- 應對網絡環境不穩定:在工業控制、遠程監控等場景中,網絡可能因電磁干擾、信號衰減等問題出現瞬時中斷。保活機制通過周期性發送心跳包(空數據包),可快速檢測鏈路異常并觸發重連,避免數據丟失。
- 支持物聯網長連接需求:CH395常用于MQTT、HTTP長連接等物聯網協議,需保持設備與服務器持續通信。若長時間無數據傳輸,服務器或網關可能關閉連接,保活包能維持連接有效性
第八步:全局中斷
?CH395GlobalInterrupt? --》??GINT_STAT_SOCK0
針對一個TCP連接,主要有以下幾個中斷
void CH395SocketInterrupt(uint8_t sockindex)
{sock_int_status[sockindex] |= CH395CMDGetSocketInt(sockindex); /* Gets the socket interrupt status */if (sock_int_status[sockindex] & SINT_STAT_RECV) /* Receive interruption */{//接收中斷,接受到數據后會進來這里,但例程代碼在Data_Loop函數中處理// Handle it in the main program}if (sock_int_status[sockindex] & SINT_STAT_SENDBUF_FREE) /* The send buffer is free and can continue writing data to be sent */{//發送緩沖區為空,可以發送數據,但例程代碼在Data_Loop函數中處理// Handle it in the main program}if (sock_int_status[sockindex] & SINT_STAT_SEND_OK) /* Send completion interrupt */{//發送完成中斷sock_int_status[sockindex] &= ~SINT_STAT_SEND_OK;}if (sock_int_status[sockindex] & SINT_STAT_CONNECT) /* The connection is interrupted, valid only in TCP mode */{//連接成功中斷sock_int_status[sockindex] &= ~SINT_STAT_CONNECT;CH395CMDSetKeepLive(sockindex, 1); /* Enable the KEEPALIVE timer */LOG("SINT_STAT_CONNECT\r\n");}if (sock_int_status[sockindex] & SINT_STAT_DISCONNECT) /* Disconnect interrupt, valid only in TCP mode */{//斷開連接中斷sock_int_status[sockindex] &= ~SINT_STAT_DISCONNECT;LOG("SINT_STAT_DISCONNECT \r\n");}if (sock_int_status[sockindex] & SINT_STAT_TIM_OUT) /* Timeout interrupt, valid only in TCP mode */{//連接超時中斷,比如服務器未打開sock_int_status[sockindex] &= ~SINT_STAT_TIM_OUT;LOG("SINT_STAT_TIM_OUT\r\n");}
}
Data_Loop函數,主要就是將接收到的數據發出去
第九步:測試
調試心得?
(1)當服務器主動關閉連接時,會進入TIME_WAIT狀態(持續2*MSL,默認約60秒),導致原端口暫時無法復用,如果服務器沒有做端口復用這個功能,可能會出現這個現象