From 7052dbcf8b6bb6ad1226030465939f2c620aadc0 Mon Sep 17 00:00:00 2001 From: Morgan Diepart Date: Sat, 10 Feb 2024 18:09:59 +0100 Subject: [PATCH] Module17: use hardware I2C for MCP4551 --- meson.build | 2 +- platform/drivers/baseband/MCP4551.c | 55 ++++++ platform/drivers/baseband/MCP4551.h | 34 ++-- platform/drivers/baseband/MCP4551_Mod17.cpp | 203 -------------------- platform/drivers/baseband/radio_Mod17.cpp | 12 +- platform/targets/Module17/hwconfig.h | 3 + platform/targets/Module17/pinmap.h | 8 +- platform/targets/Module17/platform.c | 35 +++- 8 files changed, 119 insertions(+), 233 deletions(-) create mode 100644 platform/drivers/baseband/MCP4551.c delete mode 100644 platform/drivers/baseband/MCP4551_Mod17.cpp diff --git a/meson.build b/meson.build index 535edbe8..f4946ade 100644 --- a/meson.build +++ b/meson.build @@ -422,7 +422,7 @@ mod17_src = ['platform/targets/Module17/platform.c', 'platform/drivers/baseband/radio_Mod17.cpp', 'platform/drivers/audio/audio_Mod17.c', 'platform/drivers/audio/MAX9814_Mod17.cpp', - 'platform/drivers/baseband/MCP4551_Mod17.cpp'] + 'platform/drivers/baseband/MCP4551.c'] mod17_inc = ['platform/targets/Module17'] mod17_def = {'PLATFORM_MOD17': ''} diff --git a/platform/drivers/baseband/MCP4551.c b/platform/drivers/baseband/MCP4551.c new file mode 100644 index 00000000..ca27c8b8 --- /dev/null +++ b/platform/drivers/baseband/MCP4551.c @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2021 - 2024 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * Mathis Schmieder DB9MAT * + * * + * 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 * + ***************************************************************************/ + +#include "MCP4551.h" + +// Common WIPER values +#define MCP4551_WIPER_MID 0x080 +#define MCP4551_WIPER_A 0x100 +#define MCP4551_WIPER_B 0x000 + +// Command definitions (sent to WIPER register) +#define MCP4551_CMD_WRITE 0x00 +#define MCP4551_CMD_INC 0x04 +#define MCP4551_CMD_DEC 0x08 +#define MCP4551_CMD_READ 0x0C + + +int mcp4551_init(const struct i2cDevice *i2c, const uint8_t devAddr) +{ + return mcp4551_setWiper(i2c, devAddr, MCP4551_WIPER_MID); +} + +int mcp4551_setWiper(const struct i2cDevice *i2c, const uint8_t devAddr, + const uint16_t value) +{ + uint8_t data[2] = + { + (uint8_t)(value >> 8 & 0x01) | MCP4551_CMD_WRITE, + (uint8_t) value + }; + + i2c_acquire(i2c); + int ret = i2c_write(i2c, devAddr, data, 2, true); + i2c_release(i2c); + + return ret; +} diff --git a/platform/drivers/baseband/MCP4551.h b/platform/drivers/baseband/MCP4551.h index 48d7d565..a95e5fff 100644 --- a/platform/drivers/baseband/MCP4551.h +++ b/platform/drivers/baseband/MCP4551.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * + * Copyright (C) 2021 - 2024 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * @@ -25,29 +25,31 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { #endif -// Common WIPER values -#define MCP4551_WIPER_MID 0x080 -#define MCP4551_WIPER_A 0x100 -#define MCP4551_WIPER_B 0x000 - -// Command definitions (sent to WIPER register) -#define MCP4551_CMD_WRITE 0x00 -#define MCP4551_CMD_INC 0x04 -#define MCP4551_CMD_DEC 0x08 -#define MCP4551_CMD_READ 0x0C +/** + * Initialize the MCP4551 device. + * + * @param i2c: driver managing the I2C bus the chip is connected to. + * @param devAddr: I2C device address of the chip. + * @return zero on success, a negative error code otherwise. + */ +int mcp4551_init(const struct i2cDevice *i2c, const uint8_t devAddr); /** - * Initialise I2C. + * Set the MCP4551 wiper to a given position. + * + * @param i2c: driver managing the I2C bus the chip is connected to. + * @param devAddr: I2C device address of the chip. + * @param value: new wiper position. + * @return zero on success, a negative error code otherwise. */ -void i2c_init(); - -void mcp4551_init(uint8_t addr); -void mcp4551_setWiper(uint8_t devAddr, uint16_t value); +int mcp4551_setWiper(const struct i2cDevice *i2c, const uint8_t devAddr, + const uint16_t value); #ifdef __cplusplus } diff --git a/platform/drivers/baseband/MCP4551_Mod17.cpp b/platform/drivers/baseband/MCP4551_Mod17.cpp deleted file mode 100644 index f906327f..00000000 --- a/platform/drivers/baseband/MCP4551_Mod17.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * Mathis Schmieder DB9MAT * - * * - * 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 * - ***************************************************************************/ - -#include -#include -#include -#include "MCP4551.h" - -/* - * Implementation of MCP4551 I2C interface. - * - * Hardware I2C is not yet implemented. Bit-bang, baby! - */ - -void _i2c_start(); -void _i2c_stop(); -void _i2c_write(uint8_t val); -uint8_t _i2c_read(bool ack); - -void i2c_init() -{ - gpio_setMode(I2C_SDA, INPUT); - gpio_setMode(I2C_SCL, OUTPUT); - gpio_clearPin(I2C_SCL); -} - -void mcp4551_init(uint8_t addr) -{ - mcp4551_setWiper(addr, MCP4551_WIPER_MID); -} - -void mcp4551_setWiper(uint8_t devAddr, uint16_t value) -{ - _i2c_start(); - _i2c_write(devAddr << 1); - uint8_t temp = ((value >> 8 & 0x01) | MCP4551_CMD_WRITE); - _i2c_write(temp); - temp = (value & 0xFF); - _i2c_write(temp); - _i2c_stop(); -} - -/*uint16_t i2c_readReg16(uint8_t devAddr, uint8_t reg) -{ - _i2c_start(); - _i2c_write(devAddr << 1); - _i2c_write(reg); - _i2c_start(); - _i2c_write(devAddr | 0x01); - uint8_t valHi = _i2c_read(true); - uint8_t valLo = _i2c_read(false); - _i2c_stop(); - - return (valHi << 8) | valLo; -} */ - -/* - * Software I2C routine - */ - -void _i2c_start() -{ - gpio_setMode(I2C_SDA, OUTPUT); - - /* - * Lines commented to keep SCL high when idle - * - gpio_clearPin(I2C_SCL); - delayUs(2); - */ - - gpio_setPin(I2C_SDA); - delayUs(5); - - gpio_setPin(I2C_SCL); - delayUs(5); - - gpio_clearPin(I2C_SDA); - delayUs(5); - - gpio_clearPin(I2C_SCL); - delayUs(6); -} - -void _i2c_stop() -{ - gpio_setMode(I2C_SDA, OUTPUT); - - gpio_clearPin(I2C_SCL); - delayUs(5); - - gpio_clearPin(I2C_SDA); - delayUs(5); - - gpio_setPin(I2C_SCL); - delayUs(5); - - gpio_setPin(I2C_SDA); - delayUs(5); - - /* - * Lines commented to keep SCL high when idle - * - gpio_clearPin(I2C_SCL); - delayUs(5); - */ -} - -void _i2c_write(uint8_t val) -{ - gpio_setMode(I2C_SDA, OUTPUT); - - for(uint8_t i = 0; i < 8; i++) - { - gpio_clearPin(I2C_SCL); - delayUs(1); - - if(val & 0x80) - { - gpio_setPin(I2C_SDA); - } - else - { - gpio_clearPin(I2C_SDA); - } - - val <<= 1; - delayUs(1); - gpio_setPin(I2C_SCL); - delayUs(5); - } - - /* Ensure SCL is low before releasing SDA */ - gpio_clearPin(I2C_SCL); - - /* Clock cycle for slave ACK/NACK */ - gpio_setMode(I2C_SDA, INPUT_PULL_UP); - delayUs(5); - gpio_setPin(I2C_SCL); - delayUs(5); - gpio_clearPin(I2C_SCL); - delayUs(1); - - /* Asserting SDA pin allows to fastly bring the line to idle state */ - gpio_setPin(I2C_SDA); - gpio_setMode(I2C_SDA, OUTPUT); - delayUs(6); -} - -uint8_t _i2c_read(bool ack) -{ - gpio_setMode(I2C_SDA, INPUT_PULL_UP); - gpio_clearPin(I2C_SCL); - - uint8_t value = 0; - for(uint8_t i = 0; i < 8; i++) - { - delayUs(5); - gpio_setPin(I2C_SCL); - delayUs(5); - - value <<= 1; - value |= gpio_readPin(I2C_SDA); - - gpio_clearPin(I2C_SCL); - } - - /* - * Set ACK/NACK state BEFORE putting SDA gpio to output mode. - * This avoids spurious spikes which can be interpreted as NACKs - */ - gpio_clearPin(I2C_SDA); - gpio_setMode(I2C_SDA, OUTPUT); - delayUs(5); - if(!ack) gpio_setPin(I2C_SDA); - - /* Clock cycle for ACK/NACK */ - delayUs(5); - gpio_setPin(I2C_SCL); - delayUs(5); - gpio_clearPin(I2C_SCL); - delayUs(5); - - return value; -} diff --git a/platform/drivers/baseband/radio_Mod17.cpp b/platform/drivers/baseband/radio_Mod17.cpp index d41c10ce..9455bbce 100644 --- a/platform/drivers/baseband/radio_Mod17.cpp +++ b/platform/drivers/baseband/radio_Mod17.cpp @@ -35,8 +35,8 @@ void radio_init(const rtxStatus_t *rtxState) radioStatus = OFF; - mcp4551_setWiper(SOFTPOT_TX, mod17CalData.tx_wiper); - mcp4551_setWiper(SOFTPOT_RX, mod17CalData.rx_wiper); + mcp4551_setWiper(&i2c1, SOFTPOT_TX, mod17CalData.tx_wiper); + mcp4551_setWiper(&i2c1, SOFTPOT_RX, mod17CalData.rx_wiper); } void radio_terminate() @@ -74,8 +74,8 @@ void radio_enableRx() { radioStatus = RX; - mcp4551_setWiper(SOFTPOT_TX, mod17CalData.tx_wiper); - mcp4551_setWiper(SOFTPOT_RX, mod17CalData.rx_wiper); + mcp4551_setWiper(&i2c1, SOFTPOT_TX, mod17CalData.tx_wiper); + mcp4551_setWiper(&i2c1, SOFTPOT_RX, mod17CalData.rx_wiper); // Module17 PTT output is open drain. This means that, on MCU side, we have // to assert the gpio to bring it to low state. @@ -89,8 +89,8 @@ void radio_enableTx() { radioStatus = TX; - mcp4551_setWiper(SOFTPOT_TX, mod17CalData.tx_wiper); - mcp4551_setWiper(SOFTPOT_RX, mod17CalData.rx_wiper); + mcp4551_setWiper(&i2c1, SOFTPOT_TX, mod17CalData.tx_wiper); + mcp4551_setWiper(&i2c1, SOFTPOT_RX, mod17CalData.rx_wiper); max9814_setGain(mod17CalData.mic_gain); if(mod17CalData.ptt_out_level) diff --git a/platform/targets/Module17/hwconfig.h b/platform/targets/Module17/hwconfig.h index 2c8a69a0..5f3c1d50 100644 --- a/platform/targets/Module17/hwconfig.h +++ b/platform/targets/Module17/hwconfig.h @@ -22,9 +22,12 @@ #ifndef HWCONFIG_H #define HWCONFIG_H +#include #include #include "pinmap.h" +extern const struct i2cDevice i2c1; + /* Screen dimensions */ #define CONFIG_SCREEN_WIDTH 128 #define CONFIG_SCREEN_HEIGHT 64 diff --git a/platform/targets/Module17/pinmap.h b/platform/targets/Module17/pinmap.h index 4d86e190..dbad8e43 100644 --- a/platform/targets/Module17/pinmap.h +++ b/platform/targets/Module17/pinmap.h @@ -61,9 +61,9 @@ #define POWER_SW GPIOA,15 /* I2C for MCP4551 */ -#define I2C_SDA GPIOB,7 -#define I2C_SCL GPIOB,6 -#define SOFTPOT_RX 0x2E -#define SOFTPOT_TX 0x2F +#define I2C1_SDA GPIOB,7 +#define I2C1_SCL GPIOB,6 +#define SOFTPOT_RX 0x2E +#define SOFTPOT_TX 0x2F #endif /* PINMAP_H */ diff --git a/platform/targets/Module17/platform.c b/platform/targets/Module17/platform.c index c4ea1eda..2e6ec2f5 100644 --- a/platform/targets/Module17/platform.c +++ b/platform/targets/Module17/platform.c @@ -24,12 +24,16 @@ #include #include #include +#include #include #include #include #include #include + +I2C_STM32_DEVICE_DEFINE(i2c1, I2C1, NULL) + extern mod17Calib_t mod17CalData; static hwInfo_t hwInfo = @@ -58,6 +62,32 @@ void platform_init() gpio_setMode(PTT_OUT, OUTPUT); gpio_clearPin(PTT_OUT); + /* + * Check if external I2C1 pull-ups are present. If they are not, + * enable internal pull-ups and slow-down I2C1. + */ + gpio_setMode(I2C1_SCL, INPUT_PULL_DOWN); + gpio_setMode(I2C1_SDA, INPUT_PULL_DOWN); + + uint8_t i2cSpeed = I2C_SPEED_100kHz; + bool i2cPullups = gpio_readPin(I2C1_SCL) + & gpio_readPin(I2C1_SDA); + + /* Set gpios to alternate function, connected to I2C peripheral */ + if(i2cPullups == false) + { + gpio_setMode(I2C1_SCL, ALTERNATE_OD_PU | ALTERNATE_FUNC(4)); + gpio_setMode(I2C1_SDA, ALTERNATE_OD_PU | ALTERNATE_FUNC(4)); + i2cSpeed = I2C_SPEED_LOW; + } + else + { + gpio_setMode(I2C1_SCL, ALTERNATE_OD | ALTERNATE_FUNC(4)); + gpio_setMode(I2C1_SDA, ALTERNATE_OD | ALTERNATE_FUNC(4)); + } + + i2c_init(&i2c1, i2cSpeed); + /* Set analog output for baseband signal to an idle level of 1.1V */ gpio_setMode(BASEBAND_TX, ANALOG); RCC->APB1ENR |= RCC_APB1ENR_DACEN; @@ -66,10 +96,9 @@ void platform_init() nvm_init(); adc1_init(); - i2c_init(); - mcp4551_init(SOFTPOT_RX); - mcp4551_init(SOFTPOT_TX); audio_init(); + mcp4551_init(&i2c1, SOFTPOT_RX); + mcp4551_init(&i2c1, SOFTPOT_TX); /* Set defaults for calibration */ mod17CalData.tx_wiper = 0x080;