Last active
November 26, 2019 12:23
-
-
Save zekageri/e54f8837cbb5fe887672ba0d79503e39 to your computer and use it in GitHub Desktop.
Modbus C implementation for EFM8UB2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SI_EFM8UB2_Register_Enums.h> | |
#define UART_BUFFERSIZE 64 | |
/* Supported function codes */ | |
#define _READ_SINGLE_REGISTER 0x04 | |
#define _SET_SINGLE_REGISTER 0x06 | |
/** TIMER INTERRUPT INIT **/ | |
#define SYSCLK 12000000/8 // SYSCLK in Hz (12 MHz internal oscillator / 8) the internal oscillator has a tolerance of +/- 2% | |
#define TIMER_PRESCALER 48 // Based on Timer CKCON0 settings | |
#define TOGGLE_RATE 2 // toggle rate in milliseconds if TOGGLE_RATE = 1, It will trigger an interrupt in 1millisec. | |
// There are SYSCLK/TIMER_PRESCALER timer ticks per second, so | |
// SYSCLK/TIMER_PRESCALER/1000 timer ticks per millisecond. | |
#define TIMER_TICKS_PER_MS SYSCLK/TIMER_PRESCALER/1000 | |
// Note: TOGGLE_RATE*TIMER_TICKS_PER_MS should not exceed 65535 (0xFFFF)for the 16-bit timer | |
#define AUX1 TIMER_TICKS_PER_MS*TOGGLE_RATE | |
#define AUX2 -AUX1 | |
#define AUX3 AUX2&0x00FF | |
#define AUX4 ((AUX2&0xFF00)>>8) | |
#define TIMER0_RELOAD_HIGH AUX4 // Reload value for Timer0 high byte | |
#define TIMER0_RELOAD_LOW AUX3 // Reload value for Timer0 low byte | |
/** TIMER INTERRUPT INIT **/ | |
uint8_t UART_Buffer[UART_BUFFERSIZE]; | |
uint8_t UART_Input_First; | |
uint8_t UART_Output_First; | |
uint8_t Register_Response; | |
char Chip_Address; | |
extern uint8_t UART_Buffer_Size; | |
extern uint8_t TX_Ready; | |
extern char Byte; | |
bool CrC_is_Error = false; | |
bool IO_Toggled = false; | |
bool IO_Status = false; | |
void ReLoad_Timeout_Time(){ | |
TH0 = TIMER0_RELOAD_HIGH; // Reinit Timer0 High register | |
TL0 = TIMER0_RELOAD_LOW; // Reinit Timer0 Low register | |
} | |
static void DO_Request_Message(uint8_t Address,uint8_t Code,uint8_t Data1,uint8_t Data2,uint8_t Crc1,uint8_t Crc2){ | |
// I have to do a requet message based on the things i do? | |
int i = 0; | |
if(CrC_is_Error){ | |
CrC_is_Error = false; | |
// CrC Error response message | |
}else if(IO_Toggled){ | |
IO_Toggled = false; | |
// GPIO Toggled message | |
for(i = 0;i<UART_Buffer_Size;i++){ | |
if(i < 1){ | |
UART_Buffer[i] = Address; | |
}else if(i < 2){ | |
UART_Buffer[i] = Code; | |
}else if(i < 3){ | |
UART_Buffer[i] = Data1; | |
}else if(i < 4){ | |
UART_Buffer[i] = Data2; | |
}else if(i < 5){ | |
UART_Buffer[i] = Crc1; | |
}else if(i < 6){ | |
UART_Buffer[i] = Crc2; | |
}else{ | |
break; | |
} | |
} | |
}else if(IO_Status){ | |
IO_Status = false; | |
// GPIO Status message | |
for(i = 0;i<UART_Buffer_Size;i++){ | |
if(i < 1){ | |
UART_Buffer[i] = Address; | |
}else if(i < 2){ | |
UART_Buffer[i] = Code; | |
}else if(i < 4){ | |
UART_Buffer[i] = Register_Response; | |
}else if(i < 5){ | |
UART_Buffer[i] = Crc1; | |
}else if(i < 6){ | |
UART_Buffer[i] = Crc2; | |
}else{ | |
break; | |
} | |
} | |
} | |
} | |
static void Check_GPIO_Status(uint8_t *Data){ | |
if(Data[0] == 1){ | |
Register_Response = P1; | |
}else if(Data[0] == 2){ | |
Register_Response = P2; | |
}else if(Data[0] == 3){ | |
Register_Response = P3; | |
} | |
IO_Status = true; | |
} | |
static void Toggle_IO_Pins(uint8_t *Data){ // Need to toggle the I/O pins based on the master request | |
if(Data[0] == 1){ | |
P1 = Data[1]; | |
}else if(Data[0] == 2){ | |
P2 = Data[1]; | |
}else if(Data[0] == 3){ | |
P3 = Data[1]; | |
} | |
IO_Toggled = true; | |
} | |
static int Check_Chip_Address(uint8_t Msg_Address){ // ADRESS BASED ON THE P0.1-P0.4 GPIOS | |
uint8_t Chip_Address = P0; | |
if(Chip_Address == Msg_Address){ | |
return 1; | |
}else{ | |
return -1; | |
} | |
} | |
static uint16_t Crc_Check(uint8_t *req, uint8_t req_length) | |
{ | |
uint8_t j; | |
uint16_t crc; | |
crc = 0xFFFF; | |
while (req_length--) { | |
crc = crc ^ *req++; | |
for (j = 0; j < 8; j++) { | |
if (crc & 0x0001) | |
crc = (crc >> 1) ^ 0xA001; | |
else | |
crc = crc >> 1; | |
} | |
} | |
return (crc << 8 | crc >> 8); | |
} | |
static int check_integrity(uint8_t *msg, uint8_t msg_length) | |
{ | |
uint16_t crc_calculated; | |
uint16_t crc_received; | |
if (msg_length < 2) | |
return -1; | |
crc_calculated = Crc_Check(msg, msg_length - 2); | |
crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1]; | |
/* Check CRC of msg */ | |
if (crc_calculated == crc_received) { | |
return msg_length; | |
} else { | |
return -1; | |
} | |
} | |
static void Check_Message(){ | |
uint8_t Address,Code; | |
uint8_t Data[2]; | |
uint8_t Crc[2]; | |
int k = 0; | |
int i = 0; | |
// Get the whole message in separate variables for further analization | |
for(i = 0; i < UART_Input_First;i++){ | |
if (i < 1){ | |
Address = UART_Buffer[i]; | |
} | |
else if (i < 2){ | |
Code = UART_Buffer[i]; | |
} | |
else if (i <= 3){ | |
if(i < 3){ | |
Data[0] = UART_Buffer[i]; | |
}else{ | |
Data[1] = UART_Buffer[i]; | |
} | |
}else if (i >= 4){ | |
Crc[k] = UART_Buffer[i]; | |
k++; | |
} | |
} | |
// Checking the separated variables | |
if(Check_Chip_Address(Address) != -1){ // Check if Message is ours. | |
if(check_integrity(Crc,UART_Buffer_Size) != -1){ // Check if no errors on crc | |
CrC_is_Error = false; | |
if(Code == _READ_SINGLE_REGISTER){ | |
/** MASTER WANTS TO READ THE GPIOS **/ | |
Check_GPIO_Status(Data); | |
}else if(Code == _SET_SINGLE_REGISTER){ | |
/** MASTER WANTS TO TOGGLE THE GPIOS **/ | |
Toggle_IO_Pins(Data); | |
} | |
}else{ | |
CrC_is_Error = true; | |
} | |
DO_Request_Message(Address,Code,Data[0],Data[1],Crc[0],Crc[1]); | |
} | |
} | |
SI_INTERRUPT(TIMER0_ISR, TIMER0_IRQn) // Timer interrupt fired, data transfer end. | |
{ | |
Check_Message(); // Get the whole message and brake it into pieces | |
} | |
//----------------------------------------------------------------------------- | |
// | |
// UART0 ISR Content goes here. Remember to clear flag bits: | |
// SCON0::RI (Receive Interrupt Flag) | |
// SCON0::TI (Transmit Interrupt Flag) | |
// | |
//----------------------------------------------------------------------------- | |
SI_INTERRUPT(UART0_ISR, UART0_IRQn) | |
{ | |
if (SCON0_RI == 1) | |
{ | |
if( UART_Buffer_Size == 0){ // If new word is entered | |
UART_Input_First = 0; | |
} | |
ReLoad_Timeout_Time(); | |
SCON0_RI = 0; // Clear interrupt flag | |
Byte = SBUF0; // Read a character from UART | |
if (UART_Buffer_Size < UART_BUFFERSIZE) | |
{ | |
UART_Buffer[UART_Input_First] = Byte; // Store in array | |
UART_Buffer_Size++; // Update array's size | |
UART_Input_First++; // Update counter | |
} | |
} | |
//----------------------------------------------------------------------------- | |
// ECHO BACK THE MESSAGE | |
//----------------------------------------------------------------------------- | |
if (SCON0_TI == 1) // Check if transmit flag is set | |
{ | |
SCON0_TI = 0; // Clear interrupt flag | |
if (UART_Buffer_Size != 1) // If buffer not empty | |
{ | |
// If a new word is being output | |
if ( UART_Buffer_Size == UART_Input_First ) { | |
UART_Output_First = 0; } | |
// Store a character in the variable byte | |
Byte = UART_Buffer[UART_Output_First]; | |
SBUF0 = Byte; // Transmit to Hyperterminal | |
UART_Output_First++; // Update counter | |
UART_Buffer_Size--; // Decrease array size | |
} | |
else | |
{ | |
UART_Buffer_Size = 0; // Set the array size to 0 | |
TX_Ready = 1; // Indicate transmission complete | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Not working code. Just trying to implement it to the EFM8UB2 microcontroller in Simplicity Studio.
https://www.silabs.com/documents/public/data-sheets/efm8ub2-datasheet.pdf