/*
 * Can.h
 *
 *  Created on: 01 июня 2015 г.
 *      Author: esaulenko
 */

#ifndef DRIVERS_CAN_CAN_H_
#define DRIVERS_CAN_CAN_H_


#include <stdint.h>
#include "CommonTypes.h"
#ifndef CAN_FIRMWARE
	#include "SysTimer.h"
	#include "Buffer.h"
	#include "scmRTOS.h"
#endif




struct TCanPkt
{
	uint32_t	id;
	uint8_t		data[8];
	uint8_t		data_len;

	TCanPkt()	{ }

	TCanPkt (uint32_t pkt_id):
		id(pkt_id) { }

	// принимает строку, заполняет данные пакета. Неуказанные данные заполняются нулями
	template <int N>
	inline void SetData (const char (&data_str)[N])
	{
		int data_size = (N <= 8) ? (N - 1) : 8;
		for (int i = 0; i < data_size; i++)
			data[i] = data_str[i];
		for (int i = data_size; i < 8; i++)
			data[i] = 0x00;
	}

	inline void SetData (uint8_t A)
	{	data_len = 1;
		data[0] = A; data[1] = 0; data[2] = 0; data[3] = 0;
		data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0;
	}
	inline void SetData (uint8_t A, uint8_t B)
	{	data_len = 2;
		data[0] = A; data[1] = B; data[2] = 0; data[3] = 0;
		data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0;
	}
	inline void SetData (uint8_t A, uint8_t B, uint8_t C)
	{	data_len = 3;
		data[0] = A; data[1] = B; data[2] = C; data[3] = 0;
		data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0;
	}
	inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D)
	{	data_len = 4;
		data[0] = A; data[1] = B; data[2] = C; data[3] = D;
		data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0;
	}
	inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D, uint8_t E)
	{	data_len = 5;
		data[0] = A; data[1] = B; data[2] = C; data[3] = D;
		data[4] = E; data[5] = 0; data[6] = 0; data[7] = 0;
	}
	inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D, uint8_t E, uint8_t F)
	{	data_len = 6;
		data[0] = A; data[1] = B; data[2] = C; data[3] = D;
		data[4] = E; data[5] = F; data[6] = 0; data[7] = 0;
	}
	inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D, uint8_t E, uint8_t F, uint8_t G)
	{	data_len = 7;
		data[0] = A; data[1] = B; data[2] = C; data[3] = D;
		data[4] = E; data[5] = F; data[6] = G; data[7] = 0;
	}
	inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D, uint8_t E, uint8_t F, uint8_t G, uint8_t H)
	{	data_len = 8;
		data[0] = A; data[1] = B; data[2] = C; data[3] = D;
		data[4] = E; data[5] = F; data[6] = G; data[7] = H;
	}

};


struct TCanInit
{
	uint32_t	baudrate;		// "сырое" значение регистра BTR, см. TCanBaudrate
	uint8_t		mode;			// в наст. момент не используется. TODO Должно быть RO / RW
	uint64_t	filters[27];	// массив со значениями для пар регистров; заполняется Filters::xxx
};



class CCan
{
public:

	enum TCanState : uint8_t
	{
		CanActive,
		CanRxOnly,		// внешний драйвер выключен
		CanSleep,		// внешний драйвер выключен, модуль в спящем режиме

		CanInitError = 0xFE,
		CanBusOff = 0xFF,
	};

/*	TIME QUANTUM FREQUENCY:
		Ftq = Ntq * Fbaud
	where 8 <= Ntq <= 25, and Ntq - is integer!

	BAUD RATE PRESCALER:
		BRP = (Fcan/Ftq)–1

	INDIVIDUAL BIT TIME SEGMENTS:
		(Bit Time) = (Sync Segment) + (Time Segment 1) + (Time Segment 2)
	where
		(Sync Segment) = 1*Tq (constant)
		(Time Segment 2) ~= 30% of Nominal Bit Time = Ntq/3
		(Time Segment 2) > 1*Tq
		(Time Segment 1) > 3*Tq
		(Time Segment 1) > (Time Segment 2)
		(Time Segment 1) <= (Time Segment 2)+8
		(Synchronous Jump Width) <= (Time Segment 2)

Table of Parameters:
--------+-----------------------------------------------------+
BRP@Fcan|                       Ntq                           |
  9MHz	|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8|
--------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
500.000 |  |  |  |  |  |  |  | 0|  |  |  |  |  |  |  |  | 1|  |
250.000 |  |  |  |  |  |  |  | 1|  |  |  |  |  | 2|  |  | 3|  |
125.000 |  | 2|  |  |  |  |  | 3|  |  |  |  |  | 5|  |  | 7| 8|
100.000 |  |  |  |  |  |  |  | 4|  |  | 5|  |  |  |  | 8| 9|  |
 83.333 |  |  |  |  |  |  |  | 5|  |  |  |  |  | 8|  |  |11|  |
 50.000 |  |  |  |  |  | 8|  | 9|  |  |11|  |  |14|  |17|19|  |
 33.333 |  |  |  |  |  |  |  |14|  |  |17|  |  |  |  |26|29|  |
 20.000 |17|  |  |  |  |  |  |24|  |  |29|  |  |  |  |44|49|  |
 15.000 |23|24|  |  |  |29|  |  |  |  |39|  |  |49|  |59|  |74|
 10.000 |35|  |  |  |  |44|  |49|  |  |59|  |  |74|  |89|99|  |
--------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
#define CAN_BTR(BRP,TSEG1,TSEG2,SJW)	(((((((SJW)&0x03)<<4)|((TSEG2)&0x07))<<4)|((TSEG1)&0x0F))<<16)|((BRP)&0x3FF)

	// значения регистра BTR
	enum TCanBaudrate {
		CanBaudrate500	= CAN_BTR(0,10,5,3),

		CanBaudrate250	= CAN_BTR(1,10,5,3),

//		CanBaudrate125	= CAN_BTR(2,14,7,3),
		CanBaudrate125	= CAN_BTR(3,10,5,3),

		CanBaudrate100	= CAN_BTR(4,10,5,3),

		CanBaudrate83	= CAN_BTR(5,10,5,3),

//		CanBaudrate50	= CAN_BTR(8,11,6,3),
		CanBaudrate50	= CAN_BTR(9,10,5,3),

		CanBaudrate33	= CAN_BTR(14,10,5,3),

		CanBaudrate20	= CAN_BTR(17,15,7,3),
//		CanBaudrate20	= CAN_BTR(24,10,5,3),

		CanBaudrate15	= CAN_BTR(23,15,7,3),
//		CanBaudrate15	= CAN_BTR(24,14,7,3),
//		CanBaudrate15	= CAN_BTR(29,11,6,3),

		CanBaudrate10	= CAN_BTR(35,15,7,3),
//		CanBaudrate10	= CAN_BTR(44,11,6,3),
//		CanBaudrate10	= CAN_BTR(49,10,5,3),

	};

#ifndef CAN_FIRMWARE
	CCan ();

	uint32_t Init (TCanChannel aCh, const TCanInit * apInit);

	uint32_t Send (TCanChannel aCh, TCanPkt * apPkt, uint32_t aTimeout);

	void ReceivedIsr (TCanChannel aCh, TCanPkt * apPkt);

	uint32_t ProcessQueue ();

	uint32_t GetRcv (TCanChannel aCh, TCanPkt * apPkt);

	uint32_t GetRcvTimeout (TCanChannel aCh)
	{	return _rx_tmr[aCh].Value();	}

#endif // CAN_FIRMWARE

	// объявление фильтров при инициализации
	struct Filter {
		// добавить пару идентификаторов для точного соответствия
		static constexpr uint64_t List11 (uint16_t id1, uint16_t id2)
		{
			return ((uint64_t) (IdStd (id1) | 0x01) << 32) |
					(uint64_t) (IdStd (id2) | 0x01);
		}
		static constexpr uint64_t List29 (uint32_t id1, uint32_t id2)
		{
			return ((uint64_t) (IdExt (id1) | 0x01) << 32) |
					(uint64_t) (IdExt (id2) | 0x01);
		}

		// добавить один идентификатор
		static constexpr uint64_t List11 (uint16_t id1)
		{
			return ((uint64_t) (IdStd (id1) | 0x01) << 32) |
					(uint64_t) (IdStd (id1) | 0x01);
		}
		static constexpr uint64_t List29 (uint32_t id1)
		{
			return ((uint64_t) (IdExt (id1) | 0x01) << 32) |
					(uint64_t) (IdExt (id1) | 0x01);
		}


		// добавить один идентификатор с маской
		static constexpr uint64_t Mask11 (uint16_t id, uint16_t mask)
		{
			return ((uint64_t) (IdStd (id)) << 32) |
					(uint64_t) (IdStd (mask) | 0x01);
		}
		static constexpr uint64_t Mask29 (uint32_t id, uint32_t mask)
		{
			return ((uint64_t) (IdExt (id)) << 32) |
					(uint64_t) (IdExt (mask) | 0x01);
		}

	private:
		// формирование ID для фильтров
		static constexpr uint32_t IdStd (uint16_t id)
		{	return (id & 0x7FFul) << 21; }
		static constexpr uint32_t IdExt (uint32_t id)
		{	return ((id & 0x1FFFFFFFul) << 3) | 0x04; }
	};



#ifndef CAN_FIRMWARE
private:

	uint8_t	sleep_en;
	bool	IsSleepEn (TCanChannel aCh)
	{	return sleep_en & aCh;	}


	// очередь пакетов на отправку - тот же TCanPkt, но с таймером
	struct TCanPktTxInt: public TCanPkt {
		uint32_t	timeout;
		TimerMs		tmr;
	};

	typedef CircularBuffer<TCanPkt, 16> TRxBuf;
	typedef CircularBuffer<TCanPktTxInt, 32> TTxBuf;

	TRxBuf		_rx_buf[2];
	TTxBuf		_tx_buf[2];

	uint32_t TxByTimeout (TCanChannel aCh);

	bool IsTxQueueEmpty (TCanChannel aCh);

	TCanState	_state[2];

	// Send() и обработка очереди вызываются из разных потоков, нужна защита
	OS::TMutex	can_mutex;

	// сбрасывается по приёму любого пакета
	TimerMs		_rx_tmr[2];

#endif // CAN_FIRMWARE
};



extern CCan	gCan;



#endif /* DRIVERS_CAN_CAN_H_ */
