hi every body.
Actually i make some profibus test with Arduino nano 33 iot (original) which i was able to speed up to 0.5Mbs/s. Same i have done with Arduino nano v3(Chinese fake), included slave address store in EEprom and automatic baud rate
Now starting the frustration. impossible for me to reach 1.5Mbt/s, and unfortunately my target is 12Mbs/s.
Need help.
For ho has some interesting i can share code, documentation and electrical diagrams.
Thanks in advance.
HI,
Have you googled
arduino 12Mbits profibus
You may be pushing 12Mbits with 16Mhz clock.
Code etc would be appreciated.
Tom...
Thanks for answered.
Arduino nano v3 i know is 16Hz clock and in according datasheet can reach 1Mbit/s.
I reach 0.5 because i use normal baud rate in S7 HW config but Arduino nano 33 iot is 48Mz clock and can reach 12Mbit/s. Unfortunately at 1.5Mbt/s i can 't synchronize. for RS485 am using IL3685 with galvanic power supply with DCP010507.
Hi,
Some links to these devices would be good, and a schematic.
Thanks.. Tom...
general connection RS485 download from internet
I have possibility to test in real time with cpu S7-400, scope and profi trace
. INO code:
//This is adapt for Atmega328 (ARDUINO NANO)
//For any other CPU variation adjast pin number and related cpu registers(UART, Timers, interrupts)
#include <EEPROM.h>
#include <avr/wdt.h>
#include "profibus.h"
//slave hardware address
#define addr_6 9 // 64
#define addr_5 8 // 32
#define addr_4 7 // 16
#define addr_3 6 // 8
#define addr_2 5 // 4
#define addr_1 4 // 2
#define addr_0 3 // 1
#define LED_ERROR_ON PORTB |= _BV (5); // digitalWrite (13, HIGH);
#define LED_ERROR_OFF PORTB &= ~_BV (5); // digitalWrite (13, LOW);
#define TX_ENABLE_ON PORTD |= _BV (2); // digitalWrite (2, HIGH);
#define TX_ENABLE_OFF PORTD &= ~_BV (2); // digitalWrite (2, LOW);
#define TIMER1_RUN TCCR1B |= (1 << CS11);
#define TIMER1_STOP TCCR1B &= ~(1 << CS11);
///// test only
#include <Wire.h>
#include "U8glib.h"
#define setFont_0 u8g_font_timB14
#define setFont_1 u8g_font_9x15
#define setFont_2 u8g_font_fixed_v0r
#define setFont_3 u8g_font_timB18
#define setFont_4 u8g_font_timB14
#define setFont_5 u8g_font_timB10
#define setFont_6 u8g_font_5x7
#define setFont_7 u8g_font_6x10
#define setFont_8 u8g_font_7x13
#define setFont_9 u8g_font_9x15
#define setFont_10 u8g_font_unifont
#define setFont_11 u8g_font_fixed_v0r
#define setFont_12 u8g_font_chikitar
#define setFont_num :u8g_font_freedoomr25n
U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NO_ACK); // Display which does not send AC
///////////
long int BAUD[] {12000000, 6000000, 3000000, 1500000, 500000, 187500, 93750, 45450, 19200, 9600};
uint16_t DELAY_TBIT;
uint16_t TIMEOUT_MAX_SYN_TIME; //33 * DELAY_TBIT // 33 TBit = TSYN
uint16_t TIMEOUT_MAX_RX_TIME; //16 * DELAY_TBIT
uint16_t TIMEOUT_MAX_TX_TIME; //16 * DELAY_TBIT
uint16_t TIMEOUT_MAX_SDR_TIME; //16 * DELAY_TBIT // 15 Tbit = TSDR
unsigned long samplingtime = 0;
unsigned char uart_buffer[MAX_BUFFER_SIZE];
unsigned int uart_byte_cnt = 0;
unsigned int uart_transmit_cnt = 0;
// Profibus Flags and Variables
unsigned char profibus_status;
unsigned char diagnose_status_1;
unsigned char diagnose_status_2;
unsigned char slave_addr;
unsigned char master_addr;
unsigned char Group;
unsigned char Watchdog; //ms
volatile unsigned char new_data;
//bool init_data_ok = false;
#if (MAX_OUTPUT_DATA_SIZE > 0)
volatile unsigned char Profibus_out_register[MAX_OUTPUT_DATA_SIZE];
#endif
#if (MAX_INPUT_DATA_SIZE > 0)
unsigned char Profibus_in_register [MAX_INPUT_DATA_SIZE];
#endif
#if (MAX_USER_PARA_SIZE > 0)
unsigned char User_Para[MAX_USER_PARA_SIZE];
#endif
#if (MAX_VENDOR_DATA_SIZE > 0)
unsigned char Vendor_Data[MAX_VENDOR_DATA_SIZE];
#endif
#if (MAX_EXT_DIAG_DATA_SIZE > 0)
unsigned char Diag_Data[MAX_EXT_DIAG_DATA_SIZE];
#endif
unsigned char Input_Data_size;
unsigned char Output_Data_size;
unsigned char User_Para_size;
unsigned char Vendor_Data_size; // Number of read-in manufacturer-specific bytes
////test
//unsigned char var_test[255];
uint16_t var_test[255];
float RX_time[5];
bool init_time;
float elapsedTime, currentTime, previousTime;
////
void setup()
{
////test
//clear variables
for (int i = 0; i <= 254; i++) {
var_test[i] = 0x00;
}
for (int j = 0; j <= 5; j++) {
RX_time[j] = 0x00;
}
////////
DDRB |= _BV (5); // pinMode (13, OUTPUT) LED_ERROR_PIN;
DDRD |= _BV (2); // pinMode (2, OUTPUT) TX_ENABLE_PIN;
LED_ERROR_ON;
get_Address();
init_Profibus_var();
BAUD_SEARCH(1);
TX_ENABLE_OFF; // Disable Transmit (Switch to Recieve)
}
///////////////////////////////////////////////////////////////////
void loop() {
//////////////////////////////test only
u8g.firstPage();
do {
u8g.setFont(setFont_7);
u8g.setPrintPos(0,7);
u8g.print("B0");
u8g.setFont(setFont_7);
u8g.setPrintPos(16,7);
u8g.print(var_test[0]);//value
u8g.setFont(setFont_7);
u8g.setPrintPos(0,17);
u8g.print("B1");
u8g.setFont(setFont_7);
u8g.setPrintPos(16,17);
// u8g.print(RX_time[0]);//value
u8g.print(var_test[1]);//value
u8g.setFont(setFont_7);
u8g.setPrintPos(0,27);
u8g.print("B2");
u8g.setFont(setFont_7);
u8g.setPrintPos(16,27);
// u8g.print(RX_time[1]);//value
u8g.print(var_test[2]);//value
//
u8g.setFont(setFont_7);
u8g.setPrintPos(44,7);
u8g.print("B3");
u8g.setFont(setFont_7);
u8g.setPrintPos(60,7);
u8g.print(var_test[3]);//value
u8g.setFont(setFont_7);
u8g.setPrintPos(44,17);
u8g.print("B4");
u8g.setFont(setFont_7);
u8g.setPrintPos(60,17);
u8g.print(var_test[4]);//value
u8g.setFont(setFont_7);
u8g.setPrintPos(44,27);
u8g.print("B5");
u8g.setFont(setFont_7);
u8g.setPrintPos(60,27);
u8g.print(var_test[5]);//value
//
u8g.setFont(setFont_7);
u8g.setPrintPos(88,7);
u8g.print("B6");
u8g.setFont(setFont_7);
u8g.setPrintPos(104,7);
u8g.print(var_test[6]);//value
u8g.setFont(setFont_7);
u8g.setPrintPos(88,17);
u8g.print("B7");
u8g.setFont(setFont_7);
u8g.setPrintPos(104,17);
u8g.print(var_test[7]);//value
u8g.setFont(setFont_7);
u8g.setPrintPos(88,27);
u8g.print("B8");
u8g.setFont(setFont_7);
u8g.setPrintPos(104,27);
u8g.print(var_test[8]);//value
} while( u8g.nextPage() );
/////////////////////////////////////
if ( (unsigned long) (micros() - samplingtime) > 10 )
{
// Profibus_in_register[1] =Profibus_out_register[1];
doit();
samplingtime = micros();
}
/*
if(new_data==1)
{
Profibus_out_register[0] +=new_data;
new_data=0;
Profibus_out_register[0] = Profibus_in_register[0];
}
*/
// digitalWrite(LED_PIN, bitRead(Profibus_in_register[0],0)==0?1:0); // Correct the led state on Touch button
}
///////////////////////////////////////////////////////////////////
void init_Profibus_var()
{
unsigned char cnt;
// Variable init
profibus_status = PROFIBUS_WAIT_SYN; // Wait at least Tsyn until allowing RXdata
diagnose_status_1 = STATION_NOT_READY_;
diagnose_status_2 = STATUS_2_DEFAULT_;
Input_Data_size = 0;
Output_Data_size = 0;
User_Para_size = 0;
Vendor_Data_size = 0;
Group = 0;
//slave_addr = SLAVE_ADDRESS; // <<< Temporary address assignment.
slave_addr = get_Address();
// TODO: Read address from EEPROM or switches.
// Illegal addresses are forced to DEFAULT (126). Set Address can be used to change it.
if((slave_addr == 0) || (slave_addr > 126))
slave_addr = DEFAULT_ADD;
// Clear data
#if (MAX_OUTPUT_DATA_SIZE > 0)
memset (Profibus_out_register, 0x00, MAX_OUTPUT_DATA_SIZE);
#endif
#if (MAX_INPUT_DATA_SIZE > 0)
memset (Profibus_in_register, 0x00, MAX_INPUT_DATA_SIZE);
#endif
#if (MAX_USER_PARA_SIZE > 0)
memset (User_Para, 0x00, MAX_USER_PARA_SIZE);
#endif
#if (MAX_VENDOR_DATA_SIZE > 0)
memset (Vendor_Data, 0x00, MAX_VENDOR_DATA_SIZE);
#endif
#if (MAX_DIAG_DATA_SIZE > 0)
memset (Diag_Data, 0x00, MAX_DIAG_DATA_SIZE);
#endif
new_data=0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
void init_CPU(int16_t idx)
{
// noInterrupts(); // Disable all interrupts
cli();
init_UART(BAUD[idx]);
TCCR1A = 0x00;
TCCR1B = 0x00;
TCNT1 = 0x00;
OCR1A = TIMEOUT_MAX_SYN_TIME;
TCCR1B |= (1 << WGM12); // CTC mode update Immediate (OCR1A)
TCCR1B |= (1 << CS11); // Prescaler (/8) = 2000000 Mhz
TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt
sei();
// interrupts(); // Enable all interrupts
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*!
- ISR UART0 Receive
/
ISR(USART_RX_vect)
{
/ Wait for data to be received */
while (!(UCSR0A & (1<<RXC0)));
// Retrieve RXdata to buffer
uart_buffer[uart_byte_cnt] = UDR0;// Load UART register into Buffer
// Only read data after Tsyn have expired
if (profibus_status == PROFIBUS_WAIT_DATA)
{
profibus_status = PROFIBUS_GET_DATA;
}
// Read data allowed?
if (profibus_status == PROFIBUS_GET_DATA)
{
uart_byte_cnt++;
// Check for buffer overflow!
if(uart_byte_cnt >= MAX_BUFFER_SIZE) uart_byte_cnt--;
}
TCNT1 = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
void profibus_RX (void)
{
unsigned char cnt;
unsigned char telegramm_type;// identifier SD1,SD2,SD3,SD4
unsigned char process_data;// Data receive valid format
// Profibus data types
unsigned char destination_add;
unsigned char source_add;
unsigned char function_code;
unsigned char FCS_data; // Frame Check Sequence
unsigned char PDU_size; // PDU Size
unsigned char DSAP_data; // SAP Destination
unsigned char SSAP_data; // SAP Source
unsigned char DPV1_Read;
unsigned char Slot_Number;
unsigned char Index;
unsigned char Read_req_Lenght;
//Clear variables
process_data = false;
telegramm_type = uart_buffer[0];
switch (telegramm_type)
{
case SD1: // Telegram without data, max. 6 bytes
if (uart_byte_cnt != 6) break;
//uart_buffer[0] = SD1 0x10 // Telegram without data field
destination_add = uart_buffer[1];
source_add = uart_buffer[2];
function_code = uart_buffer[3];
FCS_data = uart_buffer[4];//(Frame Check Sequence DA + SA + FC)
//Check message consistance and end message charter frame ED
if ((addmatch(destination_add) == false) || (checksum(&uart_buffer[1], 3) != FCS_data) || (uart_buffer[5] != ED)) break;
//init_data_ok = true;
process_data = true;
break;
case SD2: // Telegram with variable length of 4 to 249 bytes and therefore a payload in the range 1 to 246 bytes
// Telegram can' t be <4 bytes
if (uart_byte_cnt != uart_buffer[1] + 6) break;
//uart_buffer[0] = SD2 0x68 // Telegram with variable length
PDU_size = uart_buffer[1];
//uart_buffer[2] = Length (not include in FCS as standard protocol)
//uart_buffer[3] = Length repeated (not include in FCS as standard protocol)
destination_add = uart_buffer[4];
source_add = uart_buffer[5];
function_code = uart_buffer[6];
FCS_data = uart_buffer[PDU_size +4];//(Frame Check Sequence DA + SA + FC + PDU)
//Check message consistance and end message charter frame ED
if ((addmatch(destination_add) == false) || (checksum(&uart_buffer[4], PDU_size) != FCS_data) || (uart_buffer[PDU_size +5] != ED)) break;
//init_data_ok = true;
process_data = true;
break;
case SD3: // Telegram with 5 bytes data, max. 11 bytes
if (uart_byte_cnt != 11) break;
//uart_buffer[0] = SD3 0xA2 // Data telegram fixed
PDU_size = 8; //The PDU has a fixed length of 8 bytes.
destination_add = uart_buffer[1];
source_add = uart_buffer[2];
function_code = uart_buffer[3];
FCS_data = uart_buffer[9];
//Check message consistance and end message charter frame ED
if ((addmatch(destination_add) == false) || (checksum(&uart_buffer[1], PDU_size) != FCS_data) || (uart_buffer[10] != ED)) break;
//init_data_ok = true;
process_data = true;
break;
case SD4: // Token with 3 Byte Data
if (uart_byte_cnt != 3) break;
destination_add = uart_buffer[1];
source_add = uart_buffer[2];
//Check only message consistance because no end Charter frame
if (addmatch(destination_add) == false) break;
break;
default:
process_data = false;
break;
}
// Only evaluate if data is OK
if (process_data == true)
{
LED_ERROR_OFF;
wdt_reset(); //Reset the WDT
master_addr = source_add; // Master address is Source address
//Service Access Point detected?
if ((destination_add & 0x80) && (source_add & 0x80))
{
DSAP_data = uart_buffer[7];
SSAP_data = uart_buffer[8];
// 1) SSAP 62 -> DSAP 60 (Get Diagnostics Request)
// 2) SSAP 62 -> DSAP 61 (Set Parameters Request)
// 3) SSAP 62 -> DSAP 62 (Check Config Request)
// 4) SSAP 62 -> DSAP 60 (Get Diagnostics Request)
// 5) Data Exchange Request (normal cycle)
switch (DSAP_data)
{
case DS_REQ: //Actual data request from Master (on line monitoring)
//(REQUEST_ + FCV_ + SRD_LOW) 5C
//(REQUEST_ + FCV_ + FCB_ + SRD_LOW) 7C
if (function_code == (REQUEST_ + FCV_ + SRD_LOW) || (REQUEST_ + FCV_ + FCB_ + SRD_LOW))
{
// uart_buffer[7] DSAP_data
// uart_buffer[8] SSAP_data
// uart_buffer[9] = DPV1_Read;
// uart_buffer[10] = Slot_Number;
// uart_buffer[11] = Index;
// uart_buffer[12] = Read_req_Lenght;
uart_buffer[12] = 2; // Standard diagnostic 2 bytes
uart_buffer[13] = 0x0;
uart_buffer[14] = 0x0;
// NO STANDARD DIAGNOSTIC INTERRUPT
//profibus_send_CMD(SD2, SAP_DISAB, SAP_OFFSET, &uart_buffer[7], 8);
// STANDARD DIAGNOSTIC INTERRUPT
profibus_send_CMD(SD2, DATA_LOW, SAP_OFFSET, &uart_buffer[7], 8);
}
break;
case SAP_SET_SLAVE_ADR: // Set Slave Address (SSAP 62 -> DSAP 55)
// Only possible in the "Wait Parameter" (WPRM) state
//Telegram to change address has 4 bytes
//new_addr = uart_buffer[9];
//IDENT_HIGH_BYTE = uart_buffer[10];
//IDENT_LOW_BYTE = uart_buffer[11];
//if (uart_buffer[12] & 0x01) address_Change_lock = TRUE;
//Note:
// The PROFIBUS address of a DP slave can be adjusted in two ways:
//1.Through settings on the device with the help of switches or another control interface.
//2.By sending a special telegram via the PROFIBUS. Address 126 is reserved as the default address for a DP slave.
//After the address has been changed, it is necessary to execute a cold-start (power supply off and on again) of the DP slave.
//uart_buffer[12] bit 0 depend aboud GSD configuration if the device support change address function.
//GSD PARAMETER -> Set_Slave_Add_supp = 1
// Only accept data for our device
if ((uart_buffer[10] == IDENT_HIGH_BYTE) && (uart_buffer[11] == IDENT_LOW_BYTE) && (uart_buffer[12] == 0x00))
{
//New slave address must store in program memory.(actually this part store new value in local data variable.
//and the address is not manage to be read from eeprom memory.
slave_addr = uart_buffer[9];
profibus_send_CMD(SC, 0, SAP_OFFSET, &uart_buffer[0], 0);
}
break;
case SAP_RD_INP: // Master Get Input data (SSAP 62 -> DSAP 56)
break;
case SAP_RD_OUTP: // Master Get Output data (SSAP 62 -> DSAP 57)
break;
case SAP_GLOBAL_CONTROL: // Global Control Request (SSAP 62 -> DSAP 58)
// If "Clear Data" high, then PLC CPU on "Stop"
if (CLEAR_DATA_ == (uart_buffer[9] & CLEAR_DATA_))
{
LED_ERROR_ON; // Status "PLC not ready"
}
else
{
LED_ERROR_OFF; // Status "PLC OK"
}
// Calculate Group
for (cnt = 0; uart_buffer[10] != 0; cnt++) uart_buffer[10]>>=1;
// If command is for us
if (cnt == Group)
{
if (UNFREEZE_ == (uart_buffer[9] & UNFREEZE_))
{
// Delete FREEZE state
}
else if (UNSYNC_ == (uart_buffer[9] & UNSYNC_))
{
// Delete SYNC state
}
else if (FREEZE_ == (uart_buffer[9] & FREEZE_))
{
// Do not read inputs again
}
else if (SYNC_ == (uart_buffer[9] & SYNC_))
{
// Set outputs only with SYNC command
}
}
break;
case SAP_GET_CFG: // Get Configuration Request (SSAP 62 -> DSAP 59)
break;
case SAP_SLAVE_DIAGNOSIS: // Get Diagnostics Request (SSAP 62 -> DSAP 60)
// After receiving the diagnosis, the DP slave changes state
// "Power on Reset" (POR) in the state "Wait Parameter" (WPRM)
// At the end of initialization ("Data Exchange" state (DXCHG))
// the master sends a Diagnostics Request a second time to check correct configuration
//REQUEST_ + FCB_ + SRD_HIGH = 6D (first diagnostic request)
if (function_code == (REQUEST_ + FCB_ + SRD_HIGH))
{
// First diagnostic request (call Telegram)
//uart_buffer[4] = master_addr; // Target Master (with SAP Offset)
//uart_buffer[5] = slave_addr + SAP_OFFSET; // Source Slave (with SAP Offset)
//uart_buffer[6] = SLAVE_DATA
uart_buffer[7] = SSAP_data; // Target SAP Master
uart_buffer[8] = DSAP_data; // Source SAP Slave
uart_buffer[9] = diagnose_status_1; // Status Byte 1
uart_buffer[10] = STATUS_2_DEFAULT_ + PRM_REQ_; // Status Byte 2
uart_buffer[11] = DIAG_SIZE_OK; // Status Byte 3
uart_buffer[12] = MASTER_ADD_DEFAULT; // Status Byte 4 Master Address
uart_buffer[13] = IDENT_HIGH_BYTE; // Ident high
uart_buffer[14] = IDENT_LOW_BYTE; // Ident low
#if (MAX_EXT_DIAG_DATA_SIZE > 0)
uart_buffer[15] = MAX_EXT_DIAG_DATA_SIZE; // Device-related diagnosis (number of bytes)
/*
for (cnt = 0; cnt < MAX_EXT_DIAG_DATA_SIZE; cnt++)
{
uart_buffer[16+cnt] = Diag_Data[cnt];
}
*/
memcpy(&uart_buffer[16], &Diag_Data[0], MAX_EXT_DIAG_DATA_SIZE);
#endif
profibus_send_CMD(SD2, DATA_LOW, SAP_OFFSET, &uart_buffer[7], 8 + MAX_EXT_DIAG_DATA_SIZE);
}
//REQUEST_ + FCV_ + SRD_HIGH = 5D (Setting outputs, reading inputs)
//REQUEST_ + FCV_ + FCB_ + SRD_HIGH = 7D (Setting outputs, reading inputs (toggle)
else if (function_code == (REQUEST_ + FCV_ + SRD_HIGH) ||
function_code == (REQUEST_ + FCV_ + FCB_ + SRD_HIGH))
{
// Diagnostic request to check data from Check Config Request
uart_buffer[7] = SSAP_data; // Target SAP Master
uart_buffer[8] = DSAP_data; // Source SAP slave
/*
if (diagnose_status_1 == true)
uart_buffer[9] = EXT_DIAG_; // Status 1
else
*/
uart_buffer[9] = diagnose_status_1; // Status 1
uart_buffer[10] = diagnose_status_2; // Status 2
uart_buffer[11] = DIAG_SIZE_OK; // Status 3
uart_buffer[12] = master_addr - SAP_OFFSET; // Address Master
uart_buffer[13] = IDENT_HIGH_BYTE; // Ident high
uart_buffer[14] = IDENT_LOW_BYTE; // Ident low
//uart_buffer[15] = 0x05; // Device-related diagnostics (number of bytes)
//uart_buffer[16] = 0x00; //
//uart_buffer[17] = 0x20;
//uart_buffer[18] = 0x20;
//uart_buffer[19] = 0x00;
#if (MAX_EXT_DIAG_DATA_SIZE > 0)
uart_buffer[15] = MAX_EXT_DIAG_DATA_SIZE; // Device-related diagnosis (number of bytes)
/*
for (cnt = 0; cnt < MAX_EXT_DIAG_DATA_SIZE; cnt++)
{
uart_buffer[16+cnt] = Diag_Data[cnt];
}
*/
memcpy(&uart_buffer[16], &Diag_Data[0], MAX_EXT_DIAG_DATA_SIZE);
profibus_send_CMD(SD2, DATA_LOW, SAP_OFFSET, &uart_buffer[7], 8 + MAX_EXT_DIAG_DATA_SIZE);
#else
profibus_send_CMD(SD2, DATA_LOW, SAP_OFFSET, &uart_buffer[7], 8);
#endif
}
break;
case SAP_SET_PRM: // Set Parameters Request (SSAP 62 -> DSAP 61)
// After receiving the parameters, the DP slave changes state
// "Wait Parameter" (WPRM) in the state "Wait Configuration" (WCFG)
//uart_buffer[7] SSAP_data
//uart_buffer[8] DSAP_data
//uart_buffer[9] Set_Parameter byte 1: Station status
//uart_buffer[10] Set_Parameter bytes 2 Watchdog factor_1
//uart_buffer[11] Set_Parameter bytes 3 Watchdog factor_2
//uart_buffer[12] Set_Parameter byte 4: minimum slave response time (Min. TSDR in tBit)
//uart_buffer[13] Diagnostic bytes 5 Ident number high byte
//uart_buffer[14] Diagnostic bytes 6 Ident number low byte
//uart_buffer[15] Set_Parameter Byte 7: group allocation
//uart_buffer[16] Set_Parameter Byte 8: DPV1_Status_1 (Note: only if GSD parameter -> DPV1_Slave = 1
//uart_buffer[17] Set_Parameter Byte 9: DPV1_Status_2
//uart_buffer[18] Set_Parameter Byte 10: DPV1_Status_3
// Only accept data for our device
if ((uart_buffer[13] == IDENT_HIGH_BYTE) && (uart_buffer[14] == IDENT_LOW_BYTE))
{
//Set_Parameter byte 1: Station status
if (ACTIVATE_WATCHDOG_ == (uart_buffer[9] & ACTIVATE_WATCHDOG_))
diagnose_status_2 |= WD_ON_;
//If this bit is set to 0, watchdog monitoring will be disabled.
else
diagnose_status_2 &= ~(WD_ON_);
if (ACTIVATE_FREEZE_ == (uart_buffer[9] & ACTIVATE_FREEZE_ ))
//This bit indicates to the DP slave that it is to be operated in Sync-Mode, as soon as the command with the Global_Control function has been transferred.
//If a DP slave does not support the freeze command, it must set Not_Supported in the diagnostic data.
//This request during parameter setting avoids errors when running with payload data.
diagnose_status_2 |= FREEZE_MODE_;
else
diagnose_status_2 &= ~(FREEZE_MODE_);
if (ACTIVATE_SYNC_ == (uart_buffer[9] & ACTIVATE_SYNC_ ))
//This bit indicates to the DP slave that it is to be operated in Freeze-Mode, as soon as the command with the Global_Control function has been transferred.
//If a DP slave does not support the Sync command, it must set Not_Supported in the diagnostic data.
//This request during parameter setting avoids errors when running with payload data.
diagnose_status_2 |= SYNC_MODE_;
else
diagnose_status_2 &= ~(SYNC_MODE_);
if (UNLOCK_SLAVE_ == (uart_buffer[9] & UNLOCK_SLAVE_))
{
//The DP master sets this bit to 1 when access to a DP slave is to be enabled again for another DP master. This bit has priority over bit 7/ Lock_Req.
}
if (LOCK_SLAVE_ == (uart_buffer[9] & LOCK_SLAVE_))
{
//The DP master sets this bit to 1 when other masters are to be blocked from accessing a DP slave.
}
//Set_Parameter Byte 8: DPV1_Status_1
if (DPV1_MODE_ == (uart_buffer[16] & DPV1_MODE_))
{
//The slave should work in DP-V1 mode
}
if (FAIL_SAVE_MODE_ == (uart_buffer[16] & FAIL_SAVE_MODE_))
{
//The slave should work in Fail_Safe mode
}
if (PUBLISHER_MODE_ == (uart_buffer[16] & PUBLISHER_MODE_))
{
//The slave should work as Publisher
}
if (ACTIVATE_WATCHDOG_ == (uart_buffer[9] & ACTIVATE_WATCHDOG_))
{
//note: only if GSD parameter -> WD_Base_1ms_supp = 1
if (WATCHDOG_TB_1MS == (uart_buffer[16] & WATCHDOG_TB_1MS) && DPV1_MODE_ == (uart_buffer[16] & DPV1_MODE_))
{
//Time base of watchdog is 1 ms (and not 10 ms)
//Watchdog = factor_1 * factor_2 * 1 ms
Watchdog = uart_buffer[10] * uart_buffer[11];
}
else
{
//Watchdog = factor_1 * factor_2 * 10 ms
Watchdog = uart_buffer[10] * uart_buffer[11] *10;
}
}
else
{
Watchdog = TIMEOUT_MAX_SDR_TIME;
}
// User Parameter Size = Length - DA, SA, FC, DSAP, SSAP, 7 Parameter Bytes
Vendor_Data_size = PDU_size - 12;
// Read in user parameters
#if (VENDOR_DATA_SIZE > 0)
for (cnt = 0; cnt < Vendor_Data_size; cnt++) Vendor_Data[cnt] = uart_buffer[16+cnt];
#endif
// Read Group
for (Group = 0; uart_buffer[15] != 0; Group++) uart_buffer[15]>>=1;
profibus_send_CMD(SC, 0, SAP_OFFSET, &uart_buffer[0], 0);
}
break;
case SAP_CHK_CFG: // Check Config Request (SSAP 62 -> DSAP 62)
// After receiving the configuration, the DP slave changes state
// "Wait Configuration" (WCFG) in the "Data Exchange" state (DXCHG)
// IO configuration:
// Compact format for max. 16/32 bytes IO
// special format for max. 64/132 bytes IO
// evaluate several bytes depending on the PDU data size
// LE / LEr - (DA + SA + FC + DSAP + SSAP) = number of config bytes
Output_Data_size=0;
Input_Data_size=0;
for (cnt = 0; cnt < uart_buffer[1] - 5; cnt++)
{
switch (uart_buffer[9+cnt] & CFG_DIRECTION_)
{
case CFG_INPUT:
Input_Data_size += (uart_buffer[9+cnt] & CFG_BYTE_CNT_) + 1;
if (uart_buffer[9+cnt] & CFG_WIDTH_ & CFG_WORD)
Input_Data_size += Input_Data_size*2;
break;
case CFG_OUTPUT:
Output_Data_size += (uart_buffer[9+cnt] & CFG_BYTE_CNT_) + 1;
if (uart_buffer[9+cnt] & CFG_WIDTH_ & CFG_WORD)
Output_Data_size += Output_Data_size*2;
break;
case CFG_INPUT_OUTPUT:
Input_Data_size += (uart_buffer[9+cnt] & CFG_BYTE_CNT_) + 1;
Output_Data_size += (uart_buffer[9+cnt] & CFG_BYTE_CNT_) + 1;
if (uart_buffer[9+cnt] & CFG_WIDTH_ & CFG_WORD)
{
Input_Data_size += Input_Data_size*2;
Output_Data_size += Output_Data_size*2;
}
break;
case CFG_SPECIAL:
// Special format
// Manufacturer-specific bytes available?
if (uart_buffer[9+cnt] & CFG_SP_VENDOR_CNT_)
{
// Save the number of manufacturer data
Vendor_Data_size = uart_buffer[9+cnt] & CFG_SP_VENDOR_CNT_;
// Deduct number of total
uart_buffer[1] -= Vendor_Data_size;
}
// I/O Data
switch (uart_buffer[9+cnt] & CFG_SP_DIRECTION_)
{
case CFG_SP_VOID:
// Empty data field
break;
case CFG_SP_INPUT:
Input_Data_size += (uart_buffer[10+cnt] & CFG_SP_BYTE_CNT_) + 1;
if (uart_buffer[10+cnt] & CFG_WIDTH_ & CFG_WORD)
Input_Data_size += Input_Data_size*2;
cnt++; // We already have this byte
break;
case CFG_SP_OUTPUT:
Output_Data_size += (uart_buffer[10+cnt] & CFG_SP_BYTE_CNT_) + 1;
if (uart_buffer[10+cnt] & CFG_WIDTH_ & CFG_WORD)
Output_Data_size += Output_Data_size*2;
cnt++; //We already have this byte
break;
case CFG_SP_INPUT_OPTPUT:
// Erst Ausgang...
Output_Data_size += (uart_buffer[10+cnt] & CFG_SP_BYTE_CNT_) + 1;
if (uart_buffer[10+cnt] & CFG_WIDTH_ & CFG_WORD)
Output_Data_size += Output_Data_size*2;
// Dann Eingang
Input_Data_size += (uart_buffer[11+cnt] & CFG_SP_BYTE_CNT_) + 1;
if (uart_buffer[11+cnt] & CFG_WIDTH_ & CFG_WORD)
Input_Data_size += Input_Data_size*2;
cnt += 2; // We already have these bytes
break;
} // Switch End
break;
default:
Input_Data_size = 0;
Output_Data_size = 0;
break;
} // Switch End
} // For End
if (Vendor_Data_size != 0)
{
// Evaluate
}
/*
//In case of error -> send CFG_FAULT_ to diagnosis
#if (VENDOR_DATA_SIZE > 0)
if (Module_cnt > MODULE_CNT || Vendor_Data_size != VENDOR_DATA_SIZE)
diagnose_status_1 |= CFG_FAULT_;
#else
if (Module_cnt > MODULE_CNT)
diagnose_status_1 |= CFG_FAULT_;
#endif
else
diagnose_status_1 &= ~(STATION_NOT_READY_ + CFG_FAULT_);
//diagnose_status_1 = 0X00;
#endif
*/
// short acknowledgment
if ((Input_Data_size >0) && (Input_Data_size <= MAX_INPUT_DATA_SIZE) &&
(Output_Data_size >0) && (Output_Data_size <= MAX_OUTPUT_DATA_SIZE))
{
diagnose_status_1 = STATUS_1_DEFAULT;
}
else
{
diagnose_status_1 |= CFG_FAULT_;
}
profibus_send_CMD(SC, 0, SAP_OFFSET, &uart_buffer[0], 0);
break;
default:
// Unknown SAP
break;
} //Switch DSAP_data end
}
// Destination: Slave address
else if (destination_add == slave_addr)
{
// Status request
//REQUEST_ + FDL_STATUS 0x49 (status request)
if (function_code == (REQUEST_ + FDL_STATUS))
{
profibus_send_CMD(SD1, FDL_STATUS_OK, 0, &uart_buffer[0], 0);
}
// Master sends output data and requests input data(Send and Request Data)
//REQUEST_ + FCV_ + SRD_HIGH 0x5D (send data request)
//REQUEST_ + FCV_ + FCB_ + SRD_HIGH 0x7D (Setting outputs, reading inputs (toggle))
else if (function_code == (REQUEST_ + FCV_ + SRD_HIGH) ||
function_code == (REQUEST_ + FCV_ + FCB_ + SRD_HIGH))
{
/*
// Read data from master
#if (MAX_INPUT_DATA_SIZE > 0)
for (cnt = 0; cnt < MAX_INPUT_DATA_SIZE; cnt++)
{
Profibus_in_register[cnt] = uart_buffer[cnt + 7];
new_data=1;
}
#endif
// Write data for master in buffer
#if (MAX_OUTPUT_DATA_SIZE > 0)
for (cnt = 0; cnt < MAX_OUTPUT_DATA_SIZE; cnt++)
{
uart_buffer[cnt + 7] = Profibus_out_register[cnt];
}
#endif
*/
// Read data from master
#if (MAX_INPUT_DATA_SIZE > 0)
memcpy(&Profibus_in_register[0], &uart_buffer[7], MAX_INPUT_DATA_SIZE);
wdt_reset(); //Reset the WDT
new_data=1;
#endif
#if (MAX_OUTPUT_DATA_SIZE > 0)
memcpy(&uart_buffer[7], &Profibus_out_register[0], MAX_OUTPUT_DATA_SIZE);
#endif
#if (MAX_OUTPUT_DATA_SIZE > 0)
if (diagnose_status_1 != STATUS_1_DEFAULT)
profibus_send_CMD(SD2, DATA_HIGH, 0, &uart_buffer[7], 0); // Request a diagnosis
else
//profibus_send_CMD(SD2, DATA_LOW, 0, &uart_buffer[7], Input_Data_size); // send data
profibus_send_CMD(SD2, DATA_LOW, 0, &uart_buffer[7], Output_Data_size); // send data
#else if (diagnose_status_1 != STATUS_1_DEFAULT)
profibus_send_CMD(SD1, DATA_HIGH, 0, &uart_buffer[7], 0); // Request a diagnosis
else
profibus_send_CMD(SC, 0, 0, &uart_buffer[7], 0); // short acknowledgment
#endif
}
}
} //Data valid at the end
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*!
Compile and send * \ brief Profibus telegram
- \ param type telegram type (SD1, SD2 etc.)
- \ param function_code Function code to be transmitted
- \ param sap_offset Value of the SAP offset
- \ param pdu pointer to data field (PDU)
- \ param length_pdu Length of pure PDU without DA, SA or FC
*/
void profibus_send_CMD (unsigned char type,
unsigned char function_code,
unsigned char sap_offset,
char *pdu,
unsigned char length_pdu)
{
unsigned char length_data;
switch(type)
{
case SD1:
uart_buffer[0] = SD1;
uart_buffer[1] = master_addr;
uart_buffer[2] = slave_addr + sap_offset;
uart_buffer[3] = function_code;
uart_buffer[4] = checksum(&uart_buffer[1], 3);
uart_buffer[5] = ED;
length_data = 6;
break;
case SD2:
uart_buffer[0] = SD2;
uart_buffer[1] = length_pdu + 3; // Length of the PDU incl. DA, SA and FC
uart_buffer[2] = length_pdu + 3;
uart_buffer[3] = SD2;
uart_buffer[4] = master_addr;
uart_buffer[5] = slave_addr + sap_offset;
uart_buffer[6] = function_code;
//Data is already filled in before the function is called
uart_buffer[7+length_pdu] = checksum(&uart_buffer[4], length_pdu + 3);
uart_buffer[8+length_pdu] = ED;
length_data = length_pdu + 9;
break;
case SD3:
uart_buffer[0] = SD3;
uart_buffer[1] = master_addr;
uart_buffer[2] = slave_addr + sap_offset;
uart_buffer[3] = function_code;
// Data is already filled in before the function is called
uart_buffer[9] = checksum(&uart_buffer[4], 8);
uart_buffer[10] = ED;
length_data = 11;
break;
case SD4:
uart_buffer[0] = SD4;
uart_buffer[1] = master_addr;
uart_buffer[2] = slave_addr + sap_offset;
length_data = 3;
break;
case SC:
uart_buffer[0] = SC;
length_data = 1;
break;
default:
_NOP();
break;
}
profibus_TX(&uart_buffer[0], length_data);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*!
Send * \ letter telegram
- \ param data pointer to data field
- \ param length Length of the data
*/
void profibus_TX (char *data, unsigned char length)
{
TX_ENABLE_ON; // Enable Transmit (Switch to Transmit)
//digitalWriteFast(TX_ENABLE_PIN,HIGH);
OCR1A = TIMEOUT_MAX_TX_TIME;
profibus_status = PROFIBUS_SEND_DATA;
uart_byte_cnt = length; // Number of bytes to send
uart_transmit_cnt = 0; // Payer for sent bytes
UCSR0B |= _BV(UDRIE0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*!
Calculate brief checksum. Simple adding of all data bytes in the telegram.
- \ param data pointer to data field
- \ param length Length of the data
- \ return checksum
*/
unsigned char checksum(char *data, unsigned char length)
{
unsigned char csum = 0;
while(length--)
{
csum += data[length];
}
return csum;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*!
Check the letter destination. Address must be with slave address or broadcast (including SAP offset)
to match
- \ param destination destination address
- \ return TRUE if destination is ours, FALSE if not
*/
unsigned char addmatch (unsigned char destination)
{
if ((destination != slave_addr) && // Slave
(destination != slave_addr + SAP_OFFSET) && // SAP Slave
(destination != BROADCAST_ADD) && // Broadcast
(destination != BROADCAST_ADD + SAP_OFFSET)){ // SAP Broadcast
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*!
- ISR UART Transmit
/
ISR(USART_UDRE_vect)
{
// Everything sent?
if (uart_transmit_cnt < uart_byte_cnt)
{
/ Wait for empty transmit buffer */
while (!(UCSR0A & (1<<UDRE0)));
// TX Buffer
UDR0 = uart_buffer[uart_transmit_cnt++];
}
else
{
//clean all length transmitted buffer
memset (uart_buffer, 0, MAX_BUFFER_SIZE);
// All sent, interrupt again
UCSR0B &= ~( 1 << UDRIE0 );
}
TCNT1=0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*!
- \brief ISR TIMER1
*/
ISR (TIMER1_COMPA_vect) // Timer1 Output Compare 1A.
{
TIMER1_STOP; // Guard ourselves.
switch (profibus_status)
{
case PROFIBUS_WAIT_SYN: // TSYN expired
profibus_status = PROFIBUS_WAIT_DATA;
OCR1A = TIMEOUT_MAX_SDR_TIME;
uart_byte_cnt = 0;
break;
case PROFIBUS_WAIT_DATA: // TSDR expired but no data there
_NOP();
break;
case PROFIBUS_GET_DATA: // TSDR expired and data there
profibus_status = PROFIBUS_WAIT_SYN;
// We have already waited TIMEOUT_MAX_RX_TIME of bus idle. So subtract that from Tsyn.
OCR1A = TIMEOUT_MAX_SYN_TIME-TIMEOUT_MAX_RX_TIME;
profibus_RX();
break;
case PROFIBUS_SEND_DATA: // Transmission timeout expired, go back to reception
profibus_status = PROFIBUS_WAIT_SYN;
OCR1A = TIMEOUT_MAX_SYN_TIME;
TX_ENABLE_OFF; // Disable Transmit (Switch to Recieve)
//digitalWriteFast(TX_ENABLE_PIN,LOW);
break;
default:
_NOP();
break;
}
// Timer 1 Start
TIMER1_RUN;
TCNT1 = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
void doit(void)
{
if(new_data==1)
{
Profibus_out_register[0] +=new_data;
new_data=0;
if ((Output_Data_size >0) && (Output_Data_size <= MAX_OUTPUT_DATA_SIZE))
{
for(int i = 0; i<= Output_Data_size ;i++)
//for(int i = 0;i< MAX_OUTPUT_DATA_SIZE ;i++)
{
// Profibus_out_register[i] = Profibus_in_register[i];
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*!
- Initialize UART0
- Even parity, 1 stop bit.
/
/ */
void init_UART(unsigned long baud)
{
// Try U2X mode first
//uint16_t baud_setting = (F_CPU / 4 / baud - 1) /2;
uint8_t baud_setting = (F_CPU / 4 / baud - 1) /2;
UCSR0A = 1 << U2X0;
if (((F_CPU == 16000000UL) && (baud == 57600)) || (baud_setting >4095))
{
UCSR0A = 0;
baud_setting = (F_CPU / 8 / baud - 1) /2;
}
// assign the baud_setting, a.k.a. ubrr (USART Baud Rate Register)
UBRR0H = baud_setting >> 8;
UBRR0L = baud_setting;
UCSR0B |= ( 1 << RXEN0 ) | ( 1 << TXEN0 );
UCSR0B |= ( 1 << RXCIE0 ) | ( 1 << UDRIE0 );
//set the data bits, parity, and stop bits
//UCSR0C = (1<<UPM01) |(0<<USBS0) |(1<<UCSZ00) |(1<<UCSZ01);
UCSR0C = 0x26; // 8 bits, EVEN parity and 1 Stop bit - 8E1
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
byte get_Address()
{
pinMode(addr_0, INPUT_PULLUP);
pinMode(addr_1, INPUT_PULLUP);
pinMode(addr_2, INPUT_PULLUP);
pinMode(addr_3, INPUT_PULLUP);
pinMode(addr_4, INPUT_PULLUP);
pinMode(addr_5, INPUT_PULLUP);
pinMode(addr_6, INPUT_PULLUP);
//Variables
byte addr;
byte out_addr;
//init bariables
addr = 0xff;// init 1 because input is pull up
out_addr = 0x00;
//read addr input bit configuration.
bitWrite(addr, 0, digitalRead(addr_0));
bitWrite(addr, 1, digitalRead(addr_1));
bitWrite(addr, 2, digitalRead(addr_2));
bitWrite(addr, 3, digitalRead(addr_3));
bitWrite(addr, 4, digitalRead(addr_4));
bitWrite(addr, 5, digitalRead(addr_5));
bitWrite(addr, 6, digitalRead(addr_6));
// Last bit i not use because max address is 127 (bit 7 on is 255)
bitWrite(addr, 7, 1);
// normalize address from 0 to 127
out_addr = (addr ^ 0xff);
//Block variable return
//return out_addr;
return SLAVE_ADDRESS;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//This is the interrupt service routine for the WDT. It is called when the WDT times out and is in interrupt mode.
ISR (WDT_vect)
{
LED_ERROR_ON;
BAUD_SEARCH(0);
} // end of WDT_vect
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Routine for change Watchdog prescaler (time)
// Is also set for reboot the system after interrupt
// WDTCSR(0b01000000) | timer; //set for interrupt
// WDTCSR(0b00001000) | timer; //set for reset
//-> WDTCSR(0b01001000) | timer; //set for interrupt and then reset
// no choose if you use avr/wdt.h .Mast modifier manualy the register
void Watchdog_time(byte timer)
{
wdt_disable(); //Datasheet recommends disabling WDT right away in case of low probabibliy event
WDTCSR |= 0b00011000;
WDTCSR = (0b01001000) | timer; //Set WDT based user setting and for 1 second interval
wdt_reset();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
void BAUD_SEARCH(int InitFlag)
{
int i = EEPROM.read(0);
// Baud is lond int so divide by 4 to get correct parameter number and sub 1 to start from 0
int BAUD_NUM = (sizeof(BAUD)/4)-1;
if (InitFlag == 1)
{
// First scan. Check EEPROM idx BAUD value, probably station was in PWR off
Watchdog_time(WDTO_1S);
}
else
{ // If first scan idx value don't connect the station to master try different BAUD sequentially with different Watchdog Time out
// Different Watchdog Time out is require for diffrenece Daud speed for syncronize more faster the transmission.
//WDTO_500MS WDTO_250MS WDTO_120MS WDTO_60MS WDTO_30MS
switch (i)
{
case 0://12000000
Watchdog_time(WDTO_15MS);
i++;
break;
case 1://6000000
Watchdog_time(WDTO_15MS);
i++;
break;
case 2://3000000
Watchdog_time(WDTO_15MS);
i++;
break;
case 3://1500000
Watchdog_time(WDTO_30MS);
i++;
break;
case 4://500000
Watchdog_time(WDTO_30MS);
i++;
break;
case 5://187500
Watchdog_time(WDTO_60MS);
i++;
break;
case 6://93750
Watchdog_time(WDTO_60MS);
i++;
break;
case 7: //45450
Watchdog_time(WDTO_1S);
i++;
break;
case 8: //19200
Watchdog_time(WDTO_1S);
i++;
break;
case 9: //9600
Watchdog_time(WDTO_1S);
i++;
break;
default:
Watchdog_time(WDTO_1S);
i++;
break;
break;
}
}
// Check pointer limits
if ((i > BAUD_NUM) || (i < 0))
{
i = 0;
}
//Store pointer in EEPROM. Usefull if station is restart after power off
//for avoid baud search again.
EEPROM.write(0,i);
DELAY_TBIT = 2000000 / BAUD[i];
TIMEOUT_MAX_SYN_TIME = 33 * DELAY_TBIT;
TIMEOUT_MAX_RX_TIME = 16 * DELAY_TBIT;
TIMEOUT_MAX_TX_TIME = 16 * DELAY_TBIT;
TIMEOUT_MAX_SDR_TIME = 16 * DELAY_TBIT;
init_CPU(i);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
profibus.h :
/*!
- \file profibus.h
- \brief Ablaufsteuerung Profibus DP-Slave Kommunikation, h-Datei
- \author � Joerg S.
- \date 9.2007 (Erstellung) 9.2009 (Aktueller Stand)
- \note Verwendung nur fuer private Zwecke / Only for non-commercial use
- \note 2010.04.28. Started adaption to AVR. Einar S.
- \note 2019.08.20. AVR version adapted to Arduino MEGA2560 and Arduino IDE. TadyTheFish
- \note 2021.09.08 adaption for Atmega328 at 500Mbt/s
*/
/*
- This version works on an Atmega328 and can be compiled with Arduino IDE (i did it with 1.8.13
- This slave simply accepts 48 bytes from the Master and returns the 48 bytes to the Master (this is done in void doit() ),
- and the data is printed on the serial monitor.
- Max Data size = 48 Bytes Input and 48 Bytes Output (96)
- At the moment the Profibus speed is set to 45.45K BAUD if you want to change the speed you need to enable support in the GSD file and uncomment
- the define below. You also need to change the DELAY_TBIT to the corresponding Baudrate.
- If the communication doesn't work try swaping A and B profibus lines. I had this problem with the chinese RS485 boards from Ebay that were labeled wrong
- The original version is here: Software Profibus DP-Slave in C - Mikrocontroller.net
- It was first done on a MSP430F2252 and then ported for MEGA16 (I think) (AVRSoftBus.zip) and then for atXmega32A4U (atXSoftBus.zip)
- This is proof of concept, hobby use only!
- Tested on S7-315 CPU
- The interface IC for RS485 is MAX485 connected to Serial2. The DE and RE pins of MAX485 are both connected to TX_ENABLE_PIN 2(int0)
- \note all CPU settings are move in .ino file. Profibus .h contain only profibus declaration not related the type of CPU used.
*/
//#define BAUD 12000000 // DELAY_TBIT = 1 Not working on Nano @16MHz
//#define BAUD 6000000 // DELAY_TBIT = 1 Not working on Nano @16MHz
//#define BAUD 3000000 // DELAY_TBIT = 1 Not working on Nano @16MHz
//#define BAUD 1500000 // DELAY_TBIT = 1 Not working on Nano @16MHz
//#define BAUD 500000 // DELAY_TBIT = 4 working on Nano @16MHz
//#define BAUD 187500 // DELAY_TBIT = 11 working on Nano @16MHz
//#define BAUD 93750 // DELAY_TBIT = 21 working on Nano @16MHz
//#define BAUD 45450 // DELAY_TBIT = 44 // Working on Nano @16MHz
//#define BAUD 19200 // DELAY_TBIT = 104 // Working on Nano @16MHz
//#define BAUD 9600 // DELAY_TBIT = 208 // Working on Nano @16MHz
//#define DELAY_TBIT 21 // This is timer delay for 1 TBIT
#define SLAVE_ADDRESS 6 // This is the address of this slave device (temporary solution)
//#define TX_ENABLE_PIN 2 // This pin toggles the MAX485 IC from Transmit to Recieve
//#define LED_ERROR_PIN 13 // This is the built in led on pin 13 Arduino Uno - PB5
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//#define LED_ERROR_ON PORTB |= _BV (5); // digitalWrite (13, HIGH);
//#define LED_ERROR_OFF PORTB &= ~_BV (5); // digitalWrite (13, LOW);
//#define LED_ERROR_ON PORTB |= (1<<5);
//#define LED_ERROR_OFF PORTB &= ~(1<<5);
//#define LED_ERROR_ON digitalWriteFast(LED_ERROR_PIN,HIGH);
//#define LED_ERROR_OFF digitalWriteFast(LED_ERROR_PIN,LOW);
//#define TX_ENABLE_ON PORTD |= _BV (2); // digitalWrite (2, HIGH);
//#define TX_ENABLE_OFF PORTD &= ~_BV (2); // digitalWrite (2, LOW);
//#define TX_ENABLE_ON PORTD |= (1<<2);//PD2
//#define TX_ENABLE_OFF PORTD &= ~(1<<2);//PD2
//#define TX_ENABLE_ON PORTD |= (1<<TX_ENABLE_PIN);
//#define TX_ENABLE_OFF PORTD &= ~(1<<TX_ENABLE_PIN);
//#define TX_ENABLE_ON digitalWriteFast(TX_ENABLE_PIN,HIGH);
//#define TX_ENABLE_OFF digitalWriteFast(TX_ENABLE_PIN,LOW);
///////////////////////////////////////////////////////////////////////////////////////////////////
//#define TIMER1_RUN TCCR1B |= (1 << CS11);
//#define TIMER1_STOP TCCR1B &= ~(1 << CS11);
//////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Ident Nummer DP Slave. Arbitrarily chosen. This ID not found in my .GSD library.
///////////////////////////////////////////////////////////////////////////////////////////////////
//#define IDENT_HIGH_BYTE 0xC0
//#define IDENT_LOW_BYTE 0xDE
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Addresses
///////////////////////////////////////////////////////////////////////////////////////////////////
#define SAP_OFFSET 128 // Service Access Point Adress Offset
#define BROADCAST_ADD 127
#define DEFAULT_ADD 126 // Delivery address
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Command types
///////////////////////////////////////////////////////////////////////////////////////////////////
#define SD1 0x10 // Telegram without data field
#define SD2 0x68 // Data telegram variable
#define SD3 0xA2 // Data telegram fixed
#define SD4 0xDC // Token
#define SC 0xE5 // Short acknowledgment
#define ED 0x16 // End
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Function Codes
///////////////////////////////////////////////////////////////////////////////////////////////////
/* FC Request */
#define REQUEST_ 0x40 // SPS: Request Telegramm
#define SRD_HIGH 0x0D // SPS: Set outputs, read inputs
#define FCV_ 0x10 // SPS: Alternating bit switched on
#define FCB_ 0x20 // SPS: Alternating bit (from frame count)
#define CV_ 0x80 // SPS: Clock Value (Clock synchronization)
#define TE 0x00 // SPS: Time Event (Clock synchronization)
#define SDA_LOW 0x03 // SPS: Send Data Acknowledged - low priority
#define SDN_LOW 0x04 // SPS: Send Data Not acknowledged - low priority
#define SDA_HIGH 0x05 // SPS: Send Data Acknowledged - high priority
#define SDN_HIGH 0x06 // SPS: Send Data Not acknowledged
#define MSRD 0x07 // SPS: Send Request Data with Multicast Reply
#define FDL_STATUS 0x09 // SPS: Request FDL Status
#define SRD_LOW 0x0C // SPS: Send and Request Data low
#define SRD_HIGH 0x0D // SPS: Send and Request Data high
#define REQUEST_IDENT 0x0E // SPS: Request Ident with reply
#define REQUEST_LSAP_STS 0x0F // SPS: Request LSAP Status with reply 1)
/* FC Response */
#define FDL_STATUS_OK 0x00 // SLA: OK
#define USER_ERR 0x01 // SLA: User error
#define NO_RESORCES 0x02 // SLA: No resources
#define SAP_DISAB 0x03 // SLA: SAP not enabled
#define DATA_LOW 0x08 // SLA: (Data low) Send data inputs
#define NO_RESPONSE 0x09 // SLA: No response data ready
#define DATA_HIGH 0x0A // SLA: (Data high) Diagnosis pending
#define DATA_LOW_ERR 0x0C // SLA: Data not received and Data Low
#define DATA_HIGH_ERR 0x0D // SLA: Data not received and Data High
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Service Access Points (DP Slave) MS0
///////////////////////////////////////////////////////////////////////////////////////////////////
#define DS_REQ 51 // Master ask ACT status Diagnostic
#define SAP_Ext_USER_PRM 53 // External parameter, slave responds with SC
#define SAP_SET_SLAVE_ADR 55 // Master sets slave address, slave responds with SC
#define SAP_RD_INP 56 // Master requests input data, slave sends input data
#define SAP_RD_OUTP 57 // Master requests output data, slave sends output data
#define SAP_GLOBAL_CONTROL 58 // Master Control, Slave Does not answer
#define SAP_GET_CFG 59 // Master requests config., Slave sends configuration
#define SAP_SLAVE_DIAGNOSIS 60 // Master requests diagnosis, slave sends diagnosis Daten
#define SAP_SET_PRM 61 // Master sends parameters, slave sends SC
#define SAP_CHK_CFG 62 // Master sends configuration, Slave sends SC
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// SAP: Global Control (Data Master)
///////////////////////////////////////////////////////////////////////////////////////////////////
#define CLEAR_DATA_ 0x02
#define UNFREEZE_ 0x04
#define FREEZE_ 0x08
#define UNSYNC_ 0x10
#define SYNC_ 0x20
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// SAP: Diagnosis (Answer slave)
// Diagnostics: telegram with min. 6 and max. 244 bytes PDU
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Status Byte 1 */
#define STATUS_1_DEFAULT 0x00
#define STATION_NOT_EXISTENT_ 0x01 //Station does not exist. This bit is always set to zero by a slave. Here a master notices that this slave is not responding.
#define STATION_NOT_READY_ 0x02 //Slave is not ready for data exchange. This bit is set by the DP slave if the DP slave is not yet ready for data exchange.
#define CFG_FAULT_ 0x04 //Fault in the configuration telegram. This bit is set by the DP slave, as soon as the configuration data received most recently from the master does not match that detected by the DP slave.
#define EXT_DIAG_ 0x08 //An extended diagnosis follows in the telegram. This bit indicates that further diagnostic blocks follow starting from byte 7.
#define NOT_SUPPORTED_ 0x10 //Requested function is not supported by slave. This bit is set by a slave, as soon as a function is requested that is not supported by this slave.
#define INV_SLAVE_RESPONSE_ 0x20 //Invalid response from slave. This bit is always set to zero by a slave. Here a master notices that a slave gives invalid responses.
#define PRM_FAULT_ 0x40 //Fault in parameter telegram. This bit is set by the DP slave if the last parameter telegram was faulty.
#define MASTER_LOCK 0x80 //Slave has been locked by another master. This bit is always set to zero by a slave. Here a master notices that this slave has been already locked by another master.
/* Status Byte 2 */
#define PRM_REQ_ 0x01 //Slave parameters must be reset. If the DP slave sets this bit, its parameters must be reset followed by reconfiguration. The bit remains set until valid parameters have been implemented.
#define STAT_DIAG_ 0x02 //Status diagnostics. If the DP slave sets this bit, the DP master must continue fetching diagnostic data until this bit is deleted again. The DP slave sets this bit when, for example, it is not able to provide any valid user data.
#define STATUS_2_DEFAULT_ 0x04 //Permanently at 1, serves to detect protocol errors.
#define WD_ON_ 0x08 //Watchdog on. If this bit is set to 1, watchdog monitoring is enabled.
#define FREEZE_MODE_ 0x10 //Freeze command received. This bit is set by the DP slave as soon as this DP slave receives the Freeze command.
#define SYNC_MODE_ 0x20 //Sync command received. This bit is set by the DP slave as soon as this DP slave receives the Sync command.
//#define RESERVED_ 0x40 //RESERVED
#define DEACTIVATED_ 0x80 //Slave is deactivated. This bit is always set to zero by a slave. Here a master notices that this slave has been deactivated and should therefore no longer be controlled cyclically.
/* Status Byte 3 */
#define DIAG_SIZE_OK 0x00 //Reserved ?????
#define DIAG_SIZE_ERROR 0x80 //If this bit is set, more diagnostic information is available than could be transmitted in one telegram. The master must request further diagnosis.
/* Status Byte 4 Master Address */
#define MASTER_ADD_DEFAULT 0xFF //Address of master after setting parameters Default is 255 (0xFF)
/* Status Byte 5 Ident Nummer DP Slave */
#define IDENT_HIGH_BYTE 0xC0 //Ident number high byte. Must match GSD congifuration
/* Status Byte 6 Ident Nummer DP Slave */
#define IDENT_LOW_BYTE 0xDE //Ident number low byte. Must match GSD congifuration
/* Extended diagnosis (EXT_DIAG_ = 1) */
#define EXT_DIAG_TYPE_ 0xC0 // Bit 6-7 is Diagnostic Type
#define EXT_DIAG_BYTE_CNT_ 0x3F // Bit 0-5 are number of diagnostic bytes
#define EXT_DIAG_DEVICE 0x00 // If bits 7 and 6 = 00, then device-related
#define EXT_DIAG_IDENTIFIER 0x40 // If bits 7 and 6 = 01, then identifier-related
#define EXT_DIAG_CHANNEL 0x80 // If bits 7 and 6 = 10, then channel-related
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// SAP: Set Parameters Request (Data master)
///////////////////////////////////////////////////////////////////////////////////////////////////
/* Command */
#define ACTIVATE_WATCHDOG_ 0x08
#define ACTIVATE_FREEZE_ 0x10
#define ACTIVATE_SYNC_ 0x20
#define UNLOCK_SLAVE_ 0x40 // Slave for other masters released
#define LOCK_SLAVE_ 0x80 // Slave locked for other masters
/* DPV1 Status Byte 1 */
#define WATCHDOG_TB_1MS 0x04 //Time base of watchdog is 1 ms (and not 10 ms)
#define PUBLISHER_MODE_ 0x20 //The slave should work as Publisher
#define FAIL_SAVE_MODE_ 0x40 //The slave should work in Fail_Safe mode
#define DPV1_MODE_ 0x80 //The slave should work in DP-V1 mode
/* DPV1 Status Byte 2 */
#define CHECK_CONFIG_MODE_ 0x01 //Reduced configuration control
#define UPDATE_ALARM_ 0x04 //Switch on update alarm
#define STATUS_ALARM_ 0x08 //Switch on status alarm
#define VENDOR_ALARM_ 0x10 //Switch on vendor-specific alarm
#define DIAGNOSE_ALARM_ 0x20 //Switch on diagnostic alarm
#define PROZESS_ALARM_ 0x40 //Switch on process alarm
#define PULL_PLUG_ALARM_ 0x80 //Switch on plug alarm (Pull-Plug)
/* DPV1 Status Byte 3 */
#define PARAMETER_BLOCK_ 0x08 //Prm_Structure: a parameter block follows
#define ISOCHRON_MODE_ON_ 0x10 //Isochronous mode is switched on (Block type = 4)
#define PARAMETER_CMD_ON_ 0x80 //Redundancy commands are switched on (Block type = 2)
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// SAP: Check Config Request (Data Master)
///////////////////////////////////////////////////////////////////////////////////////////////////
#define CFG_DIRECTION_ 0x30 // Bit 4-5 is direction. 01 = input, 10 = output, 11 = input / output
#define CFG_INPUT 0x10 // Input
#define CFG_OUTPUT 0x20 // Output
#define CFG_INPUT_OUTPUT 0x30 // Input/Output
#define CFG_SPECIAL 0x00 // Special format if more than 16/32 bytes are to be transferred
#define CFG_KONSISTENZ_ 0x80 // Bit 7 is consistency. 0 = byte or word, 1 = over entire module
#define CFG_KONS_BYTE_WORT 0x00 // Byte or word
#define CFG_KONS_MODUL 0x80 // Modul
#define CFG_WIDTH_ 0x40 // Bit 6 is IO width. 0 = byte (8bit), 1 = word (16bit)
#define CFG_BYTE 0x00 // Byte
#define CFG_WORD 0x40 // Word
/* Compact format */
#define CFG_BYTE_CNT_ 0x0F // Bit 0-3 are number of bytes or words. 0 = 1 byte, 1 = 2 bytes etc.
/* Special format */
#define CFG_SP_DIRECTION_ 0xC0 // Bit 6-7 is direction. 01 = input, 10 = output, 11 = input / output
#define CFG_SP_VOID 0x00 // Empty space
#define CFG_SP_INPUT 0x40 // Input
#define CFG_SP_OUTPUT 0x80 // Output
#define CFG_SP_INPUT_OPTPUT 0xC0 // Input/Output
#define CFG_SP_VENDOR_CNT_ 0x0F // Bits 0-3 are the number of manufacturer-specific bytes. 0 = none
/* Special format / length byte */
#define CFG_SP_BYTE_CNT_ 0x3F // Bit 0-5 are number of bytes or words. 0 = 1 byte, 1 = 2 bytes etc.
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//#define TIMEOUT_MAX_SYN_TIME 33 * DELAY_TBIT // 33 TBit = TSYN
//#define TIMEOUT_MAX_RX_TIME 16 * DELAY_TBIT
//#define TIMEOUT_MAX_TX_TIME 16 * DELAY_TBIT
//#define TIMEOUT_MAX_SDR_TIME 16 * DELAY_TBIT // 15 Tbit = TSDR
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
#define MAX_BUFFER_SIZE 96
#define MAX_INPUT_DATA_SIZE 48 // Number of bytes coming from the master
#define MAX_OUTPUT_DATA_SIZE 48 // Number of bytes going to master
#define MAX_VENDOR_DATA_SIZE 0 // Number of bytes for manufacturer-specific data
#define MAX_EXT_DIAG_DATA_SIZE 0 // Number of bytes for extended diagnostics
//#define MODULE_CNT 1 // Number of modules (input and output modules) for modular station
//#define OUTPUT_MODULE_CNT 1 // Number of output modules
//#define INPUT_MODULE_CNT 1 // Number of input modules
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Profibus process control
///////////////////////////////////////////////////////////////////////////////////////////////////
#define PROFIBUS_WAIT_SYN 1
#define PROFIBUS_WAIT_DATA 2
#define PROFIBUS_GET_DATA 3
#define PROFIBUS_SEND_DATA 4
///////////////////////////////////////////////////////////////////////////////////////////////////
The original .INO i have download from internet also but has a lot of issue.
- Fix on line diagnostic loosing connection
- add other missing FC
- Fix led cpu error connection
- Modifier DI and DO pin manage from registers (more fast then digitalwrite function)
- Add Slave ADDR save on eeprom (actually in .INO the function is force to 6)
- Add Automatic baud rate detection
this is guaranty for Arduino nano v3 which can work at 0.5Mbt/s
For internal debug i add an ole display because i have some problems to use Serial.print.
Also this i don' t understand.
Same problem and in nano 33 iot where if i don't start serial monitor the communication don' t start.
Of course for nano 33 .INO file is different registers are not the same.
Please edit your earlier post with CODE TAGS wrapped around the code text block
Hi,
To add code please click this link;
Thanks.. Tom..
Sorry am new user.
let me adjust.....
The code is long i prefer attach .zip files but because am new use, can't do it.
For .INO and .GSD need wait maybe tomorrow.
but...
if you have some interesting,
i find profibus manual on line here:
https://www.felser.ch/profibus-manual/ms0_zyklischer_datenaustausch.html
and original source here:
https://www.mikrocontroller.net/topic/106174?__cf_chl_jschl_tk__=pmd_bb298ad2b8f31a765a65cc5467f3fdf13e162be2-1626767294-0-gqNtZGzNAiKjcnBszQfO
Unfortunately speed for both are only 0.5Mbt/s witch i understated for nano v3 but not for nano 33 iot and both give me problem with serial monitor.
at soon will provide the code so you can have a look.
Definitely need help
Finally !
Ok here hall what i have done:
prof_test_atmega328P.zip (15.8 KB)
prof_test_Samd21_nano33.zip (13.5 KB)
ARDUINO_gsd.zip (1.1 KB)
Ok
today some progress.
Main reason for communication instability is because for active the communication need first open serial monitor and after unplug the programming USB from Arduino nano 33 iot. After this operation profi trace don't give me any error and the connection with the PLC remain stable.
If programming USB port is plug again, in profi trace show me frame error very often.
With this tricky operation communication at 1.5Mbt/s is stable but 3Mbts/s is completely unstable. Signal in the oscilloscope is ok and not detect anything abnormal.
Now question is why arduino programming port make possible enable of UART ?
and why after enable the communication even if i close serial monitor must unplug the USB programming port for not make interference with the UART communication ??
I not write anything in the code that should working like this. Its look like the Arduino board wait some Synchro for wake up the uart and after this the board remain pending to transmit back via USB
For today i have finish any possible logic explanation (only ghost inside the chip)
By the way here is the codeprof_test_Samd21_nano33.zip (13.9 KB)
About schematics sorry but all is improvise but at soon i will work on that
here the result at 1.5Mbts/s after magic trick operation:
here the result at3Mbts/s withe same tricky thinks
I check and recheck the code but i don't know from where coming out this additional bytes.
Some ideas or suggestions ?
already give you information and code
please to every body
i need HELP
This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.