/*
 * I2CBB.cpp
 *
 *  Created on: 12 Jun 2020
 *      Author: Ralim
 */
#include "Model_Config.h"
#ifdef I2C_SOFT
#include <I2CBB.hpp>
#include "FreeRTOS.h"
SemaphoreHandle_t I2CBB::I2CSemaphore = NULL;
StaticSemaphore_t I2CBB::xSemaphoreBuffer;
SemaphoreHandle_t I2CBB::I2CSemaphore2 = NULL;
StaticSemaphore_t I2CBB::xSemaphoreBuffer2;
void I2CBB::init() {
	//Set GPIO's to output open drain
	GPIO_InitTypeDef GPIO_InitStruct;
	__HAL_RCC_GPIOA_CLK_ENABLE();
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
	GPIO_InitStruct.Pin = SDA2_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	HAL_GPIO_Init(SDA2_GPIO_Port, &GPIO_InitStruct);
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
	GPIO_InitStruct.Pin = SCL2_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	HAL_GPIO_Init(SCL2_GPIO_Port, &GPIO_InitStruct);
	SOFT_SDA_HIGH();
	SOFT_SCL_HIGH();
	I2CSemaphore = xSemaphoreCreateMutexStatic(&xSemaphoreBuffer);
	I2CSemaphore2 = xSemaphoreCreateMutexStatic(&xSemaphoreBuffer2);
	unlock();
	unlock2();

}

bool I2CBB::probe(uint8_t address) {
	if (!lock())
		return false;
	start();
	bool ack = send(address);
	stop();
	unlock();
	return ack;
}

bool I2CBB::Mem_Read(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData,
		uint16_t Size) {
	if (!lock())
		return false;
	start();
	bool ack = send(DevAddress);
	if (!ack) {
		stop();
		unlock();
		return false;
	}
	ack = send(MemAddress);
	if (!ack) {
		stop();
		unlock();
		return false;
	}
	SOFT_SCL_LOW();
	SOFT_I2C_DELAY();
//	stop();
	start();
	ack = send(DevAddress | 1);
	if (!ack) {
		stop();
		unlock();
		return false;
	}
	while (Size) {
		pData[0] = read(Size > 1);
		pData++;
		Size--;
	}
	stop();
	unlock();
	return true;
}

bool I2CBB::Mem_Write(uint16_t DevAddress, uint16_t MemAddress,
		const uint8_t *pData, uint16_t Size) {
	if (!lock())
		return false;
	start();
	bool ack = send(DevAddress);
	if (!ack) {
		stop();
		asm("bkpt");
		unlock();
		return false;
	}
	ack = send(MemAddress);
	if (!ack) {
		stop();
		asm("bkpt");
		unlock();
		return false;
	}
	while (Size) {
		resetWatchdog();
		ack = send(pData[0]);
		if (!ack) {
			stop();
			asm("bkpt");
			unlock();
			return false;
		}
		pData++;
		Size--;
	}
	stop();
	unlock();
	return true;
}

void I2CBB::Transmit(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
	if (!lock())
		return;
	start();
	bool ack = send(DevAddress);
	if (!ack) {
		stop();
		unlock();
		return;
	}
	while (Size) {
		ack = send(pData[0]);
		if (!ack) {
			stop();
			unlock();
			return;
		}
		pData++;
		Size--;
	}
	stop();
	unlock();

}

void I2CBB::Receive(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
	if (!lock())
		return;
	start();
	bool ack = send(DevAddress | 1);
	if (!ack) {
		stop();
		unlock();
		return;
	}
	while (Size) {
		pData[0] = read(Size > 1);
		pData++;
		Size--;
	}
	stop();
	unlock();
}

void I2CBB::TransmitReceive(uint16_t DevAddress, uint8_t *pData_tx,
		uint16_t Size_tx, uint8_t *pData_rx, uint16_t Size_rx) {
	if (Size_tx == 0 && Size_rx == 0)
		return;
	if (lock() == false)
		return;
	if (Size_tx) {
		start();
		bool ack = send(DevAddress);
		if (!ack) {
			stop();
			unlock();
			return;
		}
		while (Size_tx) {
			ack = send(pData_tx[0]);
			if (!ack) {
				stop();
				unlock();
				return;
			}
			pData_tx++;
			Size_tx--;
		}
	}
	if (Size_rx) {
		start();
		bool ack = send(DevAddress | 1);
		if (!ack) {
			stop();
			unlock();
			return;
		}
		while (Size_rx) {
			pData_rx[0] = read(Size_rx > 1);
			pData_rx++;
			Size_rx--;
		}
	}
	stop();
	unlock();
}

void I2CBB::start() {
	/* I2C Start condition, data line goes low when clock is high */
	SOFT_SCL_HIGH();
	SOFT_SDA_HIGH();
	SOFT_I2C_DELAY();
	SOFT_SDA_LOW();
	SOFT_I2C_DELAY();
	SOFT_SCL_LOW();
	SOFT_I2C_DELAY();
	SOFT_SDA_HIGH();
}

void I2CBB::stop() {
	/* I2C Stop condition, clock goes high when data is low */
	SOFT_SDA_LOW();
	SOFT_I2C_DELAY();
	SOFT_SCL_HIGH();
	SOFT_I2C_DELAY();
	SOFT_SDA_HIGH();
	SOFT_I2C_DELAY();
}

bool I2CBB::send(uint8_t value) {

	for (uint8_t i = 0; i < 8; i++) {
		write_bit(value & 0x80);   // write the most-significant bit
		value <<= 1;
	}

	SOFT_SDA_HIGH();
	bool ack = (read_bit() == 0);
	return ack;
}

uint8_t I2CBB::read(bool ack) {
	uint8_t B = 0;

	uint8_t i;
	for (i = 0; i < 8; i++) {
		B <<= 1;
		B |= read_bit();
	}

	SOFT_SDA_HIGH();
	if (ack)
		write_bit(0);
	else
		write_bit(1);
	return B;
}

uint8_t I2CBB::read_bit() {
	uint8_t b;

	SOFT_SDA_HIGH();
	SOFT_I2C_DELAY();
	SOFT_SCL_HIGH();
	SOFT_I2C_DELAY();

	if (SOFT_SDA_READ())
		b = 1;
	else
		b = 0;

	SOFT_SCL_LOW();
	return b;
}

void I2CBB::unlock() {
	xSemaphoreGive(I2CSemaphore);
}

bool I2CBB::lock() {
	if (I2CSemaphore == NULL) {
		asm("bkpt");
	}
	bool a = xSemaphoreTake(I2CSemaphore, (TickType_t) 100) == pdTRUE;
	return a;
}

void I2CBB::write_bit(uint8_t val) {
	if (val) {
		SOFT_SDA_HIGH();
	} else {
		SOFT_SDA_LOW();
	}

	SOFT_I2C_DELAY();
	SOFT_SCL_HIGH();
	SOFT_I2C_DELAY();
	SOFT_SCL_LOW();
}

void I2CBB::unlock2() {
	xSemaphoreGive(I2CSemaphore2);
}

bool I2CBB::lock2() {
	if (I2CSemaphore2 == NULL) {
		asm("bkpt");
	}
	bool a = xSemaphoreTake(I2CSemaphore2, (TickType_t) 500) == pdTRUE;

	return a;
}
#endif
