/* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** BusVoodoo I²C mode (code) * @file busvoodoo_i2c.c * @author King Kévin * @date 2018 * @note peripherals used: I2C @ref busvoodoo_i2c */ /* standard libraries */ #include // standard integer types #include // standard utilities #include // string utilities /* STM32 (including CM3) libraries */ #include // I2C library /* own libraries */ #include "global.h" // board definitions #include "print.h" // printing utilities #include "menu.h" // menu definitions #include "i2c_master.h" // I2C utilities #include "busvoodoo_global.h" // BusVoodoo definitions #include "busvoodoo_oled.h" // OLED utilities #include "busvoodoo_i2c.h" // own definitions /** @defgroup busvoodoo_i2c I2C peripheral to communicate with I2C devices * @{ */ #define BUSVOODOO_I2C I2C2 /**< I2C peripheral */ /** @} */ /** mode setup stage */ static enum busvoodoo_i2c_setting_t { BUSVOODOO_I2C_SETTING_NONE, BUSVOODOO_I2C_SETTING_SPEED, BUSVOODOO_I2C_SETTING_ADDRESSBITS, BUSVOODOO_I2C_SETTING_PULLUP, BUSVOODOO_I2C_SETTING_DONE, } busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_NONE; /** I2C speed (in kHz) */ uint16_t busvoodoo_i2c_speed = 100; /** I2C address bits (7 or 10) */ uint8_t busvoodoo_i2c_addressbits = 7; /** if embedded or external pull-up resistors are use */ bool busvoodoo_i2c_embedded_pullup = true; /** setup I2C mode * @param[out] prefix terminal prompt prefix * @param[in] line terminal prompt line to configure mode * @return if setup is complete */ static bool busvoodoo_i2c_setup(char** prefix, const char* line) { bool complete = false; // is the setup complete if (NULL==line) { // first call busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_NONE; // re-start configuration } switch (busvoodoo_i2c_setting) { case BUSVOODOO_I2C_SETTING_NONE: snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "speed in kHz (1-400) [%u]", busvoodoo_i2c_speed); *prefix = busvoodoo_global_string; // ask for speed busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_SPEED; break; case BUSVOODOO_I2C_SETTING_SPEED: if (NULL==line || 0==strlen(line)) { // use default setting busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_ADDRESSBITS; // go to next setting } else { // setting provided uint32_t speed = atoi(line); // parse setting if (speed>0 && speed<=400) { // check setting busvoodoo_i2c_speed = speed; // remember setting busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_ADDRESSBITS; // go to next setting } } if (BUSVOODOO_I2C_SETTING_ADDRESSBITS==busvoodoo_i2c_setting) { // if next setting snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "address size in bits (7,10) [%u]", busvoodoo_i2c_addressbits); // prepare next setting *prefix = busvoodoo_global_string; // display next setting } break; case BUSVOODOO_I2C_SETTING_ADDRESSBITS: if (NULL==line || 0==strlen(line)) { // use default setting busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_PULLUP; // go to next setting } else { // setting provided uint8_t addressbits = atoi(line); // parse setting if (7==addressbits || 10==addressbits) { // check setting busvoodoo_i2c_addressbits = addressbits; // remember setting busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_PULLUP; // go to next setting } } if (BUSVOODOO_I2C_SETTING_PULLUP==busvoodoo_i2c_setting) { // if next setting printf("1) open-drain, with embedded pull-up resistors (2kO)\n"); printf("2) open-drain, with external pull-up resistors\n"); snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "drive mode (1,2) [%c]", busvoodoo_i2c_embedded_pullup ? '1' : '2'); // show drive mode *prefix = busvoodoo_global_string; // display next setting } break; case BUSVOODOO_I2C_SETTING_PULLUP: if (NULL==line || 0==strlen(line)) { // use default setting busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_DONE; // go to next setting } else if (1==strlen(line)) { // setting provided uint8_t drive = atoi(line); // parse setting if (1==drive || 2==drive) { // check setting busvoodoo_i2c_embedded_pullup = (1==drive); // remember setting busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_DONE; // go to next setting } } if (BUSVOODOO_I2C_SETTING_DONE==busvoodoo_i2c_setting) { // we have all settings, configure I2C i2c_master_setup(BUSVOODOO_I2C, false); // setup I2C if (busvoodoo_i2c_embedded_pullup) { busvoodoo_embedded_pullup(true); // set pull-up printf("use LV to set voltage\n"); } led_off(); // disable LED because there is no activity busvoodoo_i2c_setting = BUSVOODOO_I2C_SETTING_NONE; // restart settings next time *prefix = "I2C"; // display mode busvoodoo_oled_text_left(*prefix); // set mode title on OLED display const char* pinout_io[10] = {"GND", "5V", "3V3", "LV", "SDA", "SCL", NULL, NULL}; // HiZ mode pinout for (uint8_t i=0; i7) ? "0x%03x " : "0x%02x ", address); // display address i2c_slaves++; // increase slave count } i2c_master_stop(BUSVOODOO_I2C); // send stop condition } if (i2c_slaves>0) { printf("\n"); } printf("%u slave(s) found\n", i2c_slaves); // show summary } static const struct menu_command_t busvoodoo_i2c_commands[] = { { 's', "scan", "scan for slave devices", MENU_ARGUMENT_NONE, NULL, &busvoodoo_i2c_command_scan, }, }; struct busvoodoo_mode_t busvoodoo_i2c_mode = { "i2c", "Inter-Integrated Circuit", &busvoodoo_i2c_setup, busvoodoo_i2c_commands, LENGTH(busvoodoo_i2c_commands), &busvoodoo_i2c_exit, };