Nordic Semiconductor nRF5 AirFuel SDK  version 2.2.0
ptu_sensors.c
Go to the documentation of this file.
1 /* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved.
2  *
3  * The information contained herein is property of Nordic Semiconductor ASA.
4  * Terms and conditions of usage are described in detail in NORDIC
5  * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
6  *
7  * Licensees are granted free, non-transferable use of the information. NO
8  * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
9  * the file.
10  *
11  */
12 
16 //lint -save -esym(438,res_impedance_running_average)
17 //lint -save -esym(550,res_impedance_running_average)
18 
19 #include <stdbool.h>
20 #include <stdint.h>
21 #include <string.h>
22 
23 #include "ptu_sensors.h"
24 #include "nrf_assert.h"
25 #include "ptu_registry.h"
26 #include "ptu_test_mux.h"
27 
28 
29 #define PTU_SIZEOF_LOAD_VAL_BUF 8
30 #define PTU_SENSORS_PTX_LOAD_VARIATION_MAX_PERIOD_MS 15
31 #define PTU_SENSORS_PTX_LOAD_VARIATION_MIN_PERIOD_MS 6
32 #define PTU_SENSORS_PTX_LOAD_VARIATION_MAX_PERIOD_TICKS APP_TIMER_TICKS(PTU_SENSORS_PTX_LOAD_VARIATION_MAX_PERIOD_MS, APP_TIMER_PRESCALER)
33 #define PTU_SENSORS_PTX_LOAD_VARIATION_MIN_PERIOD_TICKS APP_TIMER_TICKS(PTU_SENSORS_PTX_LOAD_VARIATION_MIN_PERIOD_MS, APP_TIMER_PRESCALER)
34 #define PREV(val) ((val - 1) < 0 ? PTU_SIZEOF_LOAD_VAL_BUF - 1 : (val - 1))
35 #define NEXT(val) ((val + 1) % PTU_SIZEOF_LOAD_VAL_BUF)
36 #define GOTO_NEXT_IDX(idx) (idx = NEXT(idx))
37 #define GET_BIT(val, idx) ((val >> idx) & 1)
38 
39 /******************************************************************************/
42 /******************************************************************************/
43 
44 
45 typedef struct ptu_load_var_measurement_s {
46  uint32_t ticks;
47  uint16_t val;
49 
51 
52 
54 APP_TIMER_DEF(m_load_var_detected_timer_id);
56 
57 
62 static void m_load_var_detect()
63 {
64  static bool m_res_imp_buf_filled_once = false;
65  static uint8_t m_res_imp_buf_index = 0;
66  static uint16_t m_res_imp_buf[PTU_Z_MEAS_BUF_LENGTH];
67  uint16_t res_impedance_max = 0;
68  uint16_t res_impedance_min = 0xffff;
69  uint16_t res_impedance_var_max = 0;
70  uint32_t err_code;
71 
72  // Save samples to array
73  m_res_imp_buf[m_res_imp_buf_index] = m_ptu_sensor_data.res_impedance;
74 
75  m_res_imp_buf_index++;
76 
77  if(m_res_imp_buf_index == PTU_Z_MEAS_BUF_LENGTH)
78  {
79  m_res_imp_buf_filled_once = true;
80  }
81 
82  m_res_imp_buf_index = (m_res_imp_buf_index % PTU_Z_MEAS_BUF_LENGTH);
83 
84  // Only detect load variation after buffer has been filled at least once
85  if(m_res_imp_buf_filled_once)
86  {
87  // Find max and min values in buffer
88  for (uint8_t i=0; i < PTU_Z_MEAS_BUF_LENGTH; i++)
89  {
90  if(m_res_imp_buf[i] > res_impedance_max)
91  {
92  res_impedance_max = m_res_imp_buf[i];
93  }
94 
95  if(m_res_imp_buf[i] < res_impedance_min)
96  {
97  res_impedance_min = m_res_imp_buf[i];
98  }
99  }
100 
101  res_impedance_var_max = (res_impedance_max - res_impedance_min);
102 
103  if(res_impedance_var_max > PTU_Z_TX_IN_LOAD_DETECT)
104  {
105  m_ptu_sensor_data.load_var_detected = true;
106  err_code = app_timer_stop(m_load_var_detected_timer_id);
107  APP_ERROR_CHECK(err_code);
108  err_code = app_timer_start(m_load_var_detected_timer_id, APP_TIMER_TICKS(PTU_LOAD_VARIATION_DETECT_LIFETIME_MS, APP_TIMER_PRESCALER), NULL);
109  APP_ERROR_CHECK(err_code);
110  }
111  }
112 }
113 
118 static void m_rogue_object_detect(void)
119 {
120  uint32_t i;
121  uint32_t err_code;
122  ptu_reg_item_t * p_reg_item = NULL;
123  uint32_t reported_power_sum = 0;
124  uint32_t poweramp_input_power = (m_ptu_sensor_data.v_ina*m_ptu_sensor_data.i_ina)/ 100000 ;//[1/10 W/]
125 
126  for(i=0;i<PTU_MAX_CONNECTIONS; i++)
127  {
128  err_code = ptu_reg_item_get_from_index(i, &p_reg_item);
129  APP_ERROR_CHECK(err_code);
130 
131  if(p_reg_item->state == REG_ITEM_STATE_REGISTERED)
132  {
133  reported_power_sum += p_reg_item->prev_pru_dynamic.prect;
134  }
135  }
136 
137  if((poweramp_input_power * PTU_POWER_EFFICIENCY) > (reported_power_sum + PTU_ROGUE_OBJECT_DETECT_THRESHOLD))
138  {
139  m_ptu_sensor_data.rogue_obj_detected = true;
140  }
141  else
142  {
143  m_ptu_sensor_data.rogue_obj_detected = false;
144  }
145 }
146 
151 static void m_load_var_detected_timer_handler(void * p_context)
152 {
153  m_ptu_sensor_data.load_var_detected = false;
154 }
155 
162 static bool m_find_first_top(uint8_t * p_first_top, uint8_t last_updated_idx)
163 {
164  uint8_t i;
165 
166  for ( i = NEXT(last_updated_idx); i != last_updated_idx ; GOTO_NEXT_IDX(i))
167  {
168  // Find index where previous value was smaller, and next value is smaller or equal to current
169  if(m_lbe_samples[i].val > m_lbe_samples[NEXT(i)].val)
170  {
171  *p_first_top = i;
172  return true;
173  }
174  }
175 
176  return false;
177 }
178 
185 static bool m_find_bottom(uint8_t * p_bottom, uint8_t first_top)
186 {
187  uint32_t i;
188 
189  for( i = NEXT(first_top); (i != first_top) && (m_lbe_samples[first_top].val - m_lbe_samples[i].val < PTU_P_TX_IN_LOAD_DETECT); GOTO_NEXT_IDX(i))
190  {
192  return false;
193  }
194 
195  *p_bottom = i;
196 
197  return true;
198 }
199 
214 static bool m_find_second_top_and_validate(uint8_t first_top, uint8_t bottom)
215 {
216  uint32_t diff;
217  int8_t i;
218 
219  i = bottom;
220 
221  // Find first point that has large enough P_TX diff from bottom, and that has the correct distance from the first top
222  do
223  {
224  GOTO_NEXT_IDX(i);
225 
226  if(m_lbe_samples[i].val < m_lbe_samples[bottom].val) // we have found a lower bottom, this will not be a second top
227  {
228  bottom = i;
229 
230  // The bottom can not be the second top, jump to next sample.
231  continue;
232  }
233 
234  // If we see a sample with value lower than the previous sample OR (the sample has the same value
235  // as the previous sample AND it is not the same value as the bottom sample), return 'false'
236  // as the sample we are looking at might be part of different load variation period or the duty cycle
237  // of the current period is incorrect.
238  else if( m_lbe_samples[PREV(i)].val > m_lbe_samples[i].val ||
239  (m_lbe_samples[PREV(i)].val == m_lbe_samples[i].val && m_lbe_samples[i].val != m_lbe_samples[bottom].val))
240  return false;
241 
242  diff = ticks_diff(m_lbe_samples[i].ticks, m_lbe_samples[first_top].ticks);
243 
244  if((diff <= PTU_SENSORS_PTX_LOAD_VARIATION_MAX_PERIOD_TICKS && // Timing is correct
246  m_lbe_samples[i].val - m_lbe_samples[bottom].val >= PTU_P_TX_IN_LOAD_DETECT)// Variation from bottom is sufficient
247  {
248  return true;
249  }
250 
251  // Make sure we only iterate once over the circular buffer.
252  // We can not compare the ticks to see if we have gone through the circular buffer as you might have overflow situations in the ticks value.
253  }while(i != first_top);
254 
255  return false;
256 }
257 
263 static void m_save_ptx_sample(uint8_t last_updated_index)
264 {
265  uint32_t err_code, curr_ticks;
266 
267  err_code = ptu_tmux_ptx_get(&(m_ptu_sensor_data.p_tx_in));
268  APP_ERROR_CHECK(err_code);
269 
270  err_code = app_timer_cnt_get(&curr_ticks);
271  APP_ERROR_CHECK(err_code);
272 
273  // Store sample with current PTX value and timestamp
274  m_lbe_samples[last_updated_index].val = m_ptu_sensor_data.p_tx_in;
275  m_lbe_samples[last_updated_index].ticks = curr_ticks;
276 }
279 /******************************************************************************/
282 /******************************************************************************/
283 
284 uint32_t ptu_sensors_init(ptu_sm_handler_t sm_handler)
285 {
286  uint32_t err_code;
287 
288  if(sm_handler == NULL)
289  return NRF_ERROR_INVALID_PARAM;
290 
291  m_sm_handler = sm_handler;
292 
293  err_code = app_timer_create(&m_load_var_detected_timer_id,
294  APP_TIMER_MODE_SINGLE_SHOT,
296  APP_ERROR_CHECK(err_code);
297 
298  memset(&m_ptu_sensor_data, 0, sizeof(ptu_sensor_data_t));
299 
300  return NRF_SUCCESS;
301 }
302 
303 uint32_t ptu_sensors_read(void)
304 {
305  uint32_t err_code;
306  bool poweramp_on;
307 
308  // Read all sensors
309  err_code = ptu_tmux_vtx_get(&m_ptu_sensor_data.v_ina);
310  if (err_code != NRF_SUCCESS) return err_code;
311 
312  err_code = ptu_tmux_itx_get(&m_ptu_sensor_data.i_ina);
313  if (err_code != NRF_SUCCESS) return err_code;
314 
315  err_code = ptu_tmux_ptx_get(&m_ptu_sensor_data.p_tx_in);
316  if (err_code != NRF_SUCCESS) return err_code;
317 
318  err_code = ptu_tmux_temperature_get(&m_ptu_sensor_data.temperature);
319  if (err_code != NRF_SUCCESS) return err_code;
320 
321  if(!m_ptu_sensor_data.temperature_warning)
322  {
323  if(m_ptu_sensor_data.temperature > PTU_TEMPERATURE_WARNING_SET)
324  {
325  m_ptu_sensor_data.temperature_warning = true;
326  }
327  }
328  else
329  {
330  if(m_ptu_sensor_data.temperature < PTU_TEMPERATURE_WARNING_CLEAR)
331  {
332  m_ptu_sensor_data.temperature_warning = false;
333  }
334  }
335 
336  // Derive load impedance from vtx and itx if resonator is on
337  err_code = ptu_tmux_poweramp_enable_get(&poweramp_on);
338  if (err_code != NRF_SUCCESS) return err_code;
339 
340  if(poweramp_on)
341  {
342  if(m_ptu_sensor_data.i_ina > 0)
343  {
344  m_ptu_sensor_data.res_impedance = m_ptu_sensor_data.v_ina / m_ptu_sensor_data.i_ina;
345  }
346  else
347  {
348  m_ptu_sensor_data.res_impedance = 0xffff; // "infinite"
349  }
350 
351  // Load variatio detection
353 
354  // Rogue object detection
356 
357  // Load detection
358  if(m_ptu_sensor_data.res_impedance < PTU_Z_TX_IN_NO_LOAD)
359  {
360  m_ptu_sensor_data.load_detected = true;
361  }
362  else
363  {
364  m_ptu_sensor_data.load_detected = false;
365  }
366  }
367 
368  // Check for event conditions:
369  bool b_under_temperature = false;
370  bool b_over_temperature = false;
371  bool b_itx_over_current = false;
372  bool b_itx_under_current = false;
373 
374  if (m_ptu_sensor_data.temperature < PTU_TEMPERATURE_MIN)
375  {
376  b_under_temperature = true;
377  }
378  else if (m_ptu_sensor_data.temperature > PTU_TEMPERATURE_MAX)
379  {
380  b_over_temperature = true;
381  }
382 
383  if (m_ptu_sensor_data.i_ina < PTU_ITX_MIN)
384  {
385  b_itx_under_current = true;
386  }
387  else if (m_ptu_sensor_data.i_ina > PTU_ITX_MAX)
388  {
389  b_itx_over_current = true;
390  }
391 
392  if (b_over_temperature || b_under_temperature || b_itx_over_current || b_itx_under_current)
393  {
394  m_sm_handler(PTU_SM_SIGNAL_LOCAL_FAULT);
395  }
396  else
397  {
398  m_sm_handler(PTU_SM_SIGNAL_LOCAL_FAULT_CLEARED);
399  }
400 
401  return NRF_SUCCESS;
402 }
403 
404 uint32_t ptu_sensors_data_get(const ptu_sensor_data_t ** sensors_data)
405 {
406  ASSERT(sensors_data != NULL);
407 
408  *sensors_data = &m_ptu_sensor_data;
409  return NRF_SUCCESS;
410 }
411 
413 {
415 }
416 
418 {
419  static uint8_t last_updated_index = 0;
420  uint8_t first_top, bottom;
421 
422  // Jump to next index of sample buffer and update its value
423  GOTO_NEXT_IDX(last_updated_index);
424 
425  m_save_ptx_sample(last_updated_index);
426 
427  // Find first top of wave
428  if(!m_find_first_top(&first_top, last_updated_index))
429  return false;
430 
431  // Find the bottom
432  if(!m_find_bottom(&bottom, first_top))
433  return false;
434 
435  // Find the second top and validate load variation
436  return m_find_second_top_and_validate(first_top, bottom);
437 }
438 
441 //lint -restore
#define PTU_P_TX_IN_LOAD_DETECT
Definition: ptu_config.h:73
static bool m_find_second_top_and_validate(uint8_t first_top, uint8_t bottom)
Definition: ptu_sensors.c:214
Registry item.
Definition: ptu_registry.h:47
#define PTU_LOAD_VARIATION_DETECT_LIFETIME_MS
Definition: ptu_config.h:70
void(* ptu_sm_handler_t)(ptu_sm_signal_type_t signal)
Definition: ptu.h:30
static bool m_find_bottom(uint8_t *p_bottom, uint8_t first_top)
Definition: ptu_sensors.c:185
uint32_t ptu_sensors_read(void)
Read sensors. This function should typically be called regularly by a timer and can generate events b...
Definition: ptu_sensors.c:303
#define PTU_ITX_MAX
Definition: ptu_hw_config.h:86
#define PTU_SENSORS_PTX_LOAD_VARIATION_MAX_PERIOD_TICKS
Max allowed period in ticks.
Definition: ptu_sensors.c:32
static ptu_sensor_data_t m_ptu_sensor_data
The latest version of the sensor data.
Definition: ptu_sensors.c:53
uint32_t ticks_diff(uint32_t ticks_now, uint32_t ticks_old)
Definition: common.c:78
uint32_t ptu_tmux_temperature_get(int16_t *p_temperature)
Test wrapper for ptu_hal_temperature_get().
Definition: ptu_test_mux.c:114
bool ptu_sensors_valid_long_beacon_extension_load_variation_found(void)
Check if a valid PTX load variation has occured.
Definition: ptu_sensors.c:417
#define PTU_ITX_MIN
Definition: ptu_hw_config.h:87
static bool m_find_first_top(uint8_t *p_first_top, uint8_t last_updated_idx)
Definition: ptu_sensors.c:162
uint32_t ptu_sensors_init(ptu_sm_handler_t sm_handler)
Initialize PTU Sensors. This function must be called before any other PTU Sensors function can be cal...
Definition: ptu_sensors.c:284
uint32_t ptu_reg_item_get_from_index(uint8_t index, ptu_reg_item_t **item_p)
Get registry item from index in database. Index must be < PTU_MAX_CONNECTIONS.
Definition: ptu_registry.c:126
uint32_t ptu_tmux_vtx_get(uint16_t *p_vtx)
Test wrapper for ptu_hal_vtx_get().
Definition: ptu_test_mux.c:100
#define PTU_SENSORS_PTX_LOAD_VARIATION_MIN_PERIOD_TICKS
Min allowed period in ticks.
Definition: ptu_sensors.c:33
APP_TIMER_DEF(m_load_var_detected_timer_id)
Load variation detect timer ID.
static void m_load_var_detect()
Definition: ptu_sensors.c:62
#define PTU_POWER_EFFICIENCY
Definition: ptu_hw_config.h:43
#define PTU_TEMPERATURE_WARNING_SET
Definition: ptu_hw_config.h:91
#define GOTO_NEXT_IDX(idx)
Circular buffer index update.
Definition: ptu_sensors.c:36
struct ptu_load_var_measurement_s ptu_sensors_load_variation_sample_t
Struct representing a single P_TX sample.
#define PTU_TEMPERATURE_MIN
Definition: ptu_hw_config.h:89
static void m_rogue_object_detect(void)
Definition: ptu_sensors.c:118
#define PTU_Z_TX_IN_LOAD_DETECT
Definition: ptu_config.h:72
uint16_t res_impedance
#define PTU_TEMPERATURE_WARNING_CLEAR
Definition: ptu_hw_config.h:92
uint32_t ptu_tmux_ptx_get(uint16_t *p_ptx)
Test wrapper for ptu_hal_ptx_get().
Definition: ptu_test_mux.c:107
static void m_save_ptx_sample(uint8_t last_updated_index)
Definition: ptu_sensors.c:263
#define APP_TIMER_PRESCALER
Definition: pru.h:33
static ptu_sm_handler_t m_sm_handler
State machine event handler.
Definition: ptu_sensors.c:55
#define NEXT(val)
Get next index in circular buffer.
Definition: ptu_sensors.c:35
uint32_t ptu_tmux_poweramp_enable_get(bool *enable)
Test wrapper for ptu_hal_poweramp_enable_get();.
Definition: ptu_test_mux.c:79
uint32_t ptu_sensors_data_get(const ptu_sensor_data_t **sensors_data)
Get the latest data read from the PTU sensors.
Definition: ptu_sensors.c:404
#define PTU_Z_TX_IN_NO_LOAD
Definition: ptu_config.h:71
#define PTU_Z_MEAS_BUF_LENGTH
Definition: ptu_config.h:79
pru_dynamic_t prev_pru_dynamic
Definition: ptu_registry.h:54
static ptu_sensors_load_variation_sample_t m_lbe_samples[PTU_SIZEOF_LOAD_VAL_BUF]
Buffer used for storing P_TX_IN load variation data.
Definition: ptu_sensors.c:50
void ptu_sensors_clear_long_beacon_extension_load_variation_buffer(void)
Empty buffer containing load variation sample data for Long Beacon Extension.
Definition: ptu_sensors.c:412
#define PTU_SIZEOF_LOAD_VAL_BUF
Size of P_TX_IN load variation buffer.
Definition: ptu_sensors.c:29
static void m_load_var_detected_timer_handler(void *p_context)
Definition: ptu_sensors.c:151
#define PTU_TEMPERATURE_MAX
Definition: ptu_hw_config.h:88
#define PREV(val)
Get previous index in circular buffer.
Definition: ptu_sensors.c:34
#define PTU_MAX_CONNECTIONS
Definition: ptu_config.h:29
uint32_t ptu_tmux_itx_get(uint16_t *p_itx)
Test wrapper for ptu_hal_itx_get().
Definition: ptu_test_mux.c:93
ptu_reg_item_state_t state
Definition: ptu_registry.h:49
uint16_t prect
Definition: wpt.h:178
#define PTU_ROGUE_OBJECT_DETECT_THRESHOLD
Definition: ptu_config.h:175
Definition of PTU sensor data.
Definition: ptu_hw_config.h:99