/* i2c.c - non interrupt driven I2C for DS1085L and others, uses timer 2.
 *  Copyright (C) 2022 by Jed Marti
 *   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 https://www.gnu.org/licenses/.
 */

#include <C8051F380.h>
#include <stdint.h>

#include "config.h"
#include "lfxmit.h"
#include "storage.h"
#include "i2c.h"
#include "ds1085.h"
#include "serial.h"

// Count of bytes to send
__xdata uint8_t i2c_ocnt;

// Bytes to send.
__xdata uint8_t i2c_cmdbuf[3];
__xdata uint8_t i2c_index;

// Address + R/W
__xdata uint8_t i2caddr;

//
extern uint16_t           DREG;
extern __bit              errorThrown;
extern __xdata uint8_t    D[];
extern __xdata floatOrInt IACC;




void initI2C(void)
// Note that init51.c has already initialized the cross bar for I2C. We run it
// at 100k since we're not doing much.
{
  // Turn off, mode0 16 bit with reload.
  TMR2CN = 0;

  // Should give us 100khtz.
  TMR2 = TMR2RL = 65536 - 400;
  TR2 = 1;

  // Timer 2 16 bit overflow at 100 khtz, and turn it on. Not interrupting though.
  SMB0CF = 0xC2;

}


static void sendI2CByte(uint8_t v)
{
  SMB0DAT = v;
  SI = 0;
  while (!SI);
}


void sendI2C(uint8_t reg, uint8_t b1, uint8_t b2)
// Send 2 bytes to the DS1085L. LED0 signals busy. Returns I2CIDLE (for success)
// or some sort of error message.
{
  LED0 = 1;
  SMB0CN = 0xE0;     // Master, transmit, initiate start.
  while (!SI);
  STA = 0;           // Clear the start.

  sendI2CByte(DS1085);  // the DS1085 address.
  sendI2CByte(reg);     // The register to read/write.
  sendI2CByte(b1);
  sendI2CByte(b2);
  
  STO = 1;
  while (!SI);
  SI = 0;
  LED0 = 0;
}



void sendI2C16(uint8_t cmd, uint16_t v)
//     Send 2 bytes - an abbreviation for sending uint16_t.
{
  sendI2C(cmd, v >> 8, v & 0xFF);
}



void sendI2C8(uint8_t reg, uint8_t b)
//     Send a single byte.
{
  LED0 = 1;
  SMB0CN = 0xE0;
  while (!SI);
  STA = 0;

  sendI2CByte(DS1085);
  sendI2CByte(reg);
  sendI2CByte(b);

  STO = 1;
  while (!SI);
  SI = 0;
  LED0 = 0;
}

  

void readI2C16(uint8_t reg, uint8_t *b1, uint8_t *b2)
// Read 16 bits - doesn't seem to work but here anyway.
{
  //  putstring("read "); puthexbyte(reg); putch(' ');
  LED0 = 1;

  /*  STA = 1;
  while (!SI);
 
  STA = 0;
  SMB0DAT = DS1085;
  SI = 0;
  while (!SI);
  if (!ACK)
  {
    putstring("No ACK on read\n");
    return;
  }
  SMB0DAT = reg;
  SI = 0;
  while (!SI);
  if (!ACK)
  {
    putstring("No ACK on read @\n");
    return;
  }
  */

  SMB0CN = 0xE0;
  while (!SI);
  STA = 0;
  sendI2CByte(DS1085);
  sendI2CByte(reg);
  
  // Repeated start.
  STA = 1;
  while (!SI);
  STA = 0;
  if (!ACK)
  {
    putstring("No ACK on repeated start\n");
    return;
  }
  SMB0DAT = DS1085 + 1;       // Compiler hates this but gets it right.
  SI = 0;
  while (!SI);
  if (!ACK)
  {
    putstring("No ACK on repeated read @\n");
    return;
  }
  SI = 0;                     // Switches to read mode.
  while (!SI);
  //puthexbyte(SMB0DAT);
  *b1 = SMB0DAT;
  ACK = 1;
  SI = 0;
  while (!SI);
  // putch(' '); puthexbyte(SMB0DAT); putch('\n');
  *b2 = SMB0DAT;
  ACK = 0;
  SI = 0;
  STO = 1;
  while (SMB0CF & 0x20);
  //  puthexbyte(SMB0CF); putch(' '); puthexbyte(SMB0CN); putch(' ');
  //  puthexbyte(SMB0DAT); putch('\n');
  LED0 = 0;
}


void runWriteI2C(void)
//     We only come here when doit is true. This is called from runWrite (files.c).
//        WRITE <fh>, <port>, <bytevector>;
{
  LED0 = 1;
  if (getPByte() == COMMA) incPC();

  // This should be the port number.
  parseExpression();
  DREG = popST();
  if (tag() != DESC_INT)
  {
    putstring("I2C port # bad\n");
    errorThrown = 1;
    return;
  }
  getIACC();
  i2caddr = IACC.i8.b8a;

  if (getPByte() == COMMA) incPC();
  parseExpression();
  DREG = popST();
  if (tag() != DESC_BYTEVECTOR)
  {
    putstring("WRITE I2C not ByteVector\n");
    errorThrown = 1;
    return;
  }

  LED0 = 1;
  SMB0CN = 0xE0;
  while (!SI);
  STA = 0;

  sendI2CByte(i2caddr);
  i2caddr = D[DREG + 3];
  for (IACC.i8.b8a = 0; IACC.i8.b8a < i2caddr; ++IACC.i8.b8a)
    sendI2CByte(D[DREG + 4 + IACC.i8.b8a]);

  STO = 1;
  while (!SI);
  SI = 0;
  LED0 = 0;
}
    
  
  

  


// EOF
