普冉PY32系列(八) GPIO模拟和硬件SPI方式驱动无线收发芯片XN297LBW

这篇具有很好参考价值的文章主要介绍了普冉PY32系列(八) GPIO模拟和硬件SPI方式驱动无线收发芯片XN297LBW。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

  • 普冉PY32系列(一) PY32F0系列32位Cortex M0+ MCU简介
  • 普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境
  • 普冉PY32系列(三) PY32F002A资源实测 - 这个型号不简单
  • 普冉PY32系列(四) PY32F002A/003/030的时钟设置
  • 普冉PY32系列(五) 使用JLink RTT代替串口输出日志
  • 普冉PY32系列(六) 通过I2C接口驱动PCF8574扩展的1602LCD
  • 普冉PY32系列(七) SOP8,SOP10,SOP16封装的PY32F002A/PY32F003管脚复用
  • 普冉PY32系列(八) GPIO模拟和硬件SPI方式驱动无线收发芯片XN297LBW
  • 普冉PY32系列(九) GPIO模拟和硬件SPI方式驱动无线收发芯片XL2400
  • 普冉PY32系列(十) 基于PY32F002A的6+1通道遥控小车I - 综述篇
  • 普冉PY32系列(十一) 基于PY32F002A的6+1通道遥控小车II - 控制篇
  • 普冉PY32系列(十二) 基于PY32F002A的6+1通道遥控小车III - 驱动篇

XN297LBW

XN297LBW 是一个SOP8封装的2.4GHz频段无线收发芯片, 价格在1元左右, 适用于低成本应用. 虽然磐启已经发布了 XN297L 的下一代产品 PAN1026, 但是市面上基本上见不到后者的身影, 零售能买到的还是 XN297L.

普冉PY32系列(八) GPIO模拟和硬件SPI方式驱动无线收发芯片XN297LBW

生产商是上海磐启, 产品页地址: https://wiki.panchip.com/ble-lite/2-4g-t-rx/xn297l_series/

磐启对 XN297L 的产品介绍: "工作在 2.400~2.483GHz 世界通用 ISM 频段的单片无线收发芯片, XN297L采用嵌入式基带协议引擎, 适用于超低功耗无线应用. 采用 GFSK 调制, 可配置频率信道, 输出功率和接口数据速率等射频参数. XN297L 支持 2Mbps, 1Mbps 和 250Kbps 的数据速率. 对于长距离应用, 输出功率可以调节高达 11dBm, 对于短距离和超低功率应用, 输出功率可以低至-23dBm."

XN297LBW 主要特性

  • 无线
    • 通信频段:2.400GHz~2.483GHz
    • 数据速率:2Mbps,1Mbps,250Kbps
    • 调制方式:GFSK
  • 发射器
    • 输出功率:11, 9, 5, -1, -10 or -23dBm
    • 18mA@2dBm
    • 30mA@9dBm
  • 接收器
    • -83dBm@2Mbps
    • -87dBm@1Mbps
    • -91dBm@250Kbps
  • 协议引擎
    • 支持1到32字节或64字节数据长度
    • 支持自动应答及自动重传
    • 6个接收数据通道构成1:6的星状网络
  • 电源管理
    • 工作电压:2.3~3.3V
    • 2uA断电模式
    • 30uA待机-Ⅰ模式
  • 主机接口
    • 支持3引脚SPI, 4Mbps SPI接口速率
    • 支持两个独立的32字节TX和RX FIFOs
    • 支持一个64字节的TX和RX FIFOs
  • 封装
    • SOP8

这里要注意的几点:

  1. 工作电压是3.3V, 不要错接5V.
  2. SPI速率为4MHz, 实测上限不会比4MHz高多少, 在6MHz频率时大概率SPI通信错误导致不能工作.
  3. TX FIFO 与NRF24L01相比只有两个32字节, 而NRF24L01是3个32字节. 性能相对缩水.

PIN脚定义和应用电路

PIN脚定义

  • VDD 和 VSS 分别接 VCC 和 GND
  • XC1 和 XC2 接晶振
  • ANT 接天线
  • 用于MCU接口通信的只有 CSN, SCK 和 DATA 这三个PIN

普冉PY32系列(八) GPIO模拟和硬件SPI方式驱动无线收发芯片XN297LBW

应用电路

普冉PY32系列(八) GPIO模拟和硬件SPI方式驱动无线收发芯片XN297LBW

模块实物

嘉立创打样的测试模块 (项目地址 https://oshwhub.com/iosetting/xn297lbw-xl2400-evb)

普冉PY32系列(八) GPIO模拟和硬件SPI方式驱动无线收发芯片XN297LBW

使用PY32F0驱动XN297LBW

XN297L最新的SDK可以从磐启的论坛下载 论坛›BLE-Lite系列2.4GHz TRX›XN297L›XN297L_SDK. 因为面向的主要是低成本应用, 大多数搭配的MCU为廉价的8位8051, 不一定有硬件SPI, 为了保证兼容在SDK中使用的都是GPIO模拟SPI方式进行驱动. 但是实际上是可以通过硬件SPI方式进行驱动的.

以下分别对GPIO模拟和硬件SPI方式的驱动进行介绍.

硬件准备

  • XN297LBW模块
  • PY32F002A/PY32F003/PY32F030 系列MCU的开发板, 建议在验证阶段使用 20PIN 及以上封装的型号, 避免PIN脚复用引起的干扰. 跑通后再迁移到低PIN型号
  • USB2TTL模块, 用于观察输出
  • 以上硬件需要两套, 测试中分别用于接收和发送

下面以PY32F002A为例. 代码不需调整可以直接运行于 PY32F003x 和 PY32F030x 系列的其它型号.

GPIO模拟方式

接线

注意电源使用3.3V

PY32          XN297LBW SOP8
PA1   ------> CLK/SCK
PA6   ------> CSN/NSS
PA7   ------> DATA/MOSI

              USB2TTL
PA2(TX) ----> RX
PA3(RX) ----> TX

代码说明

SDK代码中使用的MCU是STM8L, 需要迁移到 PY32F002A.

将 xn297l.h 中的 GPIO 设置换为PY32F002A的PIN脚

#define XN297L_DATA_OUT()        LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_7, LL_GPIO_MODE_OUTPUT)
#define XN297L_DATA_IN()         LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_7, LL_GPIO_MODE_INPUT)
#define XN297L_DATA_LOW()        LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_7)
#define XN297L_DATA_HIGH()       LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_7)
#define XN297L_DATA_READ()       LL_GPIO_IsInputPinSet(GPIOA, LL_GPIO_PIN_7)

#define XN297L_SCK_LOW()         LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_1)
#define XN297L_SCK_HIGH()        LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_1)

#define XN297L_CSN_LOW()         LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_6)
#define XN297L_CSN_HIGH()        LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_6)

#define XN297L_CE_LOW()          XN297L_WriteReg(XN297L_CMD_CE_FSPI_OFF, 0)
#define XN297L_CE_HIGH()         XN297L_WriteReg(XN297L_CMD_CE_FSPI_ON, 0)

在 main.c 中增加GPIO初始化

static void APP_GPIOConfig(void)
{
  LL_GPIO_InitTypeDef GPIO_InitStruct;

  /* PA1 CLK */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  /* PA6 CSN */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  /* PA7 DATA */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

使用GPIO模拟SPI的字节写

/**
 * Emulate SPI Write on GPIO pins
 */
void XN297L_WriteByte(uint8_t value)
{
    uint8_t i = 0;
    XN297L_SCK_LOW();
    XN297L_DATA_OUT();
    for (i = 0; i < 8; i++)
    {
        XN297L_SCK_LOW();
        if (value & 0x80)
        {
            XN297L_DATA_HIGH();
        }
        else
        {
            XN297L_DATA_LOW();
        }
        XN297L_SCK_HIGH();
        value = value << 1;
    }
    XN297L_SCK_LOW();
}

模拟字节读. 这里有个细节, 在XN297L_SCK_HIGH();之后加一个__NOP();, 如果没有这个NOP(), PY32F0在低频率(8MHz和24MHz)的时候容易产生读取错误.

/**
 * Emulate SPI Read on GPIO pins
 */
uint8_t XN297L_ReadByte(void)
{
    uint8_t i = 0, RxData = 0;

    XN297L_DATA_IN();
    for (i = 0; i < 8; i++)
    {
        RxData = RxData << 1;
        XN297L_SCK_HIGH();
        __NOP();
        if (XN297L_DATA_READ())
        {
            RxData |= 0x01;
        }
        else
        {
            RxData &= 0xfe;
        }
        XN297L_SCK_LOW();
    }
    return RxData;
}

XN297L 的初始化. 这部分是相对固定的流程, 可以根据自己的需要进行调整, 但是在测试阶段务必保持接收端和发送端的配置一致. 这里在SDK的代码上做了一些修改, 开启了发送的重试和ACK.

// 这部分来自于手册 "XN297L 软件设计和调试参考"
const uint8_t 
    BB_cal_data[]    = {0x12,0xED,0x67,0x9C,0x46},
    RF_cal_data[]    = {0xF6,0x3F,0x5D},
    RF_cal2_data[]   = {0x45,0x21,0xEF,0x2C,0x5A,0x42},
    Dem_cal_data[]   = {0x01},
    Dem_cal2_data[]  = {0x0B,0xDF,0x02};

void XN297L_Init(void)
{
    XN297L_WriteReg(XN297L_CMD_RST_FSPI, 0x5A); // Soft reset
    XN297L_WriteReg(XN297L_CMD_RST_FSPI, 0XA5);

    XN297L_WriteReg(XN297L_CMD_FLUSH_TX, 0);
    XN297L_WriteReg(XN297L_CMD_FLUSH_RX, 0);
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_STATUS, 0x70);       // Clear status flags
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_EN_AA, 0x3F);        // AutoAck on all pipes
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_EN_RXADDR, 0x3F);    // Enable all pipes (P0 ~ P5, bit0 ~ bit5)
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_SETUP_AW, XN297L_SETUP_AW_5BYTE); // Address width
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_RF_CH, 78);          // Channel 78, 2478M HZ
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_RX_PW_P0, XN297L_PLOAD_WIDTH ); // Payload width of P0
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_RX_PW_P1, XN297L_PLOAD_WIDTH ); // Payload width of P1
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_RX_PW_P2, XN297L_PLOAD_WIDTH ); // Payload width of P2
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_RX_PW_P3, XN297L_PLOAD_WIDTH ); // Payload width of P3
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_RX_PW_P4, XN297L_PLOAD_WIDTH ); // Payload width of P4
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_RX_PW_P5, XN297L_PLOAD_WIDTH ); // Payload width of P5

    XN297L_WriteFromBuf(XN297L_CMD_W_REGISTER | XN297L_REG_BB_CAL,    BB_cal_data,  sizeof(BB_cal_data));
    XN297L_WriteFromBuf(XN297L_CMD_W_REGISTER | XN297L_REG_RF_CAL2,   RF_cal2_data, sizeof(RF_cal2_data));
    XN297L_WriteFromBuf(XN297L_CMD_W_REGISTER | XN297L_REG_DEM_CAL,   Dem_cal_data, sizeof(Dem_cal_data));
    XN297L_WriteFromBuf(XN297L_CMD_W_REGISTER | XN297L_REG_RF_CAL,    RF_cal_data,  sizeof(RF_cal_data));
    XN297L_WriteFromBuf(XN297L_CMD_W_REGISTER | XN297L_REG_DEM_CAL2,  Dem_cal2_data,sizeof(Dem_cal2_data));
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_DYNPD, 0x00); // Dynamic payload width: off
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_RF_SETUP,  XN297L_RF_POWER_P_9|XN297L_RF_DR_1M); // 9dbm 1Mbps
    XN297L_WriteReg(XN297L_CMD_ACTIVATE, 0x73);

    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_SETUP_RETR, 0x10|0x05); // Retry interval 500µs, 5 times

    if(XN297L_PLOAD_WIDTH >32)
    {
        XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_FEATURE, XN297L_FEATURE_BIT5_CE_SOFT|XN297L_FEATURE_BIT43_DATA_64BYTE);
    }
    else
    {
        XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_FEATURE, XN297L_FEATURE_BIT5_CE_SOFT);
    }
}

数据发送函数. 因为前面开启了重试和ACK, 这里做了一个等待发送结果的轮询和超时判断

uint8_t XN297L_TxData(uint8_t *ucPayload, uint8_t length)
{
    uint8_t y = 100, status = 0;
    XN297L_CE_HIGH();
    __NOP();
    XN297L_WriteFromBuf(XN297L_CMD_W_TX_PAYLOAD, ucPayload, length);
    // Retry until timeout
    while (y--)
    {
        LL_mDelay(1);
        status = XN297L_ReadStatus();
        // If TX successful or retry timeout, exit
        if ((status & (XN297L_FLAG_MAX_RT | XN297L_FLAG_TX_DS)) != 0)
        {
            //printf(" %d %02x\r\n", y, status);
            break;
        }
    }
    XN297L_WriteReg(XN297L_CMD_FLUSH_TX, 0);
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_STATUS, 0x70);
    XN297L_CE_LOW();
    return status;
}

数据接收. 因为接收使用的是轮询, 所以这里只是简单地判断了接收状态, 在收到数据时读取数据.

uint8_t XN297L_DumpRxData(void)
{
    uint8_t status, rxplWidth;
    status = XN297L_ReadStatus();
    if (status & XN297L_FLAG_RX_DR)
    {
        XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_STATUS, status);
        rxplWidth = XN297L_ReadReg(XN297L_CMD_R_RX_PL_WID);
        XN297L_ReadToBuf(XN297L_CMD_R_RX_PAYLOAD, xbuf, rxplWidth);
    }
    return status;
}

完整代码

XN297L 示例代码的 GitHub 仓库地址: https://github.com/IOsetting/py32f0-template/tree/main/Examples/PY32F0xx/LL/GPIO/XN297LBW_Wireless

运行测试

修改 main.c 中的模式设置, 0为接收, 1为发送, 分别写入至两个PY32F002A开发板, 观察UART的输出.

// 0:RX, 1:TX
#define XN297L_MODE 0

接收端在每次接收到数据时, 输出第1,2,31个字节的值; 发送端每发送255组数据(每组32字节)后, 会显示发送成功的个数(十六进制), 这个输出可以用于计算发送成功率, 以及发送速度.

硬件SPI方式

接线

接线方式使用4线制全双工, PY32的MOSI和MISO都接到XN297LBW的DATA, 但是在MOSI(PA7)上串一个1K的电阻. 对于使用SPI协议的三线连接, 如果半双工SPI有问题, 都可以用这种接线试试全双工方式通信. 从实际测试看, XN297LBW 支持这种接线方式.

PY32                XN297LBW SOP8
PA0   ------------> DATA/MOSI
PA7   ---> 1KR ---> DATA/MOSI
PA1   ------------> CLK/SCK
PA6   ------------> CSN/NSS

                    USB2TTL
PA2(TX) ----------> RX
PA3(RX) ----------> TX

代码说明

SPI接口的初始化. 注意SPI的时钟频率不要超过4MHz

/**
 * SPI1 Alternative Function Pins
 * SPI1_SCK:  PA1_AF0, PA2_AF10, PA5_AF0, PA9_AF10, PB3_AF0
 * SPI1_MISO: PA0_AF10, PA6_AF0, PA7_AF10, PA11_AF0, PA13_AF10, PB4_AF0
 * SPI1_MOSI: PA1_AF10, PA2_AF0, PA3_AF10, PA7_AF0, PA8_AF10, PA12_AF0, PB5_AF0
 * SPI1_NSS:  PA4_AF0, PA10_AF10, PA15_AF0, PB0_AF0, PF1_AF10, PF3_AF10
*/
static void APP_SPI_Config(void)
{
  LL_SPI_InitTypeDef SPI_InitStruct = {0};
  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_SPI1);

  // PA1 SCK
  GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  // PA0 MISO
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_10;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  // PA7 MOSI
  GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  /*
   * Full duplex mode, MOSI and MISO both connect to DATA,
   * Add one 1KR between MOSI and DATA
  */
  SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
  SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
  SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
  SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
  SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
  SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
  // SPI的时钟频率不要超过4MHz
  SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16;
  SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
  LL_SPI_Init(SPI1, &SPI_InitStruct);
  LL_SPI_Enable(SPI1);
}

硬件SPI方式的字节读写

uint8_t SPI_TxRxByte(uint8_t data)
{
  uint8_t SPITimeout = 0xFF;
  /* Check the status of Transmit buffer Empty flag */
  while (READ_BIT(SPI1->SR, SPI_SR_TXE) == RESET)
  {
    if (SPITimeout-- == 0)
      return 0;
  }
  LL_SPI_TransmitData8(SPI1, data);
  SPITimeout = 0xFF;
  while (READ_BIT(SPI1->SR, SPI_SR_RXNE) == RESET)
  {
    if (SPITimeout-- == 0)
      return 0;
  }
  // Read from RX buffer
  return LL_SPI_ReceiveData8(SPI1);
}

对应XN297L的命令读写改造为调用硬件SPI读写函数

uint8_t XN297L_WriteReg(uint8_t reg, uint8_t value)
{
    uint8_t reg_val;
    XN297L_CSN_LOW();
    SPI_TxRxByte(reg);
    reg_val = SPI_TxRxByte(value);
    XN297L_CSN_HIGH();
    return reg_val;
}

uint8_t XN297L_ReadReg(uint8_t reg)
{
    uint8_t reg_val;
    XN297L_CSN_LOW();
    SPI_TxRxByte(reg);
    reg_val = SPI_TxRxByte(XN297L_CMD_NOP);
    XN297L_CSN_HIGH();
    return reg_val;
}

完整代码

XN297L 示例代码的 GitHub 仓库地址: https://github.com/IOsetting/py32f0-template/tree/main/Examples/PY32F0xx/LL/SPI/XN297L_Wireless

运行测试

和GPIO模拟方式的一样, 修改 main.c 中的模式设置, 0为接收, 1为发送, 分别写入至两个PY32F002A开发板, 观察UART的输出.

// 0:RX, 1:TX
#define XN297L_MODE 0

利用FIFO队列提升发送速度

在 NRF24L01 的使用中, 可以通过 "直接写入TX FIFO -> 通过 FLAG 观察 TX FIFO 是否写满判断是继续写入还是阻塞等待" 的方式提升发送速度. XN297L 的 TX FIFO 队列包含两组 32 个字节, 也可以通过这种方式进行加速.

相关的函数

ErrorStatus XN297L_TxFast(const uint8_t *ucPayload, uint8_t length)
{
    //Blocking only if FIFO is full. This will loop and block until TX is successful or fail
    while ((XN297L_ReadStatus() & XN297L_FLAG_TX_FULL)) {
        if (xn297l_state & XN297L_FLAG_MAX_RT) {
            return ERROR;
        }
    }
    XN297L_WriteFromBuf(XN297L_CMD_W_TX_PAYLOAD, ucPayload, length);
    XN297L_CE_HIGH();
    return SUCCESS;
}

// 用于 MAX_RT 状态清除标志位
void XN297L_ReuseTX(void)
{
    XN297L_WriteReg(XN297L_CMD_W_REGISTER | XN297L_REG_STATUS, XN297L_FLAG_MAX_RT); //Clear max retry flag
    XN297L_CE_LOW();
    XN297L_CE_HIGH();
}

使用方式: 在发送循环中调用 XN297L_TxFast() 进行发送, 在遇到错误时, 用 XN297L_ReuseTX() 重置状态

if (XN297L_TxFast(tmp, XN297L_PLOAD_WIDTH) == SUCCESS)
{
  j++;
}
else
{
  XN297L_ReuseTX();
}

从实际测试结果看, 用 XN297L_TxFast() 发送相比普通发送方式有10%的性能提升.文章来源地址https://www.toymoban.com/news/detail-710704.html

到了这里,关于普冉PY32系列(八) GPIO模拟和硬件SPI方式驱动无线收发芯片XN297LBW的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 普冉PY32系列(三) PY32F002A 资源实测 - 这个型号不简单

    普冉PY32系列(一) PY32F0系列32位Cortex M0+ MCU简介 普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境 普冉PY32系列(三) PY32F002A资源实测 - 这个型号不简单 普冉PY32系列(四) PY32F002A/003/030的时钟设置 普冉PY32系列(五) 使用JLink RTT代替串口输出日志 普冉PY32系列(六) 通过I2C接口驱动PCF8574扩

    2023年04月19日
    浏览(38)
  • 普冉PY32系列(十四) 从XL2400迁移到XL2400P

    普冉PY32系列(一) PY32F0系列32位Cortex M0+ MCU简介 普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境 普冉PY32系列(三) PY32F002A资源实测 - 这个型号不简单 普冉PY32系列(四) PY32F002A/003/030的时钟设置 普冉PY32系列(五) 使用JLink RTT代替串口输出日志 普冉PY32系列(六) 通过I2C接口驱动PCF8574扩

    2024年02月04日
    浏览(38)
  • 普冉PY32系列(十) 基于PY32F002A的6+1通道遥控小车I - 综述篇

    普冉PY32系列(一) PY32F0系列32位Cortex M0+ MCU简介 普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境 普冉PY32系列(三) PY32F002A资源实测 - 这个型号不简单 普冉PY32系列(四) PY32F002A/003/030的时钟设置 普冉PY32系列(五) 使用JLink RTT代替串口输出日志 普冉PY32系列(六) 通过I2C接口驱动PCF8574扩

    2024年02月05日
    浏览(40)
  • 普冉PY32系列(十一) 基于PY32F002A的6+1通道遥控小车II - 控制篇

    普冉PY32系列(一) PY32F0系列32位Cortex M0+ MCU简介 普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境 普冉PY32系列(三) PY32F002A资源实测 - 这个型号不简单 普冉PY32系列(四) PY32F002A/003/030的时钟设置 普冉PY32系列(五) 使用JLink RTT代替串口输出日志 普冉PY32系列(六) 通过I2C接口驱动PCF8574扩

    2024年02月05日
    浏览(41)
  • stm32 GPIO模拟SPI接口实现双机通信

    一、SPI协议简介     一般主从方式工作,这种模式通常有一个主设备和一个或多个从设备,通常采用的是4根线,它们是MISO(主机输入从机输出)、MOSI(主机输出,针对主机来说)、SCLK(时钟,主机产生)、CS(片选,一般由主机发送或者直接使能,通常为低电平有效) ●

    2023年04月08日
    浏览(48)
  • STM32的硬件SPI驱动AD7124的方法

    AD7124是一款适合高精度测量应用的低功耗、低噪声、完整模拟前端。该器件内置一个低噪声24位Σ-Δ型模数转换器(ADC),可配置来提供8个差分输入或15个单端或伪差分输入。片内低噪声级确保ADC中可直接输入小信号。可用于温度测量、压力测量、工业过程控制、仪器仪表和只能

    2024年02月07日
    浏览(78)
  • STM32F103硬件SPI驱动ADS1256

    最近实验室有几个项目都需要用到高分辨率AD转换,于是就开始了ADS1256的开发。 新手,焊得丑,见谅(能用就行) 二: 本以为很容易就能做完,结果被采样速率的问题困扰了很久。 代码如下,使用2.5V基准源进行测试,结果在读ADS时经常出现读出0xFFFFFF的情况,只能忍住悲伤

    2024年02月12日
    浏览(76)
  • stm32 hal库硬件spi(软件spi)驱动1.8寸tft—lcd屏幕

    屏幕是嵌入式开发中的一个重要的部分,cdsn上有许多解释原理的,还有很多是采用正点原子的屏幕来驱动的,对于刚刚入门不久的我们可能没有资金去购买较为昂贵的屏幕。而对于底层原理我们暂时也不必了解的那么深入,能点亮屏幕就是我们最大的快乐。 除了中景园的资

    2024年02月03日
    浏览(53)
  • STM32配合CubeMX硬件SPI驱动0.96寸OLED

    目录 一、简单介绍 1.1 OLED 1.2 SPI协议 接口 优点 缺点 数据传输 二、实战 2.1 工程配置 2.2 测试工程 2.3 波形分析 三、驱动OLED 3.1 初始化代码 3.2 清屏函数 3.3 设置坐标函数 3.4 显示字符函数 3.5 显示字符串函数 3.6 显示图片函数 附录 驱动代码文件 oled.c oled.h f6x8.h 1.1 OLED 有机发

    2024年02月02日
    浏览(39)
  • 基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动RC522

    目录 1. 实验硬件及原理图 1.1 RFID硬件 1.2 硬件原理图 2. 单片机与RFID硬件模块分析 3. 利用STM32CubeMX创建MDK工程 3.1 STM32CubeMX工程创建 3.2 配置调试方式 3.3 配置时钟电路 3.4 配置时钟 3.5 配置GPIO 3.6 配置SPI 3.7 配置串口 3.8 项目配置 4. MDK工程驱动代码调试 4.1 按键、LED程序 4.1.1 Us

    2024年02月09日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包