由於公司設備升級后出了問題,需要對USB驅動進行修改,原本使用的是寄存器模式進行UART傳輸,但是由於FX3寄存器模式會出現長時間延時等待的問題,不得不對其傳輸模式進行修改。雖然賽普拉斯的EZ-USB FX3系列芯片功能強大,成本適中,但共享資源太少,API參考手冊里面的干貨不多,直接導致開發困難,出現問題只能去官方社區尋找答案。新模式的開發也不是一帆風順,找來找去,只有在固件庫中找到了UartLpDmaMode這個例程還比較相似。於是便在其基礎上進行修改。
在UartLpDmaMode例程中,其數據流通方向是這樣的:
只是從將接收到的數據進行了循環發送,這樣一來,其生產者和消費者ID便很好設置,但是你無法對DMA通道進行直接操作,換句話說,你無法發送你想要發送的數據,也無法將你接收到的數據存入自己開辟的緩沖區中進行存儲使用,當然這樣並不是我想要的。
我想要操作的數據傳輸是能夠實現想傳什么傳什么,接收到的數據能想什么時候用就可以什么時候用。其數據流通就如同下圖:
但是,我在初期對FX3的DMA消費者生產者理解不深,一度認為這是不能實現的,但經過幾天的社區詢問以及個人摸索,發現可以這樣使用!由於期間走了很多彎路,深知百度找不到任何有關賽普拉斯有用資料的苦衷,現在把這段代碼分享出來。
開發環境:EZ-USB FX3 Development Kit SDK1.3.4
開發板型號:CYUSB3KIT-003(CYUSB3014)
開發目的:實現串口DMA模式的數據發送以及接收,能夠隨意發送自己緩沖區中的數據,接收到的數據能夠儲存在個人開辟的緩沖區中
1 /*此DEMO使用DMA模式,可以發送自己緩沖區中的數據,接收到數據后,可將接收到的數據存入全局變量glRxBuffer->buffer中。 2 *注意: 3 * 賽普拉斯FX3的DMA緩沖區大小最小是16個字節,緩沖區大小必須是16的倍數,也就是說,發送數據至少發送16個字節,發送的數據最大不能超過緩沖區的設定值,接收也一樣,否則緩沖區未滿,無法觸發接收和發送! 4 *如果與其他設備通訊,可以讓其他設備強制發送16個字節的數據,自己取有效位使用。如果想一個字節一個字節的發送和接收,可以使用寄存器模式。 5 */ 6 7 #include <cyu3system.h> 8 #include <cyu3os.h> 9 #include <cyu3error.h> 10 #include <cyu3uart.h> 11 12 #define CY_FX_UARTLP_THREAD_STACK (0x0400) /* UART application thread stack size */ 13 #define CY_FX_UARTLP_THREAD_PRIORITY (8) /* UART application thread priority */ 14 #define CY_FX_UART_DMA_TX_SIZE (0) /* DMA transfer size */ 15 #define CY_FX_UART_DMA_BUF_SIZE (16) /* Buffer size */ 16 17 CyU3PThread UartLpAppThread; /* UART Example application thread structure */ 18 19 uint8_t testBuffer[16] = {0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,0xff}; 20 21 CyU3PDmaChannel glUartRXChHandle; 22 CyU3PDmaChannel glUartTXChHandle; 23 CyU3PDmaBuffer_t* glTxBuffer; 24 CyU3PDmaBuffer_t* glRxBuffer; 25 uint8_t ClearFlag = 0; 26 27 /* Application error handler */ 28 void 29 CyFxAppErrorHandler ( 30 CyU3PReturnStatus_t apiRetStatus /* API return status */ 31 ) 32 { 33 /* Application failed with the error code apiRetStatus */ 34 35 /* Add custom debug or recovery actions here */ 36 37 /* Loop indefinitely */ 38 for (;;) 39 { 40 /* Thread sleep : 100 ms */ 41 CyU3PThreadSleep (100); 42 } 43 } 44 /*********************************************************************************************** 45 *函數名 : SendData 46 *函數功能描述 : 通過DMA模式 由串口發送數據 47 *函數參數 : buffer-所需要發送的數據 len-發送數據的長度 48 *函數返回值 : 無 49 *注意:len最小為16 50 ***********************************************************************************************/ 51 void SendData(uint8_t * buffer, unsigned int len) 52 { 53 CyU3PReturnStatus_t status; 54 unsigned int i = 0; 55 CyU3PDmaChannelGetBuffer(&glUartTXChHandle, glTxBuffer, 0); 56 for(i = 0; i < len; i++) 57 { 58 glTxBuffer->buffer[i] = buffer[i]; 59 } 60 CyU3PDmaChannelSetupSendBuffer(&glUartTXChHandle,glTxBuffer); 61 status = CyU3PDmaChannelCommitBuffer(&glUartTXChHandle, 16, 0); 62 if (status == CY_U3P_SUCCESS) 63 { 64 65 } 66 } 67 68 /*********************************************************************************************** 69 *函數名 : ReceivedDataCallBack 70 *函數功能描述 : 接收緩沖區充滿后的回調函數 71 *函數參數 : chHandle-DMA通道的句柄 type-事件類型 input-輸入 72 *函數返回值 : 無 73 *注意:形參已經被設置好,直接可以使用 74 ***********************************************************************************************/ 75 void ReceivedDataCallBack( 76 CyU3PDmaChannel *chHandle, /* Handle to the DMA channel. */ 77 CyU3PDmaCbType_t type, /* Callback type. */ 78 CyU3PDmaCBInput_t *input) 79 { 80 CyU3PReturnStatus_t status; 81 if(type == CY_U3P_DMA_CB_PROD_EVENT) 82 { 83 //CyU3PDmaChannelSetWrapUp(&glUartRXChHandle); 84 status = CyU3PDmaChannelGetBuffer(&glUartRXChHandle, glRxBuffer, 0); 85 //測試用,將收到的信息在發送出去,此時測試為接收到16個字節的數據 86 SendData(glRxBuffer->buffer, 16); 87 //SendData(testBuffer, 16); 88 ClearFlag = 1; 89 if (status == CY_U3P_SUCCESS) 90 { 91 CyU3PDmaChannelDiscardBuffer(&glUartRXChHandle); 92 } 93 } 94 } 95 96 /* This function initializes the UART module */ 97 void 98 CyFxUartDMAlnInit (void) 99 { 100 CyU3PUartConfig_t uartConfig; 101 CyU3PDmaChannelConfig_t dmaConfig; 102 CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS; 103 104 //開啟DCache后 一定設置為32,未開啟最好也設置成32,但也可設置為16,不影響使用 105 glTxBuffer = (CyU3PDmaBuffer_t*)CyU3PDmaBufferAlloc (32); 106 glRxBuffer = (CyU3PDmaBuffer_t*)CyU3PDmaBufferAlloc (32); 107 108 /* Initialize the UART module */ 109 apiRetStatus = CyU3PUartInit (); 110 if (apiRetStatus != CY_U3P_SUCCESS) 111 { 112 /* Error handling */ 113 CyFxAppErrorHandler(apiRetStatus); 114 } 115 116 /* Configure the UART 117 Baudrate = 115200, One stop bit, No parity, Hardware flow control enabled. 118 */ 119 CyU3PMemSet ((uint8_t *)&uartConfig, 0, sizeof(uartConfig)); 120 uartConfig.baudRate = CY_U3P_UART_BAUDRATE_115200; 121 uartConfig.stopBit = CY_U3P_UART_ONE_STOP_BIT; 122 uartConfig.parity = CY_U3P_UART_NO_PARITY; 123 uartConfig.flowCtrl = CyFalse; //一定不能為真 124 uartConfig.txEnable = CyTrue; 125 uartConfig.rxEnable = CyTrue; 126 uartConfig.isDma = CyTrue; /* DMA mode */ 127 128 /* Set the UART configuration */ 129 apiRetStatus = CyU3PUartSetConfig (&uartConfig, NULL); 130 if (apiRetStatus != CY_U3P_SUCCESS ) 131 { 132 /* Error handling */ 133 CyFxAppErrorHandler(apiRetStatus); 134 } 135 136 /* Create a DMA Manual channel between UART producer socket 137 and UART consumer socket */ 138 CyU3PMemSet ((uint8_t *)&dmaConfig, 0, sizeof(dmaConfig)); 139 dmaConfig.size = CY_FX_UART_DMA_BUF_SIZE; 140 dmaConfig.count = 1; 141 dmaConfig.prodSckId = CY_U3P_LPP_SOCKET_UART_PROD; //生產者為RX 142 dmaConfig.consSckId = CY_U3P_CPU_SOCKET_CONS; //消費者 143 dmaConfig.dmaMode = CY_U3P_DMA_MODE_BYTE; 144 dmaConfig.notification = CY_U3P_DMA_CB_PROD_EVENT; //緩沖區充滿產生的事件,此事件觸發回調函數 145 dmaConfig.cb = ReceivedDataCallBack; 146 dmaConfig.prodHeader = 0; 147 dmaConfig.prodFooter = 0; 148 dmaConfig.consHeader = 0; 149 dmaConfig.prodAvailCount = 0; 150 /* Create the channel */ 151 apiRetStatus = CyU3PDmaChannelCreate (&glUartRXChHandle, 152 CY_U3P_DMA_TYPE_MANUAL_IN, &dmaConfig); 153 154 if (apiRetStatus != CY_U3P_SUCCESS) 155 { 156 /* Error handling */ 157 CyFxAppErrorHandler(apiRetStatus); 158 } 159 160 dmaConfig.size = CY_FX_UART_DMA_BUF_SIZE; 161 dmaConfig.count = 1; 162 dmaConfig.prodSckId = CY_U3P_CPU_SOCKET_PROD; //生產者CPU 163 dmaConfig.consSckId = CY_U3P_LPP_SOCKET_UART_CONS; //消費者為TX 164 dmaConfig.dmaMode = CY_U3P_DMA_MODE_BYTE; 165 dmaConfig.notification = 0; 166 dmaConfig.cb = NULL; 167 dmaConfig.prodHeader = 0; 168 dmaConfig.prodFooter = 0; 169 dmaConfig.consHeader = 0; 170 dmaConfig.prodAvailCount = 0; 171 172 /* Create the channel */ 173 apiRetStatus = CyU3PDmaChannelCreate (&glUartTXChHandle, 174 CY_U3P_DMA_TYPE_MANUAL_OUT, &dmaConfig); 175 176 if (apiRetStatus != CY_U3P_SUCCESS) 177 { 178 /* Error handling */ 179 CyFxAppErrorHandler(apiRetStatus); 180 } 181 /* Set UART Tx and Rx transfer Size to infinite */ 182 apiRetStatus = CyU3PUartTxSetBlockXfer(0xFFFFFFFF); 183 if (apiRetStatus != CY_U3P_SUCCESS) 184 { 185 /* Error handling */ 186 CyFxAppErrorHandler(apiRetStatus); 187 } 188 189 apiRetStatus = CyU3PUartRxSetBlockXfer(0xFFFFFFFF); 190 if (apiRetStatus != CY_U3P_SUCCESS) 191 { 192 /* Error handling */ 193 CyFxAppErrorHandler(apiRetStatus); 194 } 195 196 /* Set DMA Channel transfer size */ 197 apiRetStatus = CyU3PDmaChannelSetXfer (&glUartRXChHandle, 0); 198 if (apiRetStatus != CY_U3P_SUCCESS) 199 { 200 /* Error handling */ 201 CyFxAppErrorHandler(apiRetStatus); 202 } 203 204 apiRetStatus = CyU3PDmaChannelSetXfer (&glUartTXChHandle, 0); 205 if (apiRetStatus != CY_U3P_SUCCESS) 206 { 207 /* Error handling */ 208 CyFxAppErrorHandler(apiRetStatus); 209 } 210 } 211 212 /* Entry function for the UartLpAppThread */ 213 void 214 UartLpAppThread_Entry ( 215 uint32_t input) 216 { 217 /* Initialize the UART Example Application */ 218 CyFxUartDMAlnInit(); 219 220 //uint8_t testBuffer[8] = {0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8}; 221 for (;;) 222 { 223 224 //if中的語句,是為了接收完畢后清除緩沖區,如果不清除緩沖區,如果所發數據超過緩沖區長度,第二次發送時會將上次未發送完的數據發送過來。 225 if(ClearFlag == 1) 226 { 227 //SendData(glRxBuffer->buffer, 16); 228 CyU3PDmaChannelReset(&glUartRXChHandle); 229 CyU3PThreadSleep(10); 230 CyU3PDmaChannelSetXfer(&glUartRXChHandle,0); 231 ClearFlag = 0; 232 } 233 /* No operation in the thread */ 234 CyU3PThreadSleep (100); 235 } 236 } 237 238 /* Application define function which creates the threads. */ 239 void 240 CyFxApplicationDefine ( 241 void) 242 { 243 void *ptr = NULL; 244 uint32_t retThrdCreate = CY_U3P_SUCCESS; 245 246 /* Allocate the memory for the threads */ 247 ptr = CyU3PMemAlloc (CY_FX_UARTLP_THREAD_STACK); 248 249 /* Create the thread for the application */ 250 retThrdCreate = CyU3PThreadCreate (&UartLpAppThread, /* UART Example App Thread structure */ 251 "21:UART_loopback_DMA_mode", /* Thread ID and Thread name */ 252 UartLpAppThread_Entry, /* UART Example App Thread Entry function */ 253 0, /* No input parameter to thread */ 254 ptr, /* Pointer to the allocated thread stack */ 255 CY_FX_UARTLP_THREAD_STACK, /* UART Example App Thread stack size */ 256 CY_FX_UARTLP_THREAD_PRIORITY, /* UART Example App Thread priority */ 257 CY_FX_UARTLP_THREAD_PRIORITY, /* UART Example App Thread priority */ 258 CYU3P_NO_TIME_SLICE, /* No time slice for the application thread */ 259 CYU3P_AUTO_START /* Start the Thread immediately */ 260 ); 261 262 /* Check the return code */ 263 if (retThrdCreate != 0) 264 { 265 /* Thread Creation failed with the error code retThrdCreate */ 266 267 /* Add custom recovery or debug actions here */ 268 269 /* Application cannot continue */ 270 /* Loop indefinitely */ 271 while(1); 272 } 273 } 274 275 /* 276 * Main function 277 */ 278 int 279 main (void) 280 { 281 CyU3PIoMatrixConfig_t io_cfg; 282 CyU3PReturnStatus_t status = CY_U3P_SUCCESS; 283 284 /* Initialize the device */ 285 status = CyU3PDeviceInit (0); 286 if (status != CY_U3P_SUCCESS) 287 { 288 goto handle_fatal_error; 289 } 290 291 /* Initialize the caches. Enable both Instruction and Data Caches. */ 292 status = CyU3PDeviceCacheControl (CyTrue, CyTrue, CyTrue); 293 if (status != CY_U3P_SUCCESS) 294 { 295 goto handle_fatal_error; 296 } 297 298 /* Configure the IO matrix for the device. On the FX3 DVK board, the COM port 299 * is connected to the IO(53:56). This means that either DQ32 mode should be 300 * selected or lppMode should be set to UART_ONLY. Here we are choosing 301 * UART_ONLY configuration. */ 302 CyU3PMemSet ((uint8_t *)&io_cfg, 0, sizeof(io_cfg)); 303 io_cfg.isDQ32Bit = CyFalse; 304 io_cfg.s0Mode = CY_U3P_SPORT_INACTIVE; 305 io_cfg.s1Mode = CY_U3P_SPORT_INACTIVE; 306 io_cfg.useUart = CyTrue; 307 io_cfg.useI2C = CyFalse; 308 io_cfg.useI2S = CyFalse; 309 io_cfg.useSpi = CyFalse; 310 io_cfg.lppMode = CY_U3P_IO_MATRIX_LPP_UART_ONLY; 311 /* No GPIOs are enabled. */ 312 io_cfg.gpioSimpleEn[0] = 0; 313 io_cfg.gpioSimpleEn[1] = 0; 314 io_cfg.gpioComplexEn[0] = 0; 315 io_cfg.gpioComplexEn[1] = 0; 316 status = CyU3PDeviceConfigureIOMatrix (&io_cfg); 317 if (status != CY_U3P_SUCCESS) 318 { 319 goto handle_fatal_error; 320 } 321 322 /* This is a non returnable call for initializing the RTOS kernel */ 323 CyU3PKernelEntry (); 324 325 /* Dummy return to make the compiler happy */ 326 return 0; 327 328 handle_fatal_error: 329 /* Cannot recover from this error. */ 330 while (1); 331 332 }
實驗效果:能夠實現發送和接收,FX3將接收到的數據再發送給主機,如圖:
將110行的 SendData(glRxBuffer->buffer, 16);改為111行的SendData(testBuffer, 16);能夠實現,接收16位數據后,將testBuffer中的數據返回給主機,效果如圖:
需要注意的是:DMA_BUFFER_SIZE的大小必須為16的倍數!!最小為16!!也就是說,一次至少需要發送或者接收16個字節的數據,或者說是將緩沖區填滿的數據!!