|
| 1 | +/* mbed Microcontroller Library |
| 2 | + * Copyright (c) 2017 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 "i2c_api.h" |
| 17 | +#include "cmsis.h" |
| 18 | +#include "mbed_error.h" |
| 19 | +#include "mbed_wait_api.h" |
| 20 | +#include "SMM_MPS2.h" |
| 21 | +#include "pinmap.h" |
| 22 | + |
| 23 | +/* |
| 24 | + * The ARM I2C SBCon IP (Two-wire serial bus interface) requires a |
| 25 | + * bit banging programing model. |
| 26 | + */ |
| 27 | + |
| 28 | +#if DEVICE_I2CSLAVE |
| 29 | +#error "I2C slave is not supported in MPS2 CM3DS board" |
| 30 | +#endif |
| 31 | + |
| 32 | +#define DEFAULT_I2C_SBCON_HZ 4000000U /* 4MHz */ |
| 33 | +#define STOP_REQUIRED 1 |
| 34 | +#define LAST_ACK_REQUIRED 1 |
| 35 | +#define LAST_ACK_NOT_REQUIRED 0 |
| 36 | +#define I2C_ACK_BIT 0x1 |
| 37 | + |
| 38 | +#define SDA (1 << 1) |
| 39 | +#define SCL (1 << 0) |
| 40 | +#define I2C_HIGH(p_i2c, pin) (p_i2c->CONTROLS = pin) |
| 41 | +#define I2C_LOW(p_i2c, pin) (p_i2c->CONTROLC = pin) |
| 42 | +#define I2C_GET(p_i2c, pin) ((p_i2c->CONTROL >> (pin-1)) & 0x01) |
| 43 | + |
| 44 | +static const PinMap PinMap_I2C_SDA[] = { |
| 45 | + {TSC_SDA , I2C_0, 0}, |
| 46 | + {AUD_SDA , I2C_1, 0}, |
| 47 | + {SHIELD_0_SDA , I2C_2, ALTERNATE_FUNC}, |
| 48 | + {SHIELD_1_SDA , I2C_3, ALTERNATE_FUNC}, |
| 49 | + {NC , NC , 0} |
| 50 | +}; |
| 51 | + |
| 52 | +static const PinMap PinMap_I2C_SCL[] = { |
| 53 | + {TSC_SCL , I2C_0, 0}, |
| 54 | + {AUD_SCL , I2C_1, 0}, |
| 55 | + {SHIELD_0_SCL , I2C_2, ALTERNATE_FUNC}, |
| 56 | + {SHIELD_1_SCL , I2C_3, ALTERNATE_FUNC}, |
| 57 | + {NC , NC, 0} |
| 58 | +}; |
| 59 | + |
| 60 | +/* |
| 61 | + * \brief Transmits a data bit. |
| 62 | + * |
| 63 | + * \param[in] obj I2C object |
| 64 | + * \param[in] bit Bit to transmit |
| 65 | + */ |
| 66 | +static void i2c_tx_bit(i2c_t *obj, uint8_t bit) |
| 67 | +{ |
| 68 | + if (bit != 0) { |
| 69 | + I2C_HIGH(obj->i2c, SDA); |
| 70 | + } else { |
| 71 | + I2C_LOW(obj->i2c, SDA); |
| 72 | + } |
| 73 | + |
| 74 | + wait_us(obj->freq_us); |
| 75 | + |
| 76 | + I2C_HIGH(obj->i2c, SCL); |
| 77 | + wait_us(obj->freq_us); |
| 78 | + |
| 79 | + I2C_LOW(obj->i2c, SCL); |
| 80 | + wait_us(obj->freq_us); |
| 81 | +} |
| 82 | + |
| 83 | +/* |
| 84 | + * \brief Reads a data bit |
| 85 | + * |
| 86 | + * \param[in] obj I2C object |
| 87 | + * |
| 88 | + * \returns Bit value received. |
| 89 | + */ |
| 90 | +static uint8_t i2c_rx_bit(i2c_t *obj) |
| 91 | +{ |
| 92 | + uint8_t bit; |
| 93 | + |
| 94 | + I2C_HIGH(obj->i2c, SDA); |
| 95 | + wait_us(obj->freq_us); |
| 96 | + |
| 97 | + I2C_HIGH(obj->i2c, SCL); |
| 98 | + wait_us(obj->freq_us); |
| 99 | + |
| 100 | + bit = I2C_GET(obj->i2c, SDA); |
| 101 | + |
| 102 | + I2C_LOW(obj->i2c, SCL); |
| 103 | + wait_us(obj->freq_us); |
| 104 | + |
| 105 | + return bit; |
| 106 | +} |
| 107 | + |
| 108 | +void i2c_init(i2c_t *obj, PinName sda, PinName scl) |
| 109 | +{ |
| 110 | + I2CName i2c_sda = (I2CName)pinmap_peripheral(sda, PinMap_I2C_SDA); |
| 111 | + I2CName i2c_scl = (I2CName)pinmap_peripheral(scl, PinMap_I2C_SCL); |
| 112 | + obj->i2c = (MPS2_I2C_TypeDef *)pinmap_merge(i2c_sda, i2c_scl); |
| 113 | + |
| 114 | + if ((int)obj->i2c == NC) { |
| 115 | + error("I2C pin mapping failed"); |
| 116 | + } |
| 117 | + |
| 118 | + switch ((int)obj->i2c) { |
| 119 | + case I2C_2: |
| 120 | + case I2C_3: |
| 121 | + /* Enables alt-function to use I2C SCL and SDA signals */ |
| 122 | + pin_function(sda, ALTERNATE_FUNC); |
| 123 | + pin_function(scl, ALTERNATE_FUNC); |
| 124 | + break; |
| 125 | + default: |
| 126 | + break; |
| 127 | + } |
| 128 | + |
| 129 | + /* Sets default I2C frequency in microseconds */ |
| 130 | + obj->freq_us = (SystemCoreClock / DEFAULT_I2C_SBCON_HZ); |
| 131 | +} |
| 132 | + |
| 133 | +void i2c_frequency(i2c_t *obj, int hz) |
| 134 | +{ |
| 135 | + if ((hz <= 0) || ((unsigned int)hz > SystemCoreClock)) { |
| 136 | + error("Couldn't setup requested I2C frequency %dHz", hz); |
| 137 | + } |
| 138 | + |
| 139 | + obj->freq_us = (SystemCoreClock / hz); |
| 140 | +} |
| 141 | + |
| 142 | +int i2c_start(i2c_t *obj) |
| 143 | +{ |
| 144 | + I2C_HIGH(obj->i2c, (SCL | SDA)); |
| 145 | + wait_us(obj->freq_us); |
| 146 | + |
| 147 | + I2C_LOW(obj->i2c, SDA); |
| 148 | + wait_us(obj->freq_us); |
| 149 | + |
| 150 | + I2C_LOW(obj->i2c, SCL); |
| 151 | + wait_us(obj->freq_us); |
| 152 | + |
| 153 | + return 0; |
| 154 | +} |
| 155 | + |
| 156 | +int i2c_stop(i2c_t *obj) |
| 157 | +{ |
| 158 | + I2C_LOW(obj->i2c, SDA); |
| 159 | + wait_us(obj->freq_us); |
| 160 | + |
| 161 | + I2C_HIGH(obj->i2c, SCL); |
| 162 | + wait_us(obj->freq_us); |
| 163 | + |
| 164 | + I2C_HIGH(obj->i2c, SDA); |
| 165 | + wait_us(obj->freq_us); |
| 166 | + |
| 167 | + return 0; |
| 168 | +} |
| 169 | + |
| 170 | +int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) |
| 171 | +{ |
| 172 | + int32_t nbr_bytes_read = 0; |
| 173 | + |
| 174 | + if (data == 0 || length == 0) { |
| 175 | + error("Invalid i2c_read input data"); |
| 176 | + } |
| 177 | + |
| 178 | + i2c_start(obj); |
| 179 | + |
| 180 | + i2c_byte_write(obj, (uint8_t)(address | 0x1)); |
| 181 | + |
| 182 | + while(nbr_bytes_read < (length - 1)) { |
| 183 | + data[nbr_bytes_read++] = i2c_byte_read(obj, LAST_ACK_NOT_REQUIRED); |
| 184 | + } |
| 185 | + |
| 186 | + data[nbr_bytes_read++] = i2c_byte_read(obj, LAST_ACK_REQUIRED); |
| 187 | + |
| 188 | + if (stop == STOP_REQUIRED) { |
| 189 | + i2c_stop(obj); |
| 190 | + } |
| 191 | + |
| 192 | + return nbr_bytes_read; |
| 193 | +} |
| 194 | + |
| 195 | +int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) |
| 196 | +{ |
| 197 | + int32_t nbr_bytes_write; |
| 198 | + |
| 199 | + if (data == 0) { |
| 200 | + error("Invalid i2c_write input data"); |
| 201 | + } |
| 202 | + |
| 203 | + i2c_start(obj); |
| 204 | + |
| 205 | + i2c_byte_write(obj, (uint8_t)address); |
| 206 | + |
| 207 | + for (nbr_bytes_write = 0; nbr_bytes_write < length; nbr_bytes_write++) { |
| 208 | + i2c_byte_write(obj, data[nbr_bytes_write]); |
| 209 | + } |
| 210 | + |
| 211 | + if (stop == STOP_REQUIRED) { |
| 212 | + i2c_stop(obj); |
| 213 | + } |
| 214 | + |
| 215 | + return nbr_bytes_write; |
| 216 | +} |
| 217 | + |
| 218 | +void i2c_reset(i2c_t *obj) |
| 219 | +{ |
| 220 | + uint32_t iter; |
| 221 | + |
| 222 | + /* |
| 223 | + * The reset sequence is: |
| 224 | + * - SDA line low |
| 225 | + * - 9 clock pulses |
| 226 | + * - SDA line high |
| 227 | + */ |
| 228 | + I2C_LOW(obj->i2c, SDA); |
| 229 | + wait_us(obj->freq_us); |
| 230 | + |
| 231 | + for(iter=0; iter < 9; iter++) { |
| 232 | + I2C_LOW(obj->i2c, SCL); |
| 233 | + wait_us(obj->freq_us); |
| 234 | + I2C_HIGH(obj->i2c, SCL); |
| 235 | + wait_us(obj->freq_us); |
| 236 | + } |
| 237 | + |
| 238 | + I2C_HIGH(obj->i2c, SDA); |
| 239 | + wait_us(obj->freq_us); |
| 240 | +} |
| 241 | + |
| 242 | +int i2c_byte_read(i2c_t *obj, int last) |
| 243 | +{ |
| 244 | + uint8_t nbr_bits; |
| 245 | + uint8_t data = 0; |
| 246 | + |
| 247 | + data = i2c_rx_bit(obj); |
| 248 | + for (nbr_bits = 1; nbr_bits < 8; nbr_bits++) { |
| 249 | + data <<= 1; |
| 250 | + data |= i2c_rx_bit(obj); |
| 251 | + } |
| 252 | + |
| 253 | + i2c_tx_bit(obj, last); |
| 254 | + |
| 255 | + return data; |
| 256 | +} |
| 257 | + |
| 258 | +int i2c_byte_write(i2c_t *obj, int data) |
| 259 | +{ |
| 260 | + uint8_t nbr_bits; |
| 261 | + uint8_t ack; |
| 262 | + |
| 263 | + for(nbr_bits=0; nbr_bits < 7; nbr_bits++) { |
| 264 | + i2c_tx_bit(obj, data & 0x80); |
| 265 | + data <<= 1; |
| 266 | + } |
| 267 | + |
| 268 | + i2c_tx_bit(obj, data & 0x80); |
| 269 | + |
| 270 | + ack = i2c_rx_bit(obj); |
| 271 | + |
| 272 | + return ack; |
| 273 | +} |
0 commit comments