Iis2dh
Contents
- 1 ^ Overview
- 2 ^ IIS2DH Control Registers As C Structures
- 3 ^ Possible Zephyr Driver Routines for IIS2DH
- 4 ^ Zephyr RTOS Tie-In To STMicro IIS2DH Driver API
- 5 ^ Assignment To ctx Structure
- 6 ^ Why Does iis2dh_device_id_get Return 3 When No Device Connected?
- 7 ^ Configuration Post from ST Micro Forum
- 8 *
- 9 ^ LIS2DH Data Structure
- 10 ^ LIS2DH Sample Fetch Routine
- 11 ^ LIS2DH Sample Get Routine Supports Writing Multiple sensor_type Data
- 12 ^ ENOTSUP Definition - return value of -134
- 13 ^ iis2dh_attr_set() calls iis2dh_config()
- 14 ^ edit point
- 15 *
- 16 ^ LIS2DH Driver Output at Boot Time
- 17 ^ IIS2DH Test App Output with SDA Disconnected
- 18 *
- 19 ^ STMicro Forum
- 20 ^ edit point
- 21 ^ edit point
- 22 ^ edit point
^ Overview
Notes on STMicro IIS22DH high frequency MEMS accelerometer, and drivers for IIS2DH and LIS2DH sensors in Nordic Semi sdk-nrf 1.6.1. A promising top-level starting point to understand STMicro's significant code contribution to the drivers of these and related sensors is here:
Some promising more specific STMicro projects on Github:
- https://github.com/STMicroelectronics/iis2dh
- https://github.com/STMicroelectronics/STMems_Standard_C_drivers/tree/master/iis2dh_STdC/examples
- https://github.com/STMicroelectronics/STMems_Standard_C_drivers/blob/master/iis2dh_STdC/examples/iis2dh_read_data_polling.c
A non-Zephyr forum post with configuration steps outlined (not obvious how these would all be achieved in Zephyr context):
- https://community.st.com/s/question/0D50X00009XkdfV/understanding-acceleration-data-from-iis2dh (Excerpt from this post copied to section below . . .)
Symbols in the polling code example from STMicroelectronic/iis2dh repo are defined in a sub-part of sdk-nrf modules/hal/ project space:
guest@ubuntu-vm:~/embedded/z3-on-var/modules/hal/st$ grep -nr IIS2DH_2g ./* ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.h:742: IIS2DH_2g = 0, ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c:636: case IIS2DH_2g: ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c:637: *val = IIS2DH_2g; ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c:649: *val = IIS2DH_2g; guest@ubuntu-vm:~/embedded/z3-on-var/modules/hal/st$ grep -nr IIS2DH_ODR_1Hz ./* ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.h:707: IIS2DH_ODR_1Hz = 0x01, ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c:402: case IIS2DH_ODR_1Hz: ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c:403: *val = IIS2DH_ODR_1Hz; guest@ubuntu-vm:~/embedded/z3-on-var/modules/hal/st$ grep -nr IIS2DH_TEMP_ENABLE ./* ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.h:692: IIS2DH_TEMP_ENABLE = 3, ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c:285: case IIS2DH_TEMP_ENABLE: ./sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c:286: *val = IIS2DH_TEMP_ENABLE; guest@ubuntu-vm:~/embedded/z3-on-var/modules/hal/st$ grep -nr PROPERTY_ENABLE ./*
And really the above project is pulled in to Nordic's sdk-nrf. Looks to be at a slightly older, or at least different version, but driver header and sources at https://github.com/STMicroelectronics/iis2dh are the same as in the STMicro hal code in Nordic sdk-nrf v1.6.1.
^ IIS2DH Control Registers As C Structures
An excerpt from Nordic sdk-nrf modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.h
:
#define IIS2DH_CTRL_REG4 0x23U typedef struct { #if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN uint8_t sim : 1; uint8_t st : 2; uint8_t hr : 1; uint8_t fs : 2; uint8_t ble : 1; uint8_t bdu : 1; #elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN uint8_t bdu : 1; uint8_t ble : 1; uint8_t fs : 2; uint8_t hr : 1; uint8_t st : 2; uint8_t sim : 1; #endif /* DRV_BYTE_ORDER */ } iis2dh_ctrl_reg4_t;
^ Possible Zephyr Driver Routines for IIS2DH
Following routines are not obvious nor mentioned anywhere in Zephyr Project documentation found so far, nor are they hinted at by any trace of analogous routines in the build artifacts of Zephyr lis2dh sample app. But may we use these following routines in app level code we want to have talking with STMicro's IIS2DH sensor? . . .
1 guest@vm:~/projects/zephyr-based/project-stage-1$ grep -nr stmdev_ctx_t ./* 2 Binary file ./build/zephyr/zephyr.elf matches 3 Binary file ./build/zephyr/zephyr_prebuilt.elf matches 4 ./build/zephyr/zephyr.lst:55935:int32_t iis2dh_read_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, 5 ./build/zephyr/zephyr.lst:55953:int32_t iis2dh_write_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, 6 ./build/zephyr/zephyr.lst:55973:int32_t iis2dh_operating_mode_set(stmdev_ctx_t *ctx, iis2dh_op_md_t val) 7 ./build/zephyr/zephyr.lst:56065:int32_t iis2dh_data_rate_set(stmdev_ctx_t *ctx, iis2dh_odr_t val) 8 ./build/zephyr/zephyr.lst:56107:int32_t iis2dh_full_scale_set(stmdev_ctx_t *ctx, iis2dh_fs_t val) 9 ./build/zephyr/zephyr.lst:56149:int32_t iis2dh_block_data_update_set(stmdev_ctx_t *ctx, uint8_t val) 10 ./build/zephyr/zephyr.lst:56191:int32_t iis2dh_acceleration_raw_get(stmdev_ctx_t *ctx, int16_t *val) 11 ./build/zephyr/zephyr.lst:56231:int32_t iis2dh_device_id_get(stmdev_ctx_t *ctx, uint8_t *buff) 12 Binary file ./build/zephyr/CMakeFiles/zephyr.dir/home/ted/projects/zephyr-based/modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c.obj matches 13 Binary file ./build/zephyr/libzephyr.a matches 14 Binary file ./build/zephyr/drivers/sensor/iis2dh/CMakeFiles/drivers__sensor__iis2dh.dir/iis2dh_trigger.c.obj matches 15 Binary file ./build/zephyr/drivers/sensor/iis2dh/CMakeFiles/drivers__sensor__iis2dh.dir/iis2dh_i2c.c.obj matches 16 Binary file ./build/zephyr/drivers/sensor/iis2dh/CMakeFiles/drivers__sensor__iis2dh.dir/iis2dh.c.obj matches 17 Binary file ./build/zephyr/drivers/sensor/iis2dh/libdrivers__sensor__iis2dh.a matches 18 guest@vm:~/projects/zephyr-based/project-stage-1$
Looks like there are actually many more IIS2DH API routines available in the modules/hal source file from STMicro:
ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver$ grep -nr stmdev_ctx_t ./*.c 49:int32_t iis2dh_read_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, 67:int32_t iis2dh_write_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, 182:int32_t iis2dh_temp_status_reg_get(stmdev_ctx_t *ctx, uint8_t *buff) 196:int32_t iis2dh_temp_data_ready_get(stmdev_ctx_t *ctx, uint8_t *val) 215:int32_t iis2dh_temp_data_ovr_get(stmdev_ctx_t *ctx, uint8_t *val) 234:int32_t iis2dh_temperature_raw_get(stmdev_ctx_t *ctx, int16_t *val) 253:int32_t iis2dh_temperature_meas_set(stmdev_ctx_t *ctx, iis2dh_temp_en_t val) 275:int32_t iis2dh_temperature_meas_get(stmdev_ctx_t *ctx, iis2dh_temp_en_t *val) 304:int32_t iis2dh_operating_mode_set(stmdev_ctx_t *ctx, iis2dh_op_md_t val) 343:int32_t iis2dh_operating_mode_get(stmdev_ctx_t *ctx, iis2dh_op_md_t *val) 371:int32_t iis2dh_data_rate_set(stmdev_ctx_t *ctx, iis2dh_odr_t val) 392:int32_t iis2dh_data_rate_get(stmdev_ctx_t *ctx, iis2dh_odr_t *val) 445:int32_t iis2dh_high_pass_on_outputs_set(stmdev_ctx_t *ctx, uint8_t val) 467:int32_t iis2dh_high_pass_on_outputs_get(stmdev_ctx_t *ctx, uint8_t *val) 492:int32_t iis2dh_high_pass_bandwidth_set(stmdev_ctx_t *ctx, iis2dh_hpcf_t val) 519:int32_t iis2dh_high_pass_bandwidth_get(stmdev_ctx_t *ctx, iis2dh_hpcf_t *val) 553:int32_t iis2dh_high_pass_mode_set(stmdev_ctx_t *ctx, iis2dh_hpm_t val) 574:int32_t iis2dh_high_pass_mode_get(stmdev_ctx_t *ctx, iis2dh_hpm_t *val) 608:int32_t iis2dh_full_scale_set(stmdev_ctx_t *ctx, iis2dh_fs_t val) 629:int32_t iis2dh_full_scale_get(stmdev_ctx_t *ctx, iis2dh_fs_t *val) 663:int32_t iis2dh_block_data_update_set(stmdev_ctx_t *ctx, uint8_t val) 684:int32_t iis2dh_block_data_update_get(stmdev_ctx_t *ctx, uint8_t *val) 704:int32_t iis2dh_filter_reference_set(stmdev_ctx_t *ctx, uint8_t *buff) 720:int32_t iis2dh_filter_reference_get(stmdev_ctx_t *ctx, uint8_t *buff) 734:int32_t iis2dh_xl_data_ready_get(stmdev_ctx_t *ctx, uint8_t *val) 752:int32_t iis2dh_xl_data_ovr_get(stmdev_ctx_t *ctx, uint8_t *val) 770:int32_t iis2dh_acceleration_raw_get(stmdev_ctx_t *ctx, int16_t *val) 804:int32_t iis2dh_device_id_get(stmdev_ctx_t *ctx, uint8_t *buff) 818:int32_t iis2dh_self_test_set(stmdev_ctx_t *ctx, iis2dh_st_t val) 839:int32_t iis2dh_self_test_get(stmdev_ctx_t *ctx, iis2dh_st_t *val) 870:int32_t iis2dh_data_format_set(stmdev_ctx_t *ctx, iis2dh_ble_t val) 891:int32_t iis2dh_data_format_get(stmdev_ctx_t *ctx, iis2dh_ble_t *val) 919:int32_t iis2dh_boot_set(stmdev_ctx_t *ctx, uint8_t val) 940:int32_t iis2dh_boot_get(stmdev_ctx_t *ctx, uint8_t *val) 959:int32_t iis2dh_int_occurrencies_get(stmdev_ctx_t *ctx, uint8_t *val) 974:int32_t iis2dh_status_get(stmdev_ctx_t *ctx, iis2dh_status_reg_t *val) 1001:int32_t iis2dh_int1_gen_conf_set(stmdev_ctx_t *ctx, iis2dh_int1_cfg_t *val) 1016:int32_t iis2dh_int1_gen_conf_get(stmdev_ctx_t *ctx, iis2dh_int1_cfg_t *val) 1031:int32_t iis2dh_int1_gen_source_get(stmdev_ctx_t *ctx, iis2dh_int1_src_t *val) 1047:int32_t iis2dh_int1_gen_threshold_set(stmdev_ctx_t *ctx, uint8_t val) 1070:int32_t iis2dh_int1_gen_threshold_get(stmdev_ctx_t *ctx, uint8_t *val) 1090:int32_t iis2dh_int1_gen_duration_set(stmdev_ctx_t *ctx, uint8_t val) 1114:int32_t iis2dh_int1_gen_duration_get(stmdev_ctx_t *ctx, uint8_t *val) 1147:int32_t iis2dh_int2_gen_conf_set(stmdev_ctx_t *ctx, iis2dh_int2_cfg_t *val) 1162:int32_t iis2dh_int2_gen_conf_get(stmdev_ctx_t *ctx, iis2dh_int2_cfg_t *val) 1176:int32_t iis2dh_int2_gen_source_get(stmdev_ctx_t *ctx, iis2dh_int2_src_t *val) 1192:int32_t iis2dh_int2_gen_threshold_set(stmdev_ctx_t *ctx, uint8_t val) 1215:int32_t iis2dh_int2_gen_threshold_get(stmdev_ctx_t *ctx, uint8_t *val) 1235:int32_t iis2dh_int2_gen_duration_set(stmdev_ctx_t *ctx, uint8_t val) 1259:int32_t iis2dh_int2_gen_duration_get(stmdev_ctx_t *ctx, uint8_t *val) 1291:int32_t iis2dh_high_pass_int_conf_set(stmdev_ctx_t *ctx, iis2dh_hp_t val) 1312:int32_t iis2dh_high_pass_int_conf_get(stmdev_ctx_t *ctx, iis2dh_hp_t *val) 1358:int32_t iis2dh_pin_int1_config_set(stmdev_ctx_t *ctx, iis2dh_ctrl_reg3_t *val) 1373:int32_t iis2dh_pin_int1_config_get(stmdev_ctx_t *ctx, iis2dh_ctrl_reg3_t *val) 1389:int32_t iis2dh_int2_pin_detect_4d_set(stmdev_ctx_t *ctx, uint8_t val) 1411:int32_t iis2dh_int2_pin_detect_4d_get(stmdev_ctx_t *ctx, uint8_t *val) 1432:int32_t iis2dh_int2_pin_notification_mode_set(stmdev_ctx_t *ctx, 1456:int32_t iis2dh_int2_pin_notification_mode_get(stmdev_ctx_t *ctx, 1486:int32_t iis2dh_int1_pin_detect_4d_set(stmdev_ctx_t *ctx, uint8_t val) 1508:int32_t iis2dh_int1_pin_detect_4d_get(stmdev_ctx_t *ctx, uint8_t *val) 1528:int32_t iis2dh_int1_pin_notification_mode_set(stmdev_ctx_t *ctx, 1551:int32_t iis2dh_int1_pin_notification_mode_get(stmdev_ctx_t *ctx, 1580:int32_t iis2dh_pin_int2_config_set(stmdev_ctx_t *ctx, iis2dh_ctrl_reg6_t *val) 1595:int32_t iis2dh_pin_int2_config_get(stmdev_ctx_t *ctx, iis2dh_ctrl_reg6_t *val) 1622:int32_t iis2dh_fifo_set(stmdev_ctx_t *ctx, uint8_t val) 1643:int32_t iis2dh_fifo_get(stmdev_ctx_t *ctx, uint8_t *val) 1662:int32_t iis2dh_fifo_watermark_set(stmdev_ctx_t *ctx, uint8_t val) 1685:int32_t iis2dh_fifo_watermark_get(stmdev_ctx_t *ctx, uint8_t *val) 1705:int32_t iis2dh_fifo_trigger_event_set(stmdev_ctx_t *ctx, iis2dh_tr_t val) 1728:int32_t iis2dh_fifo_trigger_event_get(stmdev_ctx_t *ctx, iis2dh_tr_t *val) 1757:int32_t iis2dh_fifo_mode_set(stmdev_ctx_t *ctx, iis2dh_fm_t val) 1780:int32_t iis2dh_fifo_mode_get(stmdev_ctx_t *ctx, iis2dh_fm_t *val) 1815:int32_t iis2dh_fifo_status_get(stmdev_ctx_t *ctx, iis2dh_fifo_src_reg_t *val) 1829:int32_t iis2dh_fifo_data_level_get(stmdev_ctx_t *ctx, uint8_t *val) 1847:int32_t iis2dh_fifo_empty_flag_get(stmdev_ctx_t *ctx, uint8_t *val) 1865:int32_t iis2dh_fifo_ovr_flag_get(stmdev_ctx_t *ctx, uint8_t *val) 1883:int32_t iis2dh_fifo_fth_flag_get(stmdev_ctx_t *ctx, uint8_t *val) 1914:int32_t iis2dh_tap_conf_set(stmdev_ctx_t *ctx, iis2dh_click_cfg_t *val) 1929:int32_t iis2dh_tap_conf_get(stmdev_ctx_t *ctx, iis2dh_click_cfg_t *val) 1943:int32_t iis2dh_tap_source_get(stmdev_ctx_t *ctx, iis2dh_click_src_t *val) 1958:int32_t iis2dh_tap_threshold_set(stmdev_ctx_t *ctx, uint8_t val) 1980:int32_t iis2dh_tap_threshold_get(stmdev_ctx_t *ctx, uint8_t *val) 2001:int32_t iis2dh_shock_dur_set(stmdev_ctx_t *ctx, uint8_t val) 2024:int32_t iis2dh_shock_dur_get(stmdev_ctx_t *ctx, uint8_t *val) 2046:int32_t iis2dh_quiet_dur_set(stmdev_ctx_t *ctx, uint8_t val) 2071:int32_t iis2dh_quiet_dur_get(stmdev_ctx_t *ctx, uint8_t *val) 2093:int32_t iis2dh_double_tap_timeout_set(stmdev_ctx_t *ctx, uint8_t val) 2117:int32_t iis2dh_double_tap_timeout_get(stmdev_ctx_t *ctx, uint8_t *val) 2151:int32_t iis2dh_act_threshold_set(stmdev_ctx_t *ctx, uint8_t val) 2174:int32_t iis2dh_act_threshold_get(stmdev_ctx_t *ctx, uint8_t *val) 2194:int32_t iis2dh_act_timeout_set(stmdev_ctx_t *ctx, uint8_t val) 2216:int32_t iis2dh_act_timeout_get(stmdev_ctx_t *ctx, uint8_t *val) 2248:int32_t iis2dh_spi_mode_set(stmdev_ctx_t *ctx, iis2dh_sim_t val) 2269:int32_t iis2dh_spi_mode_get(stmdev_ctx_t *ctx, iis2dh_sim_t *val) ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver$
^ Zephyr RTOS Tie-In To STMicro IIS2DH Driver API
File `$ENV{ZEPHYR_BASE}/drivers/sensor/iis2dh/iis2dh.c` end with:ii2
312 struct iis2dh_data iis2dh_data; 313 314 DEVICE_DT_INST_DEFINE(0, iis2dh_init, NULL, 315 &iis2dh_data, &iis2dh_cfg, POST_KERNEL, 316 CONFIG_SENSOR_INIT_PRIORITY, &iis2dh_driver_api);
...Somewhere data member `&iis2dh_data` gets assigned a value. This data member in turn is referenced when the above driver API routines are called. For example in routine `iis2dh_sample_fetch()` there is reference to an stmdev_ctx_t structure that's assigned to the device handle's "data" member:
203 static int iis2dh_sample_fetch(const struct device *dev, 204 enum sensor_channel chan) 205 { 206 struct iis2dh_data *iis2dh = dev->data; 207 int16_t buf[3]; 208 209 /* fetch raw data sample */ 210 if (iis2dh_acceleration_raw_get(iis2dh->ctx, buf) < 0) { 211 LOG_DBG("Failed to fetch raw data sample"); 212 return -EIO; 213 } 214 215 iis2dh->acc[0] = sys_le16_to_cpu(buf[0]); 216 iis2dh->acc[1] = sys_le16_to_cpu(buf[1]); 217 iis2dh->acc[2] = sys_le16_to_cpu(buf[2]); 218 219 return 0; 220 }
The structure `iis2dh_data` is defined in corresponding header file `iis2dh.h` as:
59 /* sensor data */ 60 struct iis2dh_data { 61 const struct device *bus; 62 int16_t acc[3]; 63 uint32_t gain; 64 65 stmdev_ctx_t *ctx; 66 #ifdef CONFIG_IIS2DH_TRIGGER 67 const struct device *dev; 68 const struct device *gpio; 69 uint8_t gpio_pin; 70 struct gpio_callback gpio_cb; 71 sensor_trigger_handler_t drdy_handler; 72 #if defined(CONFIG_IIS2DH_TRIGGER_OWN_THREAD) 73 K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_IIS2DH_THREAD_STACK_SIZE); 74 struct k_thread thread; 75 struct k_sem gpio_sem; 76 #elif defined(CONFIG_IIS2DH_TRIGGER_GLOBAL_THREAD) 77 struct k_work work; 78 #endif /* CONFIG_IIS2DH_TRIGGER_GLOBAL_THREAD */ 79 #endif /* CONFIG_IIS2DH_TRIGGER */ 80 #if DT_INST_SPI_DEV_HAS_CS_GPIOS(0) 81 struct spi_cs_control cs_ctrl; 82 #endif 83 };
Structure stmdev_ctx_t
is defined:
110 typedef int32_t (*stmdev_write_ptr)(void *, uint8_t, uint8_t*, uint16_t); 111 typedef int32_t (*stmdev_read_ptr) (void *, uint8_t, uint8_t*, uint16_t); 112 113 typedef struct { 114 /** Component mandatory fields **/ 115 stmdev_write_ptr write_reg; 116 stmdev_read_ptr read_reg; 117 /** Customizable optional pointer **/ 118 void *handle; 119 } stmdev_ctx_t;
^ Assignment To ctx Structure
So question, where is *ctx assigned a value? Ah, *ctx is assigned a value in the zephyr/drivers/sensor/iis2dh/iis2dh_i2c.c file. This value is a data structure which entails two C function pointers. Code excerpt here:
19 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) 20 21 static uint16_t iis2dh_i2c_slave_addr = DT_INST_REG_ADDR(0); 22 23 LOG_MODULE_DECLARE(IIS2DH, CONFIG_SENSOR_LOG_LEVEL); 24 25 static int iis2dh_i2c_read(struct iis2dh_data *data, uint8_t reg_addr, 26 uint8_t *value, uint16_t len) 27 { 28 return i2c_burst_read(data->bus, iis2dh_i2c_slave_addr, 29 reg_addr | 0x80, value, len); 30 } 31 32 static int iis2dh_i2c_write(struct iis2dh_data *data, uint8_t reg_addr, 33 uint8_t *value, uint16_t len) 34 { 35 return i2c_burst_write(data->bus, iis2dh_i2c_slave_addr, 36 reg_addr | 0x80, value, len); 37 } 38 39 stmdev_ctx_t iis2dh_i2c_ctx = { 40 .read_reg = (stmdev_read_ptr) iis2dh_i2c_read, 41 .write_reg = (stmdev_write_ptr) iis2dh_i2c_write, 42 }; 43 44 int iis2dh_i2c_init(const struct device *dev) 45 { 46 struct iis2dh_data *data = dev->data; 47 48 data->ctx = &iis2dh_i2c_ctx; 49 data->ctx->handle = data; 50 51 return 0; 52 } 53 #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */
^ edit point
Routine `int i2c_burst_read()` is a Zephyr in-tree driver routine. In possibly erroneous C syntax the assignment from the Zephyr device structure handle on down is: dev->data->ctx->.read_reg = (stmdev_read_ptr) iis2dh_i2c_read ^^^ : Valid when *data is typecast as `struct iis2dh_data`. Routine `iis2dh_i2c_read` calls `i2c_burst_read()`, which in turn calls `i2c_write_read()`, which in turn calls 'i2c_transfer()', which would seem to be the final routine call in this call stack but in fact is a pointer to a project's given processor specific I2C peripheral: 346 __syscall int i2c_transfer(const struct device *dev, 347 struct i2c_msg *msgs, uint8_t num_msgs, 348 uint16_t addr); 349 350 static inline int z_impl_i2c_transfer(const struct device *dev, 351 struct i2c_msg *msgs, uint8_t num_msgs, 352 uint16_t addr) 353 { 354 const struct i2c_driver_api *api = 355 (const struct i2c_driver_api *)dev->api; 356 357 return api->transfer(dev, msgs, num_msgs, addr); 358 }
|
Excerpt from zephyr/include/drivers/i2c.h: 607 static inline int i2c_burst_read(const struct device *dev, 608 uint16_t dev_addr, 609 uint8_t start_addr, 610 uint8_t *buf, 611 uint32_t num_bytes) 612 { 613 return i2c_write_read(dev, dev_addr, 614 &start_addr, sizeof(start_addr), 615 buf, num_bytes); 616 } 572 static inline int i2c_write_read(const struct device *dev, uint16_t addr, 573 const void *write_buf, size_t num_write, 574 void *read_buf, size_t num_read) 575 { 576 struct i2c_msg msg[2]; 577 578 msg[0].buf = (uint8_t *)write_buf; 579 msg[0].len = num_write; 580 msg[0].flags = I2C_MSG_WRITE; 581 582 msg[1].buf = (uint8_t *)read_buf; 583 msg[1].len = num_read; 584 msg[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | I2C_MSG_STOP; 585 586 return i2c_transfer(dev, msg, 2, addr); 587 } 346 __syscall int i2c_transfer(const struct device *dev, 347 struct i2c_msg *msgs, uint8_t num_msgs, 348 uint16_t addr); 349 350 static inline int z_impl_i2c_transfer(const struct device *dev, 351 struct i2c_msg *msgs, uint8_t num_msgs, 352 uint16_t addr) 353 { 354 const struct i2c_driver_api *api = 355 (const struct i2c_driver_api *)dev->api; 356 357 return api->transfer(dev, msgs, num_msgs, addr); 358 } |
^ Why Does iis2dh_device_id_get Return 3 When No Device Connected?
Following routine in STMicro's driver API code base returns ASCII "3", decimal 51 even when no device connected. This is the ID value spec'd in the IIS2DH datasheet. How is this value getting returned? The routine in question is here:
796 /** 797 * @brief DeviceWhoamI .[get] 798 * 799 * @param ctx read / write interface definitions 800 * @param buff buffer that stores data read 801 * @retval interface status (MANDATORY: return 0 -> no Error) 802 * 803 */ 804 int32_t iis2dh_device_id_get(stmdev_ctx_t *ctx, uint8_t *buff) 805 { 806 int32_t ret; 807 ret = iis2dh_read_reg(ctx, IIS2DH_WHO_AM_I, buff, 1); 808 return ret; 809 }
From comment block it looks like the value 3 in our situation is an error value coming from routine `iis2dh_read_reg()`. This routine is defined:
39 /** 40 * @brief Read generic device register 41 * 42 * @param ctx read / write interface definitions(ptr) 43 * @param reg register to read 44 * @param data pointer to buffer that store the data read(ptr) 45 * @param len number of consecutive register to read 46 * @retval interface status (MANDATORY: return 0 -> no Error) 47 * 48 */ 49 int32_t iis2dh_read_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, 50 uint16_t len) 51 { 52 int32_t ret; 53 ret = ctx->read_reg(ctx->handle, reg, data, len); 54 return ret; 55 }
Ok this begs the question then where and how are the ctx function pointers assigned to point to functions? We were working to answer this question yesterday, and it is something of a repeat of a key question we faced when building the out-of-tree driver for Kionix sensor . . .
^ Configuration Post from ST Micro Forum
Excerpt on accelerator configurations from this developer: mhackney (Community Member) Edited by ST Community July 21, 2018 at 5:26 PM Posted on January 02, 2017 at 16:21
Ok, I overlooked the data sheet that Low Power mode is 8-bit data output. But that still does not explain why the data is not left aligned?
Also, here is my complete configuration to sanity check:
// setup CTRL_REG1
accelerometer_write(CTRL_REG1, 0b10011100); // ODR 5.376kHz in LPMode [7-4] Low power enable [3] Z enable [2]
// setup CTRL_REG2
accelerometer_write(CTRL_REG2, 0b00110001); //
// setup CTRL_REG3
accelerometer_write(CTRL_REG3, 0b01000000); // AOI (And Or Interrupt) on INT1 en [6]
// setup CTRL_REG6
accelerometer_write(CTRL_REG6, 0b00000000); //
// setup CTRL_REG4
accelerometer_write(CTRL_REG4, 0b00110000); // Full-scale selection 16G [5-4]
// setup CTRL_REG5
accelerometer_write(CTRL_REG5, 0b01001010); // FIFO enable [6] Latch INT1 [3]
// setup INT1_CFG
accelerometer_write(INT1_CFG, 0b00100000); // ZHIE enabled [5]
// setup INT1_THS
accelerometer_write(INT1_THS, Z_PROBE_SENSITIVITY); // 40
// setup INT1_DURATION
accelerometer_write(INT1_DURATION, 0);
*
^ LIS2DH Data Structure
LIS2DH data structure, comparable to IIS2DH data structure but no `ctx` element:
LIS2DH data structure definition: 220 struct lis2dh_data { 221 const struct device *bus; 222 const struct lis2dh_transfer_function *hw_tf; 223 224 union lis2dh_sample sample; 225 /* current scaling factor, in micro m/s^2 / lsb */ 226 uint32_t scale; 227 228 #ifdef CONFIG_LIS2DH_TRIGGER 229 const struct device *dev; 230 const struct device *gpio_int1; 231 const struct device *gpio_int2; 232 struct gpio_callback gpio_int1_cb; 233 struct gpio_callback gpio_int2_cb; 234 235 sensor_trigger_handler_t handler_drdy; 236 sensor_trigger_handler_t handler_anymotion; 237 atomic_t trig_flags; 238 enum sensor_channel chan_drdy; 239 240 #if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD) 241 K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_LIS2DH_THREAD_STACK_SIZE); 242 struct k_thread thread; 243 struct k_sem gpio_sem; 244 #elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD) 245 struct k_work work; 246 #endif 247 248 #endif /* CONFIG_LIS2DH_TRIGGER */ 249 250 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) 251 struct spi_cs_control cs_ctrl; 252 #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ 253 }; |
LIS2DH 'transfer function' structure definition: 207 struct lis2dh_transfer_function { 208 int (*read_data)(const struct device *dev, uint8_t reg_addr, 209 uint8_t *value, uint8_t len); 210 int (*write_data)(const struct device *dev, uint8_t reg_addr, 211 uint8_t *value, uint8_t len); 212 int (*read_reg)(const struct device *dev, uint8_t reg_addr, 213 uint8_t *value); 214 int (*write_reg)(const struct device *dev, uint8_t reg_addr, 215 uint8_t value); 216 int (*update_reg)(const struct device *dev, uint8_t reg_addr, 217 uint8_t mask, uint8_t value); 218 }; LIS2DH 'transfer function' structure assignment, in file ./drivers/sensor/lis2ds12/lis2ds12_i2c.c:72: 68 int lis2ds12_i2c_init(const struct device *dev) 69 { 70 struct lis2ds12_data *data = dev->data; 71 72 data->hw_tf = &lis2ds12_i2c_transfer_fn; 73 74 return 0; 75 } 76 #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ This transfer function is a structure of five function pointers: 60 static const struct lis2ds12_transfer_function lis2ds12_i2c_transfer_fn = { 61 .read_data = lis2ds12_i2c_read_data, 62 .write_data = lis2ds12_i2c_write_data, 63 .read_reg = lis2ds12_i2c_read_reg, 64 .write_reg = lis2ds12_i2c_write_reg, 65 .update_reg = lis2ds12_i2c_update_reg, 66 }; 67 These functions are defines nearby just above, first two for examples are: 17 #include "lis2ds12.h" 18 19 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) 20 21 static uint16_t lis2ds12_i2c_slave_addr = DT_INST_REG_ADDR(0); 22 23 LOG_MODULE_DECLARE(LIS2DS12, CONFIG_SENSOR_LOG_LEVEL); 24 25 static int lis2ds12_i2c_read_data(struct lis2ds12_data *data, uint8_t reg_addr, 26 uint8_t *value, uint8_t len) 27 { 28 return i2c_burst_read(data->comm_master, lis2ds12_i2c_slave_addr, 29 reg_addr, value, len); 30 } 31 32 static int lis2ds12_i2c_write_data(struct lis2ds12_data *data, uint8_t reg_addr, 33 uint8_t *value, uint8_t len) 34 { 35 return i2c_burst_write(data->comm_master, lis2ds12_i2c_slave_addr, 36 reg_addr, value, len); 37 } |
^ LIS2DH Sample Fetch Routine
What is going on in this LIS2DH API routine by STMicro, especially at line 107?
81 static int lis2dh_sample_fetch(const struct device *dev, 82 enum sensor_channel chan) 83 { 84 struct lis2dh_data *lis2dh = dev->data; 85 size_t i; 86 int status; 87 88 __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || 89 chan == SENSOR_CHAN_ACCEL_XYZ); 90 91 /* 92 * since status and all accel data register addresses are consecutive, 93 * a burst read can be used to read all the samples 94 */ 95 status = lis2dh->hw_tf->read_data(dev, LIS2DH_REG_STATUS, 96 lis2dh->sample.raw, 97 sizeof(lis2dh->sample.raw)); 98 if (status < 0) { 99 LOG_WRN("Could not read accel axis data"); 100 return status; 101 } 102 103 for (i = 116 static inline void iis2dh_channel_get_acc(const struct device *dev, 117 enum sensor_channel chan, 118 struct sensor_value *val) 119 { 120 int i; 121 uint8_t ofs_start, ofs_stop; 122 struct iis2dh_data *iis2dh = dev->data; 123 struct sensor_value *pval = val; 124 125 switch (chan) { 126 case SENSOR_CHAN_ACCEL_X: 127 ofs_start = ofs_stop = 0U; 128 break; 129 case SENSOR_CHAN_ACCEL_Y: 130 ofs_start = ofs_stop = 1U; 131 break; 132 case SENSOR_CHAN_ACCEL_Z: 133 ofs_start = ofs_stop = 2U; 134 break; 135 default: 136 ofs_start = 0U; ofs_stop = 2U; 137 break; 138 } 139 140 for (i = ofs_start; i <= ofs_stop ; i++) { 141 iis2dh_convert(pval++, iis2dh->acc[i], iis2dh->gain); 142 } 143 } 144 145 static int iis2dh_channel_get(const struct device *dev, 146 enum sensor_channel chan, 147 struct sensor_value *val) 148 { 149 switch (chan) { 150 case SENSOR_CHAN_ACCEL_X: 151 case SENSOR_CHAN_ACCEL_Y: 152 case SENSOR_CHAN_ACCEL_Z: 153 case SENSOR_CHAN_ACCEL_XYZ: 154 iis2dh_channel_get_acc(dev, chan, val); 155 return 0; 156 default: 157 LOG_DBG("Channel not supported"); 158 break; 159 } 160 161 return -ENOTSUP; 162 } 0; i < (3 * sizeof(int16_t)); i += sizeof(int16_t)) { 104 int16_t *sample = 105 (int16_t *)&lis2dh->sample.raw[1 + i]; 106 107 *sample = sys_le16_to_cpu(*sample); 108 } 109 110 if (lis2dh->sample.status & LIS2DH_STATUS_DRDY_MASK) { 111 return 0; 112 } 113 114 return -ENODATA; 115 }
^ LIS2DH Sample Get Routine Supports Writing Multiple sensor_type Data
From [ww]/zephyr/drivers/sensor/iis2dh/iis2dh.c, note the use of `pval` and C's unary increment operator to handle returning X, Y and Z triplet readings to calling code:
116 static inline void iis2dh_channel_get_acc(const struct device *dev, 117 enum sensor_channel chan, 118 struct sensor_value *val) 119 { 120 int i; 121 uint8_t ofs_start, ofs_stop; 122 struct iis2dh_data *iis2dh = dev->data; 123 struct sensor_value *pval = val; 124 125 switch (chan) { 126 case SENSOR_CHAN_ACCEL_X: 127 ofs_start = ofs_stop = 0U; 128 <!-- comment --> break; 129 case SENSOR_CHAN_ACCEL_Y: 130 ofs_start = ofs_stop = 1U; 131 break; 132 case SENSOR_CHAN_ACCEL_Z: 133 ofs_start = ofs_stop = 2U; 134 break; 135 default: 136 ofs_start = 0U; ofs_stop = 2U; 137 break; 138 } 139 140 for (i = ofs_start; i <= ofs_stop ; i++) { 141 iis2dh_convert(pval++, iis2dh->acc[i], iis2dh->gain); 142 } 143 } 144 145 static int iis2dh_channel_get(const struct device *dev, 146 enum sensor_channel chan, 147 struct sensor_value *val) 148 { 149 switch (chan) { 150 case SENSOR_CHAN_ACCEL_X: 151 case SENSOR_CHAN_ACCEL_Y: 152 case SENSOR_CHAN_ACCEL_Z: 153 case SENSOR_CHAN_ACCEL_XYZ: 154 iis2dh_channel_get_acc(dev, chan, val); 155 return 0; 156 default: 157 LOG_DBG("Channel not supported"); 158 break; 159 } 160 161 return -ENOTSUP; 162 }
^ ENOTSUP Definition - return value of -134
ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/zephyr$ grep -nr ENOTSUP ./* | grep define | grep ENOTSUP ./drivers/flash/jesd216.h:326: * @retval -ENOTSUP if the erase type index is undefined. ./include/usb/bos.h:67:#define usb_handle_bos(x, y, z) -ENOTSUP ./lib/libc/minimal/include/errno.h:115:#define ENOTSUP 134 /**< Unsupported value */ ./subsys/net/ip/net_shell.c:3973:#define ping_ipv6(...) -ENOTSUP ./subsys/net/ip/net_shell.c:4082:#define ping_ipv4(...) -ENOTSUP ./subsys/usb/os_desc.h:34:#define usb_handle_os_desc(x, y, z) -ENOTSUP ./subsys/usb/os_desc.h:35:#define usb_handle_os_desc_feature(x, y, z) -ENOTSUP ./tests/kernel/fpu_sharing/float_disable/src/k_float_disable.c:39:#define K_FLOAT_DISABLE_SYSCALL_RETVAL -ENOTSUP ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/zephyr$
^ iis2dh_attr_set() calls iis2dh_config()
164 static int iis2dh_config(const struct device *dev, enum sensor_channel chan, 165 enum sensor_attribute attr, 166 const struct sensor_value *val) 167 { 168 switch (attr) { 169 #if (CONFIG_IIS2DH_RANGE == 0) 170 case SENSOR_ATTR_FULL_SCALE: 171 return iis2dh_set_range(dev, sensor_ms2_to_g(val)); 172 #endif 173 #if (CONFIG_IIS2DH_ODR == 0) 174 case SENSOR_ATTR_SAMPLING_FREQUENCY: 175 return iis2dh_set_odr(dev, val->val1); 176 #endif 177 default: 178 LOG_DBG("Acc attribute not supported"); 179 break; 180 } 181 182 return -ENOTSUP; 183 } 184 185 static int iis2dh_attr_set(const struct device *dev, enum sensor_channel chan, 186 enum sensor_attribute attr, 187 const struct sensor_value *val) 188 { 189 switch (chan) { 190 case SENSOR_CHAN_ACCEL_X: 191 case SENSOR_CHAN_ACCEL_Y: 192 case SENSOR_CHAN_ACCEL_Z: 193 case SENSOR_CHAN_ACCEL_XYZ: 194 return iis2dh_config(dev, chan, attr, val); 195 default: 196 LOG_DBG("Attr not supported on %d channel", chan); 197 break; 198 } 199 200 return -ENOTSUP; 201 }
In turn this routine gets called when setting ODR at run time:
86 #if (CONFIG_IIS2DH_ODR == 0) 87 /** 88 * iis2dh_set_odr - set new sampling frequency 89 * @dev: Pointer to instance of struct device (I2C or SPI) 90 * @odr: Output data rate 91 */ 92 static int iis2dh_set_odr(const struct device *dev, uint16_t odr) 93 { 94 struct iis2dh_data *iis2dh = dev->data; 95 const struct iis2dh_device_config *cfg = dev->config; 96 iis2dh_odr_t val; 97 98 val = IIS2DH_ODR_TO_REG_HR(cfg->pm, odr); 99 100 return iis2dh_data_rate_set(iis2dh->ctx, val); 101 } 102 #endif
And in turn this routine:
363 /** 364 * @brief Output data rate selection.[set] 365 * 366 * @param ctx read / write interface definitions 367 * @param val change the values of odr in reg CTRL_REG1 368 * @retval interface status (MANDATORY: return 0 -> no Error) 369 * 370 */ 371 int32_t iis2dh_data_rate_set(stmdev_ctx_t *ctx, iis2dh_odr_t val) 372 { 373 iis2dh_ctrl_reg1_t ctrl_reg1; 374 int32_t ret; 375 376 ret = iis2dh_read_reg(ctx, IIS2DH_CTRL_REG1, (uint8_t*)&ctrl_reg1, 1); 377 if (ret == 0) { 378 ctrl_reg1.odr = (uint8_t)val; 379 ret = iis2dh_write_reg(ctx, IIS2DH_CTRL_REG1, (uint8_t*)&ctrl_reg1, 1); 380 } 381 return ret; 382 }
^ edit point
Text searched in STMicro's IIS2DH mid-level driver directory, containing iis2dh.[ch]:
2032 grep -n _set *.c 2033 grep -n _get *.c ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver$
*
^ LIS2DH Driver Output at Boot Time
Some output from Kionix driver demo, interesting early message, earlier even than Zephyr RTOS banner message:
[00:00:00.408,569] <inf> lis2dh: bus=I2C_1 fs=2, odr=0x4 lp_en=0x0 scale=9576 *** Booting Zephyr OS build v2.6.0-rc1-ncs1 *** +++ Kionix Driver Demo version 0p2 +++ - SUCCESS - found Kionix accelerometer and device is ready - DEV - starting comparative LIS2DH test thread . . .zzz - - zzz Success finding lis2dh device, Call to device_is_ready() says LIS2DH sensor is ready, Device name found to be 'LIS2DH' lis2dh task at loop iteration 0x00000000 Failed to set odr: -134 Sampling at 1 Hz (asuuming so as sensor_attr_set returns 0 status - success sampling at 1 Hz, per call to sensor_attr_get(), #0 @ 1519 ms: x -0.306432 , y -0.383040 , z -9.614304 fetched and got acceleration readings 0, 4294660864 zzz - - zzz zzz - - zzz zzz - - zzz lis2dh task at loop iteration 0x00000001 Failed to set odr: -134 Sampling at 1 Hz (asuuming so as sensor_attr_set returns 0 status - success sampling at 1 Hz, per call to sensor_attr_get(), #1 @ 4525 ms: x -0.421344 , y -0.229824 , z -9.614304 fetched and got acceleration readings 0, 4294545952 zzz - - zzz zzz - - zzz zzz - - zzz lis2dh task at loop iteration 0x00000002 Failed to set odr: -134 Sampling at 1 Hz (asuuming so as sensor_attr_set returns 0 status - success sampling at 1 Hz, per call to sensor_attr_get(), #2 @ 7530 ms: x -0.268128 , y -0.268128 , z -9.576000 fetched and got acceleration readings 0, 4294699168 zzz - - zzz zzz - - zzz zzz - - zzz lis2dh task at loop iteration 0x00000003 Failed to set odr: -134 Sampling at 1 Hz (asuuming so as sensor_attr_set returns 0 status - success sampling at 1 Hz, per call to sensor_attr_get(), #3 @ 10536 ms: x -0.344736 , y -0.344736 , z -9.652608 fetched and got acceleration readings 0, 4294622560 zzz - - zzz . . .
^ IIS2DH Test App Output with SDA Disconnected
zzz - - zzz zzz - - zzz zzz - - zzz zzz - - zzz zzz - - zzz zzz - - zzz iis2dh task at loop iteration 0x0000001F [00:03:08.955,505] <err> i2c_nrfx_twim: Error 0x0BAE0001 occurred for message 0 sensor who-am-i check returns '3', [00:03:08.957,489] <err> i2c_nrfx_twim: Error 0x0BAE0001 occurred for message 0 Failed to set odr: -5 Sampling at 5 Hz (assuming so as sensor_attr_set returns 0 status - success sampling at Output Data Rate (ODR) setting 5, - DEV004 - requesting sensor fetch of all available readings... [00:03:08.961,853] <err> i2c_nrfx_twim: Error 0x0BAE0001 occurred for message 0 ERROR: Update failed: -5 - DEV004 - fetching readings again... [00:03:08.984,313] <err> i2c_nrfx_twim: Error 0x0BAE0001 occurred for message 0 - DEV004 - getting X axis reading only... fetched and got acceleration readings 0, 0
*
^ STMicro Forum
STMicro forum posts regarding IIS2DH: