/******************************************************************************
 * @file      drv_adbms1818.c
 * @brief     LT6820-ADBMS1818�ջ���оƬ�ɼ�
 * @version   V1.0
 * @author    Gary
 * @copyright
 ******************************************************************************/
#include "drv_adbms1818.h"
bool isospi_reverse = true;

const uint16_t crc15Table[256] = {0x0, 0xc599, 0xceab, 0xb32, 0xd8cf, 0x1d56, 0x1664, 0xd3fd, 0xf407, 0x319e, 0x3aac, // precomputed CRC15 Table
								  0xff35, 0x2cc8, 0xe951, 0xe263, 0x27fa, 0xad97, 0x680e, 0x633c, 0xa6a5, 0x7558, 0xb0c1,
								  0xbbf3, 0x7e6a, 0x5990, 0x9c09, 0x973b, 0x52a2, 0x815f, 0x44c6, 0x4ff4, 0x8a6d, 0x5b2e,
								  0x9eb7, 0x9585, 0x501c, 0x83e1, 0x4678, 0x4d4a, 0x88d3, 0xaf29, 0x6ab0, 0x6182, 0xa41b,
								  0x77e6, 0xb27f, 0xb94d, 0x7cd4, 0xf6b9, 0x3320, 0x3812, 0xfd8b, 0x2e76, 0xebef, 0xe0dd,
								  0x2544, 0x2be, 0xc727, 0xcc15, 0x98c, 0xda71, 0x1fe8, 0x14da, 0xd143, 0xf3c5, 0x365c,
								  0x3d6e, 0xf8f7, 0x2b0a, 0xee93, 0xe5a1, 0x2038, 0x7c2, 0xc25b, 0xc969, 0xcf0, 0xdf0d,
								  0x1a94, 0x11a6, 0xd43f, 0x5e52, 0x9bcb, 0x90f9, 0x5560, 0x869d, 0x4304, 0x4836, 0x8daf,
								  0xaa55, 0x6fcc, 0x64fe, 0xa167, 0x729a, 0xb703, 0xbc31, 0x79a8, 0xa8eb, 0x6d72, 0x6640,
								  0xa3d9, 0x7024, 0xb5bd, 0xbe8f, 0x7b16, 0x5cec, 0x9975, 0x9247, 0x57de, 0x8423, 0x41ba,
								  0x4a88, 0x8f11, 0x57c, 0xc0e5, 0xcbd7, 0xe4e, 0xddb3, 0x182a, 0x1318, 0xd681, 0xf17b,
								  0x34e2, 0x3fd0, 0xfa49, 0x29b4, 0xec2d, 0xe71f, 0x2286, 0xa213, 0x678a, 0x6cb8, 0xa921,
								  0x7adc, 0xbf45, 0xb477, 0x71ee, 0x5614, 0x938d, 0x98bf, 0x5d26, 0x8edb, 0x4b42, 0x4070,
								  0x85e9, 0xf84, 0xca1d, 0xc12f, 0x4b6, 0xd74b, 0x12d2, 0x19e0, 0xdc79, 0xfb83, 0x3e1a, 0x3528,
								  0xf0b1, 0x234c, 0xe6d5, 0xede7, 0x287e, 0xf93d, 0x3ca4, 0x3796, 0xf20f, 0x21f2, 0xe46b, 0xef59,
								  0x2ac0, 0xd3a, 0xc8a3, 0xc391, 0x608, 0xd5f5, 0x106c, 0x1b5e, 0xdec7, 0x54aa, 0x9133, 0x9a01,
								  0x5f98, 0x8c65, 0x49fc, 0x42ce, 0x8757, 0xa0ad, 0x6534, 0x6e06, 0xab9f, 0x7862, 0xbdfb, 0xb6c9,
								  0x7350, 0x51d6, 0x944f, 0x9f7d, 0x5ae4, 0x8919, 0x4c80, 0x47b2, 0x822b, 0xa5d1, 0x6048, 0x6b7a,
								  0xaee3, 0x7d1e, 0xb887, 0xb3b5, 0x762c, 0xfc41, 0x39d8, 0x32ea, 0xf773, 0x248e, 0xe117, 0xea25,
								  0x2fbc, 0x846, 0xcddf, 0xc6ed, 0x374, 0xd089, 0x1510, 0x1e22, 0xdbbb, 0xaf8, 0xcf61, 0xc453,
								  0x1ca, 0xd237, 0x17ae, 0x1c9c, 0xd905, 0xfeff, 0x3b66, 0x3054, 0xf5cd, 0x2630, 0xe3a9, 0xe89b,
								  0x2d02, 0xa76f, 0x62f6, 0x69c4, 0xac5d, 0x7fa0, 0xba39, 0xb10b, 0x7492, 0x5368, 0x96f1, 0x9dc3,
								  0x585a, 0x8ba7, 0x4e3e, 0x450c, 0x8095};

void spi_write_array(uint8_t len, uint8_t writeData[])
{
	uint8_t i = 0;
	for (i = 0; i < len; i++) // ��ѭ����writeData[] �������������һһ���ͳ�ȥ
	{
		if (isospi_reverse == false) // �ж�ѡ��ľջ���ͨ�ŷ����������������Է���
		{

			drv_spi_sync_send_rev(kSpiDev_1, (int8_t)writeData[i]);
		}
		else
		{
			drv_spi_sync_send_rev(kSpiDev_1, (int8_t)writeData[i]);
		}
	}
}

uint8_t spi_read_byte(uint8_t data)
{
	if (isospi_reverse == false) // �ж�ѡ��ľջ���ͨ�ŷ����������������Է���
	{

		return drv_spi_sync_send_rev(kSpiDev_1, data);
	}
	else
	{
		return drv_spi_sync_send_rev(kSpiDev_1, data);
	}
}

void spi_write_read(uint8_t tx_Data[], // array of data to be written on SPI port
					uint8_t tx_len,	   // length of the tx data arry
					uint8_t *rx_data,  // Input: array that will store the data read by the SPI port
					uint8_t rx_len	   // Option: number of bytes to be read from the SPI port
)
{

	uint8_t i = 0;
	for (i = 0; i < tx_len; i++) // ��ѭ����tx_Data[] �������������һһ���ͳ�ȥ
	{
		if (!isospi_reverse) // �ж�ѡ��ľջ���ͨ�ŷ����������������Է���
		{
			drv_spi_sync_send_rev(kSpiDev_1, tx_Data[i]);
		}
		else
		{
			drv_spi_sync_send_rev(kSpiDev_1, tx_Data[i]);
		}
	}

	for (i = 0; i < rx_len; i++) // ��ѭ��������������һһ����rx_data[] ������
	{
		if (!isospi_reverse) // �ж�ѡ��ľջ���ͨ�ŷ����������������Է���
		{
			rx_data[i] = (uint8_t)drv_spi_sync_send_rev(kSpiDev_1, 0x00);
		}
		else
		{
			rx_data[i] = (uint8_t)drv_spi_sync_send_rev(kSpiDev_1, 0xFF);
		}
	}
}
// �ӿ����л���׼��״̬
void wakeup_idle(uint8_t total_ic) // Number of ICs in the system
{
	if (!isospi_reverse)
	{
		for (int i = 0; i < total_ic; i++)
		{
			cs_low();
			drv_spi_sync_send_rev(kSpiDev_1, 0xff); // Guarantees the isoSPI will be in ready mode
			cs_high();
		}
	}
	else
	{
		for (int i = 0; i < total_ic; i++)
		{
			cs_low_reverse();
			drv_spi_sync_send_rev(kSpiDev_1, 0xff); // Guarantees the isoSPI will be in ready mode
			cs_high_reverse();
		}
	}
}

// ��˯��״̬����
void wakeup_sleep(uint8_t total_ic) // Number of ICs in the system
{
	if (!isospi_reverse)
	{
		for (int i = 0; i < total_ic; i++)
		{
			cs_low();
			kit_time_dly_us(300); // Guarantees the ADBMS181x will be in standby
			cs_high();
			kit_time_dly_us(300);
		}
	}
	else
	{
		for (int i = 0; i < total_ic; i++)
		{
			cs_low_reverse();
			kit_time_dly_us(300); // Guarantees the ADBMS181x will be in standby
			cs_high_reverse();
			kit_time_dly_us(300);
		}
	}
}

/* Calculates  and returns the CRC15 */
uint16_t pec15_calc(uint8_t len,  // Number of bytes that will be used to calculate a PEC
					uint8_t *data // Array of data that will be used to calculate  a PEC
)
{
	uint16_t remainder, addr;
	remainder = 16; // initialize the PEC

	for (uint8_t i = 0; i < len; i++) // loops for each byte in data array
	{
		addr = ((remainder >> 7) ^ data[i]) & 0xff; // calculate PEC table address
		remainder = (remainder << 8) ^ crc15Table[addr];
	}

	return (remainder * 2); // The CRC15 has a 0 in the LSB so the remainder must be multiplied by 2
}

void write_68(uint8_t total_ic,	 // Number of ICs to be written to
			  uint8_t tx_cmd[2], // The command to be transmitted
			  uint8_t data[]	 // Payload Data
)
{
	const uint8_t BYTES_IN_REG = 6;
	const uint8_t CMD_LEN = 4 + (8 * total_ic);
	uint8_t *cmd;
	uint16_t data_pec;
	uint16_t cmd_pec;
	uint8_t cmd_index;

	cmd = (uint8_t *)malloc(CMD_LEN * sizeof(uint8_t));
	cmd[0] = tx_cmd[0];
	cmd[1] = tx_cmd[1];
	cmd_pec = pec15_calc(2, cmd);
	cmd[2] = (uint8_t)(cmd_pec >> 8);
	cmd[3] = (uint8_t)(cmd_pec);

	cmd_index = 4;
	for (uint8_t current_ic = total_ic; current_ic > 0; current_ic--) // Executes for each ADBMS181x, this loops starts with the last IC on the stack.
	{																  // The first configuration written is received by the last IC in the daisy chain
		for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++)
		{
			cmd[cmd_index] = data[((current_ic - 1) * 6) + current_byte];
			cmd_index = cmd_index + 1;
		}

		data_pec = (uint16_t)pec15_calc(BYTES_IN_REG, &data[(current_ic - 1) * 6]); // Calculating the PEC for each ICs configuration register data
		cmd[cmd_index] = (uint8_t)(data_pec >> 8);
		cmd[cmd_index + 1] = (uint8_t)data_pec;
		cmd_index = cmd_index + 2;
	}
	cs_low();
	spi_write_array(CMD_LEN, cmd);
	cs_high();
	free(cmd);
}

/* Write the ADBMS181x CFGRA */
void ADBMS1818_wrcfg(uint8_t total_ic, // The number of ICs being written to
					 cell_asic ic[]	   // A two dimensional array of the configuration data that will be written
)
{
	uint8_t cmd[2] = {0x00, 0x01};
	uint8_t write_buffer[256];
	uint8_t write_count = 0;
	uint8_t c_ic = 0;

	for (uint8_t current_ic = 0; current_ic < total_ic; current_ic++)
	{
		if (ic->isospi_reverse == false)
		{
			c_ic = current_ic;
		}
		else
		{
			c_ic = total_ic - current_ic - 1;
		}

		for (uint8_t data = 0; data < 6; data++)
		{
			write_buffer[write_count] = ic[c_ic].config.tx_data[data];
			write_count++;
		}
	}
	write_68(total_ic, cmd, write_buffer);
}

/* Write the ADBMS181x CFGRB */
void ADBMS1818_wrcfgb(uint8_t total_ic, // The number of ICs being written to
					  cell_asic ic[]	// A two dimensional array of the configuration data that will be written
)
{
	uint8_t cmd[2] = {0x00, 0x24};
	uint8_t write_buffer[256];
	uint8_t write_count = 0;
	uint8_t c_ic = 0;

	for (uint8_t current_ic = 0; current_ic < total_ic; current_ic++)
	{
		if (ic->isospi_reverse == false)
		{
			c_ic = current_ic;
		}
		else
		{
			c_ic = total_ic - current_ic - 1;
		}

		for (uint8_t data = 0; data < 6; data++)
		{
			write_buffer[write_count] = ic[c_ic].configb.tx_data[data];
			write_count++;
		}
	}
	write_68(total_ic, cmd, write_buffer);
}

/* Generic function to write 68xx commands and read data. Function calculated PEC for tx_cmd data */
int8_t read_68(uint8_t total_ic,  // Number of ICs in the system
			   uint8_t tx_cmd[2], // The command to be transmitted
			   uint8_t *rx_data	  // Data to be read
)
{
	const uint8_t BYTES_IN_REG = 8;
	uint8_t cmd[4];
	uint8_t data[256];
	int8_t pec_error = 0;
	uint16_t cmd_pec;
	uint16_t data_pec;
	uint16_t received_pec;

	cmd[0] = tx_cmd[0];
	cmd[1] = tx_cmd[1];
	cmd_pec = pec15_calc(2, cmd);
	cmd[2] = (uint8_t)(cmd_pec >> 8);
	cmd[3] = (uint8_t)(cmd_pec);

	cs_low();
	spi_write_read(cmd, 4, data, (BYTES_IN_REG * total_ic)); // Transmits the command and reads the configuration data of all ICs on the daisy chain into rx_data[] array
	cs_high();

	for (uint8_t current_ic = 0; current_ic < total_ic; current_ic++) // Executes for each ADBMS181x in the daisy chain and packs the data
	{																  // into the rx_data array as well as check the received data for any bit errors
		for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++)
		{
			rx_data[(current_ic * 8) + current_byte] = data[current_byte + (current_ic * BYTES_IN_REG)];
		}

		received_pec = (rx_data[(current_ic * 8) + 6] << 8) + rx_data[(current_ic * 8) + 7];
		data_pec = pec15_calc(6, &rx_data[current_ic * 8]);

		if (received_pec != data_pec)
		{
			pec_error = -1;
		}
	}

	return (pec_error);
}

/* Helper function that increments PEC counters */
void ADBMS181x_check_pec(uint8_t total_ic, // Number of ICs in the system
						 uint8_t reg,	   // Type of Register
						 cell_asic *ic	   // A two dimensional array that stores the data
)
{
	switch (reg)
	{
	case CFGR:
		for (int current_ic = 0; current_ic < total_ic; current_ic++)
		{
			ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + ic[current_ic].config.rx_pec_match;
			ic[current_ic].crc_count.cfgr_pec = ic[current_ic].crc_count.cfgr_pec + ic[current_ic].config.rx_pec_match;
		}
		break;

	case CFGRB:
		for (int current_ic = 0; current_ic < total_ic; current_ic++)
		{
			ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + ic[current_ic].configb.rx_pec_match;
			ic[current_ic].crc_count.cfgr_pec = ic[current_ic].crc_count.cfgr_pec + ic[current_ic].configb.rx_pec_match;
		}
		break;
	case CELL:
		for (int current_ic = 0; current_ic < total_ic; current_ic++)
		{
			for (int i = 0; i < ic[0].ic_reg.num_cv_reg; i++)
			{
				ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + ic[current_ic].cells.pec_match[i];
				ic[current_ic].crc_count.cell_pec[i] = ic[current_ic].crc_count.cell_pec[i] + ic[current_ic].cells.pec_match[i];
			}
		}
		break;
	case AUX:
		for (int current_ic = 0; current_ic < total_ic; current_ic++)
		{
			for (int i = 0; i < ic[0].ic_reg.num_gpio_reg; i++)
			{
				ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + (ic[current_ic].aux.pec_match[i]);
				ic[current_ic].crc_count.aux_pec[i] = ic[current_ic].crc_count.aux_pec[i] + (ic[current_ic].aux.pec_match[i]);
			}
		}

		break;
	case STAT:
		for (int current_ic = 0; current_ic < total_ic; current_ic++)
		{

			for (int i = 0; i < ic[0].ic_reg.num_stat_reg - 1; i++)
			{
				ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + ic[current_ic].stat.pec_match[i];
				ic[current_ic].crc_count.stat_pec[i] = ic[current_ic].crc_count.stat_pec[i] + ic[current_ic].stat.pec_match[i];
			}
		}
		break;
	default:
		break;
	}
}

/* Read the ADBMS181x CFGA */
int8_t ADBMS1818_rdcfg(uint8_t total_ic, // Number of ICs in the system
					   cell_asic ic[]	 // A two dimensional array that the function stores the read configuration data.
)
{
	uint8_t cmd[2] = {0x00, 0x02};
	uint8_t read_buffer[256];
	int8_t pec_error = 0;
	uint16_t data_pec;
	uint16_t calc_pec;
	uint8_t c_ic = 0;

	pec_error = read_68(total_ic, cmd, read_buffer);

	for (uint8_t current_ic = 0; current_ic < total_ic; current_ic++)
	{
		if (ic->isospi_reverse == false)
		{
			c_ic = current_ic;
		}
		else
		{
			c_ic = total_ic - current_ic - 1;
		}

		for (int byte = 0; byte < 8; byte++)
		{
			ic[c_ic].config.rx_data[byte] = read_buffer[byte + (8 * current_ic)];
		}

		calc_pec = pec15_calc(6, &read_buffer[8 * current_ic]);
		data_pec = read_buffer[7 + (8 * current_ic)] | (read_buffer[6 + (8 * current_ic)] << 8);
		if (calc_pec != data_pec)
		{
			ic[c_ic].config.rx_pec_match = 1;
		}
		else
			ic[c_ic].config.rx_pec_match = 0;
	}
	ADBMS181x_check_pec(total_ic, CFGR, ic);

	return (pec_error);
}

/* Reads the ADBMS181x CFGB */
int8_t ADBMS1818_rdcfgb(uint8_t total_ic, // Number of ICs in the system
						cell_asic ic[]	  // A two dimensional array that the function stores the read configuration data.
)
{
	uint8_t cmd[2] = {0x00, 0x26};
	uint8_t read_buffer[256];
	int8_t pec_error = 0;
	uint16_t data_pec;
	uint16_t calc_pec;
	uint8_t c_ic = 0;

	pec_error = read_68(total_ic, cmd, read_buffer);

	for (uint8_t current_ic = 0; current_ic < total_ic; current_ic++)
	{
		if (ic->isospi_reverse == false)
		{
			c_ic = current_ic;
		}
		else
		{
			c_ic = total_ic - current_ic - 1;
		}

		for (int byte = 0; byte < 8; byte++)
		{
			ic[c_ic].configb.rx_data[byte] = read_buffer[byte + (8 * current_ic)];
		}

		calc_pec = pec15_calc(6, &read_buffer[8 * current_ic]);
		data_pec = read_buffer[7 + (8 * current_ic)] | (read_buffer[6 + (8 * current_ic)] << 8);
		if (calc_pec != data_pec)
		{
			ic[c_ic].configb.rx_pec_match = 1;
		}
		else
			ic[c_ic].configb.rx_pec_match = 0;
	}
	ADBMS181x_check_pec(total_ic, CFGRB, ic);

	return (pec_error);
}
/* Generic function to write 68xx commands. Function calculates PEC for tx_cmd data. */
void cmd_68(uint8_t tx_cmd[2]) // The command to be transmitted
{
	uint8_t cmd[4];
	uint16_t cmd_pec;
	//uint8_t md_bits;

	cmd[0] = tx_cmd[0];
	cmd[1] = tx_cmd[1];
	cmd_pec = pec15_calc(2, cmd);
	cmd[2] = (uint8_t)(cmd_pec >> 8);
	cmd[3] = (uint8_t)(cmd_pec);

	cs_low();
	spi_write_array(4, cmd);
	cs_high();
}

/* Starts ADC conversion for cell voltage */
void ADBMS1818_adcv(uint8_t MD,	 // ADC Mode
					uint8_t DCP, // Discharge Permit
					uint8_t CH	 // Cell Channels to be measured
)
{
	uint8_t cmd[2];
	uint8_t md_bits;

	md_bits = (MD & 0x02) >> 1;
	cmd[0] = md_bits + 0x02;
	md_bits = (MD & 0x01) << 7;
	cmd[1] = md_bits + 0x60 + (DCP << 4) + CH;

	cmd_68(cmd);
}

/* This function will block operation until the ADC has finished it's conversion */
uint32_t ADBMS1818_pollAdc()
{
	uint32_t counter = 0;
	uint8_t finished = 0;
	uint8_t current_time = 0;
	uint8_t cmd[4];
	uint16_t cmd_pec;

	cmd[0] = 0x07;
	cmd[1] = 0x14;
	cmd_pec = pec15_calc(2, cmd);
	cmd[2] = (uint8_t)(cmd_pec >> 8);
	cmd[3] = (uint8_t)(cmd_pec);

	cs_low();
	spi_write_array(4, cmd);
	while ((counter < 200000) && (finished == 0))
	{
		current_time = spi_read_byte(0xff);
		if (current_time > 0)
		{
			finished = 1;
		}
		else
		{
			counter = counter + 10;
		}
	}
	cs_high();

	return (counter);
}

/* Writes the command and reads the raw cell voltage register data */
void ADBMS181x_rdcv_reg(uint8_t reg,	  // Determines which cell voltage register is read back
						uint8_t total_ic, // the number of ICs in the
						uint8_t *data	  // An array of the unparsed cell codes
)
{
	const uint8_t REG_LEN = 8; // Number of bytes in each ICs register + 2 bytes for the PEC
	uint8_t cmd[4];
	uint16_t cmd_pec;

	if (reg == 1) // 1: RDCVA
	{
		cmd[1] = 0x04;
		cmd[0] = 0x00;
	}
	else if (reg == 2) // 2: RDCVB
	{
		cmd[1] = 0x06;
		cmd[0] = 0x00;
	}
	else if (reg == 3) // 3: RDCVC
	{
		cmd[1] = 0x08;
		cmd[0] = 0x00;
	}
	else if (reg == 4) // 4: RDCVD
	{
		cmd[1] = 0x0A;
		cmd[0] = 0x00;
	}
	else if (reg == 5) // 4: RDCVE
	{
		cmd[1] = 0x09;
		cmd[0] = 0x00;
	}
	else if (reg == 6) // 4: RDCVF
	{
		cmd[1] = 0x0B;
		cmd[0] = 0x00;
	}

	cmd_pec = pec15_calc(2, cmd);
	cmd[2] = (uint8_t)(cmd_pec >> 8);
	cmd[3] = (uint8_t)(cmd_pec);

	cs_low();
	spi_write_read(cmd, 4, data, (REG_LEN * total_ic));
	cs_high();
}

/* Helper function that parses voltage measurement registers */
int8_t parse_cells(uint8_t current_ic,	 // Current IC
				   uint8_t cell_reg,	 // Type of register
				   uint8_t cell_data[],	 // Unparsed data
				   uint16_t *cell_codes, // Parsed data
				   uint8_t *ic_pec		 // PEC error
)
{
	const uint8_t BYT_IN_REG = 6;
	const uint8_t CELL_IN_REG = 3;
	int8_t pec_error = 0;
	uint16_t parsed_cell;
	uint16_t received_pec;
	uint16_t data_pec;
	uint8_t data_counter = current_ic * NUM_RX_BYT; // data counter

	for (uint8_t current_cell = 0; current_cell < CELL_IN_REG; current_cell++) // This loop parses the read back data into the register codes, it
	{																		   // loops once for each of the 3 codes in the register

		parsed_cell = cell_data[data_counter] + (cell_data[data_counter + 1] << 8); // Each code is received as two bytes and is combined to
																					//  create the parsed code
		cell_codes[current_cell + ((cell_reg - 1) * CELL_IN_REG)] = parsed_cell;

		data_counter = data_counter + 2; // Because the codes are two bytes, the data counter
										 // must increment by two for each parsed code
	}
	received_pec = (cell_data[data_counter] << 8) | cell_data[data_counter + 1]; // The received PEC for the current_ic is transmitted as the 7th and 8th
																				 // after the 6 cell voltage data bytes
	data_pec = pec15_calc(BYT_IN_REG, &cell_data[(current_ic)*NUM_RX_BYT]);

	if (received_pec != data_pec)
	{
		pec_error = 1; // The pec_error variable is simply set negative if any PEC errors
		ic_pec[cell_reg - 1] = 1;
	}
	else
	{
		ic_pec[cell_reg - 1] = 0;
	}
	data_counter = data_counter + 2;

	return (pec_error);
}

/* Helper function that parses voltage measurement registers */
int8_t drv_parse_temp_data(uint8_t current_ic,	 // Current IC
				   uint8_t cell_reg,	 // Type of register
				   uint8_t cell_data[],	 // Unparsed data
				   uint16_t *cell_codes, // Parsed data
				   uint8_t *ic_pec		 // PEC error
)
{
	uint8_t BYT_IN_REG = 6;
	uint8_t CELL_IN_REG = 3;
	int8_t pec_error = 0;
	uint16_t parsed_cell;
	uint16_t received_pec;
	uint16_t data_pec;
	uint8_t data_counter = current_ic * NUM_RX_BYT; // data counter

	for (uint8_t current_cell = 0; current_cell < CELL_IN_REG; current_cell++) // This loop parses the read back data into the register codes, it
	{																		   // loops once for each of the 3 codes in the register

		parsed_cell = cell_data[data_counter] + (cell_data[data_counter + 1] << 8); // Each code is received as two bytes and is combined to
																					//  create the parsed code
		
		if(cell_reg == 1)
		{
			cell_codes[current_cell + ((cell_reg - 1) * CELL_IN_REG)] = parsed_cell;
		}
		else if(cell_reg == 2)
		{
			if(current_cell < 2)
			{
				cell_codes[current_cell + 3] = parsed_cell;
			}
		}
		else if(cell_reg == 3)
		{
			cell_codes[current_cell + 5] = parsed_cell;
		}
		else if(cell_reg == 4)
		{
			if(current_cell < 1)
			{
				cell_codes[current_cell + 8] = parsed_cell;
			}
		}

		data_counter = data_counter + 2; // Because the codes are two bytes, the data counter
										 // must increment by two for each parsed code
	}
	received_pec = (cell_data[data_counter] << 8) | cell_data[data_counter + 1]; // The received PEC for the current_ic is transmitted as the 7th and 8th
																				 // after the 6 cell voltage data bytes
	data_pec = pec15_calc(BYT_IN_REG, &cell_data[(current_ic)*NUM_RX_BYT]);

	if (received_pec != data_pec)
	{
		pec_error = 1; // The pec_error variable is simply set negative if any PEC errors
		ic_pec[cell_reg - 1] = 1;
	}
	else
	{
		ic_pec[cell_reg - 1] = 0;
	}
	data_counter = data_counter + 2;

	return (pec_error);
}

/*Reads and parses the ADBMS181x cell voltage registers.
The function is used to read the parsed Cell voltages codes of the ADBMS181x.
This function will send the requested read commands parse the data
and store the cell voltages in c_codes variable.*/

uint8_t ADBMS1818_rdcv(uint8_t reg,		 // Controls which cell voltage register is read back.
					   uint8_t total_ic, // The number of ICs in the system
					   cell_asic *ic	 // Array of the parsed cell codes
)
{
	int8_t pec_error = 0;
	uint8_t *cell_data;
	uint8_t c_ic = 0;
	cell_data = (uint8_t *)malloc((NUM_RX_BYT * total_ic) * sizeof(uint8_t));

	if (reg == 0)
	{
		for (uint8_t cell_reg = 1; cell_reg < ic[0].ic_reg.num_cv_reg + 1; cell_reg++) // Executes once for each of the ADBMS181x cell voltage registers
		{
			ADBMS181x_rdcv_reg(cell_reg, total_ic, cell_data);
			for (int current_ic = 0; current_ic < total_ic; current_ic++)
			{
				if (ic->isospi_reverse == false)
				{
					c_ic = current_ic;
				}
				else
				{
					c_ic = total_ic - current_ic - 1;
				}
				pec_error = pec_error + parse_cells(current_ic, cell_reg, cell_data,
													&ic[c_ic].cells.c_codes[0],
													&ic[c_ic].cells.pec_match[0]);
			}
		}
	}
	else
	{
		ADBMS181x_rdcv_reg(reg, total_ic, cell_data);

		for (int current_ic = 0; current_ic < total_ic; current_ic++)
		{
			if (ic->isospi_reverse == false)
			{
				c_ic = current_ic;
			}
			else
			{
				c_ic = total_ic - current_ic - 1;
			}
			pec_error = pec_error + parse_cells(current_ic, reg, &cell_data[8 * c_ic],
												&ic[c_ic].cells.c_codes[0],
												&ic[c_ic].cells.pec_match[0]);
		}
	}
	ADBMS181x_check_pec(total_ic, CELL, ic);
	free(cell_data);

	return (pec_error);
}

/* Start ADC Conversion for GPIO and Vref2  */
void ADBMS1818_adax(uint8_t MD, // ADC Mode
					uint8_t CHG // GPIO Channels to be measured
)
{
	uint8_t cmd[4];
	uint8_t md_bits;

	md_bits = (MD & 0x02) >> 1;
	cmd[0] = md_bits + 0x04;
	md_bits = (MD & 0x01) << 7;
	cmd[1] = md_bits + 0x60 + CHG;

	cmd_68(cmd);
}

/*
The function reads a single GPIO voltage register and stores the read data
in the *data point as a byte array. This function is rarely used outside of
the ADBMS181x_rdaux() command.
*/
void ADBMS181x_rdaux_reg(uint8_t reg,	   // Determines which GPIO voltage register is read back
						 uint8_t total_ic, // The number of ICs in the system
						 uint8_t *data	   // Array of the unparsed auxiliary codes
)
{
	const uint8_t REG_LEN = 8; // Number of bytes in the register + 2 bytes for the PEC
	uint8_t cmd[4];
	uint16_t cmd_pec;

	if (reg == 1) // Read back auxiliary group A
	{
		cmd[1] = 0x0C;
		cmd[0] = 0x00;
	}
	else if (reg == 2) // Read back auxiliary group B
	{
		cmd[1] = 0x0E;
		cmd[0] = 0x00;
	}
	else if (reg == 3) // Read back auxiliary group C
	{
		cmd[1] = 0x0D;
		cmd[0] = 0x00;
	}
	else if (reg == 4) // Read back auxiliary group D
	{
		cmd[1] = 0x0F;
		cmd[0] = 0x00;
	}
	else // Read back auxiliary group A
	{
		cmd[1] = 0x0C;
		cmd[0] = 0x00;
	}

	cmd_pec = pec15_calc(2, cmd);
	cmd[2] = (uint8_t)(cmd_pec >> 8);
	cmd[3] = (uint8_t)(cmd_pec);

	cs_low();
	spi_write_read(cmd, 4, data, (REG_LEN * total_ic));
	cs_high();
}

/*
The function is used to read the  parsed GPIO codes of the ADBMS181x.
This function will send the requested read commands parse the data
and store the gpio voltages in a_codes variable.
*/
int8_t ADBMS1818_rdaux(uint8_t reg,		 // Determines which GPIO voltage register is read back.
					   uint8_t total_ic, // The number of ICs in the system
					   cell_asic *ic	 // A two dimensional array of the gpio voltage codes.
)
{
	uint8_t *data;
	int8_t pec_error = 0;
	uint8_t c_ic = 0;
	data = (uint8_t *)malloc((NUM_RX_BYT * total_ic) * sizeof(uint8_t));

	if (reg == 0)
	{
		for (uint8_t gpio_reg = 1; gpio_reg < ic[0].ic_reg.num_gpio_reg + 1; gpio_reg++) // Executes once for each of the ADBMS181x aux voltage registers
		{
			ADBMS181x_rdaux_reg(gpio_reg, total_ic, data); // Reads the raw auxiliary register data into the data[] array
			for (int current_ic = 0; current_ic < total_ic; current_ic++)
			{
				if (ic->isospi_reverse == false)
				{
					c_ic = current_ic;
				}
				else
				{
					c_ic = total_ic - current_ic - 1;
				}
				pec_error = drv_parse_temp_data(current_ic, gpio_reg, data,
										&ic[c_ic].aux.a_codes[0],
										&ic[c_ic].aux.pec_match[0]);
			}
		}
	}
	else
	{
		ADBMS181x_rdaux_reg(reg, total_ic, data);

		for (int current_ic = 0; current_ic < total_ic; current_ic++)
		{
			if (ic->isospi_reverse == false)
			{
				c_ic = current_ic;
			}
			else
			{
				c_ic = total_ic - current_ic - 1;
			}
			pec_error = drv_parse_temp_data(current_ic, reg, data,
									&ic[c_ic].aux.a_codes[0],
									&ic[c_ic].aux.pec_match[0]);
		}
	}
	ADBMS181x_check_pec(total_ic, AUX, ic);
	free(data);

	return (pec_error);
}
