//#############################################################################################################################################################################################################
#include "IdiBusSerial.h"
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
IdiBusSerial::IdiBusSerial(void)
{
 this->RS485_DIR_GPIO.DDR  = (uint8_t*)&this->InitProtectionVar;
 this->RS485_DIR_GPIO.PIN  = (uint8_t*)&this->InitProtectionVar;
 this->RS485_DIR_GPIO.PORT = (uint8_t*)&this->InitProtectionVar;
 this->BAUDRATE_DIPSW.S0_Gpio.DDR  = (uint8_t*)&this->InitProtectionVar;
 this->BAUDRATE_DIPSW.S0_Gpio.PIN  = (uint8_t*)&this->InitProtectionVar;
 this->BAUDRATE_DIPSW.S0_Gpio.PORT = (uint8_t*)&this->InitProtectionVar;
 this->BAUDRATE_DIPSW.S1_Gpio.DDR  = (uint8_t*)&this->InitProtectionVar;
 this->BAUDRATE_DIPSW.S1_Gpio.PIN  = (uint8_t*)&this->InitProtectionVar;
 this->BAUDRATE_DIPSW.S1_Gpio.PORT = (uint8_t*)&this->InitProtectionVar;
 this->BAUDRATE_DIPSW.S2_Gpio.DDR  = (uint8_t*)&this->InitProtectionVar;
 this->BAUDRATE_DIPSW.S2_Gpio.PIN  = (uint8_t*)&this->InitProtectionVar;
 this->BAUDRATE_DIPSW.S2_Gpio.PORT = (uint8_t*)&this->InitProtectionVar;
 this->USARTN.UCSRNA = (uint8_t*)&this->InitProtectionVar;
 this->USARTN.UCSRNB = (uint8_t*)&this->InitProtectionVar;
 this->USARTN.UCSRNC = (uint8_t*)&this->InitProtectionVar;
 this->USARTN.UBRRN  = (uint16_t*)&this->InitProtectionVar;
 this->USARTN.UDRN   = (uint8_t*)&this->InitProtectionVar;
 this->TIMN.TCCRNA   = (uint8_t*)&this->InitProtectionVar;
 this->TIMN.TCCRNB   = (uint8_t*)&this->InitProtectionVar;
 this->TIMN.TIMSKN   = (uint8_t*)&this->InitProtectionVar;
 this->TIMN.TIFRN    = (uint8_t*)&this->InitProtectionVar;
 this->TIMN.OCRNA    = (uint16_t*)&this->InitProtectionVar;
 this->TIMN.OCRNB    = (uint16_t*)&this->InitProtectionVar;
 this->TIMN.TCNTN    = (uint16_t*)&this->InitProtectionVar;
 this->InitState = 0;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::Init(IDIBUS_SERIAL_INIT_TYPEDEF *InitT)
{
 if ( (this->GetGpioParamsArduino(InitT->RS485_DIR_PIN,         &this->RS485_DIR_GPIO) == 0)         ||
	  (this->GetGpioParamsArduino(InitT->BAUDRATE_DIPSW_S0_PIN, &this->BAUDRATE_DIPSW.S0_Gpio) == 0) ||
	  (this->GetGpioParamsArduino(InitT->BAUDRATE_DIPSW_S1_PIN, &this->BAUDRATE_DIPSW.S1_Gpio) == 0) ||
	  (this->GetGpioParamsArduino(InitT->BAUDRATE_DIPSW_S2_PIN, &this->BAUDRATE_DIPSW.S2_Gpio) == 0)  ) { return 0; }
 
 return this->BaseInit(InitT->USART_NUMBER, InitT->TIMER_NUMBER, InitT->CONFIG);
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::Init(IDIBUS_SERIAL_ALTERNATIVE_INIT_TYPEDEF *InitT)
{
 if ( (this->GetGpioParams(&InitT->RS485_DIR_GPIO,    &this->RS485_DIR_GPIO) == 0)         ||
	  (this->GetGpioParams(&InitT->BAUDRATE_DIPSW.S0, &this->BAUDRATE_DIPSW.S0_Gpio) == 0) ||
	  (this->GetGpioParams(&InitT->BAUDRATE_DIPSW.S1, &this->BAUDRATE_DIPSW.S1_Gpio) == 0) ||
	  (this->GetGpioParams(&InitT->BAUDRATE_DIPSW.S2, &this->BAUDRATE_DIPSW.S2_Gpio) == 0) )  { return 0; }
 
 return this->BaseInit(InitT->USART_NUMBER, InitT->TIMER_NUMBER, InitT->CONFIG);
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::BaseInit(	enum IDIBUS_SERIAL_USARTN_NUMBER USART_NUM, enum IDIBUS_SERIAL_TIMER_NUMBER TIMER_NUM, enum IDIBUS_SERIAL_USARTN_CONFIG CONF)
{
  // Pointers Init
 switch (USART_NUM)
	{
	 case (IDIBUS_SERIAL_USART0) : {
			#if defined IDIBUS_USART0_ISR_DISABLE
			return 0;
			#else
			this->USARTN.UCSRNA = &(UCSR0A);
			this->USARTN.UCSRNB = &(UCSR0B);
			this->USARTN.UCSRNC = &(UCSR0C);
			this->USARTN.UBRRN  = &(UBRR0);
			this->USARTN.UDRN   = &(UDR0);
			InterruptListenersList.USART0_CallbackInstance = this;
			#endif
			break; }
	 case (IDIBUS_SERIAL_USART1) : {
			#if defined IDIBUS_USART1_ISR_DISABLE
			return 0;
			#else
			this->USARTN.UCSRNA = &(UCSR1A);
			this->USARTN.UCSRNB = &(UCSR1B);
			this->USARTN.UCSRNC = &(UCSR1C);
			this->USARTN.UBRRN  = &(UBRR1);
			this->USARTN.UDRN   = &(UDR1);
			InterruptListenersList.USART1_CallbackInstance = this;
			#endif
			break; }
	 case (IDIBUS_SERIAL_USART2) : {
			#if defined IDIBUS_USART2_ISR_DISABLE
			return 0;
			#else
			this->USARTN.UCSRNA = &(UCSR2A);
			this->USARTN.UCSRNB = &(UCSR2B);
			this->USARTN.UCSRNC = &(UCSR2C);
			this->USARTN.UBRRN  = &(UBRR2);
			this->USARTN.UDRN   = &(UDR2);
			InterruptListenersList.USART2_CallbackInstance = this;
			#endif
			break; }
	 case (IDIBUS_SERIAL_USART3) : {
		 	#if defined IDIBUS_USART3_ISR_DISABLE
			return 0;
			#else
			this->USARTN.UCSRNA = &(UCSR3A);
			this->USARTN.UCSRNB = &(UCSR3B);
			this->USARTN.UCSRNC = &(UCSR3C);
			this->USARTN.UBRRN  = &(UBRR3);
			this->USARTN.UDRN   = &(UDR3);
			InterruptListenersList.USART3_CallbackInstance = this;
			#endif
			break; }
	 default : { return 0; }
	}

 switch (TIMER_NUM)
	{
	case (IDIBUS_TIMER_1) : {
			#if defined IDIBUS_TIM1_ISR_DISABLE
			return 0;
			#else
			this->TIMN.TCCRNA = &(TCCR1A);
			this->TIMN.TCCRNB = &(TCCR1B);
			this->TIMN.TIMSKN = &(TIMSK1);
			this->TIMN.TIFRN  = &(TIFR1);
			this->TIMN.OCRNA  = &(OCR1A);
			this->TIMN.OCRNB  = &(OCR1B);
			this->TIMN.TCNTN  = &(TCNT1);
			InterruptListenersList.TIM1_CallbackInstance = this;
			#endif
			break; }
	 case (IDIBUS_TIMER_3) : {
			#if defined IDIBUS_TIM3_ISR_DISABLE
			return 0;
			#else
			this->TIMN.TCCRNA = &(TCCR3A);
			this->TIMN.TCCRNB = &(TCCR3B);
			this->TIMN.TIMSKN = &(TIMSK3);
			this->TIMN.TIFRN  = &(TIFR3);
			this->TIMN.OCRNA  = &(OCR3A);
			this->TIMN.OCRNB  = &(OCR3B);
			this->TIMN.TCNTN  = &(TCNT3);
			InterruptListenersList.TIM3_CallbackInstance = this;
			#endif
			break; }
	 case (IDIBUS_TIMER_4) : {
			#if defined IDIBUS_TIM4_ISR_DISABLE
			return 0;
			#else
			this->TIMN.TCCRNA = &(TCCR4A);
			this->TIMN.TCCRNB = &(TCCR4B);
			this->TIMN.TIMSKN = &(TIMSK4);
			this->TIMN.TIFRN  = &(TIFR4);
			this->TIMN.OCRNA  = &(OCR4A);
			this->TIMN.OCRNB  = &(OCR4B);
			this->TIMN.TCNTN  = &(TCNT4);
			InterruptListenersList.TIM4_CallbackInstance = this;
			#endif
			break; }
	 case (IDIBUS_TIMER_5) : {
			#if defined IDIBUS_TIM5_ISR_DISABLE
			return 0;
			#else
			this->TIMN.TCCRNA = &(TCCR5A);
			this->TIMN.TCCRNB = &(TCCR5B);
			this->TIMN.TIMSKN = &(TIMSK5);
			this->TIMN.TIFRN  = &(TIFR5);
			this->TIMN.OCRNA  = &(OCR5A);
			this->TIMN.OCRNB  = &(OCR5B);
			this->TIMN.TCNTN  = &(TCNT5);
			InterruptListenersList.TIM5_CallbackInstance = this;
			#endif
			break; }
	 default : { return 0; }
	}
 
 // GPIO Init
 uint8_t SREG_Temp = SREG;
 cli();
 *this->RS485_DIR_GPIO.PORT &= ~this->RS485_DIR_GPIO.PIN_MASK;
 *this->RS485_DIR_GPIO.DDR  |=  this->RS485_DIR_GPIO.PIN_MASK;
 
 *this->BAUDRATE_DIPSW.S0_Gpio.PORT &= ~this->BAUDRATE_DIPSW.S0_Gpio.PIN_MASK;
 *this->BAUDRATE_DIPSW.S0_Gpio.DDR  &= ~this->BAUDRATE_DIPSW.S0_Gpio.PIN_MASK;
 
 *this->BAUDRATE_DIPSW.S1_Gpio.PORT &= ~this->BAUDRATE_DIPSW.S1_Gpio.PIN_MASK;
 *this->BAUDRATE_DIPSW.S1_Gpio.DDR  &= ~this->BAUDRATE_DIPSW.S1_Gpio.PIN_MASK;
 
 *this->BAUDRATE_DIPSW.S2_Gpio.PORT &= ~this->BAUDRATE_DIPSW.S2_Gpio.PIN_MASK;
 *this->BAUDRATE_DIPSW.S2_Gpio.DDR  &= ~this->BAUDRATE_DIPSW.S2_Gpio.PIN_MASK;
 if ( SREG_Temp & (0x01U << SREG_I) ) { sei(); }
 
 // USART Init
 *this->USARTN.UBRRN = 0;
 *this->USARTN.UCSRNA = (1U<<USARTN_UCSRNA_U2X_Pos);
 *this->USARTN.UCSRNB = 0;
 this->BAUDRATE = 0;
 switch (CONF)
	{
	 case (IDIBUS_SERIAL_8N1) : { *this->USARTN.UCSRNC = (1U<<USARTN_UCSRNC_UCSZ0)|(1U<<USARTN_UCSRNC_UCSZ1)|(0U<<USARTN_UCSRNC_USBS)|(0U<<USARTN_UCSRNC_UPM1)|(0U<<USARTN_UCSRNC_UPM0); break; }
	 case (IDIBUS_SERIAL_8N2) : { *this->USARTN.UCSRNC = (1U<<USARTN_UCSRNC_UCSZ0)|(1U<<USARTN_UCSRNC_UCSZ1)|(1U<<USARTN_UCSRNC_USBS)|(0U<<USARTN_UCSRNC_UPM1)|(0U<<USARTN_UCSRNC_UPM0); break; }
	 case (IDIBUS_SERIAL_8E1) : { *this->USARTN.UCSRNC = (1U<<USARTN_UCSRNC_UCSZ0)|(1U<<USARTN_UCSRNC_UCSZ1)|(0U<<USARTN_UCSRNC_USBS)|(1U<<USARTN_UCSRNC_UPM1)|(0U<<USARTN_UCSRNC_UPM0); break; }
	 case (IDIBUS_SERIAL_8E2) : { *this->USARTN.UCSRNC = (1U<<USARTN_UCSRNC_UCSZ0)|(1U<<USARTN_UCSRNC_UCSZ1)|(1U<<USARTN_UCSRNC_USBS)|(1U<<USARTN_UCSRNC_UPM1)|(0U<<USARTN_UCSRNC_UPM0); break; }
	 case (IDIBUS_SERIAL_8O1) : { *this->USARTN.UCSRNC = (1U<<USARTN_UCSRNC_UCSZ0)|(1U<<USARTN_UCSRNC_UCSZ1)|(0U<<USARTN_UCSRNC_USBS)|(1U<<USARTN_UCSRNC_UPM1)|(1U<<USARTN_UCSRNC_UPM0); break; }
	 case (IDIBUS_SERIAL_8O2) : { *this->USARTN.UCSRNC = (1U<<USARTN_UCSRNC_UCSZ0)|(1U<<USARTN_UCSRNC_UCSZ1)|(1U<<USARTN_UCSRNC_USBS)|(1U<<USARTN_UCSRNC_UPM1)|(1U<<USARTN_UCSRNC_UPM0); break; }
	 default : { return 0; }
	}
 
 // TIMER Init
 *this->TIMN.TCCRNB = 0;	// Counter stop
 *this->TIMN.TCCRNA = 0;	// Normal Mode
 *this->TIMN.TIMSKN = 0;
 
 this->InitState = 1;
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void IdiBusSerial::SetBaudrate(enum IDIBUS_SERIAL_BAUDRATE Baudrate)
{
 switch ( Baudrate )
	{
	 case (IDIBUS_BAUDRATE_DSW_CODE_2400B) : {
		this->SetBaudrateLL(2400);
		this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_2400B_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
		this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_2400B_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint32_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
		break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_9600B) : {
		this->SetBaudrateLL(9600);
		this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_9600B_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
		this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_9600B_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint32_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
		break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_19200B) : {
		this->SetBaudrateLL(19200);
		this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_19200B_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
		this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_19200B_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
		break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_115200B) : {
		this->SetBaudrateLL(115200);
		this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_115200B_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
		this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_115200B_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
		break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_250K) : { 
		this->SetBaudrateLL(250000);
		this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_250K_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
		this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_250K_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
		 break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_500K) : {
		this->SetBaudrateLL(500000);
		this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_500K_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
		this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_500K_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
		break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_1M) : {
		this->SetBaudrateLL(1000000);
		this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_1M_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
		this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_1M_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
		break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_10M) : {
		this->SetBaudrateLL(10000000);
		this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_10M_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
		this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_10M_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
		break; }
	 default : { return; }
	}
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void IdiBusSerial::UpdateBaudrateFromDipSwitch(void)
{
 uint8_t  BaudrateCode = 0;
 uint32_t BaudrateCodeValue;
 if ( (*this->BAUDRATE_DIPSW.S2_Gpio.PIN & this->BAUDRATE_DIPSW.S2_Gpio.PIN_MASK) != 0 ) { BaudrateCode |= 0x04; }
 if ( (*this->BAUDRATE_DIPSW.S1_Gpio.PIN & this->BAUDRATE_DIPSW.S1_Gpio.PIN_MASK) != 0 ) { BaudrateCode |= 0x02; }
 if ( (*this->BAUDRATE_DIPSW.S0_Gpio.PIN & this->BAUDRATE_DIPSW.S0_Gpio.PIN_MASK) != 0 ) { BaudrateCode |= 0x01; }
 
 switch (BaudrateCode)
	{
	 case (IDIBUS_BAUDRATE_DSW_CODE_2400B)   : { BaudrateCodeValue = 2400U;     break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_9600B)   : { BaudrateCodeValue = 9600U;     break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_19200B)  : { BaudrateCodeValue = 19200U;    break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_115200B) : { BaudrateCodeValue = 115200U;   break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_250K)    : { BaudrateCodeValue = 250000U;   break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_500K)    : { BaudrateCodeValue = 500000U;   break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_1M)      : { BaudrateCodeValue = 1000000U;  break; }
	 case (IDIBUS_BAUDRATE_DSW_CODE_10M)     : { BaudrateCodeValue = 10000000U; break; }
	 default : { return; }
	}
 if ( this->BAUDRATE != BaudrateCodeValue )
	{
	 this->SetBaudrateLL(BaudrateCodeValue);
	 switch ( BaudrateCode )
		{
		 case (IDIBUS_BAUDRATE_DSW_CODE_2400B) : {
			this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_2400B_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
			this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_2400B_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
			break; }
		 case (IDIBUS_BAUDRATE_DSW_CODE_9600B) : {
			this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_9600B_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
			this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_9600B_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
			break; }
		 case (IDIBUS_BAUDRATE_DSW_CODE_19200B) : {
			this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_19200B_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
			this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_19200B_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
			break; }
		 case (IDIBUS_BAUDRATE_DSW_CODE_115200B) : {
			this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_115200B_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
			this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_115200B_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
			break; }
		 case (IDIBUS_BAUDRATE_DSW_CODE_250K) : { 
			this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_250K_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
			this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_250K_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
			 break; }
		 case (IDIBUS_BAUDRATE_DSW_CODE_500K) : {
			this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_500K_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
			this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_500K_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
			break; }
		 case (IDIBUS_BAUDRATE_DSW_CODE_1M) : {
			this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_1M_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
			this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_1M_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
			break; }
		 case (IDIBUS_BAUDRATE_DSW_CODE_10M) : {
			this->TimerInterFrameCompValue = (uint16_t)( ((uint64_t)IDIBUS_10M_INTERFRAME_TIMEOUT_US      * (uint64_t)F_CPU / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE ) / 1000000ULL );
			this->TimerTimeoutCompValue    = (uint16_t)( ((uint64_t)IDIBUS_10M_MASTER_RESPONSE_TIMEOUT_US * (uint64_t)F_CPU  / (uint64_t)IDIBUS_SERIAL_TIMER_PR_VALUE) / 1000000ULL );
			break; }
		 default : { return; }
		}
	}
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void IdiBusSerial::SetBaudrateLL(uint32_t Baudrate)
{
 uint16_t UBRR_2X;
 if ( ((F_CPU / 8U) % Baudrate) < (Baudrate / 2) )
	{
	 UBRR_2X = (uint16_t)( ((uint32_t)F_CPU / 8U) / Baudrate );		
	 if ( UBRR_2X != 0 ) { UBRR_2X--; }
	}
	else { UBRR_2X = (uint16_t)( ((uint32_t)F_CPU / 16U) / Baudrate ); }	// WARNING CHANGED DIVIDER FROM 8 TO 16!!!   //(F_CPU / 8U) / Baudrate + 1 - 1
 *this->USARTN.UBRRN = UBRR_2X;
 this->BAUDRATE = Baudrate;
 if ( this->InitState == 1 ) { this->InitState = 2; }
}
//=============================================================================================================================================================================================================
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//=============================================================================================================================================================================================================
void IdiBusSerial::Start(void)
{
 *this->USARTN.UCSRNB = (1U<<USARTN_UCSRNB_TXEN_Pos) | (1U<<USARTN_UCSRNB_TXCIE_Pos);	// USART TX Periph enable
 cli();
 *this->RS485_DIR_GPIO.PORT |= this->RS485_DIR_GPIO.PIN_MASK;							// Dir to Tx
 sei();
 this->TransferComplete = 1;															//
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void IdiBusSerial::Stop(void)
{
 *this->TIMN.TCCRNB = 0;											// Timer Stop
 cli();
 *this->RS485_DIR_GPIO.PORT &= ~this->RS485_DIR_GPIO.PIN_MASK;		// Dir to RX
 sei();
 *this->USARTN.UCSRNB = 0;											// USART TX/RX Periph disable
 this->TransferComplete = 1;										// 
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::SendRequestAsync(uint8_t *_TxBuf_, uint16_t _TxBufLength_)
{
 if ( (this->TransferComplete == 0) || (this->InitState != 2) ) { return 0; }

 this->ERROR_STATUS.SumVar = 0;
 if		 ( _TxBufLength_ == 0 )						   { this->ERROR_STATUS.BF.TxZerolength = 1; return 0; }
 else if ( _TxBufLength_ > IDIBUS_SERIAL_TX_BUF_SIZE ) { this->ERROR_STATUS.BF.TxOverflow = 1;   return 0; }
 	
 memcpy(this->TxBuf, _TxBuf_, _TxBufLength_);
 this->TransferComplete = 0;
 this->RequestType = _IDIBUS_SERIAL_TX_RX_;
 
 *this->TIMN.OCRNA = this->TimerInterFrameCompValue;
 if ( this->TimerTimeoutCompValue > (0xFFFFU - this->TimerInterFrameCompValue) )
	{
	 *this->TIMN.OCRNB = 0xFFFFU;
	 this->TimerTimeoutOverflow  = (uint16_t)( this->TimerTimeoutCompValue - (0xFFFFU - this->TimerInterFrameCompValue) );
	}
	else
		{
		 *this->TIMN.OCRNB = (uint16_t)(this->TimerTimeoutCompValue + this->TimerInterFrameCompValue);
		 this->TimerTimeoutOverflow  = 0;
		}
 *this->TIMN.TCNTN = 0;
 *this->TIMN.TIFRN  = (1U << USARTN_TIM_TIFR_OCFA_Pos)  | (1U << USARTN_TIM_TIFR_OCFB_Pos);		// Interframe and Response timeouts
 *this->TIMN.TIMSKN = (1U << USRTN_TIM_TIMSK_OCIEA_Pos) | (1U << USRTN_TIM_TIMSK_OCIEB_Pos);		
 
 this->TxBufCounter = 1;
 this->TxBufLength = _TxBufLength_;
 this->RxBufCounter = 0;
 *this->USARTN.UDRN = this->TxBuf[0];
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::SendRequestSync(uint8_t *_TxBuf_, uint16_t _TxBufLength_)
{
 if ( this->InitState != 2 ) { return 0; }
 while ( this->TransferComplete == 0 ) { }
 if ( this->SendRequestAsync(_TxBuf_, _TxBufLength_) == 0 ) { return 0; }
 while ( this->TransferComplete == 0 ) { }
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::WriteAsync(uint8_t *_TxBuf_, uint16_t _TxBufLength_)
{
 if ( (this->TransferComplete == 0) || (this->InitState != 2) ) { return 0; }
 
 this->ERROR_STATUS.SumVar = 0;
 if		 ( _TxBufLength_ == 0 )						   { this->ERROR_STATUS.BF.TxZerolength = 1; return 0; }
 else if ( _TxBufLength_ > IDIBUS_SERIAL_TX_BUF_SIZE ) { this->ERROR_STATUS.BF.TxOverflow = 1;   return 0; }

 this->TransferComplete = 0;	 
 memcpy(this->TxBuf, _TxBuf_, _TxBufLength_);
 this->RequestType = _IDIBUS_SERIAL_TX_;
 
 *this->TIMN.OCRNA = this->TimerInterFrameCompValue;
 *this->TIMN.TCNTN = 0;
 *this->TIMN.TIFRN  = (1U << USARTN_TIM_TIFR_OCFA_Pos) | (1U << USARTN_TIM_TIFR_OCFB_Pos);		// Interframe Timeout
 *this->TIMN.TIMSKN = (1U << USRTN_TIM_TIMSK_OCIEA_Pos);
 	
 this->TxBufCounter = 1;
 this->TxBufLength = _TxBufLength_;
 *this->USARTN.UDRN = this->TxBuf[0];
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::WriteSync(uint8_t *_TxBuf_, uint16_t _TxBufLength_)
{
 if ( this->InitState != 2 ) { return 0; }
 while ( this->TransferComplete == 0 ) {}
 if ( this->WriteAsync(_TxBuf_, _TxBufLength_) == 0 ) { return 0; }
 while ( this->TransferComplete == 0 ) {}
 return 1;
}
//=============================================================================================================================================================================================================
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//=============================================================================================================================================================================================================
uint8_t *IdiBusSerial::getTxBufPointer(void)
{
 if ( this->TransferComplete ) { return this->TxBuf; }
 else                          { return NULL; }
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t *IdiBusSerial::getRxBufPointer (void)
{
 return this->RxBuf;	
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::SendRequestAsync(uint16_t DataLength)
{
 if ( (this->TransferComplete == 0) || (this->InitState != 2) ) { return 0; }
 this->ERROR_STATUS.SumVar = 0;
 this->TransferComplete = 0;
 this->RequestType = _IDIBUS_SERIAL_TX_RX_;
 
 *this->TIMN.OCRNA = this->TimerInterFrameCompValue;
 if ( this->TimerTimeoutCompValue > (0xFFFFU - this->TimerInterFrameCompValue) )
	{
	 *this->TIMN.OCRNB = 0xFFFFU;
	 this->TimerTimeoutOverflow  = (uint16_t)( this->TimerTimeoutCompValue - (0xFFFFU - this->TimerInterFrameCompValue) );
	}
	else
		{
		 *this->TIMN.OCRNB = (uint16_t)(this->TimerTimeoutCompValue + this->TimerInterFrameCompValue);
		 this->TimerTimeoutOverflow  = 0;
		}
 *this->TIMN.TCNTN = 0;
 *this->TIMN.TIFRN  = (1U << USARTN_TIM_TIFR_OCFA_Pos)  | (1U << USARTN_TIM_TIFR_OCFB_Pos);		// Interframe and Response timeouts
 *this->TIMN.TIMSKN = (1U << USRTN_TIM_TIMSK_OCIEA_Pos) | (1U << USRTN_TIM_TIMSK_OCIEB_Pos);		
 
 this->TxBufCounter = 1;
 this->TxBufLength = DataLength;
 this->RxBufCounter = 0;
 *this->USARTN.UDRN = this->TxBuf[0];
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::SendRequestSync(uint16_t DataLength)
{
 if ( this->InitState != 2 ) { return 0; }
 while ( this->TransferComplete == 0 ) {}
 if ( this->SendRequestAsync(DataLength) == 0 ) { return 0; }
 while ( this->TransferComplete == 0 ) {}
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::WriteAsync(uint16_t DataLength)
{
 if ( (this->TransferComplete == 0) || (this->InitState != 2) ) { return 0; }
 this->ERROR_STATUS.SumVar = 0;
 this->TransferComplete = 0;	 
 this->RequestType = _IDIBUS_SERIAL_TX_;
 
 *this->TIMN.OCRNA = this->TimerInterFrameCompValue;
 *this->TIMN.TCNTN = 0;
 *this->TIMN.TIFRN  = (1U << USARTN_TIM_TIFR_OCFA_Pos) | (1U << USARTN_TIM_TIFR_OCFB_Pos);		// Interframe Timeout
 *this->TIMN.TIMSKN = (1U << USRTN_TIM_TIMSK_OCIEA_Pos);
 	
 this->TxBufCounter = 1;
 this->TxBufLength = DataLength;
 *this->USARTN.UDRN = this->TxBuf[0];
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::WriteSync(uint16_t DataLength)
{
  if ( this->InitState != 2 ) { return 0; }
 while ( this->TransferComplete == 0 ) {}
 if ( this->WriteAsync(DataLength) == 0 ) { return 0; }
 while ( this->TransferComplete == 0 ) {}
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t  IdiBusSerial::IsTransferComplete(void) { return this->TransferComplete; }
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint16_t IdiBusSerial::GetRxBufCounter(void) { return this->RxBufCounter; }
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
IDIBUS_SERIAL_ERROR_STATUS_TYPE IdiBusSerial::GetErrorStatus(void) { return this->ERROR_STATUS; }
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint32_t IdiBusSerial::GetBaudrate(void) { return this->BAUDRATE; }
//=============================================================================================================================================================================================================
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//=============================================================================================================================================================================================================
void IdiBusSerial::USART_RxInterruptFunc (void)
{
 *this->TIMN.TCNTN = 0;
 if ( (*this->USARTN.UCSRNA & ( (1U<<USARTN_UCSRNA_FE_Pos)|(1U<<USARTN_UCSRNA_DOR_Pos)|(1U<<USARTN_UCSRNA_UPE_Pos)) ) == 0 )
	{
	 if ( this->RxBufCounter < IDIBUS_SERIAL_RX_BUF_SIZE ) { this->RxBuf[this->RxBufCounter++] = *this->USARTN.UDRN; }
	 else												   { this->ERROR_STATUS.BF.RxOverflow = 1; (void)*this->USARTN.UDRN; }
	}
	else { this->ERROR_STATUS.BF.LLComERR = 1; (void)*this->USARTN.UDRN; }
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void IdiBusSerial::USART_TxInterruptFunc (void)
{
 if ( this->TxBufCounter != this->TxBufLength ) { *this->USARTN.UDRN  = this->TxBuf[this->TxBufCounter++]; }
 else
	{
	 switch (this->RequestType)
		{
		 case (_IDIBUS_SERIAL_TX_RX_) : {
			*this->RS485_DIR_GPIO.PORT &= ~this->RS485_DIR_GPIO.PIN_MASK;
			*this->USARTN.UCSRNB |= (1U<<USARTN_UCSRNB_RXEN_Pos) | (1U<<USARTN_UCSRNB_RXCIE_Pos);
			*this->TIMN.TCCRNB = IDIBUS_SERIAL_TIMER_PR_REG;				//Timer Start
			break; }
		 case (_IDIBUS_SERIAL_TX_) : {
			*this->TIMN.TCCRNB = IDIBUS_SERIAL_TIMER_PR_REG;				//Timer Start		
			break; }
		}
	 }
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void IdiBusSerial::TIM_COMPA_InterruptFunc (void)
{
 switch (this->RequestType)
	{
	 case ( _IDIBUS_SERIAL_TX_RX_  ) : {
			 if ( this->RxBufCounter != 0 )	// Interframe Timeout < Response < Request Timeout
				{
				 *this->TIMN.TCCRNB = 0;																		// Timer Stop
				 *this->USARTN.UCSRNB &= ~( (1U<<USARTN_UCSRNB_RXEN_Pos) | (1U<<USARTN_UCSRNB_RXCIE_Pos) );		// Rx Periph Stop
				 *this->RS485_DIR_GPIO.PORT |= this->RS485_DIR_GPIO.PIN_MASK;									// Dir to TX
				 this->TransferComplete = 1;																	// Complete
				}
			break; }
	 case (_IDIBUS_SERIAL_TX_) : { 
			*this->TIMN.TCCRNB = 0;				// Timer Stop
			this->TransferComplete = 1;			// Complete
			break; }
	}
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void IdiBusSerial::TIM_COMPB_InterruptFunc (void)
{
 if ( this->TimerTimeoutOverflow != 0 )
	{
	 *this->TIMN.OCRNB = this->TimerTimeoutOverflow;
	 *this->TIMN.TCNTN = 0;
	 *this->TIMN.TIFRN = (1U << USARTN_TIM_TIFR_OCFB_Pos);
	 this->TimerTimeoutOverflow = 0;
	}
	else
		{
		 *this->TIMN.TCCRNB = 0;				//Timer Stop
		 *this->USARTN.UCSRNB &= ~( (1U<<USARTN_UCSRNB_RXEN_Pos) | (1U<<USARTN_UCSRNB_RXCIE_Pos) );
		 *this->RS485_DIR_GPIO.PORT |= this->RS485_DIR_GPIO.PIN_MASK;
		 this->ERROR_STATUS.BF.TimeoutERR = 1;
		 this->TransferComplete = 1;
		}
}
//=============================================================================================================================================================================================================
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//=============================================================================================================================================================================================================
uint8_t IdiBusSerial::GetGpioParams(IDIBUS_SERIAL_GPIO_INIT *InitSTR, IDIBUS_SERIAL_GPIO *gpio)
{
 if ( InitSTR->PIN_NUM > 7 ) { return 0; }
 
 if		 ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTA ) { gpio->PORT = &PORTA; gpio->DDR = &DDRA; gpio->PIN = &PINA; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTB ) { gpio->PORT = &PORTB; gpio->DDR = &DDRB; gpio->PIN = &PINB; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTC ) { gpio->PORT = &PORTC; gpio->DDR = &DDRC; gpio->PIN = &PINC; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTD ) { gpio->PORT = &PORTD; gpio->DDR = &DDRD; gpio->PIN = &PIND; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTE ) { gpio->PORT = &PORTE; gpio->DDR = &DDRE; gpio->PIN = &PINE; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTF ) { gpio->PORT = &PORTF; gpio->DDR = &DDRF; gpio->PIN = &PINF; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTG ) { gpio->PORT = &PORTG; gpio->DDR = &DDRG; gpio->PIN = &PING; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTH ) { gpio->PORT = &PORTH; gpio->DDR = &DDRH; gpio->PIN = &PINH; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTJ ) { gpio->PORT = &PORTJ; gpio->DDR = &DDRJ; gpio->PIN = &PINJ; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTK ) { gpio->PORT = &PORTK; gpio->DDR = &DDRK; gpio->PIN = &PINK; }
 else if ( (uint16_t)InitSTR->PORT == (uint16_t)&PORTL ) { gpio->PORT = &PORTL; gpio->DDR = &DDRL; gpio->PIN = &PINL; }
 else													 { return 0; }
 gpio->PIN_MASK = (0x01U << InitSTR->PIN_NUM);
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t IdiBusSerial::GetGpioParamsArduino(uint8_t PinNum, IDIBUS_SERIAL_GPIO *gpio)
{
 uint8_t PortCode;
 if ( (PinNum >= NUM_DIGITAL_PINS) || ((PortCode = digitalPinToPort(PinNum)) == NOT_A_PIN) ) { return 0; }
 gpio->PIN_MASK = digitalPinToBitMask(PinNum); 
 gpio->PORT     = portOutputRegister(PortCode);
 gpio->DDR      = portModeRegister(PortCode);
 gpio->PIN      = portInputRegister(PortCode);
 return 1;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//#############################################################################################################################################################################################################
