搜索
bottom↓
回复: 78

C语言封装技术

  [复制链接]

出0入0汤圆

发表于 2015-7-23 21:07:16 | 显示全部楼层 |阅读模式
        最近在研究RT_Thread的源码,发现i2c驱动里有个地方使用的封装的技术,很少见,又感觉很是精妙。所以特意写个测试模拟程序,并贴上来和大家分享。
个人觉得它非常经典。它使用的模拟的i2c。代码如下:
#include "stdafx.h"

typedef signed int rt_int32_t;
typedef unsigned int rt_uint32_t;

struct rt_i2c_bit_ops
{
    void *data;            /* private data for lowlevel routines */
    void (*set_sda)(void *data, rt_int32_t state);
    void (*set_scl)(void *data, rt_int32_t state);
    rt_int32_t (*get_sda)(void *data);
    rt_int32_t (*get_scl)(void *data);

    void (*udelay)(rt_uint32_t us);

    rt_uint32_t delay_us;  /* scl and sda line delay */
    rt_uint32_t timeout;   /* in tick */
};

//结构体功能函数的使用 它的精妙之处在这里,用宏把结构体函数使用起来
#define SET_SDA(ops, val)   ops->set_sda(ops->data, val)
#define SET_SCL(ops, val)   ops->set_scl(ops->data, val)
#define GET_SDA(ops)        ops->get_sda(ops->data)
#define GET_SCL(ops)        ops->get_scl(ops->data)
//结构体功能函数的使用
#define SDA_L(ops)          SET_SDA(ops, 0)
#define SDA_H(ops)          SET_SDA(ops, 1)
#define SCL_L(ops)          SET_SCL(ops, 0)

//提供对外使用
inline void i2c_delay(struct rt_i2c_bit_ops *ops)
{
    ops->udelay((ops->delay_us + 1) >> 1);
}

inline void i2c_delay2(struct rt_i2c_bit_ops *ops)
{
    ops->udelay(ops->delay_us);
}
//以下为rt_i2c_bit_ops指针函数
void i2c_set_sda(void *data, rt_int32_t state)
{
        if(state)
        {
                printf("sda->H\r\n");
        }else
        {
                printf("sda->L\r\n");
        }
}
void i2c_set_scl(void *data, rt_int32_t state)
{
        if(state)
        {
                printf("scl->H\r\n");
        }else
        {
                printf("scl->L\r\n");
        }
}

rt_int32_t i2c_get_sda(void *data)
{
        printf("sda return 1\r\n");
        return 1;
}
rt_int32_t i2c_get_scl(void *data)
{
        printf("scl return 1\r\n");
        return 1;
}
void udelay(rt_uint32_t us)
{
        printf("udelay %d us\r\n",us);
}
//以上为rt_i2c_bit_ops指针函数


//模拟i2c的其他信号可以类似i2c_restart一样调用
static void i2c_restart(struct rt_i2c_bit_ops *ops)
{
    SDA_H(ops);
    i2c_delay(ops);
    SDA_L(ops);
    i2c_delay(ops);
    SCL_L(ops);
}

int main(int argc, char* argv[])
{
        rt_i2c_bit_ops i2c_ops;
        i2c_ops.data = (void *)0x88;
        i2c_ops.delay_us = 5;
        i2c_ops.udelay = udelay;
        i2c_ops.get_scl = i2c_get_scl;
        i2c_ops.get_sda = i2c_get_sda;
        i2c_ops.set_scl = i2c_set_scl;
         i2c_ops.set_sda = i2c_set_sda;


        printf("==========TEST===========!\n");

        以上设置完专门的i2c接口后,类似与i2c_restart这样的i2c发送和接收的业务逻辑函数基本上不用修改了,直接按不同的i2c接口传参即可完成i2c的功能。
        i2c_restart(&i2c_ops);
        return 0;
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2015-7-23 21:20:39 | 显示全部楼层
函数指针使用的巧妙,高

出0入0汤圆

发表于 2015-7-23 21:48:27 | 显示全部楼层
顶顶,待会研究研究

出0入0汤圆

发表于 2015-7-23 23:03:17 | 显示全部楼层
语法太高深啦,不是太看得明白.

出0入0汤圆

发表于 2015-7-23 23:19:31 | 显示全部楼层
不错,收藏了

出0入0汤圆

发表于 2015-7-23 23:23:33 | 显示全部楼层
谢谢了  帮助很大 我的项目刚好可以用上 及时雨

出0入0汤圆

发表于 2015-7-23 23:28:10 | 显示全部楼层
谢谢你的分享,写得精妙,

出10入0汤圆

发表于 2015-7-23 23:34:51 | 显示全部楼层
太晚了,明天研究

出0入0汤圆

发表于 2015-7-24 10:00:30 | 显示全部楼层
ooc思想啊,值得学习。

出0入0汤圆

发表于 2015-7-24 10:19:20 | 显示全部楼层
超核K60库的作者yandld就是使用了这种方法封装SPI设备啊

出0入0汤圆

发表于 2015-7-24 10:24:46 | 显示全部楼层
用指针和结构体,感觉确实要巧妙很多~

出0入0汤圆

发表于 2015-7-24 10:37:17 | 显示全部楼层
收藏!学习。

出0入0汤圆

发表于 2015-7-24 10:56:50 | 显示全部楼层
高,实在是高。
freeMODBUS里也有类似的做法。

出20入0汤圆

发表于 2015-7-24 11:00:56 | 显示全部楼层
不错,下来学习一下,

出0入0汤圆

发表于 2015-7-24 11:16:01 | 显示全部楼层
正在学习,帮顶

出0入0汤圆

发表于 2015-7-24 11:17:04 | 显示全部楼层
我也上一段吧!
struct PolyFunc
{
  void (*oneParam)( struct PolyFunc *, int);
  int (*withReturn)( struct PolyFunc *);
};

void someFunc( struct PolyFunc * pf )
{
  int r = (pf->withReturn)( pf );
  (pf->oneParam)( pf, r );
}

struct PolyBase
{
  struct PolyFunc interfacePolyFunc;
  int value;
};

void PolyBase_oneParam( struct PolyFunc * pf, int a );
int PolyBase_withReturn( struct PolyFunc * pf );

void init_PolyBase( struct PolyBase * pb )
{
  pb->interfacePolyFunc.oneParam = &PolyBase_oneParam;
  pb->interfacePolyFunc.withReturn = &PolyBase_withReturn;
}

struct PolyBase pb;
init_PolyBase( &pb );
pb.value= 123;
someFunc( &pb.interfacePolyFunc );

int PolyBase_withReturn( struct PolyFunc * pf )
{
  struct PolyBase * this = (struct PolyBase*)pf;
  return this->value;
}

struct PolyDerived
{
  struct PolyBase base_PolyBase;
  char prefix[20];
};

void PolyDerived_oneParam( struct PolyFunc * pf, int a );

void init_PolyDerived( struct PolyDerived * pd )
{
  init_PolyBase( (struct PolyBase*)pd );
  pd->base_PolyBase.interfacePolyFunc.oneParam = &PolyDerived_oneParam;

  strcpy( pd->prefix, "ABC:" );
}

struct PolyDerived pd;
init_PolyDerived( &pd );
pd.base_PolyBase.value= 123;
someFunc( (struct PolyFunc*)&pd );

// Virtual Table

struct PolyBase
{
  struct PolyFunc * interfacePolyFunc;
  int value;
};

struct PolyFunc PolyBase_VTable =
{
  &PolyBase_oneParam,
  &PolyBase_withReturn
};

void init_PolyBase( struct PolyBase * pb )
{
  pb->interfacePolyFunc = &PolyBase_VTable;
}

void someFunc( struct PolyFunc * * pf )
{
  int r = ((*pf)->withReturn)( pf );
  ((*pf)->oneParam)( pf, r );
}

void calling()
{
  struct PolyBase pb;
  init_PolyBase( &pb );
  someFunc( &pb.interfacePolyFunc );
}


出0入0汤圆

 楼主| 发表于 2015-7-25 07:13:21 | 显示全部楼层
yaoyutaoTom 发表于 2015-7-24 11:17
我也上一段吧!
struct PolyFunc
{

        yaoyutaoTom,这段代码展现了c语言的封装、继承、多态还利用上了虚函数表技术。对于学习c语言的抽象设计,这段代码可以起到启发的作用。稍微加下注解
// struct01.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string.h>

#if 0
//接口封装
struct PolyFunc
{
  void (*oneParam)( struct PolyFunc *, int);
  int (*withReturn)( struct PolyFunc *);
};
//使用demo
void someFunc( struct PolyFunc * pf )
{
  int r = (pf->withReturn)( pf );
  (pf->oneParam)( pf, r );
}
//接口 + 属性 数据
struct PolyBase
{
  struct PolyFunc interfacePolyFunc;
  int value;
};

void PolyBase_oneParam( struct PolyFunc * pf, int a );
int PolyBase_withReturn( struct PolyFunc * pf );

//初始化接口
void init_PolyBase( struct PolyBase * pb )
{
  pb->interfacePolyFunc.oneParam = &PolyBase_oneParam;
  pb->interfacePolyFunc.withReturn = &PolyBase_withReturn;
}

//接口实现
int PolyBase_withReturn( struct PolyFunc * pf )
{
  struct PolyBase *  PolyBaseS= (struct PolyBase*)pf;
  printf("Get Value\r\n");
  return PolyBaseS->value;
}
//接口实现
void PolyBase_oneParam( struct PolyFunc * pf, int a)
{
  struct PolyBase *  PolyBaseS= (struct PolyBase*)pf;
  printf("Set Value\r\n");
  PolyBaseS->value = 5;
}

//继承PolyBase
struct PolyDerived
{
  struct PolyBase base_PolyBase;
  char prefix[20];
};

//多态 重写PolyDerived PolyDerived_oneParam
void PolyDerived_oneParam( struct PolyFunc * pf, int a)
{
  struct PolyBase *  PolyBaseS= (struct PolyBase*)pf;
  printf("Derived Set Value\r\n");
  PolyBaseS->value = 5;
}
//初始化PolyDerived
void init_PolyDerived( struct PolyDerived * pd )
{
  init_PolyBase( (struct PolyBase*)pd );
  pd->base_PolyBase.interfacePolyFunc.oneParam = &PolyDerived_oneParam;

  strcpy( pd->prefix, "ABC:" );
}

//测试demo
int main(int argc, char* argv[])
{       
        //封装
        {
                struct PolyBase pb;
                init_PolyBase( &pb );
                pb.value= 123;
                someFunc( &pb.interfacePolyFunc );
        }
        //继承
        {
                struct PolyDerived pd;
                init_PolyDerived( &pd );
                pd.base_PolyBase.value= 123;
                someFunc( (struct PolyFunc*)&pd );
        }

        return 0;
}
#endif



// Virtual Table //虚函数表技术
struct PolyBase
{
  struct PolyFunc * interfacePolyFunc;
  int value;
};
//对外提供接口
struct PolyFunc
{
  void (*oneParam)( struct PolyFunc **, int);
  int (*withReturn)( struct PolyFunc **);
};
//实现接口
int PolyBase_withReturn( struct PolyFunc ** pf )
{
  struct PolyBase *  PolyBaseS= (struct PolyBase*)(*pf);
  printf("Get Value\r\n");
  return PolyBaseS->value;
}
//实现接口
void PolyBase_oneParam( struct PolyFunc ** pf, int a)
{
  struct PolyBase *  PolyBaseS= (struct PolyBase*)(*pf);
  printf("Set Value\r\n");
  PolyBaseS->value = 5;
}

//初始化虚拟函数表
struct PolyFunc PolyBase_VTable =
{
  &PolyBase_oneParam,
  &PolyBase_withReturn
};
//初始化接口
void init_PolyBase( struct PolyBase * pb )
{
  pb->interfacePolyFunc = &PolyBase_VTable;
}

//demo 调用
void someFunc( struct PolyFunc * * pf )
{
  int r = ((*pf)->withReturn)( pf );
  ((*pf)->oneParam)( pf, r );
}

//test demo
void calling()
{
  struct PolyBase pb;
  init_PolyBase( &pb );
  someFunc( &pb.interfacePolyFunc );
}

int main()
{
        calling();
        return 1;
}


出0入0汤圆

发表于 2015-7-25 08:04:07 | 显示全部楼层
设计的相当精妙,学习了

出0入0汤圆

发表于 2015-7-25 08:04:09 来自手机 | 显示全部楼层
学习了,谢谢分享

出0入0汤圆

发表于 2015-7-25 09:02:12 | 显示全部楼层
一开始没理解这里  #define SET_SDA(ops, val)   ops->set_sda(ops->data, val),   后来查了一下,了解到宏定义函数是没有类型的, 任何参数类型都可以。  所以传数据的时候一定要注意参数类型。   学习了。

出0入0汤圆

 楼主| 发表于 2015-7-25 09:27:47 | 显示全部楼层
技术宅 发表于 2015-7-25 09:02
一开始没理解这里  #define SET_SDA(ops, val)   ops->set_sda(ops->data, val),   后来查了一下,了解到 ...

        是的,感觉这里用的非常的巧妙,就贴出来和大家分享,看看大伙的意见,一来可以加深自己的理解和印象,二来可以收获不同坛友的不同角度的看法。

出0入4汤圆

发表于 2015-7-25 11:47:41 | 显示全部楼层
是的,要好好研究一下!

出0入0汤圆

发表于 2015-7-25 13:11:07 来自手机 | 显示全部楼层
咬着牙啃完了,不容易吸收啊

出0入0汤圆

发表于 2015-7-25 13:54:09 | 显示全部楼层
不错,谢谢分享!

出0入0汤圆

发表于 2015-7-25 14:05:54 | 显示全部楼层
学习了        

出140入158汤圆

发表于 2015-7-25 14:11:09 | 显示全部楼层
这是常识,做过大规模代码的都会这么干。

出0入0汤圆

 楼主| 发表于 2015-7-25 14:13:48 | 显示全部楼层
amigenius 发表于 2015-7-25 14:11
这是常识,做过大规模代码的都会这么干。

        看来这位朋友的代码阅历比较丰富,我要不是碰到了RT_Thread的,还真的没有机会体会到这种感觉。

出0入0汤圆

发表于 2015-7-25 15:02:04 | 显示全部楼层
这常识,,,搞mcu的多是 独行侠,,,,很少用上啊学习了

出0入0汤圆

发表于 2015-7-25 16:45:56 | 显示全部楼层
这有点OO中多态的意思,驱动代码体现了“要做什么”,至于“如何做”,交给指针函数完成

出0入0汤圆

 楼主| 发表于 2015-7-25 17:26:55 | 显示全部楼层
jathenal 发表于 2015-7-25 16:45
这有点OO中多态的意思,驱动代码体现了“要做什么”,至于“如何做”,交给指针函数完成 ...

        多态只是其中的一部分,我比较欣赏的是这里宏对结构体函数的调用封装,这部分业务逻辑基本上不用再改。不同的i2c接口可以通用调用。
//模拟i2c的其他信号可以类似i2c_restart一样调用
static void i2c_restart(struct rt_i2c_bit_ops *ops)
{
    SDA_H(ops);
    i2c_delay(ops);
    SDA_L(ops);
    i2c_delay(ops);
    SCL_L(ops);
}

出0入0汤圆

发表于 2015-7-25 21:51:21 | 显示全部楼层
学习了不错

出0入0汤圆

发表于 2015-7-26 19:13:57 | 显示全部楼层
好东西。

出0入0汤圆

发表于 2015-7-28 16:57:16 来自手机 | 显示全部楼层
还没有领会到,具体的好处,哪位高手普及一下?

出0入0汤圆

发表于 2015-7-28 17:10:26 | 显示全部楼层
没看懂,还需要努力啊

出0入0汤圆

发表于 2015-7-28 23:21:47 | 显示全部楼层
说白了就是函数指针的妙用,UCGUI中大量使用

出0入0汤圆

发表于 2015-7-29 08:13:23 | 显示全部楼层
这种封装方式好,以后再开发就方便了,顺便问下楼主有没有关于C语言封装的书呢?

出0入0汤圆

 楼主| 发表于 2015-7-29 11:40:41 | 显示全部楼层
abcdzhy 发表于 2015-7-29 08:13
这种封装方式好,以后再开发就方便了,顺便问下楼主有没有关于C语言封装的书呢? ...

        有一本<c嵌入式编程设计模式>,但是像我发的帖子这样的封装技术,是在国人开发RT_Thread嵌入式操作系统上学来的。

出0入0汤圆

发表于 2015-7-29 16:08:27 | 显示全部楼层
TMD,没看明白,太笨了,还是去焊板子了

出0入0汤圆

发表于 2015-7-29 16:29:18 | 显示全部楼层
道行潜!实在是看不懂!境界不够!ops->set_sda(ops->data, val),这里的ops在哪?

出0入0汤圆

发表于 2015-7-29 16:31:41 | 显示全部楼层
lianzhong008 发表于 2015-7-25 07:13
yaoyutaoTom,这段代码展现了c语言的封装、继承、多态还利用上了虚函数表技术。对于学习c语言的抽象设计 ...

醍醐灌顶啊!
真是好东西。

出0入0汤圆

发表于 2015-7-29 16:38:42 | 显示全部楼层
看看 学习了 多谢

出0入0汤圆

发表于 2015-7-29 16:41:39 | 显示全部楼层
刚好最近需要iic,顶一下,拿来试试

出0入0汤圆

发表于 2015-7-29 17:14:22 | 显示全部楼层
rt-thread借鉴了c++的封装继承多态,在开发说明里有写的。上面这种写法在大的项目里用的比较多,要是全是自己写就用不上了。我去年就看过这种方法了,到现在都没用上

出0入9汤圆

发表于 2015-7-29 17:37:23 | 显示全部楼层
不错,这几天正在看java,非常羡慕java里面的封装方式,一直想在C里面实现,看来还真可以模拟实现

出0入0汤圆

发表于 2015-8-1 09:59:36 来自手机 | 显示全部楼层
尤其是在多人开发项目中很有用。

出0入0汤圆

发表于 2015-8-11 09:07:17 | 显示全部楼层
没有怎么看明白怎么把CLK   SDL  IO口与程序怎么好应起来 。

出0入0汤圆

发表于 2015-8-11 09:24:35 | 显示全部楼层
waymcu 发表于 2015-8-11 09:07
没有怎么看明白怎么把CLK   SDL  IO口与程序怎么好应起来 。

IO驱动在有段打印信息那里完善,这个方式的好处就是I2C的时序是固定的,也就是逻辑部分不用改,移植时把IO驱动改了就完了。
这个例子的意义在于提供一种思路,自己做I2C时用不着这么麻烦。

出0入0汤圆

发表于 2015-8-11 09:25:58 来自手机 | 显示全部楼层
这样的代码很方便,看起也很漂亮,不过我暂时是没精力搞这些了,只能是留待以后了。

出0入0汤圆

发表于 2015-8-11 09:34:30 | 显示全部楼层
jzkn 发表于 2015-8-11 09:24
IO驱动在有段打印信息那里完善,这个方式的好处就是I2C的时序是固定的,也就是逻辑部分不用改,移植时把I ...

IO好像与void *data 这个地址 对应起来了?

出0入0汤圆

发表于 2015-8-11 13:25:43 | 显示全部楼层
waymcu 发表于 2015-8-11 09:34
IO好像与void *data 这个地址 对应起来了?

差不多就这意思吧,他也没写死,Data还可以做其他用途 ,看你实现得时候得需求了。

出0入0汤圆

发表于 2015-8-11 16:24:20 | 显示全部楼层
学习一下

出0入0汤圆

发表于 2015-9-1 13:47:58 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2015-9-1 21:33:48 | 显示全部楼层
回来在程序里试一下

出0入0汤圆

发表于 2016-6-8 10:59:18 | 显示全部楼层
值得借鉴!!

出0入0汤圆

发表于 2016-6-12 16:32:03 | 显示全部楼层
用过,不过不多,换不同MCU时就直接可以移植,再调节IO就可以了

出0入0汤圆

发表于 2016-6-12 19:11:29 | 显示全部楼层
果然还是见识太短

出0入0汤圆

发表于 2016-6-12 19:34:54 | 显示全部楼层
好东西,mark

出0入0汤圆

发表于 2016-6-12 21:38:49 | 显示全部楼层
封装的好漂亮~~~

出0入0汤圆

发表于 2016-6-13 13:40:14 | 显示全部楼层
帅啊,真是不错。谢谢楼主

出0入0汤圆

发表于 2016-6-30 11:22:05 | 显示全部楼层
最近也在开始看RTT

出0入0汤圆

发表于 2016-12-19 15:18:07 | 显示全部楼层
Okar 发表于 2015-7-24 10:19
超核K60库的作者yandld就是使用了这种方法封装SPI设备啊

贴出来 大家学习下呀

出0入0汤圆

发表于 2017-1-1 08:18:59 | 显示全部楼层
niba 发表于 2016-12-19 15:18
贴出来 大家学习下呀

感兴趣的肯定自己会去搜了,网上大片的资料。

出0入0汤圆

发表于 2017-1-1 09:55:55 | 显示全部楼层
是不是STM32的驱动库也是类似的包装。之前用那些库函数都直接调用,没仔细研究。

出0入0汤圆

发表于 2017-1-1 11:05:16 | 显示全部楼层
不错,收藏了

出0入0汤圆

发表于 2017-1-1 13:21:58 | 显示全部楼层
linux内核驱动就是这样的。

出0入0汤圆

发表于 2017-1-1 15:15:26 来自手机 | 显示全部楼层
感谢楼主分享

出0入0汤圆

发表于 2017-1-1 22:16:42 | 显示全部楼层
标记慢慢看,最近一直在焊板子,很少写代码了!

出0入0汤圆

发表于 2017-1-1 23:07:04 | 显示全部楼层
C本是面向过程的语言,把C当C++来用,不如直接用面向对象的C++。

出0入0汤圆

发表于 2017-1-2 07:47:58 来自手机 | 显示全部楼层
很精妙,学习了。

出0入0汤圆

发表于 2017-1-2 11:55:18 | 显示全部楼层
rain73 发表于 2017-1-1 23:07
C本是面向过程的语言,把C当C++来用,不如直接用面向对象的C++。

说的是!

出0入0汤圆

发表于 2017-1-2 13:58:28 | 显示全部楼层
rain73 发表于 2017-1-1 23:07
C本是面向过程的语言,把C当C++来用,不如直接用面向对象的C++。

出0入0汤圆

发表于 2017-1-2 15:49:20 | 显示全部楼层
rain73 发表于 2017-1-1 23:07
C本是面向过程的语言,把C当C++来用,不如直接用面向对象的C++。

层主不妨试试用C++写一下楼主位的代码,看看是什么效果。
注意,主楼代码的结构内指针是虚的,即可以指向不同的实现实体。
拭目以待。。

出0入0汤圆

发表于 2017-1-3 00:03:46 | 显示全部楼层
stdio 发表于 2017-1-2 15:49
层主不妨试试用C++写一下楼主位的代码,看看是什么效果。
注意,主楼代码的结构内指针是虚的,即可以指向 ...

有段时间没动代码了,一下子还真写不出。不过要说有什么C能实现而C++不能实现的,那是不太可能!
C++的对象多得实在是太复杂了,相比之下上面几个概念简直是小儿科。

出0入0汤圆

发表于 2017-1-3 12:29:45 | 显示全部楼层
本帖最后由 stdio 于 2017-1-3 14:15 编辑

c++比较复杂。

出0入0汤圆

发表于 2017-2-22 22:01:01 来自手机 | 显示全部楼层
mcu用io模拟多路有什么简洁的写法?

出0入0汤圆

发表于 2017-2-22 22:02:28 来自手机 | 显示全部楼层
mcu用io模拟多路i2c有什么简洁的写法?

出0入0汤圆

发表于 2017-5-31 13:23:49 | 显示全部楼层
RT中很多组件都是这样做的,把应用层和底层分开来,中间层封装和衔接设备,并注册设备,然后就可以在不管底层的情况下使用应用层了,也便于分开协同开发。

出0入0汤圆

发表于 2017-5-31 13:39:55 | 显示全部楼层
linux驱动都是这么玩的!

出0入0汤圆

发表于 2017-7-21 11:22:50 | 显示全部楼层
涨知识了,要实际练习一下。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 00:06

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表