想实现PPC通过USB数据线与台式电脑(虚拟出一个串口)间的通讯

xietulhh 2017-09-30 05:10:21
最近遇到编程问题 实在没办法了
我手上有一个pocket pc 2003(惠普掌上电脑HP iPAQ rz1717)芯片是S3C2410
我想实现PPC通过USB数据线与台式电脑间的通讯,通过在台式电脑虚拟一个串口来通讯,这一步是为了调试上位机程序。

想通过第一步了解一下这方面的原理,最终实现PPC与Arduino Nano Mega328控制器通讯,来控制舵机;
PPC上有上位机程序,并虚拟出一串口,通过USB数据线与Arduino Nano Mega328控制器通讯

不知道谁有例程 或者了解这方面的信息。。。

我曾下载了一个例程(USB Virtual COM S3C24XX),几乎把所有代码都注释说明了,但还是搞不懂他是如何通过USB把数据发送至PC的,mai()函数如下


/****************************************************************
NAME: usbmain.c
DESC: endpoint interrupt handler:端点中断处理程序       
USB init jobs:USB初始化作业
HISTORY:
Mar.25.2002:purnnamu: ported for S3C2410X:移植到S3C2410X
Mar.27.2002:purnnamu: DMA is enabled:DMA已启用

INC目录下的文件:
2410addr.h/2410addr.inc/2410lib.h/2410slib.h/Def.h/Memcfg.inc/Option.inc/Uart0.h/mmu.h/Option.h
Def.h:基本数据类型重定义头文件,在定义数据类型时尽量使用U32,U16,S32,S16,U8,S8等类型,以增强程序的可移植性。
Option.h:硬件系统重要设置头文件,如果要修改系统的工作频率、总线宽度,一些重要的地址的值可在本文件中修改。
2410addr.h:2410的寄存器的地址宏定义头文件,方便使用。
2410addr.inc:注意,在汇编语言中,用此文件。
2410lib.h:包含调试时常用函数,还有一些其他常用函数的头文件。
2410slib.h:包含MMU相关函数的头文件。

SRC目录下的文件:
2410init.s:2410初始化启动程序,由汇编语言写成。
2410lib.c:描述了2410的调试常用函数的原型,用C语言写成。
2410slib.s:包含汇编语言写的MMU相关的程序代码。
mmu.c:包含MMU相关的程序代码。
Uart0.c:包含了串口的常用函数原型,用C语言写成。
****************************************************************/
#include <string.h>
#include <stdarg.h>//C语言中C标准函数库的头文件,stdarg是由standard(标准) arguments(参数)简化而来,主要目的为让函数能够接收可变参数
#include "option.h"
#include "2410addr.h"
#include "2410lib.h"
#include "def.h"

#include "2410usb.h"
#include "usbmain.h"
#include "usblib.h"
#include "usbsetup.h"
#include "usbout.h"
#include "usbin.h"

/**************************
Some PrepareEp1Fifo() should be deleted
一些PrepareEp1Fifo()应该被删除
**************************/

void UsbdMain(void)
{
int i;
U8 tmp1;
U8 oldTmp1=0xff;

//ChangeUPllValue(0x48,0x3,0x2); //UCLK=48Mhz :设置UCLK的值。给USB供节拍。
//ChangeUPllValue(40,1,2); //UCLK=48Mhz setting in init.s
InitDescriptorTable(); // 初始化设备描述符,比如设备描述符,配置描述符等,在usb控制传输的时候返回给设备。这个函数在usbsetup.c中
//ResetUsbd();

ConfigUsbd(); //这个函数是个封装函数,第一次配置调用他,并且开启中断。在重置USB的过程中调用的是ReconfigUsbd(),这个函数也在usblib.c中
//PrepareEp1Fifo();//准备发送数据FIFO队列(端点1的,这里没有用到)

//DetectVbus(); //not used in S3C2400X

#if 0
while(1)
{
if(DbgPrintfLoop())continue;//通过串口逐个字节输出数据

Delay(5000);
if((i++%2)==0)Led_Display(0x8);//Led_Display()位于2410lib.h
else Led_Display(0x0);
}
#endif

/*20170918help:关于“#if 0/#if 1 ... #endif”的作用
#if 0
code

#endif

C标准不提供C++里的“//”这样的单行风格注释而只提供“/星号 星号/”这样的块注释功能,我们通常使用它写代码中说明性的注释文字(注释作用)以及在调试时关闭某段代码对编译器的可见
性(屏蔽作用),当然,这里所谓的“注释作用”和“屏蔽作用”是我们从功能上下的主观定义,对预处理器而言,两者并无任何区别。

(1)code中定义的是一些调试版本的代码,此时code完全被编译器忽略。如果想让code生效,只需把#if 0改成#if 1
(2)#if 0还有一个重要的用途就是用来当成注释,如果你想要注释的程序很长,这个时候#if 0是最好的,保证不会犯错误。(但是林锐的书上说千万不要把#if 0 来当作块注释使用)
#if 1可以让其间的变量成为局部变量。
(3)这个结构表示你先前写好的code,现在用不上了,又不想删除,就用这个方法,比注释方便。
*/
}

/*******************************************************************usb设备中断服务程序**********************************************************/
/*20170928help:关于 __irq
在ADS编译器中,“__irq”专门用来生命IRQ中断服务程序,如果用__irq来声明一个函数,那么该函数表示一个IRQ中断服务程序,编译器会自动在该函数内部增加中断现场保护的代码
armcc是ARM C编译器。这个编译器通过了 Plum Hall C Validation Suite,为ANSI C的一致性测试。armcc 用于将用ANSI C编写的程序编译成32位ARM指令代码。因为armcc是我们最
常用的编译器,所以对此作一个详细的介绍。
*/
void __irq IsrUsbd(void)
{
U8 usbdIntpnd,epIntpnd;
U8 saveIndexReg=rINDEX_REG; //读取索引寄存器值(rINDEX_REG地址在2410addr.h定义)
usbdIntpnd=rUSB_INT_REG;// 读取USB 中断寄存器(rUSB_INT_REG地址在2410addr.h定义)
epIntpnd=rEP_INT_REG;// 读取端点中断寄存器(rEP_INT_REG地址在2410addr.h定义)
//DbgPrintf( "[INT:EP_I=%x,USBI=%x]",epIntpnd,usbIntpnd );

if(usbdIntpnd&SUSPEND_INT)//总线上超过 3ms 没有活动信号引起中断
{
rUSB_INT_REG=SUSPEND_INT;//清除中断标志位
DbgPrintf( "<SUS]");
}
if(usbdIntpnd&RESUME_INT)
{
rUSB_INT_REG=RESUME_INT; //恢复引起的中断
DbgPrintf("<RSM]");
}


/*01:RESET_INT复位中断
主机集线器检测到设备,主机发总线复位。这个复位与USB上电复位和系统复位是不同的。这个是SIE根据总线状态通知用户的一种复位。设备产生复位中断,如何处理由设备固
件程序决定。

SIE (Serial Interface Engine)是 USB 外设最重要的硬件组成部分之一,它主要由四部分组成:
1) 硬件上用来完成 NRZI 编/译码和加/去填充位操作的,NRZI/Bit Buffing 和NRZO/Bit Unstuffing 的部分。
2) 硬件上产生资料的 CRC 校验码并对资料包进行 CRC 校验的 CRC check & Generator 部分。
3) 用来将并行资料转化成 USB 串行资料的并/串转换部分( Packet Encode ),将主机发送的 USB 资料包转化成可以识别的并行资料的串/并转换部分( Packet Decode )。
4) 检测和产生 SOP (即每个资料包的同步字段)和 EOP 信号的部分
*/

if(usbdIntpnd&RESET_INT)//接收到复位信号引起的中断
{
DbgPrintf( "<RST]");

//ResetUsbd();
ReconfigUsbd();//位于usblib.c
/*重新配置 USB device:
01禁止设备进入挂起模式( 正常模式)
02端点0最大数据包
03对端点0清除SETUP_END和OUT_PKT_RDY状态

04接下来对具体端点进行操作.如果该端点已初始化,则读取IN_CSR2_REG和OUT_CSR2_REG寄存器的值并保存.
04.1接着设置IN_CSR2_REG,端点方向为IN,禁止的DMA中断.
设置IN_CSR1_REG清除data toggle bit,包的PID(标识符段)包含DATA0.
04.2然后设置OUT方向,
设置OUT_CSR1_REG FLUSH FIFO,清除data toggle bit,数据包顺序设为DATA0,
设置OUT_CSR2_REG,禁止DMA中断.
04.3如果初始化状态为TRUE,则恢复之前保存的寄存器内容.

05最后清除所有EndPoint中断
rEP_INT_REG=EP0_INT|EP1_INT|EP2_INT|EP3_INT|EP4_INT;
rUSB_INT_REG=RESET_INT|SUSPEND_INT|RESUME_INT;

06所有端点中断使能
rEP_INT_EN_REG=EP0_INT|EP1_INT|EP3_INT;// 中断使能寄存器
rUSB_INT_EN_REG=RESET_INT;// USB 复位中断使能

07端点0 状态设置为初始化
ep0State=EP0_SETUP_STATE;
*/

rUSB_INT_REG=RESET_INT;//清除复位标志RESET_INT should be cleared after ResetUsbd().
}
//以上三个是USB设备中断(SUSPEND_INT,RESUME_INT,RESET_INT位于2410usb.h),主要用到的是复位中断,当GPC5引脚为高时,USB 插入主机,就会产生这个中断


/*02:设备复位中断之后
正常情况下,在复位中断处理以后的下一次中断应该执行到这里
*/
if(epIntpnd&EP0_INT) //如果是端点 0 中断
{
rEP_INT_REG=EP0_INT;//清除所有端点0等待位
Ep0Handler(); //进入控制传输中断处理程序,在设备枚举的过程中使用
}


if(epIntpnd&EP1_INT)//如果是端点 1 中断
{
rEP_INT_REG=EP1_INT;
Ep1Handler();//进入 Ep1 中断处理程序(BULK IN)
}


if(epIntpnd&EP2_INT)//如果是端点 2 中断
{
rEP_INT_REG=EP2_INT;
DbgPrintf("<2:TBD]"); //not implemented yet
//Ep2Handler();
}


if(epIntpnd&EP3_INT)//如果是端点 3 中断
{
rEP_INT_REG=EP3_INT;
Ep3Handler();//进入 Ep3 中断处理程序(BULK OUT)
}


if(epIntpnd&EP4_INT)//如果是端点 4 中断
{
rEP_INT_REG=EP4_INT;
DbgPrintf("<4:TBD]"); //not implemented yet
//Ep4Handler();
}
//以上5个端点中断采用相应处理程序(EP0_INT,EP1_INT,EP2_INT,EP3_INT,EP4_INT位于2410usb.h)

ClearPending(BIT_USBD);//USB 设备中断处理完毕,清除中断标志位(BIT_USBD位于2410addr.h);ClearPending()在2410addr.h定义

rINDEX_REG=saveIndexReg;//恢复索引寄存器原来的值(恢复现场)
}




/******************* Consol printf for debug 控制台printf进行调试*********************/

#define DBGSTR_LENGTH (0x1000)
U8 dbgStrFifo[DBGSTR_LENGTH];
volatile U32 dbgStrRdPt=0;
volatile U32 dbgStrWrPt=0;



void _WrDbgStrFifo(U8 c)
{
dbgStrFifo[dbgStrWrPt++]=c;
if(dbgStrWrPt==DBGSTR_LENGTH)dbgStrWrPt=0;

}

/*******************************************************************************
* Function Name : DbgPrintfLoop
* Description : 从串口发送一个字节;依靠外部函数循环发送:main()→while 1{→DbgPrintfLoop()}
*******************************************************************************/
int DbgPrintfLoop(void)
{
if(dbgStrRdPt==dbgStrWrPt)return 0;
Uart_SendByte(dbgStrFifo[dbgStrRdPt++]);//Uart_SendByte()从串口发送一个字节;位于2410lib.h

if(dbgStrRdPt==DBGSTR_LENGTH)dbgStrRdPt=0;
return 1;
}


#if 0//我把0改成1,就可以让系统只编译A部分代码而不编译B部分代码,这样改起来就很方便啊。如果发行代码和调试代码是不同的,这是一个很常见的做法。
void DbgPrintf(char *fmt,...)//...表示可变参数(多个可变参数组成一个列表,后面有专门的指针指向他),不限定个数和类型,
{
int i,slen;
va_list ap;//初始化指向可变参数列表的指针;VA_LIST 是在C语言中解决变参问题的一组宏,所在头文件stdarg.h,用于获取不确定个数的参数。
char string[256];

va_start(ap,fmt);
//将第一个可变参数的地址付给ap,即ap指向可变参数列表的开始
//va_start(函数名称,stdarg.h),读取可变参数的过程其实就是在堆栈中,使用指针,遍历堆栈段中的参数列表,从低地址到高地址一个一个地把参数内容读出来的过程
vsprintf(string,fmt,ap);
//将参数fmt、ap指向的可变参数一起转换成格式化字符串,放string数组中,其作用同sprintf(),只是参数类型不同;vsprintf位于stdarg.h
//vsprintf() 中的 arg 参数位于数组中。数组的元素会被插入主字符串的百分比 (%) 符号处。该函数是逐步执行的。在第一个 % 符号中,插入 arg1,在第二
//个 % 符号处,插入 arg2,依此类推。

slen=strlen(string);
//strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串
//结束符'\0'为止,然后返回计数器值(长度不包含'\0')。位于stdarg.h

for(i=0;i<slen;i++)
_WrDbgStrFifo(string[i]);

va_end(ap);//ap付值为0,没什么实际用处,主要是为程序健壮性
}
#else
void DbgPrintf(char *fmt,...)
{
}
#endif

...全文
360 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
91program 2017-10-02
  • 打赏
  • 举报
回复
pocket pc 2003 + USB,可以考虑通过 ActiveSync +RAPI 来实现简单的数据传输与控制
xietulhh 2017-09-30
  • 打赏
  • 举报
回复
就在这里: void UsbdMain(void) { int i; U8 tmp1; U8 oldTmp1=0xff; //ChangeUPllValue(0x48,0x3,0x2); //这句作者注释掉了,但是这样USB不就没上电了吗??? //ChangeUPllValue(40,1,2); //UCLK=48Mhz setting in init.s InitDescriptorTable(); //ResetUsbd(); ConfigUsbd(); //PrepareEp1Fifo();//这一句也注释掉了,我看了原函数他是向端点1(IN)缓冲区FIFO中写入数据;如果注释掉的话,不就是也没办法向PC发送数据了吗 //DetectVbus(); //not used in S3C2400X #if 0 while(1) { if(DbgPrintfLoop())continue;//这句是通过串口发送数据的,看了一下原代码,他发送到了 UART发送缓冲寄存器;这样是不是没通过USB发送啊 Delay(5000); if((i++%2)==0)Led_Display(0x8);//Led_Display()位于2410lib.h else Led_Display(0x0); } #endif } PrepareEp1Fifo()函数: void PrepareEp1Fifo(char *Msg, U32 MsgLen) { U8 in_csr1; rINDEX_REG=1;//对端点1进行操作 in_csr1=rIN_CSR1_REG;//读取端点1的状态 if(sent == 1) return; WrPktEp1((U8*)Msg, MsgLen < EP1_PKT_SIZE ? MsgLen : EP1_PKT_SIZE);//WrPktEp1()向端点1缓冲区FIFO中写入数据;位于usblib.c /*20170928help:关于?:表达式 条件运算符(?:)是C语言中唯一的一个三目运算符,它是对第一个表达式作真/假检测,然后根据结果返回另外两个表达式中的一个。 <表达式1>?<表达式2>:<表达式3> 在运算中,首先对第一个表达式进行检验,如果为真,则返回表达式2的值;如果为假,则返回表达式3的值 */ SET_EP1_IN_PKT_READY(); } DbgPrintfLoop()函数: /******************************************************************************* * Function Name : DbgPrintfLoop * Description : 从串口发送一个字节;依靠外部函数循环发送:main()→while 1{→DbgPrintfLoop()} *******************************************************************************/ int DbgPrintfLoop(void) { if(dbgStrRdPt==dbgStrWrPt)return 0; Uart_SendByte(dbgStrFifo[dbgStrRdPt++]);//Uart_SendByte()从串口发送一个字节;位于2410lib.h if(dbgStrRdPt==DBGSTR_LENGTH)dbgStrRdPt=0; return 1; } DbgPrintfLoop()函数用到的Uart_SendByte(): /******************************************************************************* * Function Name : Uart_SendByte * Description : 首先是串口通道的选择;然后判断THR是否为空,直到等到发送缓存 为空,退出while循环;再要发送的字节data放到发送缓存中。如果 发送的数据是回车的话,直接发送转义字符/r,当然也要等到THR为 空才可以发送。 *******************************************************************************/ void Uart_SendByte(int data) { #if 0 if(whichUart==0)//选择通道0 { if(data=='\n')//当发送的是换行回车符时做的动作 { while(!(rUTRSTAT0 & 0x2)); //直到发送缓存区为空 //rUTRSTAT0:UART TX/RX寄存器状态 //串口发送 串口发送数据通过判断rUTRSTATn (n=0,1,2)的第2位是不是为1来判断发送缓存是否为空 Delay(15);//because the slow response of hyper_terminal:因为hyper_terminal的响应缓慢 WrUTXH0('\r'); //向发送缓存写入转义字符 //WrUTXH0():宏,将数据写到寄存器地址中并转换成char型;位于2410addr.h } //等待原来的数据已经发送结束再把数据写进发送缓冲寄存器 while(!(rUTRSTAT0 & 0x2)); //直到发送缓存区为空 //rUTRSTAT0:UART TX/RX寄存器状态 //串口发送 串口发送数据通过判断rUTRSTATn (n=0,1,2)的第2位是不是为1来判断发送缓存是否为空 Delay(15); WrUTXH0(data); //写入下一个数据到发送缓存 //WrUTXH0():宏,将数据写到寄存器地址中并转换成char型;位于2410addr.h } else if(whichUart==1)//选择通道1 { if(data=='\n')//遇到结束符 { while(!(rUTRSTAT1 & 0x2));//等发送完上一个数据 Delay(15);//because the slow response of hyper_terminal rUTXH1 = '\r';//向发送缓存写入转义字符 } while(!(rUTRSTAT1 & 0x2));//Wait until THR is empty. Delay(15); rUTXH1 = data;//写入下一个数据到发送缓存 } else if(whichUart==2)//选择通道2 { if(data=='\n')//遇到结束符 { while(!(rUTRSTAT2 & 0x2));//直到发送缓存区为空 Delay(15);//because the slow response of hyper_terminal rUTXH2 = '\r';//向发送缓存写入转义字符 } while(!(rUTRSTAT2 & 0x2));//等发送完上一个数据 Delay(15); rUTXH2 = data;//写入下一个数据到发送缓存 } #else if(whichUart==0)//选择第几个串口 { if(data=='\n')//当发送的是换行回车符时做的动作 { WrUTXH0('\r'); while(!(rUTRSTAT0 & 0x2)); } else { WrUTXH0(data); while(!(rUTRSTAT0 & 0x2));//Wait until THR is empty. } } else if(whichUart==1) { if(data=='\n') { rUTXH1 = '\r'; while(!(rUTRSTAT1 & 0x2)); } else { rUTXH1 = data; while(!(rUTRSTAT1 & 0x2));//Wait until THR is empty. } } else if(whichUart==2) { if(data=='\n') { rUTXH2 = '\r'; while(!(rUTRSTAT2 & 0x2)); } else { rUTXH2 = data; while(!(rUTRSTAT2 & 0x2));//Wait until THR is empty. } } #endif }

19,502

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 嵌入开发(WinCE)
社区管理员
  • 嵌入开发(WinCE)社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧