1 最全STM32外设资料汇总-德赢Vwin官网 网

最全STM32外设资料汇总

电子常识

2647人已加入

描述

  stm32外设指的是单片机外部的外围功能模块,比如键盘控制芯片,液晶,A/D转换芯片,等等。外设可通过单片机的I/O,SPI,I2C等总线控制。

  常见stm32外设:基本IO、定时器TIM、串口USART、ADC模数转换、DAC数模转换、SPI串行通信、EXIT外部中断、BKP备份数据、RTC闹钟SysTick系统滴答、WDG看门狗(独立+窗口)、DMA传输数据、片内FLASH编程、FSMC读写外部SRAM、外部NorFlash操作等。

  stm32外设的初始化和设置:

  在设置一个外设前,必须调用以下一个函数来使能它的时钟

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_PPPx,ENABLE);

  1,在主应用文件中声明一个结构PPP_InitTypeDef,例如:PPP_InitTypeDef

  PPP_InitStructure;

  2,为变量PPP_InitStructure的各个结构成员填入允许的值。可以采用以下2种方式:

  a)PPP_InitStructure.member1=val1;

  b)PPP_StructInit(&PPP_InitStructure)PP_InitStructure.memberX=valX;

  3,调用函数PPP_Init()来初始化外设PPP;

  4,在这一步,外设PPP已被初始化。可以调用函数PPP_Cmd()来使能之。

  使用STM32外设的步骤

  1,PPP_Init()函数,其功能是根据PPP_InitTypeDef中指定的参数,初始化外设PPP,如TIM_Init()名为PPP_DeInit()函数,其功能为复位外设PPP的所有寄存器至缺省值。

  2,PPP_StructInit()函数,其功能为通过设置PPP_InitTypeDef结构中的各种参数来定义外设的功能,例如:USART_StructInit()

  3,PPP_Cmd()函数,其功能为使能或者失能外设PPP,例如:SPI_Cmd

  4,PPP_ITConfig()函数,其功能为使能或者失能来自外设PPP的中断源,例如:RCC_ITConfig

  5,PPP_DMAConfig()的函数,其功能为使能或者失能外PPP的DMA接口

  6,PPP_GetFlagStatus()的函数,其功能为检查外设PPP某标志位被设置与否

  7,PPP_ClearFlag()的函数,其功能为清除外设PPP标志位,例如:I2C_ClearFlag.

  8,PPP_GetITStatus()的函数,其功能为判断来自外设PPP的中断发生与否

 

  9,PPP_ClearITPendingBit()的函数,其功能为清除外设PPP中断待处理标志位

STM32外设使用要点

  1、时钟安全系统(CSS)

  时钟安全系统被激活后,时钟监控器将实时监控外部高速振荡器;如果HSE时钟发生故障,外部振荡器自动被关闭,产生时钟安全中断,该中断被连接到Cortex-M3的NMI的中断;同时CSS将内部RC振荡器切换为STM32的系统时钟源(对于STM32F103,时钟失效事件还将被送到高级定时器TIM1的刹车输入端,用以实现电机保护控制)。

  操作流程:

  1)、启动时钟安全系统CSS: RCC_ClockSecuritySystemCmd(ENABLE); (NMI中断是不可屏蔽的!)

  2)外部振荡器失效时,产生NMI中断,对应的中断程序:

  void NMIException(void)

  {

  if (RCC_GetITStatus(RCC_IT_CSS) ! RESET)

  { // HSE、PLL已被禁止(但是PLL设置未变)

  …… // 客户添加相应的系统保护代码处

  // 下面为HSE恢复后的预设置代码

  RCC_HSEConfig(RCC_HSE_ON); // 使能HSE

  RCC_ITConfig(RCC_IT_HSERDY, ENABLE); // 使能HSE就绪中断

  RCC_ITConfig(RCC_IT_PLLRDY, ENABLE); // 使能PLL就绪中断

  RCC_ClearITPendingBit(RCC_IT_CSS); // 清除时钟安全系统中断的挂起位

  // 至此,一旦HSE时钟恢复,将发生HSERDY中断,在RCC中断处理程序里, 系统时钟可以设置到以前的状态

  }

  }

  3)、在RCC的中断处理程序中,再对HSE和PLL进行相应的处理。

  注意:一旦CSS被激活,当HSE时钟出现故障时将产生CSS中断,同时自动产生 NMI。NMI将被不断执行,直到CSS中断挂起位被清除。因此,在NMI的处理程序中 必须通过设置时钟中断寄存器(RCC_CIR)里的CSSC位来清除CSS中断。

  2、SysTick工作原理

  Cortex-M3的内核中包含一个SysTick时钟。SysTick 为一个24位递减计数器,SysTick设定初值并使能后, 每经过1个系统时钟周期,计数值就减1。计数到0时, SysTick计数器自动重装初值并继续计数,同时内部的 COUNTFLAG标志会置位,触发中断(如果中断使能)。

  3、内部时钟输出PA.8(MCO)

  STM32的PA.8引脚具有复用功能——时钟输出(MCO), 该功能能将STM32内部的时钟通过PA.8输出。

  操作流程:

  1)、设置PA.8为复用Push-Pull模式。

  GPIO_InitStructure.GPIO_Pin GPIO_Pin_8;

  GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

  2)、选择输出时钟源。

  时钟的选择由时钟配置寄存器(RCC_CFGR)中的MCO[2:0]位控制。

  RCC_MCOConfig(RCC_MCO);

  参数RCC_MCO为要输出的内部时钟:

  RCC_MCO_NoClock --- 无时钟输出

  RCC_MCO_SYSCLK --- 输出系统时钟(SysCLK)

  RCC_MCO_HSI --- 输出内部高速8MHz的RC振荡器的时钟(HSI)

  RCC_MCO_HSE --- 输出高速外部时钟信号(HSE)

  RCC_MCO_PLLCLK_Div2 --- 输出PLL倍频后的二分频时钟(PLLCLK/2)

  注:由于STM32 GPIO输出管脚的最大响应频率为50MHz,如果输出频率超过50MHz,则输出的波形会失真。

  4、可编程电压监测器(PVD)

  STM32内部自带PVD功能,用于对MCU供电电压VDD进行监控。通过电源控制寄存器中的PLS[2:0]位可以用来设定监控电压的阀值,通过对外部电压进行比较来监控电源。当条件触发,需要系统进入特别保护状态,执行紧急关闭任务:对系统的一些数据保存起来,同时对外设进行相应的保护操作。

  操作流程:

  1)、系统启动后启动PVD,并开启相应的中断。

  PWR_PVDLevelConfig(PWR_PVDLevel_2V8); // 设定监控阀值

  PWR_PVDCmd(ENABLE); // 使能PVD

  EXTI_StructInit(&EXTI_InitStructure);

  EXTI_InitStructure.EXTI_Line EXTI_Line16; // PVD连接到中断线16上

  EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; //使用中断模式

  EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Raising;//电压低于阀值时产生中断

  EXTI_InitStructure.EXTI_LineCmd ENABLE; // 使能中断线

  EXTI_Init(&EXTI_InitStructure); // 初始

  EXTI_InitStructure.EXTI_Trigger的赋值可选项:

  EXTI_Trigger_Rising---表示电压从高下降到低于设定阀值时产生中断;

  EXTI_Trigger_Falling---表示电压从低上升到高于设定阀值时产生中断;

  EXTI_Trigger_Rising_Falling---表示电压上升或下降越过设定阀值时都产生中断。

  2)、当工作电压低于设定阀值时,将产生PVD中断,在中断程序中进行相应的处理:

  void PVD_IRQHandler(void)

  {

  EXTI_ClearITPendingBit(EXTI_Line16);

  …… // 用户添加紧急处理代码处

  }

  5、STM32上不使用外部晶振,OSC_IN和OSC_OUT的接法

  1)、对于100脚或144脚的产品,OSC_IN应接地,OSC_OUT应悬空。

  2)、对于少于100脚的产品,有2种接法:

  2.1)、OSC_IN和OSC_OUT分别通过10K电阻接地。

  此方法可提高EMC性能。

  2.2)、分别重映射OSC_IN和OSC_OUT至PD0和PD1,再配置PD0和PD1为推挽输出并输出‘0’。

  此方法可以减小功耗并(相对上面2.1),并节省2个外部电阻。

  Interrupt/Evens

  1、NVIC的优先级概念

  占先式优先级 (pre-emption priority):

  高占先式优先级的中断事件会打断当前的主程序/中断程序运行— —抢断式优先响应,俗称中断嵌套。

  副优先级(subpriority):

  在占先式优先级相同的情况下,高副优先级的中断优先被响应;

  在占先式优先级相同的情况下,如果有低副优先级中断正在执行, 高副优先级的中断要等待已被响应的低副优先级中断执行结束后才 能得到响应——非抢断式响应(不能嵌套)。

  2、判断中断是否会被响应的依据

  首先是占先式优先级,其次是副优先级;

  占先式优先级决定是否会有中断嵌套;

  Reset、NMI、Hard Fault 优先级为负(高于普通中断优先级)且不可调整。

  3、STM32中用到的Cortex-M3寄存器说明

  在STM32中用到了Cortex-M3定义的三组寄存器,有关这三组寄存器的说明不在STM32的技术手册中,需要参考ARM公司发布的Cortex-M3 Technical Reference Manual (r2p0)。

  在STM32的固件库中定义了三个结构体与这三个寄存器组相对应,这三个结构体与ARM手册中寄存器的对应关系如下:

  1)、NVIC寄存器组

  STM32的固件库中有如下定义:

  typedef struct

  {

  vu32 ISER[2];

  u32 RESERVED0[30];

  vu32 ICER[2];

  u32 RSERVED1[30];

  vu32 ISPR[2];

  u32 RESERVED2[30];

  vu32 ICPR[2];

  u32 RESERVED3[30];

  vu32 IABR[2];

  u32 RESERVED4[62];

  vu32 IPR[11];

  } NVIC_TypeDef;

  它们对应ARM手册中的名称为

  ISER Interrupt Set-Enable Registers

  ICER Interrupt Clear-Enable Registers

  ISPR Interrupt Set-Pending Register

  ICPR Interrupt Clear-Pending Register

  IABR Active Bit Register

  IPR Interrupt Priority Registers

  每个寄存器有240位,以Interrupt Set-Enable Registers说明,ISER[0]对应中断源0~31,ISER[1]对应中断源32~63,STM32只有60个中断源,所以没有ISER[2:7]。

  参考STM32技术参考手册中的中断向量表,中断源的位置为:

  位置0 - WWDG Window Watchdog interrupt

  位置1 - PVD PVD through EXTI Line detection interrupt

  位置2 - TAMPER Tamper interrupt

  位置58 - DMA2_Channel3 DMA2 Channel3 global interrupt

  位置59 - DMA2_Channel4_5 DMA2 Channel4 and DMA2 Channel5 global interrupts

  2)、系统控制寄存器组

  STM32的固件库中有如下定义:

  typedef struct

  {

  vuc32 CPUID;

  vu32 ICSR;

  vu32 VTOR;

  vu32 AIRCR;

  vu32 SCR;

  vu32 CCR;

  vu32 SHPR[3];

  vu32 SHCSR;

  vu32 CFSR;

  vu32 HFSR;

  vu32 DFSR;

  vu32 MMFAR;

  vu32 BFAR;

  vu32 AFSR;

  } SCB_TypeDef; /* System Control Block Structure */

  它们对应ARM手册中的名称为

  CPUID CPUID Base Register

  ICSR Interrupt Control State Register

  VTOR Vector Table Offset Register

  AIRCR Application Interrupt/Reset Control Register

  SCR System Control Register

  CCR Configuration Control Register

  SHPR System Handlers Priority Register

  SHCSR System Handler Control and State Register

  CFSR Configurable Fault Status Registers

  HFSR Hard Fault Status Register

  DFSR Debug Fault Status Register

  MMFAR Mem Manage Address Register

  BFAR Bus Fault Address Register

  AFSR Auxiliary Fault Status Register

  3)、系统时钟寄存器组

  STM32的固件库中有如下定义:

  typedef struct

  {

  vu32 CTRL;

  vu32 LOAD;

  vu32 VAL;

  vuc32 CALIB;

  } SysTick_TypeDef;

  它们对应ARM手册中的名称为

  CTRL SysTick Control and Status Register

  LOAD SysTick Reload Value Register

  VAL SysTick Current Value Register

  CALIB SysTick Calibration Value Register

  DMA

  1、DMA普通模式和循环模式的区别

  循环模式:用于处理一个环形的缓冲区,每轮传输结束时数据传输 的配置会自动地更新为初始状态,DMA传输会连续不断地进行。

  普通模式:在DMA传输结束时,DMA通道被自动关闭,进一步的 DMA请求将不被满足。

  2、DMA传输需要指定的条件:

  传输源:DMA控制器从传输源读出数据;

  传输目标:DMA控制器将数据传输的目标;

  触发信号:用于触发一次数据传输的动作,执行一个单位的传输源至传输目标的数据传输。可以用来控制传输的启动条件。

  ADC

  1、STM32的内部温度传感器

  STM32内部温度传感器与ADC的通道16相连,与ADC配 合使用实现温度测量。测量范围–40~125℃,精度 ± 1.5℃

  操作流程:

  1)、设置ADC相关参数

  // ADC1 configuration -----------------------------

  ADC_InitStructure.ADC_Mode ADC_Mode_Independent;

  ADC_InitStructure.ADC_ScanConvMode ENABLE;

  ADC_InitStructure.ADC_ContinuousConvMode ENABLE;

  ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None;

  ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfChannel 1;

  ADC_Init(ADC1, &ADC_InitStructure);

  2)、选中ADC1的通道16作为输入,设置采样时间17.1us ( Ncycle × tADC 17.1靤 )。

  // ADC1 regular channel16 Temp Sensor configuration

  ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_55Cycles5);

  3)、设置寄存器ADC_CR2中的TSVREFE位激活温度传感器

  // Enable the temperature sensor and vref internal channel

  ADC_TempSensorVrefintCmd(ENABLE);

  4)、转换采样值为温度

  ADC转换结束以后,读取ADC_DR寄存器中的结果,转换温度值计算公式如下:

  V25 - VSENSE

  T(℃) ------------ + 25

  Avg_Slope

  V25: 温度传感器在25℃时 的输出电压,典型值1.43 V。

  VSENSE:温度传感器的当前输出电压,与ADC_DR 寄存器中的结果ADC_ConvertedValue之间的转换关系为:

  ADC_ConvertedValue * Vdd

  VSENSE --------------------------

  Vdd_convert_value(0xFFF)

  Avg_Slope:温度传感器输出电压和温度的关联参数,典型值4.3 mV/℃。

  //Converted Temperature

  Vtemp_sensor ADC_ConvertedValue * Vdd / Vdd_convert_value;

  Current_Temp (V25 - Vtemp_sensor)/Avg_Slope + 25;

  2、VDDA的电压范围

  STM32的数据手册规定,VDD与VDDA之间的压差不能大于300mV。ADC的工作电压范围在2.4V~3.6V,供电电压VDD范围在2.0V~3.6V.

  USB

  1、STM32的USB中断说明

  STM32的USB模块可产生三种中断:USB唤醒中断、USB高优先级中断和USB低优先级中断,这三种中断对应事件如下:

  1)、USB唤醒中断 - 在中断向量表中的位置是42

  这个中断在USB设备从暂停模式唤醒时产生,唤醒事件由USB_ISTR寄存器的WKUP位标识。

  2)、USB高优先级中断 - 在中断向量表中的位置是19

  这个中断仅由USB同步(Isochronous)模式传输或双缓冲块(Bulk)传输模式下的正确传输事件产生,正确传输事件由USB_ISTR寄存器的CTR位标识。

  3)、USB低优先级中断 - 在中断向量表中的位置是20

  这个中断由所有其它的USB事件产生,例如正确传输(不包括同步模式和双缓冲块模式)、USB复位等,事件标志位在USB_ISTR寄存器中。

  在STM提供的STM32 USB 开发包中的例程包含了上述三种中断的处理方法。例如在USB Speaker例程中,CTR_HP函数处理USB高优先级中断;在所有例子中都有USB_Istr()函数处理USB低优先级中断。

  bxCAN

  1、CAN波特率的设定计数

  Etherne

  SPI

  1、SPI外设的NSS引脚设置为通用IO口

  由于SPI外设的SPI_CR1寄存器中SSM置1时,NSS引脚可被被释放用于GPIO使用,因此无论是在SPI的主模式或是从模式下均可以将NSS引脚释放,由软件或硬件进行NSS管理;

  操作流程:

  1)、初始化SPI外设,设置NSS由软件管理:

  SPI_InitStructure.SPI_NSS SPI_NSS_Soft;

  2)、如果NSS引脚用于其他外设时,需要使能NSS输出:

  SPI_SSOutputCmd(SPIx, ENABLE);

  2、SPI 单线传输

  此模式下限制:只能用作输入或者输出,或者工作在半双工模式下。

  I2C

  USART

  Device Signature

  1、 STM32F10xxx系列MCU内部含有一个出厂被固化的96bit唯一识别ID,该ID可以用于芯片加密、设备识别等一类特殊应用。

  读取该ID的方法:

  u32 DevID[3];

  DevID[0] *(vu32*)(0x1ffff7e8);

  DevID[1] *(vu32*)(0x1ffff7ec);

  DevID[2] *(vu32*)(0x1ffff7f0);

  数组DevID[3]中即保存了MCU的ID。

  注:256K Flash或以上容量的STM32,仅“Z”版本才有,之前的“A”版本没有。

  1、STM32对内部Flash的保护措施

  所有STM32的芯片都提供对Flash的保护,防止对Flash的非法访问 - 写保护和读保护。

  1)、读保护即大家通常说的“加密”,是作用于整个Flash存储区域。一旦设置了Flash的读保护,内置的Flash存储区只能通过程序的正常执行才能读出,而不能通过下述任何一种方式读出:

  通过调试器(JTAG或SWD);

  从RAM中启动并执行的程序;

  2)、写保护是以四页(1KB/页) Flash存储区为单位提供写保护,对被保护的页实施编程或擦除操作将不被执行,同时产生操作错误标志。

  读与写设置的效果见下表:

  读保护  写保护   对Flash的操作功能

  有效   有效   CPU只能读,禁止调试和非法访问。

  有效   无效   CPU可以读写,禁止调试和非法访问,页0~3为写保护。

  无效   有效   CPU可读,允许调试和非法访问。

  无效   无效   CPU可以读写,允许调试和非法访问。

  2、当Flash读保护生效时,CPU执行程序可以读受保护的Flash区,但存在两个例外情况:

  1)、调试执行程序时;

  2)、从RAM启动并执行程序时

  STM32还提供了一个特别的保护,即对Flash存储区施加读保护后,即使没有启用写保护,Flash的第 0 ~ 3 页也将处于写保护状态,这是为了防止修改复位或中断向量而跳转到RAM区执行非法程序代码。

  3、Flash保护相关函数

  FLASH_Unlock(); //Flash解锁

  FLASH_ReadOutProtection(DISABLE); //Flash读保护禁止

  FLASH_ReadOutProtection(ENABLE); //Flash读保护允许

  CRC

  1、CRC计算公式

  所有的STM32芯片都内置了一个硬件的CRC计算模块,可应用到通信程序中,这个CRC计算模块使用常见的、在以太网中使用的计算多项式:

  X32 + X26 + X23 + X22 + X16 + X12 + X11 + X10 +X8 + X7 + X5 + X4 + X2 + X + 1

  写成16进制就是:0x04C11DB7

  2、使用这个内置CRC模块操作步骤:

  复位CRC模块(设置CRC_CR0x01),这个操作把CRC计算的余数初始化为0xFFFFFFFF

  把要计算的数据按每32位分割为一组数据字,并逐个地把这组数据字写入CRC_DR寄存器(既下图中的绿色框)

  写完所有的数据字后,就可以从CRC_DR寄存器(既下图中的兰色框)读出计算的结果。

  注意:虽然读写操作都是针对CRC_DR寄存器,但实际上是访问的不同物理寄存器。

  3、C语言描述的这个计算模块算法。可放在通信的另一端,对通信的正确性进行验证:

  DWORD dwPolynomial 0x04c11db7;

  DWORD cal_crc(DWORD *ptr, int len)

  {

  DWORD xbit;

  DWORD data;

  DWORD CRC 0xFFFFFFFF; // init

  while (len--)

  {

  xbit 1 《《 31;

  data *ptr++;

  for (int bits 0; bits 《 32; bits++)

  {

  if (CRC & 0x80000000)

  {

  CRC 《《 1;

  CRC ^ dwPolynomial;

  }else

  CRC 《《 1;

  if (data & xbit)

  CRC ^ dwPolynomial;

  xbit 》》 1;

  }

  }

  return CRC;

  }

  注意:

  1)、上述算法中变量CRC,在每次循环结束包含了计算的余数,它始终是向左移位(既从最低位向最高位移动),溢出的数据位被丢弃。

  2)、输入的数据始终是以32位为单位,如果原始数据少于32位,需要在低位补0,当然也可以高位补0。

  3)、假定输入的DWORD数组中每个分量是按小端存储。

  4)、输入数据是按照最高位最先计算,最低位最后计算的顺序进行。

  例如:

  如果输入0x44434241,内存中按字节存放的顺序是:0x41, 0x42, 0x43, 0x44。计算的结果是:0xCF534AE1

  如果输入0x41424344,内存中按字节存放的顺序是:0x44, 0x43, 0x42, 0x41。计算的结果是:0xABCF9A63

  IAR

  1、IAR环境下如果链接工程文件,出现堆栈溢出错误,该如何处理?

  打开链接文件lnkarm_flash.xcl或者是lnkarm_ram.xcl

  //*****************************

  // Stack and heap segments.

  //*****************************

  -D_CSTACK_SIZE400 《---------------修改这里

  注:该修改方式仅适合IAR for ARM 4.xx版本。

  2、IAR编译输出HEX格式的目标文件

  1)、Options-》C/C++ Compiler-》Output-》Generate debug information 选项前的钩去掉

  2)、Options-》Assembler-》Output-》Generate debug information 选项前的钩去掉

  3)、Options-》Linker-》Output-》Output File-》Override default 选项前的钩选上并把文件名的后缀改成.hex

  4)、Options-》Linker-》Output-》Output File-》Format-》Other 选项前的钩选上并把Output格式改为intel-extended

  经过以上设置,在Rebuld All之后会在/Debug/Exe下生成.hex格式目标文件

  3、IAR编译输出BIN格式的目标文件

  1)、Options-》C/C++ Compiler-》Output-》Generate debug information 选项前的钩去掉

  2)、Options-》Assembler-》Output-》Generate debug information 选项前的钩去掉

  3)、Options-》Linker-》Output-》Output File-》Override default 选项前的钩选上并把文件名的后缀改成.bin

  4)、Options-》Linker-》Output-》Output File-》Format-》Other 选项前的钩选上并把Output格式改为row-binary

  经过以上设置,在Rebuld All之后会在/Debug/Exe下生成.bin格式目标文件

  1、在MDK代码编辑环境下不能使用Goto Definition(用于查找某个变量的类型及定义)、Goto Reference(用于查找某个函数申明的原型)功能?

  解决:打开Project-》Target-Options-》Output,将Browse Information复选框勾上。

  2、当使用STM32固件库与RTX Kernel时,使用isr_evt_set(),事件无响应?

  解决:编辑STM32固件库的“stm32f10x_vector.s”文件:

  IMPORT SVC_Handler ;name changed according to RTX usage

  IMPORT DebugMonitor

  IMPORT PendSV_Handler ;修改加入

  IMPORT SysTick_Handler ;name changed according to RTX usage

  DCD SVC_Handler

  DCD DebugMonitor

  DCD 0 ; Reserved

  DCD PendSV_Handler ;修改加入

  DCD SysTick_Handler

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分