TinyUSB是一個(gè)用于嵌入式系統(tǒng)的開源的跨平臺(tái)USB協(xié)議棧,協(xié)議棧中包含了主機(jī)端及設(shè)備端的協(xié)議棧,由于不使用動(dòng)態(tài)內(nèi)存分配以及采用阻塞所有中斷事件,在非ISR任務(wù)功能中處理中斷事件的設(shè)計(jì)方式,所以此協(xié)議棧的內(nèi)存安全性及線程安全性極高。
源碼是托管在GitHub上面,地址是:https://github.com/hathach/tinyusb。

2TinyUSB基本移植介紹
MM32已基于TinyUSB開發(fā)完成相應(yīng)的參考例程,并可以給客戶提供參考,本次我們介紹的移植將基于此基礎(chǔ)上進(jìn)行。將TinyUSB從GitHub上克隆到本地,可以得到如下內(nèi)容:

圖1 TinyUSB源碼
移植TinyUSB到MM32F0160需要添加如下文件:
屬于工程專用文件:
usb_descriptors.c
usb_dcd_port.c
tusb_config.h
usb_descriptors.h
屬于庫內(nèi)文件:
tusb.c
usbd.c
usbd_control.c
*_device.c
tusb_fifo.c
將src 整個(gè)文件夾copy替換到例程components目錄下的src。
將tinyusb 目錄下example下的對(duì)應(yīng)文件,以device uac2_headset為例,將tinyusbexamplesdeviceuac2_headsetsrc 四個(gè)文件copy到例程user文件夾里面。

圖2 源碼的device文件

圖3 用戶工程文件
USB時(shí)鐘頻率是48MHz,HSE可以經(jīng)過PLL倍頻到48MHz或96MHz,然后經(jīng)過分頻到48MHz,注意需要使用外部晶振,如果使用內(nèi)部時(shí)鐘HSI,需要使能時(shí)鐘回饋系統(tǒng)CRS功能。
在main.c 增加USB時(shí)鐘配置函數(shù)void USB_DeviceClockInit(void),同時(shí)將board_init();替換成usb時(shí)鐘初始化函數(shù),主頻配置96MHz,USB選擇PLL1輸入二分頻到USB。
有使用TU_LOG做串口輸出,可以使能CFG_TUSB_DEBUG為需要的輸出等級(jí),同時(shí)將
#define tu_printf printf
改到串口輸出,Keil Options->Target 勾選Use MicroLIB,并實(shí)現(xiàn)重定向函數(shù)。
//-------------clockinitial-------------//
voidUSB_DeviceClockInit(void)//HSE96M
{
/*SelectUSBCLKsource*/
RCC->CFGR&=~(1<19);???????????//USB?CLK?SEL?PLL1
????RCC->CFGR&=~(0x03<22);
????RCC->CFGR|=0x01<22;
????RCC_AHBPeriphClockCmd(RCC_AHBENR_USB,?ENABLE);
}
//-------------?MAIN?-------------//
int?main(void)
{
????USB_DeviceClockInit();?????????????//board_init();
????CONSOLE_Init(460800);??????????????//enable?printf?debug
????//init?device?stack?on?configured?roothub?port
????tud_init(BOARD_TUD_RHPORT);
????TU_LOG1("Headset?running
");?//CFG_TUSB_DEBUG?for?debugging?#if?CFG_TUSB_DEBUG
//0?:?no?debug
//1?:?print?error
//2?:?print?warning
//3?:?print?info
????while?(1)
????{
????????tud_task();????????????????????//tinyusb?device?task
????????led_blinking_task();
????????audio_task();
????}
}
添加tud_dcd_port.c 接口函數(shù)文件,Keil下Options C/C++勾選C99和GNU externsions(tud_dcd_port.c 文件可以參考現(xiàn)有例程或者聯(lián)系靈動(dòng)技術(shù)支持)。

圖4 工程設(shè)置
移植修改其他設(shè)備基本流程和上述一致,將tinyusb 目錄exampledevice 里面將想要修改的設(shè)備src文件夾里面四個(gè)文件copy到例程user文件夾里面替換。
3修改一個(gè)uac2_headset Device設(shè)備
在克隆下來的的文件夾examplesdevice里面找到需要修改的device設(shè)備,本次修改uac2_headset。將里面的文件都copy到工程USER目錄里面,然后Keil工程按如下文件樹添加對(duì)應(yīng)文件。

圖5 uac2_headset設(shè)備描述符文件
文件樹:
1.TinyUSB_UAC
2. │
3. ├─USER
4. │ main.c
5. │ usb_descriptors.c
6. │ usb_dcd_port.c
7. │
8. └─TinyUSB
9. tusb.c
10. audio_device.c
11. tud_fifo.c
12. usbd.c
13. usb_control.c
在tusb_config.h文件里面CLASS將對(duì)應(yīng)的設(shè)備define改成1 ( #define CFG_TUD_AUDIO 1 ) ,使能AUDIO設(shè)備。
//-------------CLASS-------------// #defineCFG_TUD_CDC0 #defineCFG_TUD_MSC0 #defineCFG_TUD_HID0 #defineCFG_TUD_MIDI0 #defineCFG_TUD_AUDIO1 #defineCFG_TUD_VENDOR0
按照前面的移植步驟,只需要修改main.c 里面的時(shí)鐘初始化部分即可,其他的device設(shè)備修改流程一致。
4新增一個(gè)設(shè)備變成復(fù)合設(shè)備
USB設(shè)備主要四個(gè)描述符,分別是設(shè)備描述符(Device Descriptors),配置描述符(Configuration Descriptor),報(bào)告描述符(Configuration Descriptor)和字符描述符(String Descriptors)。
添加復(fù)合設(shè)備device文件,本例程在上述3(修改一個(gè)uac2_headset Device設(shè)備)例程的基礎(chǔ)上增加一個(gè)HID設(shè)備變成復(fù)合設(shè)備,首先將工程目錄下的components inyusbsrcclasshidhid_device.c文件添加到工程。

圖6 源碼HID設(shè)備參考文件

圖7 添加device文件
在tusb_config.h 文件里面 CLASS 使能HID宏,本例程是復(fù)合設(shè)備(Audio+HID)所以兩個(gè)宏都為1。
//-------------CLASS-------------// #defineCFG_TUD_CDC0 #defineCFG_TUD_MSC0 #defineCFG_TUD_HID1 #defineCFG_TUD_MIDI0 #defineCFG_TUD_AUDIO1 #defineCFG_TUD_VENDOR0
在usb_descriptors.c 文件里面添加HID的描述符,增加HID Report Descriptor相關(guān)函數(shù)。
//--------------------------------------------------------------------+
//HIDReportDescriptor
//--------------------------------------------------------------------+
uint8_tconstdesc_hid_report[]=
{
TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_EP_BUFSIZE)
};
//InvokedwhenreceivedGETHIDREPORTDESCRIPTOR
//Applicationreturnpointertodescriptor
//Descriptorcontentsmustexistlongenoughfortransfertocomplete
uint8_tconst*tud_hid_descriptor_report_cb(uint8_titf)
{
(void)itf;
return(desc_hid_report);
}
在usb_descriptors.c 文件里面添加HID Descriptor length(注意:長(zhǎng)度一定要和下面DESCRIPTOR對(duì)應(yīng),否則枚舉會(huì)失敗)。
#defineCONFIG_TOTAL_LEN(TUD_CONFIG_DESC_LEN+CFG_TUD_AUDIO*TUD_AUDIO_HEADSET_STEREO_DESC_LEN+TUD_HID_INOUT_DESC_LEN)
在usb_descriptors.c 文件里面添加HID描述符文件,例程使用的是TUD_HID_INOUT_DESCRIPTOR,和上述CONFIG_TOTAL_LEN里面的TUD_HID_INOUT_DESC_LEN對(duì)應(yīng),然后配置HID IN OUT通訊選擇哪個(gè)端點(diǎn)。
#defineEPNUM_HID0x03
uint8_tconstdesc_configuration[]=
{
//Interfacecount,stringindex,totallength,attribute,powerinmA
TUD_CONFIG_DESCRIPTOR(1,ITF_NUM_TOTAL,0,CONFIG_TOTAL_LEN,TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,100),
//Interfacenumber,stringindex,EPOut&EPInaddress,EPsize
TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2,EPNUM_AUDIO_OUT,EPNUM_AUDIO_IN|0x80),
//Interfacenumber,stringindex,protocol,reportdescriptorlen,EPOut&Inaddress,size&pollinginterval
TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID,6,HID_ITF_PROTOCOL_NONE,sizeof(desc_hid_report),EPNUM_HID,0x80|EPNUM_HID,CFG_TUD_HID_EP_BUFSIZE,10),
};
在usb_descriptors.c 文件里面添加 HID string字符串。
//arrayofpointertostringdescriptors
charconst*string_desc_arr[]=
{
(constchar[]){0x09,0x04},//0:issupportedlanguageisEnglish(0x0409)
"TinyUSB",//1:Manufacturer
"TinyUSBheadset",//2:Product
"000001",//3:Serials,shouldusechipID
"TinyUSBSpeakers",//4:AudioInterface
"TinyUSBMicrophone",//5:AudioInterface
"TinyUSBHID",//6:HIDInterface
};
在usb_descriptors.h 文件里面ITF_NUM_TOTAL 增加一個(gè) ITF_NUM_HID。
enum
{
ITF_NUM_AUDIO_CONTROL=0,
ITF_NUM_AUDIO_STREAMING_SPK,
ITF_NUM_AUDIO_STREAMING_MIC,
ITF_NUM_HID,
ITF_NUM_TOTAL
};
在main.c 里面增加hid_task(); 然后將HID的其他處理函數(shù)添加到main.c。
/*-------------MAIN-------------*/
intmain(void)
{
USB_DeviceClockInit();//board_init();
CONSOLE_Init(460800);//enableprintfdebug
//initdevicestackonconfiguredroothubport
tud_init(BOARD_TUD_RHPORT);
TU_LOG1("UAC2Headset&HIDrunning
");///CFG_TUSB_DEBUGfordebugging#ifCFG_TUSB_DEBUG
//0:nodebug
//1:printerror
//2:printwarning
//3:printinfo
while(1)
{
tud_task();//TinyUSBdevicetask
audio_task();
hid_task();
}
return0;
}
在hid_task()函數(shù)中添加需要處理的用戶程序。
//--------------------------------------------------------------------+
//USBHID
//--------------------------------------------------------------------+
uint8_thid_report_data[64];
staticvoidsend_hid_report(uint8_treport_id,uint32_tbtn)
{
//skipifhidisnotreadyyet
if(!tud_hid_ready())return;
switch(report_id)
{
caseREPORT_ID_MOUSE:
{
int8_tconstdelta=5;
//nobutton,right+down,noscroll,nopan
if(btn)
{
tud_hid_mouse_report(REPORT_ID_MOUSE,0x00,delta,delta,0,0);
}
}
break;
default:break;
}
}
//Every10ms,wewillsent1reportforeachHIDprofile(keyboard,mouseetc..)
//tud_hid_report_complete_cb()isusedtosendthenextreportafterpreviousoneiscomplete
voidhid_task(void)
{
uint32_tconstbtn=1u;
//Remotewakeup
if(tud_suspended()&&btn)
{
//Wakeuphostifweareinsuspendmode
//andREMOTE_WAKEUPfeatureisenabledbyhost
tud_remote_wakeup();
}else
{
//Sendthe1stofreportchain,therestwillbesentbytud_hid_report_complete_cb()
send_hid_report(REPORT_ID_MOUSE,btn);
}
}
5功能驗(yàn)證測(cè)試
完成上述移植,解決基本的編譯問題后燒錄測(cè)試能枚舉正常。

圖8 枚舉過程

圖9 枚舉成功
-
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3728瀏覽量
133426 -
接口
+關(guān)注
關(guān)注
33文章
9491瀏覽量
156581 -
usb
+關(guān)注
關(guān)注
60文章
8410瀏覽量
282967 -
串口
+關(guān)注
關(guān)注
15文章
1614瀏覽量
82306 -
GitHub
+關(guān)注
關(guān)注
3文章
486瀏覽量
18497
原文標(biāo)題:靈動(dòng)微課堂 (第263講)|基于MM32F0163D7P的USB接口TinyUSB應(yīng)用:移植和新增設(shè)備(一)
文章出處:【微信號(hào):MindMotion-MMCU,微信公眾號(hào):靈動(dòng)MM32MCU】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
NXP i.MX RT1060移植TinyUSB:快速擴(kuò)展大量USB Class示例
如何在ESP32S3上使用TinyUSB,配置為Vendor Class設(shè)備?
stm32103VB tinyusb 使用異常,無法識(shí)別:設(shè)備描述符請(qǐng)求失敗怎么解決?
使用ESP32S3和TinyUSB如何配置為Vendor Class設(shè)備?
USB_DFU_IAP例程移植
介紹攝像頭采集顯示的例程
基于CubeMX-STM32F302_uCOSII例程移植
如何對(duì)TinyUSB上游倉(cāng)庫進(jìn)行移植
如何移植FPGA的例程
基于MM32F0163D7P的USB接口TinyUSB應(yīng)用:移植和新增設(shè)備(二)
TinyUSB基本介紹和移植例程
評(píng)論