由于公司设备升级后出了问题,需要对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个字节的数据,或者说是将缓冲区填满的数据!!