現(xiàn)狀
- 基于STM32CubeMX的F103/F40X的USB堆棧測(cè)試完畢;
- 基于Mbed OS的F103/F40X的USB堆棧測(cè)試完畢;
- 主要測(cè)試USB CDC設(shè)備;
- 基于USB ACM/CDC實(shí)現(xiàn)nRF24L01及類(lèi)似“小無(wú)線(xiàn)”系統(tǒng)集成;
- 基于USB ACM/CDC開(kāi)發(fā)了VT100 cmdline
- 基于USB ACM/CDC和cmdline實(shí)現(xiàn)SPI NOR Flash的讀寫(xiě);
- 基于USB ACM/CDC開(kāi)發(fā)HCI定制協(xié)議;
- 基于Linux udev的USB設(shè)備插入拔出時(shí)間的檢測(cè);
計(jì)劃
- 開(kāi)發(fā)其他TLV類(lèi)型二進(jìn)制協(xié)議和基于字符串的JSON RPC等協(xié)議;
- 實(shí)現(xiàn)xmodem傳輸;
- 實(shí)現(xiàn)I2C設(shè)備掃描與訪問(wèn);
- 更新現(xiàn)有的LoRaPHY/Aloha/LoRaWAN USB Dongle;
- 支持C8T6/RCT6等多種核心板,以應(yīng)對(duì)更加復(fù)雜的堆棧;
- 支持USB ECM,以直接支持6LowPAN等物聯(lián)網(wǎng)設(shè)備;
- 集成Arduino STM32的Bootloader實(shí)現(xiàn)固件升級(jí)。
開(kāi)源設(shè)計(jì)與板級(jí)產(chǎn)品
- 大部分設(shè)計(jì)都是開(kāi)源設(shè)計(jì);
- 或有根據(jù)客戶(hù)要求定制進(jìn)行設(shè)計(jì);
代碼設(shè)計(jì)過(guò)程
以下內(nèi)容針對(duì)Mbed C++和STM32F103/F407
今天完成的主要是在USB通道上實(shí)現(xiàn)VT100 cmdline,可以通過(guò)TeraTerm終端來(lái)配置管理設(shè)備,或者通過(guò)專(zhuān)門(mén)的cmd/GUI上位機(jī)程序?qū)崿F(xiàn)自動(dòng)化配置。最早基于C和串口,在Mbed Serial類(lèi)上移植也很容易。但是在USB信道上實(shí)現(xiàn)cmdline很花費(fèi)了一些時(shí)間,且有了反復(fù)。主要原因是USB對(duì)象初始化的特殊性,以及Mbed C++與基于標(biāo)準(zhǔn)庫(kù)或HAL庫(kù)的原始設(shè)計(jì)的差異所造成的。
基于標(biāo)準(zhǔn)庫(kù)或者HAL庫(kù)的模板一般是:
- 將所需硬件資源(串口、GPIO)聲明為main的全局變量
- 將USB聲明為 extern 全局變量
- 在主函數(shù)中配置時(shí)鐘,初始化這些硬件資源
- 展開(kāi)應(yīng)用邏輯
發(fā)現(xiàn)STM32 CubeMX的USB實(shí)例是usb_device.c中的全局變量。 這和一般的硬件資源如GPIO/ADC/PWM/CAN/UART都有所不同。
// Private in main.c
CAN_HandleTypeDef hcan;
RTC_HandleTypeDef hrtc;
UART_HandleTypeDef huart1;
int main(void){
HAL_Init();
SystemClock_Config(); // RCC init before any other resources
MX_GPIO_Init();
MX_CAN_Init();
MX_USART1_UART_Init();
MX_RTC_Init();
MX_USB_DEVICE_Init(); // USB init here
while(1){
...
}
}
main.c
USBD_HandleTypeDef hUsbDeviceFS;
void MX_USB_DEVICE_Init(void)
{
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC);
USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);
USBD_Start(&hUsbDeviceFS);
}
usb_device.c
extern USBD_HandleTypeDef hUsbDeviceFS;
usb_device.h
而基于Mbed C++有些特殊。
DigitalOut myled(DBG_LED); // See main.h for hardware issue
cmdline cmdhandler;
// You can put USBSerial/USBTerminal here, but will not be enumerated in F103
//USBSerial usbSerial(0x1f00, 0x2012, 0x0001, false);
USBTerminal *term;
int main(){
confSysClock(); // RCC init first
Serial uart(PA_9, PA_10, 115200);
//USBSerial usbSerial(0x1f00, 0x2012, 0x0001, false);
USBTerminal usbSerial(0x1f00, 0x2012, 0x0001, false);
term = &usbSerial;
}
main.cpp
USB對(duì)于時(shí)鐘是非常敏感的,所以必須在系統(tǒng)時(shí)鐘配置正確后才能夠產(chǎn)生USB對(duì)象。
在Mbed C++中,在調(diào)用main函數(shù)之前,進(jìn)行時(shí)鐘配置和對(duì)象實(shí)例化。RCC時(shí)鐘配置隱藏在Mbed Library中,如果對(duì)象在Main函數(shù)之外,視為公有對(duì)象,也在main函數(shù)之前進(jìn)行實(shí)例化。
如果將USB對(duì)象作為公有對(duì)象,F(xiàn)407工作正常,而F103工作不正常,表現(xiàn)在枚舉失敗。換而言之,在F407代碼中,可以將USBSerial/USBTerminal在main函數(shù)之外聲明,且工作正常。但是F103代碼中,同樣的代碼,編譯通過(guò),但是枚舉失敗。
所以第三方開(kāi)發(fā)者打了一個(gè)補(bǔ)丁,在main函數(shù)中增加了一個(gè)confSysClock()。有興趣的話(huà),可以查看RCC寄存器的數(shù)值。
由于時(shí)鐘是main函數(shù)中調(diào)用的,間接造成USB對(duì)象(USBSerial及其子類(lèi)USBTerminal)是main函數(shù)中的對(duì)象,其他模塊和函數(shù)無(wú)法訪問(wèn)。
解決方法是在main.cpp中預(yù)留一個(gè)USB對(duì)象指針,讓其他函數(shù)和其他模塊可以訪問(wèn)到USB對(duì)象。代價(jià)是USB對(duì)象的所有方法必須采用“->”來(lái)訪問(wèn)。這也就導(dǎo)致了基于Serial對(duì)象和USB對(duì)象的代碼存在兩套,這實(shí)在違背了OOP的原則。
由此看來(lái),基于Serial對(duì)象,基于F103的USBSerial,基于F407的USBSerial的通道,居然出現(xiàn)了兩套(確切地說(shuō)是2.5套)代碼。這種情況可能同樣會(huì)影響到其他協(xié)議,包括HCI/SIP/TLV/JSON等。
對(duì)于ARM來(lái)說(shuō),USB不是IoT的一部分。他們的IoT/Connectivity主要包括的是Cellular Modem/WiFi/BLE/LoRaWAN/BLE/TLS/MQTT等。
要合并代碼,還需要開(kāi)發(fā)者自己動(dòng)手。要么統(tǒng)一為指針類(lèi)型;要么期待Mbed底層得到修改。然而這些代碼都是基于Mbed 2,而Mbed 5并沒(méi)有對(duì)USB堆棧進(jìn)行維護(hù)。需要開(kāi)發(fā)者自己Backport。代碼在此:ARM Mbed OS STM32F103的系統(tǒng)時(shí)鐘配置代碼