Skip to content

Instantly share code, notes, and snippets.

@zekageri
Last active November 26, 2019 12:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zekageri/e54f8837cbb5fe887672ba0d79503e39 to your computer and use it in GitHub Desktop.
Save zekageri/e54f8837cbb5fe887672ba0d79503e39 to your computer and use it in GitHub Desktop.
Modbus C implementation for EFM8UB2
#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
}
}
}
@zekageri
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment