功能说明:<br /> 上位机用逻辑端点1, 发送M个字节数据到下位机,用来传递参数。下位机接到到数据产生DMA中断,对参数处理完后发送应答信号0x01给上位机;上位机确认应答正确后,开始接收由下位机AD采集过来的数据<br /> 下位机当AD采集到512个字节数据(一个包)时,发送数据包到上位机。下位机一直处于采集发送数据过程。<br />程序如下:<br />1.上位机程序<br /> unsigned char sendbuf[512]; <br /> unsigned char recbuf[512]; <br /> unsigned char ack;<br /> int i;<br /> int ret;<br /> memset(sendbuf,0,512);<br /> memset(recbuf,0,512);<br /> // 第一步:PC机用逻辑端点1,发送参数<br /> ret = WriteData214x(1, sendbuf, sizeof(sendbuf), 1000);<br /> if (ret != sizeof(sendbuf))<br /> {<br /> AfxMessageBox(_T("逻辑端点 1 发送数据错误!"));<br /> return 1;<br /> } <br /> <br /> //第二步: 接收单片机的应答, 应答为一个字节: 0x01, 确保单片机已收到了参数,并设置成功<br /> ret = ReadData214x(0, &ack, 1, 1000);<br /> if (ret != 1)<br /> {<br /> AfxMessageBox(_T("逻辑端点 1 接收数据错误!"));<br /> return 2;<br /> }<br /> if (ack != 1)<br /> {<br /> AfxMessageBox(_T("逻辑端点 1 应答错误"));<br /> return 3;<br /> }<br /><br /> //第三步:PC机接收下位机的数据,并显示。 这个函数为一个线程, 表示一直在接收下位机数据。 <br /> CString DispRec;<br /> while(1){<br /> ret = ReadData214x(2,recbuf,sizeof(recbuf),100);<br /> if (ret != sizeof(recbuf))<br /> {<br /> AfxMessageBox(_T("receive data error!"));<br /> return 4;<br /> }<br /> for (i = 0; i < sizeof(recbuf); i++)<br /> DispRec += IntToASCII(recbuf) + " ";<br /> DispRec+=_T("接收到一个完整的包");<br /> pEdit->SetWindowText(DispRec);<br /> }<br />2.下位机程序.<br />int main (void)<br />{<br /> INT8U err, *pSrc, *pDst;<br /> INT8U a[2];//用于应答缓存<br /> uint8 Time0Flag = 0; //Timer0_Int只初始化一次标志<br /> err = USB_Initialize(); /* 初始化LPC214x USB控制器 */<br /> Init_USBInterrupt(); /* 配置USB中断向量 */<br /> IRQEnable(); /* 使能IRQ中断 */<br /> UART0_Init();//初始化串口<br /> AD_Init(); //启动A/D转换 <br /> set_tagAD(3072); //AD数据结构初始化<br /> AD.UsbRecFinish = 0; //等待参数接收完成后才可以发送数据. <br /> AD.USBPackerNo = 0; //初始化为第0个包<br /> /****************** 主函数是一个无限循环结构 **************/ <br /> while(1)<br /> {<br /> usbserve(); /* USB事件处理如控制传输、总线挂起等等*/<br /> if (bEPPflags.bits.configuration == 1)<br /> { /* USB已配置完成 */<br /> if (bEPPflags.bits.ep1_rxdma == 1)<br /> { <br /> len = USB_DMASetTransLength(2, 512); //配置逻辑端点1的DMA接收数据长度<br /> DISABLE();<br /> bEPPflags.bits.ep1_rxdma = 0;<br /> pDst = USB_DMAGetBuffer(2);<br /> ////////////////参数处理开始////////////////////////////////////////////<br /> <br /> ///////////////电机处理部分///////////////////////////<br /> Motor.MorStrNo = pDst[0]; <br /> Motor.MorStrHz = (pDst[1] * 256)+ pDst[2]; Motor.MorRunHz = (pDst[3] * 256) + pDst[4]; MotorStart(Motor.MorStrNo,Motor.MorStrHz,Motor.MorRunHz); InitMotorIO(); <br /> StepStartMotor(Motor.MorStrNo,Motor.MorStrHz,Motor.MorRunHz); ///////////////电机处理部分///////////////////////////<br /> ……..<br /> ……..<br /> ////////////////参数处理完成//////////////////////////////////////////<br /> <br /> <br /> ////////////////参数处理完通过物理端点3发送应答给PC/////////////////////////////<br /> pDst = USB_DMAGetBuffer(3);<br /> a[0]=0x01;<br /> memcpy(pDst,a,1);<br /> len =USB_DMASetTransLength(3, 1);// 配置物理端点3 DMA发送缓冲区大小<br /> USB_DMAStart_IN(3); <br /> ENABLE(); <br /> AD.UsbRecFinish = 1; //参数处理完 <br /> <br /> }<br /> <br /> if(AD.UsbRecFinish == 1) //可以发送数据了...<br /> {<br /> if(Time0Flag == 0)<br /> {<br /> Timer0_Int();<br /> }<br /> USBSendADDataToPC(); <br /> <br /> <br /> }<br /> <br /> }<br /> }<br /><br /><br /><br /><br />////////////////////////////////////////////////定时器采样AD的值/////////////////////////////////<br />void __irq IRQ_Timer0()<br />{<br /> AD.AdFlag=1; //用于标记可以采集AD数据了.<br /> <br /> T0IR = 0x01; //清除中断标志<br /> VICVectAddr = 0x00; <br /> <br />} <br /><br />/********************************************************************************************************<br />** 函数名称 :Timer0_Int()<br />** 函数功能 :定时器0初始化程序<br />** 入口参数 :无<br />** 出口参数 :无 注意:jjk11.06 加入了使用定时器来计算时间,每隔0.5s采集一次<br />********************************************************************************************************/<br />void Timer0_Int(void)<br />{<br /> /* 定时器0的初始化 */<br /> T0TC = 0; //定时器初始设置为0<br /> T0PR = 0; //时钟不分频<br /> T0MCR =0x03; //设置T0MR0匹配后复位T0TC,并产生中断标志<br /> T0MR0 = Fpclk/10000; //需要修改这里来满足不同的定时需要,Fpclk/2 0.5s定时 10K<br /> T0TCR =0x01; //启动定时器<br /> VICIntSelect = 0x00; //所有中断设置为IRQ中断,应该在系统初始化里面已经完成了<br /> VICVectCntl2 = (0x20 | 0x04); /* timer0中断分配到IRQ slot1 次高优先级 */ <br /> VICVectAddr2 = (uint32)IRQ_Timer0; /*设置中断服务程序地址*/ VICIntEnable = (1<<04); /* 使能timer0中断*/<br /> //IRQ中断使能<br />} <br />/********************************************************************************************************<br />** 函数名称 void USBSendADDataToPC(void)<br />** 函数功能 :对采集的A/D放心缓存并发送到PC处理.<br />** 入口参数 :无<br />** 出口参数 :无 <br />** 080101修改:把里面用到的获取物理端点的RAM地址,选择端点发送多少字节做成一个函数了.放在USBdma.c里<br />********************************************************************************************************/<br />void USBSendADDataToPC(void)<br />{<br />uint8 *pDst; //接收区指针<br />uint8 ack; //发送完一个包后,等待上位机的应答<br /> if(AD.Cnt >= AD.USBBufLen) //说明AD转换后的数据已经存满了.<br /> { <br /> IRQDisable(); <br /> set_tagAD(512); //重新初始化AD缓冲区<br /> USB_Buf[0] = AD.USBPackerNo / 256; //每个包的前两个字节用来存放包头,即第几个包<br /> USB_Buf[1] = AD.USBPackerNo % 256;<br /> USBDMA_EndPointerSendDataToPc(5,512,USB_Buf); //从USB_Buf缓冲区往物理端点5送512个字节<br /> AD.USBPackerNo++; //包发送完后包头加1<br /> ///////////////////////////////////////接收包的回应:应答为02////////////////<br /> if (bEPPflags.bits.ep2_rxdma == 1) //逻辑端点2接收应答信号<br /> {<br /> USB_DMASetTransLength(4, 512); //配置逻辑端点2的DMA接收数据长度<br /> bEPPflags.bits.ep2_rxdma = 0;<br /> pDst = USB_DMAGetBuffer(2);<br /> ack = pDst[0];<br /> if(ack != 0x02)<br /> {<br /> delayus(300);//延时300us<br /> }<br /> <br /> }<br /> IRQEnable(); <br /> }<br /> if(AD.AdFlag==1)<br /> {<br /> AD0CR |= 1 << 24; // 进行第一次转换<br /> while ((ADDR & 0x80000000) == 0); // 等待转换结束<br /> AD0CR |= 1 << 24; // 再次启动转换<br /> while ((ADDR & 0x80000000) == 0); // 等待转换结束<br /> ADC_Data = ADDR; // 读取ADC结果<br /> AD.AdFlag = 0; //A/D转换标记清0<br /> ADC_Data = (ADC_Data >> 6) & 0x3ff; //读取10位A/D数据<br /> ADC_Data = ADC_Data * 2480; // 参考电压经过3/4分压<br /> ADC_Data = ADC_Data / 1024;<br /> USB_Buf[AD.Cnt++] = ADC_Data/256; //转换后的数据高8位,存于缓冲区.<br /> USB_Buf[AD.Cnt++] = ADC_Data%256; //转换后的数据低8位,存于缓冲区.<br /> }<br /> <br />}<br /><br />这里AD采样的过程由一定时器控制,定时器的频率是5K<br />下位机的DMA端点RAM大小<br />#define EP02_DMA_BUFFER_LENGTH 512<br />#define EP03_DMA_BUFFER_LENGTH 512 <br />#define EP04_DMA_BUFFER_LENGTH 3072<br />#define EP05_DMA_BUFFER_LENGTH 3072<br /><br />问题:: <br />1。<br />// 第一步:PC机用逻辑端点1,发送参数<br /> ret = WriteData214x(1, sendbuf, sizeof(sendbuf), 1000);<br /> if (ret != sizeof(sendbuf))<br /> {<br /> AfxMessageBox(_T("逻辑端点 1 发送数据错误!"));<br /> return 1;<br /> }<br />我发现上位机只能发送512字节一包的数据下位机接收才正常。如果我改成<br />ret = WriteData214x(1, sendbuf, N, 1000); // 这里N的取值为2-510,任意数<br /> if (ret != N)<br /> {<br /> AfxMessageBox(_T("逻辑端点 1 发送数据错误!"));<br /> return 1;<br /> }<br />为什么只能发送下位机设置的512值 ?<br />#define EP02_DMA_BUFFER_LENGTH 512<br />#define EP03_DMA_BUFFER_LENGTH 512 <br />下位机端点缓冲区设置了512,上位机就只能发送512个字节的包吗?我这里只想发送2个字节的数据.或小于512的包,请问是不是每次都得发送512的包?<br /><br /><br />2.<br /> 下位机在采集完一包数据512字节后发送数据到上位机,上位机用一个线程一直接查询有没有数据包,并显示。也就是说下位机一直发包,上位机一直接包,这一传输过程<br /> 很容易丢包,请问为什么? <br />而且经常会出现“逻辑端点 1 接收数据错误!”<br />ret = ReadData214x(0, &ack, 1, 1000);<br /> if (ret != 1)<br /> {<br /> AfxMessageBox(_T("逻辑端点 1 接收数据错误!"));<br /> return 2;<br /> }<br /> if (ack != 1)<br /> {<br /> AfxMessageBox(_T("逻辑端点 1 应答错误"));<br /> return 3;<br /> }<br />不知为什么。<br />3. 速度问题: 在上述过程中速度都不快,测试后差不多只能达到4K/S左右(我看了其它人测试的结果7XX/S 是在一次收发过程),不像这种采样后发送的过程。<br />通过速度测试得出结论 <br />正常模式与DMA模式下上位机接收到的数据速度都比较小<br />正常模式下丢包比较少,特别是在采样频率低的时候基本没有多大问题,但高的采样频率会造成丢包,丢包头事件。(采样指定时器中数频率)(包头指一512包前两个字节表示第几个包)<br />DMA模式下,丢包严重。<br />其实在高速采样频率下,下位机发送包的速度与理论上相差无几,主要原因是上位机没有接收完全!<br />有哪位仁兄可以帮忙解答吗? <br /> |
|