学习笔记STM32标准库开发的常用API整理
昔枫沐杰以下资料适用于STM32F103C8T6标准库开发,其他型号可根据实际情况自行修改
STM32内部资源
1. 位带操作
STM32可通过位带操作来实现类似51单片机的位操作,以下是实现位带操作的io_bit.h文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #ifndef _IO_BIT_H_ #define _IO_BIT_H_ #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOA_ODR_Addr (GPIOA_BASE+0x0C) #define GPIOB_ODR_Addr (GPIOB_BASE+0x0C) #define GPIOC_ODR_Addr (GPIOC_BASE+0x0C) #define GPIOD_ODR_Addr (GPIOD_BASE+0x0C) #define GPIOE_ODR_Addr (GPIOE_BASE+0x0C) #define GPIOA_IDR_Addr (GPIOA_BASE+0x08) #define GPIOB_IDR_Addr (GPIOB_BASE+0x08) #define GPIOC_IDR_Addr (GPIOC_BASE+0x08) #define GPIOD_IDR_Addr (GPIOD_BASE+0x08) #define GPIOE_IDR_Addr (GPIOE_BASE+0x08)
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n)
#endif
|
使用示例
1 2 3 4 5
| #include "io_bit.h" ...... PCout(13) = 1; uint8_t key = PCin(14); ......
|
2. 利用系统滴答计时器实现延时
delay.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include "delay.h"
void delay_us(u32 nus) { u32 temp; SysTick->LOAD = 9*nus; SysTick->VAL=0X00; SysTick->CTRL=0X01; do { temp=SysTick->CTRL; }while((temp&0x01)&&(!(temp&(1<<16)))); SysTick->CTRL=0x00; SysTick->VAL =0X00; } void delay_ms(u16 nms) { u32 temp; SysTick->LOAD = 9000*nms; SysTick->VAL=0X00; SysTick->CTRL=0X01; do { temp=SysTick->CTRL; }while((temp&0x01)&&(!(temp&(1<<16)))); SysTick->CTRL=0x00; SysTick->VAL =0X00; }
|
delay.h
1 2 3 4 5 6 7 8 9
| #ifndef _delay_H_ #define _delay_H_
#include "stm32f10x.h"
void delay_us(u32 nus); void delay_ms(u16 nms);
#endif
|
3. 定时器
以定时器2产生1ms中断为例
timer.c
1 2 3 4 5 6 7 8 9 10 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 38 39 40
| #include "timer.h"
void tim_Init(void) { TIM_TimeBaseInitTypeDef timInitStructure; NVIC_InitTypeDef nvicInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); timInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; timInitStructure.TIM_CounterMode = TIM_CounterMode_Up; timInitStructure.TIM_Period = 1000-1; timInitStructure.TIM_Prescaler = 72-1; TIM_TimeBaseInit(TIM2, &timInitStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); nvicInitStructure.NVIC_IRQChannel = TIM2_IRQn; nvicInitStructure.NVIC_IRQChannelPreemptionPriority = 2; nvicInitStructure.NVIC_IRQChannelSubPriority = 1; nvicInitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvicInitStructure); }
uint16_t timer_t = 0; void TIM2_IRQHandler(void) { if( TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); timer_t++; } }
|
timer.h
1 2 3 4 5 6 7 8 9
| #ifndef __TIMER_H #define __TIMER_H
#include "stm32f10x.h"
void tim_Init(void); extern uint16_t timer_t;
#endif
|
4. 串口
以串口1为例
usart.c
1 2 3 4 5 6 7 8 9 10 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| #include "usart.h"
void usart_init(unsigned int bound) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 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; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_InitStructure.USART_BaudRate = bound; USART_InitStructure.USART_WordLength = USART_WordLength_8b; 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); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_Cmd(USART1, ENABLE); }
void Uart_Send(unsigned char data) { while(!USART_GetFlagStatus(USART1, USART_FLAG_TC)); USART_SendData(USART1, data); }
void Uart_SendString(unsigned char *string) { while(*string != 0) { Uart_Send(*string++); } } unsigned char Uart_Read(void) { while(!USART_GetFlagStatus(USART1, USART_FLAG_RXNE)); return USART_ReceiveData(USART1); } char Rx_buff[512]; char Rx_flag; void Uart_ReadString(void) { static unsigned cnt = 0; unsigned char ch; if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) { ch = USART_ReceiveData(USART1); if(ch != '\r' && ch != '\n') { Rx_buff[cnt] = ch; cnt++; } else if(ch == '\n') { Rx_buff[cnt] = '\0'; cnt = 0; Rx_flag = 1; } } }
void USART1_IRQHandler(void){ Uart_ReadString(); }
|
usart.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #ifndef _USART_H_ #define _USART_H_
#include "stm32f10x.h" #include "stdio.h"
extern char Rx_buff[512]; extern char Rx_flag;
void usart_init(unsigned int bound); void Uart_Send(unsigned char data); void Uart_SendString(unsigned char *string);
#endif
|
5. adc模数转换
以adc1的通道0和1为例,可自行添加或修改
adc.c
1 2 3 4 5 6 7 8 9 10 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 38 39 40 41 42 43
| #include "adc.h" void ADC1_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1) == SET); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1) == SET); }
uint16_t AD_GetValue(uint8_t ADC_Channel) { ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); return ADC_GetConversionValue(ADC1); }
|
adc.h
1 2 3 4 5 6 7 8 9
| #ifndef _ADC_H #define _ADC_H
#include "stm32f10x.h"
uint16_t AD_GetValue(uint8_t ADC_Channel);
void ADC1_Init(void); #endif
|
6. 硬件IIC
以IIC1为例,即PB6与PB7引脚,硬件iic只封装了写数据和读数据的函数,可根据需要自行拆解或修改
iic.c
1 2 3 4 5 6 7 8 9 10 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #include "iic.h"
void i2c_init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 200 * 1000; I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); }
void i2c_write_byte(uint8_t addr, uint8_t reg, uint8_t data) { I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, reg); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1, data); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1, ENABLE); }
uint8_t i2c_read_byte(uint8_t addr0, uint8_t addr1, uint8_t reg) { uint8_t data; I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, addr0, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, reg); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, addr1, I2C_Direction_Receiver); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); I2C_AcknowledgeConfig(I2C1, DISABLE); I2C_GenerateSTOP(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); data = I2C_ReceiveData(I2C1); I2C_GenerateSTOP(I2C1, ENABLE); return data; }
|
iic.h
1 2 3 4 5 6 7 8 9 10
| #ifndef _IIC_H_ #define _IIC_H_
#include "stm32f10x.h"
void i2c_init(void); uint8_t i2c_read_byte(uint8_t addr0, uint8_t addr1, uint8_t reg); void i2c_write_byte(uint8_t addr, uint8_t reg, uint8_t data);
#endif
|
7. 软件IIC
软件IIC可以在 .h 文件自定义引脚
iic_soft.c
1 2 3 4 5 6 7 8 9 10 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
| #include "iic_soft.h"
void IIC_Pin_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(IIC_GPIO_RCC,ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Pin = IIC_SCL_PIN; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(IIC_GPIO,&GPIO_InitStruct); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStruct.GPIO_Pin = IIC_SDA_PIN; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(IIC_GPIO,&GPIO_InitStruct); }
void IIC_Start(void) { IIC_SCL = 1; IIC_SDA = 1; delay_us(5); IIC_SDA = 0; delay_us(5); IIC_SCL = 0; }
void IIC_Stop(void) { IIC_SCL = 0; IIC_SDA = 1; delay_us(5); IIC_SCL = 1; delay_us(5); IIC_SDA = 0; }
void IIC_Send_Ack(u8 ack) { IIC_SCL = 1; delay_us(5); IIC_SCL = 0; delay_us(5); if(ack) { IIC_SDA = 1; } else { IIC_SDA = 0; }
delay_us(5); IIC_SCL = 1; delay_us(5); IIC_SCL = 0; }
u8 IIC_Reception_Ack(void) { IIC_SDA = 1; delay_us(5); IIC_SCL = 0; delay_us(5); IIC_SCL = 1; if(IIC_SDA_IN) { return 1; } return 0; }
u8 IIC_Send_Data(u8 data) { u8 i = 0; for(i = 0; i < 8; i++) { IIC_SCL = 0; if(data & 0x80) { IIC_SDA = 1; } else { IIC_SDA = 0; } data <<= 1; delay_us(5); IIC_SCL = 1; delay_us(5); } IIC_SCL = 0; return IIC_Reception_Ack(); }
u8 IIC_Read_Data(void) { u8 data = 0; u8 i = 0; IIC_SDA = 1; for(i = 0; i < 8; i++) { IIC_SCL = 0; delay_us(5); IIC_SCL = 1; if(IIC_SDA_IN) { data |= 1; } data <<= 1; } IIC_Send_Ack(1); return data; }
|
iic_soft.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #ifndef _IIC_SOFT_H_ #define _IIC_SOFT_H_
#include "stm32f10x.h" #include "io_bit.h" #include "delay.h"
#define IIC_GPIO_RCC RCC_APB2Periph_GPIOB #define IIC_GPIO GPIOB #define IIC_SCL_PIN GPIO_Pin_6 #define IIC_SDA_PIN GPIO_Pin_7
#define IIC_SCL PBout(6) #define IIC_SDA PBout(7) #define IIC_SDA_IN PBin(7)
void IIC_Pin_Init(void); void IIC_Start(void); void IIC_Stop(void); void IIC_Send_Ack(u8 ack); u8 IIC_Send_Data(u8 data); u8 IIC_Read_Data(void);
#endif
|
常用外设驱动
1. dht22温湿度传感器
det22的out引脚在 .h 文件中定义
dht22.c
1 2 3 4 5 6 7 8 9 10 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| #include "dht22.h"
uint8_t humi_int; uint8_t humi_deci; uint8_t temp_int; uint8_t temp_deci; static unsigned char tempIsNegative = 0;
void DHT22_Init() { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_GPIO_DHT22, ENABLE); GPIO_InitStructure.GPIO_Pin = PIN_DHT22; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_DHT22, &GPIO_InitStructure); GPIO_SetBits(GPIO_DHT22, PIN_DHT22); }
void Pin_Mode_In() { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = PIN_DHT22; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; GPIO_Init(GPIO_DHT22, &GPIO_InitStructure); }
void Pin_Mode_Out() { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = PIN_DHT22; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_DHT22, &GPIO_InitStructure); }
uint8_t Read_Byte() { uint8_t i, temp=0; for(i=0;i<8;i++) { while(Pin_Read() == Bit_RESET); delay_us(50); if(Pin_Read() == Bit_SET) { while(Pin_Read() == Bit_SET); temp|=(uint8_t)(0x01<<(7-i)); } else { temp&=(uint8_t)~(0x01<<(7-i)); } } return temp; }
uint8_t DHT22_Read() { uint8_t humiHigh; uint8_t humiLow; uint8_t tempHigh; uint8_t tempLow; uint8_t check_sum;
unsigned int humi_val; unsigned int temp_val;
Pin_Mode_Out(); Pin_Out(LOW); delay_ms(2); Pin_Out(HIGH); delay_us(30); Pin_Mode_In();
if(Pin_Read() == Bit_RESET) { while(Pin_Read() == Bit_RESET); while(Pin_Read() == Bit_SET); humiHigh = Read_Byte(); humiLow = Read_Byte(); tempHigh = Read_Byte(); tempLow = Read_Byte(); check_sum = Read_Byte(); Pin_Mode_Out(); Pin_Out(HIGH);
if(check_sum == (humiHigh + humiLow + tempHigh + tempLow) % 256) { humi_val = humiHigh * 256 + humiLow; humi_int = humi_val/10; humi_deci = humi_val%10; if(tempHigh < 128) { temp_val = tempHigh * 256 + tempLow; } else { tempIsNegative = 1; temp_val = (tempHigh - 128) * 256 + tempLow; } temp_int = temp_val/10; temp_deci = temp_val%10; return SUCCESS; } else return ERROR; } else return ERROR; }
|
dht22.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #ifndef _DHT22_H_ #define _DHT22_H_
#include "stm32f10x.h" #include "delay.h"
#define RCC_GPIO_DHT22 RCC_APB2Periph_GPIOA #define GPIO_DHT22 GPIOA #define PIN_DHT22 GPIO_Pin_12
#define HIGH 1 #define LOW 0 #define Pin_Read() GPIO_ReadInputDataBit(GPIO_DHT22,PIN_DHT22) #define Pin_Out(a) if (a) \ GPIO_SetBits(GPIO_DHT22,PIN_DHT22);\ else \ GPIO_ResetBits(GPIO_DHT22,PIN_DHT22);
void DHT22_Init(); uint8_t DHT22_Read();
extern uint8_t humi_int; extern uint8_t humi_deci; extern uint8_t temp_int; extern uint8_t temp_deci;
extern unsigned char tempIsNegative; #endif
|
大部分代码来自网上,本人对其中的代码做了修改与封装,仅供学习查阅使用,如有侵权,请联系我