mbed SDK library sources

Fork of mbed-src by mbed official

Development branch of the mbed library sources. This library is kept in synch with the latest changes from the mbed SDK and it is not guaranteed to work.

If you are looking for a stable and tested release, please import one of the official mbed library releases:

Import librarymbed

The official Mbed 2 C/C++ SDK provides the software platform and libraries to build your applications.

Committer:
mbed_official
Date:
Fri Oct 25 15:00:04 2013 +0100
Revision:
41:e8b66477f5bf
Parent:
13:0645d8841f51
Synchronized with git revision 317a1f66d2a2d4efe5544d3bf6c2aaf1d147a968

Who changed what in which revision?

UserRevisionLine numberNew contents of line
emilmont 10:3bc89ef62ce7 1 /* mbed Microcontroller Library
emilmont 10:3bc89ef62ce7 2 * Copyright (c) 2006-2013 ARM Limited
emilmont 10:3bc89ef62ce7 3 *
emilmont 10:3bc89ef62ce7 4 * Licensed under the Apache License, Version 2.0 (the "License");
emilmont 10:3bc89ef62ce7 5 * you may not use this file except in compliance with the License.
emilmont 10:3bc89ef62ce7 6 * You may obtain a copy of the License at
emilmont 10:3bc89ef62ce7 7 *
emilmont 10:3bc89ef62ce7 8 * http://www.apache.org/licenses/LICENSE-2.0
emilmont 10:3bc89ef62ce7 9 *
emilmont 10:3bc89ef62ce7 10 * Unless required by applicable law or agreed to in writing, software
emilmont 10:3bc89ef62ce7 11 * distributed under the License is distributed on an "AS IS" BASIS,
emilmont 10:3bc89ef62ce7 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
emilmont 10:3bc89ef62ce7 13 * See the License for the specific language governing permissions and
emilmont 10:3bc89ef62ce7 14 * limitations under the License.
emilmont 10:3bc89ef62ce7 15 */
emilmont 10:3bc89ef62ce7 16 #include "can_api.h"
emilmont 10:3bc89ef62ce7 17 #include "cmsis.h"
emilmont 10:3bc89ef62ce7 18 #include "pinmap.h"
emilmont 10:3bc89ef62ce7 19 #include "error.h"
emilmont 10:3bc89ef62ce7 20
emilmont 10:3bc89ef62ce7 21 #include <math.h>
emilmont 10:3bc89ef62ce7 22 #include <string.h>
emilmont 10:3bc89ef62ce7 23
emilmont 10:3bc89ef62ce7 24 /* Acceptance filter mode in AFMR register */
emilmont 10:3bc89ef62ce7 25 #define ACCF_OFF 0x01
emilmont 10:3bc89ef62ce7 26 #define ACCF_BYPASS 0x02
emilmont 10:3bc89ef62ce7 27 #define ACCF_ON 0x00
emilmont 10:3bc89ef62ce7 28 #define ACCF_FULLCAN 0x04
emilmont 10:3bc89ef62ce7 29
emilmont 10:3bc89ef62ce7 30 /* There are several bit timing calculators on the internet.
emilmont 10:3bc89ef62ce7 31 http://www.port.de/engl/canprod/sv_req_form.html
emilmont 10:3bc89ef62ce7 32 http://www.kvaser.com/can/index.htm
emilmont 10:3bc89ef62ce7 33 */
emilmont 10:3bc89ef62ce7 34
emilmont 10:3bc89ef62ce7 35 static const PinMap PinMap_CAN_RD[] = {
emilmont 10:3bc89ef62ce7 36 {P0_0 , CAN_1, 1},
emilmont 10:3bc89ef62ce7 37 {P0_4 , CAN_2, 2},
emilmont 10:3bc89ef62ce7 38 {P0_21, CAN_1, 3},
emilmont 10:3bc89ef62ce7 39 {P2_7 , CAN_2, 1},
emilmont 10:3bc89ef62ce7 40 {NC , NC , 0}
emilmont 10:3bc89ef62ce7 41 };
emilmont 10:3bc89ef62ce7 42
emilmont 10:3bc89ef62ce7 43 static const PinMap PinMap_CAN_TD[] = {
emilmont 10:3bc89ef62ce7 44 {P0_1 , CAN_1, 1},
emilmont 10:3bc89ef62ce7 45 {P0_5 , CAN_2, 2},
emilmont 10:3bc89ef62ce7 46 {P0_22, CAN_1, 3},
emilmont 10:3bc89ef62ce7 47 {P2_8 , CAN_2, 1},
emilmont 10:3bc89ef62ce7 48 {NC , NC , 0}
emilmont 10:3bc89ef62ce7 49 };
emilmont 10:3bc89ef62ce7 50
emilmont 10:3bc89ef62ce7 51 // Type definition to hold a CAN message
emilmont 10:3bc89ef62ce7 52 struct CANMsg {
emilmont 10:3bc89ef62ce7 53 unsigned int reserved1 : 16;
emilmont 10:3bc89ef62ce7 54 unsigned int dlc : 4; // Bits 16..19: DLC - Data Length Counter
emilmont 10:3bc89ef62ce7 55 unsigned int reserved0 : 10;
emilmont 10:3bc89ef62ce7 56 unsigned int rtr : 1; // Bit 30: Set if this is a RTR message
emilmont 10:3bc89ef62ce7 57 unsigned int type : 1; // Bit 31: Set if this is a 29-bit ID message
emilmont 10:3bc89ef62ce7 58 unsigned int id; // CAN Message ID (11-bit or 29-bit)
emilmont 10:3bc89ef62ce7 59 unsigned char data[8]; // CAN Message Data Bytes 0-7
emilmont 10:3bc89ef62ce7 60 };
emilmont 10:3bc89ef62ce7 61 typedef struct CANMsg CANMsg;
emilmont 10:3bc89ef62ce7 62
emilmont 10:3bc89ef62ce7 63 static uint32_t can_disable(can_t *obj) {
emilmont 10:3bc89ef62ce7 64 uint32_t sm = obj->dev->MOD;
emilmont 10:3bc89ef62ce7 65 obj->dev->MOD |= 1;
emilmont 10:3bc89ef62ce7 66 return sm;
emilmont 10:3bc89ef62ce7 67 }
emilmont 10:3bc89ef62ce7 68
emilmont 10:3bc89ef62ce7 69 static inline void can_enable(can_t *obj) {
emilmont 10:3bc89ef62ce7 70 if (obj->dev->MOD & 1) {
emilmont 10:3bc89ef62ce7 71 obj->dev->MOD &= ~(1);
emilmont 10:3bc89ef62ce7 72 }
emilmont 10:3bc89ef62ce7 73 }
emilmont 10:3bc89ef62ce7 74
mbed_official 41:e8b66477f5bf 75 int can_mode(can_t *obj, CanMode mode) {
mbed_official 41:e8b66477f5bf 76 return 0; // not implemented
mbed_official 41:e8b66477f5bf 77 }
mbed_official 41:e8b66477f5bf 78
mbed_official 41:e8b66477f5bf 79 int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t handle) {
mbed_official 41:e8b66477f5bf 80 return 0; // not implemented
mbed_official 41:e8b66477f5bf 81 }
mbed_official 41:e8b66477f5bf 82
emilmont 10:3bc89ef62ce7 83 static int can_pclk(can_t *obj) {
emilmont 10:3bc89ef62ce7 84 int value = 0;
emilmont 10:3bc89ef62ce7 85 switch ((int)obj->dev) {
emilmont 10:3bc89ef62ce7 86 case CAN_1: value = (LPC_SC->PCLKSEL0 & (0x3 << 26)) >> 26; break;
emilmont 10:3bc89ef62ce7 87 case CAN_2: value = (LPC_SC->PCLKSEL0 & (0x3 << 28)) >> 28; break;
emilmont 10:3bc89ef62ce7 88 }
emilmont 10:3bc89ef62ce7 89
emilmont 10:3bc89ef62ce7 90 switch (value) {
emilmont 10:3bc89ef62ce7 91 case 1: return 1;
emilmont 10:3bc89ef62ce7 92 case 2: return 2;
emilmont 10:3bc89ef62ce7 93 case 3: return 6;
emilmont 10:3bc89ef62ce7 94 default: return 4;
emilmont 10:3bc89ef62ce7 95 }
emilmont 10:3bc89ef62ce7 96 }
emilmont 10:3bc89ef62ce7 97
emilmont 10:3bc89ef62ce7 98 // This table has the sampling points as close to 75% as possible. The first
emilmont 10:3bc89ef62ce7 99 // value is TSEG1, the second TSEG2.
emilmont 10:3bc89ef62ce7 100 static const int timing_pts[23][2] = {
emilmont 10:3bc89ef62ce7 101 {0x0, 0x0}, // 2, 50%
emilmont 10:3bc89ef62ce7 102 {0x1, 0x0}, // 3, 67%
emilmont 10:3bc89ef62ce7 103 {0x2, 0x0}, // 4, 75%
emilmont 10:3bc89ef62ce7 104 {0x3, 0x0}, // 5, 80%
emilmont 10:3bc89ef62ce7 105 {0x3, 0x1}, // 6, 67%
emilmont 10:3bc89ef62ce7 106 {0x4, 0x1}, // 7, 71%
emilmont 10:3bc89ef62ce7 107 {0x5, 0x1}, // 8, 75%
emilmont 10:3bc89ef62ce7 108 {0x6, 0x1}, // 9, 78%
emilmont 10:3bc89ef62ce7 109 {0x6, 0x2}, // 10, 70%
emilmont 10:3bc89ef62ce7 110 {0x7, 0x2}, // 11, 73%
emilmont 10:3bc89ef62ce7 111 {0x8, 0x2}, // 12, 75%
emilmont 10:3bc89ef62ce7 112 {0x9, 0x2}, // 13, 77%
emilmont 10:3bc89ef62ce7 113 {0x9, 0x3}, // 14, 71%
emilmont 10:3bc89ef62ce7 114 {0xA, 0x3}, // 15, 73%
emilmont 10:3bc89ef62ce7 115 {0xB, 0x3}, // 16, 75%
emilmont 10:3bc89ef62ce7 116 {0xC, 0x3}, // 17, 76%
emilmont 10:3bc89ef62ce7 117 {0xD, 0x3}, // 18, 78%
emilmont 10:3bc89ef62ce7 118 {0xD, 0x4}, // 19, 74%
emilmont 10:3bc89ef62ce7 119 {0xE, 0x4}, // 20, 75%
emilmont 10:3bc89ef62ce7 120 {0xF, 0x4}, // 21, 76%
emilmont 10:3bc89ef62ce7 121 {0xF, 0x5}, // 22, 73%
emilmont 10:3bc89ef62ce7 122 {0xF, 0x6}, // 23, 70%
emilmont 10:3bc89ef62ce7 123 {0xF, 0x7}, // 24, 67%
emilmont 10:3bc89ef62ce7 124 };
emilmont 10:3bc89ef62ce7 125
emilmont 10:3bc89ef62ce7 126 static unsigned int can_speed(unsigned int sclk, unsigned int pclk, unsigned int cclk, unsigned char psjw) {
emilmont 10:3bc89ef62ce7 127 uint32_t btr;
emilmont 10:3bc89ef62ce7 128 uint16_t brp = 0;
emilmont 10:3bc89ef62ce7 129 uint32_t calcbit;
emilmont 10:3bc89ef62ce7 130 uint32_t bitwidth;
emilmont 10:3bc89ef62ce7 131 int hit = 0;
emilmont 10:3bc89ef62ce7 132 int bits;
emilmont 10:3bc89ef62ce7 133
emilmont 10:3bc89ef62ce7 134 bitwidth = sclk / (pclk * cclk);
emilmont 10:3bc89ef62ce7 135
emilmont 10:3bc89ef62ce7 136 brp = bitwidth / 0x18;
emilmont 10:3bc89ef62ce7 137 while ((!hit) && (brp < bitwidth / 4)) {
emilmont 10:3bc89ef62ce7 138 brp++;
emilmont 10:3bc89ef62ce7 139 for (bits = 22; bits > 0; bits--) {
emilmont 10:3bc89ef62ce7 140 calcbit = (bits + 3) * (brp + 1);
emilmont 10:3bc89ef62ce7 141 if (calcbit == bitwidth) {
emilmont 10:3bc89ef62ce7 142 hit = 1;
emilmont 10:3bc89ef62ce7 143 break;
emilmont 10:3bc89ef62ce7 144 }
emilmont 10:3bc89ef62ce7 145 }
emilmont 10:3bc89ef62ce7 146 }
emilmont 10:3bc89ef62ce7 147
emilmont 10:3bc89ef62ce7 148 if (hit) {
emilmont 10:3bc89ef62ce7 149 btr = ((timing_pts[bits][1] << 20) & 0x00700000)
emilmont 10:3bc89ef62ce7 150 | ((timing_pts[bits][0] << 16) & 0x000F0000)
emilmont 10:3bc89ef62ce7 151 | ((psjw << 14) & 0x0000C000)
emilmont 10:3bc89ef62ce7 152 | ((brp << 0) & 0x000003FF);
emilmont 10:3bc89ef62ce7 153 } else {
emilmont 10:3bc89ef62ce7 154 btr = 0xFFFFFFFF;
emilmont 10:3bc89ef62ce7 155 }
emilmont 10:3bc89ef62ce7 156
emilmont 10:3bc89ef62ce7 157 return btr;
emilmont 10:3bc89ef62ce7 158 }
emilmont 10:3bc89ef62ce7 159
emilmont 10:3bc89ef62ce7 160 void can_init(can_t *obj, PinName rd, PinName td) {
emilmont 10:3bc89ef62ce7 161 CANName can_rd = (CANName)pinmap_peripheral(rd, PinMap_CAN_RD);
emilmont 10:3bc89ef62ce7 162 CANName can_td = (CANName)pinmap_peripheral(td, PinMap_CAN_TD);
emilmont 10:3bc89ef62ce7 163 obj->dev = (LPC_CAN_TypeDef *)pinmap_merge(can_rd, can_td);
emilmont 10:3bc89ef62ce7 164 if ((int)obj->dev == NC) {
emilmont 10:3bc89ef62ce7 165 error("CAN pin mapping failed");
emilmont 10:3bc89ef62ce7 166 }
emilmont 10:3bc89ef62ce7 167
emilmont 10:3bc89ef62ce7 168 switch ((int)obj->dev) {
emilmont 10:3bc89ef62ce7 169 case CAN_1: LPC_SC->PCONP |= 1 << 13; break;
emilmont 10:3bc89ef62ce7 170 case CAN_2: LPC_SC->PCONP |= 1 << 14; break;
emilmont 10:3bc89ef62ce7 171 }
emilmont 10:3bc89ef62ce7 172
emilmont 10:3bc89ef62ce7 173 pinmap_pinout(rd, PinMap_CAN_RD);
emilmont 10:3bc89ef62ce7 174 pinmap_pinout(td, PinMap_CAN_TD);
emilmont 10:3bc89ef62ce7 175
emilmont 10:3bc89ef62ce7 176 can_reset(obj);
emilmont 10:3bc89ef62ce7 177 obj->dev->IER = 0; // Disable Interrupts
emilmont 10:3bc89ef62ce7 178 can_frequency(obj, 100000);
emilmont 10:3bc89ef62ce7 179
emilmont 10:3bc89ef62ce7 180 LPC_CANAF->AFMR = ACCF_BYPASS; // Bypass Filter
emilmont 10:3bc89ef62ce7 181 }
emilmont 10:3bc89ef62ce7 182
emilmont 10:3bc89ef62ce7 183 void can_free(can_t *obj) {
emilmont 10:3bc89ef62ce7 184 switch ((int)obj->dev) {
emilmont 10:3bc89ef62ce7 185 case CAN_1: LPC_SC->PCONP &= ~(1 << 13); break;
emilmont 10:3bc89ef62ce7 186 case CAN_2: LPC_SC->PCONP &= ~(1 << 14); break;
emilmont 10:3bc89ef62ce7 187 }
emilmont 10:3bc89ef62ce7 188 }
emilmont 10:3bc89ef62ce7 189
emilmont 10:3bc89ef62ce7 190 int can_frequency(can_t *obj, int f) {
emilmont 10:3bc89ef62ce7 191 int pclk = can_pclk(obj);
emilmont 10:3bc89ef62ce7 192 int btr = can_speed(SystemCoreClock, pclk, (unsigned int)f, 1);
emilmont 10:3bc89ef62ce7 193
emilmont 10:3bc89ef62ce7 194 if (btr > 0) {
emilmont 10:3bc89ef62ce7 195 uint32_t modmask = can_disable(obj);
emilmont 10:3bc89ef62ce7 196 obj->dev->BTR = btr;
emilmont 10:3bc89ef62ce7 197 obj->dev->MOD = modmask;
emilmont 10:3bc89ef62ce7 198 return 1;
emilmont 10:3bc89ef62ce7 199 } else {
emilmont 10:3bc89ef62ce7 200 return 0;
emilmont 10:3bc89ef62ce7 201 }
emilmont 10:3bc89ef62ce7 202 }
emilmont 10:3bc89ef62ce7 203
emilmont 10:3bc89ef62ce7 204 int can_write(can_t *obj, CAN_Message msg, int cc) {
emilmont 10:3bc89ef62ce7 205 unsigned int CANStatus;
emilmont 10:3bc89ef62ce7 206 CANMsg m;
emilmont 10:3bc89ef62ce7 207
emilmont 10:3bc89ef62ce7 208 can_enable(obj);
emilmont 10:3bc89ef62ce7 209
emilmont 10:3bc89ef62ce7 210 m.id = msg.id ;
emilmont 10:3bc89ef62ce7 211 m.dlc = msg.len & 0xF;
emilmont 10:3bc89ef62ce7 212 m.rtr = msg.type;
emilmont 10:3bc89ef62ce7 213 m.type = msg.format;
emilmont 10:3bc89ef62ce7 214 memcpy(m.data, msg.data, msg.len);
emilmont 10:3bc89ef62ce7 215 const unsigned int *buf = (const unsigned int *)&m;
emilmont 10:3bc89ef62ce7 216
emilmont 10:3bc89ef62ce7 217 CANStatus = obj->dev->SR;
emilmont 10:3bc89ef62ce7 218 if (CANStatus & 0x00000004) {
emilmont 10:3bc89ef62ce7 219 obj->dev->TFI1 = buf[0] & 0xC00F0000;
emilmont 10:3bc89ef62ce7 220 obj->dev->TID1 = buf[1];
emilmont 10:3bc89ef62ce7 221 obj->dev->TDA1 = buf[2];
emilmont 10:3bc89ef62ce7 222 obj->dev->TDB1 = buf[3];
emilmont 10:3bc89ef62ce7 223 if (cc) {
emilmont 10:3bc89ef62ce7 224 obj->dev->CMR = 0x30;
emilmont 10:3bc89ef62ce7 225 } else {
emilmont 10:3bc89ef62ce7 226 obj->dev->CMR = 0x21;
emilmont 10:3bc89ef62ce7 227 }
emilmont 10:3bc89ef62ce7 228 return 1;
emilmont 10:3bc89ef62ce7 229
emilmont 10:3bc89ef62ce7 230 } else if (CANStatus & 0x00000400) {
emilmont 10:3bc89ef62ce7 231 obj->dev->TFI2 = buf[0] & 0xC00F0000;
emilmont 10:3bc89ef62ce7 232 obj->dev->TID2 = buf[1];
emilmont 10:3bc89ef62ce7 233 obj->dev->TDA2 = buf[2];
emilmont 10:3bc89ef62ce7 234 obj->dev->TDB2 = buf[3];
emilmont 10:3bc89ef62ce7 235 if (cc) {
emilmont 10:3bc89ef62ce7 236 obj->dev->CMR = 0x50;
emilmont 10:3bc89ef62ce7 237 } else {
emilmont 10:3bc89ef62ce7 238 obj->dev->CMR = 0x41;
emilmont 10:3bc89ef62ce7 239 }
emilmont 10:3bc89ef62ce7 240 return 1;
emilmont 10:3bc89ef62ce7 241
emilmont 10:3bc89ef62ce7 242 } else if (CANStatus & 0x00040000) {
emilmont 10:3bc89ef62ce7 243 obj->dev->TFI3 = buf[0] & 0xC00F0000;
emilmont 10:3bc89ef62ce7 244 obj->dev->TID3 = buf[1];
emilmont 10:3bc89ef62ce7 245 obj->dev->TDA3 = buf[2];
emilmont 10:3bc89ef62ce7 246 obj->dev->TDB3 = buf[3];
emilmont 10:3bc89ef62ce7 247 if (cc) {
emilmont 10:3bc89ef62ce7 248 obj->dev->CMR = 0x90;
emilmont 10:3bc89ef62ce7 249 } else {
emilmont 10:3bc89ef62ce7 250 obj->dev->CMR = 0x81;
emilmont 10:3bc89ef62ce7 251 }
emilmont 10:3bc89ef62ce7 252 return 1;
emilmont 10:3bc89ef62ce7 253 }
emilmont 10:3bc89ef62ce7 254
emilmont 10:3bc89ef62ce7 255 return 0;
emilmont 10:3bc89ef62ce7 256 }
emilmont 10:3bc89ef62ce7 257
mbed_official 41:e8b66477f5bf 258 int can_read(can_t *obj, CAN_Message *msg, int handle) {
emilmont 10:3bc89ef62ce7 259 CANMsg x;
emilmont 10:3bc89ef62ce7 260 unsigned int *i = (unsigned int *)&x;
emilmont 10:3bc89ef62ce7 261
emilmont 10:3bc89ef62ce7 262 can_enable(obj);
emilmont 10:3bc89ef62ce7 263
emilmont 10:3bc89ef62ce7 264 if (obj->dev->GSR & 0x1) {
emilmont 10:3bc89ef62ce7 265 *i++ = obj->dev->RFS; // Frame
emilmont 10:3bc89ef62ce7 266 *i++ = obj->dev->RID; // ID
emilmont 10:3bc89ef62ce7 267 *i++ = obj->dev->RDA; // Data A
emilmont 10:3bc89ef62ce7 268 *i++ = obj->dev->RDB; // Data B
emilmont 10:3bc89ef62ce7 269 obj->dev->CMR = 0x04; // release receive buffer
emilmont 10:3bc89ef62ce7 270
emilmont 10:3bc89ef62ce7 271 msg->id = x.id;
emilmont 10:3bc89ef62ce7 272 msg->len = x.dlc;
emilmont 10:3bc89ef62ce7 273 msg->format = (x.type)? CANExtended : CANStandard;
emilmont 10:3bc89ef62ce7 274 msg->type = (x.rtr)? CANRemote: CANData;
emilmont 10:3bc89ef62ce7 275 memcpy(msg->data,x.data,x.dlc);
emilmont 10:3bc89ef62ce7 276 return 1;
emilmont 10:3bc89ef62ce7 277 }
emilmont 10:3bc89ef62ce7 278
emilmont 10:3bc89ef62ce7 279 return 0;
emilmont 10:3bc89ef62ce7 280 }
emilmont 10:3bc89ef62ce7 281
emilmont 10:3bc89ef62ce7 282 void can_reset(can_t *obj) {
emilmont 10:3bc89ef62ce7 283 can_disable(obj);
emilmont 10:3bc89ef62ce7 284 obj->dev->GSR = 0; // Reset error counter when CAN1MOD is in reset
emilmont 10:3bc89ef62ce7 285 }
emilmont 10:3bc89ef62ce7 286
emilmont 10:3bc89ef62ce7 287 unsigned char can_rderror(can_t *obj) {
emilmont 10:3bc89ef62ce7 288 return (obj->dev->GSR >> 16) & 0xFF;
emilmont 10:3bc89ef62ce7 289 }
emilmont 10:3bc89ef62ce7 290
emilmont 10:3bc89ef62ce7 291 unsigned char can_tderror(can_t *obj) {
emilmont 10:3bc89ef62ce7 292 return (obj->dev->GSR >> 24) & 0xFF;
emilmont 10:3bc89ef62ce7 293 }
emilmont 10:3bc89ef62ce7 294
emilmont 10:3bc89ef62ce7 295 void can_monitor(can_t *obj, int silent) {
emilmont 10:3bc89ef62ce7 296 uint32_t mod_mask = can_disable(obj);
emilmont 10:3bc89ef62ce7 297 if (silent) {
emilmont 10:3bc89ef62ce7 298 obj->dev->MOD |= (1 << 1);
emilmont 10:3bc89ef62ce7 299 } else {
emilmont 10:3bc89ef62ce7 300 obj->dev->MOD &= ~(1 << 1);
emilmont 10:3bc89ef62ce7 301 }
emilmont 10:3bc89ef62ce7 302 if (!(mod_mask & 1)) {
emilmont 10:3bc89ef62ce7 303 can_enable(obj);
emilmont 10:3bc89ef62ce7 304 }
emilmont 10:3bc89ef62ce7 305 }