四、基 本 输 入 / 输 出 函 数

本文详细介绍了C语言中的格式化输入输出函数printf()和scanf()的使用方法,包括各种转换字符的功能、特殊句柄的作用及示例代码。

/

 

l        l        printf( )

           printf(format_string,arg1,arg2,...argn);

 
    

 

转换字符的种类

 

(integer)

d

以十进制方式印出。

o

以八进位方式印出。

x

以十六进制方式印出。

u

以不带符号的十进制方式印出。

l

以长整数(long)方式印出。

浮点数(float)

f

xxx.xxxxxxx 方式印出。

e

以指数的方式印出。

字符(char)

c

以字符方式印出。

s

以字符串方式印出。

其它(other)

-

向左边靠齐印出

dd

指定字段宽。

.

分隔栏宽。

*.*

指定浮点数之精确度。

 

 

 

 

 

 

{
   int x=42;
   float y=12.345;
   char c='A',c1[4]="ABC";
   printf("/%d//n",x);
   printf("/%15d//n/%-15d//n",x,x);
   printf("/%f  %4.3f//n",y,y);
   printf("/%08.4f//n",y);
   printf("/%0*.*f//n",8,4,y);
   printf("/%c  %s//n",c,c1);
  }
Result
         
             
 
  /42/
    /            42/
    /42            /
    /12.3450000   12.345/
    /012.3450/
    /012.3450/
    /A  ABC/ 

 

 

表二.特殊句柄

 

/n

newline 新行。

/r

carriage return 回归键。

/t

tab 跳格。

/b

backspace 退位。

/f

form feed 跳页。

//

backslash 反斜线。

/'

single quote 单引号。

/"

double quote 双引号。

 

 

 

 

 

l        l        scanf( )

         scanf(format_string,argptr1,argptr2,...argptrn);
     
 
    
 
    

         main(  )
     {
      int x;
      float y;
      char z;
      scanf("%d %f %c",&x,&y,&z);
      printf("%d %f %c/n",x,y,z);
       scanf("%3d %4f",&x,&y);
       printf("%d %f/n",x,y);
   }


Result


24 12.45 G  -----> 此处为键盘输入之资料

24 12.4500001 G

123 4567890  ----> 此处为键盘输入之资料

123 4567.00000   

  

 

 

 

                  

      

 

 

 

 
#include "stm32f10x.h" #include "Delay.h" #include "OLED.h" #include "Encoder.h" #include "Motor.h" #include "Key.h" #include "MPU6050.h" #include "AD.h" #include "IC.h" #include "PWM.h" // 全局变量 uint8_t Page = 0; // 当前页面 (0: Motor, 1: MPU6050, 2: PWM, 3: Capture) int8_t Speed = 0; // 电机速度百分比 (0-100) uint8_t M_step = 10; // 编码器调节步长 (10 或 20) uint16_t ADValue; // ADC 原始值 float Voltage; // 电位器电压 uint32_t MeasuredFreq; // 捕获测得频率 uint32_t Freq; // PWM频率 uint8_t Duty; // PWM占空比 uint32_t CaptureFreq; // 捕获频率 int16_t AX, AY, AZ, GX, GY, GZ; uint8_t KeyNum; // 声明 uint8_t MPU6050_CheckFlip(void); void CalculatePWMFreq(void); void UpdateCaptureFreq(void); int main(void) { uint8_t flipped = 0; // 翻转状态标志 // 初始化各模块 OLED_Init(); Encoder_Init(); Motor_Init(); Key_Init(); MPU6050_Init(); PWM_Init(); AD_Init(); IC_Init(); OLED_Clear(); // 清屏 while (1) { // 获取按键状态 KeyNum = Key_GetNum(); // // === MPU6050 检测是否翻转 === // MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); // // 判断是否显著翻转 // if (MPU6050_CheckFlip() == 1) { // if (!flipped) { // Motor_SetSpeed(0); // 停止电机 // flipped = 1; // } // OLED_Clear(); // OLED_ShowString(6, 1, "TIPPED! "); // continue; // 锁定系统,不再响应其他 // } // flipped = 0; // === 旋转编码器处理 === int16_t enc = Encoder_Get(); if (enc != 0 ) { if(enc > 0 ) { Speed += M_step ; } else { Speed -= M_step ; } if (Speed > 100) Speed = 100; if (Speed < 0) Speed = 0; Motor_SetSpeed(Speed); Duty = Speed; } // 读取电位器并设置 PWM 频率 === ADValue = AD_GetValue(); Voltage = (float)ADValue / 4095 * 3.3; // 电位器控制PWM频率(1000-10000Hz) - 修正版 uint16_t targetFreq; if (Voltage >= 3.0) { targetFreq = 10000; // 3.0-3.3V对应10000Hz } else { // 0-3V对应1000-10000Hz线性变化 targetFreq = 1000 + (uint16_t)(Voltage / 3.0 * 9000); } // 计算对应的PSC值 (ARR=99) uint16_t psc = 72000000 / (targetFreq * 100) - 1; PWM_SetCompare2(Duty); if(targetFreq != Freq) { PWM_SetPrescaler(psc); } // 计算当前PWM频率 CalculatePWMFreq(); CaptureFreq = IC_GetFreq(); // 假设IC模块提供获取频率的 // 翻页控制 (第一个按键) if (KeyNum == 1) { Page = (Page + 1) % 4; // 个页面循环 OLED_Clear(); } // 据增加控制 (第二个按键) if (KeyNum == 2) { if (Page == 2) { // PWM页面控制占空比 Duty+=M_step; if (Duty > 100) Duty = 100; PWM_SetCompare2(Duty); Speed = Duty; Motor_SetSpeed(Speed); } } // 据减少控制 (第三个按键) if (KeyNum == 3) { if (Page == 2) { // PWM页面控制占空比 Duty-=M_step; if (Duty < 0)Duty=0; PWM_SetCompare2(Duty); Speed = Duty; Motor_SetSpeed(Speed); } } // 步长切换控制 (第个按键) if (KeyNum == 4) { if(M_step==10) { M_step=20; } else { M_step=10; } } // OLED显示控制 switch(Page) { case 0: // 电机状态界面 OLED_ShowString(1, 1, "Motor:"); OLED_ShowString(1, 8, "r:"); OLED_ShowNum(1, 10, Speed, 3); OLED_ShowChar(1, 13, &#39;%&#39;); OLED_ShowString(2, 1, "M_step:"); OLED_ShowNum(2, 8, M_step, 2); OLED_ShowChar(2, 11, &#39;%&#39;); break; case 1: // 陀螺仪状态界面 OLED_ShowString(1, 1, "GYRO:"); OLED_ShowString(2, 1, "X:"); OLED_ShowSignedNum(2, 3, GX, 6); OLED_ShowString(3, 1, "Y:"); OLED_ShowSignedNum(3, 3, GY, 6); OLED_ShowString(4, 1, "Z:"); OLED_ShowSignedNum(4, 3, GZ, 6); break; case 2: // PWM状态界面 OLED_ShowString(1, 1, "PWM:"); OLED_ShowString(1, 6, "F:"); OLED_ShowNum(1, 8, Freq, 5); OLED_ShowString(1, 13, "Hz"); OLED_ShowString(2, 1, "CD:"); OLED_ShowNum(2, 4, Duty, 3); OLED_ShowChar(2, 7, &#39;%&#39;); break; case 3: // 捕获状态界面 OLED_ShowString(1, 1, "CF:"); OLED_ShowNum(1, 4, CaptureFreq, 5); OLED_ShowString(1, 9, "Hz"); OLED_ShowString(2, 1, "Vol:"); OLED_ShowNum(2, 5, (uint16_t)Voltage, 1); OLED_ShowChar(2, 6, &#39;.&#39;); OLED_ShowNum(2, 7, (uint16_t)(Voltage*100)%100, 2); OLED_ShowChar(2, 9, &#39;V&#39;); break; } Delay_ms(10); // 主循环刷新率约100Hz } } // 计算PWM频率 void CalculatePWMFreq(void) { uint16_t psc = TIM2->PSC; // 直接访问寄存器 Freq = 72000000 / (psc + 1) / 100; // ARR=99 } // 检测MPU6050是否翻转 uint8_t MPU6050_CheckFlip(void) { static uint8_t flip_state = 0; // 0=正常, 1=已翻转锁定 if (AZ < -8000) { // 翻转判断阈值 flip_state = 1; } else if (AZ > 8000) { // 恢复正常阈值 flip_state = 0; } return flip_state; } #include "stm32f10x.h" // Device header /** * 捕获初始化 * 参 :无 * 返 回 值:无 */ void IC_Init(void) { /*开启时钟*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空,由外部信号决定 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉 /*配置时钟源*/ TIM_InternalClockConfig(TIM3); //选择TIM3为内部时钟,若不调用此,TIM默认也为内部时钟 /*时单元初始化*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参用于配置滤波器时钟,不影响时单元功能 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计器模式,选择向上计 TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //计周期,即ARR的值 TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //预分频器,即PSC的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计器,高级定时器才会用到 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时单元 /*PWMI模式初始化*/ TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量 TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择配置定时器通道1 TIM_ICInitStructure.TIM_ICFilter = 0xF; //滤波器参,可以过滤信号抖动 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //极性,选择为上升沿触发捕获 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //信号交叉,选择直通,不交叉 TIM_PWMIConfig(TIM3, &TIM_ICInitStructure); //将结构体变量交给TIM_PWMIConfig,配置TIM3捕获通道 //此同时会把另一个通道配置为相反的配置,实现PWMI模式 /*选择触发源及从模式*/ TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //触发源选择TI1FP1 TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位 //即TI1产生上升沿时,会触发CNT归零 /*TIM使能*/ TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行 } /** * :获取捕获的频率 * 参 :无 * 返 回 值:捕获得到的频率 */ uint32_t IC_GetFreq(void) { return 1000000 / (TIM_GetCapture1(TIM3) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可 } #include "stm32f10x.h" // Device header /** * :PWM初始化 * 参 :无 * 返 回 值:无 */ void PWM_Init(void) { /*开启时钟*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 // 使能AFIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA2引脚初始化为复用推挽 //受外设控制的引脚,均需要配置为复用模式 /*配置时钟源*/ TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此,TIM默认也为内部时钟 /*时单元初始化*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参用于配置滤波器时钟,不影响时单元功能 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计器模式,选择向上计 TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //计周期,即ARR的值 TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //预分频器,即PSC的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计器,高级定时器才会用到 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时单元 /*比较初始化*/ TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量 TIM_OCStructInit(&TIM_OCInitStructure); //结构体初始化,若结构体没有完整赋值 //则最好执行此,给结构体所有成员都赋一个默认值 //避免结构体初值不确定的问题 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //比较模式,选择PWM模式1 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //极性,选择为高,若选择极性为低,则高低电平取反 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能 TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值 TIM_OC3Init(TIM2, &TIM_OCInitStructure); TIM_OC2Init(TIM2, &TIM_OCInitStructure); /*TIM使能*/ TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行 } /** * :PWM设置CCR * 参 :Compare 要写的CCR的值,范围:0~100 * 返 回 值:无 * 注意事项:CCR和ARR共同决定占空比,此仅设置CCR的值,并不直接是占空比 * 占空比Duty = CCR / (ARR + 1) */ void PWM_SetCompare3(uint16_t Compare) { TIM_SetCompare3(TIM2, Compare); //设置CCR3的值 } void PWM_SetCompare2(uint16_t Compare) { TIM_SetCompare2(TIM2, Compare); //设置CCR3的值 } /** * :PWM设置PSC * 参 :Prescaler 要写的PSC的值,范围:0~65535 * 返 回 值:无 * 注意事项:PSC和ARR共同决定频率,此仅设置PSC的值,并不直接是频率 * 频率Freq = CK_PSC / (PSC + 1) / (ARR + 1) */ void PWM_SetPrescaler(uint16_t Prescaler) { TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate); //设置PSC的值 } 通过电位器控制 pwm 的频率,频率范围:1000-10000Hz 在 0-3V 线性变化,3-3.3V 无变化。 利用捕获功能获得任意一个 pwm 口的频率 CF。增加第个页面,显示捕 获得到的频率,以及电位器的电压 Vol。代码无法满足上述功能,频率CF固定为50Hz,没有发生线性变化,如何修改代码
最新发布
09-29
完成一个仓储环境监测的模拟系统。具体要求如下: 1. DHT11模块完成温湿度测量,5s更新,值显示于码管,同时打印到串口。温度超过阈值触发蜂鸣器短鸣,同时启动风扇小马达; 2. LDR模块完成环境照度测量,4s更新,值显示于码管,同时打印到串口。照度超过阈值启动PWM调光(高、中、低三级调光)。 3. 按键循环切换显示模式,按第1下固定显示温度,按第二下固定显示照度,按第三下恢复默认的自动显示模式。按键用外部中断实现 int main(void) { systick_init(72); led_init(); usart1_init(115200); display_init(); tim4_init(1000,72); // 1ms的定时间隔 TIM3_CH2_PWM_Init(500-1,72-1); dht11_init(); ldr_init(); //key_init(); while(1) { // 轮询DHT11的状态 if(dht11.state == WORK) { if(dht11.getData(&dht11.temp, &dht11.humi) == SUCCESS) // DHT11温湿度据更新 { printf("temperature:%d℃ humidity(RH)%d \r\n", dht11.temp, dht11.humi); dsp.interface = TEMP; dht11.onTempChange(dht11.temp_threshold); // 温度超过阈值的处理 } dht11.state = STANDBY; } // 轮询LDR的状态 if(ldr.state == WORK) { ldr.light = ldr.getData(); printf("light intensity:%d \r\n", ldr.light); dsp.interface = LIGHT; ldr.onLightChange(ldr.high_threshold,ldr.low_threshold); // 照度超过阈值的处理 ldr.state = STANDBY; } // 轮询码管界面状态 switch(dsp.interface) { case LIGHT: setLightValue(&dsp,1); break; case TEMP: setTempValue(&dsp,1); break; case HUMI: setHumiValue(&dsp,1); break; default: break; } } }#include "systick.h" static u8 fac_us=0; //us延时倍乘 static u16 fac_ms=0; //ms延时倍乘 //初始化延迟 //SYSTICK的时钟固定为AHB时钟的1/8 //SYSCLK:系统时钟频率 void systick_init(u8 SYSCLK) { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_us = SYSCLK/8; fac_ms = (u16)fac_us * 1000; } //延时nus //nus为要延时的us. void delay_us(u32 nus) { u32 temp; SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计器 SysTick->VAL =0X00; //清空计器 } //延时nms //注意nms的范围 //SysTick->LOAD为24位寄存器,所以,最大延时为: //nms<=0xffffff*8*1000/SYSCLK //SYSCLK单位为Hz,nms单位为ms //对72M条件下,nms<=1864 void delay_ms(u16 nms) { u32 temp; SysTick->LOAD = (u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit) SysTick->VAL = 0x00; //清空计器 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ; //开始倒 do { temp=SysTick->CTRL; } while((temp&0x01) && !(temp&(1<<16))); //等待时间到达 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计器 SysTick->VAL = 0x00; //清空计器 } #include "display.h" DisplayStruct dsp; void segDisplay(void); // 共阴极码管,阴极位选(0有效),阳极段选(1有效) const u16 segCode[11] = { /* 0 1 2 3 4 5 6 7 */ 0x003F, 0x0006, 0x005B, 0x004F, 0x0066, 0x006D, 0x007D, 0x0007, /* 8 9 off */ 0x007F, 0x006F, 0x0000 }; /******************************************************************************* * @brief 初始化码管用到的GPIO端口和dsp结构体 * @param None * @retval None *******************************************************************************/ void display_init(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE); // PA0~PA7: 段选(分别连接引脚A~G); // PB12~PB15:位选(分别连接引脚D1~D4) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); dsp.currDigit = 1; // dsp结构体的初始化 dsp.SegDisplay = segDisplay; } /*********************************************************************************** * @brief 设置码管要显示的4值。 * @param dsp:码管结构体指针;value[4]:4值;dotBit:小点位置(没有小点则置0) * @retval None ***********************************************************************************/ void setValue(DisplayStruct *dsp, u8 value[4], u8 dotBit) { dsp->digit[0] = value[0]; dsp->digit[1] = value[1]; dsp->digit[2] = value[2]; dsp->digit[3] = value[3]; dsp->dotDigit = dotBit; } void setLightValue(DisplayStruct *dsp, u8 dotBit) { dsp->digit[0] = 1; // 参编号 dsp->digit[1] = 10; // 第二位空缺 dsp->digit[2] = ldr.light/10; dsp->digit[3] = ldr.light%10; // dsp->dotDigit = dotBit; // 小点位置 } void setTempValue(DisplayStruct *dsp, u8 dotBit) { dsp->digit[0] = 2; dsp->digit[1] = 10; dsp->digit[2] = dht11.temp/10; dsp->digit[3] = dht11.temp%10; dsp->dotDigit = dotBit; } void setHumiValue(DisplayStruct *dsp, u8 dotBit) { dsp->digit[0] = 3; dsp->digit[1] = 10; dsp->digit[2] = dht11.humi/10; dsp->digit[3] = dht11.humi%10; dsp->dotDigit = dotBit; } /******************************************************************************* * @简 介 段选,让某一位码管显示指定的字 * @ value:段码组的序号(0~9),代表要显示的字 * @retval * 备注:只操作PA的低8位,不要影响高8位。 ********************************************************************************/ void seg(uint8_t value) { if(value < sizeof(segCode)) // 避免组溢 { GPIOA->ODR &= 0xFF00; GPIOA->ODR |= segCode[value]; } } /******************************************************************************** * 简 介 位选,一次只能选一位。 * com:位(取值14,从左到右) * 返回值 无 * 备注:只操作PB的高4位(置0),不要影响其他位。 ********************************************************************************/ void com(uint8_t currDigit) { // 码管位选(0有效) GPIOB->ODR |= 0xF000; GPIOB->ODR &= ~(0x1000<<(currDigit-1)); } /******************************************************************************** * 简介 在码管上逐位显示其段码(由Timer中断服务调用) * 参 无 * 返回值 无 ********************************************************************************/ void segDisplay(void) { seg(10); // 消隐之前的显示内容 com(dsp.currDigit); // 位选,从COM1到COM4逐次移位,实现动态显示 if(dsp.currDigit != dsp.dotDigit) // 当前位不显示小点 seg(dsp.digit[dsp.currDigit-1]); else // 当前位要显示小点 { GPIO_Write(GPIOA, segCode[dsp.digit[dsp.currDigit-1]]| 0x0080); //GPIOA->ODR &= 0xFF00; //GPIOA->ODR |= (segCode[dsp.currDigit-1]|0x0080); } if(++dsp.currDigit > DSP_DIGIT) // 从当前位右移到下一位 dsp.currDigit = 1; } #include "usart.h" int fputc(int ch,FILE *p) //默认的,在使用printf时自动调用 { USART_SendData(USART1,(u8)ch); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); return ch; } //串口1中断服务程序 //注意,读取USARTx->SR能避免莫名其妙的错误 u8 USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. //接收状态 //bit15, 接收完成标志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字节目 u16 USART1_RX_STA=0; //接收状态标记 /******************************************************************************* * 名 : USART1_Init * 功能 : USART1初始化 * : bound:波特率 * : 无 *******************************************************************************/ void usart1_init(u32 baud) { //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); /* 配置GPIO的模式和IO口 */ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //串口TX:PA9 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽 GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; //串口RX:PA10 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟 GPIO_Init(GPIOA,&GPIO_InitStructure); //USART1 初始化设置 USART_InitStructure.USART_BaudRate = baud; //波特率设置 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口1 USART_Cmd(USART1, ENABLE); //使能串口1 USART_ClearFlag(USART1, USART_FLAG_TC); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参初始化VIC寄存器、 } /******************************************************************************* * 名 : USART1_IRQHandler * 功能 : USART1中断 * : 无 * : 无 *******************************************************************************/ void USART1_IRQHandler(void) //串口1中断服务程序 { u8 r; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 { r =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的据 if((USART1_RX_STA&0x8000)==0)//接收未完成 { if(USART1_RX_STA&0x4000)//接收到了0x0d { if(r!=0x0a)USART1_RX_STA=0;//接收错误,重新开始 else USART1_RX_STA|=0x8000; //接收完成了 } else //还没收到0X0D { if(r==0x0d)USART1_RX_STA|=0x4000; else { USART1_RX_BUF[USART1_RX_STA&0X3FFF]=r; USART1_RX_STA++; if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收据错误,重新开始接收 } } } } } #include "timer.h" #include "beep.h" volatile uint16_t beep_time; /******************************************************************************* * 名 : tim4_init * 功能 : TIM4初始化 * : per:重装载值 psc:分频系 * : 无 *******************************************************************************/ void tim4_init(u16 per,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); // 使能TIM4的时钟 // 配置TIM的时 TIM_TimeBaseInitStructure.TIM_Period = per; // 自动装载值 TIM_TimeBaseInitStructure.TIM_Prescaler = psc; // 分频系 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置向上计模式 TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure); // 设置参生效 // 配置中断参 TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); // 开启定时器的更新中断 TIM_ClearITPendingBit(TIM4,TIM_IT_Update); // 中断标志位清零 // 配置NVIC NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级分组(组2) NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; // 定时器4的中断通道(30号通道) NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 响应优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 中断通道使能 NVIC_Init(&NVIC_InitStructure); // NVIC设置参生效 TIM_Cmd(TIM4,ENABLE); // 使能Timer4 } /******************************************************************************* * 名 : TIM4_IRQHandler * 功能 : TIM4中断 * : 无 * : 无 * 备 注 : 定时间隔1ms ** *****************************************************************************/ void TIM4_IRQHandler(void) { static u16 cnt_dht11; static u16 cnt_ldr; // 蜂鸣器时间处理 if(beep_time > 0) { beep_time--; if(beep_time == 0) { BEEP_OFF(); } } if(TIM_GetITStatus(TIM4,TIM_IT_Update)) // 检查更新中断是否产生 { // 处理定时任务 if(++cnt_dht11 == dht11.period) { if(dht11.state == STANDBY) { dht11.state = WORK; // DHT11状态从STANDBY切换到WORK } cnt_dht11 = 0; } if(++cnt_ldr == ldr.period) { if (ldr.state == STANDBY) { ldr.state = WORK; // LDR状态从STANDBY切换到WORK } cnt_ldr = 0; } // 刷新码管显示 dsp.SegDisplay(); } TIM_ClearITPendingBit(TIM4,TIM_IT_Update); // 手动清除中断标志位 } #include "dht11.h" #include "relay.h" #include "beep.h" Dht11Struct dht11 = {0}; void SET_DHT11_IO_OUT(void); void SET_DHT11_IO_IN(void); void DHT11_RequestData(void); u8 DHT11_RespondRequest(void); u8 DHT11_Read_Data(u8 *temp, u8 *humi); void DHT11_OnTempChange(u8 temp_threshold); // DHT11_PIN初始化 // 返回值: 无 extern void dht11_init() { GPIO_InitTypeDef GPIO_InitStructure; // 据引脚初始化 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 据引脚 GPIO_InitStructure.GPIO_Pin = DHT11_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(DHT11_PORT, &GPIO_InitStructure); GPIO_SetBits(DHT11_PORT, DHT11_PIN); // 初始状态:高电平 // 结构体初始化 dht11.period = DHT11_PERIOD; dht11.temp_threshold = TEMP_THRESHOLD; dht11.getData = DHT11_Read_Data; // 获取温湿度据的 dht11.onTempChange = DHT11_OnTempChange; // 温度超过阈值的处理 // 做一次据请求测试,检查设备状态 DHT11_RequestData(); if (DHT11_RespondRequest() == SUCCESS) { dht11.state = STANDBY; printf("DHT11 Init OK!\r\n"); } else { dht11.state = FAIL; printf("DHT11 Check Error!\r\n"); } } // 复位DHT11,单片机向DHT11发起据采集请求 // 时序图的黑线部分 static void DHT11_RequestData() { SET_DHT11_IO_OUT(); // 据引脚配置为模式 DHT11_DQ_OUT = 0; delay_ms(20); // 低电平持续至少18ms DHT11_DQ_OUT = 1; delay_us(30); // 高电平持续20~40us } // DHT11响应单片机的据请求 // 返回值:SUCCESS or FAILURE static u8 DHT11_RespondRequest() { u8 retry=0; SET_DHT11_IO_IN(); // 据引脚设为模式,接收DHT11的响应 while (DHT11_DQ_IN && retry<100) // 等待由高变低 { retry++; if(retry >= 100) return FAILURE; // 等待时间过长,返回异常。 delay_us(1); } retry=0; while (!DHT11_DQ_IN && retry<100) // 低电平持续时间80us { retry++; if(retry >= 100) return FAILURE; // 低电平持续时间过长,返回异常。 delay_us(1); } return SUCCESS; } //DHT11模式配置 static void SET_DHT11_IO_OUT() { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = DHT11_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(DHT11_PORT,&GPIO_InitStructure); } //DHT11模式配置 static void SET_DHT11_IO_IN() { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = DHT11_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空模式,最小系统板和DHT11之间并未接上拉电阻 GPIO_Init(DHT11_PORT,&GPIO_InitStructure); } //从DHT11读取一位 //返回值:bit 1或0 //注:此处并未做高低电平持续时间异常的处理,因DHT11自身有校验和,即便读错1位也不会造成最终据的错误。 // 严格来讲,除了正常情况下返回“1”或“0”,还应增加异常情况的返回值(比如“2”)。 static u8 DHT11_Read_Bit(void) { u8 retry=0; while(DHT11_DQ_IN && retry<100) // 等待电平由高变低(低电平持续50us) { retry++; delay_us(1); } retry=0; while(!DHT11_DQ_IN && retry<100) // 等待电平由低变高 { retry++; delay_us(1); } delay_us(40); // 等待40us(高电平持续26~28us表示0,持续70us表示1) if(DHT11_DQ_IN) return 1; // 40us后如果仍为高电平,则表示读1;否则表示读0。 else return 0; } //从DHT11读取一个字节 //返回值:读到的8位据 static u8 DHT11_Read_Byte(void) { u8 i,byte; byte = 0; for (i=0;i<8;i++) { byte <<= 1; // 先前读取的据(不足8位)全部左移一位 byte |= DHT11_Read_Bit(); // 最低位填新读取的1位 } return byte; } //从DHT11读取一次完整的据 //temp:温度值(整,范围:0~50°) //humi:湿度值(整,范围:20%~90%) //返回值:0,正常; 1,失败 static u8 DHT11_Read_Data(u8 *temp, u8 *humi) { u8 buf[5]; u8 i; DHT11_RequestData(); if(DHT11_RespondRequest() == SUCCESS) { for(i=0;i<5;i++) // 读取5组共40位据 { buf[i] = DHT11_Read_Byte(); } if((buf[0]+buf[1]+buf[2]+buf[3]) == buf[4]) // 据校验 { *humi = buf[0]; // 湿度整部分 *temp = buf[2]; // 温度整部分 return SUCCESS; } } return FAILURE; } // 采集DHT11据并打印至串口 void dht11DataCollect() { u8 temp; u8 humi; if (DHT11_Read_Data(&temp, &humi) == SUCCESS) printf("temperature:%d℃ humidity(RH)%d \r\n", temp, humi); // 到串口(重定向) else printf("DHT11 data error! \r\n"); } // 温度阈值处理 void DHT11_OnTempChange(u8 temp_threshold) { if (dht11.temp > temp_threshold) { // 启动风扇(继电器低电平触发) Relay_Low(); // 蜂鸣器短鸣(100ms) BEEP_ON(); beep_time = 100; // 100ms } else { // 关闭风扇 Relay_High(); } } #include "ldr.h" LdrStruct ldr; static u8 getLightIntensity(void); static void OnLightChange(u8 hiLight, u8 loLight); /******************************************************************************* * 名 : adc_init * 功能 : ADC外设的初始化 * : 无 * : 无 *******************************************************************************/ static void adc_init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; // 1.打开相关外设的总线时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE); // 2.信号引脚的参配置 GPIO_InitStructure.GPIO_Pin=LDR_PIN; // 信号引脚:PB0 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; // 设置模拟模式 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz; // 设置传速率 GPIO_Init(LDR_PORT,&GPIO_InitStructure); // 3. 时钟降频(<14MHz) RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 分频因子6,时钟为72M/6=12MHz // 4. 初始化ADC参 ADC_InitStructure.ADC_Mode=ADC_Mode_Independent; //独立模式 ADC_InitStructure.ADC_ScanConvMode=DISABLE; //单次扫描 ADC_InitStructure.ADC_ContinuousConvMode=DISABLE; //单次转换 ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; //软件触发 ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //据右对齐 ADC_InitStructure.ADC_NbrOfChannel=1; //只有1个通道 ADC_Init(ADC1,&ADC_InitStructure); // 5. 使能ADC ADC_Cmd(ADC1,ENABLE); // 6. ADC校准 ADC_ResetCalibration(ADC1); //复位校准 while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); //开启并完成校准 while(ADC_GetCalibrationStatus(ADC1)); } // LDR的设备初始化 extern void ldr_init() { adc_init(); ldr.period = LDR_PERIOD; ldr.high_threshold = HI_THRESHOLD; ldr.low_threshold = LO_THRESHOLD; ldr.getData = getLightIntensity; ldr.onLightChange = OnLightChange; ldr.state = STANDBY; } /******************************************************************************* * 名 : GET_ADC_Value * 功能 : 获取通道ch的转换值,测量times次,取平均值 * : ch:通道编号,Rank:规则序列中的第几个转换,取值116; times:测量次 * : 通道ch的times次转换结果的平均值 *******************************************************************************/ static u16 Get_ADC_Value(u8 ch, u8 times) //获取ADC1通道ch的转换值 { u8 i; u32 Temp_val=0; ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_239Cycles5); for(i=0;i<times;i++) { ADC_SoftwareStartConvCmd(ADC1,ENABLE); // 开始转换 while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)); // 等待至单次转换结束 Temp_val += ADC_GetConversionValue(ADC1); delay_ms(10); } return Temp_val/times; } /******************************************************************************* * 名 : getLightIntensity * 功能 : 将ADC转换值解读为光照强度 * : 无 * : 光照照度值(0~100, 0:照度最低;100:照度最高) *******************************************************************************/ static u8 getLightIntensity(void) //通过ADC1 通道0的值获取亮度值 { u16 value = 0; u8 lightvalue = 0; value = Get_ADC_Value(ADC_Channel_8,20); lightvalue = 100 - (u16)(value/40.95); return lightvalue; } // 添加PWM控制LED功能 static void set_led_brightness(u8 level) { switch(level) { case 0: // 低档 TIM_SetCompare2(TIM3, 125); // 25%占空比 break; case 1: // 中档 TIM_SetCompare2(TIM3, 250); // 50%占空比 break; case 2: // 高档 TIM_SetCompare2(TIM3, 375); // 75%占空比 break; } } /******************************************************************************* * 名 : OnLightChange * 功能 : 照度超过阈值(高阈值和低阈值)的处理措施 * : hiLight-高阈值; loLight-低阈值 * : 无 *******************************************************************************/ static void OnLightChange(u8 hiLight, u8 loLight) { if (ldr.light > hiLight) { set_led_brightness(0);// 照度过高,降低亮度 } else if (ldr.light < loLight) { set_led_brightness(2); // 照度过低,提升亮度 } else // 正常范围 { set_led_brightness(1); // 中等亮度 } } #include "key.h" #include "pwm.h" /******************************************************************************* * 名 : TIM3_CH2_PWM_Init * 功能 : TIM3通道2 PWM初始化 * : per:重装载值 * psc:分频系 * : 无 *******************************************************************************/ void TIM3_CH2_PWM_Init(u16 per,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; /* 开启时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE); // PB5引脚启用复用模式 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); // 选择TIM3部分重映射 GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); /* PB5作为PWM的引脚 */ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽 GPIO_Init(GPIOB,&GPIO_InitStructure); // 初始化TIM3 TIM_TimeBaseInitStructure.TIM_Period=per; //自动装载值 TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计模式 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); // 设置TIM3_CH2的PWM模式,使能CH2,呈现PPT展示的PWM波形 // PWM1即mode1,先有效电平,再无效电平;PWM2即mode2则正好相反。 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; // 设置有效电平为低电平(此案例中低电平点亮D2) TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low; TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; TIM_OC2Init(TIM3,&TIM_OCInitStructure); //比较通道2初始化 TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能TIM3在 CCR2 上的预装载寄存器 TIM_Cmd(TIM3,ENABLE); //使能定时器 } #include "relay.h" void Relay_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStruct); //默认低电平 } void Relay_High(void) { GPIO_SetBits(GPIOA, GPIO_Pin_11); } void Relay_Low(void) { GPIO_ResetBits(GPIOA, GPIO_Pin_11); } #ifndef _RELAY_H_ #define _RELAY_H_ #include "main.h" #define RELAY_HIGH 1 #define RELAY_LOW 0 void Relay_Init(void); void Relay_High(void); void Relay_Low(void); #endif #ifndef __PWM_H #define __PWM_H #include "main.h" void TIM3_CH2_PWM_Init(u16 per,u16 psc); #endif #ifndef __LDR_H #define __LDR_H #include "main.h" #define LDR_PIN GPIO_Pin_0 #define LDR_PORT GPIOB #define LDR_PERIOD 4000 // ADC转换周期:4000ms #define HI_THRESHOLD 70 // 照度高阈值 #define LO_THRESHOLD 30 // 照度低阈值 typedef struct{ u8 light; // 光照强度(相对强度:0~100) u16 period; // 重复测量周期(s) u8 high_threshold; // 高亮阈值 u8 low_threshold; // 低亮阈值 SensorState state; // 工作状态 u8 (*getData)(void); // 获取光照强度据 void (*onLightChange)(u8 hiLight, u8 loLight); // 亮度变化的响应操作 } LdrStruct; void ldr_init(void); extern LdrStruct ldr; #endif #ifndef __DHT11_H #define __DHT11_H #include "main.h" #define DHT11_PIN GPIO_Pin_12 #define DHT11_PORT GPIOA #define DHT11_DQ_IN PCin(12) // #define DHT11_DQ_OUT PCout(12) // #define SUCCESS 0 #define FAILURE 1 #define DHT11_PERIOD 5000 // 据采集周期:5000ms #define TEMP_THRESHOLD 28 // 温度阈值(℃) // 定义DHT11结构体 typedef struct{ u8 temp; // 温度值 u8 humi; // 湿度值 u16 period; // 重复测量周期(s) u8 temp_threshold; // 温度阈值 u8 humi_threshold; // 湿度阈值 SensorState state; // 设备状态 u8 (*getData)(u8 *temp, u8 *humi); // 获取温湿度据 void (*onTempChange)(u8 temp_threshold); // 高温的响应操作 } Dht11Struct; void dht11_init(void); extern Dht11Struct dht11; #endif #ifndef _usart_H #define _usart_H #include "main.h" #define USART1_REC_LEN 200 //定义最大接收字节 200 extern u8 USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 extern u16 USART1_RX_STA; //接收状态标记 void usart1_init(u32 baud); #endif #ifndef __TIM_H #define __TIM_H #include "main.h" //#include "stm32f10x.h" void tim4_init(u16 per,u16 psc); #endif #ifndef __DISPLAY_H #define __DISPLAY_H #include "main.h" #define DSP_DIGIT 4 // 4码管 typedef enum{LIGHT, TEMP, HUMI} DSP_Interface; // 码管界面 typedef struct { u16 digit[DSP_DIGIT]; // 码管的4字 u8 currDigit; // 当前显示位(14) u8 dotDigit; // 小点位置(0~4,0代表不显示小点) DSP_Interface interface; // 界面显示哪个测量值 void(*SegDisplay)(void); }DisplayStruct; void display_init(void); void segDisplay(void); //void setValue(DisplayStruct *dsp, u8 value[4], u8 dotBit); void setLightValue(DisplayStruct *dsp, u8 dotBit); void setTempValue(DisplayStruct *dsp, u8 dotBit); void setHumiValue(DisplayStruct *dsp, u8 dotBit); extern DisplayStruct dsp; #endif 在已有代码上完善该任务,STM32F103C8t6,用标准库
06-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值