From 44215ffdf081663b68bc941382168afe0d4d7fe7 Mon Sep 17 00:00:00 2001 From: Aadi Desai <21363892+supleed2@users.noreply.github.com> Date: Sat, 24 Jun 2023 20:53:04 +0100 Subject: [PATCH] Add amp control library using i2c --- demo/Makefile | 2 +- demo/amp | 33 ++++++++++ demo/amp.cpp | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 demo/amp create mode 100644 demo/amp.cpp diff --git a/demo/Makefile b/demo/Makefile index b4a494c..1249d53 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -3,7 +3,7 @@ BUILD_DIR?=../build/gsd_orangecrab/ include $(BUILD_DIR)/software/include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak -OBJECTS = audio.o can.o crt0.o donut.o isr.o led.o main.o +OBJECTS = amp.o audio.o can.o crt0.o donut.o isr.o led.o main.o CFLAGS += -DWITH_CXX diff --git a/demo/amp b/demo/amp new file mode 100644 index 0000000..bd7e683 --- /dev/null +++ b/demo/amp @@ -0,0 +1,33 @@ +#include +#include +#include + +#pragma once + +// Struct Definition + +struct amp_i2c { + uint8_t pot0; + uint8_t pot1; + uint8_t conf; +}; + +// Function Declarations + +bool amp_init(void); + +bool amp_read(amp_i2c &); + +bool amp_read_pot0(uint8_t &); + +bool amp_read_pot1(uint8_t &); + +bool amp_read_conf(uint8_t &); + +bool amp_write(amp_i2c value); + +bool amp_write_pot0(uint8_t value); + +bool amp_write_pot1(uint8_t value); + +bool amp_write_conf(uint8_t value); diff --git a/demo/amp.cpp b/demo/amp.cpp new file mode 100644 index 0000000..4dfb149 --- /dev/null +++ b/demo/amp.cpp @@ -0,0 +1,178 @@ +#include "amp" +#include +#include +#include + +#ifdef CONFIG_HAS_I2C +#define I2C_DELAY(n) busy_wait_us(n * (250000 / I2C_FREQ_HZ)) + +static inline void i2c_oe_scl_sda(bool oe, bool scl, bool sda) { + i2c_w_write( + ((oe & 1) << CSR_I2C_W_OE_OFFSET) | + ((scl & 1) << CSR_I2C_W_SCL_OFFSET) | + ((sda & 1) << CSR_I2C_W_SDA_OFFSET)); +} + +// START condition: 1-to-0 transition of SDA when SCL is 1 +static void i2c_start(void) { + i2c_oe_scl_sda(1, 1, 1); + I2C_DELAY(1); + i2c_oe_scl_sda(1, 1, 0); + I2C_DELAY(1); + i2c_oe_scl_sda(1, 0, 0); + I2C_DELAY(1); +} + +// STOP condition: 0-to-1 transition of SDA when SCL is 1 +static void i2c_stop(void) { + i2c_oe_scl_sda(1, 0, 0); + I2C_DELAY(1); + i2c_oe_scl_sda(1, 1, 0); + I2C_DELAY(1); + i2c_oe_scl_sda(1, 1, 1); + I2C_DELAY(1); + i2c_oe_scl_sda(0, 1, 1); +} + +// Call when in the middle of SCL low, advances one clk period +static void i2c_transmit_bit(int value) { + i2c_oe_scl_sda(1, 0, value); + I2C_DELAY(1); + i2c_oe_scl_sda(1, 1, value); + I2C_DELAY(2); + i2c_oe_scl_sda(1, 0, value); + I2C_DELAY(1); +} + +// Call when in the middle of SCL low, advances one clk period +static int i2c_receive_bit(void) { + int value; + i2c_oe_scl_sda(0, 0, 0); + I2C_DELAY(1); + i2c_oe_scl_sda(0, 1, 0); + I2C_DELAY(1); + // read in the middle of SCL high + value = i2c_r_read() & 1; + I2C_DELAY(1); + i2c_oe_scl_sda(0, 0, 0); + I2C_DELAY(1); + return value; +} + +// Send data byte and return 1 if slave sends ACK +static bool i2c_transmit_byte(unsigned char data) { + int i; + int ack; + + // SCL should have already been low for 1/4 cycle + // Keep SDA low to avoid short spikes from the pull-ups + i2c_oe_scl_sda(1, 0, 0); + for (i = 0; i < 8; ++i) { + // MSB first + i2c_transmit_bit((data & (1 << 7)) != 0); + data <<= 1; + } + i2c_oe_scl_sda(0, 0, 0); // release line + ack = i2c_receive_bit(); + + // 0 from slave means ack + return ack == 0; +} + +// Read data byte and send ACK if ack=1 +static unsigned char i2c_receive_byte(bool ack) { + int i; + unsigned char data = 0; + + for (i = 0; i < 8; ++i) { + data <<= 1; + data |= i2c_receive_bit(); + } + i2c_transmit_bit(!ack); + i2c_oe_scl_sda(0, 0, 0); // release line + + return data; +} + +bool amp_init(void) { + return amp_write({0x00, 0x00, 0x07}); +} + +bool amp_read(amp_i2c &rcv) { + i2c_start(); + if (!i2c_transmit_byte(I2C_ADDR_RD(0x28))) { + i2c_stop(); + return false; + } + rcv.pot0 = i2c_receive_byte(true) & 0x3F; + rcv.pot1 = i2c_receive_byte(true) & 0x3F; + rcv.conf = i2c_receive_byte(false) & 0x3F; + i2c_stop(); + return true; +} + +bool amp_read_pot0(uint8_t &pot0) { + amp_i2c temp; + bool success = amp_read(temp); + pot0 = temp.pot0; + return success; +} + +bool amp_read_pot1(uint8_t &pot1) { + amp_i2c temp; + bool success = amp_read(temp); + pot1 = temp.pot1; + return success; +} + +bool amp_read_conf(uint8_t &conf) { + amp_i2c temp; + bool success = amp_read(temp); + conf = temp.conf; + return success; +} + +bool amp_write(amp_i2c value) { + i2c_start(); + if (!i2c_transmit_byte(I2C_ADDR_WR(0x28))) { + i2c_stop(); + return true; + } + if (!i2c_transmit_byte(value.pot0)) { + i2c_stop(); + return false; + } + if (!i2c_transmit_byte(value.pot1 | 0x40)) { + i2c_stop(); + return false; + } + if (!i2c_transmit_byte(value.conf | 0x80)) { + i2c_stop(); + return false; + } + i2c_stop(); + return true; +} + +bool amp_write_pot0(uint8_t value) { + amp_i2c temp; + bool success = amp_read(temp); + temp.pot0 = value; + return success & amp_write(temp); +} + +bool amp_write_pot1(uint8_t value) { + amp_i2c temp; + bool success = amp_read(temp); + temp.pot1 = value; + return success & amp_write(temp); +} + +bool amp_write_conf(uint8_t value) { + amp_i2c temp; + bool success = amp_read(temp); + temp.conf = value; + return success & amp_write(temp); +} + +#endif