串口調(diào)試在項(xiàng)目中被使用越來越多,串口資源的緊缺也變的尤為突出。很多本本人群,更是深有體會(huì),不準(zhǔn)備一個(gè)USB轉(zhuǎn)串口工具就沒辦法進(jìn)行開發(fā)。
了解USB虛擬串口,為了在項(xiàng)目中用一下這個(gè)USB,調(diào)試方便一些,供電可直供。公司以后的產(chǎn)品開發(fā)就基于STM32這個(gè)平臺(tái),從contex_M3到contex-M4。不管速度、功耗、價(jià)格、采購的方便性都有競爭力,不想再修改了(除非它無法滿足要求)。
STM32基本上都帶有USB串口。如果不把它用上而另外加一個(gè)USB轉(zhuǎn)串口單元,未免顯得太落后了而且也是一種資源的浪費(fèi)。順便說一下,根據(jù)公司的狀況,合適的才是好的。以前一直用TI的,從430到ARM到28XX。這個(gè)ST的包含了以前的所有,TI的ARM速度比較慢,而它的普通的DSP的速度與M4相當(dāng),有些還比不過M4,耗電卻驚人,何況M4帶了浮點(diǎn)后比定點(diǎn)在某些地方要快很多。難怪TI在推M4時(shí)故意將頻率放慢,DSP功能減少,我想各個(gè)公司都有自已的考慮吧。而ST就把CMOS傳感器接口也放進(jìn)去。趕明兒啥時(shí)有空了再做塊PCB試試。ST最讓人不爽的是它的開發(fā)例程做的太淺,例如超速USB2。0,CMOS接口資料幾乎沒有,TCP/IP也淺嘗即止,網(wǎng)頁也設(shè)計(jì)得世界上最爛,讓人找個(gè)芯片,找個(gè)資料很累。在應(yīng)用上比NXP差遠(yuǎn)了,畢竟有個(gè)ZLG幫忙,F(xiàn)REESCALE在網(wǎng)絡(luò)通訊方面的應(yīng)用做得最好,看他們的TCP/IP源代碼是一種享受。看來做芯片是ST的強(qiáng)項(xiàng),做應(yīng)用還非常有待加強(qiáng)。
說明:
1.跳過驅(qū)動(dòng)。使用低速傳輸。最大可以設(shè)為921000即100KB/S。應(yīng)該也差不多了。只傳一些簡單的東西。因?yàn)槲覀児ぷ鞯闹攸c(diǎn)是工業(yè)控制,無需高速,而我們是以完成任務(wù)為主要目的。而不是非要學(xué)什么東西,完成任務(wù)才是第一重要的。
2.由于例程中沒有操作系統(tǒng)支持,也許以后可以用keil的操作系統(tǒng)。比較簡單,最重要的是ucOS不支持M4的浮點(diǎn)運(yùn)算。當(dāng)然對(duì)于M3我們可以將USB部分移到ucOS上。而對(duì)于M4,我們直接用KeilOS,不想花很大力氣去做OS移植了。
總是先從main開始
Set_System();///設(shè)置系統(tǒng)
Set_USBClock();///設(shè)置USB時(shí)鐘
USB_Interrupts_Config();///配置USB中斷
USB_Init();///USB初始化
while(1)
{
if((count_out!=0)&&(bDeviceState==CONFIGURED))
{
USB_To_USART_Send_Data(&buffer_out[0],count_out);//如果有數(shù)據(jù)將它發(fā)送到串口中去
count_out=0;/////發(fā)送完后這個(gè)清零
}
}
初看一下,還算比較好理解,但是由于這個(gè)例子好象只有發(fā),沒有收。我想它的收大約在中斷中進(jìn)行的(也就是串口向USB發(fā)的過程,估計(jì)在串口中斷中進(jìn)行,后面我們可以再分析,不行可能需要自行加上這部分代碼,希望不要這樣)
我們還是一條一條的來看,首先看Set_System()這個(gè)函數(shù),如果沒猜錯(cuò)的話,應(yīng)該是設(shè)置時(shí)鐘吧。
果然如此,我們下面一條一條看一下,它先是允許外部晶振---這里哆嗦一下,外部晶振我公司采用12M。而一般開發(fā)版采用8M。所以配置stm32f10x_conf.h文件中,要將外部晶振頻率從8000000改為12000000。
然后,等外部晶振起來,如果晶振沒焊接好,此時(shí)就會(huì)死在這里。如果你一運(yùn)行就死,可找一下這個(gè)地方。
然后它做下列工作:
允許FLASH取指緩沖
FLASH時(shí)鐘分頻2倍-------這是否意味著FLASH的時(shí)鐘是36M呢,記得好象有ST的文章中說FLASH可工作在50M的時(shí)鐘下。
系統(tǒng)頻率HCLK配置成SYSCLK
APB2的時(shí)鐘配置成SYSCLK不分頻
APB1的時(shí)鐘配置成2分頻。但要注意它下面的定時(shí)器2,3,4。。頻率仍是72M因有倍頻
ADC的時(shí)鐘配置成6分頻,即它是12MHz。注意AD需13.5個(gè)時(shí)鐘完成意味著差不多1us可完成一次AD轉(zhuǎn)換。
PLL配置成9倍頻。8X9=72MHz。注意到對(duì)于12MHz就只能是6了。此處一定要注意。
允許PLL然后等PLL都OK了再做別的。--此后由PLL進(jìn)行工作而不是HSI。
然后,我們要允許GPIOA,GPIOB和串口的時(shí)鐘。因?yàn)槲覀冎挥玫搅诉@幾個(gè)資源。當(dāng)然USB是另外的,我想總會(huì)在某個(gè)地方允許的。下面這個(gè)就不好理解了:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT,ENABLE);這個(gè)叫允許USB斷開線。從原理圖上看好象是PE7來控制的。因?yàn)镻E7連接到一個(gè)DP+的一個(gè)上拉電阻控制的三極管上面。請(qǐng)參考原理圖。但是比較奇怪的是在platform_config.h中有下列定義:
#defineRCC_APB2Periph_GPIO_DISCONNECTRCC_APB2Periph_GPIOD
它將這個(gè)設(shè)為端口D。所以這個(gè)USB斷開引腳到底是由誰來控制的還未確定。這里留下一個(gè)問號(hào),等以后再解決。
接下來,將USB的斷開腳配置成上拉的。這也就意味著在開始時(shí),這個(gè)上拉電阻使得三極管導(dǎo)通,從而使這個(gè)DP腳被加了一個(gè)1.5K的電阻,可以開始枚舉的。再看這個(gè)斷開腳指的是哪一個(gè)腳,它不是PD9就是PB14。怎么也沒有PE7的說法(原理圖)。所以這里就有點(diǎn)不知所以然了。難道這個(gè)原理圖與程序有沖突?
接下來配置PA10為輸入浮空,配置PA9為PP輸出。這兩個(gè)腳是串口。這個(gè)是正確的。我們幾孚所有的板子都用UART0,它就是這兩個(gè)腳的。至此這個(gè)部分結(jié)束了。
下面再來看第2個(gè)函數(shù)Set_USBClock()-------兩句話:
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);/*EnableUSBclock*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB,ENABLE);72M除1。5=48然后允許USB的時(shí)鐘。這個(gè)要看一下數(shù)據(jù)手冊(cè)。從數(shù)據(jù)手冊(cè)比較好理解。它是直接從PCLK中除一個(gè)數(shù),可以除1也可以除1.5.。這段還是比較好理解的。
再看第3個(gè)函數(shù)USB_Interrupts_Config()-------配置USB中斷
在該函數(shù)中使能了兩個(gè)中斷,一個(gè)是USB,一個(gè)是串口。至于中斷放在RAM或FLASH當(dāng)然一般是后者,所以在這也就沒有什么意義了。串口優(yōu)先級(jí)為1,USB為0。中斷分組中我們將一位作為可重入的優(yōu)先組,另3位作為優(yōu)先級(jí)組。由于一個(gè)為0一個(gè)為1,故這兩個(gè)中斷都不會(huì)相互被另一個(gè)中斷打斷。
現(xiàn)在既然允許了兩個(gè)中斷,估計(jì)在中斷中將做很多事情,大多數(shù)事情都在中斷中完成的。這個(gè)中斷程序等一會(huì)馬上就來解讀。最后是USB_Init()這個(gè)函數(shù)了。在這是里先看到:
pInformation=&Device_Info;這個(gè)是設(shè)備資料部分,里面的全部變量,靜態(tài)的。pInformation-》ControlState=2;
pInformation是一個(gè)指向Device_Info的指針。不知為什么要這樣大費(fèi)周折。不可以直接這樣寫嗎:
Device_Info.ControlState=2
接下來:
pProperty=&Device_Property;
pUser_Standard_Requests=&User_Standard_Requests;pProperty是一個(gè)指針,指向DEVICE_PROP這個(gè)是設(shè)備屬性部分
設(shè)備屬性部分包含一些方法,即函數(shù)。也包括兩個(gè)參數(shù),一個(gè)是接收區(qū)的緩沖區(qū)地址,一個(gè)是最大的包的長度。都是用字節(jié)表示的。
這個(gè)屬性是一個(gè)通用的屬性,它指以相當(dāng)于實(shí)例化套到USB這個(gè)頭上去。故我們?cè)谶@里看上去運(yùn)行一個(gè)pProperty-》Init();實(shí)際上運(yùn)行的是DEVICE_PROP中的Virtual_Com_Port_init()這個(gè)函數(shù)。我們看在這個(gè)函數(shù)中做了什么:
Get_SerialNum();設(shè)置芯片序列號(hào),將描述符中的例如STM等字符串修改沒太大意義。
PowerOn(void)先使能芯片(三極管B極變高)強(qiáng)迫USB復(fù)位。再將全部USB中斷都屏蔽后,將中斷清除,最后再允許以下中斷:CNTR_RESETM|CNTR_SUSPM|CNTR_WKUPM;
接下來,再一次清除中斷標(biāo)志,然后使能中斷(CNTR_CTRM|CNTR_SOFM|CNTR_RESETM)這幾個(gè)中斷就是復(fù)位中斷,正確傳輸中斷,SOF中斷
配置串口至缺省狀態(tài)---在這里波特率被設(shè)為9600,并且允許了接收中斷。發(fā)送中斷沒有允許。
將當(dāng)前的狀態(tài)定義為未連接狀態(tài)。bDeviceState=UNCONNECTED;什么時(shí)候連接不知道。
至此,初始化結(jié)束。我們現(xiàn)在要看的是中斷函數(shù)了。中斷函數(shù)不外于一個(gè)是串口的接收中斷。串口的發(fā)送中斷是沒有允許的。
串口是如何發(fā)送的呢?它直接寫串口寄存器,顯然如果有大量數(shù)據(jù)發(fā)送時(shí)就會(huì)出問題的。因?yàn)樗静慌袛嗍欠癜l(fā)送緩沖區(qū)為空。因此,感覺這個(gè)程序要進(jìn)行大量的數(shù)據(jù)交互不可能,最多是敲打一下鍵盤可能還差不多。
看它的庫中的函數(shù):直接發(fā)送,也不管是否空。難道它有16字節(jié)緩沖嗎?沒有。voidUSART_SendData(USART_TypeDef*USARTx,u16Data){
/*TransmitData*/
USARTx-》DR=(Data&(u16)0x01FF);}
先從簡單的看起,串口的接收中斷,它在哪里呢?發(fā)現(xiàn)它在stm32f10x_it.c中有如下:
USART_To_USB_Send_Data();表示從串口向USB端發(fā)送數(shù)據(jù)。
再看這個(gè)定義如下:
voidUSART_To_USB_Send_Data(void){
if(USART_InitStructure.USART_WordLength==USART_WordLength_8b){
buffer_in[count_in]=USART_ReceiveData(USART1)&0x7F;}
elseif(USART_InitStructure.USART_WordLength==USART_WordLength_9b){
buffer_in[count_in]=USART_ReceiveData(USART1);}
count_in++;
UserToPMABufferCopy(buffer_in,ENDP1_TXADDR,count_in);SetEPTxCount(ENDP1,count_in);SetEPTxValid(ENDP1);}
輸入的數(shù)據(jù)長度++即count_in++
將收到的數(shù)據(jù)拷貝到端口1的發(fā)送緩沖區(qū)中。設(shè)置發(fā)送緩沖區(qū)的長度
發(fā)送數(shù)據(jù),它是從ENDP1發(fā)送。
我相信,發(fā)送完后,這個(gè)count_in會(huì)被清零。
果然,在EP1_IN_Callback()函數(shù)中,它被清零。
最后,我們就來看一下USB的中斷。USB的中斷入口有一個(gè)還是多個(gè)?它有一個(gè)高優(yōu)先級(jí)中斷和一個(gè)低優(yōu)先級(jí)中斷。應(yīng)該只用其中一個(gè)。
看程序中有下列:
voidUSB_LP_CAN_RX0_IRQHandler(void){
USB_Istr();}
這個(gè)說明,在程序中將USB設(shè)為相對(duì)低的優(yōu)先級(jí)中斷。回想起我們好象在什么地方這樣設(shè)過?果然,在USB_Interrupts_Config()中,有這么一段:
NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN_RX0_IRQChannel;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_Init(&NVIC_InitStructure);
所以所有的中斷都是進(jìn)到這個(gè)中斷中去了。趕緊看一看,這個(gè)是怎么處理的?它被發(fā)現(xiàn)在usb_istr.c中。我想所有的最麻煩的部分就是比較精彩的部分應(yīng)該就在這里了(對(duì)初學(xué)者)下面我們就來分析這個(gè)部分,分析完之后就可以回去了,做完今天的工作。因?yàn)榫剩钥截愒谙旅媪耍?/p>
voidUSB_Istr(void){
wIstr=_GetISTR();
#if(IMR_MSK&ISTR_RESET)
if(wIstr&ISTR_RESET&wInterrupt_Mask){
_SetISTR((u16)CLR_RESET);Device_Property.Reset();#ifdefRESET_CALLBACK
RESET_Callback();#endif}#endif
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
#if(IMR_MSK&ISTR_DOVR)
if(wIstr&ISTR_DOVR&wInterrupt_Mask){
_SetISTR((u16)CLR_DOVR);#ifdefDOVR_CALLBACK
DOVR_Callback();#endif}#endif
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
#if(IMR_MSK&ISTR_ERR)
if(wIstr&ISTR_ERR&wInterrupt_Mask){
SetISTR((u16)CLR_ERR);#ifdefERR_CALLBACK
ERR_Callback();#endif}#endif
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/#if(IMR_MSK&ISTR_WKUP)
if(wIstr&ISTR_WKUP&wInterrupt_Mask){
_SetISTR((u16)CLR_WKUP);Resume(RESUME_EXTERNAL);#ifdefWKUP_CALLBACK
WKUP_Callback();#endif}#endif
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/#if(IMR_MSK&ISTR_SUSP)
if(wIstr&ISTR_SUSP&wInterrupt_Mask){
/*checkifSUSPENDispossible*/if(fSuspendEnabled){
Suspend();}else{
/*ifnotpossiblethenresumeafterxxms*/Resume(RESUME_LATER);}
/*clearoftheISTRbitmustbedoneaftersettingofCNTR_FSUSP*/_SetISTR((u16)CLR_SUSP);#ifdefSUSP_CALLBACK
SUSP_Callback();#endif}#endif
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/#if(IMR_MSK&ISTR_SOF)
if(wIstr&ISTR_SOF&wInterrupt_Mask){
_SetISTR((u16)CLR_SOF);
bIntPackSOF++;
#ifdefSOF_CALLBACK
SOF_Callback();#endif}#endif
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/#if(IMR_MSK&ISTR_ESOF)
if(wIstr&ISTR_ESOF&wInterrupt_Mask){
_SetISTR((u16)CLR_ESOF);
/*resumehandlingtimingismadewithESOFs*/
Resume(RESUME_ESOF);/*requestwithoutchangeofthemachinestate*/#ifdefESOF_CALLBACK
ESOF_Callback();#endif}#endif
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/#if(IMR_MSK&ISTR_CTR)
if(wIstr&ISTR_CTR&wInterrupt_Mask){
/*servicingoftheendpointcorrecttransferinterrupt*//*clearoftheCTRflagintothesub*/CTR_LP();
#ifdefCTR_CALLBACK
CTR_Callback();#endif}#endif
}/*USB_Istr*/
下面象城管一樣,逐一分拆每戶。
wIstr=_GetISTR();得到中斷的原因,這個(gè)根本不是函數(shù),而是得到ISTR的值。
由于我們沒有外掛復(fù)位,故外掛的復(fù)位就不進(jìn)行了。我們只處理這個(gè):voidVirtual_Com_Port_Reset(void),這個(gè)函數(shù)在usb_prop.c這個(gè)文件中。它的目的是恢復(fù)上電時(shí)的缺省設(shè)置。這個(gè)我們就不去深究了。因?yàn)楹孟笠矝]有必要。
首先將全局變量pInformation(它定義在初始化中usb_init.c)中的配置值置為0表示設(shè)備還沒配置過。(這個(gè)變量猜想應(yīng)該在枚舉之類的地方用于判斷是否已枚舉過)其次將當(dāng)前的特征值賦值Virtual_Com_Port_ConfigDescriptor[7]。(含義先不管它)然后再將當(dāng)前的通訊口設(shè)為端口0即pInformation-》Current_Interface=0;端口0大約就是控制口吧。
接下來設(shè)置緩沖表的地址或寄存器為00。這個(gè)我們暫不管它含義是什么放一邊去。
再接下來,初始化三個(gè)端口,它們是端口0,1和2。其中端口0是控制口,端口1是發(fā)送口,端口3是接收口。
在.h中定義了緩沖區(qū)表的基地址為00,而端口0收為0x40,端口0的發(fā)為0x80,端口1的發(fā)地址為0xC0.端口2的發(fā)地址為0x100.端3的收地址為0x110.可以看到其緩沖區(qū)端口2的為16字節(jié),其它的都為64字節(jié)。感覺ST公司太節(jié)省了點(diǎn)吧。這么短的包,如果用480M的超速的話怎么夠?
最后一句:bDeviceState=ATTACHED;表示USB進(jìn)入一個(gè)新狀態(tài)。
接下來,看中斷是否響應(yīng)DMA上溢下溢,錯(cuò)誤處理,喚醒、掛起中斷我們都沒有用到,故全部不看它。現(xiàn)在主要要看的一個(gè)終于出現(xiàn)了,它就是我們?nèi)齻€(gè)要響應(yīng)的中斷之一(三個(gè)中斷分別是復(fù)位中斷,幀頭SOF中斷,正確收發(fā)中斷)SOF中斷。這個(gè)表示幀的起始中斷。不過這個(gè)中斷的處理卻是異常的簡單,就是將這個(gè)SOF標(biāo)志清除后再bIntPackSOF++;即可
當(dāng)然最重要的總是在總后的,接下來的一個(gè)中斷可要費(fèi)點(diǎn)周章了。它就是正確的收發(fā)到數(shù)據(jù)的中斷,它調(diào)用了CTR_LP()函數(shù),這個(gè)函數(shù)它定義在usb_int.c中。我們重點(diǎn)解讀它:這個(gè)程序名為低優(yōu)先級(jí)正確接收中斷
★首先這個(gè)中斷是一個(gè)循環(huán),它一直在等ISTR_CTR==0為止。因?yàn)楫?dāng)它等于0時(shí),表示里面的數(shù)據(jù)已經(jīng)全部取完。沒取完它是不會(huì)罷休的。就象強(qiáng)盜進(jìn)了金庫要將它搬空為止,結(jié)果是阿里巴巴勝出一樣。
★清除這個(gè)CTR標(biāo)志位,為了這個(gè),我們?nèi)タ匆幌聰?shù)據(jù)手冊(cè),慶幸是中文的看得快一點(diǎn)(如果老是這么想,也許是不幸的開始)。發(fā)現(xiàn)CTR標(biāo)志位只是一個(gè)只讀的位,要清除它,它能是去清除USB_EpnR中的對(duì)應(yīng)位。所以為什么用一個(gè)while()的原因是中斷一個(gè)處理完后,可能還有其它的中斷未處理完,如果是這樣的話,這個(gè)CTR位就一直是高電平。可是程序中卻將ISTR的CTR位清除(在數(shù)據(jù)手冊(cè)中它被說明為只讀位)難道這是ST的一個(gè)小失誤?別人不信,反正我是信了。
★根據(jù)端點(diǎn)的ID號(hào)(ISTR寄存器)決定它是控制端點(diǎn)0的響應(yīng)呢還是其它端點(diǎn)的響應(yīng)。原來控制端點(diǎn)0的響應(yīng)在這里,估計(jì)枚舉就在這里進(jìn)行的吧。不過枚舉過程我暫不想看,因?yàn)槲蚁嘈臩T會(huì)把所有的過程都給搞定的。讀程序時(shí),如果過分的分支再分支,最后就一無所有,有時(shí)要反復(fù)4~5遍才知道,如此就只好先舍去一些確定性的,象兩平行線一定不相交的證明就不要看了。先看與要達(dá)到的目的密切相關(guān)的才行。如果要看枚舉過程,“圈圈的教我玩USB”寫得非常好,完全是由淺入深,建議買這本書看一看(贊一個(gè),盡管不很深入,談到教書育人,比清華的教授要強(qiáng)得多了,某些教授就是只會(huì)騙國家經(jīng)費(fèi),找學(xué)生做事,大學(xué)搞出來的科研成果99%沒有價(jià)值)。
★重點(diǎn)看其它端點(diǎn)的響應(yīng)中斷由于前面我們已經(jīng)得知了ID號(hào),我們就到對(duì)應(yīng)的端點(diǎn)寄存器中去找,即臂如是端點(diǎn)2的響應(yīng),我們就到端點(diǎn)2的USB_EP2R中去找。看它是發(fā)送中斷還是接收中斷。它是B15位就是它的CTR_RX,如果不等于0說明它就是該端點(diǎn)的接收中斷。
★接收中斷處理的過程:
_ClearEP_CTR_RX(EPindex);///清除這個(gè)接收標(biāo)志
(*pEpInt_OUT[EPindex-1])();///調(diào)用相應(yīng)的接收中斷的處理函數(shù)
注意這個(gè)函數(shù)數(shù)組的用法。它的定義如下:void(*pEpInt_OUT[7])(void)={
EP1_OUT_Callback,EP2_OUT_Callback,EP3_OUT_Callback,。。。
};回憶一下,大學(xué)C語言學(xué)過的函數(shù)的定義:void*function(void)學(xué)的時(shí)候沒用功吧。其實(shí),要是我來做的話,還不如用幾個(gè)if語句來得簡明。
★所幸,我們?cè)谶@里只用到兩個(gè)回調(diào)函數(shù),只需看2個(gè)即可,一個(gè)是EP1_IN_Callback()另一個(gè)是EP3_OUT_Callback()
而EP1這個(gè),只是執(zhí)行這么簡單的一句:count_in=0;這個(gè)是當(dāng)串口向USB發(fā)時(shí)時(shí),串口的數(shù)據(jù),在串口中斷中已經(jīng)做了處理。它就是我們前面看過的:
buffer_in[count_in]=USART_ReceiveData(USART1);count_in++;
UserToPMABufferCopy(buffer_in,ENDP1_TXADDR,count_in);SetEPTxCount(ENDP1,count_in);SetEPTxValid(ENDP1);
如果串口上我們連一個(gè)鍵盤,當(dāng)敲打它時(shí),就產(chǎn)生了串口中斷,這個(gè)中斷中將數(shù)據(jù)接收好,然后拷到緩沖區(qū)中(這是一個(gè)64字節(jié)的緩沖區(qū))然后只需設(shè)置EP1的長度,開始發(fā)送就可以了。注意到這個(gè)發(fā)送字節(jié)的個(gè)數(shù)的寄存器在緩沖區(qū)中的某個(gè)地方。它在[USB_BTABLE]+n×16+4處。這點(diǎn)還請(qǐng)參考數(shù)據(jù)手冊(cè)。
SetEPTxValid(ENDP1)這個(gè)函數(shù)還有點(diǎn)煩。要是將它全面解剖開來,就有下列東東:(為簡化起見,中間的一些變量我用實(shí)際的數(shù)表表示了)#define_SetEPTxStatus(1,0x0030){\////我們這里是將兩位都置為11
registeru16_wRegVal;\
_wRegVal=_GetENDPOINT(1)&EPTX_DTOGMASK;\///這個(gè)數(shù)就是0x0030/*togglefirstbit?*/\if((EPTX_DTOG1&wState)!=0)\///如果原來的值不為0_wRegVal^=EPTX_DTOG1;
\///異或一下即原來如果為0則變?yōu)?而如果已經(jīng)是1了就變?yōu)?---已經(jīng)為1就不能再發(fā)了
/*togglesecondbit?*/\if((EPTX_DTOG2&wState)!=0)\///第5位也是如此做_wRegVal^=EPTX_DTOG2;\_SetENDPOINT(bEpNum,_wRegVal);\}
于我我們就知道,如果原來的STAT_TX[1:0](位于第5,4位就是0x0030處)就是為00的,則我們就置這個(gè)STAT_TX[1:0]=11發(fā)送就成功了。而如果原來就是11,則置為00。意味著發(fā)送就失敗了。原來為01就變?yōu)?0,原來為10就變?yōu)?1。而01代表的是STALL。10代表的是NAK。具體為什么要這樣,這個(gè)STALL,NAK在USB中的含義到現(xiàn)在有點(diǎn)模糊,可參考圈圈的書。但精確的在這里的含義還有待以后再弄清楚
STM32 USB轉(zhuǎn)串口
1、適用于stm32f10xxe系列。
2、本代碼為從iNEMO? module STEVAL-MKI062V2中提取出來,使用芯片為STM32F103RE。外圍設(shè)備如下:
3、使用。IDE為IAR,在main函數(shù)里完成相應(yīng)的初始化函數(shù),就可以輸出數(shù)據(jù)到端口(詳見代碼)。前提是要裝好驅(qū)動(dòng)文件。
4、輸出效果。
評(píng)論
查看更多