qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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), &regaddr, 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 }