GPS定位stm32通过串口iap升级哪个串口

3310人阅读
Android源码(44)
Android:V4.2.2
Source Insight
在漫长的Android源码编译等待过程中,想起之前写过一部分的Android定位实现的探究小品,于是继续探究。
注:代码都是片段化的代码,用来提纲挈领的说明问题。
定位的基础知识:
1、定位芯片和CPU之间通过串口进行通信
2、串口和CPU之间传输的是ASCII格式的NMEA(National Marine Electronics Association)信息,如:
$GPGGA,,,S,,E,1,04,24.4,19.7,M,,,,0000*1F
$GPGLL,,S,,E,,A*2D
$GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,069,,13,32,252,45*70
$GPRMC,,A,,S,,E,0.00,89.68,211200,,*25
基于以上两点,要探知定位数据从GPS芯片到应用层的流程,最好的途径就是从应用层输出NEMA信息的地方开始。
NMEA资料参见:
一、GPS定位的应用层实现
Luckily,在应用层我们可以通过onNmeaReceived()方法获取到NMEA信息,如下Code Fragment:
public class GpsTestActivity extends ActionBarActivity {
/* Other Codes */
/** 获取系统的定位服务,记得在AndroidManifest中赋予定位方面的权限:
* &uses-permission android:name=&android.permission.ACCESS_FINE_LOCATION&/&
* &uses-permission android:name=&android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&/&
* &uses-permission android:name=&android.permission.ACCESS_COARSE_LOCATION&/&
LocationManager mLocationService = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mLocationService.addNmeaListener(mNmeaListener);
private GpsStatus.NmeaListener mNmeaListener = new NmeaListener() {
public void onNmeaReceived(long timestamp, String nmea) {
System.out.println(nmea + &\n&);
二、GPS定位的Framework层实现
GpsStatus.NmeaListener是一个接口类,来自GpsStatus.java文件:
frameworks\base\location\java\android\location\GpsStatus.java
* Used for receiving NMEA sentences from the GPS.
* NMEA 0183 is a standard for communicating with marine electronic devices
* and is a common method for receiving data from a GPS, typically over a serial port.
* See &a href=&http://en.wikipedia.org/wiki/NMEA_0183&&NMEA 0183&/a& for more details.
* You can implement this interface and call {@link LocationManager#addNmeaListener}
* to receive NMEA data from the GPS engine.
public interface NmeaListener {
void onNmeaReceived(long timestamp, String nmea);
}在上述App中,我们的应用程序实现了该方法,一旦NMEA数据到来,onNmeaReceived()方法就被调用一次,我们在Console上可以看到原始的NEMA信息。
那么接下来,就要寻找nmea数据的来源了。
mNmeaListener通过LocationManager类的addNmeaListener()方法进行注册(register):
frameworks\base\location\java\android\location\LocationManager.java
* Adds an NMEA listener.
* @param listener a {@link GpsStatus.NmeaListener} object to register
* @return true if the listener was successfully added
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
/* mNmeaListeners是LocationManager类的成员变量:
* private final HashMap&GpsStatus.NmeaListener, GpsStatusListenerTransport& mNmeaListeners =
new HashMap&GpsStatus.NmeaListener, GpsStatusListenerTransport&();
if (mNmeaListeners.get(listener) != null) {
// listener is already registered
GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
result = mService.addGpsStatusListener(transport);
if (result) {
mNmeaListeners.put(listener, transport);
} catch (RemoteException e) {
Log.e(TAG, &RemoteException in registerGpsStatusListener: &, e);
}这里,先检测定义的NmeaListener有没有被注册过,若果没有,注册之。
注册到哪里去了呢?
由mNmeaListeners成员的定义可知,和GpsStatus.NmeaListener进行关联的是GpsStatusListenerTransport,而它是LocationManager类的一个内部类。
只看相关的部分:
// This class is used to send GPS status events to the client's main thread.
private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
private final GpsStatus.NmeaListener mNmeaL
// This must not equal any of the GpsStatus event IDs
private static final int NMEA_RECEIVED = 1000;
private class Nmea {
Nmea(long timestamp, String nmea) {
mTimestamp =
private ArrayList&Nmea& mNmeaB
//G psStatusListenerTransport(GpsStatus.Listener listener){}
GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
mNmeaListener =
mListener =
mNmeaBuffer = new ArrayList&Nmea&();
public void onNmeaReceived(long timestamp, String nmea) {
if (mNmeaListener != null) {
synchronized (mNmeaBuffer) {
mNmeaBuffer.add(new Nmea(timestamp, nmea));
Message msg = Message.obtain();
msg.what = NMEA_RECEIVED;
// remove any NMEA_RECEIVED messages already in the queue
mGpsHandler.removeMessages(NMEA_RECEIVED);
mGpsHandler.sendMessage(msg);
private final Handler mGpsHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == NMEA_RECEIVED) {
synchronized (mNmeaBuffer) {
int length = mNmeaBuffer.size();
for (int i = 0; i & i++) {
Nmea nmea = mNmeaBuffer.get(i);
mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
mNmeaBuffer.clear();
// synchronize on mGpsStatus to ensure the data is copied atomically.
}在GpsStatusListenerTransport类中:
定义一个Nmea类型的链表mNmeaBuffer,一旦onNmeaReceived()接收到NMEA数据,新数据被加载到链表mNmeaBuffer中(mNmeaBuffer.add(new Nmea(timestamp, nmea))),然手置消息标志为NMEA_RECEIVED(msg.what = NMEA_RECEIVED)。
mGpsHandler对上述NMEA_RECEIVED消息进行处理,最终把传过来的NMEA数据发往应用层GpsTestActivity中的onNmeaReceived()。
那么,GpsStatusListenerTransport类中onNmeaReceived(long timestamp, String nmea)方法的nmea数据有谁提供呢?
GpsStatusListenerTransport类继承自IGpsStatusListener,由类前的字符&I&我们得知,它是一个扩展名为.aidl的文件。
AIDL:AIDL机制用来完成在进程之间进行通信(在Android中不允许进程间共享数据),它的详细知识另外Google之。
这里,我们再次见到了onNmeaReceived():
rameworks\base\location\java\android\location\IGpsStatusListener.aidl
oneway interface IGpsStatusListener
void onGpsStarted();
void onGpsStopped();
void onFirstFix(int ttff);
void onSvStatusChanged(int svCount, in int[] prns, in float[] snrs, in float[] elevations, in float[] azimuths, int ephemerisMask, int almanacMask, int usedInFixMask);
void onNmeaReceived(long timestamp, String nmea);
oneway关键字是用来修饰远程调用行为。使用该关键词时,远程调用不是阻塞的,它只是发送事物数据并立即返回。接口的最终实现是把普通的远程调用按照Binder线程池的调用规则来接收,如果oneway是使用在本地调用上,那么不会有任何影响,并且调用依然是异步的。
下面,探究必须进入第三层。
三、GPS定位的Lib层实现
和IGpsStatusListener接头的是GpsLocationProvider类:
frameworks\base\services\java\com\android\server\location\GpsLocationProvider.java
public class GpsLocationProvider implements LocationProviderInterface {
// 此处省略1000+N行
private ArrayList&Listener& mListeners = new ArrayList&Listener&();
private final class Listener implements IBinder.DeathRecipient {
final IGpsStatusListener mL
Listener(IGpsStatusListener listener) {
mListener =
public void binderDied() {
if (DEBUG) Log.d(TAG, &GPS status listener died&);
synchronized (mListeners) {
mListeners.remove(this);
if (mListener != null) {
mListener.asBinder().unlinkToDeath(this, 0);
* called from native code to report NMEA data received
private void reportNmea(long timestamp) {
synchronized (mListeners) {
int size = mListeners.size();
if (size & 0) {
// don't bother creating the String if we have no listeners
int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
String nmea = new String(mNmeaBuffer, 0, length);
for (int i = 0; i & i++) {
Listener listener = mListeners.get(i);
listener.mListener.onNmeaReceived(timestamp, nmea);
} catch (RemoteException e) {
Log.w(TAG, &RemoteException in reportNmea&);
mListeners.remove(listener);
// adjust for size of list changing
}GPS定位功能最终需要调用硬件实现,操作硬件就必须通过C/C++完成,GpsLocationProvider中包含许多native方法,采用JNI机制为上层提供服务。
在上面的Code Frame中,通过调用本地方法native_read_nmea()获取到NMEA数据,然后传数据到IGpsStatusListener接口类的onNmeaReceived()方法。
reportNmea()是被JNI方法回调的方法,在 JNI 的实现中,通过这些方法的回调来传递JNI层的执行结果。
源码编译出错,解决问题去。。。
native_read_nmea()在GpsLocationProvider类中定义:
private native int native_read_nmea(byte[] buffer, int bufferSize);native指明它是本地方法,和它对应的C/C++文件的实现是:
static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, jbyteArray nmeaArray, jint buffer_size);How?Next...
frameworks\base\services\jni\com_android_server_location_GpsLocationProvider.cpp
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
/* other members... */
{&native_read_nmea&, &([BI)I&, (void*)android_location_GpsLocationProvider_read_nmea},
/* other members... */
};JNINativeMethod是Android中采用的Java和C/C++函数的映射方式,并在其中描述了函数的参数和返回值:
typedef struct {
const char*
// Java文件中的本地方法
const char* // 述了函数的参数和返回值
// 指针,指向具体的C/C++函数
} JNINativeM详细内容这里还是不展开了。
来看android_location_GpsLocationProvider_read_nmea()的实现:
static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj,
jbyteArray nmeaArray, jint buffer_size)
// this should only be called from within a call to reportNmea
jbyte* nmea = (jbyte *)env-&GetPrimitiveArrayCritical(nmeaArray, 0);
int length = sNmeaStringL
if (length & buffer_size)
length = buffer_
memcpy(nmea, sNmeaString, length);
env-&ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT);
}虽然不清楚JNI深入含义,但这个函数意思还是挺明显的,我们推断:
第5行:用来动态分配内存,nmea指向获取到的内存区域,同时把nmea和nmeaArray进行关联;
第6行:sNmeaStringLength指示一次从串口读取到的字节长度
第7、8行:在Java中调用native_read_nmea()方法时指明了我们需要取的数据长度,所以,如果从串口实际读取的数据长度大于我们需要的,我们对串口数据进行截取:即,只取指定长度的数据;
第9行:从串口读出的数据存在sNmeaString中,这里Copy到nmea指向的内存区域;
第10行:nmea指向的内存区域中的数据交给nmeaArray,然后释放nmea指向的内存空间。这里也可以看到,函数调用是通过nmeaArray传递NMEA数据的
下面应该看sNmeaStringLength、sNmeaString的设置过程:
static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length)
JNIEnv* env = AndroidRuntime::getJNIEnv();
// The Java code will call back to read these values
// We do this to avoid creating unnecessary String objects
sNmeaString =
sNmeaStringLength =
env-&CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}method_reportNmea、、、有没有熟悉的感觉?
对,在GpsLocationProvider类中见过reportNmea(long timestamp)函数。
下面的代码片段表明,method_reportNmea()和reportNmea()是绑定在一起的,调用C/C++函数method_reportNmea,也就间接调用Java的reportNmea()方法。这中间的机制,就是JNI!
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
/* other definitions... */
method_reportNmea = env-&GetMethodID(clazz, &reportNmea&, &(J)V&);
/* other definitions... */
}而method_reportNmea是在nmea_callback()函数中被调用的,哪里又调用nmea_callback()函数呢?
Let's go to neXt Layer...
四、GPS定位HAL层的实现
所谓Android的HAL层,也就是是Linux的应用程序。至于串口具体配置,比如寄存器配置、数据收发等芯片级实现,是在在Linux内核里的。
com_android_server_location_GpsLocationProvider.cpp文件中另外出现nmea_callback的地方是:
GpsCallbacks sGpsCallbacks = {
sizeof(GpsCallbacks),
location_callback,
status_callback,
sv_status_callback,
nmea_callback,
set_capabilities_callback,
acquire_wakelock_callback,
release_wakelock_callback,
create_thread_callback,
request_utc_time_callback,
};GpsCallbacks结构体封装了所有需要回调的函数(确切的说是函数指针),sGpsCallbacks调用关系:
static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
// this must be set before calling into the HAL library
if (!mCallbacksObj)
mCallbacksObj = env-&NewGlobalRef(obj);
// fail if the main interface fails to initialize
if (!sGpsInterface || sGpsInterface-&init(&sGpsCallbacks) != 0)
/* other codes */
}而android_location_GpsLocationProvider_init()在GpsLocationProvider类中调用native_init()时被调用:
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{&native_init&, &()Z&, (void*)android_location_GpsLocationProvider_init}
}这里,我们找到了和上层的关系,和下层如何打交道呢?
下面需要贴一大段代码:/** Represents the standard GPS interface. */
typedef struct {
/** set to sizeof(GpsInterface) */
* Opens the interface and provides the callback routines
* to the implemenation of this interface.
(*init)( GpsCallbacks* callbacks );
/** Starts navigating. */
(*start)( void );
/** Stops navigating. */
(*stop)( void );
/** Closes the interface. */
(*cleanup)( void );
/** Injects the current time. */
(*inject_time)(GpsUtcTime time, int64_t timeReference,
int uncertainty);
/** Injects current location from another location provider
(typically cell ID).
latitude and longitude are measured in degrees
expected accuracy is measured in meters
(*inject_location)(double latitude, double longitude, float accuracy);
* Specifies that the next call to start will not use the
* information defined in the flags. GPS_DELETE_ALL is passed for
* a cold start.
(*delete_aiding_data)(GpsAidingData flags);
* min_interval represents the time between fixes in milliseconds.
* preferred_accuracy represents the requested fix accuracy in meters.
* preferred_time represents the requested time to first fix in milliseconds.
(*set_position_mode)(GpsPositionMode mode, GpsPositionRecurrence recurrence,
uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time);
/** Get a pointer to extension information. */
const void* (*get_extension)(const char* name);
} GpsIGpsInterface结构体封装了GPS实现的标准接口——接口,注意!接口不就时用来连接两端的吗?一端是com_android_server_location_GpsLocationProvider.cpp文件里的实现,那另一端就是。。。都探到这个地步了,另一端应该是串口方式直接和GPS芯片打交道的Linux驱动了吧?
确是,但是还需要一个媒介:
struct gps_device_t {
struct hw_device_
* Set the provided lights to the provided values.
* Returns: 0 on succes, error code on failure.
const GpsInterface* (*get_gps_interface)(struct gps_device_t* dev);
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
hw_module_t*
/* other codes..*/
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
hw_device_t*
err = module-&methods-&open(module, GPS_HARDWARE_MODULE_ID, &device);
if (err == 0) {
gps_device_t* gps_device = (gps_device_t *)
sGpsInterface = gps_device-&get_gps_interface(gps_device);
/* other codes..*/
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{&class_init_native&, &()V&, (void *)android_location_GpsLocationProvider_class_init_native},
}GpsLocationProvider.java通过class_init_native的调用实现对C/C++文件中android_location_GpsLocationProvider_class_init_native的调用;
com_android_server_location_GpsLocationProvider.cpp通过gps_device_t获取操作GPS芯片的接口。How????
重点来了:GPS_HARDWARE_MODULE_ID
对,就是GPS_HARDWARE_MODULE_ID!
ardware\qcom\gps\loc_api\libloc_api\gps.c
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = GPS_HARDWARE_MODULE_ID,
.name = &loc_api GPS Module&,
.author = &Qualcomm USA, Inc.&,
.methods = &gps_module_methods,
};有木有?GPS_HARDWARE_MODULE_ID!
hardware\qcom\gps\loc_api\libloc_api\gps.c
extern const GpsInterface* gps_get_hardware_interface();
const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)
return gps_get_hardware_interface();
static int open_gps(const struct hw_module_t* module, char const* name,
struct hw_device_t** device)
struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
memset(dev, 0, sizeof(*dev));
dev-&common.tag = HARDWARE_DEVICE_TAG;
dev-&common.version = 0;
dev-&common.module = (struct hw_module_t*)
dev-&get_gps_interface = gps__get_gps_
*device = (struct hw_device_t*)
static struct hw_module_methods_t gps_module_methods = {
.open = open_gps
};流程很清楚了:
gps_get_hardware_interface()函数在驱动程序中实现
&& &——在gps__get_gps_interface()中被调用
&& &&& &——在open_gps()被调用
&& &&& &&& &——在gps_module_methods中例化
&& &&& &&& &&& &——HAL_MODULE_INFO_SYM
const GpsInterface* gps_get_hardware_interface()函数在其他C文件实现,该C文件是和Linux驱动打交道的应用程序。基本功能:
1、open处理器CPU和GPS芯片连接的串口;
2、read串口NEMA数据,并解析;
3、根据上层传进来的回调函数,打包数据,调用相应Callback,进而发送到Android应用层。
static const GpsInterface
mGpsInterface = {
.size =sizeof(GpsInterface),
.init = gps_init,
|--1、接收从上层传下来的GpsCallbacks变量,用它初始化GpsState-&callbacks成员
|--2、GpsState结构体的其他成员初始化
|--3、GpsState-&init状态设置为:STATE_INIT
|--4、最重要:启动GPS线程,进行数据的读取、处理:
state-&thread = state-&callbacks.create_thread_cb(&gps&, gps_state_thread, state);
--gps_create_thread create_thread_
--typedef pthread_t (* gps_create_thread)(const char* name, void (*start)(void *), void* arg);
.start = gps_start,
--设置GPS的状态为开始:GPS_STATUS_SESSION_BEGIN
.stop = gps_stop,
--设置GPS的状态为结束:GPS_STATUS_SESSION_END
.cleanup = gps_cleanup,
--退出需要进行的一些清理工作,如GpsState-&init = STATE_QUIT,GpsCallbacks指针归null,信号量回收
.inject_time = gps_inject_time,
--可为空函数
.inject_location = gps_inject_location,
--可为空函数
.delete_aiding_data = gps_delete_aiding_data,
--可为空函数
.set_position_mode = gps_set_position_mode,
--设置GPS工作模式:单GPS、单BD、GPS/BD双系统
.get_extension = gps_get_extension,
--定位之外的扩展功能实现
state-&thread = state-&callbacks.create_thread_cb(&gps&, gps_state_thread, state);
--static void gps_state_thread(void*
1、state通过arg参数传入函数
2、创建了Time和Nmea数据处理两个线程
state-&nmea_thread = state-&callbacks.create_thread_cb(&nmea_thread&, gps_nmea_thread, state);
--static void gps_nmea_thread(void*
--gps_opentty(state);
nmea_reader_init(reader);
--nmea_reader_parse(NmeaReader*
if (gps_state-&callbacks.nmea_cb) {
gettimeofday(&tv,NULL);
mytimems = tv.tv_sec * 1000 + tv.tv_usec / 1000;
gps_state-&callbacks.nmea_cb(mytimems, r-&in, r-&pos);
D(&reader_parse. %.*s &, r-&pos, r-&in );
我们是从APP层NMEA信息输出自定向下分析的,APP层信息输出的最终起始是:gps_state-&callbacks.nmea_cb(mytimems, r-&in, r-&pos);
到这里还有个问题:GPS芯片和CPU连接,使用的是哪个串口?这个串口号怎么确定的呢?
打算贴个完整HAL层的实例,考虑到代码很多,下篇在说吧。。。
阅读:21886
文章:16篇
阅读:12405>> C#读取gps串口信息,可以实时读取gps模块定位信息
C#读取gps串口信息,可以实时读取gps模块定位信息
所属分类:
下载地址:
GPSReadCSharp.rar文件大小:52.73 kB
分享有礼! 》
请点击右侧的分享按钮,把本代码分享到各社交媒体。
通过您的分享链接访问Codeforge,每来2个新的IP,您将获得0.1 积分的奖励。
通过您的分享链接,每成功注册一个用户,该用户在Codeforge上所获得的每1个积分,您都将获得0.2 积分的分成奖励。
可以选取串口号,解析gps模块获取的坐标,并且实时显示获取的字符串信息,可以判断当前定位状态是否处于锁定状态。
Sponsored links
源码文件列表
温馨提示: 点击源码文件名可预览文件内容哦 ^_^
GPSReadCSharp.exe16.00 kB23-12-15 11:32
GPSReadCSharp.pdb31.50 kB23-12-15 11:32
GPSReadCSharp.vshost.exe11.33 kB08-05-16 17:21
GPSReadCSharp.vshost.exe.manifest490.00 B17-03-10 22:39
6.26 kB23-12-15 11:32
9.93 kB23-12-15 11:32
Form1.resx5.88 kB23-12-15 11:32
GPSReadCSharp.csproj3.64 kB22-12-15 21:53
DesignTimeResolveAssemblyReferences.cache4.40 kB23-12-15 09:10
DesignTimeResolveAssemblyReferencesInput.cache6.20 kB23-12-15 11:32
GenerateResource.read.1.tlog320.00 B23-12-15 11:32
GenerateResource.write.1.tlog762.00 B23-12-15 11:32
895.00 B08-05-16 17:21
GPSReadCSharp.exe16.00 kB23-12-15 11:32
GPSReadCSharp.Form1.resources180.00 B23-12-15 11:32
GPSReadCSharp.pdb31.50 kB23-12-15 11:32
GPSReadCSharp.Properties.Resources.resources180.00 B23-12-15 09:19
ResolveAssemblyReference.cache7.84 kB23-12-15 09:19
494.00 B22-12-15 18:42
1.35 kB22-12-15 18:42
2.81 kB22-12-15 18:42
Resources.resx5.48 kB22-12-15 18:42
1.07 kB22-12-15 18:42
Settings.settings249.00 B22-12-15 18:42
603.00 B22-12-15 21:52
GPSReadCSharp.sln881.00 B22-12-15 18:42
GPSReadCSharp.suo22.00 kB08-05-16 17:22
&TempPE&0.00 B22-12-15 18:42
&Debug&0.00 B23-12-15 11:32
&Debug&0.00 B22-12-15 19:18
&x86&0.00 B22-12-15 18:42
&bin&0.00 B22-12-15 18:42
&obj&0.00 B22-12-15 18:42
&Properties&0.00 B22-12-15 18:42
&GPSReadCSharp&0.00 B22-12-15 21:50
&GPSReadCSharp&0.00 B22-12-15 18:42
(提交有效评论获得积分)
评论内容不能少于15个字,不要超出160个字。
ayy媛一直在找关于实时采集全球定位系统数据的代码,小菜鸟一只,自己弄了好久都没有弄好,终于找到了
评价成功,多谢!
下载GPSReadCSharp.rar
CodeForge积分(原CF币)全新升级,功能更强大,使用更便捷,不仅可以用来下载海量源代码马上还可兑换精美小礼品了
您的积分不足,优惠套餐快速获取 30 积分
10积分 / ¥100
30积分 / ¥200原价 ¥300 元
100积分 / ¥500原价 ¥1000 元
订单支付完成后,积分将自动加入到您的账号。以下是优惠期的人民币价格,优惠期过后将恢复美元价格。
支付宝支付宝付款
微信钱包微信付款
更多付款方式:、
您本次下载所消耗的积分将转交上传作者。
同一源码,30天内重复下载,只扣除一次积分。
鲁ICP备号-3 runtime:Elapsed:ms - init:0.1;find:0.9;t:13.1;tags:0.2;related:2210.9;comment:18.3; 5.8
登录 CodeForge
还没有CodeForge账号?
Switch to the English version?
^_^"呃 ...
Sorry!这位大神很神秘,未开通博客呢,请浏览一下其他的吧}

我要回帖

更多关于 树莓派3通过串口连接 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信