用国产CH32替代STM32,快来试试看!
/* 作者: 羅冰? https://blog.csdn.net/luobing4365 */
隨著芯片價格瘋漲,項目的不可控性越來越大。特別是價格方面,達到了無法想象的地步了。
按我的記憶,之前項目中所用的STM32F103C8T6,價格在9元左右;而現在到立創商城上去查,單片價格到了驚人的109元!十幾倍的漲幅,哪個項目還敢用它?
因此,大部分公司,都在準備各種替代方案。
我們也一樣,預備使用CH32F103C8T6替代STM32F103C8T6。這兩種芯片引腳兼容,內部的資源差不多,理論上代碼移植也比較方便。
我就是這么想的,然后就被打臉了。
最大的原因在于,廠家提供的資料太少了!編程相關的CH32F103應用手冊,只有短短的31頁。我想看的USB設備控制器的寄存器細節,甚至都沒有。想想STM32豐富的應用資料、例程和各種視頻,感覺從新手級難度到了骨灰級難度了。
不過,再想想CH32這友好的價格,也就釋然了。
周末兩天,把之前的USB HID通信,在CH32F103C8T6上實現了,估計不久能很快地應用到項目中去。
預計也也有不少朋友有類似的需求,我把探索的過程記錄下來。
1. 固件下載
CH32F103的芯片,支持WCH-Link或者其他SW仿真工具下載,也支持使用WCHISPTool通過USB和串口下載。考慮到后續開發的時候需要調試,我使用的是WCH-Link進行下載。
如圖1所示,給出了WCH-Link的實物圖(摘自《WCH-Link使用說明-V1.3》)。
圖1 WCH-Link實物
由于我的目標是使用它下載程序到CH32F103C8T6中,只需要使用ARM模式就行了,不需要關注RISC-V模式。
拿到的WCH-Link,一般是RISC-V模式,需要將其切換到ARM模式。
模式切換的方法如下:
WCH-Link 斷電, 將圖一正面圖 1 中排針, TX 接 GND;
WCH-Link 上電, 切換模式成功后, 斷開 TX 和 GND;
后續使用時, WCH-Link 保持切換后的模式。
判斷的方法如下:
WCH-Link空閑時藍燈常滅,是為RISC-V模式;
WCH-Link空閑時藍燈常亮,為ARM模式。
在ARM模式下,Windows 10下是不需要安裝驅動的,而Win7有些情況下需要更換驅動,具體可以向廠家索取資料。
圖2是WCH-Link在Win7下的設備顯示。
圖2 WCH-Link的ARM模式
實際使用中,直接使用SWD協議的兩線以及GND就可以下載了。軟件的使用方法,可以參考官方提供的《CH32F103評估板說明書》,其中介紹了詳細的下載和仿真調試方法。
2. 代碼編寫
使用CH32F103C8T6實現之前的USB HID雙向通信。
在經歷了若干款MCU編寫USB代碼后,對這塊內容已經比較熟悉了。簡單來說,只要在USB HID的示例上,修改各類描述符,添加需要的命令處理就可以了。
可惜的是,廠家提供的示例代碼非常少。CH32F103C8T6支持兩個USB端口,一個是可做全速主機或設備的USBHD,另一個是全速設備USBD。
提供的示例代碼中,USBD給出了VirtualCom的工程;USBHD給出了DEVICE、HOSG、HOST_Udisk三個示例。
USBD的工程,類似于STM32的Legacy Library;而USBHD的工程,則使用了沁恒電子自己的庫。
我的目標很明確,實在沒太多時間去研究沁恒電子的USB庫,因此采用了USBD的示例作為模板,進行開發。
由于USBD的工程與STM32的USB庫類似,我選擇深入研究下STM32的USB庫(畢竟資料更多,而且之前學習過)。
2.1 STM32的USB-FS Device Library
UEFI開發探索85中,曾經介紹過如何使用STM32F103C8T6制作HID設備。不過,對于所使用的的USB Library,并沒有討論。
STMF103的USB庫,可以在STSW-STM32121中找到,其應用文檔為UM0424。文檔中給出了非常詳盡的庫說明,如圖3為USB庫的代碼結構。
圖3 USB庫代碼結構
USB-FS-Device 庫主要分為兩層:
STM32_USB-FS_Device_Driver: 驅動層,訪問USB全速設備外圍和USB標準協議,兼容USB2.0標準,與STM32標準庫分離;這層不能由用戶修改;
Applicaton Interface:在庫和最終用戶層之間,提供完成的接口,可以由用戶修改;
驅動層的代碼,大部分情況下是不用修改的,它所包含的源文件說明如下:
USB-FS外圍部件接口:
usb_reg (.h, .c):硬件抽象層 usb_int.c:傳輸中斷服務函數 usb_mem(.h,.c):數據傳輸管理USB-FS設備驅動中間層:
usb_init (.h,.c) :USB設備初始化全局變量 usb_core (.h , .c) :USB協議管理(兼容USB2.0規范第9章) usb_sil (.h,.c) :讀寫端點的簡化函數(USB-FS_Device外圍的抽象層) usb_def.h / usb_type.h:用于庫中的USB定義和類型 platform_config.h:評估板上用到的硬件定義應用層代碼是提供給用戶修改用的,所需要實現的功能都在此層實現。它所包含的源文件說明如下:
usb_conf.h:配置文件 usb_desc (.h, .c):描述符 usb_prop (.h, .c):應用規范屬性 usb_endp.c:非控制端口的傳輸中斷處理函數 usb_istr (.h,.c):中斷處理函數 usb_pwr (.h, .c) :電源和連接管理函數對照CH32F103C8T6提供的USBD例程,可以發現其結構與STM32的是一樣的。可以斷定,它是模仿了STM32的USB Library編寫了自己的庫函數接口。
這種設計方法,對習慣了STM32編程的工程師是非常好的。大部分情況下,可以直接把STM32的示例工程,直接移植到WCH的芯片上來(畢竟STM32的例程還是比較豐富的)。
本篇所實現的USB HID雙向通信,就是參考了STM32的CustomHID例程,在CH32F103的USBD例子上實現的。
2.2 代碼移植和修改
如圖4所示,給出了CH32F103的USBD工程的代碼結構。
圖4 CH32F103的USBD工程代碼
驅動層的代碼完全不用修改。為了確定此事,我對照著STM32的驅動層代碼,一個個函數研究了下,除去與芯片相關的部分,其實現代碼幾乎一致。
所要修改的代碼在應用層,也不是所有源文件需要修改,需要修改的文件包括三個:
usb_desc.c、usb_endp.c和usb_prop.c。
看過我UEFI開發探索和YIE002開發探索兩個系列博客的網友,應該了解之前我使用STM32開發USB HID設備的過程。而且相關的工程代碼,在博客中也提供了(UEFI開發探索85和YIE002開發探索09,前者使用Legacy Library,后者使用Cube Library開發。)。
實際的開發過程,與之前的開發過程類似,只不過由于芯片的不同,有些代碼需要進行移植。
2.2.1 usb_desc.c代碼修改
所要修改的是各種描述符,包括設備描述符、配置描述符、端點描述符等。
需要注意的地方,是CH32F103的最大包長度為8。如下給出了設備描述符和配置描述符等的代碼,其余的代碼與之前開發的STM32F103工程相同,就不再給出了。
#define LOBYTE(x) ((u8)(x & 0x00FF)) #define HIBYTE(x) ((u8)((x & 0xFF00) >>8)) #define?USBD_VID?????????????????????0x8765 #define USBD_PID 0x4321 /*?USB?Device?Descriptors?*/ const uint8_t USBD_DeviceDescriptor[CUSTOMHID_SIZ_DEVICE_DESC] = { 0x12, /*bLength */USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/0x10, /*bcdUSB */0x01,0x00, /*bDeviceClass*/0x00, /*bDeviceSubClass*/0x00, /*bDeviceProtocol*/0x08, /*bMaxPacketSize*/LOBYTE(USBD_VID), /*idVendor (0x1234)*/HIBYTE(USBD_VID),LOBYTE(USBD_PID), /*idProduct = 0x4321*/HIBYTE(USBD_PID),0x00, /*bcdDevice rel. 1.00*/0x01,1, /*Index of string descriptor describingmanufacturer */2, /*Index of string descriptor describingproduct*/3, /*Index of string descriptor describing thedevice serial number */0x01 /*bNumConfigurations*/ }; /* USB Configration Descriptors */ const uint8_t USBD_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] = { 0x09, /* bLength: Configuration Descriptor size */USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */CUSTOMHID_SIZ_CONFIG_DESC,/* wTotalLength: Bytes returned */0x00,0x01, /* bNumInterfaces: 1 interface */0x01, /* bConfigurationValue: Configuration value */0x00, /* iConfiguration: Index of string descriptor describingthe configuration*/0x80, /* bmAttributes: Self powered */0x32, /* MaxPower 100 mA: this current is used for detecting Vbus *//************** Descriptor of Custom HID interface ****************//* 09 */0x09, /* bLength: Interface Descriptor size */USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType: Interface descriptor type */0x00, /* bInterfaceNumber: Number of Interface */0x00, /* bAlternateSetting: Alternate setting */0x02, /* bNumEndpoints */0x03, /* bInterfaceClass: HID */0x00, /* bInterfaceSubClass : 1=BOOT, 0=no boot */0x00, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */0, /* iInterface: Index of string descriptor *//******************** Descriptor of Custom HID HID ********************//* 18 */0x09, /* bLength: HID Descriptor size */HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID */0x10, /* bcdHID: HID Class Spec release number */0x01,0x00, /* bCountryCode: Hardware target country */0x01, /* bNumDescriptors: Number of HID class descriptors to follow */0x22, /* bDescriptorType */CUSTOMHID_SIZ_REPORT_DESC,/* wItemLength: Total length of Report descriptor */0x00,/******************** Descriptor of Custom HID endpoints ******************//* 27 */0x07, /* bLength: Endpoint Descriptor size */USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */0x82, /* bEndpointAddress: Endpoint Address (IN) */0x03, /* bmAttributes: 00: Control endpoint 01: Isochronous endpoint 02: Bulk endpoint 03: Interrupt endpoint */0x40, /* wMaxPacketSize: 64 Bytes max */0x00,0x00, /* bInterval: Polling Interval *//* 34 */0x07, /* bLength: Endpoint Descriptor size */USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: *//* Endpoint descriptor type */0x02, /* bEndpointAddress: *//* Endpoint Address (OUT) */0x03, /* bmAttributes: Interrupt endpoint */0x40, /* wMaxPacketSize: 64 Bytes max */0x00,0x00, /* bInterval: Polling Interval */ };2.2.2 usb_prop.c代碼修改
這是開發中的重點,原有的CHF103的USBD工程中,包括端口0的控制傳輸以及若干USB命令都沒有實現。
簡單來說,就是需要把端口0的控制傳輸代碼實現,以支持各種USB標準命令和USB類命令(主要是HID類)。
2.2.3 usb_endp.c代碼修改
usb_endp.c中主要實現的端點讀寫通信,也即對應上位機的ReadFile()和WriteFile()。
在usb_desc.c中的配置描述符中,包含了端點描述符的內容。我們聲明了端點2的IN和OUT作為讀寫接口。所實現的代碼如下:
uint8_t Receive_Buffer[0xff]; /******************************************************************************* * Function Name : EP2_IN_Callback * Description : Endpoint 2 IN. * Input : None. * Return : None. *******************************************************************************/ void EP2_IN_Callback (void) { } /******************************************************************************* * Function Name : EP2_OUT_Callback * Description : Endpoint 2 IN. * Input : None. * Return : None. *******************************************************************************/ void EP2_OUT_Callback(void) { uint32_t DataLength = 0;DataLength=USB_SIL_Read(EP2_OUT, Receive_Buffer); //讀取端點得到的數據SetEPRxStatus(ENDP2, EP_RX_VALID);if (Receive_Buffer[0] == 0xA0) //將第二個字節改為1返回,表示是采用端點發送的方式{Receive_Buffer[1]=0x1;}USB_SIL_Write(EP2_IN,Receive_Buffer,DataLength);SetEPTxStatus(ENDP2,EP_TX_VALID); }至此,就完成了所有編程工作了。將其編譯下載到CH32F103C8T6的開發板上,就可以進行測試了。
3 .?測試
仍舊使用我之前開發的UsbHID上位機工具進行測試(UEFI開發探索74附帶的測試工具),結果如下:
圖4 CH32F103的USBD工程代碼
從圖5的測試可以看出,三種讀寫方式都實現了。
不得不承認,國產的單片機相比于國外的大廠來說,支持資料做得很不足。不過,從功能上來說,還是會有一些亮點的。比如CH32F103C8T6相比于STM32F103C8T6,3個串口保留了,而且還增加了一個USB HOST。
另外,即便是在正常的情況下(現在芯片短缺屬于不正常狀態),其價格也只有STM32F103C8T6的一半。這對于批量出貨的產品來說,是個不能忽視的優勢。
我相信隨著這波芯片短缺的影響,很多的廠商都會逐漸使用國產單片機了。這種變化,對軟硬件工程師來說,可是個不小的考驗。
開放了個人微信,歡迎大家添加,一起交流學習!
關于替換STM32的文章,之前發了很多,特此匯總,大家可以選擇性閱讀!
STM32漲價?那就用國產32替代吧!
用GD32替代STM32,是什么體驗?
GD32替換STM32,這些細節一定要知道。
在STM32價格瘋長下,哪些國產32可以替代?
GD32如何替換STM32?
STM32和GD32有什么區別?
-END-
我是張巧龍,一名教電子的大學老師,歡迎關注!
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的用国产CH32替代STM32,快来试试看!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 低价保量,消息称宁德时代“锂矿返利”计划
- 下一篇: 这次,带你做一个属于自己的无人机!