//-----------------------------------------------------------------------------
//  FILE:   I2C master.c
//-----------------------------------------------------------------------------
//  Copyright 2022 Sonix Technology Corp. All rights reserved. 
//
//  AUTHOR:     Sonix
//  DATE:       2022/05/30
//
//  The program is an example of I2C master to implement it.
//
//  I2C decription:
//      1. User need to external pull-high at SDA and SCL pin
//      2. Mast Send data (0x55¡X0xBB¡X0xCC) to slave
//
//  The system clock frequency is IHRC 32MHz / 4
//
//  Device:     SN8F5959
//  Tool chain: KEIL C51 V9.54
//

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#define __XRAM_SFR_H__
#include <SN8F5959.H>
#include "GenericTypeDefs.h"
#include <intrins.h>
#include <string.h>

//-----------------------------------------------------------------------------
// Global Constants 
//-----------------------------------------------------------------------------
#define EN_STO      I2CCON |= 0x10
#define MAXDATALEN  10

//-----------------------------------------------------------------------------
// Global Variables 
//-----------------------------------------------------------------------------
UINT8 u8I2CFlag = 0;
UINT8 u8I2CData[MAXDATALEN] = {0};
UINT8 u8I2CAddr = 0;
UINT8 u8I2CLen = 0;
UINT8 u8I2CCnt = 0;

//UINT8 u8I2CStatLog[20] = {0};
unsigned char u8I2CStatLog[20] = {0};

UINT8 u8StatLogCnt = 0;
//-----------------------------------------------------------------------------
// Function Prototypes 
//-----------------------------------------------------------------------------
void I2C_Init(void);
void I2C_ISR(void); 
void GPIO_Init(void);

UINT8 I2C_Master_Write_One_Byte(UINT8 u8Addr, UINT8 u8data);
UINT8 I2C_Master_Write_N_Byte(UINT8 u8Addr, UINT8 *u8data, UINT8 u8size);
UINT8 I2C_Master_Read_N_Byte(UINT8 u8Addr, UINT8 *u8data, UINT8 u8size);

//-----------------------------------------------------------------------------
// Main loop
//-----------------------------------------------------------------------------
void main(void) 
{
    UINT8 u8TxData[3] = {0x55, 0xBB, 0xCC};     // for test
    UINT8 u8RxData[5] = {0};
        
    UINT8 u8SlaveAddr = 0x70;                   // set addr -- for Mast tx
//  UINT8 u8SlaveAddr = 0x70;                   // set addr -- for Mast tx    FOR    DS
//  UINT8 u8SlaveAddr = 0x2D;                   // set addr -- for Mast rx

    WDTR = 0x5A;               // clear watchdog if watchdog enable
    
    GPIO_Init();
    I2C_Init();    
/*
    // Write one data to slave
    I2C_Master_Write_One_Byte(u8SlaveAddr, u8TxData[0]);
    I2C_Master_Write_One_Byte(u8SlaveAddr, u8TxData[1]);
    I2C_Master_Write_One_Byte(u8SlaveAddr, u8TxData[2]);

    // Write multi data to slave
    I2C_Master_Write_N_Byte(u8SlaveAddr, u8TxData, sizeof(u8TxData));

    // read multi data form slave
    I2C_Master_Read_N_Byte(u8SlaveAddr, u8RxData, 2);
    I2C_Master_Read_N_Byte(u8SlaveAddr, u8RxData, sizeof(u8RxData));
*/
    while (1) {
        WDTR = 0x5A;        // clear watchdog if watchdog enable

        // To Do...
        I2C_Master_Write_N_Byte(u8SlaveAddr, u8TxData, sizeof(u8TxData));//for Mast tx
        //I2C_Master_Read_N_Byte(u8SlaveAddr, u8RxData, sizeof(u8RxData));//for Mast rx
    }
}
//-----------------------------------------------------------------------------
//  Function:   void GPIO_Init(void)
//  Description:
//          GPIO P4 Output
//-----------------------------------------------------------------------------
void GPIO_Init(void)
{
    P4 = 0x00;                      // Initial P4 = 0x00
    P4M |= 0xFF;                    // Set P4 output
}
//-----------------------------------------------------------------------------
//  Function:   void I2C_Init(void)
//  Description:
//          SCL and SDA need to external pull high
//-----------------------------------------------------------------------------
void I2C_Init(void)
{
    CLKSEL = 0x05;                  // set Fcpu = Fhosc/4  (8M Hz)        
    CLKCMD = 0x69;                  // Apply CLKSEL¡¦s setting

    P02 = 0;
    P03 = 0;
    P0M |= 0x00;                    // P02 & P03 as input

    I2CCON |= 0x40;                 // I2C enable (ENS1)

    //select clk rate
    //I2CCON |= 0x01;               // Clock rate bit : 001b ; 142.9KHz at 32MHz
    //I2CCON |= 0x03;               // Clock rate bit : 011b ; 200KHz at 32MHz
    //I2CCON |= 0x81;               // Clock rate bit : 101b ; 226.7KHz at 32MHz
    I2CCON |= 0x82;                 // Clock rate bit : 110b ; 533.3KHz at 32MHz
        
    EI2C = 1;                       // I2C interrupt enable
    EAL = 1;                        // Interrupt enable
}

//-----------------------------------------------------------------------------
//  Function:   UINT8 I2C_Master_Write_One_Byte(UINT8 u8Addr, UINT8 u8Data)
//  Description:
//
//-----------------------------------------------------------------------------
UINT8 I2C_Master_Write_One_Byte(UINT8 u8Addr, UINT8 u8Data)
{
    if (!u8I2CFlag) {
        u8I2CFlag = 1;              // get Use Right            
        u8I2CLen = 1;               // set length
        u8I2CCnt = 0;               // clear tx count
        u8I2CData[0] = u8Data;
        u8I2CAddr = u8Addr | 0x00;  // write mode                
        I2CCON |= 0x20;             // START (STA) = 1

        while(u8I2CFlag);           // wait Trasmit end

        return TRUE;
    }
    else
        return FALSE;
}

//-----------------------------------------------------------------------------
//  Function:   UINT8 I2C_Master_Write_N_Byte(UINT8 u8Addr, UINT8 *u8data, UINT8 u8size)
//  Description:
//
//-----------------------------------------------------------------------------
UINT8 I2C_Master_Write_N_Byte(UINT8 u8Addr, UINT8 *u8data, UINT8 u8size)
{
    if (u8size >= MAXDATALEN)    
        return FALSE;               // over buf size
    
    if (u8size == 0)    
        return FALSE;               // error size
    
    if (!u8I2CFlag) {
        u8I2CFlag = 1;              // get Use Right
        u8I2CLen = u8size;          // set length
        u8I2CCnt = 0;               // clear tx count
        u8I2CAddr = u8Addr | 0x00;  // write mode            
        memcpy(u8I2CData, u8data, u8size);
        I2CCON |= 0x20;             // START (STA) = 1

        while(u8I2CFlag);           // wait Trasmit end

        return TRUE;
    }
    else
        return FALSE;
}

//-----------------------------------------------------------------------------
//  Function:   UINT8 I2C_Master_Read_N_Byte(UINT8 u8Addr, UINT8 *u8data, UINT8 u8size)
//  Description:
//
//-----------------------------------------------------------------------------
UINT8 I2C_Master_Read_N_Byte(UINT8 u8Addr, UINT8 *u8data, UINT8 u8size)
{
    if (u8size >= MAXDATALEN)    
        return FALSE;               // over buf size
    if (u8size == 0)    
        return FALSE;               // error size
    if (!u8I2CFlag) {
        u8I2CFlag = 1;              // get Use Right
        u8I2CLen = u8size;          // set length
        u8I2CCnt = 0;
        u8I2CAddr = u8Addr | 0x01;  // read mode
        I2CCON |= 0x20;             // START (STA) = 1
    
        while(u8I2CFlag);           // wait recevier end
    
        memcpy(u8data, u8I2CData, u8size);
    
        return TRUE;
    }
    else
        return FALSE;
}

//-----------------------------------------------------------------------------
//  Function:   I2C interrupt
//  Description:      
//
//-----------------------------------------------------------------------------
void I2C_ISR(void) interrupt ISRI2c     // Vector @  0xAB
{  
    P4 = I2CSTA;
    //u8I2CStatLog[u8StatLogCnt++] = I2CSTA;
    switch (I2CSTA)
    {
        // tx mode
        case 0x08:
            I2CCON &= 0xDF;             // START (STA) = 0
            I2CDAT = u8I2CAddr;         // Tx/Rx addr             
            break;
                
        case 0x18:  

                        I2CDAT = u8I2CData[u8I2CCnt++];    
              u8I2CLen--;                 
            break;
                
        case 0x28:  
            if (u8I2CLen == 0) {        // chk length if empty
                I2CCON |= 0x10;         // STOP (STO)
                u8I2CFlag = 0;
                u8I2CCnt = 0;    
            }
            else {
                I2CDAT = u8I2CData[u8I2CCnt++];
                u8I2CLen--;
            }                            
            break;
                        
        case 0x30:                          // Arbitration lost  
            I2CCON |= 0x10;                 // STOP (STO)
            u8I2CFlag = 0;  
            break;  
                
        //    rx mode       
        case 0x40:                          // get slave addr
            if((u8I2CCnt+1) == u8I2CLen){
                    I2CCON &= 0xFB;         // AA = 0    (last byte,not ACK will be returned)                            
            }        
            else{   
                    I2CCON |= 0x04;         // AA = 1     (not last byte,ACK will be returned)    
            }        
            break;
        case 0x50:                          // read n byte
            u8I2CData[u8I2CCnt++] = I2CDAT;                     
            if((u8I2CCnt+1) == u8I2CLen){
                    I2CCON &= 0xFB;         // AA = 0    (last byte,not ACK will be returned)                            
            }        
            else{   
                    I2CCON |= 0x04;         // AA = 1     (not last byte,ACK will be returned)    
            }        
            break;  
        case 0x58:                          // read last byte & stop
            u8I2CData[u8I2CCnt] = I2CDAT; 
            I2CCON |= 0x10;                 // STOP (STO)
            u8I2CFlag = 0;  
            break;  
        default:    
            I2CCON |= 0x10;                 // STOP (STO)
            u8I2CFlag = 0;  
    }   
    
    I2CCON &= 0xF7;                         // Clear I2C flag (SI)        
}
