打印
[ZLG-ARM]

lpc2148传输丢包和速度问题

[复制链接]
1878|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
helfen|  楼主 | 2008-1-3 22:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
功能说明:
    上位机用逻辑端点1, 发送M个字节数据到下位机,用来传递参数。下位机接到到数据产生DMA中断,对参数处理完后发送应答信号0x01给上位机;上位机确认应答正确后,开始接收由下位机AD采集过来的数据
   下位机当AD采集到512个字节数据(一个包)时,发送数据包到上位机。下位机一直处于采集发送数据过程。
程序如下:
1.上位机程序
    unsigned char sendbuf[512];    
    unsigned char recbuf[512];    
    unsigned char ack;
    int i;
    int ret;
    memset(sendbuf,0,512);
    memset(recbuf,0,512);
    // 第一步:PC机用逻辑端点1,发送参数
    ret = WriteData214x(1, sendbuf, sizeof(sendbuf), 1000);
    if (ret != sizeof(sendbuf))
    {
        AfxMessageBox(_T("逻辑端点 1 发送数据错误!"));
        return 1;
    }    
    
    //第二步: 接收单片机的应答, 应答为一个字节: 0x01, 确保单片机已收到了参数,并设置成功
    ret = ReadData214x(0, &ack, 1, 1000);
    if (ret != 1)
    {
        AfxMessageBox(_T("逻辑端点 1 接收数据错误!"));
        return 2;
    }
    if (ack != 1)
    {
        AfxMessageBox(_T("逻辑端点 1 应答错误"));
        return 3;
    }

    //第三步:PC机接收下位机的数据,并显示。 这个函数为一个线程, 表示一直在接收下位机数据。  
    CString DispRec;
    while(1){
        ret = ReadData214x(2,recbuf,sizeof(recbuf),100);
        if (ret != sizeof(recbuf))
        {
            AfxMessageBox(_T("receive data error!"));
            return 4;
        }
       for (i = 0; i < sizeof(recbuf); i++)
         DispRec += IntToASCII(recbuf) + " ";
       DispRec+=_T("接收到一个完整的包");
       pEdit->SetWindowText(DispRec);
    }
2.下位机程序.
int main (void)
{
    INT8U err, *pSrc, *pDst;
    INT8U a[2];//用于应答缓存
    uint8 Time0Flag = 0; //Timer0_Int只初始化一次标志
      err = USB_Initialize();            /* 初始化LPC214x USB控制器 */
    Init_USBInterrupt();            /* 配置USB中断向量 */
    IRQEnable();            /* 使能IRQ中断 */
    UART0_Init();//初始化串口
    AD_Init();                                //启动A/D转换    
    set_tagAD(3072);             //AD数据结构初始化
    AD.UsbRecFinish = 0;       //等待参数接收完成后才可以发送数据. 
    AD.USBPackerNo = 0;            //初始化为第0个包
    /****************** 主函数是一个无限循环结构 **************/    
    while(1)
    {
        usbserve();        /* USB事件处理如控制传输、总线挂起等等*/
        if (bEPPflags.bits.configuration == 1)
        {                                        /* USB已配置完成 */
            if (bEPPflags.bits.ep1_rxdma == 1)
            {    
                len = USB_DMASetTransLength(2, 512);    //配置逻辑端点1的DMA接收数据长度
                DISABLE();
                bEPPflags.bits.ep1_rxdma = 0;
                pDst = USB_DMAGetBuffer(2);
                ////////////////参数处理开始////////////////////////////////////////////
                
                ///////////////电机处理部分///////////////////////////
                Motor.MorStrNo = pDst[0];  
                Motor.MorStrHz = (pDst[1] * 256)+ pDst[2];                                        Motor.MorRunHz = (pDst[3] * 256) + pDst[4];                                        MotorStart(Motor.MorStrNo,Motor.MorStrHz,Motor.MorRunHz);                          InitMotorIO(); 
                StepStartMotor(Motor.MorStrNo,Motor.MorStrHz,Motor.MorRunHz);                      ///////////////电机处理部分///////////////////////////
                ……..
                ……..
                ////////////////参数处理完成//////////////////////////////////////////
                
                
                ////////////////参数处理完通过物理端点3发送应答给PC/////////////////////////////
                pDst = USB_DMAGetBuffer(3);
                a[0]=0x01;
                memcpy(pDst,a,1);
                len =USB_DMASetTransLength(3, 1);// 配置物理端点3 DMA发送缓冲区大小
                USB_DMAStart_IN(3);             
                ENABLE();               
                AD.UsbRecFinish = 1;  //参数处理完 
                
            }
             
            if(AD.UsbRecFinish == 1)         //可以发送数据了...
            {
                if(Time0Flag == 0)
                {
                Timer0_Int();
                }
                USBSendADDataToPC();                                        
                
                
            }
            
        }
    }




////////////////////////////////////////////////定时器采样AD的值/////////////////////////////////
void __irq IRQ_Timer0()
{
        AD.AdFlag=1;         //用于标记可以采集AD数据了.
    
    T0IR = 0x01;   //清除中断标志
    VICVectAddr = 0x00;    
 


/********************************************************************************************************
** 函数名称 :Timer0_Int()
** 函数功能 :定时器0初始化程序
** 入口参数 :无
** 出口参数 :无   注意:jjk11.06   加入了使用定时器来计算时间,每隔0.5s采集一次
********************************************************************************************************/
void Timer0_Int(void)
{
     /* 定时器0的初始化 */
      T0TC = 0;              //定时器初始设置为0
      T0PR = 0;             //时钟不分频
      T0MCR =0x03;          //设置T0MR0匹配后复位T0TC,并产生中断标志
      T0MR0 = Fpclk/10000;      //需要修改这里来满足不同的定时需要,Fpclk/2 0.5s定时 10K
      T0TCR =0x01;          //启动定时器
    VICIntSelect = 0x00;                         //所有中断设置为IRQ中断,应该在系统初始化里面已经完成了
    VICVectCntl2 = (0x20 | 0x04);                            /* timer0中断分配到IRQ slot1 次高优先级 */    
    VICVectAddr2 = (uint32)IRQ_Timer0;                  /*设置中断服务程序地址*/    VICIntEnable = (1<<04);                             /* 使能timer0中断*/
                                   //IRQ中断使能
}        
/********************************************************************************************************
** 函数名称 void USBSendADDataToPC(void)
** 函数功能 :对采集的A/D放心缓存并发送到PC处理.
** 入口参数 :无
** 出口参数 :无 
** 080101修改:把里面用到的获取物理端点的RAM地址,选择端点发送多少字节做成一个函数了.放在USBdma.c里
********************************************************************************************************/
void USBSendADDataToPC(void)
{
uint8 *pDst;   //接收区指针
uint8 ack;     //发送完一个包后,等待上位机的应答
    if(AD.Cnt >= AD.USBBufLen)            //说明AD转换后的数据已经存满了.
     {                
        IRQDisable();   
        set_tagAD(512);     //重新初始化AD缓冲区
        USB_Buf[0] = AD.USBPackerNo / 256;  //每个包的前两个字节用来存放包头,即第几个包
        USB_Buf[1] = AD.USBPackerNo % 256;
        USBDMA_EndPointerSendDataToPc(5,512,USB_Buf);   //从USB_Buf缓冲区往物理端点5送512个字节
        AD.USBPackerNo++;  //包发送完后包头加1
        ///////////////////////////////////////接收包的回应:应答为02////////////////
        if (bEPPflags.bits.ep2_rxdma == 1) //逻辑端点2接收应答信号
        {
            USB_DMASetTransLength(4, 512);    //配置逻辑端点2的DMA接收数据长度
            bEPPflags.bits.ep2_rxdma = 0;
            pDst = USB_DMAGetBuffer(2);
            ack = pDst[0];
            if(ack != 0x02)
            {
                delayus(300);//延时300us
            }
        
        }
        IRQEnable();      
     }
    if(AD.AdFlag==1)
    {
        AD0CR |= 1 << 24;                    // 进行第一次转换
        while ((ADDR & 0x80000000) == 0);    // 等待转换结束
        AD0CR |= 1 << 24;                    // 再次启动转换
        while ((ADDR & 0x80000000) == 0);    // 等待转换结束
        ADC_Data = ADDR;                    // 读取ADC结果
        AD.AdFlag = 0;                      //A/D转换标记清0
        ADC_Data = (ADC_Data >> 6) & 0x3ff;     //读取10位A/D数据
        ADC_Data = ADC_Data * 2480;                // 参考电压经过3/4分压
        ADC_Data = ADC_Data / 1024;
        USB_Buf[AD.Cnt++] = ADC_Data/256;      //转换后的数据高8位,存于缓冲区.
        USB_Buf[AD.Cnt++] = ADC_Data%256;      //转换后的数据低8位,存于缓冲区.
     }
    
}

这里AD采样的过程由一定时器控制,定时器的频率是5K
下位机的DMA端点RAM大小
#define EP02_DMA_BUFFER_LENGTH         512
#define EP03_DMA_BUFFER_LENGTH         512        
#define EP04_DMA_BUFFER_LENGTH         3072
#define EP05_DMA_BUFFER_LENGTH         3072

问题:: 
1。
// 第一步:PC机用逻辑端点1,发送参数
    ret = WriteData214x(1, sendbuf, sizeof(sendbuf), 1000);
    if (ret != sizeof(sendbuf))
    {
        AfxMessageBox(_T("逻辑端点 1 发送数据错误!"));
        return 1;
    }
我发现上位机只能发送512字节一包的数据下位机接收才正常。如果我改成
ret = WriteData214x(1, sendbuf, N, 1000);      // 这里N的取值为2-510,任意数
    if (ret != N)
    {
        AfxMessageBox(_T("逻辑端点 1 发送数据错误!"));
        return 1;
    }
为什么只能发送下位机设置的512值 ?
#define EP02_DMA_BUFFER_LENGTH         512
#define EP03_DMA_BUFFER_LENGTH         512    
下位机端点缓冲区设置了512,上位机就只能发送512个字节的包吗?我这里只想发送2个字节的数据.或小于512的包,请问是不是每次都得发送512的包?


2.
   下位机在采集完一包数据512字节后发送数据到上位机,上位机用一个线程一直接查询有没有数据包,并显示。也就是说下位机一直发包,上位机一直接包,这一传输过程
    很容易丢包,请问为什么? 
而且经常会出现“逻辑端点 1 接收数据错误!”
ret = ReadData214x(0, &ack, 1, 1000);
    if (ret != 1)
    {
        AfxMessageBox(_T("逻辑端点 1 接收数据错误!"));
        return 2;
    }
    if (ack != 1)
    {
        AfxMessageBox(_T("逻辑端点 1 应答错误"));
        return 3;
    }
不知为什么。
3. 速度问题: 在上述过程中速度都不快,测试后差不多只能达到4K/S左右(我看了其它人测试的结果7XX/S 是在一次收发过程),不像这种采样后发送的过程。
通过速度测试得出结论 
正常模式与DMA模式下上位机接收到的数据速度都比较小
正常模式下丢包比较少,特别是在采样频率低的时候基本没有多大问题,但高的采样频率会造成丢包,丢包头事件。(采样指定时器中数频率)(包头指一512包前两个字节表示第几个包)
DMA模式下,丢包严重。
其实在高速采样频率下,下位机发送包的速度与理论上相差无几,主要原因是上位机没有接收完全!
有哪位仁兄可以帮忙解答吗? 

相关帖子

沙发
smartcode| | 2008-1-4 09:06 | 只看该作者

re:

第一个问题:如果您采用DMA方式传输,是这样的。因为您的程序中有这样一段
            话:USB_DMASetTransLength(2, 512);    所以DMA控制器不读完
            512字节是不会罢休的。
            解决方法:不要采用DMA模式接收逻辑端点1的命令参数,而要采
            用从模式(CPU控制模式)。即:不使能逻辑端点1的DMA功能,直
            接调用函数:USB_ReadEndpoint()
第二个问题:原因请看周立功单片机论坛相关帖子,请点击以下链接:
            http://www.zlgmcu.com.cn/dispbbs.asp?boardID=29&ID=287&replyid=1302&skin=1
第三个问题:若要保证速度,请采用中断模式端点。
相关链接:http://www.zlgmcu.com.cn/dispbbs.asp?boardID=29&ID=287&replyid=1302&skin=1

使用特权

评论回复
板凳
helfen|  楼主 | 2008-1-4 09:49 | 只看该作者

感谢smartcode

谢谢这位仁兄
    当我上位机发送2个、64、128、256个字节时,下位机的NUM值是和上位机对应的。USB_DMASetTransLength(2, NUM); 写成512是因为上位机发的是512。
    我现在的解决办法也是逻辑端点1用一般模式。只是感觉DMA模式下为什么就不能这样?
     
    谢谢你的指点,我用中断模式试试看。

使用特权

评论回复
地板
江理默默| | 2008-1-7 20:00 | 只看该作者

回复

上述问题已给您回复了e-mail!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

5

帖子

1

粉丝