TI CC32XX SDA中SimpleLink Academy教程翻译(RTOS部分的基础介绍非常易懂)
SimpleLink MCU平台是业界最广泛的基于ARM Cortex-M的有线和无线微控制器组合。
目录
一、Debug Printing
1、printf
2、Display_printf
3、System_printf
二、Real-Time OS
1、RTOS
RTOS的常用术语
裸机开发与实时操作系统开发的区别
POSIX
RTOS中线程的通信机制
RTOS的组成成分
2、TI-RTOS
TI-RTOS kernel
SDK中支持TI-RTOS的组成成分
TI-RTOS的配置和示例
调试工具
3、POSIX
一、Debug Printing
TI的SDK为程序的调试提供许多信息打印的API,这一节是为了展示与信息打印相关的的API在程序调试过程中的作用,并不包括CCS自带的log或trace功能。
下表是TI对各不同系列的产品提供的SDK中各包含的打印 API的简单介绍:
下面仅对前三中进行详细介绍:
1、printf
printf是C语言的一部分,它适用于TI-RTOS,Free-RTOS和NO-RTOS程序,但Printf对于嵌入式开发并不理想。
由于默认情况下,TI- rtos在TI编译器中插入一个锁定机制(GateMutex),以保证线程安全。TI编译器的printf实现调用这种锁定机制。因此,默认情况下,在使用TI- rtos和TI Compiler的应用程序中,不能在Hwi或Swi中调用printf。如果在TI-RTOS内核中启用了断言,则会出现断言失败。如果没有启用断言,可能会得到不确定的结果。
滥用printf会严重影响程序的实时性,因为默认情况下,在CCS (IAR也是类似)中,printf输出会进入CIO ('C' Input/ output)缓冲区。当CIO缓冲区已满或EOL字符(即。'\n')被写入,CCS将大侠一个断点,转而读取缓冲区的内容,读取完毕后才恢复目标,这个操作对实时性能有非常糟糕的影响。
2、Display_printf
Display_printf允许用户将输出发送到不同的位置:LCD、IDE控制台或UART。在开启display是选择
Display_printf的格式选项非常类似于printf。许多SDK示例使用Display通过UART输出数据,以允许与操作系统和IDE隔离,在通过UART或IDE Console输出数据时,其行和列参数是被忽略的。
#include <ti/display/Display.h>
/* 以uart方式调用Display_printf */
Display_Handle display;
display = Display_open(Display_Type_UART, NULL);
...
Display_printf(display, 0, 0, "Count = %d and it's address is 0x%x\n", count, &count);/* 以IDE Console方式调用Display_printf */
Display_Handle display;
display = Display_open(Display_Type_HOST, NULL);
...
Display_printf(display, 0, 0, "Count = %d and it's address is 0x%x\n", count, &count);
在通过UART调用Display_printf API打印信息时,要注意在RTOS程序中,Display函数只能在task或pthread中被调用,不能再main或中断服务函数中使用。并且Dispaly_printf API将UART实例设置为仅可传输数据。
当通过IDE Console调用Display_printf API打印信息时,我们可以在任意地方调用打印函数,但是注意,调用Display_printf函数时,仍然会影响程序的实时性。
注:关于Display模块的更多细节可以在“SimpleLink MCU SDK用户指南”和“驱动运行时api (doxygen)”中找到。
3、System_printf
System_printf仅在应用程序使用TI-RTOS时可用。System_printf支持与printf相同的格式字符串和一些附加的格式字符串。
该API定义在在build工程文件中
#include <xdc/runtime/System.h>System_printf("Count = %d and it's address is 0x%x\n", count, &count);
System_printf模块允许应用不同的系统“Proxy”。选择Proxy会导致System_printf API的底层实现不同,主要支持两种proxy方式:
- SysMin模式常用于ROV或IDE Console的输出中,SysMin模块将ASCII数据存储到一个内部缓冲区中。内部缓冲区的大小是可配置的。直到收到指令(例如调用System_flush或配置为在应用程序退出时刷新),数据才刷新到CIO缓冲区。因此,SysMin不会像Display_printf (IDE控制台外)或printf那样影响实时性能。
- SysCallback模式主要用于串口输出,SysCallback模块允许用户插入一个类似于putc的函数(putchFxn),该函数在格式化完成后在System_printf中调用。默认的putchFxn函数什么都不做。一个常见的用例是插入一个将字符写入UART的putchFxn函数。
System_printf模块是存在于内核中的,在RTOS操作系统中一般不允许直接访问内核的程序,因此如果要修改System_printf的属性,如proxy时,常常需要
二、Real-Time OS
1、RTOS
“A real-time operating system (RTOS) is an operating system (OS) intended to serve real-time application process data as it comes in, typically without buffering delays. ”
上面这段话时Wiki对RTOS的定义,大意时RTOS是一种可以以最快的速度使应用程序处理到来的数据的操作系统。其中最关键的两点是:最小的中断延迟和最小的线程切换延迟。RTOS的价值在于他的可预测性,这一点很关键,并非处理速度快的操作系统就是实时操作系统,实时性指的是一个任务一定会在产生后的一定时间内被处理,而并非在规定时间内可以完成的工作量。
因此对于一个RTOS而言,必须满足以下几个要个:
低延迟:毕竟是实时操作系统!
- 确定性:再次强调,一个实时操作系统必须要要知道处理一件事情需要多长时间!
- 结构化软件:以结构化的方式对不同的任务分而治之,添加新任务也十分方便。
- 可扩展性:RTOS必须能够从一个简单的应用程序扩展到一个具有堆栈、驱动程序、文件系统等的复杂应用程序。
- 与程序分离:RTOS通常与调度器一起,负责处理电源管理、中断表管理、内存管理和异常处理等事务,从而可以使开发人员专注于应用程序的开发
在TI给出的SimpleLink SDK中支持两种RTOS内核,分别是TI-RTOS和FreeRTOS,并且二者都支持POSIX类型的实现方式。因此在SDK中我们可以通过调用POSIX API来实现RTOS的具体功能。
在学习RTOS之前,我们必须保证对嵌入式系统有一定的理解,比如我们需要知道什么是中断,什么是堆栈等等。
RTOS的常用术语
1、线程的标准类型
- Interrupt Service Routine (ISR):中断服务程序,我们需要知道所有的ISR都是由硬件中断启动的线程,一个中断服务程序被产生后就会一直运行到完成,所有的ISR共用一个栈。
- Tasks: 任务,任务线程是传统的长期的线程,这种线程可以被突发事件阻塞,并且拥有自己的堆栈,创建时需要划分堆栈大小。(注意下文的“任务”字眼)
- Idle:空闲,只有在没有其他线程准备执行时才运行的最低优先级线程。通常Idle只是一个优先级尽可能低的特殊进程。
2、调度器(Schedulers)
每个RTOS的核心中都有一调度器,它负责调配系统中各线程的执行。
调度器对线程的管理方式主要有两种:
- 抢占式调度(Preemptive Scheduling):这是最常见的RTOS调度类型,通过抢占式调度器,正在运行的线程会一直持续运行直到以下某一种情况出现:
- 运行完成
- 有更高优先级的线程变为就绪状态(发生抢占),高优先级线程会替代低优先级线程运行
- 线程在等待某个资源的释放时释放了处理器
对于抢占式调度,相同优先级的任务秉持着先入为主的原则执行,假设有相同优先级的任务A和任务B先后被创建,则先被创建的任务A先开始运行,而后被创建的任务B则一直处于就绪态,当任务A被阻塞或完成后,且同一时刻没有更高优先级的线程产生,则处于就绪态的任务B开始执行,但当被阻塞的任务A解除阻塞后,并不能继续运行,而是转为就绪态,只有等到当前正在运行的任务B进入阻塞或完成后,任务A才可以继续运行。
- 时间片调度(Time-slice Scheduling):这种类型的调度保证每个线程都有一个执行槽。这种调度方式通常不利于实时应用,通常只出现在相同优先级的Task中。如果需要,TI-RTOS内核支持Task的时间切片调度。
3、线程安全(Thread-safe)
如果一段代码以一种方式可以保证在同一时间内能够被多个线程同时正确访问(读/写)其中的可共享的数据结构,那么这段代码就是线程安全的。
需要注意的是线程安全是RTOS中的应用程序必须要遵守的一个原则,但并非RTOS独有的特点,比如在修改相同内存的中断时也必须要注意线程安全的问题。
4、阻塞(Blocked)
当一个任务(Task)正在等待某个资源时并且该任务没有占用CPU资源,此时处于被阻塞的状态。例如当一个任务调用了Task_sleep或Semaphore_pend时,该任务将被阻塞,此时另外一个线程将允许运行。
注意:处于一个紧密的循环的旋转寄存器不会被阻塞,其任务切换的方式称为轮询(Polling)。
5、裸机(Bare-Metal)
对于不使用OS的应用程序统称为Bare-metal,也就是我们常说的裸机。
裸机开发与实时操作系统开发的区别
对于一个简单的程序一般可分为三部分:初始化部分、应用程序部分和中断服务程序部分。
下图是裸机,小型RTOS和标准的RTOS的功能比较图:
可以看到,对于裸机而言,其初始化主要是在main函数的开始部分,而应用程序部分则存在于一个超级大循环当中(相当于一个超级循环状态机),其他需要处理的事务的响应则由中断来完成。
而小型RTOS与裸机程序的分布几乎相同,稍有不同的是,在main函数中我们需要开启BIOS内核,之后RTOS会将空闲循环状态机和中断服务程序的线程切换交给调度器来控制。在此基础上,我们可以自由的添加多个不同的任务程序,而不用考虑维持主循环的问题。
POSIX
POSIX是一种对于操作系统兼容性的IEEE工业接口标准,他是在RTOS实现的功能的基础上的进一步封装。
TI-RTOS和FreeRTOS都支持该封装标准的API,我们可以使用POSIX API来实现具体的RTOS,而不需要考虑底层是TI-RTOS还是FreeRTOS。例如当我们使用POSIX API创建了一个线程时,其底层的TI-RTOS或FreeRTOS的线程也会被创建。
但是需要注意的是POSIX并不是一种RTOS,它是一种操作系统兼容性层,允许应用程序在操作系统之间轻松移植。
想要更深入的了解POSIX,请看:POSIX Project Zero (ti.com)
RTOS中线程的通信机制
所有的RTOS都提供了标准的通信机制,如信号量、互斥锁、消息队列、链表等。
下面将介绍几个重要的通信机制,这些机制的定义都是以POSIX API给出的,同时在TI-RTOS和FreeRTOS中也有类似的定义。
1、信号量
信号量允许资源管理。任务可以通过sem_wait()被阻塞,该任务会停止占用CPU资源,直到其他任务通过sem_post()释放该信号量位置。
一个常见的用例是Hwi接收数据并在中断程序的最后释放信号量,以便任务可以处理它。这是可取的,因为它最小化了中断的持续时间。
2、消息队列
消息队列用于在线程之间发送数据。消息队列可以配置为发送/接收任何大小的用户定义消息。
如下图便是两个任务之间的消息通信的实现程序:
当您希望将特定功能集中到单个任务中时,消息队列非常有用。所有其他线程都可以将消息发送到集中任务进行处理。消息队列以线程安全的方式处理消息。
RTOS的组成成分
RTOS并不仅仅是一种应用程序,要实现操作系统的管理理念需要融合很多复杂的元素,因此RTOS的组成成分包括但不限于:
调度器:保证运行最高优先级线程的抢占式调度器。
通信机制:信号量、消息队列、队列等。
关键区域机制:互斥、门、锁等。
计时服务:时钟、计时器等。
电源管理:对于低功耗设备,电源管理通常是RTOS的一部分,因为它知道设备的状态。
内存管理:可变大小的堆、固定大小的堆等。
外设驱动程序:UART, SPI, I2C等。
协议栈:BLE、WiFi等。
文件系统:FatFs等。
设备管理:异常处理,启动等。'
2、TI-RTOS
TI-RTOS从一个占用空间最小的实时多任务内核(以前称为SYS/BIOS))扩展到一个完整的RTOS解决方案,包括各种协议栈、多核通信机制、设备驱动程序和电源管理。
TI-RTOS kernel
TI-RTOS的核心便是内核,关于内核的详细介绍可以参考:<SimpleLink_SDK_Install_Dir>\docs\Documentation_Overview.html
下面将介绍内核的主要功能或组成
1、程序调度
内核的主要功能是负责程序的调度,该工作由其内部的调度器来完成,它将严格保证当前处于就绪态的线程中优先级最高的优先运行。
TI-RTOS中不同种类的线程的优先级如下表所示:
如上图所示:
- 优先级最高的是硬件中断HWI,它是被硬件事件所触发,硬件中断一直运行到结束,不会被任何线程阻塞,但是注意它可以被优先级更高的HWI抢占·。所有的HWI共用一个堆栈空间。HWI可以通过C语言来编写,它们被TI-RTOS调度器管理,但是有一个例外——零延迟中断。应用程序可以将任意中断表明为“零延迟”中断,这意味着TI-RTOS调度器将无法直接控制该中断,TI-RTOS的内核为这些中断的执行添加了0延迟,当然这些中断也无法使用内核调度器的API。
- 优先级次之的是软件中断SWI,它与HWI同属于中断服务程序,它是被软件开启的中断,SWI与HWI共用一篇堆栈空间,其优先级比HWI低,因此常常用来执行被延迟的HWI的相关工作。
- 任务TASK是一中普通的操作系统线程,每个任务都有自己的堆栈空间来保存它们的运行状态。在系统内存允许的前提下,用户可以自行添加任意多个任务线程,并且每个线程在某一时刻都必定保持在某一种状态下。
- 空闲时一种特殊的任务,它永远以尽可能低的优先级运行(0)。空闲任务主要负责执行后台任务,比如系统堆栈的检查,CPU负载的检测等,用户也可以将一些待机时执行的程序写入到idle任务当中,比如在idle线程中将设备置于低功耗模式。
2、任务线程的状态
正如上文提到的,任意时刻下每个任务都具有一种确定的状态,在不同时刻,任务之间通过阻塞、抢占等方式实现状态切换,其任务状态切换图如下图所示:
如上图所示,任意时刻下一个任务可能处于以上四种状态的其中一种,这四种状态分别是就绪态、运行态、阻塞态和终止态。
当一个任务被创建后首先进入到就绪态,此时调度器会判断目前正在运行的线程的优先级是否小于被创建的线程,若小于则发生线程抢占,优先级高的任务进入到运行态,否则则一直去除就绪态直到满足运行条件为止。
正在运行的任务有三种情况发生线程切换:
- 运行种的任务执行完毕或被强制退出,进入到了终止态
- 运行中的任务需要等待某些资源而进入到阻塞状态
- 有更高优先级的线程产生,运行中的任务被抢占,此时被抢占的任务切换为就绪态
注意,处于阻塞和就绪态的任务都可以被应用程序强制删除,处于运行态的任务需要先退出。
3、线程通信机制
TI-RTOS常用的线程通信机制主要有以下几种:
- Semaphores: 用于控制对公共资源的访问的对象。它们可以用于任务同步和互斥。
- Mailboxes:消息传递模块
- Queues: 双向链表(不过没有同步)
- Gates: 用于保护对关键数据结构的同时访问。Gate是一个可重入的互斥锁。
- Events: 允许通过多个事件同步的通信模块
下面将举例说明上述通信机制再线程种的作用。
实例1:二值信号量作为互斥锁保护共享结构体的数据更新
该实例演示了信号量如何作为互斥锁保护共享结构体的更新。被保护的区域通常被称为临界区域
当有两个任务Task1和Task2,其中pri1<pri2,
任务1的目的是规定操作流为写入模式,即将src和dst变量中的数据写入到内存A,B中
任务2的目的是规定操作流为读取模式,即将内存C,D中的数据读取到src和dst变量中
如果在任务1在运行期间,任务2进入到了就绪状态,情况如下图所示,会发生什么呢?
会发生任务抢占!当src写入内存A时,发生了任务抢占,此时任务2将改变该存储区的操作流,进而将C,D的内存数据存入变量src和dst中,再回来执行任务1时,任务1会将dst中的数据写入内存B中!!!这产生了一个错误,我们间接的将内存C中的数据写入到了内存B中,可很显然我们并不想这样做。
我们并不想在一个任务中对一块数据结构访问时有其他同样需要访问该数据结构的任务干扰,因此我们必须要加上一个互斥锁,规定当某一任务访问了某个数据结构时,其他任务必须等待该任务完成访问。
实现的方式也很简单,我们需要定义一个二值信号量,该信号量初始值必须为1,并为相应的数据结构“上锁”,所谓随手关门是个好习惯,不然容易有老六!!!
void write1(UArg arg0, UArg arg1){Semphore_pend(semHander, BIOS_WAIT_FOREVER);myGlobal.cmd = WRITE_BUFFER;myGlobal.src = bufferInFlash;myGlobal.dst = bufferInRAM;Semphore_post(semHander);
}
相同的互斥锁也可以使用GATE来实现,并且对于重要的数据区域,我们在修改它们时也可以通过
Hwi_disable()
&Hwi_restore()
来短暂的禁用中断。
实例2:利用计数信号量控制链表(队列)的数据传输
该实例演示了如何通过链表(队列)完成两任务之间的数据通信,并且如何通过信号量对数据传输加以控制。
某些情况下,我们希望被放置到队列中的数据可以实时的发送到需要的任务当中,但也有一些情况下,我们希望存放到队列中的数据可以存够一定数量后再统一发送到需要的任务当中,这个实时传输(瞬时或定期)数据的工作便是由信号量完成的。
上述代码中,我们若使得发送任务的优先级大于接受任务,则数据会存满5个后一起发送;若想要产生的数据立刻被接收任务接收到,则需要设置接受任务的优先级大于发送任务。
我们还可以注意到,对于上面用户队列的实现,用户的数据结构通过MyMsg结构中的Queue_Elem字段提供prev和next指针。这避免了在队列模块中分配内存,理论上如果内存充足的话链表可以无限长。同时Queue_put()和Queue_get()是以原子操作的方式从链表中添加/删除元素,该操作不会被线程调度所打断,每次仅添加或删除一个元素。
4、时间戳服务
- Timer:允许管理硬件定时器的模块。
- Clock: TI-RTOS,默认情况下,使用定时器来驱动定时服务(例如Task_sleep()等)。应用程序可以将函数插入Clock模块,这些函数将按照它们请求的速率被调用。您插入的Clock函数可以是周期性的或一次性的。
- Second:统一前端到设备的RTC定时器。
5、内存管理
下图是主堆栈的实现概述
Heap | Description | Reason to use |
---|---|---|
HeapMem | Variable size allocation | Very flexible |
HeapBuf | Fixed size allocation | Fast and deterministic |
HeapMultiBuf | Multiple fixed size allocation | Fast and deterministic |
HeapMin | Variable size, growth only | Fast and deterministic (but cannot call free) |
HeapTrack | Stacking diagnostic heap | Helps find memory leaks, corruption, etc. |
堆位于Memory_alloc()和Memory_free() API 的底层。
默认情况下,TI-RTOS创建一个系统堆(或默认堆)。我们为Memory_alloc()和Memory_free()中的IHeapHandle参数传入NULL时,将使用此堆。系统堆也用于malloc()和free()函数(内核替换了RTS malloc()和free()函数)。默认情况下,系统堆是一个HeapMem实例,其大小在链接器命令文件(Linker command file)中的设置。
SDK中支持TI-RTOS的组成成分
1、TI Drivers
TI Drivers是一组通用的、一致的函数api,在TI SimpleLink相关产品中都得到支持,并支持最大限度的代码可移植性。
TI驱动程序可以与RTOS一起使用,也可以不使用RTOS。SimpleLink平台中的所有设备都支持TI-RTOS。大多数SimpleLink设备也支持FreeRTOS。然而,TI Driver并非直接调用内核程序API,而是在内核的上层提供了一个统一的驱动移植层(DPL)来为TI Driver的访问提供接口。
但是DPL API是为了满足TI Drivers而专门开发的,并不建议应用程序的代码使用DPL的相关API。
DPL与内核属于同一层级的抽象,若应用程序想要使用有关操作系统的抽象,可以调用POSIX API
2、Display
3、Fatfs
TI-RTOS的配置和示例
对于传统的TI例程,都是通过release.cfg文件来配置TI-RTOS内核的。这种.cfg文件时一种Java Script文件,可以编辑成文本文件或图形化文件。
对于TI-RTOS7,对RTOS的设置的改动变为使用SysConfig。有关TI-RTOS向TI-RTOS7的移植,可以在SDK种找到相关说明。
在TI-RTOS例程被导入时,内核工程也会被自动导入,这便是我们见到的build release工程文件,该工程文件是我们导入的工程的必须依赖项。同时在SDK种也提供了不同种类的内核工程,我们可以在 kernel/tirtos/builds/BOARD/release and debug 路径下找到它们。
一般情况下,我们导入工程时内核工程都是默认导入release版本,但TI强烈建议在应用程序的开发过程中使用debug内核配置工程,其中包含了许多优秀的调试功能,如栈溢出检测,断言检测等
如需更改或手动配置内核工程文件,可以参考SimpleLink SDK User Guide。
由于考虑了TI-RTOS和FreeRTOS的兼容性问题,TI-RTOS的例程中调用的大多是POSIX API,但这对于TI-RTOS程序并非必须的,我们在开发时可以自由的使用TI-RTOS API和POSIX API。
调试工具
1、Runtime Object View(ROV2)
该工具可以在调试过程中提供每个任务的堆栈的占用情况和执行到某个断点处时各各线程的状态。
我们在使用ROV2的时候需要在建立连接后,把本工程下的overview.rov.json作为dashboard文件导入进去,才可以正常检测程序的运行状态。
其右侧的选项“BIOS->Scan for errors…”是一种快速而简单的方法来确定RTOS是否处于一个糟糕的状态(例如,堆栈损坏,由于糟糕的应用程序指针或缓冲区溢出导致的数据损坏,等等)
2、System Analyzer
CCS中的System Analyzer工具允许您直观地查看关键项目,如执行图(execution graph)、CPU负载(CPU load)、代码段的平均/最大/最小执行时间(average/max/min execution times for code segments)等。
这是利用UIA中的Logging records功能来完成的。典型的用法是将记录保存在目标上的缓冲区中,当目标停止时,System Analyzer读取该缓冲区。然而,也有运行时读取功能,用于从目标(例如UART)获取日志记录。
其中最典型的执行图(execution graph)可以让我们清晰的看到在一段时间内线程的运行情况:
但是要注意由于使用了logging功能,我们需要找到工程对应的内核.cfg文件,来使能logs功能。
BIOS.logsEnabled = true;
//BIOS.logsEnabled = false;
并在该文件中添加一下代码,来配置内核来维护驻留日志记录的目标上的缓冲区。
var LoggingSetup = xdc.useModule('ti.uia.sysbios.LoggingSetup');
LoggingSetup.sysbiosLoggerSize = 1024;
LoggingSetup.loadLogging = false;
注意:这也是在驱动程序示例使用的调试TI-RTOS内核配置项目中。上面代码只使用了内核的日志记录功能,而没有使用CPU负载日志记录功能。
此外,对于CC13XX/CC26XX的设备,在内核中ROM的记录功能需要被失能。因为我们无法在ROM中使用内核,若要使用内核记录事件,我们需要将内核放在CC26xx的flash中,操作方法便是通过删除(或注释掉). cfg文件中的以下行,
var ROM = xdc.useModule('ti.sysbios.rom.ROM'); if (Program.cpu.deviceName.match(/CC2640R2F/)) {ROM.romName = ROM.CC2640R2F; } else if (Program.cpu.deviceName.match(/CC26.2/)) {ROM.romName = ROM.CC26X2; } else if (Program.cpu.deviceName.match(/CC13.2/)) {ROM.romName = ROM.CC13X2; } else if (Program.cpu.deviceName.match(/CC26/)) {ROM.romName = ROM.CC2650; } else if (Program.cpu.deviceName.match(/CC13/)) {ROM.romName = ROM.CC1350; }
3、POSIX
POSIX是用于OS兼容性的IEEE行业API标准
POSIX不是RTOS。它驻留在TI-RTOS或FreeRTOS之上。从下图中可以看到,POSIX对应用程序隐藏了底层的RTOS。
目前,SimpleLink设备的SDK已经支持的POSIX API有:
- Pthread (includes mutexes, barriers, condition variables and read-write locks).
- Semaphores
- Clocks/Timers/sleep
- Message Queue
POSX API都包含在<pthread.h>头文件中,而TI-RTOS API都包含在<BIOS.h>路径下
有关TI POSIX的详细信息还需要参考“TI-POSIX User's Guide”。
TI CC32XX SDA中SimpleLink Academy教程翻译(RTOS部分的基础介绍非常易懂)相关推荐
- React + webpack 开发单页面应用简明中文文档教程(一)一些基础概念
React + webpack 开发单页面应用简明中文文档教程(一)一些基础概念 React 入门系列教程导航 React + webpack 开发单页面应用简明中文文档教程(一)一些基础概念 Rea ...
- python中newfile是干嘛用的_Python基础介绍 | File I\O 读写文件
如何用Python读写文件呢?我们有许多种办法,包括使用Pandas或者使用os相关的工具,我们来看一下: 首先,得明白文件路径的事情: import os current_file = os.pat ...
- ios php mysql数据库_IOS_iOS中SQLite使用教程,SQLite,是一款轻型的数据库, - phpStudy...
iOS中SQLite使用教程 SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中 ...
- metaProdigal:宏基因组序列中的基因和翻译起始位点预测
文章目录 metaProdigal:宏基因组序列中的基因和翻译起始位点预测 热心肠日报 摘要 动机 Motivation 结果 Results 可用性 Availability 主要结果 表1. 大肠 ...
- caffe教程翻译:在caffe上训练与测试数据
本文为caffe官网上ImageNet教程翻译,地址:http://caffe.berkeleyvision.org/gathered/examples/imagenet.html 本教程旨在教我们用 ...
- Deep Learning 教程翻译
Deep Learning 教程翻译 非常激动地宣告,Stanford 教授 Andrew Ng 的 Deep Learning 教程,于今日,2013年4月8日,全部翻译成中文.这是中国屌丝军团,从 ...
- ubuntu中flashcache使用教程
原文:ubuntu中flashcache使用教程 当前存在多种性能价格不一的存储设备,以价格高性能好的存储设备来加速价格低性能较差的存储设备,是一种提升系统整体性能的方案.flashcache便可以做 ...
- JavaBeginnersTutorial 中文系列教程·翻译完成
原文:JavaBeginnersTutorial 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. 在线阅读 ApacheCN 学习资源 目录 ...
- beginnersbook C 语言教程·翻译完成 | ApacheCN
来源:ApacheCN beginnersbook 翻译项目 译者:飞龙 协议:CC BY-NC-SA 4.0 首先学习 C 基础知识 如何安装 Turbo C++:编译并运行 C 程序 C 程序结构 ...
最新文章
- c++ 遍历多级目录
- leetcode 21 Merge Two Sorted Lists
- SpringBoot2.0实现静态资源版本控制
- iOS开发-多线程编程技术(Thread、Cocoa operations、GCD)
- Vue 方法与事件处理器
- 微课|中学生可以这样学Python(7.3.1节):私有成员与公有成员
- 关于集成通用mapper的Mybatis代码生成器产生的model类注解
- bzoj 4127: Abs(树链剖分+线段树)
- Python--网络编程-----通过socket收发文件软件开发--面向对象开发
- 日常推荐大神操作,如何再oracle中delete数据后恢复
- JavaScript遍历DOM
- bootstrap 检验 法 原理_Bootstrap教程-用SPSS中的Process插件做中介效应分析
- Pointnet网络结构与代码解读
- 多个元素过渡---过渡模式
- 享元模式——滴滴打车的共享经济思维
- 美国佐治亚大学计算机专业,美国佐治亚大学排名
- 关于汽油必须知道的11件事 可能是最全的油品全解
- JAVA设计模式之访问者模式
- 像素、分辨率及PPI各自含义与区别及目前主流手机的分辨率介绍
- Shell脚本编程--cut命令