#include <Wire.h>
#include <FS.h>
#include <SD.h>
#include <SPI.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_FT6206.h>
#include <EEPROM.h>
#include "TLV320_Audio_Controller.h"
#include "Touch_Handler.h"
#include "BQ24_Charger_Control.h"
#include "TCA64_Expander.h"
//I2S Uses Philips Format
#include "driver/i2s_std.h"
#include "driver/gpio.h"
#include "driver/sdspi_host.h"
/*
esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, size_t *bytes_read, uint32_t timeout_ms)
I2S read data.
Note: Only allowed to be called when the channel state is RUNNING but the RUNNING only stands for the software state,
it doesn’t mean there is no the signal transporting on line.
Parameters
handle – [in] I2S channel handler
dest – [in] The pointer of receiving data buffer
size – [in] Max data buffer length
bytes_read – [out] Byte number that actually be read
timeout_ms – [in] Max block time
Returns
ESP_OK Read successfully
ESP_ERR_INVALID_ARG NULL pointer or this handle is not rx handle
ESP_ERR_TIMEOUT Reading timeout, no reading event received from ISR within ticks_to_wait
ESP_ERR_INVALID_STATE I2S is not ready to read
esp_err_t i2s_channel_write(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_written, uint32_t timeout_ms)
I2S write data.
Note: Only allowed to be called when the channel state is RUNNING, (i.e., tx channel has been started and is not writing now)
but the RUNNING only stands for the software state, it doesn’t mean there is no the signal transporting on line.
Parameters
handle – [in] I2S channel handler
src – [in] The pointer of sent data buffer
size – [in] Max data buffer length
bytes_written – [out] Byte number that actually be sent
timeout_ms – [in] Max block time
Returns
ESP_OK Write successfully
ESP_ERR_INVALID_ARG NULL pointer or this handle is not tx handle
ESP_ERR_TIMEOUT Writing timeout, no writing event received from ISR within ticks_to_wait
ESP_ERR_INVALID_STATE I2S is not ready to write
*/
//Charger, Expander, Audio
static const byte DEVICE_ADDRESS[3] = { 0x6B, 0x20, 0x18 };
//TODO: Find the optimal Headset Detection and Button Debounce times
static const int HEADSET_DETECT_DEBOUNCE = 16;// In milliseconds
static const int HEADSET_BUTTON_DEBOUNCE = 8;// In milliseconds
//Left Volume, Right Volume, Master Setting, Frequency and Duration
static const int WARNING_BEEP_DURATION = 100;
static const float WARNING_BEEP_FREQUENCY = 50.0;
static const int WARNING_BEEP_VOLUME = -20;
static const int WARNING_BEEP_SETTING = 2;
static const int SD_FREQUENCY = 4000;//In kHz, only integers
enum PINS{
EXPANDER_INT = GPIO_NUM_4,
SPI_DIN = GPIO_NUM_2,
SPI_DOUT = GPIO_NUM_7,
I2C_SDA = GPIO_NUM_3,
I2S_BCLK = GPIO_NUM_0,
I2S_DIN = GPIO_NUM_1,
I2S_MCLK = GPIO_NUM_10,
I2S_WCLK = GPIO_NUM_9,
I2S_DOUT = GPIO_NUM_18,
DISPLAY_CS = GPIO_NUM_5,
DISPLAY_DC = GPIO_NUM_8,
CLK = GPIO_NUM_6
};
enum EXPANDER_PINS{
MEM_CS = 0,
CONVERT_INT1 = 1,
CONVERT_INT2 = 2,
SD_CS = 3,
TP_CS = 4,
TP_INT = 5,
LCD_RST = 6,
MNTR_INT = 7,
CHGR_CE = 8,
CHGR_STAT = 9,
CHGR_INT = 10,
LED_EN = 11,
LED_VSEL1 = 12,
LED_VSEL2 = 13,
LED_VSEL3 = 14,
LED_VSEL4 = 15
};
//Holds the prevous data about the five input pins
bool previousStates[5];
//If true, pause any recording/playing. If false, continue playing
bool isPaused;
//If true, recording and saving to the MEM chip. If false, go to other things
bool isRecording;
//Display object, the ILI9341
Adafruit_ILI9341 display = Adafruit_ILI9341(DISPLAY_CS, DISPLAY_DC, SPI_DOUT, CLK, -1, SPI_DIN);
//Touch screen object, the FT6206 ControlLer IC
Adafruit_FT6206 touch = Adafruit_FT6206();
//My TLV3204 Controller
TLV3204 audio = TLV3204(I2C_SDA, CLK);
//Creates the object used for I2C interface to the TCA Expander
TCA64_Expander tc = TCA64_Expander(DEVICE_ADDRESS[1], I2C_SDA, CLK);
//Charger Controller
BQ24_Charger_Control bq = BQ24_Controller(DEVICE_ADDRESS[0], I2C_SDA, CLK);
//My SD Card controller
//SD_Card sd = SD_Card();
//Touch handler for the various buttons
Touch_Handler handler;
//Setting up I2S Communication
i2s_chan_handle_t txHandle;
i2s_chan_handle_t rxHandle;
i2s_chan_config_t channelConfig = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
//Allocate a new tx and rx channel and get the handle of this channel
i2s_new_channel(&channelConfig, &txHandle, &rxHandle);
//Setting up configurations for the pins, data width, speed, and type of audio to be sent
i2s_std_config_t standardConfig = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = GPIO_NUM_10,
.bclk = GPIO_NUM_0,
.ws = GPIO_NUM_9,
.dout = GPIO_NUM_18,
.din = GPIO_NUM_1,
.invert_flags = {.mclk_inv = false, .bclk_inv = false, .ws_inv = false}
}
};
//Initializes the I2S Channels
i2s_channel_init_std_mode(txHandle, &standardConfig);
i2s_channel_init_std_mode(rxHandle, &standardConfig);
//Configure the pins of the bus
spi_bus_config_t spiConfig = {
.mosi_io_num = SPI_DOUT, .miso_io_num = SPI_DIN,
.sclk_io_num = CLK, .max_transfer_size = 4092,
};
//Creates the config for the SD card and the Memory IC
sdspi_device_config_t sdConfig = {
.host_id = SPI2_HOST,
.gpio_cs = SDSPI_SLOT_NO_CS, .gpio_cd = SDSPI_SLOT_NO_CD,
.gpio_wp = SDSPI_SLOT_NO_WP, .gpio_int = SDSPI_SLOT_NO_INT,
};
//Handles for each of the Memory Thingys
sdspi_dev_handle_t sdHandle;
sdspi_dev_handle_t memHandle;
//Initalizes the SPI bus
spi_bus_initalize(SPI2_HOST, spiConfig, SPI_DMA_CH_AUTO);
void setup(){
float time = millis();
Wire.begin(I2C_SDA, CLK);
display.begin(); touch.begin(128,Wire*);
tc.setExpanderPins(EXPANDER_PINS);
byte regConfig[2][8] = {
{0,1,1,0,0,1,0,0},
{1,0,0,0,0,0,0,0}
};
tc.configureReg(regConfig);
display.setCursor(0,0);
display.println("Setting up SD Card...");
//Sets MEM_CS to low, SD_CS to high to create the card handle
activateSD();
sdHandle = printMemInfo(sdHandle);
display.println("Setting up MEM chip...");
activateMEM();
memHandle = printMemInfo(memHandle);
deactivateExternalMem();//CS pins low
attachInterrupt(digitalPinToInterrupt(EXPANDER_INT),readInterrupt,RISING);
display.println("Creating log folder...");
beginLog();
Log("Created log folder");
//updateDate(date,time);
//255 means that the selected address has not been written to
if(EEPROM.read(0x00) == 255){
display.setCursor(0,0); display.setTextColor(0xFFFFFF);
display.setTextSize(1);
display.print("Enter date, dd/mm/yy");
display.setCursor(0,25); display.setTextSize(3);
display.print("0 1 2 3 4 5 6 7 8 9 <=");
//Do stuff to get the date
tc.setOutput(LCD_RST, true);
delayMicroseconds(5);//Allow the display to reset
tc.setOutput(LCD_RST, false);
display.setCursor(0,0); display.setTextColor(0xFFFFFF);
display.setTextSize(1);
display.print("Enter time, hh/mm");
display.setCursor(0,25); display.setTextSize(3);
display.print("0 1 2 3 4 5 6 7 8 9 <=");
//Do stuff to get the time
/*time_t now;
char strftime_buf[64];
struct tm timeinfo;
time(&now);
// Set timezone to China Standard Time
setenv("TZ", "CST-8", 1);
tzset();
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);*/
//Configure the TLV320
audio.audioInterface(audio.KEYWORDS.I2S,32);//I2S Interface, 32 bit bus
audio.generalInputOutput(audio.KEYWORDS.INT1);//Set GPIO to INT1
audio.dataInSPIControl(audio.KEYWORDS.INT2);//Set MISO to INT2
audio.headsetDetectDebounce(HEADSET_DETECT_DEBOUNCE);
audio.headsetButtonDebounce(HEADSET_BUTTON_DEBOUNCE);
audio.headsetInsertion(true,false);
audio.buttonPress(true,false);
audio.headphoneOverCurrent(true,false);
//INT1 will generate an interrupt whenever a headset insertion event, button
//press, or an over-current on the headset is detected
audio.analogOutDRCSignalThreshold(true,true);
audio.autoGainControl(true,true);
audio.analogOverflow(true,true);
//INT2 will generate an interrupt whenever the DRC exceeds the set Signal
//Threshold, the AGC has noise that exceeds the Noise Threshold, or either
//of the Analog converters has an overflow.
EEPROM.write(0x00,0);//Writes a zero to address 0x00 to signify that stuff has been set up
}
Log("Startup time: " + time);
}
/*!
This is where everything should happen. Keep things pretty fast when going between differnt
methods and classes
*/
void loop(){
if(touch.touched()){}
}
FS fs;
File file;
String currentLogName;
/*!
Opens a file on the SD Card, if there is one. If a card is not present, or was not properly
set up, a 1 will be returned. Otherwise, a 0. Names the file the current date.
*/
byte beginLog(){
SPIClass spi = SPIClass(VSPI);
activateSD();
spi.begin(PINS.CLK, PINS.SPI_DIN, PINS.SPI_DOUT, 0);
if(!SD.begin(0,spi,80000000)){
display.println("No SD Card!");
return 1;
}
//Checks if a directory called "logs" exists
file = SD.open("logs/");
//If it doesn't, makes a new one
if(!file) SD.mkdir("logs/");
SD.open("logs/Pending_Date.txt");
currentLogName = "Pending_Date";
deactivateExternalMem();
return 0;
}
/*!
Puts information about an event to a log. Timestamp is collected from the millis() after start
@param info What to put into the log.
*/
void Log(String info){
activateSD();
file = fs.open("logs/" + currentLogName +".txt", FILE_WRITE);
file.println((millis() / 1000) + ": " + info);
deactivateExternalMem();
/*
File file = fs.open(path, FILE_WRITE);
file.print(message)
while(file.available()) Serial.write(file.read());
fs.mkdir(path))
*/
}
/*!
Updates the name of the file after the current date has been set
@param date An array with three members: the day, month, and year.
@param time An array with two members: the hour and the minute, in 24 hour format.
*/
void updateDate(byte date[3], byte time[2]){
String name;
for(byte i = 0; i < 3; i++) name += date[i] + "_";
name += "__" + time[0] + "_" + time[1];
fs.rename("logs/" + name);
currentLogName = name;
}
/*!
Ends the log
*/
void close(){ file.close(); }
//The following is for accesing the memory cards
EXPANDER_PINS memChipSelect[2] = {MEM_CS, SD_CS};
bool states[2];
void activateSD(){
states[0] = false;
states[1] = true;
tc.setOutput(memChipSelect, states);
}
void activateMEM(){
states[0] = true;
states[1] = false;
tc.setOutput(memChipSelect, states);
}
void deactivateExternalMem(){
states[0] = false;
states[1] = false;
tc.setOutput(memChipSelect, states);
}
sdspi_dev_handle_t printMemInfo(sdspi_dev_handle_t handle){
sdmmc_card_t card;
sdspi_host_init_device(sdConfig, handle);
sdmmc_card_init(SPI2_HOST, card);
display.println("Manufacturer ID: "+card.cid.mfg_id);
display.println("OEM ID: "+card.cid.oem_id);
String name;
for(byte i = 0; i < 8; i++) name += card.cid.name[i];
display.println("Name: "+name);
display.println("Rev. Num: "+card.cid.revision);
display.println("Serial Num: "+card.cid.serial);
display.println("Manufac. Date: "+card.cid.date);
Log("Manufacturer ID: "+card.cid.mfg_id);
Log("OEM ID: "+card.cid.oem_id);
name = "";
for(byte i = 0; i < 8; i++) name += card.cid.name[i];
Log("Name:"+name);
Log("Rev. Num: "+card.cid.revision);
Log("Serial Num: "+card.cid.serial);
Log("Manufac. Date: "+card.cid.date);
return handle;
}
/*
Media data structure:
- Starts at the memory address listed in the directory section
First bits: Where the media ends
Bits through : The total length of the media.
- The maximum length of any media is 256 minutes.
- If less than an hour, displayed as 00:00.000, or a setting can turn off the miliseconds view
- If more than an hour, displayed as 0:00:00. Millisecond view is not allowed, but the amount is known.
- Since the sample rate is known, the total amount of bits to skip over can be calculated
*/
int recordStartTime;
void startRecord(){
display.setTextSize(3);
display.setCursor(0,75);
//Update the play to pause
//Show the stop
//Display the current length/time
//Get the audio data from the TLV, filling up an internal buffer, emptying it as fast as it can into the
//mem chip
//Inform the user if noise was detected, and at what time in the recording
//Store the time of which it was detected into RAM
//Repeat this process until the user presses stop or pause
//All data will be loaded into mem
//Pause will update to play
//Wait until the user presses Stop or Play
//Play: continue the recording
//Stop: Prompt to enter a name of song
}
/*
2.3.3.1.12 ADC Data Interface:
The decimation filter and signal processing block in the ADC channel passes 32-bit data words to the
audio serial interface once every cycle of fS,ADC. During each cycle of fS,ADC, a pair of data words (for
left and right channel) are passed. The audio serial interface rounds the data to the required word length
of the interface before converting to serial data as per the different modes for audio serial interface.
*/
int statStartTime;
//Things to look for:
//INT always goes low when reading from the Expander
void readInterrupt(){
//All INT's will generate a tone that overwrites the current audio being
//played, as well as display the error on the upper left corner of the screen
audio.beepTime(WARNING_BEEP_DURATION);//Duration to the set time
audio.beepFrequency(WARNING_BEEP_FREQUENCY);// Frequency of the beep
//Left volume, and the master setting
audio.beepVolumeControl(WARNING_BEEP_VOLUME, 0, WARNING_BEEP_SETTING);
//The beep will immediatly play
bool currentStates[5] = {
tc.readPin(CONVERT_INT1),
tc.readPin(CONVERT_INT2),
tc.readPin(MNTR_INT),
tc.readPin(CHGR_STAT),
tc.readPin(CHGR_INT)
};
byte pin;
for(byte i = 0; i < 5; i++){
pin += (currentStates[i] == previousStates[i] ? 0 : 1);
}
previousStates = currentStates;//Updating the read data
display.setTextSize(1);//Size 1
display.setTextColor(ILI9341_RED);//Red for maximum visual effect
display.setCursor(0,0);
switch(pin){
//Convert Int 1
case 0b10000:
//Headset Button Press
if(audio.toggleable(0,44,5)){
Log("Headset Button press");
}
//Headset Removal/insertion
if(audio.toggleable(0,44,4)){
Log("Headset inserted");
}
//Headset HPL Overcurrent
if(audio.toggleable(0,44,7)){
Log("Over current on Left Channel");
}
//Headset HPR Overcurrent
if(audio.toggleable(0,44,6)){
Log("Over current on Right Channel");
}
break;
//Convert Int 2
case 0b01000:
//Left DRC Exceeded Signal Threshold
if(audio.toggleable(0,44,3)){
Log("Left DRC Excceded set Signal Threshold");
}
//Right DRC Exceeded Signal Threshold
if(audio.toggleable(0,44,2)){
Log("Right DRC Excceded set Signal Threshold");
}
//Left AGC Exceeded Noise Threshold
if(audio.toggleable(0,45,6)){
Log("Left AGC Excceded set Noise Threshold");
}
//Right AGC Exceeded Noise Threshold
if(audio.toggleable(0,45,5)){
Log("Right AGC Excceded set Noise Threshold");
}
//Left DAC Data Overflow
if(audio.toggleable(0,42,7)){
Log("Left DAC Overflow!");
}
//Right DAC Data Overflow
if(audio.toggleable(0,42,6)){
Log("Right DAC Overflow!");
}
//Left ADC Data Overflow
if(audio.toggleable(0,42,3)){
Log("Left ADC Overflow!");
}
//Right ADC Data Overflow
if(audio.toggleable(0,42,2)){
Log("Right ADC Overflow!");
}
break;
//Moniter Int
case 0b00100:
break;
//Charger Stat
case 0b00010:
//Gets the current time to determine if the STAT pin is blinking at 1 Hz
if(statStartTime == 0) statStartTime = millis();
else if(statStartTime >= millis() - 1000){
bq.STAT currentStatus[6] = bq.statusRegister();
switch(currentStatus[0]){
case bq.STAT.UNKNOWN:
Log("Unknown Voltage Source");
break;
case bq.STAT.USB_HOST:
Log("USB Host");
break;
case bq.STAT.ADAPTER_SOURCE:
Log("Adapter Source");
break;
case bq.STAT.OTG_SOURCE:
Log("USB On the Go Source");
break;
}
switch(currentStatus[1]){
case bq.STAT.NCHARGING:
Log("No Charge");
break;
case bq.STAT.PRECHARGE:
Log("Precharging");
break;
case bq.STAT.FAST_CHARGE:
Log("Fast Charging");
break;
case bq.STAT.CHARGE_DONE:
Log("Charging complete");
break;
}
if(currentStatus[2] == bq.STAT.IN_DPM){
Log("In Dynamic Power Management");
}
if(currentStatus[3] == bq.STAT.POWER_NGOOD){
Log("Power is not good");
}
if(currentStatus[4] == bq.STAT.THERMAL_NGOOD){
Log("Thermals are not good");
}
if(currentStatus[5] == bq.STAT.VSYS_REGULATION){
Log("VSYS is in regulation");
}
}
break;
//Charger Int
case 0b00001:
bq.FAULT currentFault[3] = bq.getFault();
if(currentFault[0] != bq.FAULT.NO_FAULT){
Log("Voltage Bus Overloaded");
}
switch(currentFault[1]){
case bq.FAULT.INPUT_FAULT:
Log("Input Fault");
break;
case bq.FAULT.THERMAL_SHUTDOWN:
Log("Thermal Shutdown");
break;
case bq.FAULT.TIMER_EXPIRATION:
Log("Charger Timer Expired");
break;
}
if(currentFault[2] != bq.FAULT.NO_FAULT){
Log("Battery Over-Voltage");
}
break;
}
}
MEM Chip
Actual SD Card
Loading
ili9341-cap-touch
ili9341-cap-touch