Committer:
narshu
Date:
Thu Apr 26 20:11:48 2012 +0000
Revision:
2:8aa491f77a0b
Parent:
1:4964fa534202

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
narshu 0:e238496b8073 1 //***************************************************************************************
narshu 0:e238496b8073 2 //Kalman Filter implementation
narshu 0:e238496b8073 3 //***************************************************************************************
narshu 0:e238496b8073 4 #include "Kalman.h"
narshu 0:e238496b8073 5 #include "rtos.h"
narshu 0:e238496b8073 6 #include "RFSRF05.h"
narshu 0:e238496b8073 7 #include "math.h"
narshu 0:e238496b8073 8 #include "globals.h"
narshu 0:e238496b8073 9 #include "motors.h"
narshu 0:e238496b8073 10 #include "system.h"
narshu 0:e238496b8073 11 #include "geometryfuncs.h"
narshu 0:e238496b8073 12
narshu 0:e238496b8073 13 #include <tvmet/Matrix.h>
narshu 0:e238496b8073 14 #include <tvmet/Vector.h>
narshu 0:e238496b8073 15 using namespace tvmet;
narshu 0:e238496b8073 16
narshu 1:4964fa534202 17 Kalman::Kalman(Motors &motorsin,
narshu 2:8aa491f77a0b 18 UI &uiin,
narshu 2:8aa491f77a0b 19 PinName Sonar_Trig,
narshu 2:8aa491f77a0b 20 PinName Sonar_Echo0,
narshu 2:8aa491f77a0b 21 PinName Sonar_Echo1,
narshu 2:8aa491f77a0b 22 PinName Sonar_Echo2,
narshu 2:8aa491f77a0b 23 PinName Sonar_Echo3,
narshu 2:8aa491f77a0b 24 PinName Sonar_Echo4,
narshu 2:8aa491f77a0b 25 PinName Sonar_Echo5,
narshu 2:8aa491f77a0b 26 PinName Sonar_SDI,
narshu 2:8aa491f77a0b 27 PinName Sonar_SDO,
narshu 2:8aa491f77a0b 28 PinName Sonar_SCK,
narshu 2:8aa491f77a0b 29 PinName Sonar_NCS,
narshu 2:8aa491f77a0b 30 PinName Sonar_NIRQ,
narshu 2:8aa491f77a0b 31 PinName IR_Tx,
narshu 2:8aa491f77a0b 32 PinName IR_Rx) :
narshu 2:8aa491f77a0b 33 ir(*this,IR_Tx,IR_Rx),
narshu 1:4964fa534202 34 sonararray(Sonar_Trig,
narshu 2:8aa491f77a0b 35 Sonar_Echo0,
narshu 2:8aa491f77a0b 36 Sonar_Echo1,
narshu 2:8aa491f77a0b 37 Sonar_Echo2,
narshu 2:8aa491f77a0b 38 Sonar_Echo3,
narshu 2:8aa491f77a0b 39 Sonar_Echo4,
narshu 2:8aa491f77a0b 40 Sonar_Echo5,
narshu 2:8aa491f77a0b 41 Sonar_SDI,
narshu 2:8aa491f77a0b 42 Sonar_SDO,
narshu 2:8aa491f77a0b 43 Sonar_SCK,
narshu 2:8aa491f77a0b 44 Sonar_NCS,
narshu 2:8aa491f77a0b 45 Sonar_NIRQ),
narshu 0:e238496b8073 46 motors(motorsin),
narshu 2:8aa491f77a0b 47 ui(uiin),
narshu 0:e238496b8073 48 predictthread(predictloopwrapper, this, osPriorityNormal, 512),
narshu 0:e238496b8073 49 predictticker( SIGTICKARGS(predictthread, 0x1) ),
narshu 0:e238496b8073 50 // sonarthread(sonarloopwrapper, this, osPriorityNormal, 256),
narshu 0:e238496b8073 51 // sonarticker( SIGTICKARGS(sonarthread, 0x1) ),
narshu 0:e238496b8073 52 updatethread(updateloopwrapper, this, osPriorityNormal, 512) {
narshu 0:e238496b8073 53
narshu 0:e238496b8073 54 Kalman_init = false;
narshu 0:e238496b8073 55 //Intialising some arrays to zero
narshu 0:e238496b8073 56 for (int kk = 0; kk < 3; kk ++) {
narshu 0:e238496b8073 57 SonarMeasure_Offset[kk] = 0;
narshu 0:e238496b8073 58 }
narshu 0:e238496b8073 59 //Initialising other vars
narshu 0:e238496b8073 60
narshu 0:e238496b8073 61
narshu 0:e238496b8073 62 //Initilising matrices
narshu 0:e238496b8073 63
narshu 0:e238496b8073 64 // X = x, y, theta;
narshu 0:e238496b8073 65 X = 0.5, 0, 0;
narshu 0:e238496b8073 66
narshu 0:e238496b8073 67 P = 1, 0, 0,
narshu 0:e238496b8073 68 0, 1, 0,
narshu 0:e238496b8073 69 0, 0, 0.04;
narshu 0:e238496b8073 70
narshu 0:e238496b8073 71 //measurment variance R is provided by each sensor when calling runupdate
narshu 0:e238496b8073 72
narshu 0:e238496b8073 73 //attach callback
narshu 0:e238496b8073 74 sonararray.callbackobj = (DummyCT*)this;
narshu 0:e238496b8073 75 sonararray.mcallbackfunc = (void (DummyCT::*)(int beaconnum, float distance, float variance)) &Kalman::runupdate;
narshu 0:e238496b8073 76
narshu 0:e238496b8073 77
narshu 0:e238496b8073 78 predictticker.start(20);
narshu 0:e238496b8073 79 // sonarticker.start(50);
narshu 0:e238496b8073 80
narshu 0:e238496b8073 81 }
narshu 0:e238496b8073 82
narshu 0:e238496b8073 83
narshu 0:e238496b8073 84 //Note: this init function assumes that the robot faces east, theta=0, in the +x direction
narshu 0:e238496b8073 85 void Kalman::KalmanInit() {
narshu 0:e238496b8073 86 float SonarMeasuresx1000[3];
narshu 0:e238496b8073 87 float IRMeasuresloc[3];
narshu 0:e238496b8073 88 int beacon_cnt = 0;
narshu 0:e238496b8073 89 // set initiating flag to false
narshu 0:e238496b8073 90 Kalman_init = false;
narshu 2:8aa491f77a0b 91
narshu 0:e238496b8073 92 // init the offset array
narshu 0:e238496b8073 93 for (int k = 0; k < 3; k ++) {
narshu 0:e238496b8073 94 SonarMeasure_Offset[k] = 0;
narshu 0:e238496b8073 95 IRMeasures[k] = 0;
narshu 0:e238496b8073 96 }
narshu 0:e238496b8073 97
narshu 0:e238496b8073 98 LPC_UART0->FCR = LPC_UART0->FCR | 0x06; // Flush the serial FIFO buffer / OR with FCR
narshu 0:e238496b8073 99 //wating untill the IR has reved up and picked up some data
narshu 0:e238496b8073 100 wait(1);
narshu 0:e238496b8073 101
narshu 0:e238496b8073 102 //temporaraly disable IR updates
narshu 0:e238496b8073 103 ir.detachisr();
narshu 0:e238496b8073 104 //IRturret.attach(NULL,Serial::RxIrq);
narshu 0:e238496b8073 105
narshu 0:e238496b8073 106 //lock the state throughout the computation, as we will override the state at the end
narshu 0:e238496b8073 107 statelock.lock();
narshu 0:e238496b8073 108
narshu 0:e238496b8073 109 SonarMeasuresx1000[0] = SonarMeasures[0]*1000.0f;
narshu 0:e238496b8073 110 SonarMeasuresx1000[1] = SonarMeasures[1]*1000.0f;
narshu 0:e238496b8073 111 SonarMeasuresx1000[2] = SonarMeasures[2]*1000.0f;
narshu 0:e238496b8073 112 IRMeasuresloc[0] = IRMeasures[0];
narshu 0:e238496b8073 113 IRMeasuresloc[1] = IRMeasures[1];
narshu 0:e238496b8073 114 IRMeasuresloc[2] = IRMeasures[2];
narshu 0:e238496b8073 115 //printf("0: %0.4f, 1: %0.4f, 2: %0.4f \n\r", IRMeasuresloc[0]*180/PI, IRMeasuresloc[1]*180/PI, IRMeasuresloc[2]*180/PI);
narshu 0:e238496b8073 116
narshu 0:e238496b8073 117 float d = beaconpos[2].y - beaconpos[1].y;
narshu 0:e238496b8073 118 float i = beaconpos[0].y - beaconpos[1].y;
narshu 0:e238496b8073 119 float j = beaconpos[0].x - beaconpos[1].x;
narshu 0:e238496b8073 120 float y_coor = (SonarMeasuresx1000[1]*SonarMeasuresx1000[1]- SonarMeasuresx1000[2]*SonarMeasuresx1000[2] + d*d) / (2*d);
narshu 0:e238496b8073 121 float x_coor = (SonarMeasuresx1000[1]*SonarMeasuresx1000[1] - SonarMeasuresx1000[0]*SonarMeasuresx1000[0] + i*i + j*j)/(2*j) - i*y_coor/j;
narshu 0:e238496b8073 122
narshu 0:e238496b8073 123 //Compute sonar offset
narshu 0:e238496b8073 124 float Dist_Exp[3];
narshu 0:e238496b8073 125 for (int k = 0; k < 3; k++) {
narshu 0:e238496b8073 126 Dist_Exp[k] = sqrt((beaconpos[k].y - y_coor)*(beaconpos[k].y - y_coor)+(beaconpos[k].x - x_coor)*(beaconpos[k].x - x_coor));
narshu 0:e238496b8073 127 SonarMeasure_Offset[k] = (SonarMeasuresx1000[k]-Dist_Exp[k])/1000.0f;
narshu 0:e238496b8073 128 }
narshu 0:e238496b8073 129
narshu 0:e238496b8073 130 //Compute IR offset
narshu 0:e238496b8073 131 ir.angleOffset = 0;
narshu 0:e238496b8073 132 for (int i = 0; i < 3; i++) {
narshu 0:e238496b8073 133 float angle_est = atan2(beaconpos[i].y - y_coor,beaconpos[i].x - x_coor);
narshu 0:e238496b8073 134 // take average offset angle from valid readings
narshu 0:e238496b8073 135 if (IRMeasuresloc[i] != 0) {
narshu 0:e238496b8073 136 beacon_cnt ++;
narshu 0:e238496b8073 137 // changed to current angle - estimated angle
narshu 0:e238496b8073 138 float angle_temp = IRMeasuresloc[i] - angle_est;
narshu 0:e238496b8073 139 angle_temp -= (floor(angle_temp/(2*PI)))*2*PI;
narshu 0:e238496b8073 140 ir.angleOffset += angle_temp;
narshu 0:e238496b8073 141 }
narshu 0:e238496b8073 142 }
narshu 0:e238496b8073 143 ir.angleOffset = ir.angleOffset/float(beacon_cnt);
narshu 0:e238496b8073 144 //printf("\n\r");
narshu 0:e238496b8073 145
narshu 0:e238496b8073 146 //statelock already locked
narshu 0:e238496b8073 147 ir.angleInit = true;
narshu 0:e238496b8073 148 // set int flag to true
narshu 0:e238496b8073 149 Kalman_init = true;
narshu 0:e238496b8073 150 X(0) = x_coor/1000.0f;
narshu 0:e238496b8073 151 X(1) = y_coor/1000.0f;
narshu 0:e238496b8073 152 X(2) = 0;
narshu 0:e238496b8073 153 statelock.unlock();
narshu 0:e238496b8073 154
narshu 0:e238496b8073 155 //printf("x: %0.4f, y: %0.4f, offset: %0.4f \n\r", x_coor, y_coor, angleOffset*180/PI);
narshu 0:e238496b8073 156
narshu 0:e238496b8073 157 //reattach the IR processing
narshu 0:e238496b8073 158 ir.attachisr();
narshu 0:e238496b8073 159 //IRturret.attach(&IR::vIRValueISR,Serial::RxIrq);
narshu 0:e238496b8073 160 }
narshu 0:e238496b8073 161
narshu 0:e238496b8073 162
narshu 0:e238496b8073 163 void Kalman::predictloop() {
narshu 0:e238496b8073 164
narshu 0:e238496b8073 165 float lastleft = 0;
narshu 0:e238496b8073 166 float lastright = 0;
narshu 0:e238496b8073 167
narshu 0:e238496b8073 168 while (1) {
narshu 0:e238496b8073 169 Thread::signal_wait(0x1);
narshu 0:e238496b8073 170 OLED1 = !OLED1;
narshu 0:e238496b8073 171
narshu 0:e238496b8073 172 int leftenc = motors.getEncoder1();
narshu 0:e238496b8073 173 int rightenc = motors.getEncoder2();
narshu 0:e238496b8073 174
narshu 0:e238496b8073 175 float dleft = motors.encoderToDistance(leftenc-lastleft)/1000.0f;
narshu 0:e238496b8073 176 float dright = motors.encoderToDistance(rightenc-lastright)/1000.0f;
narshu 0:e238496b8073 177
narshu 0:e238496b8073 178 lastleft = leftenc;
narshu 0:e238496b8073 179 lastright = rightenc;
narshu 0:e238496b8073 180
narshu 0:e238496b8073 181
narshu 0:e238496b8073 182 //The below calculation are in body frame (where +x is forward)
narshu 0:e238496b8073 183 float dxp, dyp,d,r;
narshu 0:e238496b8073 184 float thetap = (dright - dleft)*PI / (float(robotCircumference)/1000.0f);
narshu 0:e238496b8073 185 if (abs(thetap) < 0.02) { //if the rotation through the integration step is small, approximate with a straight line to avoid numerical error
narshu 0:e238496b8073 186 d = (dright + dleft)/2.0f;
narshu 0:e238496b8073 187 dxp = d*cos(thetap/2.0f);
narshu 0:e238496b8073 188 dyp = d*sin(thetap/2.0f);
narshu 0:e238496b8073 189
narshu 0:e238496b8073 190 } else { //calculate circle arc
narshu 0:e238496b8073 191 //float r = (right + left) / (4.0f * PI * thetap);
narshu 0:e238496b8073 192 r = (dright + dleft) / (2.0f*thetap);
narshu 0:e238496b8073 193 dxp = abs(r)*sin(thetap);
narshu 0:e238496b8073 194 dyp = r - r*cos(thetap);
narshu 0:e238496b8073 195 }
narshu 0:e238496b8073 196
narshu 0:e238496b8073 197 statelock.lock();
narshu 0:e238496b8073 198
narshu 0:e238496b8073 199 //rotating to cartesian frame and updating state
narshu 0:e238496b8073 200 X(0) += dxp * cos(X(2)) - dyp * sin(X(2));
narshu 0:e238496b8073 201 X(1) += dxp * sin(X(2)) + dyp * cos(X(2));
narshu 0:e238496b8073 202 X(2) = rectifyAng(X(2) + thetap);
narshu 0:e238496b8073 203
narshu 0:e238496b8073 204 //Linearising F around X
narshu 0:e238496b8073 205 Matrix<float, 3, 3> F;
narshu 0:e238496b8073 206 F = 1, 0, (dxp * -sin(X(2)) - dyp * cos(X(2))),
narshu 0:e238496b8073 207 0, 1, (dxp * cos(X(2)) - dyp * sin(X(2))),
narshu 0:e238496b8073 208 0, 0, 1;
narshu 0:e238496b8073 209
narshu 0:e238496b8073 210 //Generating forward and rotational variance
narshu 0:e238496b8073 211 float varfwd = fwdvarperunit * (dright + dleft) / 2.0f;
narshu 0:e238496b8073 212 float varang = varperang * thetap;
narshu 0:e238496b8073 213 float varxydt = xyvarpertime * PREDICTPERIOD;
narshu 0:e238496b8073 214 float varangdt = angvarpertime * PREDICTPERIOD;
narshu 0:e238496b8073 215
narshu 0:e238496b8073 216 //Rotating into cartesian frame
narshu 0:e238496b8073 217 Matrix<float, 2, 2> Qsub,Qsubrot,Qrot;
narshu 0:e238496b8073 218 Qsub = varfwd + varxydt, 0,
narshu 0:e238496b8073 219 0, varxydt;
narshu 0:e238496b8073 220
narshu 0:e238496b8073 221 Qrot = Rotmatrix(X(2));
narshu 0:e238496b8073 222
narshu 0:e238496b8073 223 Qsubrot = Qrot * Qsub * trans(Qrot);
narshu 0:e238496b8073 224
narshu 0:e238496b8073 225 //Generate Q
narshu 0:e238496b8073 226 Matrix<float, 3, 3> Q;//(Qsubrot);
narshu 0:e238496b8073 227 Q = Qsubrot(0,0), Qsubrot(0,1), 0,
narshu 0:e238496b8073 228 Qsubrot(1,0), Qsubrot(1,1), 0,
narshu 0:e238496b8073 229 0, 0, varang + varangdt;
narshu 0:e238496b8073 230
narshu 0:e238496b8073 231 P = F * P * trans(F) + Q;
narshu 0:e238496b8073 232
narshu 0:e238496b8073 233 statelock.unlock();
narshu 0:e238496b8073 234 //Thread::wait(PREDICTPERIOD);
narshu 0:e238496b8073 235
narshu 0:e238496b8073 236 //cout << "predict" << X << endl;
narshu 0:e238496b8073 237 //cout << P << endl;
narshu 0:e238496b8073 238 }
narshu 0:e238496b8073 239 }
narshu 0:e238496b8073 240
narshu 0:e238496b8073 241 //void Kalman::sonarloop() {
narshu 0:e238496b8073 242 // while (1) {
narshu 0:e238496b8073 243 // Thread::signal_wait(0x1);
narshu 0:e238496b8073 244 // sonararray.startRange();
narshu 0:e238496b8073 245 // }
narshu 0:e238496b8073 246 //}
narshu 0:e238496b8073 247
narshu 0:e238496b8073 248
narshu 0:e238496b8073 249 void Kalman::runupdate(measurement_t type, float value, float variance) {
narshu 0:e238496b8073 250 //printf("beacon %d dist %f\r\n", sonarid, dist);
narshu 0:e238496b8073 251 //led2 = !led2;
narshu 0:e238496b8073 252
narshu 0:e238496b8073 253 measurmentdata* measured = (measurmentdata*)measureMQ.alloc();
narshu 0:e238496b8073 254 if (measured) {
narshu 0:e238496b8073 255 measured->mtype = type;
narshu 0:e238496b8073 256 measured->value = value;
narshu 0:e238496b8073 257 measured->variance = variance;
narshu 0:e238496b8073 258
narshu 0:e238496b8073 259 osStatus putret = measureMQ.put(measured);
narshu 0:e238496b8073 260 if (putret)
narshu 0:e238496b8073 261 OLED4 = 1;
narshu 0:e238496b8073 262 // printf("putting in MQ error code %#x\r\n", putret);
narshu 0:e238496b8073 263 } else {
narshu 0:e238496b8073 264 OLED4 = 1;
narshu 0:e238496b8073 265 //printf("MQalloc returned NULL ptr\r\n");
narshu 0:e238496b8073 266 }
narshu 0:e238496b8073 267
narshu 0:e238496b8073 268 }
narshu 0:e238496b8073 269
narshu 0:e238496b8073 270 void Kalman::updateloop() {
narshu 0:e238496b8073 271 measurement_t type;
narshu 0:e238496b8073 272 float value,variance,rbx,rby,expecdist,Y;
narshu 0:e238496b8073 273 float dhdx,dhdy;
narshu 0:e238496b8073 274 bool aborton2stddev = false;
narshu 0:e238496b8073 275
narshu 0:e238496b8073 276 Matrix<float, 1, 3> H;
narshu 0:e238496b8073 277
narshu 0:e238496b8073 278 float S;
narshu 0:e238496b8073 279 Matrix<float, 3, 3> I3( identity< Matrix<float, 3, 3> >() );
narshu 0:e238496b8073 280
narshu 0:e238496b8073 281
narshu 0:e238496b8073 282 while (1) {
narshu 0:e238496b8073 283 OLED2 = !OLED2;
narshu 0:e238496b8073 284
narshu 0:e238496b8073 285 osEvent evt = measureMQ.get();
narshu 0:e238496b8073 286
narshu 0:e238496b8073 287 if (evt.status == osEventMail) {
narshu 0:e238496b8073 288
narshu 0:e238496b8073 289 measurmentdata &measured = *(measurmentdata*)evt.value.p;
narshu 0:e238496b8073 290 type = measured.mtype; //Note, may support more measurment types than sonar in the future!
narshu 0:e238496b8073 291 value = measured.value;
narshu 0:e238496b8073 292 variance = measured.variance;
narshu 0:e238496b8073 293
narshu 0:e238496b8073 294 // don't forget to free the memory
narshu 0:e238496b8073 295 measureMQ.free(&measured);
narshu 0:e238496b8073 296
narshu 0:e238496b8073 297 if (type <= maxmeasure) {
narshu 0:e238496b8073 298
narshu 0:e238496b8073 299 if (type <= SONAR3) {
narshu 0:e238496b8073 300
narshu 0:e238496b8073 301 float dist = value / 1000.0f; //converting to m from mm
narshu 0:e238496b8073 302 int sonarid = type;
narshu 0:e238496b8073 303 aborton2stddev = false;
narshu 0:e238496b8073 304
narshu 0:e238496b8073 305 // Remove the offset if possible
narshu 0:e238496b8073 306 if (Kalman_init)
narshu 0:e238496b8073 307 dist = dist - SonarMeasure_Offset[sonarid];
narshu 0:e238496b8073 308
narshu 0:e238496b8073 309 statelock.lock();
narshu 0:e238496b8073 310 //update the current sonar readings
narshu 0:e238496b8073 311 SonarMeasures[sonarid] = dist;
narshu 0:e238496b8073 312
narshu 0:e238496b8073 313 rbx = X(0) - beaconpos[sonarid].x/1000.0f;
narshu 0:e238496b8073 314 rby = X(1) - beaconpos[sonarid].y/1000.0f;
narshu 0:e238496b8073 315
narshu 0:e238496b8073 316 expecdist = hypot(rbx, rby);//sqrt(rbx*rbx + rby*rby);
narshu 0:e238496b8073 317 Y = dist - expecdist;
narshu 0:e238496b8073 318
narshu 0:e238496b8073 319 dhdx = rbx / expecdist;
narshu 0:e238496b8073 320 dhdy = rby / expecdist;
narshu 0:e238496b8073 321
narshu 0:e238496b8073 322 H = dhdx, dhdy, 0;
narshu 0:e238496b8073 323
narshu 0:e238496b8073 324 } else if (type <= IR3) {
narshu 0:e238496b8073 325
narshu 0:e238496b8073 326 aborton2stddev = false;
narshu 0:e238496b8073 327 int IRidx = type-3;
narshu 0:e238496b8073 328
narshu 0:e238496b8073 329 statelock.lock();
narshu 0:e238496b8073 330 IRMeasures[IRidx] = value;
narshu 0:e238496b8073 331
narshu 0:e238496b8073 332 rbx = X(0) - beaconpos[IRidx].x/1000.0f;
narshu 0:e238496b8073 333 rby = X(1) - beaconpos[IRidx].y/1000.0f;
narshu 0:e238496b8073 334
narshu 0:e238496b8073 335 float expecang = atan2(-rby, -rbx) - X(2);
narshu 0:e238496b8073 336 Y = rectifyAng(value - expecang);
narshu 0:e238496b8073 337
narshu 0:e238496b8073 338 float dstsq = rbx*rbx + rby*rby;
narshu 0:e238496b8073 339 H = -rby/dstsq, rbx/dstsq, -1;
narshu 0:e238496b8073 340 }
narshu 0:e238496b8073 341
narshu 0:e238496b8073 342 Matrix<float, 3, 1> PH (P * trans(H));
narshu 0:e238496b8073 343 S = (H * PH)(0,0) + variance;
narshu 0:e238496b8073 344
narshu 0:e238496b8073 345 if (aborton2stddev && Y*Y > 4 * S) {
narshu 0:e238496b8073 346 statelock.unlock();
narshu 0:e238496b8073 347 continue;
narshu 0:e238496b8073 348 }
narshu 0:e238496b8073 349
narshu 0:e238496b8073 350 Matrix<float, 3, 1> K (PH * (1/S));
narshu 0:e238496b8073 351
narshu 0:e238496b8073 352 //Updating state
narshu 0:e238496b8073 353 X += col(K, 0) * Y;
narshu 0:e238496b8073 354 X(2) = rectifyAng(X(2));
narshu 0:e238496b8073 355
narshu 0:e238496b8073 356 P = (I3 - K * H) * P;
narshu 0:e238496b8073 357
narshu 0:e238496b8073 358 statelock.unlock();
narshu 0:e238496b8073 359
narshu 0:e238496b8073 360 }
narshu 0:e238496b8073 361
narshu 0:e238496b8073 362 } else {
narshu 0:e238496b8073 363 OLED4 = 1;
narshu 0:e238496b8073 364 //printf("ERROR: in updateloop, code %#x", evt);
narshu 0:e238496b8073 365 }
narshu 0:e238496b8073 366
narshu 0:e238496b8073 367 }
narshu 0:e238496b8073 368
narshu 0:e238496b8073 369 }