• LSM6DS3THC 6轴传感器驱动


    LSM6DS3TH-C 6轴传感器驱动

    说明

    • 测试平台:STM32F4
    • 使用说明:
      • 在platform_ReadByte、与platform_WriteByte中实现对应平台的I2C读写操作接口
      • 默认应用接口为弱定义,定义此USER_APP_INTERFACE宏定义可以开启示例的应用接口,用户可自定义自己的接口函数,打印信息可自行关闭,默认的接口函数不包含单位转换,只实现了原始数据存储
      • 存储数据使用数据类型sLSM6DS3_Data
      • 默认定义了对象sLSM6DS3_Dev g_lsm6ds3,可包含lsm6ds3.h头文件,即可使用此对象去访问用户接口函数

    驱动源码

    #ifndef __LSM6DS3_H__
    #define __LSM6DS3_H__
    
    #include "main.h"
    
    #define LSM6DS3_WHO_AM_I                0X0F // 能读出ID,根据芯片不同而不同,可能是0x69,也或许是0x6a
    #define LSM6DS3_CTRL1_XL                0X10 // 线性加速度传感器控制寄存器1 (r/w)。
    /*  bit7						~						  bit0
    	ODR_XL3 ODR_XL2 ODR_XL1 ODR_XL0 FS_XL1 FS_XL0 BW_XL1 BW_XL0
    	其中:ODR_XL3 ODR_XL2 ODR_XL1 ODR_XL0 输出数据速率和电源模式选择
    	FS_XL1 FS_XL0:ACC满量程选择
    	BW_XL1 BW_XL0:抗混叠滤波器带宽选择。
    */
    #define LSM6DS3_CTRL2_G                 0X11
    /*  bit7						~			   bit0
    	ODR_G3  ODR_G2 ODR_G1 ODR_G0 FS_G1  FS_G0 FS_125
    	其中:ODR_G3  ODR_G2 ODR_G1 ODR_G0:陀螺仪输出速率选择
    	FS_G1  FS_G0:陀螺仪满量程选择
    	FS_125:陀螺仪满量程为125dps;	
    */
    #define LSM6DS3_CTRL3_C                 0X12
    /*  bit7						~			   bit0
    	BooT BDU H_LACTIVE PP_OD SIM IF_INC BLE SW_RESET
    	BooT:重启内存里的内容
    	BDU:更新块数据
    	H_LACTIVE:中断活跃水平
    	PP_OD:INT1和INT2衬垫上的推拉/开式排水选择。
    	SIM:SPI Serial interface mode select
    	IF_INC:寄存器地址在进行串行多字节访问时自动增加
    	BLE:大/小端数据选择
    	SW_RESET:软件复位
    */
    #define LSM6DS3_CTRL4_C                 0X13
    /*  bit7						~			   bit0
    	XL_BW_SCAL_ODR:加速度计带宽的选择;
    	SLEEP_G:陀螺仪睡眠模式使能;
    	INT2_on_INT1:所有中断信号在INT1上可用
    	FIFO_TEMP_EN:使能稳如数据作为4th FIFO数据设置
    	DRDY_MASK:数据读掩码使能;
    	I2C_disable:disable i2c接口
    	0
    	STOP_ON_FTH:启用FIFO阈值级别使用;
    */
    #define LSM6DS3_CTRL5_C                 0X14
    /*
    	ROUNDING2 ROUNDING1 ROUNDING0 0 ST1_G  ST0_G ST1_XL ST0_XL
    	ROUNDING2 ROUNDING1 ROUNDING0:从输出寄存器中读取循环突发模式(舍入)。
    	ST1_G  ST0_G:角速率传感器自检使能。
    	ST1_XL ST0_XL:线性加速度传感器自检使能。
    */
    
    #define LSM6DS3_CTRL6_C                 0X15
    /*
    	角速率传感器控制寄存器
    	TRIG_EN LVLen LVL2_EN XL_HM_MODE 0 0 0 0
    	TRIG_EN:陀螺仪数据边缘敏感触发启用。
    	LVLen:陀螺仪数据电平敏感触发启用。
    	LVL2_EN:陀螺仪电平敏感锁存使能。
    	XL_HM_MODE:加速计高性能工作模式禁用.
    */
    #define LSM6DS3_CTRL7_G                 0X16
    /*
    	角速率传感器控制寄存器7
    	G_HM_MODE HP_G_EN HPCF_G1 HPCF_G0 HP_G_RST ROUNDING_STATUE 0 0
    	G_HM_MODE:陀螺仪的高性能工作模式禁用
    	HP_G_EN:陀螺数字高通滤波器使能。只有当陀螺仪处于HP模式时,才启用过滤器。
    	HPCF_G1 HPCF_G0:陀螺仪高通滤波器截止频率选择。
    	HP_G_RST:陀螺数字HP滤波器复位
    	ROUNDING_STATUE:源寄存器四舍五入功能使能在1E,53,1B	
    */
    #define LSM6DS3_CTRL8_XL                0X17
    /*
    	线性加速度传感器控制寄存器8 (r/w)。
    	LPF2_XL_EN	HPCF_XL1 HPCF_XL0 0 0 HP_SLOPE_XL_EN 0 LOW_PASS_ON_6D
    	LPF2_XL_EN: 加速度计低通滤波器LPF2选择。
    	HPCF_XL1 HPCF_XL0:加速度计斜率滤波器和高通滤波器配置和截止设置。
    		见表68。它还用于选择LPF2滤波器的截止频率,如表69所示。通过将CTRL8XL (17h)的LOW PASS ON 6D位设置为1,这种低通滤波器也可以用于6D/4D功能。
    	HP_SLOPE_XL_EN:加速度计斜率滤波器/高通滤波器选择。
    	LOW_PASS_ON_6D:低通滤波器对6D功能的选择。
    */
    #define LSM6DS3_CTRL9_XL                0X18
    /*
    	0 0 Zen_XL Yen_XL Xen_XL SOFT_EN 0 0
    	Zen_XL:加速计z轴输出启用。
    	Yen_XL:加速计y轴输出启用。
    	Xen_XL:加速计x轴输出启用.
    	SOFT_EN:启用磁强计软铁校正算法
    */
    #define LSM6DS3_CTRL10_C                0X19
    /*
    	0 0 Zen_G Yen_G Xen_G FUNC_EN FEDO_RST_STEP SIGN_MOTION_EN
    	Zen_G:陀螺偏航轴(Z)输出使能。
    	Yen_G:陀螺滚轴(Y)输出使能。
    	Xen_G:陀螺螺距轴(X)输出使能。
    	FUNC_EN:启用嵌入式功能(计步器、倾斜、显著运动、传感器轮毂和熨烫)和加速度计HP和LPF2滤波器
    		(参见图6)。默认值:0
    	FEDO_RST_STEP:重置计步器步长计数器。
    	SIGN_MOTION_EN:使能重要运动功能
    */
    
    //i2c master config register
    #define LSM6DS3_MASTER_CONFIG           0X1A
    
    //interrupts register
    #define LSM6DS3_WAKE_UP_SRC             0X1B
    /*
    	0 0 FF_IA SLEEP_STATE_IA WU_IA X_WU Y_WU Z_WU
    	FF_IA:自由落体时间检测状态
    	SLEEP_STATE_IA:睡眠时间状态
    	WU_IA:唤醒时间检测状态
    	X_WU:x轴上的唤醒事件检测状态。
    	Y_WU: y轴上的唤醒事件检测状态。
    	Z_WU: z轴上的唤醒事件检测状态。
    */
    #define LSM6DS3_TAP_SRC                 0X1C
    /*
    	0 TAP_IA SIGLE_TAP DOUBLE_TAP TAP_SIGN X_TAP Y_TAP Z_TAP
    	TAP_IA:轻击事件检测状态
    	SIGLE_TAP:单击事件检测状态
    	DOUBLE_TAP:双击事件检测状态
    	TAP_SIGN:轻击事件检测到的加速标志。
    	X_TAP Y_TAP Z_TAP:点击x/y/z轴上的事件检测状态
    */
    #define LSM6DS3_D6D_SRC                 0X1D
    /*
    	纵向、横向、面朝上和面朝下源寄存器(r)
    	0 D6D_IA ZH ZL YH YL XH XL 
    	D6D_IA:激活中断以改变位置纵向,横向,正面向上,正面向下。
    	ZH: Z轴高事件(高于阈值)
    	ZL:Z轴低事件(低于阈值)
    	...
    */
    //status data register
    #define LSM6DS3_STATUS_REG              0X1E
    /*
    	- - - - - TDE GDA XLDA
    	TDE:温度可用的新数据
    	GDA:陀螺仪可用的新数据
    	XLDA:加速度计可用的新数据
    */
    #define LSM6DS3_TAP_CFG			0x58
    #define LSM6DS3_TAP_THS_6D		0x59
    #define LSM6DS3_WAKE_UP_THS	        0x5B
    #define LSM6DS3_WAKE_UP_DUR		0x5C
    #define LSM6DS3_MD1_CFG			0x5E
    
    //temperature output data register
    #define LSM6DS3_OUT_TEMP_L              0X20
    #define LSM6DS3_OUT_TEMP_H              0X21
    /*温度数据输出寄存器(r)。L寄存器和H寄存器一起表示一个16位的字。*/
    
    //gyroscope output register
    #define LSM6DS3_OUTX_L_G                0X22
    #define LSM6DS3_OUTX_H_G                0X23
    #define LSM6DS3_OUTY_L_G                0X24
    #define LSM6DS3_OUTY_H_G                0X25
    #define LSM6DS3_OUTZ_L_G                0X26
    #define LSM6DS3_OUTZ_H_G                0X27
    
    //acc output register
    #define LSM6DS3_OUTX_L_XL               0X28
    #define LSM6DS3_OUTX_H_XL               0X29
    #define LSM6DS3_OUTY_L_XL               0X2A
    #define LSM6DS3_OUTY_H_XL               0X2B
    #define LSM6DS3_OUTZ_L_XL               0X2C
    #define LSM6DS3_OUTZ_H_XL               0X2D
    
    #define LSM6DS3_INT1_CTRL		0X0D
    /*
    	Write INT1_CTRL = 01h // Acc Data Ready interrupt on INT1
    	Write INT1_CTRL = 02h // Gyro Data Ready interrupt on INT1
    */
    
    #define I2C_Handle			hi2c1
    #define LSM6DS3_SlaveAddress		0xd4
    #define I2C_Status			HAL_StatusTypeDef
    #define LSM6DS3_DeviceID		0x6a
    
    //#define USER_APP_INTERFACE
    #undef	USER_APP_INTERFACE
    
    // 操作模式读/写
    typedef enum 
    {
    	eRead = 0u,
    	eWrite = 1u,
    } OPmode_t;
    
    // 查询状态标志
    typedef enum{
        STATUS_XLDA_ACC_E = 0x01, // acc 数据可读
        STATUS_GDA_GYRO_E = 0x02, // gyro 数据可读
        STATUS_TDA_TEMP_E = 0x04, // temp 数据可读
    }LSM6DS3_STATUS_t;
    
    // 数据类型
    typedef struct 
    {
    	int16_t data_raw_acceleration[3];
    	int16_t data_raw_angular_rate[3];
    	int16_t data_raw_temperature;
    	float acceleration_mg[3];
    	float angular_rate_mdps[3];
    	float temperature_degC;
    	uint8_t whoamI;
    	uint8_t uReaddata;
    	uint8_t orientation;
    } sLSM6DS3_Data;
    
    typedef struct 
    {
    	/* I2C句柄,使用IO模拟I2C则设置句柄为NULL */
    	void *handle;
    	/* 读数据 */
    	sLSM6DS3_Data m_dataBuf;
    	/* 设备从机地址 */
    	uint8_t slaveAddress;
    	/* 设备ID */
    	uint8_t deviceID;
    
    	// 接口函数区域
    	void (*Init)(void);
    	uint8_t (*ReadByte)(uint8_t reg);
    	void (*WriteByte)(uint8_t reg, uint8_t data);
    	uint16_t (*ReadMultiple)(uint8_t reg_begin, uint8_t reg_end, uint8_t *data);
    	
    	// 扩展用户接口,默认为弱定义,用户可自行实现
    	uint8_t (*ReadOrigin)(void);
    	uint8_t (*ReadWhoAmI)(void);
    	int16_t *(*ReadAngularRateRaw)(int16_t *pbuf);
    	int16_t *(*ReadAccelerationRaw)(int16_t *pbuf);
    	int16_t *(*ReadTemperatureRaw)(int16_t *pbuf);
    	
    } sLSM6DS3_Dev;
    
    // 声明对象
    extern sLSM6DS3_Dev g_lsm6ds3;
    
    #endif
    
    #include "LSM6DS3.h"
    #include "i2c.h"
    #include "debug.h"
    #include <string.h>
    
    /* 私有函数声明区域 */
    static void Lsm6ds3_Init(void);
    static void Lsm6ds3_WriteByte(uint8_t reg, uint8_t data);
    static uint8_t Lsm6ds3_ReadByte(uint8_t reg);
    static uint16_t Lsm6ds3_ReadMultiple(uint8_t reg_addr_begin, uint8_t reg_addr_end, uint8_t *data);
    
    uint8_t Lsm6ds3_ReadOrigin(void);
    uint8_t Lsm6ds3_ReadWhoAmI(void);
    int16_t *Lsm6ds3_ReadAccelerationRaw(int16_t *pbuf);
    int16_t *Lsm6ds3_ReadAngularRateRaw(int16_t *pbuf);
    int16_t *Lsm6ds3_ReadTemperatureRaw(int16_t *pbuf);
    
    /* lsm6ds3传感器对象初始化 */
    sLSM6DS3_Dev g_lsm6ds3 =
    {
    	&hi2c1,
    	{0},
    	LSM6DS3_SlaveAddress,
    	LSM6DS3_DeviceID,
    	
    	Lsm6ds3_Init,
    	Lsm6ds3_ReadByte,
    	Lsm6ds3_WriteByte,
    	Lsm6ds3_ReadMultiple,
    	
    	Lsm6ds3_ReadOrigin,
    	Lsm6ds3_ReadWhoAmI,
    	Lsm6ds3_ReadAngularRateRaw,
    	Lsm6ds3_ReadAccelerationRaw,
    	Lsm6ds3_ReadTemperatureRaw,
    };
    
    /*
     * @brief  Read generic device register (platform dependent)
     *
     * @param[in]  handle    customizable argument. In this examples is used in
     *                       order to select the correct sensor bus handler.
     * @param[in]  reg       register to read
     * @param[in]  bufp      pointer to buffer that store the data read
     * @param[in]  len       number of consecutive register to read
     *
     */
    static I2C_Status platform_ReadByte(void *handle, uint8_t slaveAddress, uint8_t reg, uint8_t *bufp, uint16_t len)
    {
        return HAL_I2C_Mem_Read(handle, slaveAddress, reg, I2C_MEMADD_SIZE_8BIT, bufp, len, 1000);
    }
    
    /*
     * @brief  Write generic device register (platform dependent)
     *
     * @param[in]  handle    customizable argument. In this examples is used in
     *                   order to select the correct sensor bus handler.
     * @param[in]  reg       register to write
     * @param[in]  bufp      pointer to data to write in register reg
     * @param[in]  len       number of consecutive register to write
     *
     */
    static I2C_Status platform_WriteByte(void *handle, uint8_t slaveAddress, uint8_t reg, uint8_t *bufp, uint16_t len)
    {
    	return HAL_I2C_Mem_Write(handle, slaveAddress, reg, I2C_MEMADD_SIZE_8BIT, (uint8_t *)bufp, len, 1000);
    }
    
    /**
     * @brief      写入一个字节
     *
     * @param[in]  reg   寄存器地址
     * @param[in]  data  待写入的数据
     */
    static void Lsm6ds3_WriteByte(uint8_t reg, uint8_t data)
    {
    	I2C_Status ret = platform_WriteByte(g_lsm6ds3.handle,  g_lsm6ds3.slaveAddress | eWrite, 
    		reg, &data, sizeof(data));
    	if(ret != HAL_OK)
    		p_err("error_code:%d", ret);
    }
    
    /**
     * @brief      读取一个字节
     *
     * @param[in]  reg   寄存器地址
     *
     * @return     返回读取到的数据
     */
    static uint8_t Lsm6ds3_ReadByte(uint8_t reg)
    {
    	I2C_Status ret = platform_ReadByte(g_lsm6ds3.handle,  g_lsm6ds3.slaveAddress | eRead, 
    		reg, &(g_lsm6ds3.m_dataBuf.uReaddata), sizeof(g_lsm6ds3.m_dataBuf.uReaddata));
    	if(ret != HAL_OK)
    	{
    		p_err("error_code:%d", ret);
    		g_lsm6ds3.m_dataBuf.uReaddata= 0xff;
    	}
    	p_info("Lsm6ds3_ReadByte ret=%d, reg=0x%x, data=0x%x", ret, reg, g_lsm6ds3.m_dataBuf.uReaddata);
    	return g_lsm6ds3.m_dataBuf.uReaddata;
    }
    
    /**
     * @brief      连续地址读取传感器参数/数据
     *
     * @param[in]  reg_addr_begin  The register address begin
     * @param[in]  reg_addr_end    The register address end
     * @param[out] data            The data
     *
     * @return     返回读取的字节数
     */
    static uint16_t Lsm6ds3_ReadMultiple(uint8_t reg_addr_begin, uint8_t reg_addr_end, uint8_t *data)
    {
    	uint16_t nCount = 0;
    	if(reg_addr_begin > reg_addr_end)
    	{
    		p_err("register address invalid!");
    		return 0;
    	}
    	
    	while(nCount < (reg_addr_end - reg_addr_begin + 1)) 
    	{
    		data[nCount] = Lsm6ds3_ReadByte(reg_addr_begin + nCount);
    		nCount++;
    	}
    	
    	return nCount;
    }
    
    /**
     * @brief      初始化陀螺仪和加速度传感器
     */
    static void Lsm6ds3_Init(void)
    {
    	uint8_t whoAmI = Lsm6ds3_ReadByte(LSM6DS3_WHO_AM_I);
    	
    	p_info("Lsm6ds3_Init[G-SensorId] -> 0x%x", whoAmI);
    	if(whoAmI != LSM6DS3_DeviceID)
    	{
    		p_err("read who am i failed!");
    		return;
    	}
    	// 加速度计52HZ(倾斜角检测功能工作在26HZ,因此加速度计ODR必须设置为>=26hz),2g量程。
    	Lsm6ds3_WriteByte(LSM6DS3_CTRL1_XL, 0x20);
    	// 使能加速度计x,y,z轴
    	Lsm6ds3_WriteByte(LSM6DS3_CTRL9_XL, 0x38);
    	// enable accelerometer int1
    	Lsm6ds3_WriteByte(LSM6DS3_INT1_CTRL, 0x01); 
    	
    	// 陀螺仪208hz  2000dps
    	Lsm6ds3_WriteByte(LSM6DS3_CTRL2_G, 0x5C);
    	// 使能陀螺仪x,y,z轴
    	Lsm6ds3_WriteByte(LSM6DS3_CTRL10_C, 0x38);
    
    	// WAKE_UP INTERRUPT Configuration
    	Lsm6ds3_WriteByte(LSM6DS3_TAP_CFG, 0x90);
    	Lsm6ds3_WriteByte(LSM6DS3_WAKE_UP_DUR, 0x00);
    	Lsm6ds3_WriteByte(LSM6DS3_WAKE_UP_THS, 0x02);
    	Lsm6ds3_WriteByte(LSM6DS3_MD1_CFG, 0x20);
    	
    	// 6D Orientation Configuration
    	Lsm6ds3_WriteByte(LSM6DS3_TAP_THS_6D, 0x40);
    	Lsm6ds3_WriteByte(LSM6DS3_CTRL8_XL, 0x01);
    
    	p_info("Lsm6ds3 init successfule!");
    }
    
    /****************************************** 应用接口 *********************************************/
    #ifdef USER_APP_INTERFACE
    /**
     * @brief      获取传感器坐标零点
     *
     * @return     见#define LSM6DS3_D6D_SRC
     */
    uint8_t Lsm6ds3_ReadOrigin(void)
    {
    	g_lsm6ds3.m_dataBuf.orientation = Lsm6ds3_ReadByte(LSM6DS3_D6D_SRC); 
    	return g_lsm6ds3.m_dataBuf.orientation;
    }
    
    /**
     * @brief      获取芯片ID
     *
     * @return     返回芯片ID
     */
    uint8_t Lsm6ds3_ReadWhoAmI(void)
    {
    	g_lsm6ds3.m_dataBuf.whoamI = Lsm6ds3_ReadByte(LSM6DS3_WHO_AM_I); 
    	return g_lsm6ds3.m_dataBuf.whoamI;
    }
    
    /**
     * @brief      读取角速度寄存器原始值
     *
     * @param[out] pbuf  The pbuf
     *
     * @return     返回角速度寄存器原始值(带符号数值)
     */
    int16_t *Lsm6ds3_ReadAngularRateRaw(int16_t *pbuf)
    {
    	// 读取寄存器值使用无符号类型,读取后取值再转为有符号
    	uint8_t buf[6] = {0};
    
    	if((g_lsm6ds3.ReadByte(LSM6DS3_STATUS_REG) & STATUS_GDA_GYRO_E) != 0)
    	{
    		Lsm6ds3_ReadMultiple(LSM6DS3_OUTX_L_G, LSM6DS3_OUTZ_H_G, buf);
    		g_lsm6ds3.m_dataBuf.data_raw_angular_rate[0] = (buf[1] << 8) | buf[0];
    		g_lsm6ds3.m_dataBuf.data_raw_angular_rate[1] = (buf[3] << 8) | buf[2];
    		g_lsm6ds3.m_dataBuf.data_raw_angular_rate[2] = (buf[5] << 8) | buf[4];
    	}
    	
    	if(NULL != pbuf)
    		memcpy(pbuf, buf, sizeof(buf));
    	
    	return g_lsm6ds3.m_dataBuf.data_raw_angular_rate;
    }
    
    /**
     * @brief      读取加速度寄存器原始值
     *
     * @param[out] pbuf  The pbuf
     *
     * @return     返回加速度寄存器原始值(带符号数值)
     */
    int16_t *Lsm6ds3_ReadAccelerationRaw(int16_t *pbuf)
    {
    	// 读取寄存器值使用无符号类型,读取后取值再转为有符号
    	uint8_t buf[6] = {0};
    
    	if((g_lsm6ds3.ReadByte(LSM6DS3_STATUS_REG) & STATUS_XLDA_ACC_E) != 0)
    	{
    		Lsm6ds3_ReadMultiple(LSM6DS3_OUTX_L_XL, LSM6DS3_OUTZ_H_XL, buf);
    		g_lsm6ds3.m_dataBuf.data_raw_acceleration[0] = (buf[1] << 8) | buf[0];
    		g_lsm6ds3.m_dataBuf.data_raw_acceleration[1] = (buf[3] << 8) | buf[2];
    		g_lsm6ds3.m_dataBuf.data_raw_acceleration[2] = (buf[5] << 8) | buf[4];
    	}
    	
    	if(NULL != pbuf)
    		memcpy(pbuf, buf, sizeof(buf));
    	
    	return g_lsm6ds3.m_dataBuf.data_raw_acceleration;
    }
    
    /**
     * @brief      读取温度寄存器原始值
     *
     * @param[out] pbuf  The pbuf
     *
     * @return     返回温度存器原始值(带符号数值)
     */
    int16_t *Lsm6ds3_ReadTemperatureRaw(int16_t *pbuf)
    {
    	// 读取寄存器值使用无符号类型,读取后取值再转为有符号
    	uint8_t buf[2] = {0};
    
    	if((g_lsm6ds3.ReadByte(LSM6DS3_STATUS_REG) & STATUS_TDA_TEMP_E) != 0)
    	{
    		Lsm6ds3_ReadMultiple(LSM6DS3_OUT_TEMP_L, LSM6DS3_OUT_TEMP_H, buf);
    		g_lsm6ds3.m_dataBuf.data_raw_temperature = (buf[1] << 8) | buf[0];
    	}
    	
    	if(NULL != pbuf)
    		memcpy(pbuf, buf, sizeof(buf));
    	
    	return &(g_lsm6ds3.m_dataBuf.data_raw_temperature);
    }
    #else
    __weak uint8_t Lsm6ds3_ReadOrigin(void)
    {
    	return 0xff;
    }
    
    __weak uint8_t Lsm6ds3_ReadWhoAmI(void)
    {
    	return 0xff;
    }
    
    __weak int16_t *Lsm6ds3_ReadAngularRateRaw(int16_t *pbuf)
    {
    	return NULL;
    }
    
    __weak int16_t *Lsm6ds3_ReadAccelerationRaw(int16_t *pbuf)
    {
    	return NULL;
    }
    
    __weak int16_t *Lsm6ds3_ReadTemperatureRaw(int16_t *pbuf)
    {
    	return NULL;
    }
    #endif
    
  • 相关阅读:
    SQL(4)— Date、order by
    步入三十(二)
    【js】特殊字符转义
    【ionic】项目中$ionicModal简单用法
    【ionic】常见问题处理
    【js】视频截图
    实现笔记本拔除电源后修改屏幕缩放比例
    激活 公众号java
    【Golang】基于beego/orm实现相同表结构不同表名的分表方法实现
    .Net Cors 跨域配置域名通配符
  • 原文地址:https://www.cnblogs.com/veis/p/16152526.html
Copyright © 2020-2023  润新知