适用于升特的SX1281、SX1280射频芯片以及相关射频模块

dropLin 983e400181 修改keil的默认配置选项 3 ヶ月 前
STM32F10x_FWLib 8ea20d1579 first commit 5 ヶ月 前
app 8ea20d1579 first commit 5 ヶ月 前
core 8ea20d1579 first commit 5 ヶ月 前
keil_v5 983e400181 修改keil的默认配置选项 3 ヶ月 前
peripheral 8ea20d1579 first commit 5 ヶ月 前
project 79c0529100 V11,优化固件默认参数 3 ヶ月 前
radio 214c1ca4a8 更新readme 5 ヶ月 前
.gitignore 8ea20d1579 first commit 5 ヶ月 前
README.md 79c0529100 V11,优化固件默认参数 3 ヶ月 前
keilkilll.bat 8ea20d1579 first commit 5 ヶ月 前

README.md

[toc]

软件开发移植,先看这里 跳转

基本说明

  • 编程语言:C99
  • 开发环境:MDK-ARM Standard Version: 5.14.0.0
  • 芯片型号:AT32F413RCT7 或 STM32F103RCT6,这两个型号软硬件兼容
  • 软件LIB版本:STM32F10x_StdPeriph_Lib_V3.5.0
  • 射频驱动官方库版本:
  • 烧录仿真接口:SWD
  • 烧录仿真器:J-Link 或 DAP
  • 文档编写日期:2024年5月14日

已适配的无线模块

  • VG239xS240N0M1系列
  • VG239xS240X0M1系列

IO口定义

  • VG239xS240N0M1系列、VG239xS240X0M1系列

    序号 模块IO MCU IO 转接板排针 模块功能 必需 备注
    14 ANT
    13 GND G
    5 NSS PA4 NSS NSS 从机SPI片选脚
    4 SCK PA5 SCK SCK 主机SPI时钟输出
    6 VCC VCC
    3 MOSI PA7 DO MOSI 主机SPI数据发送,从机SPI数据接收
    2 MISO PA6 DI MISO 主机SPI数据接收,从机SPI数据发送,3线SPI可以不接
    1 RST PC5 RST
    11 BUSY PA2 BUSY
    10 DIO1 PB0 IO0 收发状态中断信号输出 输出高电平,外部可做上升沿触发检测
    8 DIO2 PB1 IO2 N
    8 DIO3 PB1 IO3 N
    4 TXEN\NC PB11 TE 内部PA使能 P 高电平使能,VG239xS240N0M1系列为NC
    3 RXEN\NC PB10 RE 内部LNA使能 P 高电平使能,VG239xS240N0M1系列为NC
    7 GND G
    9 NC G X

说明:

  • :表示必须连接
  • N:表示可以根据实际需要选择是否连接
  • P:表示根据SPI模式选择是否连接
  • X:表示不需要连接

SPI驱动接口

  • 固件已适配3线和4线SPI接口
  • 支持SPI时钟频率范围:最大10MHz
  • 支持SPI时钟极性:低电平,空闲状态下SCK为低电平
  • 支持SPI时钟相位:第一边沿

工程文件架构

..\adapterBoardDriver_xxxxxxxxxxxxxxx_Vxx
├──app \\常用应用模块封装
|  └──
├──core    \\MCU内核文件
|  └──
├──STM32F10x_FWLib \\MCU官方库函数
   └──
├──image   \\md文件显示用的图片
|  └──
├──keil_v5 \\keil编译器工程文件,包含编译生成的HEX文件
|  └──Object  \\编译生成的HEX文件在此文件夹
├──peripheral  \\项目用到的MCU外设
|  └──
├──project \\项目的主函数和GPIO定义包含文件
|  └──
├──radio   \\射频底层驱动文件
|  ├──myRadio_gpio.c  \\射频驱动接口硬件初始化
|  └──myRadio.c   \\为无线应用通用封装API
|  └──...

无线通讯开发注意事项

  • 电源
    1. 一定要根据规格书规定的供电电压选择合适电压,同时还要关注射频发送时的电流问题,防止有些电源的电流驱动能力不足导致压降,电压过低会影响射频信号的辐射,最终影响通讯距离,也有可能导致模块无法正常工作。
  • 无线频率
    1. 避免使用中心频率为射频芯片使用的晶体频率整数倍的,比如晶体频率为32MHz,就需要避免使用448MHz的中心频点
  • SPI驱动调试
    1. 首先保证SPI通讯正常,具体SPI时序需根据射频芯片要求设置,可通过示波器或者逻辑分析仪进行硬件分析
    2. SPI通讯正常后,进一步调试查看寄存器操作,读写寄存器,若能正常操作,基本可判定移植成功了
    3. 直观判断可以看寄存器操作radio\sx1280-hal.c\void SX1280HalWaitOnBusy( void )这个函数的busy脚的状态,如果无法往下执行,则表示spi操作失败,需要检查硬件连接或者spi的时序是否正确。
  • 通讯距离 影响无线传输距离的因素
    1. 无线电频谱,包括使用的无线频段和无线波特率,原则上无线波特率越低,无线信号传播距离越远,但是时间延迟也越大,由于模块的射频芯片使用的是扩频调制技术,其无线波特率主要有扩频因子、带宽、容错率三个参数控制,根据这个三个参数不同组合可以得到不同的无线波特率,可以通过计算器LoRa Calculator.exe计算得到最终的波特率
    2. 发射功率,原则上发射功率越大,无线信号传播距离越远
    3. 天线增益,不同增益的天线对无线信号的接收效果影响很大
    4. 路径损耗,主要是包括无线使用的周围环境,比如楼宇、树木山峰遮挡
    5. 其他的无线信号干扰

硬件篇

  1. 无线模组转接板插槽,带金手指,可以适配不同的模块转接板
  2. 普通 LED 指示灯
  3. 显示屏,显示工作状态以及工作使用参数
  4. MCU 预留 I/O 口
  5. 主板 MCU 复位按键
  6. SWD 主板 MCU 程序下载调与调试接口
  7. 供电电源 3 档开关,可以用于切换选择测试板的供电电源,切换到 BT 档, 测试板为底部的 3 节 5 号电池供电,切换到 USB 档,测试板为⑧的 Micro-B 座子 供电,打在中间档关闭供电
  8. Micro-B USB 座子,可以用于测试板的供电;连接电脑可以做 TTL 转 USB 功 能,测试板内含 CP2102N 芯片

    编译软件工具KEIL篇

    编译参数选择

keil工程已经设置了4个选项:

  • projecet_AT:表示该工程的MCU型号选择的是雅特力AT32F413RCT7单片机,不带自定义boot功能,即不设置偏移地址
  • projecet_ST:表示该工程的MCU型号选择的是ST意法半导体STM32F103RCT6单片机,不带自定义boot功能,即不设置偏移地址
  • projecet_AT_1W:表示该工程的MCU型号选择的是雅特力AT32F413RCT7单片机,不带自定义boot功能,即不设置偏移地址,适配1W大功率模块
  • projecet_ST_1W:表示该工程的MCU型号选择的是ST意法半导体STM32F103RCT6单片机,不带自定义boot功能,即不设置偏移地址,适配1W大功率模块
  • projecet_AT_APP:表示该工程的MCU型号选择的是雅特力AT32F413RCT7单片机,带自定义boot功能,设置偏移地址为0x000C800
  • projecet_ST_APP:表示该工程的MCU型号选择的是ST意法半导体STM32F103RCT6单片机,带自定义boot功能,设置偏移地址为0x000C800
  • projecet_AT_1W_APP:表示该工程的MCU型号选择的是雅特力AT32F413RCT7单片机,带自定义boot功能,设置偏移地址为0x000C800,适配1W大功率模块
  • projecet_ST_1W_APP:表示该工程的MCU型号选择的是ST意法半导体STM32F103RCT6单片机,带自定义boot功能,设置偏移地址为0x000C800,适配1W大功率模块

模块演示板出厂默认烧录projecet_ST_APP工程,该工程是带boot功能的,默认选择的Debug工具是CMSIS-DAP Debugger工具,在Option->Debug->Setting->Flash Download->Programming Algorithm->Start中设置了起始地址,如果更换了Debug工具,起始地址会恢复默认,需要重新设置起始地址。

更新固件方式

  1. 使用Debug工具通过keil的Download下载,需要注意编译参数选择。
  2. 使用串口下载,通过USB数据线连接电脑,通过vollgoKit-update.exe工具升级,升级固件文件:keil_v5\Listings\*.bin

软件开发篇

软件功能框图

软件开发主要就是涉及射频模块驱动,与硬件相关的主要就是SPI接口和一些辅助IO口(比如中断信号脚),这部分软件主要放在./radio/myRadio_gpio.c中。 中断信号是通过回调函数RADIO_GPIO_CALLBACK的方式从./radio/myRadio_gpio.c回调到./radio/myRadio.c中处理,回调函数的方式只是为了方便软件编写,用户可以直接把外部中断函数直接放在./radio/myRadio.c中处理。 所有的中断入口函数放在.\project\stm32f10x_it.c,在需要的地方通过回调注册的方式回调中断状态,比如注册回调PB0的中断函数回调:

void rfIrq_callback(uint8_t status, uint32_t param)
{

}
myIrqCallback_rfIrq.thisCb = rfIrq_callback;
EXTILINE0_callbackRegiste(&myIrqCallback_rfIrq); 

也可以把void EXTI0_IRQHandler(void)中断入口函数直接拿来用,注册回调的方式只是为了方便调用。

注意事项

  • 4线spi接口的硬件和软件模拟方式切换 通过宏定义SPI_HARD来选择,具体见./radio/myRadio_gpio.c/void myRadio_gpio_init(RADIO_GPIO_CALLBACK cb)
  • 大功率(VGdd79SxxxX0Mx系列、VG237xSxxxX0Mx系列)小功率(VGdd79SxxxN0S1系列、VG237xSxxxN0Sx系列)模块操作区别 本项目工程对这两种模块做了兼容,在移植时软件和硬件操作都需要注意区别,
    • 引脚控制:大功率模块需增加两个控制脚,RF_EXT_PA_TERF_EXT_PA_RE,如果使用的是小功率模块,这两个引脚可以不用控制
    • 软件:大功率模块的最大发射功率为0dbm,小功率的没有限制。大功率模块初始化完成后需要调用myRadio_setTxPower(power);设置,再去发送数据。

1、进入低功耗

调用./radio/myRadio.c/myRadio_abort()函数,将射频模块进入低功耗模式,进入低功耗模式后,射频模块将不接收任何无线信号,只有当射频模块进入接收状态后,才能再次接收无线信号。

void myRadio_abort(void)
{
    if (rf_handle == 0)
    {
        return;
    }
    RF_EXT_PA_TO_IDLE();

    SleepParams_t SleepParams;
    SleepParams.DataBufferRetention = 1;
    SleepParams.DataRamRetention = 1;
    SleepParams.InstructionRamRetention = 1;
    SleepParams.WakeUpRTC = 0;
    // Radio.SetSleep( SleepParams );
    Radio.SetStandby( STDBY_RC );
}

2、射频模块进入接收状态

调用./radio/myRadio.c/myRadio_receiver()函数,将射频模块进入接收状态,进入接收状态后,射频模块将接收周围环境中的无线信号,并可以接收数据包。在接收状态或者发送过程不能重新调用该函数,发送需要等待发送完成才能再次调用该函数,不然会打断无线发送。接收到无线数据后,射频模块会通过DIO1引脚产生输出个信号,然后在./radio/myRadio.c/myRadio_process(void)函数中读取fifo中的数据包。

void myRadio_receiver(void)
{ 
    if (rf_handle == 0)
    {
        return;
    }
    RF_EXT_PA_TO_RX();
    TickTime_t TickTime ={ RX_TIMEOUT_TICK_SIZE, RX_TIMEOUT_VALUE };
    Radio.SetDioIrqParams( RxIrqMask, RxIrqMask, IRQ_RADIO_NONE, IRQ_RADIO_NONE );
    Radio.SetRx( TickTime );
}

3、射频模块发送数据包

调用./radio/myRadio.c/myRadio_transmit(rfTxPacket_ts *packet)函数,将射频模块进入发送状态,进入发送状态后,射频模块将发送数据包,发送完成后才可以调用接收函数进入接收状态,发送完成后,射频模块会通过IRQ引脚产生输出个信号,然后在./radio/myRadio.c/myRadio_process(void)函数中读取发送完成状态。

void myRadio_transmit(rfTxPacket_ts *packet)
{
    if (rf_handle == 0)
    {
        return;
    }
    RF_EXT_PA_TO_TX();
    if (Radio.GetOpMode() == MODE_SLEEP)
    {        
        myRadio_delay(1);
    }
	TickTime_t TickTime ={ RX_TIMEOUT_TICK_SIZE, TX_TIMEOUT_VALUE };
    Radio.SetDioIrqParams( TxIrqMask, TxIrqMask, IRQ_RADIO_NONE, IRQ_RADIO_NONE );
    packet->len = 64;
    if(rfModuleMode == RF_MM_MODE_GFSK)
    {
        packetParams.Params.Gfsk.PayloadLength = packet->len;
    }
    else if (rfModuleMode == RF_MM_MODE_LORA)
    {        
        packetParams.Params.LoRa.PayloadLength = packet->len;
    }
    else if (rfModuleMode == RF_MM_MODE_FLRC)
    {           
        packetParams.Params.Flrc.PayloadLength = packet->len;
    }
    else
    {
        packetParams.Params.Flrc.PayloadLength = packet->len;
    }
    Radio.SetPacketParams( &packetParams );
    Radio.SendPayload( packet->payload, packet->len, TickTime);
}

4、射频初始化

调用./radio/myRadio.c/myRadio_init(int agr0, void *agr1_ptr)函数,将射频模块初始化,初始化完成后,射频模块将进入接收状态,该函数会初始化一个默认的参数,如果需要自定义参数,比如频率信道,发射功率,无线波特率等参数,可以调用相关函数接口重新设置,如果不需要自定义参数,也可以通过修改rf_set_default_para();中的默认参数:

  • ./radio/myRadio.c/myRadio_setFrequency(uint32_t freq):设置射频模块工作频率
  • ./radio/myRadio.c/myRadio_setTxPower(uint8_t power):设置射频模块发射功率
  • ./radio/myRadio.c/void myRadio_setBaudrate(uint32_t br):设置射频模块无线波特率 初始化完成后就可以进入接收状态或者发送无线数据包了,具体使用方法可以参考示例代码。

    void myRadio_init(int agr0, void *agr1_ptr)
    {
    myRadio_gpio_init(myRadio_gpioCallback);
        
    /**-------------------------radio init----------------------------------**/
    uint16_t ver = 0;
    myRadio_delay(1000);
    Radio.Init( &Callbacks );
    Radio.SetRegulatorMode( USE_DCDC ); // Can also be set in LDO mode but consume more power
    {
            
       ver = Radio.GetFirmwareVersion();//获取芯片信息 0xA9B5 可用于测试SPI的读写情况
    }
    if (rfModuleMode == RF_MM_MODE_BLE)
    {
        modulationParams.PacketType = PACKET_TYPE_BLE;
        modulationParams.Params.Ble.BitrateBandwidth = GFS_BLE_BR_1_000_BW_1_2;
        modulationParams.Params.Ble.ModulationIndex = GFS_BLE_MOD_IND_0_50;
        modulationParams.Params.Ble.ModulationShaping = RADIO_MOD_SHAPING_BT_0_5;
            
        packetParams.PacketType = PACKET_TYPE_BLE;
        packetParams.Params.Ble.BlePacketType = BLE_EYELONG_1_0;
        packetParams.Params.Ble.ConnectionState = BLE_ADVERTISER;
        packetParams.Params.Ble.CrcField = BLE_CRC_3B;
        packetParams.Params.Ble.Whitening = RADIO_WHITENING_ON;
    }
    else if(rfModuleMode == RF_MM_MODE_LORA)
    {        
        modulationParams.PacketType = PACKET_TYPE_LORA;
        modulationParams.Params.LoRa.SpreadingFactor = LORA_SF5;//
        modulationParams.Params.LoRa.Bandwidth = LORA_BW_0400;
        modulationParams.Params.LoRa.CodingRate = LORA_CR_LI_4_5;
            
        packetParams.PacketType = PACKET_TYPE_LORA;
        packetParams.Params.LoRa.PreambleLength = 12;
        packetParams.Params.LoRa.HeaderType = LORA_PACKET_VARIABLE_LENGTH;
        packetParams.Params.LoRa.PayloadLength = BUFFER_SIZE;
        packetParams.Params.LoRa.CrcMode = LORA_CRC_ON;
        packetParams.Params.LoRa.InvertIQ = LORA_IQ_NORMAL;
    }
    else if(rfModuleMode == RF_MM_MODE_GFSK)
    {
        modulationParams.PacketType = PACKET_TYPE_GFSK;
        modulationParams.Params.Gfsk.BitrateBandwidth = GFS_BLE_BR_0_125_BW_0_3;
        modulationParams.Params.Gfsk.ModulationIndex = GFS_BLE_MOD_IND_1_00;
        modulationParams.Params.Gfsk.ModulationShaping = RADIO_MOD_SHAPING_BT_1_0;
    
        packetParams.PacketType = PACKET_TYPE_GFSK;
        packetParams.Params.Gfsk.PreambleLength = PREAMBLE_LENGTH_24_BITS;
        packetParams.Params.Gfsk.SyncWordLength = GFS_SYNCWORD_LENGTH_5_BYTE;
        packetParams.Params.Gfsk.SyncWordMatch = RADIO_RX_MATCH_SYNCWORD_1_2_3;
        packetParams.Params.Gfsk.HeaderType = RADIO_PACKET_FIXED_LENGTH;
        packetParams.Params.Gfsk.PayloadLength = BUFFER_SIZE;
        packetParams.Params.Gfsk.CrcLength = RADIO_CRC_OFF;
        packetParams.Params.Gfsk.Whitening = RADIO_WHITENING_OFF;
    
        // packetParams.PacketType = PACKET_TYPE_GFSK;
        // packetParams.Params.Gfsk.PreambleLength = PREAMBLE_LENGTH_32_BITS;
        // packetParams.Params.Gfsk.SyncWordLength = GFS_SYNCWORD_LENGTH_5_BYTE;
        // packetParams.Params.Gfsk.SyncWordMatch = RADIO_RX_MATCH_SYNCWORD_1;
        // packetParams.Params.Gfsk.HeaderType = RADIO_PACKET_VARIABLE_LENGTH;
        // packetParams.Params.Gfsk.PayloadLength = BUFFER_SIZE;
        // packetParams.Params.Gfsk.CrcLength = RADIO_CRC_3_BYTES;
        // packetParams.Params.Gfsk.Whitening = RADIO_WHITENING_ON;
            
            
    }
    else if(rfModuleMode == RF_MM_MODE_FLRC)
    {
        modulationParams.PacketType = PACKET_TYPE_FLRC;
        modulationParams.Params.Flrc.BitrateBandwidth = FLRC_BR_0_325_BW_0_3;
        modulationParams.Params.Flrc.CodingRate = FLRC_CR_1_2;
        modulationParams.Params.Flrc.ModulationShaping = RADIO_MOD_SHAPING_BT_1_0;
    
        packetParams.PacketType = PACKET_TYPE_FLRC;
        packetParams.Params.Flrc.PreambleLength = PREAMBLE_LENGTH_32_BITS;
        packetParams.Params.Flrc.SyncWordLength = FLRC_SYNCWORD_LENGTH_4_BYTE;
        packetParams.Params.Flrc.SyncWordMatch = RADIO_RX_MATCH_SYNCWORD_1;
        packetParams.Params.Flrc.HeaderType = RADIO_PACKET_VARIABLE_LENGTH;
        packetParams.Params.Flrc.PayloadLength = BUFFER_SIZE;
        packetParams.Params.Flrc.CrcLength = RADIO_CRC_3_BYTES;
        packetParams.Params.Flrc.Whitening = RADIO_WHITENING_OFF;
    }
    else
    {
        modulationParams.PacketType = PACKET_TYPE_FLRC;
        modulationParams.Params.Flrc.BitrateBandwidth = FLRC_BR_0_325_BW_0_3;
        modulationParams.Params.Flrc.CodingRate = FLRC_CR_1_2;
        modulationParams.Params.Flrc.ModulationShaping = RADIO_MOD_SHAPING_BT_1_0;
    
        packetParams.PacketType = PACKET_TYPE_FLRC;
        packetParams.Params.Flrc.PreambleLength = PREAMBLE_LENGTH_32_BITS;
        packetParams.Params.Flrc.SyncWordLength = FLRC_SYNCWORD_LENGTH_4_BYTE;
        packetParams.Params.Flrc.SyncWordMatch = RADIO_RX_MATCH_SYNCWORD_1;
        packetParams.Params.Flrc.HeaderType = RADIO_PACKET_VARIABLE_LENGTH;
        packetParams.Params.Flrc.PayloadLength = BUFFER_SIZE;
        packetParams.Params.Flrc.CrcLength = RADIO_CRC_3_BYTES;
        packetParams.Params.Flrc.Whitening = RADIO_WHITENING_OFF;
            
    }
        
        
        
        
    //     #elif defined( MODE_GFSK )
        
    // #elif defined( MODE_LORA )
        
        
    // #elif defined( MODE_FLRC )
        
    
    // #else
    // #error "Please select the mode of operation for the Ping Ping demo"
    // #endif
        
    Radio.SetStandby( STDBY_RC );
    Radio.SetPacketType( modulationParams.PacketType );
    Radio.SetModulationParams( &modulationParams );
    Radio.SetPacketParams( &packetParams );
    Radio.SetRfFrequency( RF_FREQUENCY );//频点设置
    Radio.SetBufferBaseAddresses( 0x00, 0x00 );
    Radio.SetTxParams( TX_OUTPUT_POWER, RADIO_RAMP_02_US );//功率设置
        
    //    Radio.SetInterruptMode();
    Radio.SetPollingMode();
        
    if(rfModuleMode == RF_MM_MODE_GFSK)
    {
    //接收灵敏度测试
        Radio.SetSyncWord( 1, ( uint8_t[] ){ 0xDD, 0xA0, 0x96, 0x69, 0xDD } );
    }
    if(rfModuleMode == RF_MM_MODE_BLE)
    {
        // only used in GENERIC and BLE mode
        Radio.WriteRegister(0x9c7, 0x55 );
        Radio.WriteRegister(0x9c8, 0x55 );
        Radio.WriteRegister(0x9c9, 0x55 );
        //Radio.WriteRegister( 0x9c5, 0x33 );
        Radio.SetBleAdvertizerAccessAddress( );
        Radio.SetWhiteningSeed( 0x33 );
        ble_header_adv.Fields.length = PINGPONGSIZE + 2;
        ble_header_adv.Fields.pduType = 2;
    }
    AppState = APP_LOWPOWER;
    /**-------------------------radio init end----------------------------------**/
    RF_EXT_PA_TO_IDLE();
    if ((rfRxCallBack )agr1_ptr)
    {
        rxCb = (rfRxCallBack )agr1_ptr;
    }
    rf_handle = 0xe5;
    }
    

5、射频底层执行

调用./radio/myRadio.c/myRadio_process(void)函数,该函数需要放在主函数中不断判断检测是否有中断触发(可以放在while循环中执行),然后根据中断标志来解析处理。状态处理可以直接在相应的位置处理,或者通过回调函数rxCb将结果返回上一层处理

void myRadio_process(void)
{
    if (rf_handle == 0)
    {
        return;
    }
    SX1280ProcessIrqs(); 
}

在初始化的时候已经通过回调注册了几个回调函数

RadioEvents.TxDone = OnTxDone;
RadioEvents.RxDone = OnRxDone;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxTimeout = OnRxTimeout;
RadioEvents.RxError = OnRxError; 

当有相关状态触发时,会通过回调函数将状态回调到./radio/myRadio.c中对应的函数中执行。

6、无线波特率设置

可以通过void myRadio_setBaudrate(uint32_t br)来设置。

  • void myRadio_setBaudrate(uint32_t br):通过数组选择定义好的几组扩频因子、带宽、码率的组合即可

版本更新

  • V11 2024年6月24日
    • 优化固件默认参数

免责说明

1、本工程驱动软件只提供做演示项目使用,未经过大批量项目验证,客户需谨慎使用,如果使用在正式项目中引发的所有问题,本司概不负责。使用过程若发现任何问题,可及时与本司相关人员联系。 2、本工程所有文件可以用于商业性项目移植,无需向本司申请。