两块STM32之间 SPI DMA通信

@[两块STM32之间 SPI DMA通信]

这里讲的是两块STM32F407板子的互相通讯,折腾了3天,终于比较清楚了,特此记录。
两块板子互相通讯,除了SPI的接线,肯定还有其他信号线,必须注意这些信号线的上拉下拉,输出口,最好配置为上拉,输入口,直接不拉。
我遇到,一边输入口,一边输出口,2边都配成下拉,接口当输出口输出时,电压只有2.01V,然后通信通不起,把我整了很久。其实想想原因很简单,当输出时,内部电路给电,然后两边的下拉电阻相当于并联对地(下拉电阻相当于减小一般),然后STM32内部向上驱动力不足,导致对方检测不到输入。所以看来STM32,能用上拉电阻就用上拉,使用时向下的驱动强些。

下面进入正题,直接上代码,都是在用的。
首先,硬件连接方式,
----- 主机 --------------------------------------从机
NSS(PB12) --------------------------NSS(PB12)
SPI_SCK ---------------------------------SPI_SCK
SPI_MISO--------------------------------SPI_MISO
SPI_MOSI---------------------------------SPI_MOSI
PDin(6)-------------------<-----------PCout(0) //含义 1 = 从机初始化完成了,开始吧。
PDout(10)-------------->--------------PCin(4) //含义 1 = 主机要发送主机了

//NSS引脚,其实可以不用。我已经测试,在用的时候和不用的时候,实际没有区别。

先上从机代码
//从机SPI初始化
void SPI2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//使能SPI2时钟
//GPIOFB13,14,15初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14|GPIO_Pin_15;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ;//PB12复用NSS
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
//GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
//GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//
//GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
//GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_SPI2); //PB13复用为 SPI2 (这里是注释点了的,注释过后,其实NSS引脚也就没用了)
GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2); //PB13复用为 SPI2
GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_SPI2); //PB14复用为 SPI2
GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2); //PB15复用为 SPI2
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave ; //设置SPI工作模式:设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //设置SPI的数据大小:SPI发送接收16位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为低电平,这个高低电平,必须配合时钟线来,我这里时钟线是PB13,是采用的下拉
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样,这个跳变沿,和主机一样。
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制,这个选为软件,硬件,似乎,没有什么影响。网上有人说,还是选为硬件,他的解释是,要通信的时候,主机先发送片选命令,就是NSS引脚置低(当为从机时,NSS引脚拉低就表示选中了这个芯片,当为主机时,NSS引脚拉高就表示选中了这个芯片),这样让从机提前一点点知道要发送数据了,SPI外设就自动提前准备好,表面上一看比较有道理。我的理解是,如果设置为NSS软件管理,那么这个芯片是否被片选,就由SPI_InitStructure.SPI_Mode = SPI_Mode_Slave 这个来决定的,那么说白了,芯片只要一初始化,SPI外设就随时都初始化好了,只要在至收发数据期间,没有干扰,就没为题。所以,这个选为硬件,不是那么必要。
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //定义波特率预分频的值:波特率预分频值为4
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx ,ENABLE); //使能SPI2 DMA功能
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
}

//从机SPI读写函数
//SPI2 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个byte 数据
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
}

// 从机 DMAx的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx:DMA数据流,DMA1_Stream07/DMA2_Stream07
//chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:存储器地址
//ndtr:数据传输量
//Ddtd:存储器到外设模式DMA_DIR_MemoryToPeripheral ,外设到存储器DMA_DIR_PeripheralToMemory,
// DMA_DIR_MemoryToMemory 内存到内存
void MYDMA_Config(DMA_Stream_TypeDef DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr,u32 Ddtd)(这是个通用函数,主机也是这个函数,只是参数不一样
{
DMA_InitTypeDef DMA_InitStructure;
if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
}else
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
}
DMA_DeInit(DMA_Streamx);
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置
/
配置 DMA Stream */
DMA_InitStructure.DMA_Channel = chx; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器0地址
DMA_InitStructure.DMA_DIR = Ddtd;//存储器到外设模式DMA_DIR_MemoryToPeripheral ,外设到存储器DMA_DIR_PeripheralToMemory
DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据长度:16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//存储器数据长度:16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化DMA Stream
}

// 从机 开启一次DMA传输
//DMA_Streamx:DMA数据流,DMA1_Stream07/DMA2_Stream07
//ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)(这是个通用函数,主机也是这个函数,只是参数不一样
{
DMA_Cmd(DMA_Streamx, DISABLE); //关闭DMA传输
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量
DMA_Cmd(DMA_Streamx, ENABLE); //开启DMA传输
}

//从机 接收数据的函数,主函数调用就行
u32 DataDranInit_Check(void)
{
u16 l;
SPI2_Init(); //第一步,SPI初始化,SPI已使能
MYDMA_Config(DMA1_Stream3,DMA_Channel_0,(u32)&SPI2->DR,(u32)test_8,10,DMA_DIR_PeripheralToMemory);
//Delay_ms(500);
l = SPI2->DR;//第三步,这一步相当重要,我们这个是从机的函数。
//从机的SPI设置为了从站模式,并且上面第一步已经使能,然后由于可能主机还在初始化,在其初始化过程中,有可能SPI时钟线会产生震荡,导致从机SPI->SP寄存器之RXNE位置1,最终导致下面DMA开启的时候发生错位。
//作为从机来讲,SPI外设与DMA外设的通信工作过程就是,SPI收到信号,产生RXNE标志位,DMA收到该标志位,就进行一次数据传输。
//第三部的操作,就是读SPI数据寄存器的值,将自动复位RXNE标志。相当于有些人说的对SPI->DR的清零。

ReData:
DMA_ClearFlag(DMA1_Stream3, DMA_FLAG_TCIF3 | DMA_FLAG_HTIF3 | DMA_FLAG_TEIF3 | DMA_FLAG_DMEIF3 | DMA_FLAG_FEIF3 );//清除DMA的标志位,必须要清除这些标志位,才能重新对DMA进行使能,DMA才会重启。光关闭DMA使能,再开DMA使能,DMA是不会重启的。
PDout(0)=0; //告诉对方初始化完成
while(PDin(4)==1){} //等待对方的信号
MYDMA_Enable(DMA1_Stream3,10); //检测到响应数据,转SPI DMA,接收数据
while(DMA1_Stream3->NDTR){} //等待传输完成
PDout(0)=1; //复位初始化信号
for(i=0; i<10; i++)
{ printf("\n–%d—%d----%d—\n",i,test_8[i],DMA1_Stream3->NDTR);}
Delay_ms(30);
goto ReData; }//循环采集数据。这个只是测试程序,所以这样做的,个人根据实际情况修改

下面是主机的函数
//主机 SPI初始化
void SPI2_Tx_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//使能SPI2时钟
//GPIOFB13,14,15初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14|GPIO_Pin_15;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ;//
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ;//
//GPIO_InitStructure.GPIO_OType = GPIO_OType_PP ;//推挽输出
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz ;//100MHz
//GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;//上拉
//GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2); //PB13复用为 SPI2
GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_SPI2); //PB14复用为 SPI2
GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2); //PB15复用为 SPI2
//这里只针对SPI口初始化
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master ; //设置SPI工作模式:设置为从SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //设置SPI的数据大小:SPI发送接收16位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //定义波特率预分频的值:波特率预分频值为4
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE); //使能SPI2 DMA功能
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
}
//主机 SPI2 读写一个字节(和从机是一样的
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个byte 数据
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
}

//主机 DMAx的各通道配置 (和从机一样的
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx:DMA数据流,DMA1_Stream07/DMA2_Stream07
//chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:存储器地址
//ndtr:数据传输量
//Ddtd:存储器到外设模式DMA_DIR_MemoryToPeripheral ,外设到存储器DMA_DIR_PeripheralToMemory,
// DMA_DIR_MemoryToMemory 内存到内存
void MYDMA_Config(DMA_Stream_TypeDef DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr,u32 Ddtd)
{
DMA_InitTypeDef DMA_InitStructure;
if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
}else
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
}
DMA_DeInit(DMA_Streamx);
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置
/
配置 DMA Stream */
DMA_InitStructure.DMA_Channel = chx; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = par; //DMA外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = mar; //DMA 存储器0地址
DMA_InitStructure.DMA_DIR = Ddtd; //存储器到外设模式DMA_DIR_MemoryToPeripheral ,
//外设到存储器DMA_DIR_PeripheralToMemory
DMA_InitStructure.DMA_BufferSize = ndtr; //数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据长度:16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据长度:16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //中等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输
DMA_Init(DMA_Streamx, &DMA_InitStructure); //初始化DMA Stream
}
//(主机)开启一次DMA传输(和从机一样的
//DMA_Streamx:DMA数据流,DMA1_Stream07/DMA2_Stream07
//ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
DMA_Cmd(DMA_Streamx, DISABLE); //关闭DMA传输
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量
DMA_Cmd(DMA_Streamx, ENABLE); //开启DMA传输
}

u16 test_8[37]={40960,53,46,288,72,6,97,8,91,130,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37}; //主机的 输入参数

//主机,SPI DMA发送数据的程序
void DataDranInit_Check(void)
{
SPI2_Tx_Init(); //使能SPI为从,开启SPI2发送数据的DMA,等待发送数据
//配置DMA为发送数据模式
MYDMA_Config(DMA1_Stream4,DMA_Channel_0,(u32)&SPI2->DR,(u32)test_8,10,DMA_DIR_MemoryToPeripheral);
//Delay_ms(200);
PBout(12)= 1 ;
PCout(10)=1; //告诉对方,可以读取了。
sendData:
if ( PCin(6) == 0 ) //等待对方初始化完成的信号
{
//Delay_us(10); //合理延时,等待对方响应
//这个延时可以不要,只是做测试时加在这的,我不删,是如果不行,给你提供的思路。。。
if(test_8[1]>32767)
test_8[1]=1;
test_8[1]++;//这只是让发送的数据变化,测试时,好知道传输没断,是一直在传而已。
DMA_ClearFlag(DMA1_Stream4, DMA_FLAG_TCIF4 | DMA_FLAG_HTIF4 | DMA_FLAG_TEIF4 | DMA_FLAG_DMEIF4 | DMA_FLAG_FEIF4 ); //必须清数据位,否则DMA根本不会重启。
PCout(10)=0; //告诉对方,主机准备好了,要发送开始数据了。
Delay_us(4); //这个延时很随意,酌情。。。
PBout(12)= 0 ; //使能NSS,片选从机,从机如果是Soft就不用这个了。
Delay_us(1); //这个延时很随意,酌情。。。
MYDMA_Enable(DMA1_Stream4,10); //使能发送数据
while(DMA_GetCurrDataCounter(DMA1_Stream4)){}
PCout(10)=1; //发送完毕,复位。
Delay_ms(1); //这个延时很随意,酌情。。。
PBout(12)= 1 ; //恢复片选
aafd = DMA_GetCurrDataCounter(DMA1_Stream4);
Delay_ms(1);
}
goto sendData; //
}

STM32 的SPI DMA通讯,也不算坑吧,用着还好。
另外,所谓的 CPOL CHPA,就是SPI的四种模式,建议用逻辑分析仪看下,根据清空设置就行了,我这的话,反正主从设置为一样,我觉得应该都是一样就行了。

  • 6
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值