SPI驱动

这一节我们详细讲解TI CC13x0/CC26x0 SDK开发平台 基于TI-RTOS的SPI驱动实现,主要了解SPI驱动的分层实现、驱动接口,以及结合开发板板载SPIFlash调试通过驱动。

概述

SPI(Serial Perripheral Interface),串行外设接口,是Motorala公司推出的一种同步串行接口技术,它能够使MCU以全双工(数据能够同时进行双向传输)的同步串行方式与各种外围设备进行高速数据通信。它主要用在EEPROM、Flash、实时时钟(RTC)、数模转换器(ADC)、数字信号处理器(DSP)以及数字信号解码器之间,他只占用芯片的4根管脚来实现控制和数据传输,现在很多芯片上都集成了SPI技术。SPI有时也称为SSI(同步串行接口)。SPI的控制方式采用主-从模式(Master-Slave)。
SPI驱动程序能够驱动芯片与外围设备在SPI总线上进行数据的传输和接收。SPI驱动程序接口是典型的RTOS应用程序调用接口,它们被重定向到SPI_FxnTalble中的指针指定的特定驱动程序实现,增强了程序的移植性。

SPI驱动的分层实现

虽然我们在应用层直接调用几个驱动接口就可以在SPI总线上发送数据,但是在驱动程序内部从接口函数到底层硬件操作是通过了多层封装的。如图1所示是SPI驱动程序的分层实现图:

图1 SPI驱动程序的分层实现
由图1我们可以看到,应用程序开发者只需要直接调用中间件层的驱动接口(例如:SPI_init,SPI_open等等)就可以实现SPI的驱动功能,这里的中间件层就是我们程序中的SPI.c和SPI.h所在层。这一层规范统一了应用程序的调用接口,也就是说对于TI不同类型的芯片平台它们在这一层给出的接口都是一样的。应用层都是调用相同的接口来实现SPI功能,这样做的好处在于增强了程序的可移植性,不管你的平台怎么换,我的应用程序都是不变的,因为驱动的接口相同。

中间件层往下就是业务逻辑层,从业务逻辑层开始往下根据不同的芯片平台其接口封装实现就不尽相同了。这里我们以CC26XX芯片平台为例,业务逻辑层就位于SPICC26XXDMA.c和SPICC26XXDMA.h所在的层。这里采用DMA的数据传输方式,所以这一层主要在操控DMA以进行数据传输以及调用驱动库中的一些函数实现相应功能。需要注意的是这一层封装的驱动接口函数被全部放在一个函数指针结构体中,如List1所示,中间件层不直接调用这些驱动接口,而是通过一个配置文件(CC2640R2_LAUNCHXL.c)将装有驱动接口指针的结构体指针注册到SPI_config中,如List2所示,这样中间件层通过调用SPI_config中的结构体指针就可以指定使用业务逻辑层的驱动接口了。
List1:业务逻辑层驱动接口指针结构体

const SPI_FxnTable SPICC26XXDMA_fxnTable = {SPICC26XXDMA_close,SPICC26XXDMA_control,SPICC26XXDMA_init,SPICC26XXDMA_open,SPICC26XXDMA_transfer,SPICC26XXDMA_transferCancel};

List2:SPI_config中的驱动接口结构体指针注册

const SPI_Config SPI_config[CC2640R2_LAUNCHXL_SPICOUNT] = {{.fxnTablePtr = &SPICC26XXDMA_fxnTable,.object      = &spiCC26XXDMAObjects[CC2640R2_LAUNCHXL_SPI0],.hwAttrs     = &spiCC26XXDMAHWAttrs[CC2640R2_LAUNCHXL_SPI0]},{.fxnTablePtr = &SPICC26XXDMA_fxnTable,.object      = &spiCC26XXDMAObjects[CC2640R2_LAUNCHXL_SPI1],.hwAttrs     = &spiCC26XXDMAHWAttrs[CC2640R2_LAUNCHXL_SPI1]},
};

业务逻辑层再往下就是驱动库层(driver library),业务逻辑层会直接调用这一层的接口函数来来进行相应逻辑操作的。驱动库层位于ssi.c和ssi.h所在的层,这一层就开始与硬件接触,进行相应寄存器操作来实现SPI驱动了。

SPI的驱动配置

在上文中我们已经提到SPI的配置数组SPI_config[],它位于相应芯片平台的配置文件中,这里我们以CC26XX芯片平台为例,其配置文件为CC2640R2_LAUNCHXL.c。如List3所示,是CC2640R2_LAUNCHXL.c中关于SPI的配置代码段。
List3:UART的配置代码段

/*
 *  =============================== SPI DMA ===============================
 */
#include <ti/drivers/SPI.h>
#include <ti/drivers/spi/SPICC26XXDMA.h>SPICC26XXDMA_Object spiCC26XXDMAObjects[CC2640R2_LAUNCHXL_SPICOUNT];const SPICC26XXDMA_HWAttrsV1 spiCC26XXDMAHWAttrs[CC2640R2_LAUNCHXL_SPICOUNT] = {{.baseAddr           = SSI0_BASE,.intNum             = INT_SSI0_COMB,.intPriority        = ~0,.swiPriority        = 0,.powerMngrId        = PowerCC26XX_PERIPH_SSI0,.defaultTxBufValue  = 0,.rxChannelBitMask   = 1<<UDMA_CHAN_SSI0_RX,.txChannelBitMask   = 1<<UDMA_CHAN_SSI0_TX,.mosiPin            = CC2640R2_LAUNCHXL_SPI0_MOSI,.misoPin            = CC2640R2_LAUNCHXL_SPI0_MISO,.clkPin             = CC2640R2_LAUNCHXL_SPI0_CLK,.csnPin             = CC2640R2_LAUNCHXL_SPI0_CSN},
};const SPI_Config SPI_config[CC2640R2_LAUNCHXL_SPICOUNT] = {{.fxnTablePtr = &SPICC26XXDMA_fxnTable,.object      = &spiCC26XXDMAObjects[CC2640R2_LAUNCHXL_SPI0],.hwAttrs     = &spiCC26XXDMAHWAttrs[CC2640R2_LAUNCHXL_SPI0]},
};const uint_least8_t SPI_count = CC2640R2_LAUNCHXL_SPICOUNT;

我们可以看到SPI_config[]数组中的元素有三个参数,分别是.fxnTablePtr,.object,.hwAttrs,下面我们分别来看一下这三个参数的意义。

  • .fxnTablePtr
    .fxnTablePtr里面放的就是我们驱动具体的实现函数,这些驱动函数就是来自业务逻辑层。我们看到这里它被赋值成SPICC26XXDMA_fxnTable的指针,这个结构体就是List1中所示的业务逻辑层的驱动函数列表,在这里进行赋值配置之后,中间层的接口函数就可以链接使用它们了。
  • .object
    .object是用来存放SPI的各种参数数据的,例如控制参数,传输参数等。
  • .hwAttrs
    .hwAttrs是用来存放SPI硬件配置参数的数组,如List3所示,这些硬件参数是需要我们在使用SPI之前需要配置好的。

SPI驱动接口

在SPI的中间件文件SPI.c和SPI.h中,我们看到一共给出了7个接口函数,下面我们分别看一下每个接口函数的功能,形参,返回值以及注意事项。

  1. void SPI_init(void)
  • 函数功能:利用配置文件中设置的配置参数初始化SPI模块
  • 形参:无
  • 返回值:无
  • 注意事项:在调用此函数之前,SPI_config结构体中的参数必须配置好;此函数必须先于其他任何SPI接口函数调用。
  1. void SPI_Params_init(SPI_Params *params)
  • 函数功能:将SPI_Params结构体中的参数初始化为默认值
  • 形参:params需要进行初始化的SPI_Params结构体的指针
  • 返回值:无
  • 注意事项:初始化SPI_Params结构体的默认值如List4所示
    List4:初始化SPI_Params结构体的默认值
/* Default SPI parameters structure */
const SPI_Params SPI_defaultParams = {SPI_MODE_BLOCKING,  /* transferMode */SPI_WAIT_FOREVER,   /* transferTimeout */NULL,               /* transferCallbackFxn */SPI_MASTER,         /* mode */1000000,            /* bitRate */8,                  /* dataSize */SPI_POL0_PHA0,      /* frameFormat */NULL                /* custom */
};
  1. SPI_Handle SPI_open(uint_least8_t index, SPI_Params *params)
  • 函数功能:打开给定的SPI外设
  • 形参:index编入SPI_config中的外设索引,params指向SPI_Params参数块的指针,如果为NULL就使用默认值
  • 返回值:SPI_Handle如果成功打开了SPI接口就返回此接口的参数配置数组(SPI_config)的句柄,如果发生错误则返回NULL
  • 注意事项:在调用SPI_open()之前,应先调用SPI_init()进行了初始化
  1. bool SPI_transfer(SPI_Handle handle, SPI_Transaction *transaction)
  • 函数功能:执行SPI事务,发送/接收数据
  • 形参:handleSPI_open()中返回的句柄,transaction指向传输数据结构
  • 返回值:boolSPI_transfer是否启动成功,启动成功返回true,否则返回false
  • 注意事项:执行SPI事务有两种模式:SPI_MODE_BLOCKING和SPI_MODE_CALLBACK,在SPI_MODE_BLOCKING模式下,SPI_transfer将阻止任务的执行,直达事务完成。在SPI_MODE_CALLBACK模式下,SPI_transfer会调用SPI_CallbackFxn,不会阻止任务执行
  1. void SPI_transferCancel(SPI_Handle handle)
  • 函数功能:取消执行SPI事务
  • 形参:handleSPI_open()中返回的句柄
  • 返回值:无
  • 注意事项:在SPI_MODE_BLOCKING中,SPI_transferCancel()不起作用。在SPI_MODE_CALLBACK中,如果SPI_transfer()正在进行中,SPI_transferCancel()将停止SPI传输。
  1. int_fast16_t SPI_control(SPI_Handle handle, uint_fast16_t cmd, void *controlArg)
  • 函数功能:在给定的SPI接口上执行特定的操作
  • 形参:handleSPI_open()中返回的句柄;cmd特定的处理命令(该命令可以来自SPI.h或者逻辑应用层的.h文件,例如SPICC26XX.h);controlArg伴随cmd的可选择的读写命令
  • 返回值:不同命令处理的特定返回值,如果为负值表示操作不成功
  • 注意事项:在调用该函数之前必须先调用SPI_open()
  1. void SPI_close(SPI_Handle handle)
  • 函数功能:关闭指定的SPI外设接口
  • 形参:handleSPI_open()中返回的句柄
  • 返回值:无
  • 注意事项:在调用该函数之前,必须先调用SPI_close()

SPI数据传输的程序实现

下面我们调用SPI接口函数,将SPI设置为Master,实现SPI的数据传输。这里我们直接给出函数的实现。

SPI_Handle      spi;
SPI_Params      spiParams;
SPI_Transaction spiTransaction;
uint8_t         transmitBuffer[MSGSIZE];
uint8_t         receiveBuffer[MSGSIZE];
bool            transferOK;
SPI_init();  // Initialize the SPI driver
SPI_Params_init(&spiParams);  // Initialize SPI parameters
spiParams.dataSize = 8;       // 8-bit data size
spi = SPI_open(Board_SPI0, &spiParams);
if (spi == NULL) {while (1);  // SPI_open() failed
}
// Fill in transmitBuffer
spiTransaction.count = MSGSIZE;
spiTransaction.txBuf = transmitBuffer;
spiTransaction.rxBuf = receiveBuffer;
transferOK = SPI_transfer(spi, &spiTransaction);
if (!transferOK) {// Error in SPI or transfer already in progress.
}
  1. 我们可以看到,程序首先调用SPI_init(),利用SPI_config中的配置参数对SPI进行初始化,这一步函数的调用必须要在其他SPI接口函数调用之前进行,且SPI_config中的配置参数已经设置完成。
  2. 调用SPI_Params_init()将SPI_Params中的参数全部初始化为默认值,这些默认值在该接口函数说明中已经给出。
  3. 对于各自的应用,SPI_Params结构体中的参数值可能和默认值不同,接下来可以对它们进行相应的修改。
  4. 调用SPI_open()打开对应的SPI外设接口
  5. 为数据传输结构体SPI_Transaction进行赋值,设置数据接收和传输缓冲区,以及传输事务的帧数(MSGSIZE)。
  6. 调用SPI_transfer()开始进行数据传输。我们看到在结构体SPI_Transaction中有两个参数,分别是*txBuf(输入缓冲区),*rxBuf(输出缓冲区),当我们调用SPI_transfer()进行数据输出的时候,我们只需要将数据放入*txBuf中,调用函数之后数据就会传输出去;当我们调用SPI_transfer()进行数据接收的时候更加方便,调用函数之后,接收到的数据直接就会放入*rxBuf中。至此我们就完成了SPI接口的打开与数据传输。
  7. 根据以上接口的调用我们在例程SPIFlash.c中又写了一下函数,根据上面的调用实现,应该不难理解。
static bool spi_open(uint32_t bitRate) {SPI_Params spiParams;/*  Configure SPI as master */SPI_Params_init(&spiParams);//!< 参数初始化spiParams.bitRate = bitRate;spiParams.mode = SPI_MASTER;spiParams.transferMode = SPI_MODE_BLOCKING;/* Attempt to open SPI. */spiHandle = SPI_open(Board_SPI0, &spiParams);//!< 打开Board_SPI0return spiHandle != NULL;
}
static bool spi_write(const uint8_t *buf, size_t len) {SPI_Transaction masterTransaction;masterTransaction.count  = len;masterTransaction.txBuf  = (void*)buf;//!< 将要传输的数据存放的地址赋给*txBufmasterTransaction.arg    = NULL;masterTransaction.rxBuf  = NULL;return SPI_transfer(spiHandle, &masterTransaction) ? 1 : 0;//!< 调用SPI_transfer()写入数据
}
static bool spi_read(uint8_t *buf, size_t len) {SPI_Transaction masterTransaction;masterTransaction.count = len;masterTransaction.rxBuf = buf;//!< 将要接收到的数据存入buf所指的地址中masterTransaction.txBuf = NULL;masterTransaction.arg = NULL;return SPI_transfer(spiHandle, &masterTransaction) ? 1 : 0;//!< 调用SPI_transfer()读出数据
}

使用SPI从Flash中读取数据示例

在上面中我们已经讲过,芯片与外围设备之间的SPI接口是通过4个管脚连接进行控制和数据交换的,我们要通过SPI进行数据传输就必须有效控制这些管脚,打通数据交换链路。其次就是要根据外围设备自己的特性进行相关处理命令控制以读取写入数据。图2展示了数据通过SPI在Master和Slave之间的传输示意图:

  • SS/CS(Slave Select/Chip Select), 用于Master设备片选Slave设备, 使被选中的Slave设备能够被Master设备所访问;
  • SCK(Serial Clock)主要的作用是 Master 设备往 Slave 设备传输时钟信号, 控制数据交换的时机以及速率;
  • SDO/MOSI(Serial Data Output/Master Out Slave In)在Master上面也被称为Tx-Channel,作为数据的出口,主要用于SPI设备发送数据;
  • SDI/MISO(Serial Data Input/Master In Slave Out)在Master上面也被称为Rx-Channel,作为数据的入口,主要用于SPI设备接收数据;

上面我们调用SPI接口已经能够驱动芯片进行数据传输了,下面我们来讲讲要使Flash能够被写入和读出数据需要做哪些设置。这里我们利用以成都乐控畅联科技有限公司自主研发的CC13X0/CC26X0 Evaluation Board上搭载的SPI Flash W25Q80BV为例来进行讲解如何读出其Manufacturer ID和Device ID。你需要下载W25Q80BV的datasheet,上面关于W25Q80BV的很多信息对我们至关重要。
在W25Q80BV的datasheet上我们可以看见其管脚封装图如图3所示:

它与CC2640R2F之间的硬件连接已按照图2的原理连接好。我们看到管脚1是CS端,用于使能选中该Flash,所以在每次传输数据的时候我们首先需要将该管脚置为低电平来使能选中该Flash,不进行数传输的时候置为高电平,使其失能。在示例程序中,List1和List2就是在做这件事。
List1:使能Flash

static void flashSelect(void){PIN_setOutputValue(hFlashPin,Board_SPI_FLASH_CS,Board_FLASH_CS_ON);
}

List2:失能Flash

static void flashDeSelect(void){PIN_setOutputValue(hFlashPin,Board_SPI_FLASH_CS,Board_FLASH_CS_OFF);
}

通过阅读W25Q80BV的datasheet我们可以知道要对其进行读写操作是要写入相应指令的,每条指令都有其时序图。表1列出了部分指令,完整的指令集请查看其datasheet。
表1: W25Q80BV部分指令

指令名 指令码
写使能 06h
写失能 04h
写状态寄存器 01h
页编程 02h
扇区擦除 20h
读数据 03h
读Manufacturer/Device ID 90h

List3展示了程序中对这些指令的宏定义。
List3:W25Q80BV部分指令的宏定义

#define FLASH_WRITE_ENABLE          0x06
#define FLASH_WRITE_DISABLE         0x04
#define FLASH_WRITE_STATUS_REG      0x01
#define FLASH_WRITE_DATA            0x02
#define FLASH_SECTOR_ERASE          0x20
#define FLASH_READ_DATA             0x03
#define FLASH_READ_MANUDEVID        0x90

看了指令,我们再来看看相应指令的时序图。


图4.写使能时序图


图5.写失能时序图


图6.读数据时序图


图7.页编程时序图


图8.读readManufacturerDeviceID时序图

所以我们要想读出该Flash的Manufacturer/Device ID,要进行以下步骤:

  1. 写入写使能指令,保证我们能向Flash中写指令和数据。List4是这一步的代码实现。
    List4:写使能函数
static bool flash_writeEnable(void) {bool flag;const uint8_t comadd[] = {FLASH_WRITE_ENABLE};//!< 将指令“写使能”放入数组中待写入flashSelect();//!< 选中使能Flashflag = spi_write(comadd,sizeof(comadd));//!< 调用spi_write写入指令flashDeSelect();//!< 失能Flashreturn flag;
}
  1. 写入读Manufacturer/Device ID指令。
  2. 写入地址000000h。这在图8,读readManufacturerDeviceID时序图中我们可以看到在写入读Manufacturer/Device ID指令后,紧接着有3个字节的地址(Address):000000h需要写入。
  3. 读取两个字节的Manufacturer/Device ID。这在图8的时序图中也可以看到,在指令和地址写入之后,后面紧接着的两个字节就是Manufacturer/Device ID。List4展示了将读Manufacturer/Device ID指令和地址按时序写入,读出Manufacturer/Device ID的代码实现
static bool flash_getInfo(void) {bool flag;const uint8_t comadd[] = {FLASH_READ_MANUDEVID, 0x00, 0x00, 0x00};//!< 将读Manufacturer/Device ID指令以及地址放在数组中待写入flashSelect();//!< 选中使能Flashflag = spi_write(comadd, sizeof(comadd));//!< 调用spi_write写入指令和地址if(!flag) {flashDeSelect();return false;}flag = spi_read(devInfoBuf, sizeof(devInfoBuf));//!< 调用spi_read读出Manufacturer/Device IDflashDeSelect();//!< 失能Flashreturn flag;
}

完整的代码实现文件你可以在SPIFlash.c中查看,下面我们编译程序,利用CC13X0/CC26X0 Evaluation Board下载到CC2640R2F模块中中,然后利用逻辑分析仪和串口工具查看我们对Flash的读写效果。

  1. 将源文件SPIFlash.c放入SDK安装目录C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\drivers\uartecho
  2. C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\drivers\uartecho\tirtos\iar文件夹下打开uartecho.ewwIAR工程,这时我们在IAR可以看到如图2所示的工程目录。
  3. 选中uartcho.c文件,点击右键,选择remove,这时你可以看到uartcho.c文件被移出工程。
  4. 在工程目录下选中source files文件夹,选择Add条目下的Add Files...,然后将我们存放的SPIFlash.c添加进工程项目,如图9所示。
  5. 选中工程文件uartecho-Debug点击右键,选择Rebuild All编译工程。
  6. 保证已经下载了蓝牙协议栈镜像文件的调试板接入电脑,选择Project->Download->Download Active Application如图4所示,将程序下载到调试板中。
  7. 打开串口调试助手,连接上对应的端口,在CC13X0/CC26X0 Evaluation Board上按下复位键,你可以看到Manufacturer/Device ID被打印到串口调试工具上如图10所示。
  8. 如图11所示,将逻辑分析仪的各个通道分别连接CC13X0/CC26X0 Evaluation Board上对应的SPI四个端口SS/CS(DIO20)、SCK(DIO10)、SDO/MOSI(DIO9)、SDI/MISO(DIO8),对于具体是哪几个IO口,你也可以在文档CC13X0CC26X0EvaluationBoard中查看。
  9. 打开逻辑分析仪(Saleae Logic 1.1.15),首先配置好SPI接口,这在软件右侧Analyzers下的SPI选项下配置,如图12所示。
  10. 选择之后你可以看到如图13所示的配置界面,将各个端口对应的通道配置好,点击Save保存。
  11. 点击界面上方的Start按钮如图14所示,会弹出如图15所示的界面

  12. 在CC13X0/CC26X0 Evaluation Board上按下复位键,点击图15所示界面的stop按钮。
  13. 这样我们就可以看到我们向Flash输入数据,读出数据的时序图,如图16所示。我们可以看到,我们输入的指令与我们程序是完全符合的,读出Manufacturer/Device ID与我们串口打印出来的是完全符合的。

加入我们

文章所有代码、工具、文档开源。加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5.0。

© Copyright 2017, 成都乐控畅联科技有限公司.

CC2640R2F BLE5.0 CC2640R2F SPI驱动实现相关推荐

  1. CC2640R2F BLE5.0 CC2640R2F UART驱动

    UART驱动 这一节我们讲一下UART驱动的分层实现,UART APIs以及如何调用UART APIs来实现基本的串口打印. 概述 UART用于芯片和串行端口之间的数据传输,UART驱动程序经过多层的 ...

  2. CC2640R2F BLE5.0 CC2640R2F软件架构

    软件架构 开发模型 下图展示CC2640R2F支持的两种开发模型,本文讲解都集中在图一,也就是整个ble协议栈和应用都工作在一个CC2640R2F的单SOC解决方案. 单一设备:Controller. ...

  3. CC2640R2F BLE5.0 CC2640R2F Evaluation Board功耗测量

    CC2640R2F Evaluation Board功耗测量 简介 蓝牙低耗能标准在开发过程中就考虑到了长电池寿命, 可支持器件依靠单节纽扣电池运行数年.我们假定读者对基本的电气功能概念有所了解, 并 ...

  4. CC2640R2F BLE5.0 CC2640R2F/BLE5.0 距离测试

    说明 本文根据使用成都乐控畅联公司自主设计的开发板进行距离测试,同时对比官方LaunchPad.测试环境选在城市公路边,无线环境比较复杂.本公司的测试结果仅供参考. 软件环境 SmartRF Stud ...

  5. CC2640R2F BLE5.0 CC2640R2F增加一个具有通知属性的characteristic

    CC2640增加一个具有通知属性的characteristic 介绍 本章使用simple_peripheral作为实验平台.创建一个自定义属性的characteristic.simple_perip ...

  6. CC2640R2F BLE5.0 CC2640R2BLE5.0开发文档

    CC2640R2&BLE5.0开发 关于我们 关于我们 开发板介绍 文章所有代码.工具.文档开源.加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5. ...

  7. CC2640R2F BLE5.0 TI-RTOS概述

    TI-RTOS概述 TI-RTOS是CC2640R2F设备上低功耗蓝牙项目的运行环境.TI-RTOS内核是传统SYS/BIOS内核的定制版本,是一个具有驱动程序,同步和调度工具的实时抢占式多线程操作系 ...

  8. CC2640R2F BLE5.0 蓝牙协议栈Off-Chip OAD功能

    Off-Chip OAD 本节描述了分离镜像的片外OAD(Split Image Off-Chip OAD)和片外OAD库(Library off-chip OAD)之间的差异.片外OAD利用一个外部 ...

  9. CC2640R2F BLE5.0 PacketSniffer BLE 抓包

    PacketSniffer BLE 抓包 对于任何复杂协议的深入学习,我们都建议一个通用的学习方法,从规范->实现->抓包,规范是无关编程语言.语法文档表达,实现是各家SDK.API.源码 ...

  10. CC2640R2F BLE5.0 开发工具集

    CC2640R2BLE5.0开发工具集 对于CC2640开发的全部工具集,我们建议直接从官方下载,因为整个TI Simplelink工具集更新频率特别快,一定程度上的更新会导致向上不兼容或者功能性的差 ...

最新文章

  1. spring+cxf 开发webService(主要是记录遇到spring bean注入不进来的解决方法)
  2. 基于谱减法的声音去噪
  3. 如何用javac 和java 编译运行整个Java工程
  4. 黄聪:C#代码生成工具:文本模板初体验 Hello,World!
  5. oracle 控制文件作用是什么,Oracle控制文件(controlfile)作用
  6. 信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1078:求分数序列和
  7. openssh升级后root_又一root神器停止营业!时至今日你还需要root吗
  8. 幕后产品_使用Bitwarden密码管理器在幕后
  9. Android 系统(48)---WindowManager.LayoutParams 详解
  10. 如何调用华为云api_如何部署模型到ModelArts并远程调用 (五):如何调用在线API服务...
  11. OpenShift 4 Tekton - Tekton实现包含Gogs+SonaQube+Nexus+Report+WebHook的Pipeline
  12. IDEA代码格式化快捷键
  13. 麻省理工-机器学习导论
  14. 无线网络技术(实验)——无线局域网组成与管理实验
  15. 智慧城市顶层设计规划方案
  16. python国际象棋游戏_国际象棋版AlphaZero出来了 还开源了Keras实现
  17. e的ax次方怎么求导
  18. 最小函数依赖集,候选码,保持3NF依赖性的分解例题
  19. HDU-4826 Labyrinth
  20. 供应商分级方案:企业供应商管理系统助力汽车零部件企业实现供应商自动分级

热门文章

  1. 手把手教你批量下载微博视频
  2. 【Java的Excel操作】MyExcel
  3. TPP并不可怕,可怕的是我们开始自我封闭
  4. 3343:热血格斗场
  5. 无线键鼠接收器配对怎么就那么难?简直就是浪费
  6. PHP基础: CLI模式开发不需要任何一种Web服务器
  7. Processing绘制星空-2-绘制流星
  8. linux gbk 语言包安装,linux 安装gbk字符集
  9. 从今天起,我决定去掉cnzz统计
  10. “辣条一哥”冲刺IPO,卫龙三年净赚近20亿,小辣条赚大钱