Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RS485
SPI
瑞萨_RA6M4
基于RT-Thread+RA6M4的智能网关设计
发布于 2022-07-30 22:49:13 浏览:581
订阅该版
1.引言: 生活中现在越来越多的产品智能化,而总线的接口繁多,每个设备都会有一个控制器,为了方便管理,设计了一款LoRa+RS485接口的智能网关,可以通过定制将RS485接口和LoRa接口的模块通过一个网关控制。同时可以设计各种协议转LoRa协议,从而实现一个网关管理所有设备。 2.总体方案 本系统采用瑞萨电子的RA6M4 32位ARM微控制器作为系统主控,该芯片采用40nm工艺,高达200MHz的主频,包括丰富的外设,如USB、串口、SPI、SDHI、CAN2.0、RMMI和I2C等,支持密钥管理和篡改检测、具有奇偶校验和ECC的256KB的SRAM,512KB~1MB的Flash。非常适用于对安全高的通信设备中。并围绕该芯片设计了RS485、LoRa等通信接口,采用国内知名的嵌入式操作系统RT-Thread,完美的实现对RS485和LoRa通讯数据的管理、指令下发。 ![系统框图.png](https://oss-club.rt-thread.org/uploads/20220730/bd358904594c38e2c5f92e0709ae822f.png "系统框图.png") 采用该框架,后续增加CAN和网络管理,打通远程通讯,实现远程控制和数据信息的上传,实现远端交互(由于时间有限,这块暂未实现)。 3、硬件设计 硬件采用SX1278 LoRa模块作为LoRa的收发器,并设计了485电路接口和OLED显示屏接口。 ![外围电路.png](https://oss-club.rt-thread.org/uploads/20220730/33b51ed10c904696a75eefcf2a1539d8.png.webp "外围电路.png") 4、主控器 控制器选用RA6M4单片机,RA6M4配合RT-Thread Studio 编程而不用51单片机或者STM32单片机,主频高达200MHz, 采用Arm Cortex?-M33 内核,IO口全部引出,采用MOS管输出稳定且可承载一定功率,8路UART串口,10路PWM,还设计了Arduino UNO板载接口,可通过WiFi接入网络来智能控制执行器,让使用者更好地进行人机交互,使用起来更加方便。RA6M4性能强而且功率低,对比相同性能的主控板,RA6M4的性价比更高。 ![主控器.png](https://oss-club.rt-thread.org/uploads/20220730/7c2bdb3790465aba092cb7655a18bddc.png.webp "主控器.png") 5、LoRa模块 该模块体积小,功率低,传输距离远,工作频率高达525MHz。 5.1 产品特性 LoRaTM 调制解调器 最大链路预算可达168dB +20dBm(100mW) 持续发射功率 +14dBm 的高效率功率放大器 高灵敏度: 低至-148dBm 高可靠性的前端:IIP3=-11dBm 9.9mA 最低接收电流,200nA 寄存器保持电流 支持FSK 、GFSK 、MSK 、GMSK 、LoRaTM 及OOK 调制方式 内置式位同步,用于时钟恢复 前导码检测 127dB 的RSSI 动态范围 自动射频信号检测,CAD 模式和超高速AFC 带有CRC 、高达256 字节的数据包引擎 内置温度传感器和低电量指示器 5.2 工作参数 ![LoRa工作参数.png](https://oss-club.rt-thread.org/uploads/20220730/3b2529cca65666917cf916f337675dd3.png.webp "LoRa工作参数.png") 5.3 引脚分布 ![引脚.png](https://oss-club.rt-thread.org/uploads/20220730/65b91a7ac1f1538b4bf36ad85d441373.png "引脚.png") 6、OLED显示模组 本系统选用一个0.96寸的OLED显示屏,作为人机交互界面,显示分辨率为128*64。驱动IC为SSD1306,采用四线制的SPI通讯方式。 引脚定义如下: OLED引脚,是7针SPI模式。 1). GND 电源地 2). VCC 电源正(3~5.5V) 3). D0 OLED 的 D0 脚,在 SPI 和 IIC 通信中为时钟管脚 4). D1 OLED 的 D1 脚,在 SPI 和 IIC 通信中为数据管脚 5). RES OLED 的 RES#脚,用来复位(低电平复位) 6). DC OLED 的 D/C#E 脚,数据和命令控制管脚 ![096OLEDZ.jpg](https://oss-club.rt-thread.org/uploads/20220730/5c9c0b24a527c155150597b7d0bb2603.jpg.webp "096OLEDZ.jpg") 7、RS485电路 采用SN65HVD12作为RS485的收发器,可以将串口输出信号转送到RS485总线上,也可以将RS485总线上的信号转换后送到串口的输入引脚,从而实现RS485信号的收发。 引脚定义如下: ![RS485.png](https://oss-club.rt-thread.org/uploads/20220730/5b89049476758106dd9410674d4d011f.png.webp "RS485.png") 8、软件设计 8.1、目录及工程结构 该软件开发环境使用RT-Thread Studio开发,创建工作程,目录如下图所示: ![软件目录结构.png](https://oss-club.rt-thread.org/uploads/20220730/e5aa272187195b1bb89b14c540c589dc.png "软件目录结构.png") 在工程中,文件显示结构如下图所示: ![工程界面.png](https://oss-club.rt-thread.org/uploads/20220730/0cd2bf997ac3e2bea0a54f0439ba40fa.png "工程界面.png") 在该软件中,总共使用了3个线程,一个用于处理LoRa网关收发消息,一个用于处理RS485数据,一个用于LoRa收发的软时钟管理。 8.2 瑞萨RA Smart Configurator配置 配置SPI和外部中断接口代码如下: ![配置SPI.png](https://oss-club.rt-thread.org/uploads/20220730/0761ec7e2a475671ba1d36455b86c1c9.png "配置SPI.png") 配置外部中断接口: ![5个中断口.png](https://oss-club.rt-thread.org/uploads/20220730/09e2320b50dc031c489d596103f3951f.png "5个中断口.png") 8.3、部份代码展示 SPI驱动的代码如下如示: ```c /* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2022-07-27 Administrator the first version */ #include
#include
#include
#include "SX1278_SPI.h" #include "Oled_Entry.h" #include "LoRa.h" #include "LoRa.h" #include "LoRaMsgHandler.h" Exti_State_s ExtiState; /********************************************************************************/ #define USER_INPUT "P105" #define SX1278_IRQ0 "P101" #define SX1278_IRQ1 "P000" #define SX1278_IRQ2 "P001" #define SX1278_IRQ3 "P502" /********************************************************************************/ static struct rt_spi_device spi_dev_SX1278; static rt_uint8_t LoRaGW_SendFlag = 0x00; /******************************************************************************* ** FUNCTION : SX1278_Irq0_Callback ** DESCRIPTION : DIO0中断 ** PARAMETERS : None ** IN : ** OUT : ** RETURN : ret rt_eok: success, RT_ERROR:failed *******************************************************************************/ void irq_callback_IRQ0(void *args) { ExtiState.Bits.SX1278DIO0Flag = 0x01; rt_kprintf("\n SX1278_Irq0 triggered \n"); } /******************************************************************************* ** FUNCTION : SX1278_Irq1_Callback ** DESCRIPTION : DIO1中断 ** PARAMETERS : None ** IN : ** OUT : ** RETURN : ret rt_eok: success, RT_ERROR:failed *******************************************************************************/ void SX1278_Irq1_Callback(void *args) { ExtiState.Bits.SX1278DIO1Flag = 0x01; rt_kprintf("\n SX1278_Irq1 triggered \n"); } /******************************************************************************* ** FUNCTION : SX1278_Irq2_Callback ** DESCRIPTION : DIO3中断 ** PARAMETERS : None ** IN : ** OUT : ** RETURN : ret rt_eok: success, RT_ERROR:failed *******************************************************************************/ void SX1278_Irq2_Callback(void *args) { ExtiState.Bits.SX1278DIO2Flag = 0x01; rt_kprintf("\n SX1278_Irq2 triggered \n"); } /******************************************************************************* ** FUNCTION : SX1278_Irq3_Callback ** DESCRIPTION : DIO3中断 ** PARAMETERS : None ** IN : ** OUT : ** RETURN : ret rt_eok: success, RT_ERROR:failed *******************************************************************************/ void SX1278_Irq3_Callback(void *args) { ExtiState.Bits.SX1278DIO3Flag = 0x01; rt_kprintf("\n SX1278_Irq3 triggered \n"); } /** *该函数通过发送指令,写一个数据 * op 发送的写命令 * data 需要写入的数据 */ int SX1278_write_data(rt_uint8_t op, rt_uint8_t data) { uint32_t level; uint8_t buffer[2]; level = rt_hw_interrupt_disable(); buffer[0] = op |0x80 ; buffer[1] = data; rt_spi_send(&spi_dev_SX1278, buffer, 2); // #ifdef RT_DEBUG_TEST // rt_kprintf("use rt_spi_transfer_message() write command and register is:%x\n,the value is %x\n",buffer[0], buffer[1]); // #endif rt_hw_interrupt_enable(level); return RT_EOK; } /** *该函数主要功能是将send_cmd存放的命令下发,命令长度为cmdlenght, *然后datalenght个字节数据,存send_buf *send_cmd 待发送的写数据地址,最高位为0或1均可 *cmdlenght 发送的写数据地址长度 *send_buf 接收数据缓 *sendlenght 发送数据的长度 */ int SX1278_write_bytes(rt_uint8_t * send_cmd, rt_size_t cmdlenght, rt_uint8_t * send_buf, rt_size_t datalenght) { // *send_cmd=*send_cmd|0x80; //最高位为1,表示指令为写,后7位表示写的127个寄存器地址。 // rt_spi_send_then_send(&spi1301,send_cmd,cmdlenght,send_buf,datalenght); struct rt_spi_message devaddr, recdata; *send_cmd=*send_cmd|0x80; //最高位为1,表示指令为写,后7位表示写的127个寄存器地址。 devaddr.send_buf=send_cmd; devaddr.recv_buf=RT_NULL; devaddr.length=cmdlenght; devaddr.cs_take=1; devaddr.cs_release=0; devaddr.next=&recdata; recdata.send_buf=send_buf; recdata.recv_buf=RT_NULL; recdata.length=datalenght; recdata.cs_take=0; recdata.cs_release=1; recdata.next=RT_NULL; rt_spi_transfer_message(&spi_dev_SX1278 , &devaddr); // #ifdef RT_DEBUG_TEST // rt_kprintf("use rt_spi_transfer_message() write command and register is:%x\n,the value is %x\n",*send_cmd, *send_buf); // #endif return RT_EOK; } /** *该函数主要功能是将send_cmd存放的命令下发,命令长度为cmdlenght, *然后datalenght个字节数据,存send_buf *send_cmd 待发送的写数据地址,最高位为0或1均可 *cmdlenght 发送的写数据地址长度 *recebuf 接收数据缓存 *reclenght 需要接收数据的长度 */ int SX1278_read_bytes(rt_uint8_t * sendcmd, rt_size_t cmdlenght,rt_uint8_t * recebuf, rt_size_t reclenght) { // *sendcmd=*sendcmd&0x7F; //最高位为0,表示指令为读,后7位表示读取的127个寄存器地址。 // rt_spi_send_then_recv(&spi1301,sendcmd,cmdlenght,recebuf,reclenght); struct rt_spi_message devaddr, recdata; devaddr.send_buf=sendcmd; devaddr.recv_buf=RT_NULL; devaddr.length=cmdlenght; devaddr.cs_take=1; devaddr.cs_release=0; devaddr.next=&recdata; recdata.send_buf=RT_NULL; recdata.recv_buf=recebuf; recdata.length=reclenght; recdata.cs_take=0; recdata.cs_release=1; recdata.next=RT_NULL; rt_spi_transfer_message(&spi_dev_SX1278 , &devaddr); // #ifdef RT_DEBUG_TEST // rt_kprintf("use rt_spi_transfer_message() read command and register is:%x\n,the value is %x %x %x %x\n",*sendcmd, recebuf[0],recebuf[1],recebuf[2],recebuf[3]); // #endif return RT_EOK; } /******************************************************************************* ** FUNCTION : SX1278_Interface_Configuration ** DESCRIPTION : SPI Init ** PARAMETERS : None ** IN : ** OUT : ** RETURN : ret rt_eok: success, RT_ERROR:failed *******************************************************************************/ static int SX1278_Interface_Configuration(void) { rt_err_t ret; struct rt_spi_configuration sx1278cfg; uint32_t cs_pin = SX1278_SPI_NSS; rt_pin_mode(SX1278_SPI_NSS,PIN_MODE_OUTPUT); rt_pin_mode(SX1278_RESET_PIN,PIN_MODE_OUTPUT); ret = rt_spi_bus_attach_device(&spi_dev_SX1278, "SX1278", "spi1", (void *)&cs_pin); if (ret != RT_EOK) { rt_kprintf("%s attach to %s failed, %d\n", "SX1278", "spi1", ret); return RT_ERROR; } else { sx1278cfg.data_width = 0x08; sx1278cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB; sx1278cfg.max_hz = 5*1000*1000; ret = rt_spi_configure(&spi_dev_SX1278, &sx1278cfg); } return ret; } void irq_callback_Send(void *args) { LoRaGW_SendFlag = 0x02; rt_kprintf("\n LoRaGW ready Send!\r\n"); } /******************************************************************************* ** FUNCTION : SX1278_Interface_Configuration ** DESCRIPTION : SPI Init ** PARAMETERS : None ** IN : ** OUT : ** RETURN : ret rt_eok: success, RT_ERROR:failed *******************************************************************************/ static int SX1278_DIO_Configuration(void) { rt_uint32_t pin = rt_pin_get(SX1278_IRQ0); rt_kprintf("\n pin number : 0x%04X \n", pin); rt_err_t err = rt_pin_attach_irq(pin, PIN_IRQ_MODE_RISING, irq_callback_IRQ0, RT_NULL); if(RT_EOK != err) { rt_kprintf("\n attach irq failed. \n"); } err = rt_pin_irq_enable(pin, PIN_IRQ_ENABLE); if(RT_EOK != err) { rt_kprintf("\n enable irq failed. \n"); } pin = rt_pin_get(SX1278_IRQ1); rt_kprintf("\n pin number : 0x%04X \n", pin); err = rt_pin_attach_irq(pin, PIN_IRQ_MODE_RISING, SX1278_Irq1_Callback, RT_NULL); if(RT_EOK != err) { rt_kprintf("\n attach irq failed. \n"); } err = rt_pin_irq_enable(pin, PIN_IRQ_ENABLE); if(RT_EOK != err) { rt_kprintf("\n enable irq failed. \n"); } pin = rt_pin_get(SX1278_IRQ2); rt_kprintf("\n pin number : 0x%04X \n", pin); err = rt_pin_attach_irq(pin, PIN_IRQ_MODE_RISING, SX1278_Irq2_Callback, RT_NULL); if(RT_EOK != err) { rt_kprintf("\n attach irq failed. \n"); } err = rt_pin_irq_enable(pin, PIN_IRQ_ENABLE); if(RT_EOK != err) { rt_kprintf("\n enable irq failed. \n"); } pin = rt_pin_get(SX1278_IRQ3); rt_kprintf("\n pin number : 0x%04X \n", pin); err = rt_pin_attach_irq(pin, PIN_IRQ_MODE_RISING, SX1278_Irq3_Callback, RT_NULL); if(RT_EOK != err) { rt_kprintf("\n attach irq failed. \n"); } err = rt_pin_irq_enable(pin, PIN_IRQ_ENABLE); if(RT_EOK != err) { rt_kprintf("\n enable irq failed. \n"); } return RT_EOK; } MSH_CMD_EXPORT(SX1278_DIO_Configuration, SX1278_DIO_Configuration); void icu_sample(void) { /* init */ rt_uint32_t pin = rt_pin_get(USER_INPUT); rt_kprintf("\n pin number : 0x%04X \n", pin); rt_err_t err = rt_pin_attach_irq(pin, PIN_IRQ_MODE_RISING, irq_callback_Send, RT_NULL); if(RT_EOK != err) { rt_kprintf("\n attach irq failed. \n"); } err = rt_pin_irq_enable(pin, PIN_IRQ_ENABLE); if(RT_EOK != err) { rt_kprintf("\n enable irq failed. \n"); } } MSH_CMD_EXPORT(icu_sample, icu sample); /******************************************************************************* ** FUNCTION : rt_spi2_SX1278_Init ** DESCRIPTION : SX1278 Init ** PARAMETERS : None ** IN : ** OUT : ** RETURN : ret rt_eok: success, RT_ERROR:failed *******************************************************************************/ void SX1278_thread_entry(void *parameter) { while(1) { LoRaRfMode_ProcessSystemState(); if(LoRaGW_RecFlag == 0x01) { Oled_Clear_Display(); Oled_Display_String(0,0,"LoRaGW Receive Message\0",OLED_DISPLAY_SIZE16); LoRaGW_RecFlag = 0x00; } LoRaRfMode_LoRaMacDataProcess(); rt_thread_delay(2000); LoRaRf_StartOnRxFrameMsg(); Oled_Clear_Display(); Oled_Display_String(0,0,"LoRaGW Ready Receive\0",OLED_DISPLAY_SIZE16); rt_thread_delay(2000); if(LoRaGW_SendFlag == 0x02) { LoRaRfMode_SendData(); LoRaGW_SendFlag = 0x01; Oled_Clear_Display(); Oled_Display_String(0,0,"LoRaGW Send Message\0",OLED_DISPLAY_SIZE16); rt_thread_delay(2000); } } } /******************************************************************************* ** FUNCTION : rt_spi2_SX1278_Init ** DESCRIPTION : SX1278 Init ** PARAMETERS : None ** IN : ** OUT : ** RETURN : ret rt_eok: success, RT_ERROR:failed *******************************************************************************/ static int rt_spi2_SX1278_Init(void) { rt_err_t result = RT_EOK; rt_thread_t tid; rt_kprintf("Initial rt_spi1_SX1278_Init.\r\n"); SX1278_Interface_Configuration(); icu_sample(); SX1278_DIO_Configuration(); LoRaRfMode_Init(); rt_kprintf("Initial SX1278 parameter success.\r\n"); tid = rt_thread_create("SX1278 Radio", SX1278_thread_entry, RT_NULL, 2048, 8, 30); if (tid == RT_NULL) { return -RT_ERROR; } rt_thread_startup(tid); return result; } /* 导出到自动初始化*//* 使用RT-Thread的组件自动初始化机制 */ INIT_COMPONENT_EXPORT(rt_spi2_SX1278_Init); ``` LoRa收发管理代码如下: ```c /*------------------------------------------------------------------------ 文件名称:Protocal/LoRaMsgHandler.c 编写: 日期: 2021-01-17 版本:V1.0 说明:LoRa协议处理 ---------------------------------------------------------------------------*/ /* 头文件------------------------------------------------------------------*/ #include "LoRaMsgHandler.h" #include "LoRa.h" #include "SX1278_SPI.h" #include "utilities.h" #include "stdlib.h" #include "string.h" #include "rthw.h" #include
#include
#define N 10 /*局000部0/全局变量定义--------------------------------------------------------*/ LoRaRFCtx_t RFCtx; rt_uint8_t LoRaGW_RecFlag; static Radio_State_s RadioFlag; uint8_t LoRaMacState; static RxDoneParams_s RxDoneParams; LoRaMsg_NetCtl_u NetCtl_RxMsg; LoRaMsg_NetCtl_u NetCtl_TxMsg; //double hr; LoRaTx_HBMsg_u HB_TxMsg;// __attribute__ ((at(0x20001F08))); System_State_s Sysflag;// __attribute__ ((at(0x20001F04))); uint16_t BCounter ;// __attribute__ ((at(0x20001F00))); void findPeaks(int *src, int src_lenth, int distance, int *indMax, int *indMax_len, int *indMin, int *indMin_len); void smooth(int data[], int size); /* 功能函数----------------------------------------------------------------*/ /******************************************************************************* ** FUNCTION : OnRadioTxDone ** DESCRIPTION : 射频发送完成处理 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void OnRadioTxDone( void ) { RFCtx.LastTxTime = TimerGetCurrentTime( ); RadioFlag.Bits.TxFrameFlag = 0x01; } /******************************************************************************* ** FUNCTION : OnRadioRxDone ** DESCRIPTION : 射频接收完成处理 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) { RxDoneParams.Payload = payload; RxDoneParams.Size = size; RxDoneParams.Rssi = rssi; RxDoneParams.Snr = snr; LoRaGW_RecFlag = 0x01; RadioFlag.Bits.RxFrameFlag = 0x01; } /******************************************************************************* ** FUNCTION : OnRadioTxTimeout ** DESCRIPTION : 射频发射超时处理 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void OnRadioTxTimeout( void ) { RadioFlag.Bits.TxTimeoutFlag = 0x01; } /******************************************************************************* ** FUNCTION : OnRadioRxError ** DESCRIPTION : 射频接收错误处理 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void OnRadioRxError( void ) { RadioFlag.Bits.RxErrorFlag = 0x01; } /******************************************************************************* ** FUNCTION : OnRadioRxTimeout ** DESCRIPTION : 射频接收超时处理 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void OnRadioRxTimeout( void ) { RadioFlag.Bits.RxTimeoutFlag = 0x01; } /******************************************************************************* ** FUNCTION : LoRaRfMode_IsBusy ** DESCRIPTION : 射频忙检测 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ bool LoRaRfMode_IsBusy( void ) { if( RFCtx.LoRaRFState == LORARF_IDLE ) { return false; } return true; } /******************************************************************************* ** FUNCTION : RxWindowSetup ** DESCRIPTION : 射频接收窗口设置 ** PARAMETERS : rxContinuous:连续接收 ** :maxRxWindow:最大接收窗口时间 ** RETURN : None *******************************************************************************/ static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow ) { if( rxContinuous == false ) { Radio.Rx( maxRxWindow ); } else { Radio.Rx( 0 ); // Continuous mode } } /******************************************************************************* ** FUNCTION : LoRaRf_StartOnRxFrameMsg ** DESCRIPTION : 设置射频为接收状态,开始接收数据 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ bool LoRaRf_StartOnRxFrameMsg( void ) { RxConfigParams_s RxConfig; if( Radio.GetStatus( ) != RF_IDLE) { return false; } RxConfig.Datarate = RFCtx.RxWindowConfig.Datarate; RxConfig.Frequency = RFCtx.RxWindowConfig.Frequency; RxConfig.RepeaterSupport = RFCtx.Txconfig.RepeaterSupport; RxConfig.symb_timeout = RFCtx.RxWindowConfig.symb_timeout; RxConfig.Bandwidth = RFCtx.RxWindowConfig.Bandwidth; // Setup continuous listening for class c RxConfig.RxContinuous = true; RegionComputeRxWindowParameters( RxConfig.Datarate, RFCtx.RxWindowConfig.MinRxSymbols, RFCtx.RxWindowConfig.SystemMaxRxError , &RxConfig); RxConfig.Timeout = RFCtx.RxWindowConfig.Timeout; RxConfig.RxWindow = RFCtx.RxWindowConfig.RxWindow; rt_kprintf("GW Parameter:%d;%ld;%ld;\r\n",RxConfig.Datarate,RxConfig.Frequency,RxConfig.Bandwidth); //配置接收窗口 if( LoRaRF_RxConfig( &RxConfig, ( int8_t* ) &RxConfig.Datarate ) == true ) { //设置接收模式和接收时间 RxWindowSetup( RxConfig.RxContinuous, RxConfig.Timeout ); return true; } return false; } /******************************************************************************* ** FUNCTION : LoRaRf_StartOnRxFrameMsg ** DESCRIPTION : 通过射频发送一帧数据 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ bool LoRaRf_SendFrameMsg( uint8_t *databuff, uint8_t datalen ) { TxConfigParams_s txConfig; uint32_t RfChannel; bool sFlag; uint32_t i; /* if( Radio.GetStatus( ) != RF_IDLE) { return false; } if(datalen > sizeof(LoRaMsgStruct_s)) { return false; } */ Radio.SetPublicNetwork(false); RfChannel = LoRaRfMode_GetRandm(RFCtx.MinChannel, RFCtx.MaxChannel); txConfig.Frequency = RFCtx.TxStartFreq + RfChannel*RFCtx.TxRfBw; txConfig.Datarate=RFCtx.Txconfig.Datarate; txConfig.TxPower=RFCtx.Txconfig.TxPower; txConfig.PktLen = datalen; sFlag = Radio.IsChannelFree(MODEM_LORA,txConfig.Frequency,RF_RSSITHRESH_THRESHOLD,RF_CARRIERSENSETIME); if(sFlag == false) { return false; } LoRaRF_TxConfig(&txConfig, &RFCtx.TxTimeOnAir); Radio.Send( databuff, datalen ); rt_kprintf("LoRaGW Send Data:\r\n"); for(i = 0; i < datalen; i++) { rt_kprintf(" %2x",databuff[i]); } rt_kprintf("\r\n"); return true; } /******************************************************************************* ** FUNCTION : LoRaRfMode_InitParameters ** DESCRIPTION : 初始化射频参数 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void LoRaRfMode_InitParameters(void ) { RFCtx.LoRaRFState = LORARF_IDLE; RFCtx.MaxChannel = LORARF_MAXCCHL_NUMBER; RFCtx.MinChannel = LORARF_MINCCHL_NUMBER; RFCtx.PktBufferLen = 0x00; // RFCtx.TxDelayedTimer = 0x00; RFCtx.TxRfBw = LORARF_SET_BW; RFCtx.TxStartFreq = LORARF_TX_BASE_FREQ; RFCtx.Txconfig.AntennaGain = CN470_DEFAULT_ANTENNA_GAIN; RFCtx.Txconfig.Bandwidth = LoRaBandWidths[7]; RFCtx.Txconfig.Coderate = LORACR45; RFCtx.Txconfig.CrcON = LORARF_SET_CRCON; RFCtx.Txconfig.Datarate = LORARFDR3; RFCtx.Txconfig.FixLen = false; RFCtx.Txconfig.FreqHopOn = false; RFCtx.Txconfig.HopPeriod = 0x00; RFCtx.Txconfig.IQInverted = false; RFCtx.Txconfig.MaxEirp = CN470_DEFAULT_MAX_EIRP; RFCtx.Txconfig.PktLen = 0x00; RFCtx.Txconfig.Preamblelen = LORARF_SET_PREAMBLENUM; RFCtx.Txconfig.RepeaterSupport = 0x01; RFCtx.Txconfig.RfModem = MODEM_LORA; RFCtx.Txconfig.RxContinuous = true; RFCtx.Txconfig.Timeout = 3000; RFCtx.Txconfig.TxPower = 20; RFCtx.RxWindowDelay = LORARF_RX_MAX_WINDOW; RFCtx.RxWindowConfig.Bandwidth = LoRaBandWidths[7]; RFCtx.RxWindowConfig.BandWidthAfc =0x00; RFCtx.RxWindowConfig.Coderate = LORACR45; RFCtx.RxWindowConfig.CrcON = 0x01; RFCtx.RxWindowConfig.Datarate = LORARFDR0; RFCtx.RxWindowConfig.FixLen = false; RFCtx.RxWindowConfig.FreqHopOn = false; RFCtx.RxWindowConfig.Frequency = LORARF_RX_WND_FREQ; RFCtx.RxWindowConfig.HopPeriod = 0x00; RFCtx.RxWindowConfig.IQInverted = false; RFCtx.RxWindowConfig.RxWindow = LORARF_RX_WINDOW; RFCtx.RxWindowConfig.PayloadLen = 0x00; RFCtx.RxWindowConfig.Preamblelen = 0x08; RFCtx.RxWindowConfig.symb_timeout = 10; RFCtx.RxWindowConfig.RfModem = MODEM_LORA; RFCtx.RxWindowConfig.RxContinuous = false; RFCtx.RxWindowConfig.Timeout = LORARF_RX_WINDOW; RFCtx.RxWindowConfig.MinRxSymbols =10; RFCtx.RxWindowConfig.symb_timeout =LORARF_RX_SYMB_TIMEOUT; RFCtx.RxWindowConfig.SystemMaxRxError = 10; }; /******************************************************************************* ** FUNCTION : LoRaRfMode_Init ** DESCRIPTION : 射频初始化 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ void LoRaRfMode_Init(void) { Radio.IoInit(); RFCtx.RadioEvents.TxDone = OnRadioTxDone; RFCtx.RadioEvents.RxDone = OnRadioRxDone; RFCtx.RadioEvents.RxError = OnRadioRxError; RFCtx.RadioEvents.TxTimeout = OnRadioTxTimeout; RFCtx.RadioEvents.RxTimeout = OnRadioRxTimeout; Radio.Init(&RFCtx.RadioEvents); // Radio.Sleep( ); // i = STM32L0_GetResetState(); /* if((BCounter > 10) || ((i &0x01) == 0x01)) { BCounter = 0x00; Sysflag.Systate = 0x00; } ExtiState.Bits.LSM6DINT1Flag = 0x01; */ Sysflag.Systate = 0x00; LoRaRfMode_InitParameters(); /**LoRaRF State*/ RFCtx.LoRaRFState = LORARF_IDLE; LoRaMacState = 0x00; RadioFlag.FlagValue = 0x00; NetCtl_TxMsg.MsgData.FrameHeader = Message_Header; RFCtx.GWAddr = 0x12345678; LoRaRf_StartOnRxFrameMsg(); } /******************************************************************************* ** FUNCTION : LoRaRfMode_ProcessRadioRxDone ** DESCRIPTION : 处理接收数据 ** PARAMETERS : recData 指针指向接收数据buffer ** recLen 接收数据长度 ** RETURN : None *******************************************************************************/ static bool LoRaRfMode_ProcessRadioRxDone( uint8_t *recData , uint8_t recLen ) { uint16_t size; uint8_t i; uint32_t tempval; uint16_t CrcVal; if(recLen > sizeof(NetCtl_RxMsg.DataBuffer)) { return false; } size = recLen; for(i = 0; i < size; i ++) { NetCtl_RxMsg.DataBuffer[i] = recData[i]; } tempval = 0x12345678; if(NetCtl_RxMsg.MsgData.GWAddr != tempval) { return false; } if(NetCtl_RxMsg.MsgData.FrameHeader == Message_Header) { CrcVal = CRC16_XMODEMAlgorithm( NetCtl_RxMsg.DataBuffer,NetCtl_RxMsg.MsgData.FrameLen+4); if(CrcVal == NetCtl_RxMsg.MsgData.FrameCrc) { switch(NetCtl_RxMsg.MsgData.FrameType) { case Message_EnquireTimeHB: tempval= 0x12345678; NetCtl_TxMsg.MsgData.FrameType = Message_SetHBAns; NetCtl_TxMsg.MsgData.Data = tempval; NetCtl_TxMsg.MsgData.GWAddr = RFCtx.GWAddr; break; case Message_SetTimeHB: NetCtl_RxMsg.MsgData.Data = 0x12345678; tempval= 0x12345678; NetCtl_TxMsg.MsgData.FrameType = Message_SetHBAns; NetCtl_TxMsg.MsgData.GWAddr = RFCtx.GWAddr; NetCtl_TxMsg.MsgData.Data = tempval; break; default: return false; } NetCtl_TxMsg.MsgData.FrameLen = 9; NetCtl_TxMsg.MsgData.FrameCrc = CRC16_XMODEMAlgorithm(NetCtl_TxMsg.DataBuffer, 13); LoRaRf_SendFrameMsg(NetCtl_TxMsg.DataBuffer,15); } /*CRC校验失败*/ else { NetCtl_TxMsg.MsgData.FrameType = NetCtl_RxMsg.MsgData.FrameType; NetCtl_TxMsg.MsgData.GWAddr = RFCtx.GWAddr; NetCtl_TxMsg.MsgData.Data = 0xFFFFFFFF; NetCtl_TxMsg.MsgData.FrameLen = 9; NetCtl_TxMsg.MsgData.FrameCrc = CRC16_XMODEMAlgorithm(NetCtl_TxMsg.DataBuffer, 13); LoRaRf_SendFrameMsg(NetCtl_TxMsg.DataBuffer,15); } } else { return false; } return true; } /******************************************************************************* ** FUNCTION : LoRaRfMode_ProcessRadioTxTimeout ** DESCRIPTION : 处理射频发送超时 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void LoRaRfMode_ProcessRadioTxTimeout( void ) { /*nothing to do */ rt_hw_cpu_reset(); } /******************************************************************************* ** FUNCTION : LoRaRfMode_HandleRadioRxTimeout ** DESCRIPTION : 处理射频接收超时 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void LoRaRfMode_HandleRadioRxTimeout(void) { /*nothing to do */ // Radio.Sleep( ); LoRaRf_StartOnRxFrameMsg(); Sysflag.Bits.SysIDLEFlag = 0x01; } /******************************************************************************* ** FUNCTION : LoRaRfMode_ProcessRadioRxError ** DESCRIPTION : 处理射频接收错误 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void LoRaRfMode_ProcessRadioRxError( void ) { /*nothing to do */ rt_hw_cpu_reset(); } /******************************************************************************* ** FUNCTION : LoRaRfMode_ProcessRadioTxDone ** DESCRIPTION : 处理射频发射完成 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void LoRaRfMode_ProcessRadioTxDone( void ) { LoRaRf_StartOnRxFrameMsg(); } /******************************************************************************* ** FUNCTION : LoRaRfMode_PrepareRxDoneAbort ** DESCRIPTION : 处理射频接收终止 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void LoRaRfMode_PrepareRxDoneAbort( void ) { /*nothing to do */ } /******************************************************************************* ** FUNCTION : LoRaRfMode_PrepareRxAbort ** DESCRIPTION : 处理射频接收准备终止 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void LoRaRfMode_PrepareRxAbort( void ) { /*nothing to do */ LoRaRf_StartOnRxFrameMsg(); } /******************************************************************************* ** FUNCTION : LoRaRfMode_PrepareTxAbort ** DESCRIPTION : 处理射频发射终止 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ static void LoRaRfMode_PrepareTxAbort( void ) { } /******************************************************************************* ** FUNCTION : LoRaRfMode_ProcessSystemState ** DESCRIPTION : 处理系统中断状态 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ void LoRaRfMode_ProcessSystemState(void) { if(ExtiState.ExtiFlag > 0) { if(ExtiState.Bits.SX1278DIO0Flag == 0x01) { Radio.Irq0_Process(); ExtiState.Bits.SX1278DIO0Flag = 0x00; } else if(ExtiState.Bits.SX1278DIO1Flag == 0x01) { Radio.Irq1_Process(); ExtiState.Bits.SX1278DIO1Flag = 0x00; } else if(ExtiState.Bits.SX1278DIO2Flag == 0x01) { Radio.Irq2_Process(); ExtiState.Bits.SX1278DIO2Flag = 0x00; } else if(ExtiState.Bits.SX1278DIO3Flag == 0x01) { Radio.Irq3_Process(); ExtiState.Bits.SX1278DIO3Flag = 0x00; } else if(ExtiState.Bits.LSM6DINT0Flag == 0x01) { } else if(ExtiState.Bits.LSM6DINT1Flag == 0x01) { } else if(ExtiState.Bits.HBIntFlag == 0x01) { } else if(ExtiState.Bits.HBGPIOFlag == 0x01) { } } } /******************************************************************************* ** FUNCTION : LoRaRfMode_LoRaMacDataProcess ** DESCRIPTION : 处理射频状态 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ void LoRaRfMode_LoRaMacDataProcess(void) { rt_uint32_t i; if(RadioFlag.FlagValue > 0x00) { if(RadioFlag.Bits.RxFrameFlag == 0x01) { LoRaRfMode_ProcessRadioRxDone(RxDoneParams.Payload,RxDoneParams.Size); rt_kprintf("LoRaGW Receive Data:\r\n"); for(i = 0; i < RxDoneParams.Size; i++) { rt_kprintf(" %2x",RxDoneParams.Payload[i]); } rt_kprintf("\r\n"); RadioFlag.Bits.RxFrameFlag = 0x00; } else if(RadioFlag.Bits.TxFrameFlag == 0x01) { LoRaRfMode_ProcessRadioTxDone(); RadioFlag.Bits.TxFrameFlag = 0x00; } else if(RadioFlag.Bits.RxTimeoutFlag == 0x01) { LoRaRfMode_HandleRadioRxTimeout(); RadioFlag.Bits.RxTimeoutFlag = 0x00; } else if(RadioFlag.Bits.TxTimeoutFlag == 0x01) { LoRaRfMode_ProcessRadioTxTimeout(); RadioFlag.Bits.TxTimeoutFlag = 0x00; } else if(RadioFlag.Bits.RxErrorFlag == 0x01) { LoRaRfMode_ProcessRadioRxError(); RadioFlag.Bits.RxErrorFlag = 0x00; } else if(RadioFlag.Bits.TxAbortFlag == 0x01) { LoRaRfMode_PrepareTxAbort(); RadioFlag.Bits.TxAbortFlag = 0x00; } else if(RadioFlag.Bits.RxAbortFlag == 0x01) { LoRaRfMode_PrepareRxAbort(); RadioFlag.Bits.RxAbortFlag = 0x00; } else if(RadioFlag.Bits.RxDoneAbortFlag == 0x01) { LoRaRfMode_PrepareRxDoneAbort(); RadioFlag.Bits.RxDoneAbortFlag = 0x00; } } } /******************************************************************************* ** FUNCTION : LoRaRfMode_LoRaMacDataProcess ** DESCRIPTION : 处理射频状态 ** PARAMETERS : None ** RETURN : None *******************************************************************************/ void LoRaRfMode_SendData(void) { uint16_t CrcVal; uint16_t i; HB_TxMsg.MsgData.DeviceAddr = 0x12345678; HB_TxMsg.MsgData.DataPktHeader = Message_Header; HB_TxMsg.MsgData.DataLen = sizeof(HB_TxMsg.DataBuffer); for(i = 0; i < 20; i++) { HB_TxMsg.MsgData.HBValue[i] = 0xFF; } CrcVal = CRC16_XMODEMAlgorithm( HB_TxMsg.DataBuffer,HB_TxMsg.MsgData.DataLen-2); HB_TxMsg.MsgData.CRCVal = CrcVal; if(LoRaRf_SendFrameMsg(HB_TxMsg.DataBuffer,sizeof(HB_TxMsg.DataBuffer)) == true) { Sysflag.Systate = 0x00; Sysflag.Bits.SysSendFlag = 0x01; } else { rt_thread_mdelay(1000); } } 软件定时器管理代码: ```c /** ****************************************************************************** * @file Application/utility.c * @author jiang * @version V0.0.0 * @date 09-Dec-2019 * @brief This file contains the utility function ****************************************************************************** * @attention *RCC_APB2ENR_SPI1EN ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include
#include
#include "TimerService.h" #include "rthw.h" #include "TimerBase.h" /* Private or public typedef -------------------------------------------------*/ TimerEvent_t *TimerListHeader = NULL; /* founction ------------------------------------------------------------------*/ /*! * 安全调用函数 *_callback_为需要调用的函数 */ #define exec_cb( _callback_ ) \ do { \ if( _callback_ == NULL ) \ { \ while(1); \ } \ else \ { \ _callback_( ); \ } \ } while(0); /* ** 函 数 名: Initial_SoftTimer ** 功能说明: 初始化软定时器 ** [IN]: obj 初始化对象 callback 定时器中断时的回调处理函数 ** [return]: 无 **/ void Initial_SoftTimer(TimerEvent_t *obj, void ( *callback )( void )) { obj->countval = 0; obj->preval = 0; obj->isRun = false; obj->callback = callback; obj->Next = NULL; } /* ** 函 数 名: Initial_SoftTimer ** 功能说明: 初始化软定时器 ** [IN]: obj 初始化对象 countval 定时器的计数值,从高往低减 ** [return]: 无 **/ void SetVale_SoftTimer(TimerEvent_t *obj,uint32_t countval) { TimerEvent_t * curtimer = TimerListHeader->Next; obj->countval = countval; obj->preval = countval; if(TimerListHeader == NULL || obj == NULL) { return; } else { Stop_SoftTimer(obj); if(TimerListHeader == obj) { TimerListHeader->preval = countval; TimerListHeader->countval = countval; } else { while(curtimer != NULL) { if(curtimer == obj) { curtimer->preval = countval; curtimer->countval = countval; curtimer = curtimer->Next; } else { curtimer = curtimer->Next; } } } } } /* ** 函 数 名: Insert_SoftTimer ** 功能说明: 在定时器链表中插入一个定时器 ** [IN]: obj 待插入的定时器对象 ** [return]: 无 **/ void Insert_SoftTimer(TimerEvent_t *obj) { TimerEvent_t * pretimer = TimerListHeader; TimerEvent_t * curtimer = TimerListHeader->Next; rt_base_t level0; level0 = rt_hw_interrupt_disable(); if(obj == NULL) //如果插入定时器为空,则返回 { rt_hw_interrupt_enable(level0); return; } if(TimerListHeader == NULL ) //如果链表头为空,则直接插入链表头 { TimerListHeader = obj; } else { if(TimerListHeader->countval > obj->countval) //如果链表头的计时大于插入定时器时间,则在链表头位置插入 { obj->Next = pretimer; TimerListHeader = obj; } else { if(TimerListHeader->Next == NULL) { TimerListHeader->Next = obj; } else { while(curtimer != NULL) //查找插入位置 { if(curtimer->countval > obj->countval) { obj->Next = curtimer; pretimer->Next = obj; break; } else { pretimer = curtimer; curtimer = curtimer->Next; if(curtimer == NULL) { pretimer->Next =obj; break; } } } } } } rt_hw_interrupt_enable(level0); } /* ** 函 数 名: Start_SoftTimer ** 功能说明: 启动一个定时器,并插入到链表中 ** [IN]: obj 待启动的定时器对象 ** [return]: 无 **/ void Start_SoftTimer(TimerEvent_t *obj) { TimerEvent_t * pretimer = TimerListHeader; if(obj == NULL || TimerListHeader == NULL) return;//如果待启动的定时器对象,则返回 while(pretimer != NULL) { if(pretimer == obj) { pretimer->isRun =true; pretimer->countval = pretimer->preval; break; } else { pretimer = pretimer->Next; } } } /* ** 函 数 名: Stop_SoftTimer ** 功能说明: 停止一个定时器,保存在链表中,但停止计数 ** [IN]: obj 停止计数的定时器对象 ** [return]: 无 **/ void Stop_SoftTimer(TimerEvent_t *obj) { rt_base_t level0; TimerEvent_t * pretimer = TimerListHeader; level0 = rt_hw_interrupt_disable(); if(TimerListHeader == NULL || obj == NULL) { rt_hw_interrupt_enable(level0); return; } while(pretimer != NULL) { if(pretimer == obj) { pretimer->isRun = false; break; } else { pretimer = pretimer->Next; } } rt_hw_interrupt_enable(level0); } void Delete_SoftTimer(TimerEvent_t *obj) { TimerEvent_t * pretimer = TimerListHeader; TimerEvent_t * curtimer = TimerListHeader->Next; if(obj == NULL) return; if(TimerListHeader == obj) //如果是表头 { TimerListHeader = curtimer; } else { while(curtimer == NULL) { if(curtimer == obj) { if(curtimer->Next == NULL) { curtimer = NULL; } else { pretimer->Next = curtimer->Next; } } else { pretimer=pretimer->Next; curtimer = curtimer->Next; } } } } /* ** 函 数 名: TimerSeverIRQHandler ** 功能说明: 软定时器中断处理函数 ** [IN]:无 ** [return]: 无 **/ void SoftTimerSever_IRQHandler(void) { TimerEvent_t * curtimer = TimerListHeader->Next; if(TimerListHeader == NULL) { return ; } else { if(TimerListHeader->isRun == true) { if(TimerListHeader->countval < Timer_tick_per_ms) { TimerListHeader->callback(); TimerListHeader->isRun = false; } else { TimerListHeader->countval -=Timer_tick_per_ms; } } while(curtimer != NULL) { if(curtimer->isRun ) { if(curtimer->countval < Timer_tick_per_ms) { curtimer->callback(); curtimer->isRun = false; curtimer = curtimer->Next; } else { curtimer->countval -= Timer_tick_per_ms; curtimer = curtimer->Next; } } else { curtimer = curtimer->Next; } } } } TimerTime_t TimerGetCurrentTime( void ) { return Timer_TimerGetCurrentTime(); } TimerTime_t TimerGetElapsedTime( TimerTime_t past ) { return Timer_TimerGetElapsedTime(past); } ``` 9、作品展示 完成后整体如下图所示: ![作品.jpg](https://oss-club.rt-thread.org/uploads/20220730/3e0567de50644641062e47f3cf041e70.jpg.webp "作品.jpg") 通讯成功图片: ![收发一体.png](https://oss-club.rt-thread.org/uploads/20220731/de30bc5aa2140032caa6145ff1f98c00.png "收发一体.png") 10、总结 这次的项目是第一次真正的使用RT-Thread Studio进行完整的开发,也是第一次基于瑞萨的芯片进行RT-Thread嵌入式操作系统开发,通过瑞萨的BSP配置工具和RT-Thread的丰富开源资料,能够快速的开发出产品。学习了瑞萨RA6M4的BSP配置和RT-Thread Studio开发。 代码地址:https://gitee.com/ensoulofthing/rt-thread_-lo-ra-r 作品展示:https://www.bilibili.com/video/BV1GU4y1v7VF/
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
我回来了
这家伙很懒,什么也没写!
文章
2
回答
0
被采纳
0
关注TA
发私信
相关文章
1
BBB的SPI驱动
2
求个SPI上挂两个或多个设备的使用例子
3
SPI设备有个bug
4
spi flash 的fatfs使用一段时间后读写文件出现故障
5
SPI驱动
6
请教rt_spi_configure函数理解
7
SPI FLASH挂载的问题
8
SPI-FLASH 文件系统 SPIFFS
9
求助一个完整的 spi flash 驱动
10
关于同时使用文件系统与SPI FLASH的问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部