Newer
Older
Florian Pose
committed
/******************************************************************************
* s l a v e . c
Florian Pose
committed
* $Id$
Florian Pose
committed
*****************************************************************************/
Florian Pose
committed
#include <linux/delay.h>
Florian Pose
committed
#include "../include/EtherCAT_si.h"
#include "globals.h"
#include "slave.h"
#include "command.h"
#include "master.h"
Florian Pose
committed
/*****************************************************************************/
Florian Pose
committed
*/
Florian Pose
committed
void ec_slave_init(ec_slave_t *slave, /**< EtherCAT-Slave */
ec_master_t *master /**< EtherCAT-Master */
)
{
slave->master = master;
slave->base_type = 0;
slave->base_revision = 0;
slave->base_build = 0;
slave->base_fmmu_count = 0;
slave->base_sync_count = 0;
slave->ring_position = 0;
slave->station_address = 0;
slave->sii_vendor_id = 0;
slave->sii_product_code = 0;
slave->sii_revision_number = 0;
slave->sii_serial_number = 0;
slave->type = NULL;
slave->registered = 0;
slave->fmmu_count = 0;
}
Florian Pose
committed
/*****************************************************************************/
Florian Pose
committed
/**
EtherCAT-Slave-Destruktor.
Florian Pose
committed
void ec_slave_clear(ec_slave_t *slave /**< EtherCAT-Slave */)
Florian Pose
committed
// Nichts freizugeben
}
/*****************************************************************************/
/**
Liest alle bentigten Informationen aus einem Slave.
Florian Pose
committed
*/
int ec_slave_fetch(ec_slave_t *slave /**< EtherCAT-Slave */)
{
ec_command_t command;
Florian Pose
committed
// Read base data
ec_command_init_nprd(&command, slave->station_address, 0x0000, 6);
if (unlikely(ec_master_simple_io(slave->master, &command))) {
EC_ERR("Reading base datafrom slave %i failed!\n",
Florian Pose
committed
return -1;
}
slave->base_type = EC_READ_U8 (command.data);
slave->base_revision = EC_READ_U8 (command.data + 1);
slave->base_build = EC_READ_U16(command.data + 2);
slave->base_fmmu_count = EC_READ_U8 (command.data + 4);
slave->base_sync_count = EC_READ_U8 (command.data + 5);
Florian Pose
committed
if (slave->base_fmmu_count > EC_MAX_FMMUS)
slave->base_fmmu_count = EC_MAX_FMMUS;
// Read identification from "Slave Information Interface" (SII)
if (unlikely(ec_slave_sii_read(slave, 0x0008, &slave->sii_vendor_id))) {
Florian Pose
committed
return -1;
}
if (unlikely(ec_slave_sii_read(slave, 0x000A, &slave->sii_product_code))) {
EC_ERR("Could not read SII product code!\n");
Florian Pose
committed
return -1;
}
if (unlikely(ec_slave_sii_read(slave, 0x000C,
&slave->sii_revision_number))) {
EC_ERR("Could not read SII revision number!\n");
Florian Pose
committed
return -1;
}
if (unlikely(ec_slave_sii_read(slave, 0x000E,
&slave->sii_serial_number))) {
EC_ERR("Could not read SII serial number!\n");
Florian Pose
committed
return -1;
}
return 0;
}
/*****************************************************************************/
/**
Liest Daten aus dem Slave-Information-Interface
eines EtherCAT-Slaves.
\return 0 bei Erfolg, sonst < 0
*/
int ec_slave_sii_read(ec_slave_t *slave,
/**< EtherCAT-Slave */
unsigned short int offset,
/**< Adresse des zu lesenden SII-Registers */
unsigned int *target
/**< Zeiger auf einen 4 Byte groen Speicher zum Ablegen
der Daten */
)
{
ec_command_t command;
Florian Pose
committed
unsigned char data[10];
cycles_t start, end, timeout;
Florian Pose
committed
// Initiate read operation
Florian Pose
committed
EC_WRITE_U8 (data, 0x00);
EC_WRITE_U8 (data + 1, 0x01);
EC_WRITE_U16(data + 2, offset);
EC_WRITE_U16(data + 4, 0x0000);
Florian Pose
committed
ec_command_init_npwr(&command, slave->station_address, 0x502, 6, data);
if (unlikely(ec_master_simple_io(slave->master, &command))) {
EC_ERR("SII-read failed on slave %i!\n", slave->ring_position);
Florian Pose
committed
return -1;
}
// Der Slave legt die Informationen des Slave-Information-Interface
// in das Datenregister und lscht daraufhin ein Busy-Bit. Solange
// den Status auslesen, bis das Bit weg ist.
start = get_cycles();
timeout = cpu_khz; // 1ms
Florian Pose
committed
do
{
ec_command_init_nprd(&command, slave->station_address, 0x502, 10);
if (unlikely(ec_master_simple_io(slave->master, &command))) {
EC_ERR("Getting SII-read status failed on slave %i!\n",
Florian Pose
committed
return -1;
}
end = get_cycles();
if (likely((EC_READ_U8(command.data + 1) & 0x81) == 0)) {
memcpy(target, command.data + 6, 4);
Florian Pose
committed
break;
}
}
while (likely((end - start) < timeout));
Florian Pose
committed
if (unlikely((end - start) >= timeout)) {
EC_ERR("SSI-read. Slave %i timed out!\n", slave->ring_position);
Florian Pose
committed
return -1;
}
return 0;
}
/*****************************************************************************/
/**
Besttigt einen Fehler beim Zustandswechsel.
Florian Pose
committed
*/
void ec_slave_state_ack(ec_slave_t *slave,
/**< Slave, dessen Zustand gendert werden soll */
uint8_t state
/**< Alter Zustand */
)
{
ec_command_t command;
Florian Pose
committed
unsigned char data[2];
cycles_t start, end, timeout;
Florian Pose
committed
Florian Pose
committed
EC_WRITE_U16(data, state | EC_ACK);
Florian Pose
committed
ec_command_init_npwr(&command, slave->station_address, 0x0120, 2, data);
if (unlikely(ec_master_simple_io(slave->master, &command))) {
EC_WARN("State %02X acknowledge failed on slave %i!\n",
state, slave->ring_position);
Florian Pose
committed
return;
}
start = get_cycles();
timeout = cpu_khz; // 1ms
Florian Pose
committed
do
{
ec_command_init_nprd(&command, slave->station_address, 0x0130, 2);
if (unlikely(ec_master_simple_io(slave->master, &command))) {
EC_WARN("State %02X acknowledge checking failed on slave %i!\n",
state, slave->ring_position);
Florian Pose
committed
return;
}
end = get_cycles();
if (unlikely(EC_READ_U8(command.data) != state)) {
EC_WARN("Could not acknowledge state %02X on slave %i (code"
" %02X)!\n", state, slave->ring_position,
EC_READ_U8(command.data));
Florian Pose
committed
return;
}
if (likely(EC_READ_U8(command.data) == state)) {
EC_INFO("Acknowleged state %02X on slave %i.\n", state,
slave->ring_position);
Florian Pose
committed
return;
}
}
while (likely((end - start) < timeout));
Florian Pose
committed
if (unlikely((end - start) >= timeout)) {
EC_WARN("Could not check state acknowledgement %02X of slave %i -"
" Timeout while checking!\n", state, slave->ring_position);
Florian Pose
committed
return;
}
}
/*****************************************************************************/
/**
ndert den Zustand eines Slaves.
\return 0 bei Erfolg, sonst < 0
*/
int ec_slave_state_change(ec_slave_t *slave,
/**< Slave, dessen Zustand gendert werden soll */
uint8_t state
/**< Neuer Zustand */
)
{
ec_command_t command;
Florian Pose
committed
unsigned char data[2];
cycles_t start, end, timeout;
Florian Pose
committed
Florian Pose
committed
EC_WRITE_U16(data, state);
Florian Pose
committed
ec_command_init_npwr(&command, slave->station_address, 0x0120, 2, data);
if (unlikely(ec_master_simple_io(slave->master, &command))) {
EC_ERR("Failed to set state %02X on slave %i!\n",
state, slave->ring_position);
Florian Pose
committed
return -1;
}
start = get_cycles();
timeout = cpu_khz; // 1ms
Florian Pose
committed
do
{
udelay(100); // Dem Slave etwas Zeit lassen...
Florian Pose
committed
ec_command_init_nprd(&command, slave->station_address, 0x0130, 2);
if (unlikely(ec_master_simple_io(slave->master, &command))) {
EC_ERR("Failed to check state %02X on slave %i!\n",
Florian Pose
committed
return -1;
}
end = get_cycles();
if (unlikely(EC_READ_U8(command.data) & 0x10)) { // State change error
EC_ERR("Could not set state %02X - Slave %i refused state change"
" (code %02X)!\n", state, slave->ring_position,
EC_READ_U8(command.data));
ec_slave_state_ack(slave, EC_READ_U8(command.data) & 0x0F);
Florian Pose
committed
return -1;
}
if (likely(EC_READ_U8(command.data) == (state & 0x0F))) {
Florian Pose
committed
// State change successful
break;
}
}
while (likely((end - start) < timeout));
Florian Pose
committed
if (unlikely((end - start) >= timeout)) {
EC_ERR("Could not check state %02X of slave %i - Timeout!\n", state,
Florian Pose
committed
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
slave->ring_position);
return -1;
}
return 0;
}
/*****************************************************************************/
/**
Merkt eine FMMU-Konfiguration vor.
Die FMMU wird so konfiguriert, dass sie den gesamten Datenbereich des
entsprechenden Sync-Managers abdeckt. Fr jede Domne werden separate
FMMUs konfiguriert.
Wenn die entsprechende FMMU bereits konfiguriert ist, wird dies als
Erfolg zurckgegeben.
\return 0 bei Erfolg, sonst < 0
*/
int ec_slave_set_fmmu(ec_slave_t *slave, /**< EtherCAT-Slave */
const ec_domain_t *domain, /**< Domne */
const ec_sync_t *sync /**< Sync-Manager */
)
{
unsigned int i;
// FMMU schon vorgemerkt?
for (i = 0; i < slave->fmmu_count; i++)
if (slave->fmmus[i].domain == domain && slave->fmmus[i].sync == sync)
return 0;
Florian Pose
committed
if (slave->fmmu_count >= slave->base_fmmu_count) {
EC_ERR("Slave %i FMMU limit reached!\n", slave->ring_position);
Florian Pose
committed
return -1;
}
slave->fmmus[slave->fmmu_count].domain = domain;
slave->fmmus[slave->fmmu_count].sync = sync;
slave->fmmus[slave->fmmu_count].logical_start_address = 0;
slave->fmmu_count++;
slave->registered = 1;
return 0;
}
/*****************************************************************************/
/**
Gibt alle Informationen ber einen EtherCAT-Slave aus.
*/
void ec_slave_print(const ec_slave_t *slave /**< EtherCAT-Slave */)
{
EC_INFO("--- EtherCAT slave information ---\n");
Florian Pose
committed
if (slave->type) {
EC_INFO(" Vendor \"%s\", Product \"%s\": %s\n",
slave->type->vendor_name, slave->type->product_name,
slave->type->description);
Florian Pose
committed
}
else {
EC_INFO(" *** This slave has no type information! ***\n");
Florian Pose
committed
}
EC_INFO(" Ring position: %i, Station address: 0x%04X\n",
slave->ring_position, slave->station_address);
Florian Pose
committed
EC_INFO(" Base information:\n");
EC_INFO(" Type %u, Revision %i, Build %i\n",
slave->base_type, slave->base_revision, slave->base_build);
EC_INFO(" Supported FMMUs: %i, Sync managers: %i\n",
slave->base_fmmu_count, slave->base_sync_count);
Florian Pose
committed
EC_INFO(" Slave information interface:\n");
EC_INFO(" Vendor-ID: 0x%08X, Product code: 0x%08X\n",
slave->sii_vendor_id, slave->sii_product_code);
EC_INFO(" Revision number: 0x%08X, Serial number: 0x%08X\n",
slave->sii_revision_number, slave->sii_serial_number);
Florian Pose
committed
/*****************************************************************************/
/**
Gibt die Zhlerstnde der CRC-Fault-Counter aus und setzt diese zurck.
\return 0 bei Erfolg, sonst < 0
*/
int ec_slave_check_crc(ec_slave_t *slave /**< EtherCAT-Slave */)
{
ec_command_t command;
uint8_t data[4];
ec_command_init_nprd(&command, slave->station_address, 0x0300, 4);
if (unlikely(ec_master_simple_io(slave->master, &command))) {
EC_WARN("Reading CRC fault counters failed on slave %i!\n",
return -1;
}
// No CRC faults.
if (!EC_READ_U16(command.data) && !EC_READ_U16(command.data + 2)) return 0;
EC_WARN("CRC faults on slave %i. A: %i, B: %i\n", slave->ring_position,
EC_READ_U16(command.data), EC_READ_U16(command.data + 2));
// Reset CRC counters
Florian Pose
committed
EC_WRITE_U16(data, 0x0000);
EC_WRITE_U16(data + 2, 0x0000);
ec_command_init_npwr(&command, slave->station_address, 0x0300, 4, data);
if (unlikely(ec_master_simple_io(slave->master, &command))) {
EC_WARN("Resetting CRC fault counters failed on slave %i!\n",
return -1;
}
return 0;
}
/*****************************************************************************/
/* Emacs-Konfiguration
;;; Local Variables: ***
Florian Pose
committed
;;; c-basic-offset:4 ***