上一篇文章解讀了CYPRESS FX3的GPIO的操作過程,下面解讀同步FIFO的一個例子(slaveFifoSync)。
*生產者,消費者。
1、首先看DMA的回呼函數(cyu3dma.h):
typedef void (*CyU3PDmaCallback_t) (
CyU3PDmaChannel *handle, /* Handle to the DMA channel. */
CyU3PDmaCbType_t type, /* The type ofcallback notification being generated. */
CyU3PDmaCBInput_t *input /* Union that contains data related to the notification.
The input parameter willbe a pointer to a CyU3PDmaBuffer_t
variable in the cases where the callback type is CY_U3P_DMA_CB_RECV_CPLTor CY_U3P_DMA_CB_PROD_EVENT. */
);
根據其說明,解讀如下:
1. 對每一個DMA通道,回呼函數必須被註冊。如果沒有註冊或者相應的通知事件沒有被註冊,則回呼函數不會被執行。
2. 回呼函數不能被阻塞。即不能用SLEEP()之類的函數。如果資料需要處理,必須在回呼函數之外。
3. 在生產者事件中,應用希望儘可能快地處理輸入的資料。如果緩衝的處理不能在規定的時間內完成,則輸入的可能是陳舊的資料。在自動訊號通道中,輸入參量指向最新的資料。如果處理延時,生產者socket可能覆蓋部分資料。
4. 在手動或手動IN通道模式時,輸入參量指向第一個緩衝(用於去消費者socket).如果在第二次調用時,這個緩衝仍沒有被處理,輸入參量中將是被陳舊的資料。如果資料處理必須在通道中做, CyU3PDmaChannelGetBuffer函數必須被應用,而回呼函數必須作為一個通知。
2、而輸入指標input的定義如下:
typedef struct CyU3PDmaBuffer_t
{
uint8_t *buffer; /* Pointer to the buffer */
uint16_t count; /* Byte count of valid data in buffer */
uint16_t size; /* Buffer size */
uint16_t status; /* Buffer status. This is a four bit data field…
} CyU3PDmaBuffer_t;
CyU3PDmaChannel這個結構中包含20個左右的參數,其中含回呼函數。
1> .定義了一個全域變數:CyBool_t glIsApplnActive=CyFalse; 這個變數是一個BOOL型先設為FALSE.
2> .程式然後定義了一個錯誤處理,CyFxAppErrorHandler()我們不處理錯誤,故是一個死迴圈語句。
3>. 然後,定義一個CyFxSlFifoApplnDebugInit()用串口來顯示一些資訊。初始化串口,設傳輸速率—只允許發不允許收,另外,採用DMA模式來處理UART。
*注意到這個函數:CyU3PDebugInit(CY_U3P_LPP_SOCKET_UART_CONS,8)表示只處理8以下的顯示,大於8將不顯示。
3、 接下來就是兩個回調處理函數
1>. DMA回呼函數 ,生產者----->消費者
CyFxSlFifoUtoPDmaCallback(
CyU3PDmaChannel *chHandle,
CyU3PDmaCbType_t type,
CyU3PDmaCBInput_t *input)
{
CyU3PReturnStatus_t status =CYU3P_SUCESS;
If(type== CY_U3P_DMA_CB_PROD_EVENT){
Status=
CyU3PDmaChannelCommitBuffer(chHandler,input->buff_p.count,0);
glDMARxCount++;}
}
其中,CommitBuffer這個函數通常在手動DMA方式下被調用,它3個參數的含義分別為:DMA的控制代碼號,處理的位元組數及當前的狀態。其中地址由通道描述符隱含著。這個函數發送一個buffer向消費者socket.
2>.DMA 回呼函數 ,消費者----->生產者
CyFxSlFifoPtoUDmaCallback (
CyU3PDmaChannel *chHandle,
CyU3PDmaCbType_t type,
CyU3PDmaCBInput_t *input
)
{
CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
if(type == CY_U3P_DMA_CB_PROD_EVENT)
{
status = CyU3PDmaChannelCommitBuffer (chHandle, input->buffer_p.count,0);
if (status != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "CyU3PDmaChannelCommitBuffer failed, Error code= %d\n", status);
}
glDMATxCount++;
}
}
1>.這個回調跟上一個功能類似,只是這個是 PtoU( 消費者----->生產者)。
4、 接下來,是一個比較複雜的程式
VoidCyFxSlFifoApplnStart(void)
這個函數啟動一個slave fifo應用。當從USB介面收到一個SET_CONF事件時,即設定配置事件時,它被調用。在這個函數中,端點被配置,DMA管道被建立。我們稍後將看到它就是在USB配置時被調用的。
首先,根據USB的介面速度,決定這個DMA緩衝區的大小為多少位元組。對於3.0是1024 Byte。
然後,端點配置。而得到速度是一個庫函數,如何得到速度不得而知。
不過,由於配置是在裝置描述符得到後,並且是設定地址後調用的。故此時估計PC機已經與下位機協商好速度了。例如PC為2.0則速度只能設為2.0
連接埠設為BULK方式,且被允許。突髮長度為1。尺寸也被設為1024。
先配置生產者:
允許端點1的收,尺寸按速度設定好,其它沒什麼。
不過IN端點1定義成0x81 OUT定義成0x01
接下來,要產生一個DMA_MANUAL通道 為U TO P
看dmaCfg的一些參數填充:
尺寸,即1024。
緩衝區個數,2個。
生產者ID號 從0X401開始的。
消費者socket連接埠,從0X103開始的。
DMA模式:為0表示按位元組計數。
DMA事件:CY_U3P_DMA_CB_PROD_EVENT表示收到一個生產者發來的緩衝
DMA的回呼函數 UtoP
頭 0
尾 0
消費socket的頭的編移 0
最少要多少個空的緩衝才會在生產者啟用前。0 表示任何時候都要啟用它。
在接收PtoU的DMA通道中,修改了這些:
產生者socketID被定義為0x100
消費者socketID被定義為0x301
回呼函數改變了。
然後是產生DMA通道。
再就是重新整理生產者端點 EP。
再就是重新整理消費者端點 EP
設定DMA傳輸尺寸。設為0表示無限。
最後將glIsApplnActive = CyTrue; 將這個全域變數設為TRUE。
下面是一個停止FIFO迴圈的程式。斷開時或複位時會被調用。此處暫不管它。
再下面是一個當USB在SETUP時的回調處理
由於SETUP時交由DRIVER預設處理,故直接返回一個FALSE.
USB事件處理回呼函數
當設定配置時,調用AppStart() 但是如果已啟用又來這麼一下,則直接調用ApplnStop()
複位和斷開時,調用ApplnStop()
5、下面又定義一個比較重要的函數:用於初始化GPIF和USB介面。
CyFxSlFifoApplnInit(void) ///下面將這個函數寫於此
{
CyU3PPibClock_tpitClock;
CyU3PReturnStatus_tapiRetStatus =CY_U3P_SUCCESS;
//以下初始化p-port塊
pibClock.clkDiv =2;
pibClock.clkSrc =CY_U3P_SYS_CLK;
pibClock.isHanfDiv= CyFalse;
pibClock.isDllEnable= CyFalse;
apiRetStatus =CyU3PPibInit(CyTrue,&pibClock); //這裡是設定好時鐘
///以下裝載GPIF Slave_Fifo----未明白它是如何配置的。
apiRetStatus =CyU3PGpifLoad(&Sync_Slave_Fifo_2Bit_CyFxGpifConfig);
///接下來是啟動狀態機器(略),啟動USB函數:
apiRetStatus =CyU3PUsbStart(0;---開始USB功能
接下來
註冊回呼函數用於USB的SETUP過程。但它是一個返回FALSE的函數。
CyU3PUsbRegisterSetupCallback(CyFxSlFifoApplnUSBSetupCB,CyTrue);
CyU3PUsbRegisterEventCallback(CyFxSlFifoApplnUSBEventCB);交易處理不是預設的,而是我們上面定義過的。例如ApplnStart()就是在配置過程中啟動的。
接下來,要開始配置裝置描述符了,因為描述符中含有PID和VID的值。所以必須配置。
apiRetStatus=CyU3PUsbSetDesc(CY_U3P_USB_SET_SS_DEVICE_DESCR,NULL,(uint8*)CyFxUSB30DeviceDscr);
接下來是二進Object Storage Service描述符的設定。
接下來是裝置量化描述符。
接下來是超速配置描述符,高速裝置配置描述符,
重點看一下超速配置描述符,它含配置描述符 主要指明了介面數,配置數,配置字串(無)
特性-自供電,遠端喚醒功能
電流消耗400mA
介面描述符有:
連接埠數量,2個。
介面類 FF,子類00 介面協議0 介面協議字串0
生產者端點描述符如下:
端點地址 0x01 生產者。最大包的長度1024,資料間隔傳輸類型 0表示BULK。
超速端點公司描述符,基本上全是0。
消費者端點,與生產者基本類似。只是連接埠地址不一樣,其它一樣的。
接下來是高速,全速描述符。
接下來是語言描述符。
接下來是製造廠名描述符。 為CYPRESS
接下來是產品描述符 FX3
最後串連USB物理層。至此枚舉將開始。
}
然後定義了一個線程進入點,如下:
SlFifoAppThread_Entry(uint32input)
{
CyFxSlFifoApplnDegugInit(); ///這裡是UART串口初始化
CyFxSlFifoApplnInit(); ///初始化FIFO 應用在這中間是GPIF和USB的初始化程式。
for(;;)
{ CyU3PThreadSleep(1000); //sleep
If(glIsApplnActive) ///如果還是啟用狀態
CyU3PDebugPring(6,”Datatracker:…%d,buffer send:%d\n”,glDMARxCount,glDMATxCount);
}
}
}
/////
下面是應用定義函數,估計這個函數名是不能改的。在這個函數中,我們先分配一個堆棧空間
Ptr=CyU3PMemAlloc(CY_FX_SLFIFO_THREAD_STACK);
//然後產生一個線程
retThrdCreate =CyU3PThreadCreate(*slFifoAppThread,
“ 21:Slave_FIFO_syne”,
slFifoAppThread_Entry,
0,ptr,
CY_FX_SLFIFO_THREAD_STACK,
CY_FX_SLFIFO_THREAD_PRIOITY,
CY_FX_SLFIFO_THREAD_PRIORITY,
CYU3P_NO_THIME_SLICE,
CYU3P_AUTO_START
);
6、最後是主程式main()
首先初始化裝置,裝置指的是CPU,主要是時鐘和堆棧等。
然後cachecontrol 不用DATA CACHE。
在開發板上,由於53:56腳被串連到UART,這意味著我們要麼選擇DQ32模式,要麼選擇LppMode.
不然UART就沒辦法用了。Lpp模式好象是GPIO+UART模式,見datasheet33頁。
幾個參數 用UART,不用IIC ,不用IIS,不用SPI。沒有GPIO使用到(簡單複雜都沒有)。
然後就是設定配置了。
最後進入到核心。不返回。結束主程式。
核心於是調用某一個函數(具體實現核心已經封裝好了),也就是CyFxApplicationDefine(void)。程式就從此開始了。
*問:
1程式是如何下載到USB中去的。要怎麼做。例如將這個編譯好的代碼放到什麼目錄裡還是怎麼辦,還是啟動時,安裝driver時,自動下載,這個下載的東西是個什麼檔案格式?該放在哪裡?不明白
2.難道GPIF口上沒接任何東西, 如何將PC機發下來的資料發回到PC機上去?
生產者:USB的OUT介面1,向GPIF發送一批資料
消費者:GPIF向IN介面81,由它消費掉一批資料。然後通過IN發回PC機。