i2c_master.c (7983B)
1 /* Copyright 2018 Jack Humbert 2 * Copyright 2018 Yiancar 3 * Copyright 2023 customMK 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 /* This library is only valid for STM32 processors. 20 * This library follows the convention of the AVR i2c_master library. 21 * As a result addresses are expected to be already shifted (addr << 1). 22 * I2CD1 is the default driver which corresponds to pins B6 and B7. This 23 * can be changed. 24 * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that 25 * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. Pins B6 and B7 are used 26 * but using any other I2C pins should be trivial. 27 */ 28 29 #include "i2c_master.h" 30 #include "gpio.h" 31 #include "chibios_config.h" 32 #include <ch.h> 33 #include <hal.h> 34 35 #ifndef I2C_DRIVER 36 # define I2C_DRIVER I2CD1 37 #endif 38 39 #ifndef I2C1_SCL_PIN 40 # define I2C1_SCL_PIN B6 41 #endif 42 43 #ifndef I2C1_SCL_PAL_MODE 44 # ifdef USE_GPIOV1 45 # define I2C1_SCL_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN 46 # else 47 # define I2C1_SCL_PAL_MODE 4 48 # endif 49 #endif 50 51 #ifndef I2C1_SDA_PIN 52 # define I2C1_SDA_PIN B7 53 #endif 54 55 #ifndef I2C1_SDA_PAL_MODE 56 # ifdef USE_GPIOV1 57 # define I2C1_SDA_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN 58 # else 59 # define I2C1_SDA_PAL_MODE 4 60 # endif 61 #endif 62 63 #ifdef USE_I2CV1 64 # ifndef I2C1_OPMODE 65 # define I2C1_OPMODE OPMODE_I2C 66 # endif 67 # ifndef I2C1_CLOCK_SPEED 68 # define I2C1_CLOCK_SPEED 100000 /* 400000 */ 69 # endif 70 # ifndef I2C1_DUTY_CYCLE 71 # define I2C1_DUTY_CYCLE STD_DUTY_CYCLE /* FAST_DUTY_CYCLE_2 */ 72 # endif 73 #else 74 // The default timing values below configures the I2C clock to 400khz assuming a 72Mhz clock 75 // For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html 76 # ifndef I2C1_TIMINGR_PRESC 77 # define I2C1_TIMINGR_PRESC 0U 78 # endif 79 # ifndef I2C1_TIMINGR_SCLDEL 80 # define I2C1_TIMINGR_SCLDEL 7U 81 # endif 82 # ifndef I2C1_TIMINGR_SDADEL 83 # define I2C1_TIMINGR_SDADEL 0U 84 # endif 85 # ifndef I2C1_TIMINGR_SCLH 86 # define I2C1_TIMINGR_SCLH 38U 87 # endif 88 # ifndef I2C1_TIMINGR_SCLL 89 # define I2C1_TIMINGR_SCLL 129U 90 # endif 91 #endif 92 93 static const I2CConfig i2cconfig = { 94 #if defined(USE_I2CV1_CONTRIB) 95 I2C1_CLOCK_SPEED, 96 #elif defined(USE_I2CV1) 97 I2C1_OPMODE, 98 I2C1_CLOCK_SPEED, 99 I2C1_DUTY_CYCLE, 100 #elif defined(WB32F3G71xx) || defined(WB32FQ95xx) 101 I2C1_OPMODE, 102 I2C1_CLOCK_SPEED, 103 #else 104 // This configures the I2C clock to 400khz assuming a 72Mhz clock 105 // For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html 106 STM32_TIMINGR_PRESC(I2C1_TIMINGR_PRESC) | STM32_TIMINGR_SCLDEL(I2C1_TIMINGR_SCLDEL) | STM32_TIMINGR_SDADEL(I2C1_TIMINGR_SDADEL) | STM32_TIMINGR_SCLH(I2C1_TIMINGR_SCLH) | STM32_TIMINGR_SCLL(I2C1_TIMINGR_SCLL), 0, 0 107 #endif 108 }; 109 110 /** 111 * @brief Handles any I2C error condition by stopping the I2C peripheral and 112 * aborting any ongoing transactions. Furthermore ChibiOS status codes are 113 * converted into QMK codes. 114 * 115 * @param status ChibiOS specific I2C status code 116 * @return i2c_status_t QMK specific I2C status code 117 */ 118 static i2c_status_t i2c_epilogue(const msg_t status) { 119 if (status == MSG_OK) { 120 return I2C_STATUS_SUCCESS; 121 } 122 123 // From ChibiOS HAL: "After a timeout the driver must be stopped and 124 // restarted because the bus is in an uncertain state." We also issue that 125 // hard stop in case of any error. 126 i2cStop(&I2C_DRIVER); 127 128 return status == MSG_TIMEOUT ? I2C_STATUS_TIMEOUT : I2C_STATUS_ERROR; 129 } 130 131 __attribute__((weak)) void i2c_init(void) { 132 static bool is_initialised = false; 133 if (!is_initialised) { 134 is_initialised = true; 135 136 // Try releasing special pins for a short time 137 palSetLineMode(I2C1_SCL_PIN, PAL_MODE_INPUT); 138 palSetLineMode(I2C1_SDA_PIN, PAL_MODE_INPUT); 139 140 chThdSleepMilliseconds(10); 141 #if defined(USE_GPIOV1) 142 palSetLineMode(I2C1_SCL_PIN, I2C1_SCL_PAL_MODE); 143 palSetLineMode(I2C1_SDA_PIN, I2C1_SDA_PAL_MODE); 144 #else 145 palSetLineMode(I2C1_SCL_PIN, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN); 146 palSetLineMode(I2C1_SDA_PIN, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN); 147 #endif 148 } 149 } 150 151 i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) { 152 i2cStart(&I2C_DRIVER, &i2cconfig); 153 msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (address >> 1), data, length, 0, 0, TIME_MS2I(timeout)); 154 return i2c_epilogue(status); 155 } 156 157 i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) { 158 i2cStart(&I2C_DRIVER, &i2cconfig); 159 msg_t status = i2cMasterReceiveTimeout(&I2C_DRIVER, (address >> 1), data, length, TIME_MS2I(timeout)); 160 return i2c_epilogue(status); 161 } 162 163 i2c_status_t i2c_transmit_and_receive(uint8_t address, const uint8_t* tx_data, uint16_t tx_length, uint8_t* rx_data, uint16_t rx_length, uint16_t timeout) { 164 i2cStart(&I2C_DRIVER, &i2cconfig); 165 msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (address >> 1), tx_data, tx_length, rx_data, rx_length, TIME_MS2I(timeout)); 166 return i2c_epilogue(status); 167 } 168 169 i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { 170 i2cStart(&I2C_DRIVER, &i2cconfig); 171 172 uint8_t complete_packet[length + 1]; 173 for (uint16_t i = 0; i < length; i++) { 174 complete_packet[i + 1] = data[i]; 175 } 176 complete_packet[0] = regaddr; 177 178 msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (devaddr >> 1), complete_packet, length + 1, 0, 0, TIME_MS2I(timeout)); 179 return i2c_epilogue(status); 180 } 181 182 i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { 183 i2cStart(&I2C_DRIVER, &i2cconfig); 184 185 uint8_t complete_packet[length + 2]; 186 for (uint16_t i = 0; i < length; i++) { 187 complete_packet[i + 2] = data[i]; 188 } 189 complete_packet[0] = regaddr >> 8; 190 complete_packet[1] = regaddr & 0xFF; 191 192 msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (devaddr >> 1), complete_packet, length + 2, 0, 0, TIME_MS2I(timeout)); 193 return i2c_epilogue(status); 194 } 195 196 i2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { 197 i2cStart(&I2C_DRIVER, &i2cconfig); 198 msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (devaddr >> 1), ®addr, 1, data, length, TIME_MS2I(timeout)); 199 return i2c_epilogue(status); 200 } 201 202 i2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { 203 i2cStart(&I2C_DRIVER, &i2cconfig); 204 uint8_t register_packet[2] = {regaddr >> 8, regaddr & 0xFF}; 205 msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (devaddr >> 1), register_packet, 2, data, length, TIME_MS2I(timeout)); 206 return i2c_epilogue(status); 207 } 208 209 __attribute__((weak)) i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout) { 210 // ChibiOS does not provide low level enough control to check for an ack. 211 // Best effort instead tries reading register 0 which will either succeed or timeout. 212 // This approach may produce false negative results for I2C devices that do not respond to a register 0 read request. 213 uint8_t data = 0; 214 return i2c_read_register(address, 0, &data, sizeof(data), timeout); 215 }