The VL53L1CB proximity sensor, based on ST’s FlightSense™, Time-of-Flight technology.

Dependencies:   X_NUCLEO_COMMON ST_INTERFACES

Dependents:   VL53L1CB_noshield_1sensor_polls_auton VL53L1CB_noshield_1sensor_interrupt_auton X_NUCLEO_53L1A2

Based on VL53L1 library, this is a library for the VL53L1CB ToF chip.

Committer:
lugandc
Date:
Wed Jul 21 17:06:38 2021 +0200
Revision:
18:0696efe39d08
Parent:
0:3ac96e360672
Cleanup i2c functions, removed all bad references to L1X
Cleanup VL53L1CB class:
- i2c device object is passed in a consistent way in MyDevice structure
- removed useless functions
Updated VL53L1CB component driver with bare driver release 6.6.7 content

Who changed what in which revision?

UserRevisionLine numberNew contents of line
charlesmn 0:3ac96e360672 1
lugandc 18:0696efe39d08 2 // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
lugandc 18:0696efe39d08 3 /******************************************************************************
lugandc 18:0696efe39d08 4 * Copyright (c) 2020, STMicroelectronics - All Rights Reserved
lugandc 18:0696efe39d08 5
lugandc 18:0696efe39d08 6 This file is part of VL53L1 and is dual licensed,
lugandc 18:0696efe39d08 7 either GPL-2.0+
lugandc 18:0696efe39d08 8 or 'BSD 3-clause "New" or "Revised" License' , at your option.
lugandc 18:0696efe39d08 9 ******************************************************************************
lugandc 18:0696efe39d08 10 */
charlesmn 0:3ac96e360672 11
charlesmn 0:3ac96e360672 12 unsigned int i2creadCount = 0;
charlesmn 0:3ac96e360672 13 unsigned int i2cwriteCount = 0;
charlesmn 0:3ac96e360672 14 unsigned char SPI2C_Buffer[256];
charlesmn 0:3ac96e360672 15
charlesmn 0:3ac96e360672 16
charlesmn 0:3ac96e360672 17 #include <mbed_wait_api.h>
charlesmn 0:3ac96e360672 18 #include "vl53l1_platform.h"
charlesmn 0:3ac96e360672 19 #include "vl53l1_platform_log.h"
charlesmn 0:3ac96e360672 20 #include "vl53l1_api.h"
charlesmn 0:3ac96e360672 21 #include "spi_interface.h"
charlesmn 0:3ac96e360672 22 #include <string.h>
charlesmn 0:3ac96e360672 23 #include <time.h>
charlesmn 0:3ac96e360672 24 #include <math.h>
charlesmn 0:3ac96e360672 25
charlesmn 0:3ac96e360672 26
charlesmn 0:3ac96e360672 27
charlesmn 0:3ac96e360672 28 #define I2C_TIME_OUT_BASE 10
charlesmn 0:3ac96e360672 29 #define I2C_TIME_OUT_BYTE 1
charlesmn 0:3ac96e360672 30
charlesmn 0:3ac96e360672 31 #ifdef VL53L1_LOG_ENABLE
charlesmn 0:3ac96e360672 32 #define trace_print(level, ...) VL53L1_trace_print_module_function(VL53L1_TRACE_MODULE_PLATFORM, level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__)
charlesmn 0:3ac96e360672 33 #define trace_i2c(...) VL53L1_trace_print_module_function(VL53L1_TRACE_MODULE_NONE, VL53L1_TRACE_LEVEL_NONE, VL53L1_TRACE_FUNCTION_I2C, ##__VA_ARGS__)
charlesmn 0:3ac96e360672 34 #endif
charlesmn 0:3ac96e360672 35
charlesmn 0:3ac96e360672 36 /* when not customized by application define dummy one */
charlesmn 0:3ac96e360672 37 #ifndef VL53L1_GetI2cBus
charlesmn 0:3ac96e360672 38 /** This macro can be overloaded by user to enforce i2c sharing in RTOS context
charlesmn 0:3ac96e360672 39 */
charlesmn 0:3ac96e360672 40 # define VL53L1_GetI2cBus(...) (void)0
charlesmn 0:3ac96e360672 41 #endif
charlesmn 0:3ac96e360672 42
charlesmn 0:3ac96e360672 43 #ifndef VL53L1_PutI2cBus
charlesmn 0:3ac96e360672 44 /** This macro can be overloaded by user to enforce i2c sharing in RTOS context
charlesmn 0:3ac96e360672 45 */
charlesmn 0:3ac96e360672 46 # define VL53L1_PutI2cBus(...) (void)0
charlesmn 0:3ac96e360672 47 #endif
charlesmn 0:3ac96e360672 48
charlesmn 0:3ac96e360672 49 uint8_t _I2CBuffer[256];
charlesmn 0:3ac96e360672 50
charlesmn 0:3ac96e360672 51
charlesmn 0:3ac96e360672 52 VL53L1_Error VL53L1_WriteMulti(VL53L1_DEV Dev, uint16_t index, uint8_t *pdata, uint32_t count)
charlesmn 0:3ac96e360672 53 {
charlesmn 0:3ac96e360672 54 int status;
charlesmn 0:3ac96e360672 55 // printf("VL53L1_WriteMulti %d %d %d \n",Dev->I2cDevAddr,index,count);
lugandc 18:0696efe39d08 56 status = v53l1cb_i2c_write_if(Dev->dev_i2c, pdata,Dev->i2c_slave_address, index,count);
charlesmn 0:3ac96e360672 57 return status;
charlesmn 0:3ac96e360672 58 }
charlesmn 0:3ac96e360672 59
charlesmn 0:3ac96e360672 60
charlesmn 0:3ac96e360672 61 VL53L1_Error VL53L1_ReadMulti(VL53L1_DEV Dev, uint16_t index, uint8_t *pdata, uint32_t count)
charlesmn 0:3ac96e360672 62 {
charlesmn 0:3ac96e360672 63 int status;
charlesmn 0:3ac96e360672 64
lugandc 18:0696efe39d08 65 status = v53l1cb_i2c_read_if(Dev->dev_i2c, pdata,Dev->i2c_slave_address, index,count);
charlesmn 0:3ac96e360672 66
charlesmn 0:3ac96e360672 67 return status;
charlesmn 0:3ac96e360672 68 }
charlesmn 0:3ac96e360672 69
charlesmn 0:3ac96e360672 70
charlesmn 0:3ac96e360672 71 VL53L1_Error VL53L1_WrByte(VL53L1_DEV Dev, uint16_t index, uint8_t data)
charlesmn 0:3ac96e360672 72 {
charlesmn 0:3ac96e360672 73 int status;
charlesmn 0:3ac96e360672 74
lugandc 18:0696efe39d08 75 status = v53l1cb_i2c_write_if(Dev->dev_i2c, &data,Dev->i2c_slave_address, index,1);
charlesmn 0:3ac96e360672 76 return status;
charlesmn 0:3ac96e360672 77 }
charlesmn 0:3ac96e360672 78
charlesmn 0:3ac96e360672 79
charlesmn 0:3ac96e360672 80 VL53L1_Error VL53L1_WrWord(VL53L1_DEV Dev, uint16_t index, uint16_t data)
charlesmn 0:3ac96e360672 81 {
charlesmn 0:3ac96e360672 82 int status;
charlesmn 0:3ac96e360672 83 uint8_t buffer[2];
charlesmn 0:3ac96e360672 84
charlesmn 0:3ac96e360672 85 buffer[0] = data >> 8;
charlesmn 0:3ac96e360672 86 buffer[1] = data & 0x00FF;
lugandc 18:0696efe39d08 87 status = v53l1cb_i2c_write_if(Dev->dev_i2c, (uint8_t *)buffer,Dev->i2c_slave_address, index,2);
charlesmn 0:3ac96e360672 88 return status;
charlesmn 0:3ac96e360672 89 }
charlesmn 0:3ac96e360672 90
charlesmn 0:3ac96e360672 91
charlesmn 0:3ac96e360672 92 VL53L1_Error VL53L1_WrDWord(VL53L1_DEV Dev, uint16_t index, uint32_t data)
charlesmn 0:3ac96e360672 93 {
charlesmn 0:3ac96e360672 94 int status;
charlesmn 0:3ac96e360672 95 uint8_t buffer[4];
charlesmn 0:3ac96e360672 96
charlesmn 0:3ac96e360672 97 buffer[0] = (data >> 24) & 0xFF;
charlesmn 0:3ac96e360672 98 buffer[1] = (data >> 16) & 0xFF;
charlesmn 0:3ac96e360672 99 buffer[2] = (data >> 8) & 0xFF;
charlesmn 0:3ac96e360672 100 buffer[3] = (data >> 0) & 0xFF;
lugandc 18:0696efe39d08 101 status = v53l1cb_i2c_write_if(Dev->dev_i2c, (uint8_t *)buffer,Dev->i2c_slave_address, index,4);
charlesmn 0:3ac96e360672 102 return status;
charlesmn 0:3ac96e360672 103 }
charlesmn 0:3ac96e360672 104
charlesmn 0:3ac96e360672 105 VL53L1_Error VL53L1_UpdateByte(VL53L1_DEV Dev, uint16_t index, uint8_t AndData, uint8_t OrData)
charlesmn 0:3ac96e360672 106 {
charlesmn 0:3ac96e360672 107 int status;
charlesmn 0:3ac96e360672 108 uint8_t buffer = 0;
charlesmn 0:3ac96e360672 109
charlesmn 0:3ac96e360672 110 /* read data direct onto buffer */
lugandc 18:0696efe39d08 111 status = v53l1cb_i2c_read_if(Dev->dev_i2c, &buffer,Dev->i2c_slave_address, index,1);
charlesmn 0:3ac96e360672 112 if (!status)
charlesmn 0:3ac96e360672 113 {
charlesmn 0:3ac96e360672 114 buffer = (buffer & AndData) | OrData;
lugandc 18:0696efe39d08 115 status = v53l1cb_i2c_write_if(Dev->dev_i2c, &buffer,Dev->i2c_slave_address, index,1);
charlesmn 0:3ac96e360672 116 }
charlesmn 0:3ac96e360672 117 return status;
charlesmn 0:3ac96e360672 118 }
charlesmn 0:3ac96e360672 119
charlesmn 0:3ac96e360672 120 VL53L1_Error VL53L1_RdByte(VL53L1_DEV Dev, uint16_t index, uint8_t *data)
charlesmn 0:3ac96e360672 121 {
charlesmn 0:3ac96e360672 122 int status;
charlesmn 0:3ac96e360672 123
lugandc 18:0696efe39d08 124 status = v53l1cb_i2c_read_if(Dev->dev_i2c, data,Dev->i2c_slave_address, index,1); //is this correct
charlesmn 0:3ac96e360672 125 // printf("VL53L1_RdByte %d %d %d\n",Dev->i2c_slave_address, status,*data);
charlesmn 0:3ac96e360672 126 if(status)
charlesmn 0:3ac96e360672 127 return -1;
charlesmn 0:3ac96e360672 128
charlesmn 0:3ac96e360672 129 return 0;
charlesmn 0:3ac96e360672 130 }
charlesmn 0:3ac96e360672 131
charlesmn 0:3ac96e360672 132
charlesmn 0:3ac96e360672 133 VL53L1_Error VL53L1_RdWord(VL53L1_DEV Dev, uint16_t index, uint16_t *data)
charlesmn 0:3ac96e360672 134 {
charlesmn 0:3ac96e360672 135 int status;
charlesmn 0:3ac96e360672 136 uint8_t buffer[2] = {0,0};
charlesmn 0:3ac96e360672 137
lugandc 18:0696efe39d08 138 status = v53l1cb_i2c_read_if(Dev->dev_i2c, buffer,Dev->i2c_slave_address, index,2); //is this correct
charlesmn 0:3ac96e360672 139 if (!status)
charlesmn 0:3ac96e360672 140 {
charlesmn 0:3ac96e360672 141 *data = (buffer[0] << 8) + buffer[1];
charlesmn 0:3ac96e360672 142 }
charlesmn 0:3ac96e360672 143 return status;
charlesmn 0:3ac96e360672 144
charlesmn 0:3ac96e360672 145 }
charlesmn 0:3ac96e360672 146
charlesmn 0:3ac96e360672 147
charlesmn 0:3ac96e360672 148
charlesmn 0:3ac96e360672 149 VL53L1_Error VL53L1_RdDWord(VL53L1_DEV Dev, uint16_t index, uint32_t *data)
charlesmn 0:3ac96e360672 150 {
charlesmn 0:3ac96e360672 151 int status;
charlesmn 0:3ac96e360672 152 uint8_t buffer[4] = {0,0,0,0};
charlesmn 0:3ac96e360672 153
lugandc 18:0696efe39d08 154 status = v53l1cb_i2c_read_if(Dev->dev_i2c, buffer,Dev->i2c_slave_address, index,4);
charlesmn 0:3ac96e360672 155 if(!status)
charlesmn 0:3ac96e360672 156 {
charlesmn 0:3ac96e360672 157 *data = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
charlesmn 0:3ac96e360672 158 }
charlesmn 0:3ac96e360672 159 return status;
charlesmn 0:3ac96e360672 160
charlesmn 0:3ac96e360672 161 }
charlesmn 0:3ac96e360672 162
charlesmn 0:3ac96e360672 163
charlesmn 0:3ac96e360672 164 VL53L1_Error VL53L1_GetTickCount(
lugandc 18:0696efe39d08 165 VL53L1_DEV Dev,
charlesmn 0:3ac96e360672 166 uint32_t *ptick_count_ms)
charlesmn 0:3ac96e360672 167 {
charlesmn 0:3ac96e360672 168
charlesmn 0:3ac96e360672 169 /* Returns current tick count in [ms] */
charlesmn 0:3ac96e360672 170
charlesmn 0:3ac96e360672 171 VL53L1_Error status = VL53L1_ERROR_NONE;
lugandc 18:0696efe39d08 172 (void) Dev;
charlesmn 0:3ac96e360672 173
charlesmn 0:3ac96e360672 174 GetTickCount( ptick_count_ms);
charlesmn 0:3ac96e360672 175
charlesmn 0:3ac96e360672 176 #ifdef VL53L1_LOG_ENABLE
charlesmn 0:3ac96e360672 177 trace_print(
charlesmn 0:3ac96e360672 178 VL53L1_TRACE_LEVEL_DEBUG,
charlesmn 0:3ac96e360672 179 "VL53L1_GetTickCount() = %5u ms;\n",
charlesmn 0:3ac96e360672 180 *ptick_count_ms);
charlesmn 0:3ac96e360672 181 #endif
charlesmn 0:3ac96e360672 182
charlesmn 0:3ac96e360672 183 return status;
charlesmn 0:3ac96e360672 184 }
charlesmn 0:3ac96e360672 185
charlesmn 0:3ac96e360672 186
charlesmn 0:3ac96e360672 187 #define trace_print(level, ...) \
charlesmn 0:3ac96e360672 188 _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_PLATFORM, \
charlesmn 0:3ac96e360672 189 level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__)
charlesmn 0:3ac96e360672 190
charlesmn 0:3ac96e360672 191 #define trace_i2c(...) \
charlesmn 0:3ac96e360672 192 _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_NONE, \
charlesmn 0:3ac96e360672 193 VL53L1_TRACE_LEVEL_NONE, VL53L1_TRACE_FUNCTION_I2C, ##__VA_ARGS__)
charlesmn 0:3ac96e360672 194
charlesmn 0:3ac96e360672 195
charlesmn 0:3ac96e360672 196 VL53L1_Error VL53L1_GetTimerFrequency(int32_t *ptimer_freq_hz)
charlesmn 0:3ac96e360672 197 {
charlesmn 0:3ac96e360672 198 *ptimer_freq_hz = 0;
charlesmn 0:3ac96e360672 199
charlesmn 0:3ac96e360672 200 trace_print(VL53L1_TRACE_LEVEL_INFO, "VL53L1_GetTimerFrequency: Freq : %dHz\n", *ptimer_freq_hz);
charlesmn 0:3ac96e360672 201 return VL53L1_ERROR_NONE;
charlesmn 0:3ac96e360672 202 }
charlesmn 0:3ac96e360672 203
charlesmn 0:3ac96e360672 204
charlesmn 0:3ac96e360672 205 VL53L1_Error VL53L1_WaitMs(VL53L1_Dev_t *pdev, int32_t wait_time){
charlesmn 0:3ac96e360672 206 (void)pdev;
lugandc 18:0696efe39d08 207 wait_us(wait_time * 1000);
charlesmn 0:3ac96e360672 208 return VL53L1_ERROR_NONE;
charlesmn 0:3ac96e360672 209 }
charlesmn 0:3ac96e360672 210
charlesmn 0:3ac96e360672 211
charlesmn 0:3ac96e360672 212 VL53L1_Error VL53L1_WaitUs(VL53L1_Dev_t *pdev, int32_t wait_time){
charlesmn 0:3ac96e360672 213 (void)pdev;
charlesmn 0:3ac96e360672 214 wait_us(wait_time);
charlesmn 0:3ac96e360672 215 return VL53L1_ERROR_NONE;
charlesmn 0:3ac96e360672 216 }
charlesmn 0:3ac96e360672 217
charlesmn 0:3ac96e360672 218 VL53L1_Error VL53L1_WaitValueMaskEx(
charlesmn 0:3ac96e360672 219 VL53L1_Dev_t *pdev,
charlesmn 0:3ac96e360672 220 uint32_t timeout_ms,
charlesmn 0:3ac96e360672 221 uint16_t index,
charlesmn 0:3ac96e360672 222 uint8_t value,
charlesmn 0:3ac96e360672 223 uint8_t mask,
charlesmn 0:3ac96e360672 224 uint32_t poll_delay_ms)
charlesmn 0:3ac96e360672 225 {
charlesmn 0:3ac96e360672 226
charlesmn 0:3ac96e360672 227 /*
charlesmn 0:3ac96e360672 228 * Platform implementation of WaitValueMaskEx V2WReg script command
charlesmn 0:3ac96e360672 229 *
charlesmn 0:3ac96e360672 230 * WaitValueMaskEx(
charlesmn 0:3ac96e360672 231 * duration_ms,
charlesmn 0:3ac96e360672 232 * index,
charlesmn 0:3ac96e360672 233 * value,
charlesmn 0:3ac96e360672 234 * mask,
charlesmn 0:3ac96e360672 235 * poll_delay_ms);
charlesmn 0:3ac96e360672 236 */
charlesmn 0:3ac96e360672 237
charlesmn 0:3ac96e360672 238 VL53L1_Error status = VL53L1_ERROR_NONE;
charlesmn 0:3ac96e360672 239 uint32_t start_time_ms = 0;
charlesmn 0:3ac96e360672 240 uint32_t current_time_ms = 0;
charlesmn 0:3ac96e360672 241 uint32_t polling_time_ms = 0;
charlesmn 0:3ac96e360672 242 uint8_t byte_value = 0;
charlesmn 0:3ac96e360672 243 uint8_t found = 0;
charlesmn 0:3ac96e360672 244 #ifdef VL53L1_LOG_ENABLE
charlesmn 0:3ac96e360672 245 uint8_t trace_functions = VL53L1_TRACE_FUNCTION_NONE;
charlesmn 0:3ac96e360672 246 #endif
charlesmn 0:3ac96e360672 247
charlesmn 0:3ac96e360672 248 char register_name[VL53L1_MAX_STRING_LENGTH];
charlesmn 0:3ac96e360672 249
charlesmn 0:3ac96e360672 250 /* look up register name */
charlesmn 0:3ac96e360672 251 #ifdef PAL_EXTENDED
charlesmn 0:3ac96e360672 252 VL53L1_get_register_name(
charlesmn 0:3ac96e360672 253 index,
charlesmn 0:3ac96e360672 254 register_name);
charlesmn 0:3ac96e360672 255 #else
charlesmn 0:3ac96e360672 256 VL53L1_COPYSTRING(register_name, "");
charlesmn 0:3ac96e360672 257 #endif
charlesmn 0:3ac96e360672 258
charlesmn 0:3ac96e360672 259 /* Output to I2C logger for FMT/DFT */
charlesmn 0:3ac96e360672 260
charlesmn 0:3ac96e360672 261 /*trace_i2c("WaitValueMaskEx(%5d, 0x%04X, 0x%02X, 0x%02X, %5d);\n",
charlesmn 0:3ac96e360672 262 timeout_ms, index, value, mask, poll_delay_ms); */
charlesmn 0:3ac96e360672 263 trace_i2c("WaitValueMaskEx(%5d, %s, 0x%02X, 0x%02X, %5d);\n",
charlesmn 0:3ac96e360672 264 timeout_ms, register_name, value, mask, poll_delay_ms);
charlesmn 0:3ac96e360672 265
charlesmn 0:3ac96e360672 266 /* calculate time limit in absolute time */
charlesmn 0:3ac96e360672 267
lugandc 18:0696efe39d08 268 VL53L1_GetTickCount(pdev, &start_time_ms);
charlesmn 0:3ac96e360672 269
charlesmn 0:3ac96e360672 270
lugandc 18:0696efe39d08 271 VL53L1_WaitMs(pdev, 10);
charlesmn 0:3ac96e360672 272
charlesmn 0:3ac96e360672 273 /* remember current trace functions and temporarily disable
charlesmn 0:3ac96e360672 274 * function logging
charlesmn 0:3ac96e360672 275 */
charlesmn 0:3ac96e360672 276
charlesmn 0:3ac96e360672 277 #ifdef VL53L1_LOG_ENABLE
charlesmn 0:3ac96e360672 278 trace_functions = VL53L1_get_trace_functions();
charlesmn 0:3ac96e360672 279 VL53L1_set_trace_functions(VL53L1_TRACE_FUNCTION_NONE);
charlesmn 0:3ac96e360672 280 #endif
charlesmn 0:3ac96e360672 281
charlesmn 0:3ac96e360672 282 /* wait until value is found, timeout reached on error occurred */
charlesmn 0:3ac96e360672 283 while ((status == VL53L1_ERROR_NONE) &&
charlesmn 0:3ac96e360672 284 (polling_time_ms < timeout_ms) &&
charlesmn 0:3ac96e360672 285 (found == 0)) {
charlesmn 0:3ac96e360672 286
charlesmn 0:3ac96e360672 287 if (status == VL53L1_ERROR_NONE)
charlesmn 0:3ac96e360672 288 status = VL53L1_RdByte(
charlesmn 0:3ac96e360672 289 pdev,
charlesmn 0:3ac96e360672 290 index,
charlesmn 0:3ac96e360672 291 &byte_value);
charlesmn 0:3ac96e360672 292
charlesmn 0:3ac96e360672 293 if ((byte_value & mask) == value)
charlesmn 0:3ac96e360672 294 found = 1;
charlesmn 0:3ac96e360672 295
charlesmn 0:3ac96e360672 296 if (status == VL53L1_ERROR_NONE &&
charlesmn 0:3ac96e360672 297 found == 0 &&
charlesmn 0:3ac96e360672 298 poll_delay_ms > 0)
charlesmn 0:3ac96e360672 299 status = VL53L1_WaitMs(
charlesmn 0:3ac96e360672 300 pdev,
charlesmn 0:3ac96e360672 301 poll_delay_ms);
charlesmn 0:3ac96e360672 302 /* Update polling time (Compare difference rather than absolute to
charlesmn 0:3ac96e360672 303 negate 32bit wrap around issue) */
lugandc 18:0696efe39d08 304 VL53L1_GetTickCount(pdev, &current_time_ms);
charlesmn 0:3ac96e360672 305 polling_time_ms = current_time_ms - start_time_ms;
charlesmn 0:3ac96e360672 306
charlesmn 0:3ac96e360672 307 }
charlesmn 0:3ac96e360672 308 // printf("polling_time_ms %d \n",polling_time_ms);
charlesmn 0:3ac96e360672 309 #ifdef VL53L1_LOG_ENABLE
charlesmn 0:3ac96e360672 310 /* Restore function logging */
charlesmn 0:3ac96e360672 311 VL53L1_set_trace_functions(trace_functions);
charlesmn 0:3ac96e360672 312 #endif
charlesmn 0:3ac96e360672 313
charlesmn 0:3ac96e360672 314 if (found == 0 && status == VL53L1_ERROR_NONE)
charlesmn 0:3ac96e360672 315 status = VL53L1_ERROR_TIME_OUT;
charlesmn 0:3ac96e360672 316
charlesmn 0:3ac96e360672 317 return status;
charlesmn 0:3ac96e360672 318 }
charlesmn 0:3ac96e360672 319
charlesmn 0:3ac96e360672 320
charlesmn 0:3ac96e360672 321
charlesmn 0:3ac96e360672 322