Mouse code for the MacroRat
mbed-dev/targets/TARGET_NXP/TARGET_LPC11XX_11CXX/TARGET_LPC11CXX/can_api.c@46:b156ef445742, 2017-06-03 (annotated)
- Committer:
- sahilmgandhi
- Date:
- Sat Jun 03 00:22:44 2017 +0000
- Revision:
- 46:b156ef445742
- Parent:
- 18:6a4db94011d3
Final code for internal battlebot competition.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
sahilmgandhi | 18:6a4db94011d3 | 1 | /* mbed Microcontroller Library |
sahilmgandhi | 18:6a4db94011d3 | 2 | * Copyright (c) 2006-2013 ARM Limited |
sahilmgandhi | 18:6a4db94011d3 | 3 | * |
sahilmgandhi | 18:6a4db94011d3 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
sahilmgandhi | 18:6a4db94011d3 | 5 | * you may not use this file except in compliance with the License. |
sahilmgandhi | 18:6a4db94011d3 | 6 | * You may obtain a copy of the License at |
sahilmgandhi | 18:6a4db94011d3 | 7 | * |
sahilmgandhi | 18:6a4db94011d3 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
sahilmgandhi | 18:6a4db94011d3 | 9 | * |
sahilmgandhi | 18:6a4db94011d3 | 10 | * Unless required by applicable law or agreed to in writing, software |
sahilmgandhi | 18:6a4db94011d3 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
sahilmgandhi | 18:6a4db94011d3 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
sahilmgandhi | 18:6a4db94011d3 | 13 | * See the License for the specific language governing permissions and |
sahilmgandhi | 18:6a4db94011d3 | 14 | * limitations under the License. |
sahilmgandhi | 18:6a4db94011d3 | 15 | */ |
sahilmgandhi | 18:6a4db94011d3 | 16 | #include "can_api.h" |
sahilmgandhi | 18:6a4db94011d3 | 17 | |
sahilmgandhi | 18:6a4db94011d3 | 18 | #include "cmsis.h" |
sahilmgandhi | 18:6a4db94011d3 | 19 | #include "mbed_error.h" |
sahilmgandhi | 18:6a4db94011d3 | 20 | |
sahilmgandhi | 18:6a4db94011d3 | 21 | #include <math.h> |
sahilmgandhi | 18:6a4db94011d3 | 22 | #include <string.h> |
sahilmgandhi | 18:6a4db94011d3 | 23 | |
sahilmgandhi | 18:6a4db94011d3 | 24 | /* Handy defines */ |
sahilmgandhi | 18:6a4db94011d3 | 25 | #define MSG_OBJ_MAX 32 |
sahilmgandhi | 18:6a4db94011d3 | 26 | #define DLC_MAX 8 |
sahilmgandhi | 18:6a4db94011d3 | 27 | |
sahilmgandhi | 18:6a4db94011d3 | 28 | #define ID_STD_MASK 0x07FF |
sahilmgandhi | 18:6a4db94011d3 | 29 | #define ID_EXT_MASK 0x1FFFFFFF |
sahilmgandhi | 18:6a4db94011d3 | 30 | #define DLC_MASK 0x0F |
sahilmgandhi | 18:6a4db94011d3 | 31 | |
sahilmgandhi | 18:6a4db94011d3 | 32 | static uint32_t can_irq_id = 0; |
sahilmgandhi | 18:6a4db94011d3 | 33 | static can_irq_handler irq_handler; |
sahilmgandhi | 18:6a4db94011d3 | 34 | |
sahilmgandhi | 18:6a4db94011d3 | 35 | static uint32_t can_disable(can_t *obj) { |
sahilmgandhi | 18:6a4db94011d3 | 36 | uint32_t sm = LPC_CAN->CNTL; |
sahilmgandhi | 18:6a4db94011d3 | 37 | LPC_CAN->CNTL |= CANCNTL_INIT; |
sahilmgandhi | 18:6a4db94011d3 | 38 | return sm; |
sahilmgandhi | 18:6a4db94011d3 | 39 | } |
sahilmgandhi | 18:6a4db94011d3 | 40 | |
sahilmgandhi | 18:6a4db94011d3 | 41 | static inline void can_enable(can_t *obj) { |
sahilmgandhi | 18:6a4db94011d3 | 42 | if (LPC_CAN->CNTL & CANCNTL_INIT) { |
sahilmgandhi | 18:6a4db94011d3 | 43 | LPC_CAN->CNTL &= ~CANCNTL_INIT; |
sahilmgandhi | 18:6a4db94011d3 | 44 | } |
sahilmgandhi | 18:6a4db94011d3 | 45 | } |
sahilmgandhi | 18:6a4db94011d3 | 46 | |
sahilmgandhi | 18:6a4db94011d3 | 47 | int can_mode(can_t *obj, CanMode mode) { |
sahilmgandhi | 18:6a4db94011d3 | 48 | int success = 0; |
sahilmgandhi | 18:6a4db94011d3 | 49 | switch (mode) { |
sahilmgandhi | 18:6a4db94011d3 | 50 | case MODE_RESET: |
sahilmgandhi | 18:6a4db94011d3 | 51 | LPC_CAN->CNTL &=~CANCNTL_TEST; |
sahilmgandhi | 18:6a4db94011d3 | 52 | can_disable(obj); |
sahilmgandhi | 18:6a4db94011d3 | 53 | success = 1; |
sahilmgandhi | 18:6a4db94011d3 | 54 | break; |
sahilmgandhi | 18:6a4db94011d3 | 55 | case MODE_NORMAL: |
sahilmgandhi | 18:6a4db94011d3 | 56 | LPC_CAN->CNTL &=~CANCNTL_TEST; |
sahilmgandhi | 18:6a4db94011d3 | 57 | can_enable(obj); |
sahilmgandhi | 18:6a4db94011d3 | 58 | success = 1; |
sahilmgandhi | 18:6a4db94011d3 | 59 | break; |
sahilmgandhi | 18:6a4db94011d3 | 60 | case MODE_SILENT: |
sahilmgandhi | 18:6a4db94011d3 | 61 | LPC_CAN->CNTL |= CANCNTL_TEST; |
sahilmgandhi | 18:6a4db94011d3 | 62 | LPC_CAN->TEST |= CANTEST_SILENT; |
sahilmgandhi | 18:6a4db94011d3 | 63 | LPC_CAN->TEST &=~CANTEST_LBACK; |
sahilmgandhi | 18:6a4db94011d3 | 64 | success = 1; |
sahilmgandhi | 18:6a4db94011d3 | 65 | break; |
sahilmgandhi | 18:6a4db94011d3 | 66 | case MODE_TEST_LOCAL: |
sahilmgandhi | 18:6a4db94011d3 | 67 | LPC_CAN->CNTL |= CANCNTL_TEST; |
sahilmgandhi | 18:6a4db94011d3 | 68 | LPC_CAN->TEST &=~CANTEST_SILENT; |
sahilmgandhi | 18:6a4db94011d3 | 69 | LPC_CAN->TEST |= CANTEST_LBACK; |
sahilmgandhi | 18:6a4db94011d3 | 70 | success = 1; |
sahilmgandhi | 18:6a4db94011d3 | 71 | break; |
sahilmgandhi | 18:6a4db94011d3 | 72 | case MODE_TEST_SILENT: |
sahilmgandhi | 18:6a4db94011d3 | 73 | LPC_CAN->CNTL |= CANCNTL_TEST; |
sahilmgandhi | 18:6a4db94011d3 | 74 | LPC_CAN->TEST |= (CANTEST_LBACK | CANTEST_SILENT); |
sahilmgandhi | 18:6a4db94011d3 | 75 | success = 1; |
sahilmgandhi | 18:6a4db94011d3 | 76 | break; |
sahilmgandhi | 18:6a4db94011d3 | 77 | case MODE_TEST_GLOBAL: |
sahilmgandhi | 18:6a4db94011d3 | 78 | default: |
sahilmgandhi | 18:6a4db94011d3 | 79 | success = 0; |
sahilmgandhi | 18:6a4db94011d3 | 80 | break; |
sahilmgandhi | 18:6a4db94011d3 | 81 | } |
sahilmgandhi | 18:6a4db94011d3 | 82 | |
sahilmgandhi | 18:6a4db94011d3 | 83 | return success; |
sahilmgandhi | 18:6a4db94011d3 | 84 | } |
sahilmgandhi | 18:6a4db94011d3 | 85 | |
sahilmgandhi | 18:6a4db94011d3 | 86 | int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t handle) { |
sahilmgandhi | 18:6a4db94011d3 | 87 | uint16_t i; |
sahilmgandhi | 18:6a4db94011d3 | 88 | |
sahilmgandhi | 18:6a4db94011d3 | 89 | // Find first free message object |
sahilmgandhi | 18:6a4db94011d3 | 90 | if(handle == 0) { |
sahilmgandhi | 18:6a4db94011d3 | 91 | uint32_t msgval = LPC_CAN->MSGV1 | (LPC_CAN->MSGV2 << 16); |
sahilmgandhi | 18:6a4db94011d3 | 92 | // Find first free messagebox |
sahilmgandhi | 18:6a4db94011d3 | 93 | for(i = 0; i < 32; i++) { |
sahilmgandhi | 18:6a4db94011d3 | 94 | if((msgval & (1 << i)) == 0) { |
sahilmgandhi | 18:6a4db94011d3 | 95 | handle = i+1; |
sahilmgandhi | 18:6a4db94011d3 | 96 | break; |
sahilmgandhi | 18:6a4db94011d3 | 97 | } |
sahilmgandhi | 18:6a4db94011d3 | 98 | } |
sahilmgandhi | 18:6a4db94011d3 | 99 | } |
sahilmgandhi | 18:6a4db94011d3 | 100 | |
sahilmgandhi | 18:6a4db94011d3 | 101 | if(handle > 0 && handle < 32) { |
sahilmgandhi | 18:6a4db94011d3 | 102 | if(format == CANExtended) { |
sahilmgandhi | 18:6a4db94011d3 | 103 | // Mark message valid, Direction = TX, Extended Frame, Set Identifier and mask everything |
sahilmgandhi | 18:6a4db94011d3 | 104 | LPC_CAN->IF1_ARB1 = BFN_PREP(id, CANIFn_ARB1_ID); |
sahilmgandhi | 18:6a4db94011d3 | 105 | LPC_CAN->IF1_ARB2 = CANIFn_ARB2_MSGVAL | CANIFn_ARB2_XTD | BFN_PREP(id >> 16, CANIFn_ARB2_ID); |
sahilmgandhi | 18:6a4db94011d3 | 106 | LPC_CAN->IF1_MSK1 = BFN_PREP(mask, CANIFn_MSK1_MSK); |
sahilmgandhi | 18:6a4db94011d3 | 107 | LPC_CAN->IF1_MSK2 = CANIFn_MSK2_MXTD /* | CANIFn_MSK2_MDIR */ | BFN_PREP(mask >> 16, CANIFn_MSK2_MSK); |
sahilmgandhi | 18:6a4db94011d3 | 108 | } |
sahilmgandhi | 18:6a4db94011d3 | 109 | else { |
sahilmgandhi | 18:6a4db94011d3 | 110 | // Mark message valid, Direction = TX, Set Identifier and mask everything |
sahilmgandhi | 18:6a4db94011d3 | 111 | LPC_CAN->IF1_ARB2 = CANIFn_ARB2_MSGVAL | BFN_PREP(id << 2, CANIFn_ARB2_ID); |
sahilmgandhi | 18:6a4db94011d3 | 112 | LPC_CAN->IF1_MSK2 = /* CANIFn_MSK2_MDIR | */ BFN_PREP(mask << 2, CANIFn_MSK2_MSK); |
sahilmgandhi | 18:6a4db94011d3 | 113 | } |
sahilmgandhi | 18:6a4db94011d3 | 114 | |
sahilmgandhi | 18:6a4db94011d3 | 115 | // Use mask, single message object and set DLC |
sahilmgandhi | 18:6a4db94011d3 | 116 | LPC_CAN->IF1_MCTRL = CANIFn_MCTRL_UMASK | CANIFn_MCTRL_EOB | CANIFn_MCTRL_RXIE | BFN_PREP(DLC_MAX, CANIFn_MCTRL_DLC); |
sahilmgandhi | 18:6a4db94011d3 | 117 | |
sahilmgandhi | 18:6a4db94011d3 | 118 | // Transfer all fields to message object |
sahilmgandhi | 18:6a4db94011d3 | 119 | LPC_CAN->IF1_CMDMSK = CANIFn_CMDMSK_WR | CANIFn_CMDMSK_MASK | CANIFn_CMDMSK_ARB | CANIFn_CMDMSK_CTRL; |
sahilmgandhi | 18:6a4db94011d3 | 120 | |
sahilmgandhi | 18:6a4db94011d3 | 121 | // Start Transfer to given message number |
sahilmgandhi | 18:6a4db94011d3 | 122 | LPC_CAN->IF1_CMDREQ = BFN_PREP(handle, CANIFn_CMDREQ_MN); |
sahilmgandhi | 18:6a4db94011d3 | 123 | |
sahilmgandhi | 18:6a4db94011d3 | 124 | // Wait until transfer to message ram complete - TODO: maybe not block?? |
sahilmgandhi | 18:6a4db94011d3 | 125 | while( LPC_CAN->IF1_CMDREQ & CANIFn_CMDREQ_BUSY ); |
sahilmgandhi | 18:6a4db94011d3 | 126 | } |
sahilmgandhi | 18:6a4db94011d3 | 127 | |
sahilmgandhi | 18:6a4db94011d3 | 128 | return handle; |
sahilmgandhi | 18:6a4db94011d3 | 129 | } |
sahilmgandhi | 18:6a4db94011d3 | 130 | |
sahilmgandhi | 18:6a4db94011d3 | 131 | static inline void can_irq() { |
sahilmgandhi | 18:6a4db94011d3 | 132 | irq_handler(can_irq_id, IRQ_RX); |
sahilmgandhi | 18:6a4db94011d3 | 133 | } |
sahilmgandhi | 18:6a4db94011d3 | 134 | |
sahilmgandhi | 18:6a4db94011d3 | 135 | // Register CAN object's irq handler |
sahilmgandhi | 18:6a4db94011d3 | 136 | void can_irq_init(can_t *obj, can_irq_handler handler, uint32_t id) { |
sahilmgandhi | 18:6a4db94011d3 | 137 | irq_handler = handler; |
sahilmgandhi | 18:6a4db94011d3 | 138 | can_irq_id = id; |
sahilmgandhi | 18:6a4db94011d3 | 139 | } |
sahilmgandhi | 18:6a4db94011d3 | 140 | |
sahilmgandhi | 18:6a4db94011d3 | 141 | // Unregister CAN object's irq handler |
sahilmgandhi | 18:6a4db94011d3 | 142 | void can_irq_free(can_t *obj) { |
sahilmgandhi | 18:6a4db94011d3 | 143 | LPC_CAN->CNTL &= ~CANCNTL_IE; // Disable Interrupts :) |
sahilmgandhi | 18:6a4db94011d3 | 144 | |
sahilmgandhi | 18:6a4db94011d3 | 145 | can_irq_id = 0; |
sahilmgandhi | 18:6a4db94011d3 | 146 | NVIC_DisableIRQ(CAN_IRQn); |
sahilmgandhi | 18:6a4db94011d3 | 147 | } |
sahilmgandhi | 18:6a4db94011d3 | 148 | |
sahilmgandhi | 18:6a4db94011d3 | 149 | // Clear or set a irq |
sahilmgandhi | 18:6a4db94011d3 | 150 | void can_irq_set(can_t *obj, CanIrqType type, uint32_t enable) { |
sahilmgandhi | 18:6a4db94011d3 | 151 | // Put CAN in Reset Mode and enable interrupt |
sahilmgandhi | 18:6a4db94011d3 | 152 | can_disable(obj); |
sahilmgandhi | 18:6a4db94011d3 | 153 | if(enable == 0) { |
sahilmgandhi | 18:6a4db94011d3 | 154 | LPC_CAN->CNTL &= ~(CANCNTL_IE | CANCNTL_SIE); |
sahilmgandhi | 18:6a4db94011d3 | 155 | } |
sahilmgandhi | 18:6a4db94011d3 | 156 | else { |
sahilmgandhi | 18:6a4db94011d3 | 157 | LPC_CAN->CNTL |= CANCNTL_IE | CANCNTL_SIE; |
sahilmgandhi | 18:6a4db94011d3 | 158 | } |
sahilmgandhi | 18:6a4db94011d3 | 159 | // Take it out of reset... |
sahilmgandhi | 18:6a4db94011d3 | 160 | can_enable(obj); |
sahilmgandhi | 18:6a4db94011d3 | 161 | |
sahilmgandhi | 18:6a4db94011d3 | 162 | // Enable NVIC if at least 1 interrupt is active |
sahilmgandhi | 18:6a4db94011d3 | 163 | NVIC_SetVector(CAN_IRQn, (uint32_t) &can_irq); |
sahilmgandhi | 18:6a4db94011d3 | 164 | NVIC_EnableIRQ(CAN_IRQn); |
sahilmgandhi | 18:6a4db94011d3 | 165 | } |
sahilmgandhi | 18:6a4db94011d3 | 166 | |
sahilmgandhi | 18:6a4db94011d3 | 167 | // This table has the sampling points as close to 75% as possible. The first |
sahilmgandhi | 18:6a4db94011d3 | 168 | // value is TSEG1, the second TSEG2. |
sahilmgandhi | 18:6a4db94011d3 | 169 | static const int timing_pts[23][2] = { |
sahilmgandhi | 18:6a4db94011d3 | 170 | {0x0, 0x0}, // 2, 50% |
sahilmgandhi | 18:6a4db94011d3 | 171 | {0x1, 0x0}, // 3, 67% |
sahilmgandhi | 18:6a4db94011d3 | 172 | {0x2, 0x0}, // 4, 75% |
sahilmgandhi | 18:6a4db94011d3 | 173 | {0x3, 0x0}, // 5, 80% |
sahilmgandhi | 18:6a4db94011d3 | 174 | {0x3, 0x1}, // 6, 67% |
sahilmgandhi | 18:6a4db94011d3 | 175 | {0x4, 0x1}, // 7, 71% |
sahilmgandhi | 18:6a4db94011d3 | 176 | {0x5, 0x1}, // 8, 75% |
sahilmgandhi | 18:6a4db94011d3 | 177 | {0x6, 0x1}, // 9, 78% |
sahilmgandhi | 18:6a4db94011d3 | 178 | {0x6, 0x2}, // 10, 70% |
sahilmgandhi | 18:6a4db94011d3 | 179 | {0x7, 0x2}, // 11, 73% |
sahilmgandhi | 18:6a4db94011d3 | 180 | {0x8, 0x2}, // 12, 75% |
sahilmgandhi | 18:6a4db94011d3 | 181 | {0x9, 0x2}, // 13, 77% |
sahilmgandhi | 18:6a4db94011d3 | 182 | {0x9, 0x3}, // 14, 71% |
sahilmgandhi | 18:6a4db94011d3 | 183 | {0xA, 0x3}, // 15, 73% |
sahilmgandhi | 18:6a4db94011d3 | 184 | {0xB, 0x3}, // 16, 75% |
sahilmgandhi | 18:6a4db94011d3 | 185 | {0xC, 0x3}, // 17, 76% |
sahilmgandhi | 18:6a4db94011d3 | 186 | {0xD, 0x3}, // 18, 78% |
sahilmgandhi | 18:6a4db94011d3 | 187 | {0xD, 0x4}, // 19, 74% |
sahilmgandhi | 18:6a4db94011d3 | 188 | {0xE, 0x4}, // 20, 75% |
sahilmgandhi | 18:6a4db94011d3 | 189 | {0xF, 0x4}, // 21, 76% |
sahilmgandhi | 18:6a4db94011d3 | 190 | {0xF, 0x5}, // 22, 73% |
sahilmgandhi | 18:6a4db94011d3 | 191 | {0xF, 0x6}, // 23, 70% |
sahilmgandhi | 18:6a4db94011d3 | 192 | {0xF, 0x7}, // 24, 67% |
sahilmgandhi | 18:6a4db94011d3 | 193 | }; |
sahilmgandhi | 18:6a4db94011d3 | 194 | |
sahilmgandhi | 18:6a4db94011d3 | 195 | static unsigned int can_speed(unsigned int sclk, unsigned int cclk, unsigned char psjw) { |
sahilmgandhi | 18:6a4db94011d3 | 196 | uint32_t btr; |
sahilmgandhi | 18:6a4db94011d3 | 197 | uint32_t clkdiv = 1; |
sahilmgandhi | 18:6a4db94011d3 | 198 | uint16_t brp = 0; |
sahilmgandhi | 18:6a4db94011d3 | 199 | uint32_t calcbit; |
sahilmgandhi | 18:6a4db94011d3 | 200 | uint32_t bitwidth; |
sahilmgandhi | 18:6a4db94011d3 | 201 | int hit = 0; |
sahilmgandhi | 18:6a4db94011d3 | 202 | int bits = 0; |
sahilmgandhi | 18:6a4db94011d3 | 203 | |
sahilmgandhi | 18:6a4db94011d3 | 204 | bitwidth = sclk / cclk; |
sahilmgandhi | 18:6a4db94011d3 | 205 | |
sahilmgandhi | 18:6a4db94011d3 | 206 | brp = bitwidth / 0x18; |
sahilmgandhi | 18:6a4db94011d3 | 207 | while ((!hit) && (brp < bitwidth / 4)) { |
sahilmgandhi | 18:6a4db94011d3 | 208 | brp++; |
sahilmgandhi | 18:6a4db94011d3 | 209 | for (bits = 22; bits > 0; bits--) { |
sahilmgandhi | 18:6a4db94011d3 | 210 | calcbit = (bits + 3) * (brp + 1); |
sahilmgandhi | 18:6a4db94011d3 | 211 | if (calcbit == bitwidth) { |
sahilmgandhi | 18:6a4db94011d3 | 212 | hit = 1; |
sahilmgandhi | 18:6a4db94011d3 | 213 | break; |
sahilmgandhi | 18:6a4db94011d3 | 214 | } |
sahilmgandhi | 18:6a4db94011d3 | 215 | } |
sahilmgandhi | 18:6a4db94011d3 | 216 | } |
sahilmgandhi | 18:6a4db94011d3 | 217 | |
sahilmgandhi | 18:6a4db94011d3 | 218 | /* This might be funky |
sahilmgandhi | 18:6a4db94011d3 | 219 | while(btr > 63 && clkdiv < 16) { |
sahilmgandhi | 18:6a4db94011d3 | 220 | btr = btr / 2; |
sahilmgandhi | 18:6a4db94011d3 | 221 | clkdiv = clkdiv * 2; |
sahilmgandhi | 18:6a4db94011d3 | 222 | } |
sahilmgandhi | 18:6a4db94011d3 | 223 | */ |
sahilmgandhi | 18:6a4db94011d3 | 224 | clkdiv = clkdiv - 1; |
sahilmgandhi | 18:6a4db94011d3 | 225 | |
sahilmgandhi | 18:6a4db94011d3 | 226 | if (hit) { |
sahilmgandhi | 18:6a4db94011d3 | 227 | btr = BFN_PREP(timing_pts[bits][1], CANBT_TSEG2) |
sahilmgandhi | 18:6a4db94011d3 | 228 | | BFN_PREP(timing_pts[bits][0], CANBT_TSEG1) |
sahilmgandhi | 18:6a4db94011d3 | 229 | | BFN_PREP(psjw, CANBT_SJW) |
sahilmgandhi | 18:6a4db94011d3 | 230 | | BFN_PREP(brp, CANBT_BRP); |
sahilmgandhi | 18:6a4db94011d3 | 231 | btr = btr | (clkdiv << 16); |
sahilmgandhi | 18:6a4db94011d3 | 232 | |
sahilmgandhi | 18:6a4db94011d3 | 233 | } else { |
sahilmgandhi | 18:6a4db94011d3 | 234 | btr = 0; |
sahilmgandhi | 18:6a4db94011d3 | 235 | } |
sahilmgandhi | 18:6a4db94011d3 | 236 | |
sahilmgandhi | 18:6a4db94011d3 | 237 | return btr; |
sahilmgandhi | 18:6a4db94011d3 | 238 | } |
sahilmgandhi | 18:6a4db94011d3 | 239 | |
sahilmgandhi | 18:6a4db94011d3 | 240 | |
sahilmgandhi | 18:6a4db94011d3 | 241 | int can_config_rxmsgobj(can_t *obj) { |
sahilmgandhi | 18:6a4db94011d3 | 242 | uint16_t i = 0; |
sahilmgandhi | 18:6a4db94011d3 | 243 | |
sahilmgandhi | 18:6a4db94011d3 | 244 | // Make sure the interface is available |
sahilmgandhi | 18:6a4db94011d3 | 245 | //while( LPC_CAN->IF1_CMDREQ & CANIFn_CMDREQ_BUSY ); |
sahilmgandhi | 18:6a4db94011d3 | 246 | |
sahilmgandhi | 18:6a4db94011d3 | 247 | // Mark message valid, Direction = RX, Don't care about anything else |
sahilmgandhi | 18:6a4db94011d3 | 248 | LPC_CAN->IF1_ARB1 = 0; |
sahilmgandhi | 18:6a4db94011d3 | 249 | LPC_CAN->IF1_ARB2 = 0; |
sahilmgandhi | 18:6a4db94011d3 | 250 | LPC_CAN->IF1_MCTRL = 0; |
sahilmgandhi | 18:6a4db94011d3 | 251 | |
sahilmgandhi | 18:6a4db94011d3 | 252 | for ( i = 0; i < MSG_OBJ_MAX; i++ ) |
sahilmgandhi | 18:6a4db94011d3 | 253 | { |
sahilmgandhi | 18:6a4db94011d3 | 254 | // Transfer arb and control fields to message object |
sahilmgandhi | 18:6a4db94011d3 | 255 | LPC_CAN->IF1_CMDMSK = CANIFn_CMDMSK_WR | CANIFn_CMDMSK_ARB | CANIFn_CMDMSK_CTRL | CANIFn_CMDMSK_TXRQST; |
sahilmgandhi | 18:6a4db94011d3 | 256 | |
sahilmgandhi | 18:6a4db94011d3 | 257 | // Start Transfer to given message number |
sahilmgandhi | 18:6a4db94011d3 | 258 | LPC_CAN->IF1_CMDREQ = BFN_PREP(i, CANIFn_CMDREQ_MN); |
sahilmgandhi | 18:6a4db94011d3 | 259 | |
sahilmgandhi | 18:6a4db94011d3 | 260 | // Wait until transfer to message ram complete - TODO: maybe not block?? |
sahilmgandhi | 18:6a4db94011d3 | 261 | while( LPC_CAN->IF1_CMDREQ & CANIFn_CMDREQ_BUSY ); |
sahilmgandhi | 18:6a4db94011d3 | 262 | } |
sahilmgandhi | 18:6a4db94011d3 | 263 | |
sahilmgandhi | 18:6a4db94011d3 | 264 | // Accept all messages |
sahilmgandhi | 18:6a4db94011d3 | 265 | can_filter(obj, 0, 0, CANStandard, 1); |
sahilmgandhi | 18:6a4db94011d3 | 266 | |
sahilmgandhi | 18:6a4db94011d3 | 267 | return 1; |
sahilmgandhi | 18:6a4db94011d3 | 268 | } |
sahilmgandhi | 18:6a4db94011d3 | 269 | |
sahilmgandhi | 18:6a4db94011d3 | 270 | |
sahilmgandhi | 18:6a4db94011d3 | 271 | void can_init(can_t *obj, PinName rd, PinName td) { |
sahilmgandhi | 18:6a4db94011d3 | 272 | // Enable power and clock |
sahilmgandhi | 18:6a4db94011d3 | 273 | LPC_SYSCON->PRESETCTRL |= PRESETCTRL_CAN_RST_N; |
sahilmgandhi | 18:6a4db94011d3 | 274 | LPC_SYSCON->SYSAHBCLKCTRL |= SYSAHBCLKCTRL_CAN; |
sahilmgandhi | 18:6a4db94011d3 | 275 | |
sahilmgandhi | 18:6a4db94011d3 | 276 | // Enable Initialization mode |
sahilmgandhi | 18:6a4db94011d3 | 277 | if (!(LPC_CAN->CNTL & CANCNTL_INIT)) { |
sahilmgandhi | 18:6a4db94011d3 | 278 | LPC_CAN->CNTL |= CANCNTL_INIT; |
sahilmgandhi | 18:6a4db94011d3 | 279 | } |
sahilmgandhi | 18:6a4db94011d3 | 280 | |
sahilmgandhi | 18:6a4db94011d3 | 281 | can_frequency(obj, 125000); |
sahilmgandhi | 18:6a4db94011d3 | 282 | |
sahilmgandhi | 18:6a4db94011d3 | 283 | // Resume operation |
sahilmgandhi | 18:6a4db94011d3 | 284 | LPC_CAN->CNTL &= ~CANCNTL_INIT; |
sahilmgandhi | 18:6a4db94011d3 | 285 | while ( LPC_CAN->CNTL & CANCNTL_INIT ); |
sahilmgandhi | 18:6a4db94011d3 | 286 | |
sahilmgandhi | 18:6a4db94011d3 | 287 | // Initialize RX message object |
sahilmgandhi | 18:6a4db94011d3 | 288 | can_config_rxmsgobj(obj); |
sahilmgandhi | 18:6a4db94011d3 | 289 | } |
sahilmgandhi | 18:6a4db94011d3 | 290 | |
sahilmgandhi | 18:6a4db94011d3 | 291 | void can_free(can_t *obj) { |
sahilmgandhi | 18:6a4db94011d3 | 292 | LPC_SYSCON->SYSAHBCLKCTRL &= ~(SYSAHBCLKCTRL_CAN); |
sahilmgandhi | 18:6a4db94011d3 | 293 | LPC_SYSCON->PRESETCTRL &= ~(PRESETCTRL_CAN_RST_N); |
sahilmgandhi | 18:6a4db94011d3 | 294 | } |
sahilmgandhi | 18:6a4db94011d3 | 295 | |
sahilmgandhi | 18:6a4db94011d3 | 296 | int can_frequency(can_t *obj, int f) { |
sahilmgandhi | 18:6a4db94011d3 | 297 | int btr = can_speed(SystemCoreClock, (unsigned int)f, 1); |
sahilmgandhi | 18:6a4db94011d3 | 298 | int clkdiv = (btr >> 16) & 0x0F; |
sahilmgandhi | 18:6a4db94011d3 | 299 | btr = btr & 0xFFFF; |
sahilmgandhi | 18:6a4db94011d3 | 300 | |
sahilmgandhi | 18:6a4db94011d3 | 301 | if (btr > 0) { |
sahilmgandhi | 18:6a4db94011d3 | 302 | uint32_t cntl_init = LPC_CAN->CNTL | CANCNTL_INIT; |
sahilmgandhi | 18:6a4db94011d3 | 303 | // Set the bit clock |
sahilmgandhi | 18:6a4db94011d3 | 304 | LPC_CAN->CNTL |= CANCNTL_CCE | CANCNTL_INIT; |
sahilmgandhi | 18:6a4db94011d3 | 305 | LPC_CAN->CLKDIV = clkdiv; |
sahilmgandhi | 18:6a4db94011d3 | 306 | LPC_CAN->BT = btr; |
sahilmgandhi | 18:6a4db94011d3 | 307 | LPC_CAN->BRPE = 0x0000; |
sahilmgandhi | 18:6a4db94011d3 | 308 | LPC_CAN->CNTL &= ~(CANCNTL_CCE | CANCNTL_INIT); |
sahilmgandhi | 18:6a4db94011d3 | 309 | LPC_CAN->CNTL |= cntl_init; |
sahilmgandhi | 18:6a4db94011d3 | 310 | return 1; |
sahilmgandhi | 18:6a4db94011d3 | 311 | } |
sahilmgandhi | 18:6a4db94011d3 | 312 | return 0; |
sahilmgandhi | 18:6a4db94011d3 | 313 | } |
sahilmgandhi | 18:6a4db94011d3 | 314 | |
sahilmgandhi | 18:6a4db94011d3 | 315 | int can_write(can_t *obj, CAN_Message msg, int cc) { |
sahilmgandhi | 18:6a4db94011d3 | 316 | uint16_t msgnum = 0; |
sahilmgandhi | 18:6a4db94011d3 | 317 | |
sahilmgandhi | 18:6a4db94011d3 | 318 | // Make sure controller is enabled |
sahilmgandhi | 18:6a4db94011d3 | 319 | can_enable(obj); |
sahilmgandhi | 18:6a4db94011d3 | 320 | |
sahilmgandhi | 18:6a4db94011d3 | 321 | // Make sure the interface is available |
sahilmgandhi | 18:6a4db94011d3 | 322 | while( LPC_CAN->IF1_CMDREQ & CANIFn_CMDREQ_BUSY ); |
sahilmgandhi | 18:6a4db94011d3 | 323 | |
sahilmgandhi | 18:6a4db94011d3 | 324 | // Set the direction bit based on the message type |
sahilmgandhi | 18:6a4db94011d3 | 325 | uint32_t direction = 0; |
sahilmgandhi | 18:6a4db94011d3 | 326 | if (msg.type == CANData) { |
sahilmgandhi | 18:6a4db94011d3 | 327 | direction = CANIFn_ARB2_DIR; |
sahilmgandhi | 18:6a4db94011d3 | 328 | } |
sahilmgandhi | 18:6a4db94011d3 | 329 | |
sahilmgandhi | 18:6a4db94011d3 | 330 | if(msg.format == CANExtended) { |
sahilmgandhi | 18:6a4db94011d3 | 331 | // Mark message valid, Extended Frame, Set Identifier and mask everything |
sahilmgandhi | 18:6a4db94011d3 | 332 | LPC_CAN->IF1_ARB1 = BFN_PREP(msg.id, CANIFn_ARB1_ID); |
sahilmgandhi | 18:6a4db94011d3 | 333 | LPC_CAN->IF1_ARB2 = CANIFn_ARB2_MSGVAL | CANIFn_ARB2_XTD | direction | BFN_PREP(msg.id >> 16, CANIFn_ARB2_ID); |
sahilmgandhi | 18:6a4db94011d3 | 334 | LPC_CAN->IF1_MSK1 = BFN_PREP(ID_EXT_MASK, CANIFn_MSK1_MSK); |
sahilmgandhi | 18:6a4db94011d3 | 335 | LPC_CAN->IF1_MSK2 = CANIFn_MSK2_MXTD | CANIFn_MSK2_MDIR | BFN_PREP(ID_EXT_MASK >> 16, CANIFn_MSK2_MSK); |
sahilmgandhi | 18:6a4db94011d3 | 336 | } |
sahilmgandhi | 18:6a4db94011d3 | 337 | else { |
sahilmgandhi | 18:6a4db94011d3 | 338 | // Mark message valid, Set Identifier and mask everything |
sahilmgandhi | 18:6a4db94011d3 | 339 | LPC_CAN->IF1_ARB2 = CANIFn_ARB2_MSGVAL | direction | BFN_PREP(msg.id << 2, CANIFn_ARB2_ID); |
sahilmgandhi | 18:6a4db94011d3 | 340 | LPC_CAN->IF1_MSK2 = CANIFn_MSK2_MDIR | BFN_PREP(ID_STD_MASK << 2, CANIFn_MSK2_MSK); |
sahilmgandhi | 18:6a4db94011d3 | 341 | } |
sahilmgandhi | 18:6a4db94011d3 | 342 | |
sahilmgandhi | 18:6a4db94011d3 | 343 | // Use mask, request transmission, single message object and set DLC |
sahilmgandhi | 18:6a4db94011d3 | 344 | LPC_CAN->IF1_MCTRL = CANIFn_MCTRL_UMASK | CANIFn_MCTRL_TXRQST | CANIFn_MCTRL_EOB | BFN_PREP(msg.len, CANIFn_MCTRL_DLC); |
sahilmgandhi | 18:6a4db94011d3 | 345 | |
sahilmgandhi | 18:6a4db94011d3 | 346 | LPC_CAN->IF1_DA1 = BFN_PREP(msg.data[1], CANIFn_DA1_DATA1) | BFN_PREP(msg.data[0], CANIFn_DA1_DATA0); |
sahilmgandhi | 18:6a4db94011d3 | 347 | LPC_CAN->IF1_DA2 = BFN_PREP(msg.data[3], CANIFn_DA2_DATA3) | BFN_PREP(msg.data[2], CANIFn_DA2_DATA2); |
sahilmgandhi | 18:6a4db94011d3 | 348 | LPC_CAN->IF1_DB1 = BFN_PREP(msg.data[5], CANIFn_DB1_DATA5) | BFN_PREP(msg.data[4], CANIFn_DB1_DATA4); |
sahilmgandhi | 18:6a4db94011d3 | 349 | LPC_CAN->IF1_DB2 = BFN_PREP(msg.data[7], CANIFn_DB2_DATA7) | BFN_PREP(msg.data[6], CANIFn_DB2_DATA6); |
sahilmgandhi | 18:6a4db94011d3 | 350 | |
sahilmgandhi | 18:6a4db94011d3 | 351 | // Transfer all fields to message object |
sahilmgandhi | 18:6a4db94011d3 | 352 | LPC_CAN->IF1_CMDMSK = CANIFn_CMDMSK_WR | CANIFn_CMDMSK_MASK | CANIFn_CMDMSK_ARB | CANIFn_CMDMSK_CTRL | CANIFn_CMDMSK_TXRQST | CANIFn_CMDMSK_DATA_A | CANIFn_CMDMSK_DATA_B; |
sahilmgandhi | 18:6a4db94011d3 | 353 | |
sahilmgandhi | 18:6a4db94011d3 | 354 | // Start Transfer to given message number |
sahilmgandhi | 18:6a4db94011d3 | 355 | LPC_CAN->IF1_CMDREQ = BFN_PREP(msgnum, CANIFn_CMDREQ_MN); |
sahilmgandhi | 18:6a4db94011d3 | 356 | |
sahilmgandhi | 18:6a4db94011d3 | 357 | // Wait until transfer to message ram complete - TODO: maybe not block?? |
sahilmgandhi | 18:6a4db94011d3 | 358 | while( LPC_CAN->IF1_CMDREQ & CANIFn_CMDREQ_BUSY); |
sahilmgandhi | 18:6a4db94011d3 | 359 | |
sahilmgandhi | 18:6a4db94011d3 | 360 | // Wait until TXOK is set, then clear it - TODO: maybe not block |
sahilmgandhi | 18:6a4db94011d3 | 361 | //while( !(LPC_CAN->STAT & CANSTAT_TXOK) ); |
sahilmgandhi | 18:6a4db94011d3 | 362 | LPC_CAN->STAT &= ~(CANSTAT_TXOK); |
sahilmgandhi | 18:6a4db94011d3 | 363 | |
sahilmgandhi | 18:6a4db94011d3 | 364 | return 1; |
sahilmgandhi | 18:6a4db94011d3 | 365 | } |
sahilmgandhi | 18:6a4db94011d3 | 366 | |
sahilmgandhi | 18:6a4db94011d3 | 367 | int can_read(can_t *obj, CAN_Message *msg, int handle) { |
sahilmgandhi | 18:6a4db94011d3 | 368 | uint16_t i; |
sahilmgandhi | 18:6a4db94011d3 | 369 | |
sahilmgandhi | 18:6a4db94011d3 | 370 | // Make sure controller is enabled |
sahilmgandhi | 18:6a4db94011d3 | 371 | can_enable(obj); |
sahilmgandhi | 18:6a4db94011d3 | 372 | |
sahilmgandhi | 18:6a4db94011d3 | 373 | // Find first message object with new data |
sahilmgandhi | 18:6a4db94011d3 | 374 | if(handle == 0) { |
sahilmgandhi | 18:6a4db94011d3 | 375 | uint32_t newdata = LPC_CAN->ND1 | (LPC_CAN->ND2 << 16); |
sahilmgandhi | 18:6a4db94011d3 | 376 | // Find first free messagebox |
sahilmgandhi | 18:6a4db94011d3 | 377 | for(i = 0; i < 32; i++) { |
sahilmgandhi | 18:6a4db94011d3 | 378 | if(newdata & (1 << i)) { |
sahilmgandhi | 18:6a4db94011d3 | 379 | handle = i+1; |
sahilmgandhi | 18:6a4db94011d3 | 380 | break; |
sahilmgandhi | 18:6a4db94011d3 | 381 | } |
sahilmgandhi | 18:6a4db94011d3 | 382 | } |
sahilmgandhi | 18:6a4db94011d3 | 383 | } |
sahilmgandhi | 18:6a4db94011d3 | 384 | |
sahilmgandhi | 18:6a4db94011d3 | 385 | if(handle > 0 && handle < 32) { |
sahilmgandhi | 18:6a4db94011d3 | 386 | // Wait until message interface is free |
sahilmgandhi | 18:6a4db94011d3 | 387 | while( LPC_CAN->IF2_CMDREQ & CANIFn_CMDREQ_BUSY ); |
sahilmgandhi | 18:6a4db94011d3 | 388 | |
sahilmgandhi | 18:6a4db94011d3 | 389 | // Transfer all fields to message object |
sahilmgandhi | 18:6a4db94011d3 | 390 | LPC_CAN->IF2_CMDMSK = CANIFn_CMDMSK_RD | CANIFn_CMDMSK_MASK | CANIFn_CMDMSK_ARB | CANIFn_CMDMSK_CTRL | CANIFn_CMDMSK_CLRINTPND | CANIFn_CMDMSK_TXRQST | CANIFn_CMDMSK_DATA_A | CANIFn_CMDMSK_DATA_B; |
sahilmgandhi | 18:6a4db94011d3 | 391 | |
sahilmgandhi | 18:6a4db94011d3 | 392 | // Start Transfer from given message number |
sahilmgandhi | 18:6a4db94011d3 | 393 | LPC_CAN->IF2_CMDREQ = BFN_PREP(handle, CANIFn_CMDREQ_MN); |
sahilmgandhi | 18:6a4db94011d3 | 394 | |
sahilmgandhi | 18:6a4db94011d3 | 395 | // Wait until transfer to message ram complete |
sahilmgandhi | 18:6a4db94011d3 | 396 | while( LPC_CAN->IF2_CMDREQ & CANIFn_CMDREQ_BUSY ); |
sahilmgandhi | 18:6a4db94011d3 | 397 | |
sahilmgandhi | 18:6a4db94011d3 | 398 | if (LPC_CAN->IF2_ARB2 & CANIFn_ARB2_XTD) { |
sahilmgandhi | 18:6a4db94011d3 | 399 | msg->format = CANExtended; |
sahilmgandhi | 18:6a4db94011d3 | 400 | msg->id = (LPC_CAN->IF2_ARB1 & CANIFn_ARB2_ID_MASK) << 16; |
sahilmgandhi | 18:6a4db94011d3 | 401 | msg->id |= (LPC_CAN->IF2_ARB2 & CANIFn_ARB2_ID_MASK); |
sahilmgandhi | 18:6a4db94011d3 | 402 | } |
sahilmgandhi | 18:6a4db94011d3 | 403 | else { |
sahilmgandhi | 18:6a4db94011d3 | 404 | msg->format = CANStandard; |
sahilmgandhi | 18:6a4db94011d3 | 405 | msg->id = (LPC_CAN->IF2_ARB2 & CANIFn_ARB2_ID_MASK) >> 2; |
sahilmgandhi | 18:6a4db94011d3 | 406 | } |
sahilmgandhi | 18:6a4db94011d3 | 407 | |
sahilmgandhi | 18:6a4db94011d3 | 408 | if (LPC_CAN->IF2_ARB2 & CANIFn_ARB2_DIR) { |
sahilmgandhi | 18:6a4db94011d3 | 409 | msg->type = CANRemote; |
sahilmgandhi | 18:6a4db94011d3 | 410 | } |
sahilmgandhi | 18:6a4db94011d3 | 411 | else { |
sahilmgandhi | 18:6a4db94011d3 | 412 | msg->type = CANData; |
sahilmgandhi | 18:6a4db94011d3 | 413 | } |
sahilmgandhi | 18:6a4db94011d3 | 414 | |
sahilmgandhi | 18:6a4db94011d3 | 415 | msg->len = BFN_GET(LPC_CAN->IF2_MCTRL, CANIFn_MCTRL_DLC); // TODO: If > 8, len = 8 |
sahilmgandhi | 18:6a4db94011d3 | 416 | msg->data[0] = BFN_GET(LPC_CAN->IF2_DA1, CANIFn_DA1_DATA0); |
sahilmgandhi | 18:6a4db94011d3 | 417 | msg->data[1] = BFN_GET(LPC_CAN->IF2_DA1, CANIFn_DA1_DATA1); |
sahilmgandhi | 18:6a4db94011d3 | 418 | msg->data[2] = BFN_GET(LPC_CAN->IF2_DA2, CANIFn_DA2_DATA2); |
sahilmgandhi | 18:6a4db94011d3 | 419 | msg->data[3] = BFN_GET(LPC_CAN->IF2_DA2, CANIFn_DA2_DATA3); |
sahilmgandhi | 18:6a4db94011d3 | 420 | msg->data[4] = BFN_GET(LPC_CAN->IF2_DB1, CANIFn_DB1_DATA4); |
sahilmgandhi | 18:6a4db94011d3 | 421 | msg->data[5] = BFN_GET(LPC_CAN->IF2_DB1, CANIFn_DB1_DATA5); |
sahilmgandhi | 18:6a4db94011d3 | 422 | msg->data[6] = BFN_GET(LPC_CAN->IF2_DB2, CANIFn_DB2_DATA6); |
sahilmgandhi | 18:6a4db94011d3 | 423 | msg->data[7] = BFN_GET(LPC_CAN->IF2_DB2, CANIFn_DB2_DATA7); |
sahilmgandhi | 18:6a4db94011d3 | 424 | |
sahilmgandhi | 18:6a4db94011d3 | 425 | LPC_CAN->STAT &= ~(CANSTAT_RXOK); |
sahilmgandhi | 18:6a4db94011d3 | 426 | return 1; |
sahilmgandhi | 18:6a4db94011d3 | 427 | } |
sahilmgandhi | 18:6a4db94011d3 | 428 | |
sahilmgandhi | 18:6a4db94011d3 | 429 | return 0; |
sahilmgandhi | 18:6a4db94011d3 | 430 | } |
sahilmgandhi | 18:6a4db94011d3 | 431 | |
sahilmgandhi | 18:6a4db94011d3 | 432 | void can_reset(can_t *obj) { |
sahilmgandhi | 18:6a4db94011d3 | 433 | LPC_SYSCON->PRESETCTRL &= ~PRESETCTRL_CAN_RST_N; |
sahilmgandhi | 18:6a4db94011d3 | 434 | LPC_CAN->STAT = 0; |
sahilmgandhi | 18:6a4db94011d3 | 435 | |
sahilmgandhi | 18:6a4db94011d3 | 436 | can_config_rxmsgobj(obj); |
sahilmgandhi | 18:6a4db94011d3 | 437 | } |
sahilmgandhi | 18:6a4db94011d3 | 438 | |
sahilmgandhi | 18:6a4db94011d3 | 439 | unsigned char can_rderror(can_t *obj) { |
sahilmgandhi | 18:6a4db94011d3 | 440 | return BFN_GET(LPC_CAN->EC, CANEC_REC); |
sahilmgandhi | 18:6a4db94011d3 | 441 | } |
sahilmgandhi | 18:6a4db94011d3 | 442 | |
sahilmgandhi | 18:6a4db94011d3 | 443 | unsigned char can_tderror(can_t *obj) { |
sahilmgandhi | 18:6a4db94011d3 | 444 | return BFN_GET(LPC_CAN->EC, CANEC_TEC); |
sahilmgandhi | 18:6a4db94011d3 | 445 | } |
sahilmgandhi | 18:6a4db94011d3 | 446 | |
sahilmgandhi | 18:6a4db94011d3 | 447 | void can_monitor(can_t *obj, int silent) { |
sahilmgandhi | 18:6a4db94011d3 | 448 | if (silent) { |
sahilmgandhi | 18:6a4db94011d3 | 449 | LPC_CAN->CNTL |= CANCNTL_TEST; |
sahilmgandhi | 18:6a4db94011d3 | 450 | LPC_CAN->TEST |= CANTEST_SILENT; |
sahilmgandhi | 18:6a4db94011d3 | 451 | } else { |
sahilmgandhi | 18:6a4db94011d3 | 452 | LPC_CAN->CNTL &= ~(CANCNTL_TEST); |
sahilmgandhi | 18:6a4db94011d3 | 453 | LPC_CAN->TEST &= ~CANTEST_SILENT; |
sahilmgandhi | 18:6a4db94011d3 | 454 | } |
sahilmgandhi | 18:6a4db94011d3 | 455 | |
sahilmgandhi | 18:6a4db94011d3 | 456 | if (!(LPC_CAN->CNTL & CANCNTL_INIT)) { |
sahilmgandhi | 18:6a4db94011d3 | 457 | LPC_CAN->CNTL |= CANCNTL_INIT; |
sahilmgandhi | 18:6a4db94011d3 | 458 | } |
sahilmgandhi | 18:6a4db94011d3 | 459 | } |