TinyUSB是一個用于嵌入式系統的開源的跨平臺USB協議棧,協議棧中包含了主機端及設備端的協議棧,由于不使用動態內存分配以及采用阻塞所有中斷事件,在非ISR任務功能中處理中斷事件的設計方式,所以此協議棧的內存安全性及線程安全性極高。
源碼是托管在GitHub上面,地址是:https://github.com/hathach/tinyusb。
2TinyUSB基本移植介紹
MM32已基于TinyUSB開發完成相應的參考例程,并可以給客戶提供參考,本次我們介紹的移植將基于此基礎上進行。將TinyUSB從GitHub上克隆到本地,可以得到如下內容:
圖1 TinyUSB源碼
移植TinyUSB到MM32F0160需要添加如下文件:
屬于工程專用文件:
usb_descriptors.c
usb_dcd_port.c
tusb_config.h
usb_descriptors.h
屬于庫內文件:
tusb.c
usbd.c
usbd_control.c
*_device.c
tusb_fifo.c
將src 整個文件夾copy替換到例程components目錄下的src。
將tinyusb 目錄下example下的對應文件,以device uac2_headset為例,將tinyusbexamplesdeviceuac2_headsetsrc 四個文件copy到例程user文件夾里面。
圖2 源碼的device文件
圖3 用戶工程文件
USB時鐘頻率是48MHz,HSE可以經過PLL倍頻到48MHz或96MHz,然后經過分頻到48MHz,注意需要使用外部晶振,如果使用內部時鐘HSI,需要使能時鐘回饋系統CRS功能。
在main.c 增加USB時鐘配置函數void USB_DeviceClockInit(void),同時將board_init();替換成usb時鐘初始化函數,主頻配置96MHz,USB選擇PLL1輸入二分頻到USB。
有使用TU_LOG做串口輸出,可以使能CFG_TUSB_DEBUG為需要的輸出等級,同時將
#define tu_printf printf
改到串口輸出,Keil Options->Target 勾選Use MicroLIB,并實現重定向函數。
//-------------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 接口函數文件,Keil下Options C/C++勾選C99和GNU externsions(tud_dcd_port.c 文件可以參考現有例程或者聯系靈動技術支持)。
圖4 工程設置
移植修改其他設備基本流程和上述一致,將tinyusb 目錄exampledevice 里面將想要修改的設備src文件夾里面四個文件copy到例程user文件夾里面替換。
3修改一個uac2_headset Device設備
在克隆下來的的文件夾examplesdevice里面找到需要修改的device設備,本次修改uac2_headset。將里面的文件都copy到工程USER目錄里面,然后Keil工程按如下文件樹添加對應文件。
圖5 uac2_headset設備描述符文件
文件樹:
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將對應的設備define改成1 ( #define CFG_TUD_AUDIO 1 ) ,使能AUDIO設備。
//-------------CLASS-------------// #defineCFG_TUD_CDC0 #defineCFG_TUD_MSC0 #defineCFG_TUD_HID0 #defineCFG_TUD_MIDI0 #defineCFG_TUD_AUDIO1 #defineCFG_TUD_VENDOR0
按照前面的移植步驟,只需要修改main.c 里面的時鐘初始化部分即可,其他的device設備修改流程一致。
4新增一個設備變成復合設備
USB設備主要四個描述符,分別是設備描述符(Device Descriptors),配置描述符(Configuration Descriptor),報告描述符(Configuration Descriptor)和字符描述符(String Descriptors)。
添加復合設備device文件,本例程在上述3(修改一個uac2_headset Device設備)例程的基礎上增加一個HID設備變成復合設備,首先將工程目錄下的components inyusbsrcclasshidhid_device.c文件添加到工程。
圖6 源碼HID設備參考文件
圖7 添加device文件
在tusb_config.h 文件里面 CLASS 使能HID宏,本例程是復合設備(Audio+HID)所以兩個宏都為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相關函數。
//--------------------------------------------------------------------+ //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(注意:長度一定要和下面DESCRIPTOR對應,否則枚舉會失敗)。
#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對應,然后配置HID IN OUT通訊選擇哪個端點。
#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 增加一個 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的其他處理函數添加到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()函數中添加需要處理的用戶程序。
//--------------------------------------------------------------------+ //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功能驗證測試
完成上述移植,解決基本的編譯問題后燒錄測試能枚舉正常。
圖8 枚舉過程
圖9 枚舉成功
-
嵌入式系統
+關注
關注
41文章
3587瀏覽量
129435 -
接口
+關注
關注
33文章
8575瀏覽量
151015 -
usb
+關注
關注
60文章
7936瀏覽量
264474 -
串口
+關注
關注
14文章
1551瀏覽量
76421 -
GitHub
+關注
關注
3文章
468瀏覽量
16428
原文標題:靈動微課堂 (第263講)|基于MM32F0163D7P的USB接口TinyUSB應用:移植和新增設備(一)
文章出處:【微信號:MindMotion-MMCU,微信公眾號:靈動MM32MCU】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論