关于C语言的一个简单工具类(包含常用进制转换和CRC,MD5,BBC)

目录

一. 前言

二. 进制转换和格式控制

十进制转为十六进制函数

十六进制字符串转换成十六进制数组

把字符串中的空格符号删除

比较两个数组之间是否相同

命令响应数据中查找关键字

16位数据高低字节互换 

三. 算法类

MD5

CRC效验

BBC效验

四. 完整文件

tool.c

tool.h


一. 前言

搞嵌入式开发的小伙伴,平时主要就是用到的C语言,当然我们很多时候再处理数据的时候需要用到一下进制的转换和一些效验方法,今天我整理了一些我常用的一些工具类分享给大家,希望对大家有所帮助😁😁😁

二. 进制转换和格式控制

十进制转为十六进制函数

/*********************************************************************************
* @Function	:  十进制转为十六进制函数
* @Input 		:  <*value>,为要被转换的十进制
*						:  <buffer>,转换完成保存的十六进制的位置
*						:  <length>,参数为转换后十六进制的长度
* @Output 	:  None
* @Return 	:  None
* @Others  	:  None
* @Date 		:  2022-10-10
**********************************************************************************/
void NumToHex(U32  value, char buffer[], int length)
{
  U32 i=(sizeof(unsigned  int)*2);
  U32 temp;
	int j=0;
    while(i--)
    {
        temp = (value&(0xf<<(4*i)))>>(4*i);
        if(temp>9)
            buffer[j] = 'A'+temp-10;
		else
			buffer[j] = '0'+temp;
		j++;
    }
	buffer[length] = '\0';
}

十六进制字符串转换成十六进制数组

/*********************************************************************************
* @Function	:  十六进制字符串转换成十六进制数组
* @Input 		:  <*str>,字符串输入
*						:  <*out>,十六进制数组输出缓冲区
*						:  <*outlen>,十六进制数组长度
* @Output 	:  None
* @Return 	:  数组长度
* @Others  	:  十六进制数组长度
* @Date 		:  2022-08-23
**********************************************************************************/
U32 StringToHex(char *str, U8 *out, U32 *outlen)
{
    char *p = str;
    char high = 0, low = 0;
    int tmplen = strlen(p), cnt = 0;
    tmplen = strlen(p);
    while(cnt < (tmplen / 2))
    {
        high = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;
        low = (*(++ p) > '9' && ((*p <= 'F') || (*p <= 'f'))) ? *(p) - 48 - 7 : *(p) - 48;
        out[cnt] = ((high & 0x0f) << 4 | (low & 0x0f));
        p ++;
        cnt ++;
    }
    if(tmplen % 2 != 0) out[cnt] = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;

    if(outlen != NULL) *outlen = tmplen / 2 + tmplen % 2;
    return tmplen / 2 + tmplen % 2;
}

把字符串中的空格符号删除

/*********************************************************************************
* @Function	:  把字符串中的空格符号删除
* @Input		:  <str>,字符串内容 如:{123  abcA  BC}
* @Output		:  None
* @Return		:  返回 <str> 返回的hex内容
* @Others		:  None
* @Date			:  2022-06-30
**********************************************************************************/
char * delete_str_blank(char *str)
{
	char *p=str;
	int i=0;
	while(*p)
	{
		if(*p!=' ')
			str[i++]=*p;
		p++;
	}
	str[i]='\0';
	return str;
}

比较两个数组之间是否相同

/*********************************************************************************
* @Function	:  比较两个数组之间是否相同
* @Input 		:  <Array1>,比较数组
*						:  <Array2>,被比较数组
* @Output 	:  None
* @Return 	:  返回<MY_SUCCESSFUL>两数组数据相同,返回<MY_FAIL>两数组数据不同
* @Others  	:  None
* @Date 		:  2022-08-26
**********************************************************************************/
mySta Compare_Array_Data(U8 Array1[256], U8 Array2[256])
{
	U8 i = 0,j = 0;
	U8 flag = 0;
	U8 Array1_len =sizeof((const char*) Array1);//获取数组一长度
	U8 Array2_len =sizeof((const char*) Array1);//获取数组二长度
	if(Array1_len == Array2_len){
		for(i=0; i<Array1_len; i++)
		{
			if(Array1[i] == Array2[j++])
			{
				flag=1;
			}else break;
		}
	}
	if(!flag){
		return MY_FAIL;
	}else return MY_SUCCESSFUL;
}

命令响应数据中查找关键字

/*********************************************************************************
* @Function	:  命令响应数据中查找关键字
* @Input 		:  <UartRecvBuf>,字符串内容
*						:  <keyword>,被检索的关键字
* @Output 	:  None
* @Return 	:  返回<1>找到关键字,返回<0>未找到
* @Others  	:  None
* @Date 		:  2022-08-23
**********************************************************************************/
mySta find_keyword_matching(U8 *UartRecvBuf, U8 *keyword)
{

	/* 检索检索到关键词 */
	if (( bool ) strstr ( (char*)UartRecvBuf, (char*)keyword))
	{
		return MY_SUCCESSFUL;
	}
	return MY_FAIL;
}

16位数据高低字节互换 

/*********************************************************************************
* @Function	:	16位数据高低字节互换
* @Input		:	pData,数据
*						:	size,数据的大小 单位字节
* @Output		: 	None
* @Return		: 	None
* @Others		: 	None
*						:	同步设备寄存器中的信息
* @Date			:  2022-04-05
**********************************************************************************/
void data_max_to_min( U16 *pData, U8 size )
{
	U8 i = 0;
	U16 tempData = 0 ;
	for( i = 0; i < size / 2; i ++ )
	{
		tempData = (U16)(*( pData + i ) >> 8 );//获取高字节
		tempData |= (U16)(*( pData + i ) << 8 );//获取低字节
		*( pData + i ) = tempData;
	}

}

三. 算法类

MD5

md5.h

#ifndef __MD5_H
#define	__MD5_H
 
typedef struct
{
    unsigned int count[2];
    unsigned int state[4];
    unsigned char buffer[64];   
}MD5_CTX;
 
                         
#define F(x,y,z) ((x & y) | (~x & z))
#define G(x,y,z) ((x & z) | (y & ~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y ^ (x | ~z))
#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
#define FF(a,b,c,d,x,s,ac) \
          { \
          a += F(b,c,d) + x + ac; \
          a = ROTATE_LEFT(a,s); \
          a += b; \
          }
					
#define GG(a,b,c,d,x,s,ac) \
          { \
          a += G(b,c,d) + x + ac; \
          a = ROTATE_LEFT(a,s); \
          a += b; \
          }
					
#define HH(a,b,c,d,x,s,ac) \
          { \
          a += H(b,c,d) + x + ac; \
          a = ROTATE_LEFT(a,s); \
          a += b; \
          }
					
#define II(a,b,c,d,x,s,ac) \
          { \
          a += I(b,c,d) + x + ac; \
          a = ROTATE_LEFT(a,s); \
          a += b; \
          } 
					
void MD5Init(MD5_CTX *context);
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
void MD5Final(MD5_CTX *context,unsigned char digest[16]);
void MD5Transform(unsigned int state[4],unsigned char block[64]);
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len); 

#endif /* __MD5_H */
					

md5.c

#include <math.h>
#include "string.h"
#include "md5.h"
 
unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

/********************************************************
* 名    称: MD5Init()
* 功    能: 初始化MD5结构体
* 入口参数: 
   context:要初始化的MD5结构体 
* 出口参数: 无
*********************************************************/                      
void MD5Init(MD5_CTX *context)
{
     context->count[0] = 0;
     context->count[1] = 0;
     context->state[0] = 0x67452301;
     context->state[1] = 0xEFCDAB89;
     context->state[2] = 0x98BADCFE;
     context->state[3] = 0x10325476;
}
 
/*********************************************************
* 名    称: MD5Update()
* 功    能: 将要加密的信息传递给初始化过的MD5结构体,无返回值
* 入口参数: 
   context:初始化过了的MD5结构体 
    input:需要加密的信息,可以任意长度
    inputLen:指定input的长度
* 出口参数: 无
*********************************************************/
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen)
{
    unsigned int i = 0,index = 0,partlen = 0;
    index = (context->count[0] >> 3) & 0x3F;
    partlen = 64 - index;
    context->count[0] += inputlen << 3;
    if(context->count[0] < (inputlen << 3))
       context->count[1]++;
    context->count[1] += inputlen >> 29;
    
    if(inputlen >= partlen)
    {
       memcpy(&context->buffer[index],input,partlen);
       MD5Transform(context->state,context->buffer);
       for(i = partlen;i+64 <= inputlen;i+=64)
           MD5Transform(context->state,&input[i]);
       index = 0;        
    }  
    else
    {
        i = 0;
    }
    memcpy(&context->buffer[index],&input[i],inputlen-i);
}
/*********************************************************
* 名    称: MD5Update()
* 功    能: 将加密结果存储到,无返回值
* 入口参数: 
   context:初始化过了的MD5结构体 
    digest :加密过的结果
* 出口参数: 无
*********************************************************/
void MD5Final(MD5_CTX *context,unsigned char digest[16])
{
    unsigned int index = 0,padlen = 0;
    unsigned char bits[8];
    index = (context->count[0] >> 3) & 0x3F;
    padlen = (index < 56)?(56-index):(120-index);
    MD5Encode(bits,context->count,8);
    MD5Update(context,PADDING,padlen);
    MD5Update(context,bits,8);
    MD5Encode(digest,context->state,16);
}
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)
{
    unsigned int i = 0,j = 0;
    while(j < len)
    {
         output[j] = input[i] & 0xFF;  
         output[j+1] = (input[i] >> 8) & 0xFF;
         output[j+2] = (input[i] >> 16) & 0xFF;
         output[j+3] = (input[i] >> 24) & 0xFF;
         i++;
         j+=4;
    }
}
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)
{
     unsigned int i = 0,j = 0;
     while(j < len)
     {
           output[i] = (input[j]) |
                       (input[j+1] << 8) |
                       (input[j+2] << 16) |
                       (input[j+3] << 24);
           i++;
           j+=4; 
     }
}
void MD5Transform(unsigned int state[4],unsigned char block[64])
{
     unsigned int a = state[0];
     unsigned int b = state[1];
     unsigned int c = state[2];
     unsigned int d = state[3];
     unsigned int x[64];
     MD5Decode(x,block,64);
     FF(a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */
 FF(d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */
 FF(c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */
 FF(b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */
 FF(a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */
 FF(d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */
 FF(c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */
 FF(b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */
 FF(a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */
 FF(d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */
 FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
 FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
 FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
 FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
 FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
 FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
 
 /* Round 2 */
 GG(a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */
 GG(d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */
 GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
 GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */
 GG(a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */
 GG(d, a, b, c, x[10], 9,  0x2441453); /* 22 */
 GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
 GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */
 GG(a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */
 GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
 GG(c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */
 GG(b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */
 GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
 GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */
 GG(c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */
 GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
 
 /* Round 3 */
 HH(a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */
 HH(d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */
 HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
 HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
 HH(a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */
 HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */
 HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */
 HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
 HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
 HH(d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */
 HH(c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */
 HH(b, c, d, a, x[ 6], 23,  0x4881d05); /* 44 */
 HH(a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */
 HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
 HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
 HH(b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */
 
 /* Round 4 */
 II(a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */
 II(d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */
 II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
 II(b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */
 II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
 II(d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */
 II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
 II(b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */
 II(a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */
 II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
 II(c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */
 II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
 II(a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */
 II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
 II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */
 II(b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */
     state[0] += a;
     state[1] += b;
     state[2] += c;
     state[3] += d;
}

CRC效验

循环校验码(CRC码):是数据通信领域中最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。


//用于CRC校验
const U8  auchCRCHi[]={
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40};

const U8  auchCRCLo[]=
{
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
};

/*********************************************************************************
* @Function	:  计算数据的CRC校验值
* @Input		:  buf,将计算CRC校验的数据
*						:	 len,目标数据长度
* @Output		:  None
* @Return		:  None
* @Others		:  None
* @Date			:  2022-03-30
**********************************************************************************/
U16  Chkcrc( U8 *buf, U16 len )
{
  U8 uchCRCHi=0xff;
  U8 uchCRCLo=0xff;
  U16 uindex;
  U16 uwModbus_back;
  while(	len--	)
  {
	  uindex	=	uchCRCHi ^ *buf++;
	  uchCRCHi	=	uchCRCLo ^ auchCRCHi[uindex];
	  uchCRCLo	=	auchCRCLo[ uindex ];
  }
  uwModbus_back	=	uchCRCHi << 8 | uchCRCLo;
  return( uwModbus_back );
}

BBC效验

BCC(Block Check Character//信息组校验码),因校验码是将所有的数据异或得出结果,故称为异或校验。具体算法(将每一个字节的数据,一般是两个16进制的字符,进行异或后即得到校验码)

/*********************************************************************************
* @Function	:  计算数据的BBC校验数据
* @Input		:  initData,初始校验值
*						:	 pdata,需要计算的数据开始地址
*						:	 len,数据长度
* @Output		:  None
* @Return		:  bcc_check_data 
* @Others		:  None
* @Date		:  2022-03-04
**********************************************************************************/
U8 bcc_check(U8  bcc_check_data,U8 *Pdata, U16 len)
{
	while(len)
	{
		bcc_check_data ^= (*Pdata);
		--len;
		++Pdata;
	}
	return bcc_check_data;
}


四. 完整文件

tool.c

#include "tool.h"
#include <string.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <stdbool.h>
#include <stdarg.h>
/********************************************************************************
* @file    	  tool.c
* @function   工具类
* @brief 	    系统通用
* @attention	该文件主要提供一下封装好了的工具类,例如文字查询,CRC算法等
* @version 	  V1.0
* @Author     HR
* @date    		2022/08/05
********************************************************************************/
//用于CRC校验
const U8  auchCRCHi[]={
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40};

const U8  auchCRCLo[]=
{
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
};
/*********************************************************************************
* @Function	:  命令响应数据中查找关键字
* @Input 		:  <UartRecvBuf>,字符串内容
*						:  <keyword>,被检索的关键字
* @Output 	:  None
* @Return 	:  返回<1>找到关键字,返回<0>未找到
* @Others  	:  None
* @Date 		:  2022-08-23
**********************************************************************************/
mySta find_keyword_matching(U8 *UartRecvBuf, U8 *keyword)
{

	/* 检索检索到关键词 */
	if (( bool ) strstr ( (char*)UartRecvBuf, (char*)keyword))
	{
		return MY_SUCCESSFUL;
	}
	return MY_FAIL;
}
/*********************************************************************************
* @Function	:  比较两个数组之间是否相同
* @Input 		:  <Array1>,比较数组
*						:  <Array2>,被比较数组
* @Output 	:  None
* @Return 	:  返回<MY_SUCCESSFUL>两数组数据相同,返回<MY_FAIL>两数组数据不同
* @Others  	:  None
* @Date 		:  2022-08-26
**********************************************************************************/
mySta Compare_Array_Data(U8 Array1[256], U8 Array2[256])
{
	U8 i = 0,j = 0;
	U8 flag = 0;
	U8 Array1_len =sizeof((const char*) Array1);//获取数组一长度
	U8 Array2_len =sizeof((const char*) Array1);//获取数组二长度
	if(Array1_len == Array2_len){
		for(i=0; i<Array1_len; i++)
		{
			if(Array1[i] == Array2[j++])
			{
				flag=1;
			}else break;
		}
	}
	if(!flag){
		return MY_FAIL;
	}else return MY_SUCCESSFUL;
}
/*********************************************************************************
* @Function	:  把字符串中的空格符号删除
* @Input		:  <str>,字符串内容 如:{123  abcA  BC}
* @Output		:  None
* @Return		:  返回 <str> 返回的hex内容
* @Others		:  None
* @Date			:  2022-06-30
**********************************************************************************/
char * delete_str_blank(char *str)
{
	char *p=str;
	int i=0;
	while(*p)
	{
		if(*p!=' ')
			str[i++]=*p;
		p++;
	}
	str[i]='\0';
	return str;
}
/*********************************************************************************
* @Function	:  十进制转为十六进制函数
* @Input 		:  <*value>,为要被转换的十进制
*						:  <buffer>,转换完成保存的十六进制的位置
*						:  <length>,参数为转换后十六进制的长度
* @Output 	:  None
* @Return 	:  None
* @Others  	:  None
* @Date 		:  2022-10-10
**********************************************************************************/
void NumToHex(U32  value, char buffer[], int length)
{
  U32 i=(sizeof(unsigned  int)*2);
  U32 temp;
	int j=0;
    while(i--)
    {
        temp = (value&(0xf<<(4*i)))>>(4*i);
        if(temp>9)
            buffer[j] = 'A'+temp-10;
		else
			buffer[j] = '0'+temp;
		j++;
    }
	buffer[length] = '\0';
}
/*********************************************************************************
* @Function	:  十六进制字符串转换成十六进制数组
* @Input 		:  <*str>,字符串输入
*						:  <*out>,十六进制数组输出缓冲区
*						:  <*outlen>,十六进制数组长度
* @Output 	:  None
* @Return 	:  数组长度
* @Others  	:  十六进制数组长度
* @Date 		:  2022-08-23
**********************************************************************************/
U32 StringToHex(char *str, U8 *out, U32 *outlen)
{
    char *p = str;
    char high = 0, low = 0;
    int tmplen = strlen(p), cnt = 0;
    tmplen = strlen(p);
    while(cnt < (tmplen / 2))
    {
        high = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;
        low = (*(++ p) > '9' && ((*p <= 'F') || (*p <= 'f'))) ? *(p) - 48 - 7 : *(p) - 48;
        out[cnt] = ((high & 0x0f) << 4 | (low & 0x0f));
        p ++;
        cnt ++;
    }
    if(tmplen % 2 != 0) out[cnt] = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;

    if(outlen != NULL) *outlen = tmplen / 2 + tmplen % 2;
    return tmplen / 2 + tmplen % 2;
}

/*********************************************************************************
* @Function	:  计算数据的CRC校验值
* @Input		:  buf,将计算CRC校验的数据
*						:	 len,目标数据长度
* @Output		:  None
* @Return		:  None
* @Others		:  None
* @Date			:  2022-03-30
**********************************************************************************/
U16  Chkcrc( U8 *buf, U16 len )
{
  U8 uchCRCHi=0xff;
  U8 uchCRCLo=0xff;
  U16 uindex;
  U16 uwModbus_back;
  while(	len--	)
  {
	  uindex	=	uchCRCHi ^ *buf++;
	  uchCRCHi	=	uchCRCLo ^ auchCRCHi[uindex];
	  uchCRCLo	=	auchCRCLo[ uindex ];
  }
  uwModbus_back	=	uchCRCHi << 8 | uchCRCLo;
  return( uwModbus_back );
}
/*********************************************************************************
* @Function	:  计算数据的BBC校验数据
* @Input		:  initData,初始校验值
*						:	 pdata,需要计算的数据开始地址
*						:	 len,数据长度
* @Output		:  None
* @Return		:  bcc_check_data 
* @Others		:  None
* @Date		:  2022-03-04
**********************************************************************************/
U8 bcc_check(U8  bcc_check_data,U8 *Pdata, U16 len)
{
	while(len)
	{
		bcc_check_data ^= (*Pdata);
		--len;
		++Pdata;
	}
	return bcc_check_data;
}
/*********************************************************************************
* @Function	:	16位数据高低字节互换
* @Input		:	pData,数据
*						:	size,数据的大小 单位字节
* @Output		: 	None
* @Return		: 	None
* @Others		: 	None
*						:	同步设备寄存器中的信息
* @Date			:  2022-04-05
**********************************************************************************/
void data_max_to_min( U16 *pData, U8 size )
{
	U8 i = 0;
	U16 tempData = 0 ;
	for( i = 0; i < size / 2; i ++ )
	{
		tempData = (U16)(*( pData + i ) >> 8 );//获取高字节
		tempData |= (U16)(*( pData + i ) << 8 );//获取低字节
		*( pData + i ) = tempData;
	}

}





tool.h

#ifndef __TOOL_H
#define	__TOOL_H

typedef unsigned char		U8;
typedef signed char 			S8;
typedef unsigned short 		U16;
typedef signed short 		S16;
typedef unsigned int			U32;
typedef signed int 			S32;
typedef float 					F32;
/*********************************************************************************
* @Function	:  串口日志打印开关
* @Input		:  PRINTF_TURN_ON=0 ;关闭日志打印模式
*							 PRINTF_TURN_ON=1 :打开日志打印模式
* @Date			:  2022-08-25
**********************************************************************************/
#define  PRINTF_TURN_ON		1
/*********************************************************************************
* @Function	:  串口2接收日志消息日志打印
* @Input		:  USARE5_TURN_ON=0 ;关闭日志打印模式
*							 USARE5_TURN_ON=1 :打开日志打印模式
* @Date			:  2022-08-25
**********************************************************************************/
#define  USARE2_TURN_ON		1
/*********************************************************************************
* @Function	:  串口3接收日志消息日志打印
* @Input		:  USARE4_TURN_ON=0 ;关闭日志打印模式
*							 USARE4_TURN_ON=1 :打开日志打印模式
* @Date			:  2022-08-25
**********************************************************************************/
#define  USARE3_TURN_ON		0
/*********************************************************************************
* @Function	:  串口4接收日志消息日志打印
* @Input		:  USARE4_TURN_ON=0 ;关闭日志打印模式
*							 USARE4_TURN_ON=1 :打开日志打印模式
* @Date			:  2022-08-25
**********************************************************************************/
#define  USARE4_TURN_ON		0
/*********************************************************************************
* @Function	:  串口5接收日志消息日志打印
* @Input		:  USARE5_TURN_ON=0 ;关闭日志打印模式
*							 USARE5_TURN_ON=1 :打开日志打印模式
* @Date			:  2022-08-25
**********************************************************************************/
#define  USARE5_TURN_ON		1

//自定义使能开关
typedef enum
{
	MY_ENABLE,
	MY_DISABLE
}myEN;

//自定义布尔类型
typedef enum
{
	MY_TRUE,
	MY_FALSE
}myBool;

//自定义开关控制类型
typedef enum
{
	POWER_ON,
	POWER_OFF,
	POWER_PAUSE	//暂停
}switchEN;

//自定义执行结果类型
typedef enum
{
	MY_SUCCESSFUL = 0x01,
	MY_FAIL = 0x00
}mySta;

/*********************************************************************************
* @Function	:  命令响应数据中查找关键字
* @Input 		:  <UartRecvBuf>,字符串内容
*						:  <keyword>,被检索的关键字
* @Output 	:  None
* @Return 	:  返回<1>找到关键字,返回<0>未找到
* @Others  	:  None
* @Date 		:  2022-08-23
**********************************************************************************/
mySta find_keyword_matching(U8 *UartRecvBuf, U8 *keyword);
/*********************************************************************************
* @Function	:  比较两个数组之间是否相同
* @Input 		:  <Array1>,数组一
*						:  <Array2>,数组二
* @Output 	:  None
* @Return 	:  返回<MY_SUCCESSFUL>两数组数据相同,返回<MY_FAIL>两数组数据不同
* @Others  	:  None
* @Date 		:  2022-08-26
**********************************************************************************/
mySta Compare_Array_Data(U8 Array1[256], U8 Array2[256]);
/*********************************************************************************
* @Function	:  把字符串中的空格符号删除
* @Input		:  <str>,字符串内容 如:{123  abcA  BC}
* @Output		:  None
* @Return		:  返回 <str> 返回的hex内容
* @Others		:  None
* @Date			:  2022-06-30
**********************************************************************************/
char * delete_str_blank(char *str);
/*********************************************************************************
* @Function	:  十六进制字符串转换成十六进制数组
* @Input 		:  <*str>,字符串输入
*						:  <*out>,十六进制数组输出缓冲区
*						:  <*outlen>,十六进制数组长度
* @Output 	:  None
* @Return 	:  返回<1>找到关键字,返回<0>未找到
* @Others  	:  十六进制数组长度
* @Date 		:  2022-08-23
**********************************************************************************/
U32 StringToHex(char *str, U8 *out, U32 *outlen);
/*********************************************************************************
* @Function	:  十进制转为十六进制函数
* @Input 		:  <*value>,为要被转换的十进制
*						:  <buffer>,转换完成保存的十六进制的位置
*						:  <length>,参数为转换后十六进制的长度
* @Output 	:  None
* @Return 	:  None
* @Others  	:  None
* @Date 		:  2022-10-10
**********************************************************************************/
void NumToHex(U32  value, char buffer[], int length);
/*********************************************************************************
* @Function	:  计算数据的CRC校验值
* @Input		: 	buf,将计算CRC校验的数据
*						:	len,目标数据长度
* @Output		:  None
* @Return		:  None
* @Others		:  None
* @Date			:  2022-03-30
**********************************************************************************/
U16  Chkcrc( U8 *buf, U16 len ); 
/*********************************************************************************
* @Function	:  计算数据的BBC校验数据
* @Input		:  initData,初始校验值
*						:	 pdata,需要计算的数据开始地址
*						:	 len,数据长度
* @Output		:  None
* @Return		:  None
* @Others		:  None
* @Date		:  2022-03-04
**********************************************************************************/
U8 bcc_check(U8  bcc_check_data,U8 *Pdata, U16 len); 
/*********************************************************************************
* @Function	:	16位数据高低字节互换
* @Input		:	pData,数据
*						:	size,数据的大小 单位字节
* @Output		: 	None
* @Return		: 	None
* @Others		: 	None
*						:	同步设备寄存器中的信息
* @Date			:  2022-04-05
**********************************************************************************/
void data_max_to_min( U16 *pData, U8 size );
#endif /* __TOOL_H */

五、 参考

常见几种校验方法(CS和校验、CRC16、CRC32、BCC异或校验)icon-default.png?t=N7T8https://blog.csdn.net/ieeso/article/details/96997279?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170212621916800215075663%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=170212621916800215075663&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-3-96997279-null-null.142^v96^pc_search_result_base7&utm_term=%20%E5%85%B3%E4%BA%8EC%E8%AF%AD%E8%A8%80%E7%9A%84%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E5%B7%A5%E5%85%B7%E7%B1%BB%EF%BC%88%E5%8C%85%E5%90%AB%E5%B8%B8%E7%94%A8%E8%BF%9B%E5%88%B6%E8%BD%AC%E6%8D%A2%E5%92%8CCRC%EF%BC%8CMD5%EF%BC%8CBBC%EF%BC%89&spm=1018.2226.3001.4187C语言学习—进制之间相互转换icon-default.png?t=N7T8https://blog.csdn.net/qq_41884147/article/details/121888278?ops_request_misc=&request_id=&biz_id=102&utm_term=%20%E5%85%B3%E4%BA%8EC%E8%AF%AD%E8%A8%80%E7%9A%84%E4%B8%80%E4%B8%AA%E8%BF%9B%E5%88%B6%E8%BD%AC%E6%8D%A2&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-0-121888278.nonecase&spm=1018.2226.3001.4450

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
当然可以,以下是一个简单C语言计算器和进制转换程序的代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> int add(int a, int b) { return a + b; } int subtract(int a, int b) { return a - b; } int multiply(int a, int b) { return a * b; } int divide(int a, int b) { return a / b; } void convertToBinary(int n) { if (n > 1) { convertToBinary(n / 2); } printf("%d", n % 2); } void convertToOctal(int n) { if (n > 7) { convertToOctal(n / 8); } printf("%d", n % 8); } void convertToHexadecimal(int n) { if (n > 15) { convertToHexadecimal(n / 16); } int remainder = n % 16; if (remainder < 10) { printf("%d", remainder); } else { printf("%c", remainder - 10 + 'A'); } } int main() { int num1, num2, choice; char operation; printf("Enter two numbers: "); scanf("%d %d", &num1, &num2); printf("Enter the operation (+, -, *, /): "); scanf(" %c", &operation); switch (operation) { case '+': printf("%d + %d = %d\n", num1, num2, add(num1, num2)); break; case '-': printf("%d - %d = %d\n", num1, num2, subtract(num1, num2)); break; case '*': printf("%d * %d = %d\n", num1, num2, multiply(num1, num2)); break; case '/': printf("%d / %d = %d\n", num1, num2, divide(num1, num2)); break; default: printf("Invalid operation!\n"); } printf("Enter a number to convert to binary: "); scanf("%d", &choice); printf("%d in binary is: ", choice); convertToBinary(choice); printf("\n"); printf("Enter a number to convert to octal: "); scanf("%d", &choice); printf("%d in octal is: ", choice); convertToOctal(choice); printf("\n"); printf("Enter a number to convert to hexadecimal: "); scanf("%d", &choice); printf("%d in hexadecimal is: ", choice); convertToHexadecimal(choice); printf("\n"); return 0; } ``` 该程序使用了简单的函数来执行加法、减法、乘法、除法和进制转换,通过 switch 语句来决定执行哪种操作。在进制转换部分,程序使用递归函数来将一个整数换为二进制、八进制或十六进制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿柒学起来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值