// Sample program demonstrating the use of the Big Integer Library. // Standard libraries #include #include // `BigIntegerLibrary.hh' includes all of the library headers. #include "BigIntegerLibrary.hh" int main() { /* The library throws `const char *' error messages when things go * wrong. It's a good idea to catch them using a `try' block like this * one. Your C++ compiler might need a command-line option to compile * code that uses exceptions. */ try { BigInteger a; // a is 0 int b = 535; /* Any primitive integer can be converted implicitly to a * BigInteger. */ a = b; /* The reverse conversion requires a method call (implicit * conversions were previously supported but caused trouble). * If a were too big for an int, the library would throw an * exception. */ b = a.toInt(); BigInteger c(a); // Copy a BigInteger. // The int literal is converted to a BigInteger. BigInteger d(-314159265); /* This won't compile (at least on 32-bit machines) because the * number is too big to be a primitive integer literal, and * there's no such thing as a BigInteger literal. */ //BigInteger e(3141592653589793238462643383279); // Instead you can convert the number from a string. std::string s("3141592653589793238462643383279"); BigInteger f = stringToBigInteger(s); // You can convert the other way too. std::string s2 = bigIntegerToString(f); // f is implicitly stringified and sent to std::cout. std::cout << f << std::endl; /* Let's do some math! The library overloads most of the * mathematical operators (including assignment operators) to * work on BigIntegers. There are also ``copy-less'' * operations; see `BigUnsigned.hh' for details. */ // Arithmetic operators BigInteger g(314159), h(265); std::cout << (g + h) << '\n' << (g - h) << '\n' << (g * h) << '\n' << (g / h) << '\n' << (g % h) << std::endl; // Bitwise operators BigUnsigned i(0xFF0000FF), j(0x0000FFFF); // The library's << operator recognizes base flags. std::cout.flags(std::ios::hex | std::ios::showbase); std::cout << (i & j) << '\n' << (i | j) << '\n' << (i ^ j) << '\n' // Shift distances are ordinary unsigned ints. << (j << 21) << '\n' << (j >> 10) << '\n'; std::cout.flags(std::ios::dec); // Let's do some heavy lifting and calculate powers of 314. int maxPower = 10; BigUnsigned x(1), big314(314); for (int power = 0; power <= maxPower; power++) { std::cout << "314^" << power << " = " << x << std::endl; x *= big314; // A BigInteger assignment operator } // Some big-integer algorithms (albeit on small integers). std::cout << gcd(BigUnsigned(60), 72) << '\n' << modinv(BigUnsigned(7), 11) << '\n' << modexp(BigUnsigned(314), 159, 2653) << std::endl; // Add your own code here to experiment with the library. } catch(char const* err) { std::cout << "The library threw an exception:\n" << err << std::endl; } return 0; } /* The original sample program produces this output: 3141592653589793238462643383279 314424 313894 83252135 1185 134 0xFF 0xFF00FFFF 0xFF00FF00 0x1FFFE00000 0x3F 314^0 = 1 314^1 = 314 314^2 = 98596 314^3 = 30959144 314^4 = 9721171216 314^5 = 3052447761824 314^6 = 958468597212736 314^7 = 300959139524799104 314^8 = 94501169810786918656 314^9 = 29673367320587092457984 314^10 = 9317437338664347031806976 12 8 1931 */ 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
# I2C Master Driver

The I2C Master drivers used in QMK have a set of common functions to allow portability between MCUs.

## Available functions

|Function                                                                                                          |Description                                                                                                                                                                  |
|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|`void i2c_init(void);`                                                                                            |Initializes the I2C driver. This function should be called once before any transaction is initiated.                                                                         |
|`uint8_t i2c_start(uint8_t address);`                                                                             |Starts an I2C transaction. Address is the 7-bit slave address without the direction bit.                                                                                     |
|`uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);`                        |Transmit data over I2C. Address is the 7-bit slave address without the direction. Returns status of transaction.                                                             |
|`uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);`                         |Receive data over I2C. Address is the 7-bit slave address without the direction. Saves number of bytes specified by `length` in `data` array. Returns status of transaction. |
|`uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);`       |Same as the `i2c_transmit` function but `regaddr` sets where in the slave the data will be written.                                                                          |
|`uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);`        |Same as the `i2c_receive` function but `regaddr` sets from where in the slave the data will be read.                                                                         |
|`uint8_t i2c_stop(void);`                                                                                         |Ends an I2C transaction.                                                                                                                                                     |

### Function Return

All the above functions, except `void i2c_init(void);` return the following truth table:

|Return Value   |Description                                        |
|---------------|---------------------------------------------------|
|0              |Operation executed successfully.                   |
|-1             |Operation failed.                                  |
|-2             |Operation timed out.                               |


## AVR

### Configuration

The following defines can be used to configure the I2C master driver.

|Variable          |Description                                        |Default|
|------------------|---------------------------------------------------|-------|
|`F_SCL`           |Clock frequency in Hz                              |400KHz |
|`Prescaler`       |Divides master clock to aid in I2C clock selection |1      |

AVRs usually have set GPIO which turn into I2C pins, therefore no further configuration is required.

## ARM

For ARM the Chibios I2C HAL driver is under the hood.
This section assumes an STM32 MCU.

### Configuration

The configuration for ARM MCUs can be quite complex as often there are multiple I2C drivers which can be assigned to a variety of ports.

Firstly the `mcuconf.h` file must be setup to enable the necessary hardware drivers.

|Variable                      |Description                                                                        |Default|
|------------------------------|------------------------------------------------------------------------------------|-------|
|`#STM32_I2C_USE_XXX`          |Enable/Disable the hardware driver XXX (each driver should be explicitly listed)    |FALSE  |
|`#STM32_I2C_BUSY_TIMEOUT`     |Time in ms until the I2C command is aborted if no response is received              |50     |
|`#STM32_I2C_XXX_IRQ_PRIORITY` |Interrupt priority for hardware driver XXX (THIS IS AN EXPERT SETTING)              |10     |
|`#STM32_I2C_USE_DMA`          |Enable/Disable the ability of the MCU to offload the data transfer to the DMA unit  |TRUE   |
|`#STM32_I2C_XXX_DMA_PRIORITY` |Priority of DMA unit for hardware driver XXX (THIS IS AN EXPERT SETTING)            |1      |

Secondly, in the `halconf.h` file, `#define HAL_USE_I2C` must be set to `TRUE`. This allows ChibiOS to load its I2C driver.

Lastly, we need to assign the correct GPIO pins depending on the I2C hardware driver we want to use.

By default the I2C1 hardware driver is assumed to be used. If another hardware driver is used,  `#define I2C_DRIVER I2CDX` should be added to the `config.h` file with X being the number of hardware driver used. For example is I2C3 is enabled, the `config.h` file should contain `#define I2C_DRIVER I2CD3`. This aligns the QMK I2C driver with the Chibios I2C driver.

STM32 MCUs allows a variety of pins to be configured as I2C pins depending on the hardware driver used. By default B6 and B7 are set to I2C. You can use these defines to set your i2c pins:

| Variable                 | Description                                                                                  | Default |
|--------------------------|----------------------------------------------------------------------------------------------|---------|
| `I2C1_SCL_BANK`          | The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`) to use for SCL                                  | `GPIOB` |
| `I2C1_SDA_BANK`          | The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`) to use for SDA                                  | `GPIOB` |
| `I2C1_SCL`               | The pin number for the SCL pin (0-9)                                                         | `6`     |
| `I2C1_SDA`               | The pin number for the SDA pin (0-9)                                                         | `7`     |
| `I2C1_BANK` (deprecated) | The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`), superceded by `I2C1_SCL_BANK`, `I2C1_SDA_BANK` | `GPIOB` |

STM32 MCUs allow for different timing parameters when configuring I2C. These can be modified using the following parameters, using https://www.st.com/en/embedded-software/stsw-stm32126.html as a reference:

| Variable              | Default |
|-----------------------|---------|
| `I2C1_TIMINGR_PRESC`  | `15U`   |
| `I2C1_TIMINGR_SCLDEL` | `4U`    |
| `I2C1_TIMINGR_SDADEL` | `2U`    |
| `I2C1_TIMINGR_SCLH`   | `15U`   |
| `I2C1_TIMINGR_SCLL`   | `21U`   |

STM32 MCUs allow for different "alternate function" modes when configuring GPIO pins. These are required to switch the pins used to I2C mode. See the respective datasheet for the appropriate values for your MCU.

| Variable            | Default |
|---------------------|---------|
| `I2C1_SCL_PAL_MODE` | `4`     |
| `I2C1_SDA_PAL_MODE` | `4`     |

You can also overload the `void i2c_init(void)` function, which has a weak attribute. If you do this the configuration variables above will not be used. Please consult the datasheet of your MCU for the available GPIO configurations. The following is an example initialization function:

```C
void i2c_init(void)
{
  setPinInput(B6); // Try releasing special pins for a short time
  setPinInput(B7);
  wait_ms(10); // Wait for the release to happen

  palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B6 to I2C function
  palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B7 to I2C function
}
```