/*Includes ----------------------------------------------*/ #include "nrf_delay.h" #include "drv_iic_middle.h" #include "drv_qma7981.h" /*Private macro ------------------------------------------------*/ #define ADO 0 // I2C Address pin, 0 for low and 1 for high #define QMA7981_ADDRESS_LEN 1 #if ADO == 1 #define I2C_ADDRESS (0x13 << 1) //0b0010011 Device address when ADO = 1 #else #define I2C_ADDRESS (0x12 << 1) //0b0010010 Device address when ADO = 0 #endif /*qma7981 full scale range*/ #define RANGE_2G 0x01 #define RANGE_4G 0x02 #define RANGE_8G 0x04 #define RANGE_16G 0x08 #define RANGE_32G 0x0F /*qma7981 bandwidth*/ #define MCLK_DIV_BY_7695 0x00 #define MCLK_DIV_BY_3855 0x01 #define MCLK_DIV_BY_1935 0x02 #define MCLK_DIV_BY_975 0x03 #define MCLK_DIV_BY_15375 0x05 #define MCLK_DIV_BY_30735 0x06 #define MCLK_DIV_BY_61455 0x07 /*qma7981 clock freq*/ #define CLK_500_KHZ 0x01 #define CLK_333_KHZ 0x00 #define CLK_200_KHZ 0x02 #define CLK_100_KHZ 0x03 #define CLK_50_KHZ 0x04 #define CLK_25_KHZ 0x05 #define CLK_12_KHZ_5 0x06 #define CLK_5_KHZ 0x07 /*qma7981 no motion duration*/ #define NO_MOTION_1_SEC 0x00 #define NO_MOTION_2_SEC 0x01 #define NO_MOTION_3_SEC 0x02 #define NO_MOTION_5_SEC 0x04 #define NO_MOTION_10_SEC 0x09 #define NO_MOTION_15_SEC 0x0E #define NO_MOTION_30_SEC 0x12 #define NO_MOTION_1_MIN 0x18 #define NO_MOTION_2_MIN 0x22 #define NO_MOTION_3_MIN 0x28 #define NO_MOTION_4_MIN 0x2E /*qma7981 any motion samples*/ #define NUM_SAMPLES_1 0x00 #define NUM_SAMPLES_2 0x01 #define NUM_SAMPLES_3 0x02 #define NUM_SAMPLES_4 0x03 /*qma7981 mode*/ #define MODE_STANDBY 0x00 #define MODE_ACTIVE 0x01 /*qma7981 motion_detect*/ #define MOTION_DETECT_NOTHING 0x00 #define MOTION_DETECT_ANY_MOTION 0x01 #define MOTION_DETECT_NO_MOTION 0x02 /*STRUCTION -----------------------------------------------------*/ typedef enum qma7981_mode { Qma7981_Mode_Standby = 0, Qma7981_Mode_Active = 1, } Qma7981_Mode_t; typedef int32_t (*qma_write_ptr)(void *, uint8_t, const uint8_t *, uint16_t); typedef int32_t (*qma_read_ptr)(void *, uint8_t, uint8_t *, uint16_t); typedef struct { /** Component mandatory fields **/ qma_write_ptr write_reg; qma_read_ptr read_reg; } qma_ctx_t; typedef struct drv_qma7981 { qma_ctx_t ctx; //抽象层接口(如I2C/SPI,外部不可操作,属于私有成员) qma_data_t cur_data; //当前QMA数据 drv_qma_config_param_t cur_param; //当前QMA配置 } Drv_Qma7981_t; /*Local Variable ----------------------------------------------*/ static Drv_Qma7981_t ob_qma7981; static qma_data_t data_raw_acceleration; //临时存储加速度原始数据 /*Local Functions ----------------------------------------------*/ static void platform_delay_ms(uint32_t ms) { nrf_delay_ms(ms); } static int32_t qma7981_platform_write(void *handle, uint8_t reg, const uint8_t *bufp, uint16_t len) { int32_t ierror = 0; if(IIC_MIDDLE_WriteBytes(I2C_ADDRESS,reg,(uint8_t *)bufp,len) != true) { ierror = -1; } return ierror; } static int32_t qma7981_platform_read(void *handle, uint8_t reg, uint8_t *bufp, uint16_t len) { int32_t ierror = 0; if(IIC_MIDDLE_ReadBytes(I2C_ADDRESS,reg,bufp,len) != true) { ierror = -1; } return ierror; } static int read_accel_axis(uint8_t address_msb, int16_t *result) { int ret; int16_t data; uint8_t accel_axis_buf[2]; ret = ob_qma7981.ctx.read_reg(0,address_msb,accel_axis_buf,2); if(ret == -1)return -1; data = (accel_axis_buf[0]&0xFC) | (accel_axis_buf[1]<< 8); data = data / 4; // divide the result by 4 to maintain the sign, since the data is 14 bits *result = data; return 0; } static int get_accel_x(int16_t *result) { int ret; int16_t data; ret = read_accel_axis(0x01, &data); if(ret == -1)return -1; *result = data; return 0; } static int get_accel_y(int16_t *result) { int ret; int16_t data; ret = read_accel_axis(0x03, &data); if(ret == -1)return -1; *result = data; return 0; } static int get_accel_z(int16_t *result) { int ret; int16_t data; ret = read_accel_axis(0x05, &data); if(ret == -1)return -1; *result = data; return 0; } static void set_bit(uint8_t *byte, uint8_t n, bool value) { *byte = (*byte & ~(1UL << n)) | (value << n); } static void get_bit(uint8_t *byte, uint8_t n, bool *p_value) { if((*byte & (1UL << n)) > 0) { *p_value = true; } else { *p_value = false; } } static int soft_reset(void) { int ret; uint8_t data; data = 0xB6; ret = ob_qma7981.ctx.write_reg(0,0x36,&data,1); //0xB6, soft reset all of the registers. if(ret == -1)return 1; data = 0x00; ret = ob_qma7981.ctx.write_reg(0,0x36,&data,1); //After soft-reset, user should write 0x00 back if(ret == -1)return 1; return 0; } static int set_mode(Qma7981_Mode_t mode) { int ret; uint8_t data; ret = ob_qma7981.ctx.read_reg(0,0x11,&data,1); if(ret == -1)return -1; set_bit(&data,7,mode); ret = ob_qma7981.ctx.write_reg(0,0x11,&data,1); if(ret == -1)return -1; return 0; } static int get_mode(Qma7981_Mode_t *p_mode) { int ret; uint8_t data; bool is_set; ret = ob_qma7981.ctx.read_reg(0,0x11,&data,1); if(ret == -1)return -1; get_bit(&data, 7, &is_set); if(is_set) { *p_mode = Qma7981_Mode_Active; } else { *p_mode = Qma7981_Mode_Standby; } return 0; } static int set_clock_freq(uint8_t freq) { int ret; uint8_t data; ret = ob_qma7981.ctx.read_reg(0,0x11,&data,1); if(ret == -1)return -1; data &= 0xF0; data |= (freq & 0x0F); ret = ob_qma7981.ctx.write_reg(0,0x11,&data,1); if(ret == -1)return -1; return 0; } static int get_clock_freq(uint8_t *p_freq) { int ret; uint8_t data; ret = ob_qma7981.ctx.read_reg(0,0x11,&data,1); if(ret == -1)return -1; *p_freq = (data & 0x0F); return 0; } static int set_bandwidth(uint8_t bandwidth) { int ret; uint8_t data; data = 0xE0; data |= (bandwidth & 0x07); ret = ob_qma7981.ctx.write_reg(0,0x10,&data,1); if(ret == -1)return -1; return 0; } static int get_bandwidth(uint8_t *p_bandwidth) { int ret; uint8_t data; ret = ob_qma7981.ctx.read_reg(0,0x10,&data,1); if(ret == -1)return -1; data &= 0x07; *p_bandwidth = data; return 0; } static int set_interrupt_pin_1_type(bool open_drain, bool active_high) { int ret; uint8_t data; ret = ob_qma7981.ctx.read_reg(0,0x20,&data,1); if(ret == -1)return -1; set_bit(&data, 0, active_high); set_bit(&data, 1, open_drain); ret = ob_qma7981.ctx.write_reg(0,0x20,&data,1); if(ret == -1)return -1; return 0; } static int set_full_scale_range(uint8_t range) { int ret; uint8_t data; ret = ob_qma7981.ctx.read_reg(0,0x0F,&data,1); if(ret == -1)return -1; data &= 0xF0; data |= (range & 0x0F); ret = ob_qma7981.ctx.write_reg(0,0x0F,&data,1); if(ret == -1)return -1; return 0; } static int get_full_scale_range(uint8_t *p_range) { int ret; uint8_t data; ret = ob_qma7981.ctx.read_reg(0,0x0F,&data,1); if(ret == -1)return -1; data &= 0x0F; *p_range = data; return 0; } /*API ----------------------------------------------*/ /** @brief 初始化QMA驱动 @param 无 @return 错误代码 - [out] -1失败,0成功 */ int drv_qma_Init(void) { int ret; //初始化结构体 memset(&ob_qma7981.cur_data,0,sizeof(ob_qma7981.cur_data)); ob_qma7981.cur_param.acc_fs = QMA_ACC_FS_2G; ob_qma7981.cur_param.acc_odr = QMA_ACC_ODR_OFF; ob_qma7981.ctx.read_reg = qma7981_platform_read; ob_qma7981.ctx.write_reg = qma7981_platform_write; //供电 nrf_gpio_cfg( PIN_QMA7981_POWER, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE); nrf_gpio_pin_write(PIN_QMA7981_POWER,0); platform_delay_ms(200); nrf_gpio_pin_write(PIN_QMA7981_POWER,1); nrf_gpio_cfg_output(PIN_QMA7981_AD0); nrf_gpio_pin_write(PIN_QMA7981_AD0,ADO); platform_delay_ms(200); IIC_MIDDLE_Init(); ret = soft_reset(); //soft reset all of the registers, need to at least delay 10ms after called the function. if(ret == -1)return -1; return 0; } /** @brief QMA断电 @param 无 @return 错误代码 - [out] -1失败,0成功 */ int drv_qma_power_off(void) { //断电,清空qma配置 nrf_gpio_cfg( PIN_QMA7981_POWER, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE); nrf_gpio_cfg_output(PIN_QMA7981_AD0); nrf_gpio_cfg_output(PIN_QMA7981_SDA); nrf_gpio_cfg_output(PIN_QMA7981_SCLK); nrf_gpio_cfg_output(PIN_QMA7981_nCS); nrf_gpio_pin_write(PIN_QMA7981_AD0,0); nrf_gpio_pin_write(PIN_QMA7981_SDA,0); nrf_gpio_pin_write(PIN_QMA7981_SCLK,0); nrf_gpio_pin_write(PIN_QMA7981_nCS,0); nrf_gpio_pin_write(PIN_QMA7981_POWER,0); return 0; } /** @brief QMA上电(默认配置挂起) @param 无 @return 错误代码 - [out] -1失败,0成功 */ int drv_qma_power_on(void) { int ret; nrf_gpio_cfg( PIN_QMA7981_POWER, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE); nrf_gpio_pin_write(PIN_QMA7981_POWER,1); nrf_gpio_cfg_output(PIN_QMA7981_AD0); nrf_gpio_pin_write(PIN_QMA7981_AD0,ADO); IIC_MIDDLE_Init(); //初始化结构体 memset(&ob_qma7981.cur_data,0,sizeof(ob_qma7981.cur_data)); ob_qma7981.cur_param.acc_fs = QMA_ACC_FS_2G; ob_qma7981.cur_param.acc_odr = QMA_ACC_ODR_OFF; // platform_delay_ms(10); ret = soft_reset(); //soft reset all of the registers, need to at least delay 10ms after called the function. if(ret == -1)return -1; return 0; } /** @brief 设置ACC量程 @param acc_fs - [in] ACC量程 @return 错误代码 - [out] -1失败,0成功 */ int drv_qma_set_acc_fs(QMA_ACC_FS_e acc_fs) { int ret; uint8_t range; if(ob_qma7981.cur_param.acc_fs != acc_fs) { switch(acc_fs) { case QMA_ACC_FS_2G: ret = set_full_scale_range(RANGE_2G); //set full scale acceleration range if(ret == -1)return -1; ret = get_full_scale_range(&range); if(ret == -1)return -1; if(range != RANGE_2G)return -1; ob_qma7981.cur_param.acc_fs = QMA_ACC_FS_2G; break; case QMA_ACC_FS_16G: ret = set_full_scale_range(RANGE_16G); //set full scale acceleration range if(ret == -1)return -1; ret = get_full_scale_range(&range); if(ret == -1)return -1; if(range != RANGE_16G)return -1; ob_qma7981.cur_param.acc_fs = QMA_ACC_FS_16G; break; } } return 0; } /** @brief 设置ACC采样频率 @param acc_odr - [in] ACC采样频率 @return 错误代码 - [out] -1失败,0成功 */ int drv_qma_set_acc_odr(QMA_ACC_ODR_e acc_odr) { int ret; uint8_t freq, bandwidth; Qma7981_Mode_t mode; if(ob_qma7981.cur_param.acc_odr != acc_odr) { switch(acc_odr) { case QMA_ACC_ODR_OFF: ret = set_mode(Qma7981_Mode_Standby); //sleep mode if(ret == -1)return -1; ret = get_mode(&mode); if(ret == -1)return -1; if(mode != Qma7981_Mode_Standby)return -1; ob_qma7981.cur_param.acc_odr = QMA_ACC_ODR_OFF; break; case QMA_ACC_ODR_12HZ5: ret = set_mode(Qma7981_Mode_Active); //bring out of sleep mode if(ret == -1)return -1; ret = get_mode(&mode); if(ret == -1)return -1; if(mode != Qma7981_Mode_Active)return -1; //odr = 2 * BW; odr = 2 * (12.5KHZ / 1935) = 12.91HZ; ret = set_clock_freq(CLK_12_KHZ_5); //set digital clock freq if(ret == -1)return -1; ret = get_clock_freq(&freq); if(ret == -1)return -1; if(freq != CLK_12_KHZ_5)return -1; ret = set_bandwidth(MCLK_DIV_BY_1935); //set bandwitch (samples per sec) if(ret == -1)return -1; ret = get_bandwidth(&bandwidth); if(ret == -1)return -1; if(bandwidth != MCLK_DIV_BY_1935)return -1; ob_qma7981.cur_param.acc_odr = QMA_ACC_ODR_12HZ5; break; case QMA_ACC_ODR_104HZ: ret = set_mode(Qma7981_Mode_Active); //bring out of sleep mode if(ret == -1)return -1; ret = get_mode(&mode); if(ret == -1)return -1; if(mode != Qma7981_Mode_Active)return -1; //odr = 2 * BW; odr = 2 * (200KHZ / 3855) = 103.76HZ; ret = set_clock_freq(CLK_200_KHZ); //set digital clock freq if(ret == -1)return -1; ret = get_clock_freq(&freq); if(ret == -1)return -1; if(freq != CLK_200_KHZ)return -1; ret = set_bandwidth(MCLK_DIV_BY_3855); //set bandwitch (samples per sec) if(ret == -1)return -1; ret = get_bandwidth(&bandwidth); if(ret == -1)return -1; if(bandwidth != MCLK_DIV_BY_3855)return -1; ob_qma7981.cur_param.acc_odr = QMA_ACC_ODR_104HZ; break; } } return 0; } /** @brief 获取QMA配置参数 @param param - [in] QMA配置参数 @return 错误代码 - [out] -1失败,0成功 */ int drv_qma_get_config_param(drv_qma_config_param_t *p_param) { *p_param = ob_qma7981.cur_param; return 0; } /** @brief 获取QMA的ACC数据 @param p_data - [out] 返回的ACC三轴数据 @return 错误代码 - [out] -1失败,0成功 */ int drv_qma_get_acc_data(qma_data_t *p_data) { int ret; if(ob_qma7981.cur_param.acc_odr != QMA_ACC_ODR_OFF) { memset( &data_raw_acceleration, 0, sizeof(data_raw_acceleration)); ret = get_accel_x(&data_raw_acceleration.acc[0]); if(ret == -1)return -1; ret = get_accel_y(&data_raw_acceleration.acc[1]); if(ret == -1)return -1; ret = get_accel_z(&data_raw_acceleration.acc[2]); if(ret == -1)return -1; p_data->acc[0] = data_raw_acceleration.acc[0]; p_data->acc[1] = data_raw_acceleration.acc[1]; p_data->acc[2] = data_raw_acceleration.acc[2]; } else { return -1; } return 0; }