mfc 串口 mysql 教程_Win32串口操作的技巧

1.开启一个 Serial Port:

利用一般开启档案的 CreatFile() 即可开启 serial port device用 CreateFile() API.

751147c70ac3323490ecb77e66084f29.gifHANDLE CreateFile(

751147c70ac3323490ecb77e66084f29.gif LPCTSTR lpFileName,//pointer to name of the file751147c70ac3323490ecb77e66084f29.gifDWORD dwDesiredAccess,//access (read-write) mode751147c70ac3323490ecb77e66084f29.gifDWORD dwShareMode,//share mode751147c70ac3323490ecb77e66084f29.gifLPSECURITY_ATTRIBUTES lpSecurityAttributes,//pointer to security attributes751147c70ac3323490ecb77e66084f29.gifDWORD dwCreationDistribution,//how to create751147c70ac3323490ecb77e66084f29.gifDWORD dwFlagsAndAttributes,//file attributes751147c70ac3323490ecb77e66084f29.gifHANDLE hTemplateFile//handle to file with attributes to copy751147c70ac3323490ecb77e66084f29.gif);

751147c70ac3323490ecb77e66084f29.gif lpFileName 为 "COM1" 或是 "COM2"

dwDersiredAccess 一般为 GENERIC_READ|GENERIC_WRITE

dwShareMode "必须"为 0, 即不能共享, 但同一个 process 中的不同 thread 在一开启之后就可以共享.

lpSecurityAttributes 一般为 NULL

dwCreateionDistributon 在这里"必须"为 OPEN_EXISTING

dwFlagsAndAttributes 定义了开启的属性, 若是设成 FILE_FLAG_OVERLAPPED 则可使用异步的 I/O.

hTemplateFile "必须"为 NULL

传回档案 handle

设定 Serial Port 传送及接收缓冲区的大小

在开启完 serial port 之后, 可以藉由呼叫 SetupComm() 来进行配置传送时的缓冲区及接收时的缓冲区. 如果没有呼叫 SetupComm() 的话, Win95 会配置内定的缓冲区

751147c70ac3323490ecb77e66084f29.gifBOOL SetupComm(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifDWORD dwInQueue,//size of input buffer751147c70ac3323490ecb77e66084f29.gifDWORD dwOutQueue//size of output buffer751147c70ac3323490ecb77e66084f29.gif);

2.关闭 Serial Port file

利用一般的 CloseHandle() 即可.

751147c70ac3323490ecb77e66084f29.gifBOOL CloseHandle(

751147c70ac3323490ecb77e66084f29.gif HANDLE hObject//handle to object to close751147c70ac3323490ecb77e66084f29.gif)  3.取得 Seial Port 的信息

在 Win32 里头, 将一些通讯时会用到的信息用 COMMPROP 这个结构来表示. (当然不仅仅是 Serial Port) 可以用 GetCommProperties() 来取得:

751147c70ac3323490ecb77e66084f29.gifBOOL GetCommProperties(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifLPCOMMPROP lpCommProp//address of communications properties structure751147c70ac3323490ecb77e66084f29.gif);

751147c70ac3323490ecb77e66084f29.gif COMMPROP 长的像这个样子:

8c3277616daf8184bbc0ce976df0d648.gif

89eefe80a74901fde3b3c1d81791f1f3.giftypedefstruct_COMMPROP28e9a52847c69d510634cd2fa5de495a.gif{//cmmp42d6d3b4f9bf6b772bc0416ef2a7f122.gifWORD wPacketLength;//packet size, in bytes42d6d3b4f9bf6b772bc0416ef2a7f122.gifWORD wPacketVersion;//packet version42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwServiceMask;//services implemented42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwReserved1;//reserved42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwMaxTxQueue;//max Tx bufsize, in bytes42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwMaxRxQueue;//max Rx bufsize, in bytes42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwMaxBaud;//max baud rate, in bps42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwProvSubType;//specific provider type42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwProvCapabilities;//capabilities supported42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwSettableParams;//changable parameters42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwSettableBaud;//allowable baud rates42d6d3b4f9bf6b772bc0416ef2a7f122.gifWORD wSettableData;//allowable byte sizes42d6d3b4f9bf6b772bc0416ef2a7f122.gifWORD wSettableStopParity;//stop bits/parity allowed42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwCurrentTxQueue;//Tx buffer size, in bytes42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwCurrentRxQueue;//Rx buffer size, in bytes42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwProvSpec1;//provider-specific data42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD dwProvSpec2;//provider-specific data42d6d3b4f9bf6b772bc0416ef2a7f122.gifWCHAR wcProvChar[1];//provider-specific dataeb0fb5b852ef6cef467387d7fbbef20a.gif}COMMPROP;  在这里, lpCommProp 需要 programmer 自行配置空间. 有趣的问题是, 系统在这个结构之后会需要额外的空间. 但是配置者也就是 programmer 却不知道系统会需要多少. 很简单的做法是配置一大块绝对会够的空间. 另一个聪明的做法是执行两次 GetCommProperties() , 第一次只配置 sizeof(COMMPROP) 这么大的空间, 因为还没有开始执行一些动作, 所以系统并不会尝试着在后面填东西, 所以不会出问题. 接着执行第一次的 GetCommProperties(), 得到结果, 取出结构中的 wPacketLength, 这个 member 代表实际上需要的大小, 然后依据这个大小重新配置一个新的. 这样的话 , 就不会有浪费任何空间的问题了.

至于上述 COMMPROP 结构的成员所代表的意思, on-line help 中应该写的都满清楚的 .4.设定及取得通讯状态

你可以利用 COMMPROP 来取得一些状态, 但是当你想改变目前的设定时你需要两个 API 来完成:

751147c70ac3323490ecb77e66084f29.gifBOOL GetCommState(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifLPDCB lpDCB//address of device-control block structure751147c70ac3323490ecb77e66084f29.gif);

751147c70ac3323490ecb77e66084f29.gif

751147c70ac3323490ecb77e66084f29.gifBOOL SetCommState(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifLPDCB lpDCB//address of device-control block structure751147c70ac3323490ecb77e66084f29.gif);

你可以用 GetCommState() 来取得目前 Serial Port 的状态, 也可以用 SetCommState() 来设定 Serial Port 的状态.

DCB 的结构就请自行翻阅 help 啰.

另外, programmer 最常控制的几个设定就是 baud rate, parity method, data bits, 还有 stop bit. BuildCommDCB() 提供了对于这几个常见设定的控制.

751147c70ac3323490ecb77e66084f29.gifBOOL BuildCommDCB(

751147c70ac3323490ecb77e66084f29.gif LPCTSTR lpDef,//pointer to device-control string751147c70ac3323490ecb77e66084f29.gifLPDCB lpDCB//pointer to device-control block751147c70ac3323490ecb77e66084f29.gif);

751147c70ac3323490ecb77e66084f29.giflpDef 长的像这样: "baud=2400 parity=N data=8 stop=1"

5.通讯设定对话盒

Win32 API 中提供了一个开启通讯设定对话盒的 API: CommConfigDialog(), 当呼叫这个 API 时, 会蹦现一个可供设定 Baud Rate, Data Bits, Parity .. 等信息的对话盒, programmer 可以利用它来让使用者设定一些信息, 并且取得结果.

751147c70ac3323490ecb77e66084f29.gifBOOL CommConfigDialog(

751147c70ac3323490ecb77e66084f29.gif LPTSTR lpszName,//pointer to device name string751147c70ac3323490ecb77e66084f29.gifHWND hWnd,//handle to window751147c70ac3323490ecb77e66084f29.gifLPCOMMCONFIG lpCC//pointer to comm. configuration structure751147c70ac3323490ecb77e66084f29.gif);  其中 lpCC 被用来存放设定值的结果.

8c3277616daf8184bbc0ce976df0d648.gif

89eefe80a74901fde3b3c1d81791f1f3.giftypedefstruct_COMM_CONFIG28e9a52847c69d510634cd2fa5de495a.gif{

42d6d3b4f9bf6b772bc0416ef2a7f122.gif DWORD dwSize;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif WORD wVersion;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif WORD wReserved;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif DCB dcb;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif DWORD dwProviderSubType;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif DWORD dwProviderOffset;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif DWORD dwProviderSize;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif WCHAR wcProviderData[1];

eb0fb5b852ef6cef467387d7fbbef20a.gif}COMMCONFIG,*LPCOMMCONFIG;

在我们呼叫 CommConfigDialog() 之前, dwSize 要设为 sizeof(COMMCONFIG), wVersion 的值在这边似乎不重要(我不清楚, VC5 的 on-line help 说可以设为 1, 我手中的 book 的范例是设为 0x100), 呼叫完 CommConfigDialog() 之后, 成员 dcb 中的 BaudRate, ByteSize, StopBits, Parity 就是使用者的设定.

6.Timeout 的机制

因为传输时并不会维持一个绝对稳定的速率. 因为传输品质的关系, programer 会需要 timeout 的机制来协助他们做一些控制. 在 Win32 通讯 Timeout 的机制中, timeout 的性质共分为两类, 先来看看 COMMTIMEOUTS 这个结构:

8c3277616daf8184bbc0ce976df0d648.gif

89eefe80a74901fde3b3c1d81791f1f3.giftypedefstruct_COMMTIMEOUTS28e9a52847c69d510634cd2fa5de495a.gif{//ctmo42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD ReadIntervalTimeout;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif DWORD ReadTotalTimeoutMultiplier;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif DWORD ReadTotalTimeoutConstant;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif DWORD WriteTotalTimeoutMultiplier;

42d6d3b4f9bf6b772bc0416ef2a7f122.gif DWORD WriteTotalTimeoutConstant;

eb0fb5b852ef6cef467387d7fbbef20a.gif}COMMTIMEOUTS,*LPCOMMTIMEOUTS;  programmer 可以利用 GetCommTimeouts() 和 SetCommTimeouts() 来读取或是设定目前的 timeout 值.

751147c70ac3323490ecb77e66084f29.gifBOOL GetCommTimeouts(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifLPCOMMTIMEOUTS lpCommTimeouts//address of comm. time-outs structure751147c70ac3323490ecb77e66084f29.gif);

751147c70ac3323490ecb77e66084f29.gif

751147c70ac3323490ecb77e66084f29.gifBOOL SetCommTimeouts(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifLPCOMMTIMEOUTS lpCommTimeouts//address of communications time-out structure751147c70ac3323490ecb77e66084f29.gif);第一种 timeout 的机制称为 interval timeout, 从字面上的意义很容易可以理解这种 timeout 的机制是读取字符之间的间隔时间的 timeout, 只有读取字符时才能够使用interval timeout. 也就是在这个结构中的 ReadIntervalTimeout, 单位为 ms, 当读取完一个字符后, 超过了 ReadIntervalTimeout 的值, 却还没有读到下一个字符时, timeout 就发生了.

第二种 timeout 的机制称为 total timeout, 顾名思义即是传输的总时间的 timeout . 在这种 timeout 的机制下, Win32 提供了一个具有弹性的方式来设定 total timeout. 以读取的 total timeout 为例, 利用 ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 构成了一个线性的上限值. 什么意思呢? 实际上的 total timeout 应该是这样的一个式子:

ReadTotalTimeout = ReadTotalTimeOutMultiplier * BytesToRead + ReadTotalTimeoutConstant

WriteTotalTimeout 用同样的公式来计算. 这样的话, 不仅可以用一个固定的值来做为 timeout 值, 也可以用条线来做为 timeout 的值, 而随着要读取或是要写的 bytes 数而变动.

如果不想使用 timeout, 就把 COMMTIMEOUTS 里头的资料成员都填为 0.

如果你将 ReadIntervalTimeout 设为 MAXDWORD, 且将 ReadTotalTimeOutMultiplier 和 ReadTotalTimeoutConstant 都设为 0 的话, 那么读取时, 如果 receive queue 里头并没有资料, 读取的动作将会马上返回, 而不会停滞在读取的动作.

这里有一个和 BuildCommDCB() 很像的 API 叫 BuildCommDCBAndTimeouts():

751147c70ac3323490ecb77e66084f29.gifBOOL BuildCommDCBAndTimeouts(

751147c70ac3323490ecb77e66084f29.gif LPCTSTR lpDef,//pointer to the device-control string751147c70ac3323490ecb77e66084f29.gifLPDCB lpDCB,//pointer to the device-control block751147c70ac3323490ecb77e66084f29.gifLPCOMMTIMEOUTS lpCommTimeouts//pointer to comm. time-out structure751147c70ac3323490ecb77e66084f29.gif);

lpDef 一样是控制字符串, 可以给像 BuildCommDCB() 中的 lpDef 那样格式的字符串, 但是多了 "TO=XXX" 这个设定. 如果 "TO=ON", 这个 API 会依据 lpCommTimeouts 里头的值来设定读和写的 timeout 值. 如果 "TO=OFF", 则会设定这个 device 没有 timeout. 如果是 "ON" 和 "OFF" 之外的其它值, 则 lpCommTimeouts 的设定将会被忽略.

对了, 在设定完 timeout 值之后, 记得要检查 COMMPROP 里的 dwProvCapabilities 中的 PCF_INTTIMEOUTS 和 PCF_TOTALTIMEOUTS 两个 flags 是否有被 set, 以确认 interval timeout 和 total timeout 是否有支持.

7.读取资料

从 serial port 里头读取资料就跟读取一般的档案一样, 使用 ReadFile() 来达成.

751147c70ac3323490ecb77e66084f29.gifBOOL ReadFile(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of file to read751147c70ac3323490ecb77e66084f29.gifLPVOID lpBuffer,//address of buffer that receives data751147c70ac3323490ecb77e66084f29.gifDWORD nNumberOfBytesToRead,//number of bytes to read751147c70ac3323490ecb77e66084f29.gifLPDWORD lpNumberOfBytesRead,//address of number of bytes read751147c70ac3323490ecb77e66084f29.gifLPOVERLAPPED lpOverlapped//address of structure for data751147c70ac3323490ecb77e66084f29.gif);

要注意的是, nNumberOfBytesToRead 设定的是一次最多的读取量, 很有可能所读取的值(检查 lpNumberOfBytesRead)小于这个值. 通常在错误发生或是 timeout 发生时这个 API 就会返回.

PurgeComm() 这个 API 可以用来终止目前正在进行的读或写的动作, 也可以 flush 掉 I/O buffer 内等待读或写的资料.

751147c70ac3323490ecb77e66084f29.gifBOOL PurgeComm(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications resource751147c70ac3323490ecb77e66084f29.gifDWORD dwFlags//action to perform751147c70ac3323490ecb77e66084f29.gif);

其中 dwFlags 共有四种 flags:

PURGE_TXABORT: 终止目前正在进行的(背景)写入动作

PURGE_RXABORT: 终正目前正在进行的(背景)读取动作

PURGE_TXCLEAR: flush 写入的 buffer

PURGE_RXCLEAR: flush 读取的 buffer

而使用 FlushFileBuffers() 可以确保所有的资料都被送出, 这个 API 才会返回.

另外一个有趣的 API 是 ClearCommError(), 从字面上的意思看来, 它是用来清除错误情况用的, 但是实际上它还可以拿来取得目前通讯设备的一些信息.

751147c70ac3323490ecb77e66084f29.gifBOOL ClearCommError(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle to communications device751147c70ac3323490ecb77e66084f29.gifLPDWORD lpErrors,//pointer to variable to receive error codes751147c70ac3323490ecb77e66084f29.gifLPCOMSTAT lpStat//pointer to buffer for communications status751147c70ac3323490ecb77e66084f29.gif);

呼叫这个 API 之后, 关于通讯设备的一些信息会被储存在 lpStat 中, COMSTAT 的结构如下:

751147c70ac3323490ecb77e66084f29.gif8c3277616daf8184bbc0ce976df0d648.gif

89eefe80a74901fde3b3c1d81791f1f3.giftypedefstruct_COMSTAT28e9a52847c69d510634cd2fa5de495a.gif{//cst42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD fCtsHold :1;//Tx waiting for CTS signal42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD fDsrHold :1;//Tx waiting for DSR signal42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD fRlsdHold :1;//Tx waiting for RLSD signal42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD fXoffHold :1;//Tx waiting, XOFF char rec'd42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD fXoffSent :1;//Tx waiting, XOFF char sent42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD fEof :1;//EOF character sent42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD fTxim :1;//character waiting for Tx42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD fReserved :25;//reserved42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD cbInQue;//bytes in input buffer42d6d3b4f9bf6b772bc0416ef2a7f122.gifDWORD cbOutQue;//bytes in output buffereb0fb5b852ef6cef467387d7fbbef20a.gif}COMSTAT,*LPCOMSTAT

藉由 fCtsHold, fDsrHold, fRlsdHold, fXoffHold, fXoffSent 可以知道目前因为什么因素而使通讯阻碍住了.( 跟 handshaking 和 flow control 有关) cbInque 和 cbOutQue 则可以显示出还有多少 bytes 在读取或是写入 queue 中.

8.写入资料

和读取资料一样, programmer 可以使用 WriteFile() 来将资料写入 serial port.

751147c70ac3323490ecb77e66084f29.gifBOOL WriteFile(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle to file to write to751147c70ac3323490ecb77e66084f29.gifLPCVOID lpBuffer,//pointer to data to write to file751147c70ac3323490ecb77e66084f29.gifDWORD nNumberOfBytesToWrite,//number of bytes to write751147c70ac3323490ecb77e66084f29.gifLPDWORD lpNumberOfBytesWritten,//pointer to number of bytes written751147c70ac3323490ecb77e66084f29.gifLPOVERLAPPED lpOverlapped//pointer to structure needed for overlapped I/O751147c70ac3323490ecb77e66084f29.gif);

关于通讯设备的写入有三个很有趣的 API, 它们分别是 SetCommBreak(), ClearCommBreak, 和 TransmitCommChar().

751147c70ac3323490ecb77e66084f29.gifBOOL SetCommBreak(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile//handle of communications device751147c70ac3323490ecb77e66084f29.gif);

751147c70ac3323490ecb77e66084f29.gif

751147c70ac3323490ecb77e66084f29.gifBOOL ClearCommBreak(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile//handle to communications device751147c70ac3323490ecb77e66084f29.gif);

751147c70ac3323490ecb77e66084f29.gif

751147c70ac3323490ecb77e66084f29.gifBOOL TransmitCommChar(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifcharcChar//character to transmit751147c70ac3323490ecb77e66084f29.gif);

SetCommBreak() 是用来暂停目前的传输作业, 它会使 buffer 中的资料都不再被送出, 这个时候, program 可以去做些杂七杂八的事, 之后, 再利用 ClearCommBreak() 回复传输作业.

TransmitCommChar() 是用来立即性的赶在所有 buffer 数据被送出去之前, 传输一个字符的数据出去, 即使 buffer 中还有资料. 换句话说, 这个字符有最高的优先权被送出去.

9.事件驱动式的 I/O

在 Win32 里头, 对于通讯设备的 I/O 可以用像是事件驱动式的方法来达成. 主要是利用一个叫 WaitCommEvent() 的 API. 呼叫这个 API 之后, 会一直 block 到设定的事件发生之后才会返回. 我们先来看看如何设定事件, 再回过头来看 WaitCommEvent() .

programer 可以用 GetCommMask() 和 SetCommMask() 来取得或是设定目前设定的通讯事件.

751147c70ac3323490ecb77e66084f29.gifBOOL GetCommMask(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifLPDWORD lpEvtMask//address of variable to get event mask751147c70ac3323490ecb77e66084f29.gif);

751147c70ac3323490ecb77e66084f29.gif

751147c70ac3323490ecb77e66084f29.gifBOOL SetCommMask(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifDWORD dwEvtMask//mask that identifies enabled events751147c70ac3323490ecb77e66084f29.gif);

可以设定的事件有 EV_BREAK, EV_CTS, EV_DSR, EV_ERR, EV_RING, EV_RLSD, EV_RXCHAR, EV_RXFLAG, EV_TXEMPTY.(其意义请自行参考 help), 当然, 你可以把它们 or 起来成为组合的事件.

在设定完想要处理的事件之后, 可以使用 WaitCommEvent()

751147c70ac3323490ecb77e66084f29.gifBOOL WaitCommEvent(

751147c70ac3323490ecb77e66084f29.gif HANDLE hFile,//handle of communications device751147c70ac3323490ecb77e66084f29.gifLPDWORD lpEvtMask,//address of variable for event that occurred751147c70ac3323490ecb77e66084f29.gifLPOVERLAPPED lpOverlapped,//address of overlapped structure0T944A64-0.g<div%20class='share%20layui-clear%20bdsharebuttonbox'><li%20><a%20href='javascript:;'%20data-cmd=微信

本文原创发布php中文网,转载请注明出处,感谢您的尊重!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值