|
| 1 | +/* mbed Microcontroller Library |
| 2 | + * Copyright (c) 2006-2013 ARM Limited |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | +#include "can_api.h" |
| 17 | + |
| 18 | +#include "cmsis.h" |
| 19 | +#include "pinmap.h" |
| 20 | +#include "error.h" |
| 21 | + |
| 22 | +#include <math.h> |
| 23 | +#include <string.h> |
| 24 | +#warning (matthewelse) Uses LPC1768 code, and hasn't been ported yet... |
| 25 | +#if 0 |
| 26 | +// Type definition to hold a CAN message |
| 27 | +struct CANMsg { |
| 28 | + unsigned int reserved1 : 16; |
| 29 | + unsigned int dlc : 4; // Bits 16..19: DLC - Data Length Counter |
| 30 | + unsigned int reserved0 : 10; |
| 31 | + unsigned int rtr : 1; // Bit 30: Set if this is a RTR message |
| 32 | + unsigned int type : 1; // Bit 31: Set if this is a 29-bit ID message |
| 33 | + unsigned int id; // CAN Message ID (11-bit or 29-bit) |
| 34 | + unsigned char data[8]; // CAN Message Data Bytes 0-7 |
| 35 | +}; |
| 36 | +typedef struct CANMsg CANMsg; |
| 37 | + |
| 38 | +static uint32_t can_irq_id = 0; |
| 39 | +static can_irq_handler irq_handler; |
| 40 | + |
| 41 | +static uint32_t can_disable(can_t *obj) { |
| 42 | + uint32_t sm = LPC_CAN->CNTL; |
| 43 | + LPC_CAN->CNTL |= 1; |
| 44 | + return sm; |
| 45 | +} |
| 46 | + |
| 47 | +static inline void can_enable(can_t *obj) { |
| 48 | + if (LPC_CAN->CNTL & 1) { |
| 49 | + LPC_CAN->CNTL &= ~(1); |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +int can_mode(can_t *obj, CanMode mode) |
| 54 | +{ |
| 55 | + return 0; // not implemented |
| 56 | +} |
| 57 | + |
| 58 | +static inline void can_irq(uint32_t icr, uint32_t index) { |
| 59 | + uint32_t i; |
| 60 | + |
| 61 | + for(i = 0; i < 8; i++) |
| 62 | + { |
| 63 | + if((can_irq_id != 0) && (icr & (1 << i))) |
| 64 | + { |
| 65 | + switch (i) { |
| 66 | + case 0: irq_handler(can_irq_id, IRQ_RX); break; |
| 67 | + case 1: irq_handler(can_irq_id, IRQ_TX); break; |
| 68 | + case 2: irq_handler(can_irq_id, IRQ_ERROR); break; |
| 69 | + case 3: irq_handler(can_irq_id, IRQ_OVERRUN); break; |
| 70 | + case 4: irq_handler(can_irq_id, IRQ_WAKEUP); break; |
| 71 | + case 5: irq_handler(can_irq_id, IRQ_PASSIVE); break; |
| 72 | + case 6: irq_handler(can_irq_id, IRQ_ARB); break; |
| 73 | + case 7: irq_handler(can_irq_id, IRQ_BUS); break; |
| 74 | + case 8: irq_handler(can_irq_id, IRQ_READY); break; |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +// Have to check that the CAN block is active before reading the Interrupt |
| 81 | +// Control Register, or the mbed hangs |
| 82 | +void can_irq_n() { |
| 83 | + uint32_t icr; |
| 84 | + |
| 85 | + if(LPC_SYSCON->PCONP & (1 << 13)) { |
| 86 | + //icr = LPC_CAN1->ICR & 0x1FF; |
| 87 | + can_irq(icr, 0); |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +// Register CAN object's irq handler |
| 92 | +void can_irq_init(can_t *obj, can_irq_handler handler, uint32_t id) { |
| 93 | + irq_handler = handler; |
| 94 | + can_irq_id = id; |
| 95 | +} |
| 96 | + |
| 97 | +// Unregister CAN object's irq handler |
| 98 | +void can_irq_free(can_t *obj) { |
| 99 | + LPC_CAN->CNTL &= ~(1 << 1); // Disable Interrupts :) |
| 100 | + |
| 101 | + can_irq_id = 0; |
| 102 | + NVIC_DisableIRQ(CAN_IRQn); |
| 103 | +} |
| 104 | + |
| 105 | +// Clear or set a irq |
| 106 | +void can_irq_set(can_t *obj, CanIrqType type, uint32_t enable) { |
| 107 | + uint32_t ier; |
| 108 | + |
| 109 | + switch (type) { |
| 110 | + case IRQ_RX: ier = (1 << 0); break; |
| 111 | + case IRQ_TX: ier = (1 << 1); break; |
| 112 | + case IRQ_ERROR: ier = (1 << 2); break; |
| 113 | + case IRQ_OVERRUN: ier = (1 << 3); break; |
| 114 | + case IRQ_WAKEUP: ier = (1 << 4); break; |
| 115 | + case IRQ_PASSIVE: ier = (1 << 5); break; |
| 116 | + case IRQ_ARB: ier = (1 << 6); break; |
| 117 | + case IRQ_BUS: ier = (1 << 7); break; |
| 118 | + case IRQ_READY: ier = (1 << 8); break; |
| 119 | + default: return; |
| 120 | + } |
| 121 | + |
| 122 | + // Put CAN in Reset Mode. |
| 123 | + LPC_CAN->CNTL |= 1; |
| 124 | + if(enable == 0) { |
| 125 | + LPC_CAN->CNTL &= ~(1 << ier); |
| 126 | + } |
| 127 | + else { |
| 128 | + LPC_CAN->CNTL &= ~(1 << ier); |
| 129 | + } |
| 130 | + // Take it out of reset... |
| 131 | + LPC_CAN->CNTL &= ~(1); |
| 132 | + |
| 133 | + // Enable NVIC if at least 1 interrupt is active |
| 134 | + NVIC_SetVector(CAN_IRQn, (uint32_t) &can_irq_n); |
| 135 | + NVIC_EnableIRQ(CAN_IRQn); |
| 136 | +} |
| 137 | + |
| 138 | +// This table has the sampling points as close to 75% as possible. The first |
| 139 | +// value is TSEG1, the second TSEG2. |
| 140 | +static const int timing_pts[23][2] = { |
| 141 | + {0x0, 0x0}, // 2, 50% |
| 142 | + {0x1, 0x0}, // 3, 67% |
| 143 | + {0x2, 0x0}, // 4, 75% |
| 144 | + {0x3, 0x0}, // 5, 80% |
| 145 | + {0x3, 0x1}, // 6, 67% |
| 146 | + {0x4, 0x1}, // 7, 71% |
| 147 | + {0x5, 0x1}, // 8, 75% |
| 148 | + {0x6, 0x1}, // 9, 78% |
| 149 | + {0x6, 0x2}, // 10, 70% |
| 150 | + {0x7, 0x2}, // 11, 73% |
| 151 | + {0x8, 0x2}, // 12, 75% |
| 152 | + {0x9, 0x2}, // 13, 77% |
| 153 | + {0x9, 0x3}, // 14, 71% |
| 154 | + {0xA, 0x3}, // 15, 73% |
| 155 | + {0xB, 0x3}, // 16, 75% |
| 156 | + {0xC, 0x3}, // 17, 76% |
| 157 | + {0xD, 0x3}, // 18, 78% |
| 158 | + {0xD, 0x4}, // 19, 74% |
| 159 | + {0xE, 0x4}, // 20, 75% |
| 160 | + {0xF, 0x4}, // 21, 76% |
| 161 | + {0xF, 0x5}, // 22, 73% |
| 162 | + {0xF, 0x6}, // 23, 70% |
| 163 | + {0xF, 0x7}, // 24, 67% |
| 164 | +}; |
| 165 | + |
| 166 | +static unsigned int can_speed(unsigned int sclk, unsigned int cclk, unsigned char psjw) { |
| 167 | + #warning (matthewelse) This is the bit that I'm going to have no clue about :P |
| 168 | + uint32_t btr; |
| 169 | + uint16_t brp = 0; |
| 170 | + uint32_t calcbit; |
| 171 | + uint32_t bitwidth; |
| 172 | + int hit = 0; |
| 173 | + int bits; |
| 174 | + |
| 175 | + #warning the calculation of bitwidth may be wrong... |
| 176 | + bitwidth = sclk / cclk; |
| 177 | + |
| 178 | + brp = bitwidth / 0x18; |
| 179 | + while ((!hit) && (brp < bitwidth / 4)) { |
| 180 | + brp++; |
| 181 | + for (bits = 22; bits > 0; bits--) { |
| 182 | + calcbit = (bits + 3) * (brp + 1); |
| 183 | + if (calcbit == bitwidth) { |
| 184 | + hit = 1; |
| 185 | + break; |
| 186 | + } |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + if (hit) { |
| 191 | + btr = ((timing_pts[bits][1] << 20) & 0x00700000) |
| 192 | + | ((timing_pts[bits][0] << 16) & 0x000F0000) |
| 193 | + | ((psjw << 14) & 0x0000C000) |
| 194 | + | ((brp << 0) & 0x000003FF); |
| 195 | + } else { |
| 196 | + btr = 0xFFFFFFFF; |
| 197 | + } |
| 198 | + |
| 199 | + return btr; |
| 200 | + |
| 201 | +} |
| 202 | + |
| 203 | +void can_init(can_t *obj, PinName rd, PinName td) { |
| 204 | + LPC_SYSCON->SYSAHBCLKCTRL |= 1 << 17; |
| 205 | + obj->index = 0; |
| 206 | + can_reset(obj); |
| 207 | + LPC_CAN->CNTL &= ~(1 << 2); // Disable Interrupts |
| 208 | + can_frequency(obj, 100000); |
| 209 | + |
| 210 | + // This would be the place to include filter :) |
| 211 | + #warning (matthewelse) This doesn't yet implement filters :( |
| 212 | +} |
| 213 | + |
| 214 | +void can_free(can_t *obj) { |
| 215 | + LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 17); |
| 216 | +} |
| 217 | + |
| 218 | +int can_frequency(can_t *obj, int f) { |
| 219 | + int btr = can_speed(SystemCoreClock, (unsigned int)f, 1); |
| 220 | +#if 0 |
| 221 | + if (btr > 0) { |
| 222 | + uint32_t modmask = can_disable(obj); |
| 223 | + obj->dev->BTR = btr; |
| 224 | + obj->dev->MOD = modmask; |
| 225 | + return 1; |
| 226 | + } else { |
| 227 | + return 0; |
| 228 | + } |
| 229 | +#endif |
| 230 | +} |
| 231 | + |
| 232 | +int can_write(can_t *obj, CAN_Message msg, int cc) { |
| 233 | + unsigned int CANStatus; |
| 234 | + CANMsg m; |
| 235 | + |
| 236 | + can_enable(obj); |
| 237 | + |
| 238 | + m.id = msg.id ; |
| 239 | + m.dlc = msg.len & 0xF; |
| 240 | + m.rtr = msg.type; |
| 241 | + m.type = msg.format; |
| 242 | + memcpy(m.data, msg.data, msg.len); |
| 243 | + const unsigned int *buf = (const unsigned int *)&m; |
| 244 | + |
| 245 | + CANStatus = obj->dev->SR; |
| 246 | + if (CANStatus & 0x00000004) { |
| 247 | + obj->dev->TFI1 = buf[0] & 0xC00F0000; |
| 248 | + obj->dev->TID1 = buf[1]; |
| 249 | + obj->dev->TDA1 = buf[2]; |
| 250 | + obj->dev->TDB1 = buf[3]; |
| 251 | + if(cc) { |
| 252 | + obj->dev->CMR = 0x30; |
| 253 | + } else { |
| 254 | + obj->dev->CMR = 0x21; |
| 255 | + } |
| 256 | + return 1; |
| 257 | + |
| 258 | + } else if (CANStatus & 0x00000400) { |
| 259 | + obj->dev->TFI2 = buf[0] & 0xC00F0000; |
| 260 | + obj->dev->TID2 = buf[1]; |
| 261 | + obj->dev->TDA2 = buf[2]; |
| 262 | + obj->dev->TDB2 = buf[3]; |
| 263 | + if (cc) { |
| 264 | + obj->dev->CMR = 0x50; |
| 265 | + } else { |
| 266 | + obj->dev->CMR = 0x41; |
| 267 | + } |
| 268 | + return 1; |
| 269 | + |
| 270 | + } else if (CANStatus & 0x00040000) { |
| 271 | + obj->dev->TFI3 = buf[0] & 0xC00F0000; |
| 272 | + obj->dev->TID3 = buf[1]; |
| 273 | + obj->dev->TDA3 = buf[2]; |
| 274 | + obj->dev->TDB3 = buf[3]; |
| 275 | + if (cc) { |
| 276 | + obj->dev->CMR = 0x90; |
| 277 | + } else { |
| 278 | + obj->dev->CMR = 0x81; |
| 279 | + } |
| 280 | + return 1; |
| 281 | + } |
| 282 | + |
| 283 | + return 0; |
| 284 | +} |
| 285 | + |
| 286 | +int can_read(can_t *obj, CAN_Message *msg) { |
| 287 | + CANMsg x; |
| 288 | + unsigned int *i = (unsigned int *)&x; |
| 289 | + |
| 290 | + can_enable(obj); |
| 291 | + |
| 292 | + if (obj->dev->GSR & 0x1) { |
| 293 | + *i++ = obj->dev->RFS; // Frame |
| 294 | + *i++ = obj->dev->RID; // ID |
| 295 | + *i++ = obj->dev->RDA; // Data A |
| 296 | + *i++ = obj->dev->RDB; // Data B |
| 297 | + obj->dev->CMR = 0x04; // release receive buffer |
| 298 | + |
| 299 | + msg->id = x.id; |
| 300 | + msg->len = x.dlc; |
| 301 | + msg->format = (x.type)? CANExtended : CANStandard; |
| 302 | + msg->type = (x.rtr)? CANRemote: CANData; |
| 303 | + memcpy(msg->data,x.data,x.dlc); |
| 304 | + return 1; |
| 305 | + } |
| 306 | + |
| 307 | + return 0; |
| 308 | +} |
| 309 | + |
| 310 | +void can_reset(can_t *obj) { |
| 311 | + can_disable(obj); |
| 312 | + obj->dev->GSR = 0; // Reset error counter when CAN1MOD is in reset |
| 313 | +} |
| 314 | + |
| 315 | +unsigned char can_rderror(can_t *obj) { |
| 316 | + return (obj->dev->GSR >> 16) & 0xFF; |
| 317 | +} |
| 318 | + |
| 319 | +unsigned char can_tderror(can_t *obj) { |
| 320 | + return (obj->dev->GSR >> 24) & 0xFF; |
| 321 | +} |
| 322 | + |
| 323 | +void can_monitor(can_t *obj, int silent) { |
| 324 | + uint32_t mod_mask = can_disable(obj); |
| 325 | + if (silent) { |
| 326 | + obj->dev->MOD |= (1 << 1); |
| 327 | + } else { |
| 328 | + obj->dev->MOD &= ~(1 << 1); |
| 329 | + } |
| 330 | + if (!(mod_mask & 1)) { |
| 331 | + can_enable(obj); |
| 332 | + } |
| 333 | +} |
| 334 | +#endif |
0 commit comments