当前位置:   article > 正文

IgH EhterCat主站 EC Master 使用心得:SII,ENI和SM踩坑记录_ethercat sii check/fetch

ethercat sii check/fetch

声明一下:本文假设你对EtherCat有理解和对IgH有使用经验,遇到问题的时候来看最合适。假如你是初学者,类似帖子有很多的,也可以找ChatGPT对话学习效果更好。

2013年,我首次接触EtherCat通讯。初碰实时Linux+Xenomai2(Ubuntu10.04)的时候,有很多东西要做,根本没经历研究IgH的主站,直接选择acontis收费主站。

2017年,我与北京acontis总代的技术支持聊了一下才知道,原来以第一个带时钟的从站作为DC时钟时,acontis会新开一个实时线程来作通讯。这对于更低的通讯周期的要求的我来说,是很麻烦的事情。于是我转过头来奔向IgH。

此时我已经使用了Ubuntu14.04.5+Linux3.18.20,这个内核版本是Xenomai官方patch支持Linux3.x的最后一个版本。别问我为什么不选Linux3.14,问就是Uubntu14.04.5在Linux3.14下不稳定。而IgH的网卡驱动最接近这个内核版本的的就是Linux3.14。直接暴力手动patch移植intel网卡驱动。运气很好没怎么踩坑,经过无数次强制重启直到我开始怀疑我的硬盘还行不行的时候,我成功了 。这个时候我飘了,决定把我的Y430P笔记本电脑网卡的也移植一下。结果失败了,我猜想原因是出现在硬件联络层。这个时候我才想起来当初acontis技术说过的:事实上,Realtek网卡效果没有Intel网卡效果好的。后来我看了这两家驱动代码才有点明白,大概就是网卡厂商开放的功能是不同的,Intel的内核驱动代码量比Realtek多很多。这或许对实时数据收发是有益处的。

用过IgH的人都知道,IgH只需要配置从站的Process Data(SM2和SM3)就够了。其他配置直接从EEPROM读取的。做EtherCat产品嘛,TwinCat肯定是标杆,我就像模像样的写了一个解析acontis的ENI中的SM2和SM3的PDO部分配置的工具(其他的不用我管,不改源代码我也管不了),用来配置IgH。除了显得我们很专业,也为了防止用户自己换其他品牌驱动后出现生产或事故问题。

本以为事情就这样愉快的结束了。我可以一直陪甘雨老婆逛提瓦特大陆了。然而一年后,同事说:我们采购了一家I/O板卡,IgH切不到OP。What?Master代码我一共没看几行,直接凌乱了,甘雨老婆都不香了。我开始阅读和调试ioctl.c,master.c和slave.c。是的,你没有看错,我没读fsm*.c,我就是这么的无知。问题已经被我找到了,IgH读EEPROM的SII来配置SM0~SM3,但是没读到,或读到了错误的SII(这点我一直不理解,为什么SII和ESI会不一样),当时我定位到了slave部分(最外围嘛),SII不对,所以MailBox失败,COE失败,SM1到SM3就都没有配置。之前说了,我只配置了SM2和SM3。没有SM0和SM1的时候,IgH就把SM2和SM3配置到了SM0和SM1。而IgH是写死的,SM0和SM1走MailBox ,SM3和SM4走Process Date,通讯肯定有问题的。

既然如此,我从ENI读一下SM0和SM1,在ioctl.c加一个函数传递配置,slave.c加一个函数配置sync数组,搞定。甘雨老婆我来了!

  1. //伪代码,照这个生成SII数据
  2. uint8_t data[8 * EC_MAX_SYNC_MANAGERS] = {0};
  3. size_t size = sii_mb_sync_count * 8 + sii_pd_sync_count * 8;
  4. for(j = 0; j < sii_mb_sync_count; j++){
  5. EC_WRITE_U16(data + j*8 + 0, sii_mb_sync[j].physical_start_address);
  6. EC_WRITE_U16(data + j*8 + 2, sii_mb_sync[j].default_length);
  7. EC_WRITE_U8(data + j*8 + 4, sii_mb_sync[j].control_bytes);
  8. EC_WRITE_U8(data + j*8 + 6, sii_mb_sync[j].enable);
  9. }
  10. for(k = 0; k < sii_pd_sync_count; k++){
  11. EC_WRITE_U16(data + (j+k)*8 + 0, sii_pd_sync[k].physical_start_address);
  12. EC_WRITE_U16(data + (j+k)*8 + 2, sii_pd_sync[k].default_length);
  13. EC_WRITE_U8(data + (j+k)*8 + 4, sii_pd_sync[k].control_bytes);
  14. EC_WRITE_U8(data + (j+k)*8 + 6, sii_pd_sync[k].enable);
  15. }
  16. ecrt_master_set_sii_sync(master, Index, mailbox_protocols, data, size);
  1. // lib/master.C下添加
  2. int ecrt_master_set_sii_sync(ec_master_t *master, uint16_t slave_position, uint16_t mailbox_protocols,
  3. uint8_t *data, size_t size)
  4. {
  5. ec_ioctl_slave_sii_sync_t sii;
  6. int ret;
  7. sii.mailbox_protocols = mailbox_protocols;
  8. sii.slave_position = slave_position;
  9. sii.size = size;
  10. sii.data = data;
  11. ret = ioctl(master->fd, EC_IOCTL_SLAVE_INIT_SII_SYNC, &sii);
  12. if (EC_IOCTL_IS_ERROR(ret)) {
  13. fprintf(stderr, "Failed to execute SDO download: %s\n",
  14. strerror(EC_IOCTL_ERRNO(ret)));
  15. return -EC_IOCTL_ERRNO(ret);
  16. }
  17. return 0;
  18. }
  1. //ioctl.h
  2. typedef struct {
  3. // inputs
  4. uint16_t slave_position;
  5. uint16_t mailbox_protocols; /**< Supported mailbox protocols. */
  6. uint8_t *data; /**< Category data. */
  7. size_t size; /**< Number of bytes. */
  8. } ec_ioctl_slave_sii_sync_t;
  9. #define EC_IOCTL_SLAVE_INIT_SII_SYNC EC_IOW(0x5a, ec_ioctl_slave_sii_sync_t)
  1. //ioctl.c中添加,记得在EC_IOCTL中i调用这个函数
  2. /*****************************************************************************/
  3. static ATTRIBUTES int ec_ioctl_slave_init_sii_sync(
  4. ec_master_t *master, /**< EtherCAT master. */
  5. void *arg, /**< Userspace address to store the results. */
  6. ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
  7. )
  8. {
  9. //ec_slave_config_t *sc;
  10. ec_slave_t *slave;
  11. ec_ioctl_slave_sii_sync_t data;
  12. uint8_t *sii_data = NULL;
  13. int ret;
  14. if (unlikely(!ctx->requested))
  15. return -EPERM;
  16. if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
  17. return -EFAULT;
  18. if (!data.size)
  19. return -EINVAL;
  20. if (!(sii_data = kmalloc(data.size, GFP_KERNEL))) {
  21. return -ENOMEM;
  22. }
  23. if (copy_from_user(sii_data, (void __user *) data.data, data.size)) {
  24. kfree(sii_data);
  25. return -EFAULT;
  26. }
  27. if (down_interruptible(&master->master_sem)) {
  28. kfree(sii_data);
  29. return -EINTR;
  30. }
  31. if (!(slave = ec_master_find_slave(
  32. master, 0, data.slave_position))) {
  33. up(&master->master_sem);
  34. EC_MASTER_ERR(master, "Slave %u does not exist!\n",
  35. data.slave_position);
  36. kfree(sii_data);
  37. return -EINVAL;
  38. }
  39. /*
  40. if (!(sc = ec_master_get_config(master, data.slave_position))) {
  41. up(&master->master_sem);
  42. EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
  43. data.slave_position);
  44. kfree(sii_data);
  45. return -ENOENT;
  46. }
  47. sc->use_eni_sii = true;
  48. sc->mailbox_protocols = data.mailbox_protocols;
  49. sc->sii_syncs_data = kmalloc(data.size, GFP_KERNEL);
  50. memcpy(sc->sii_syncs_data, sii_data, data.size);
  51. sc->sii_syncs_size = data.size;
  52. */
  53. //ec_slave_clear_sync_managers(slave);
  54. slave->sii.mailbox_protocols = data.mailbox_protocols;
  55. if(0 != (ret = ec_slave_init_sii_syncs(slave, sii_data, data.size))){
  56. EC_MASTER_ERR(master, "Slave %u fetch_sii_syncs Failed!\n",
  57. data.slave_position);
  58. }
  59. up(&master->master_sem); /** \todo sc could be invalidated */
  60. kfree(sii_data);
  61. return 0;
  62. }
  1. //slave.c中添加
  2. int ec_slave_init_sii_syncs(
  3. ec_slave_t *slave, /**< EtherCAT slave. */
  4. const uint8_t *data, /**< Category data. */
  5. size_t data_size /**< Number of bytes. */
  6. )
  7. {
  8. unsigned int i, count, total_count;
  9. ec_sync_t *sync;
  10. size_t memsize;
  11. ec_sync_t *syncs;
  12. uint8_t index;
  13. // one sync manager struct is 4 words long
  14. if (data_size % 8) {
  15. EC_SLAVE_ERR(slave, "Invalid SII sync manager category size %zu.\n",
  16. data_size);
  17. return -EINVAL;
  18. }
  19. count = data_size / 8;
  20. if (count) {
  21. slave->sii.sync_count = 0;
  22. total_count = count + slave->sii.sync_count;
  23. if (total_count > EC_MAX_SYNC_MANAGERS) {
  24. EC_SLAVE_ERR(slave, "Exceeded maximum number of"
  25. " sync managers!\n");
  26. return -EOVERFLOW;
  27. }
  28. memsize = sizeof(ec_sync_t) * total_count;
  29. if (!(syncs = kmalloc(memsize, GFP_KERNEL))) {
  30. EC_SLAVE_ERR(slave, "Failed to allocate %zu bytes"
  31. " for sync managers.\n", memsize);
  32. return -ENOMEM;
  33. }
  34. for (i = 0; i < slave->sii.sync_count; i++)
  35. ec_sync_init_copy(syncs + i, slave->sii.syncs + i);
  36. // initialize new sync managers
  37. for (i = 0; i < count; i++, data += 8) {
  38. index = i + slave->sii.sync_count;
  39. sync = &syncs[index];
  40. ec_sync_init(sync, slave);
  41. sync->physical_start_address = EC_READ_U16(data);
  42. sync->default_length = EC_READ_U16(data + 2);
  43. sync->control_register = EC_READ_U8(data + 4);
  44. sync->enable = EC_READ_U8(data + 6);
  45. EC_SLAVE_DBG(slave, 1, "index = %d, start_addr = %d, def_len = %d, control_reg = %d, enable = %d\n",
  46. index, sync->physical_start_address, sync->default_length, sync->control_register, sync->enable);
  47. }
  48. if (slave->sii.syncs)
  49. kfree(slave->sii.syncs);
  50. slave->sii.syncs = syncs;
  51. slave->sii.sync_count = total_count;
  52. }
  53. return 0;
  54. }

又过差不多一年,同事说,客户要求,加工过程中,从站要经常断电,从新上电以后切不到OP!要知道,不管是机器人还是机床,基本都是要么一起通电要么一起断电的。所以这个真没考虑过。这回直接查到fsm*.c了, 当网络拓扑结构发生变化,slaves被从新扫描,我之前配置的slave部分丢了。断电以后没有人去配置slave的sync部分了,因此切不到OP。一番debug后,最新的配置方式是把SM信息写到master结构的slave中,在fsm阶段替换slave的SII中的sync部分,这样SM部分彻底告别SII了。SM的部分我想基本完事了吧。

  1. //伪代码,照这个生成SII数据
  2. uint8_t data[8 * EC_MAX_SYNC_MANAGERS] = {0};
  3. size_t size = sii_mb_sync_count * 8 + sii_pd_sync_count * 8;
  4. for(j = 0; j < sii_mb_sync_count; j++){
  5. EC_WRITE_U16(data + j*8 + 0, sii_mb_sync[j].physical_start_address);
  6. EC_WRITE_U16(data + j*8 + 2, sii_mb_sync[j].default_length);
  7. EC_WRITE_U8(data + j*8 + 4, sii_mb_sync[j].control_bytes);
  8. EC_WRITE_U8(data + j*8 + 6, sii_mb_sync[j].enable);
  9. }
  10. for(k = 0; k < sii_pd_sync_count; k++){
  11. EC_WRITE_U16(data + (j+k)*8 + 0, sii_pd_sync[k].physical_start_address);
  12. EC_WRITE_U16(data + (j+k)*8 + 2, sii_pd_sync[k].default_length);
  13. EC_WRITE_U8(data + (j+k)*8 + 4, sii_pd_sync[k].control_bytes);
  14. EC_WRITE_U8(data + (j+k)*8 + 6, sii_pd_sync[k].enable);
  15. }
  16. ecrt_slave_config_sii_sync(slave_config, mailbox_protocols, data, size);
  1. // lib/slave_config.c
  2. /*****************************************************************************/
  3. int ecrt_slave_config_sii_sync(ec_slave_config_t *sc, uint16_t mailbox_protocols,
  4. uint8_t *data, size_t size)
  5. {
  6. ec_ioctl_config_sii_sync_t sii;
  7. int ret;
  8. sii.config_index = sc->index;
  9. sii.mailbox_protocols = mailbox_protocols;
  10. sii.size = size;
  11. sii.data = data;
  12. ret = ioctl(sc->master->fd, EC_IOCTL_SC_INIT_SII_SYNC, &sii);
  13. if (EC_IOCTL_IS_ERROR(ret)) {
  14. fprintf(stderr, "Failed to execute SDO download: %s\n",
  15. strerror(EC_IOCTL_ERRNO(ret)));
  16. return -EC_IOCTL_ERRNO(ret);
  17. }
  18. return 0;
  19. }
  1. //ioctl.h
  2. typedef struct {
  3. // inputs
  4. uint32_t config_index;
  5. uint16_t mailbox_protocols; /**< Supported mailbox protocols. */
  6. uint8_t *data; /**< Category data. */
  7. size_t size; /**< Number of bytes. */
  8. } ec_ioctl_config_sii_sync_t;
  9. #define EC_IOCTL_SC_INIT_SII_SYNC EC_IOW(0x5b, ec_ioctl_config_sii_sync_t)
  1. //ioctl.c中添加,记得在EC_IOCTL中i调用这个函数
  2. /*****************************************************************************/
  3. static ATTRIBUTES int ec_ioctl_config_init_sii_sync(
  4. ec_master_t *master, /**< EtherCAT master. */
  5. void *arg, /**< ioctl() argument. */
  6. ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
  7. )
  8. {
  9. ec_ioctl_config_sii_sync_t data;
  10. ec_slave_config_t *sc;
  11. uint8_t *sii_data = NULL;
  12. if (unlikely(!ctx->requested))
  13. return -EPERM;
  14. if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
  15. return -EFAULT;
  16. }
  17. if (!data.size)
  18. return -EINVAL;
  19. if (!(sii_data = kmalloc(data.size, GFP_KERNEL))) {
  20. return -ENOMEM;
  21. }
  22. if (copy_from_user(sii_data, (void __user *) data.data, data.size)) {
  23. kfree(sii_data);
  24. return -EFAULT;
  25. }
  26. if (down_interruptible(&master->master_sem)) {
  27. kfree(sii_data);
  28. return -EINTR;
  29. }
  30. if (!(sc = ec_master_get_config(
  31. master, data.config_index))) {
  32. up(&master->master_sem);
  33. EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
  34. data.config_index);
  35. kfree(sii_data);
  36. return -EINVAL;
  37. }
  38. sc->use_eni_sii = true;
  39. sc->mailbox_protocols = data.mailbox_protocols;
  40. sc->sii_syncs_data = kmalloc(data.size, GFP_KERNEL);
  41. memcpy(sc->sii_syncs_data, sii_data, data.size);
  42. sc->sii_syncs_size = data.size;
  43. up(&master->master_sem); /** \todo sc could be invalidated */
  44. kfree(sii_data);
  45. return 0;
  46. }
  1. //fsm_slave_config.c 中的函数,自己对比修改一下 #if 1 部分
  2. void ec_fsm_slave_config_enter_mbox_sync(
  3. ec_fsm_slave_config_t *fsm /**< slave state machine */
  4. )
  5. {
  6. ec_slave_t *slave = fsm->slave;
  7. ec_datagram_t *datagram = fsm->datagram;
  8. #if 1
  9. ec_slave_config_t *sc = slave->config;
  10. ec_master_t *master = slave->master;
  11. #endif
  12. unsigned int i;
  13. // slave is now in INIT
  14. if (slave->current_state == slave->requested_state) {
  15. fsm->state = ec_fsm_slave_config_state_end; // successful
  16. EC_SLAVE_DBG(slave, 1, "Finished configuration.\n");
  17. return;
  18. }
  19. #if 1
  20. if(master->active && sc && sc->use_eni_sii){
  21. if(0 != (ec_slave_init_sii_syncs(slave, sc->sii_syncs_data, sc->sii_syncs_size)) ){
  22. EC_SLAVE_ERR(slave, "init_sii_syncs Failed!\n");
  23. }
  24. slave->sii.mailbox_protocols = sc->mailbox_protocols;
  25. }
  26. #endif
  27. if (!slave->sii.mailbox_protocols) {
  28. // no mailbox protocols supported
  29. EC_SLAVE_DBG(slave, 1, "Slave does not support"
  30. " mailbox communication.\n");
  31. #ifdef EC_SII_ASSIGN
  32. ec_fsm_slave_config_enter_assign_pdi(fsm);
  33. #else
  34. ec_fsm_slave_config_enter_boot_preop(fsm);
  35. #endif
  36. return;
  37. }
  38. EC_SLAVE_DBG(slave, 1, "Configuring mailbox sync managers...\n");
  39. if (slave->requested_state == EC_SLAVE_STATE_BOOT) {
  40. ec_sync_t sync;
  41. ec_datagram_fpwr(datagram, slave->station_address, 0x0800,
  42. EC_SYNC_PAGE_SIZE * 2);
  43. ec_datagram_zero(datagram);
  44. ec_sync_init(&sync, slave);
  45. sync.physical_start_address = slave->sii.boot_rx_mailbox_offset;
  46. sync.control_register = 0x26;
  47. sync.enable = 1;
  48. ec_sync_page(&sync, 0, slave->sii.boot_rx_mailbox_size,
  49. EC_DIR_INVALID, // use default direction
  50. 0, // no PDO xfer
  51. datagram->data);
  52. slave->configured_rx_mailbox_offset =
  53. slave->sii.boot_rx_mailbox_offset;
  54. slave->configured_rx_mailbox_size =
  55. slave->sii.boot_rx_mailbox_size;
  56. ec_sync_init(&sync, slave);
  57. sync.physical_start_address = slave->sii.boot_tx_mailbox_offset;
  58. sync.control_register = 0x22;
  59. sync.enable = 1;
  60. ec_sync_page(&sync, 1, slave->sii.boot_tx_mailbox_size,
  61. EC_DIR_INVALID, // use default direction
  62. 0, // no PDO xfer
  63. datagram->data + EC_SYNC_PAGE_SIZE);
  64. slave->configured_tx_mailbox_offset =
  65. slave->sii.boot_tx_mailbox_offset;
  66. slave->configured_tx_mailbox_size =
  67. slave->sii.boot_tx_mailbox_size;
  68. } else if (slave->sii.sync_count >= 2) { // mailbox configuration provided
  69. ec_datagram_fpwr(datagram, slave->station_address, 0x0800,
  70. EC_SYNC_PAGE_SIZE * slave->sii.sync_count);
  71. ec_datagram_zero(datagram);
  72. for (i = 0; i < 2; i++) {
  73. ec_sync_page(&slave->sii.syncs[i], i,
  74. slave->sii.syncs[i].default_length,
  75. NULL, // use default sync manager configuration
  76. 0, // no PDO xfer
  77. datagram->data + EC_SYNC_PAGE_SIZE * i);
  78. }
  79. slave->configured_rx_mailbox_offset =
  80. slave->sii.syncs[0].physical_start_address;
  81. slave->configured_rx_mailbox_size =
  82. slave->sii.syncs[0].default_length;
  83. slave->configured_tx_mailbox_offset =
  84. slave->sii.syncs[1].physical_start_address;
  85. slave->configured_tx_mailbox_size =
  86. slave->sii.syncs[1].default_length;
  87. } else { // no mailbox sync manager configurations provided
  88. ec_sync_t sync;
  89. EC_SLAVE_DBG(slave, 1, "Slave does not provide"
  90. " mailbox sync manager configurations.\n");
  91. ec_datagram_fpwr(datagram, slave->station_address, 0x0800,
  92. EC_SYNC_PAGE_SIZE * 2);
  93. ec_datagram_zero(datagram);
  94. ec_sync_init(&sync, slave);
  95. sync.physical_start_address = slave->sii.std_rx_mailbox_offset;
  96. sync.control_register = 0x26;
  97. sync.enable = 1;
  98. ec_sync_page(&sync, 0, slave->sii.std_rx_mailbox_size,
  99. NULL, // use default sync manager configuration
  100. 0, // no PDO xfer
  101. datagram->data);
  102. slave->configured_rx_mailbox_offset =
  103. slave->sii.std_rx_mailbox_offset;
  104. slave->configured_rx_mailbox_size =
  105. slave->sii.std_rx_mailbox_size;
  106. ec_sync_init(&sync, slave);
  107. sync.physical_start_address = slave->sii.std_tx_mailbox_offset;
  108. sync.control_register = 0x22;
  109. sync.enable = 1;
  110. ec_sync_page(&sync, 1, slave->sii.std_tx_mailbox_size,
  111. NULL, // use default sync manager configuration
  112. 0, // no PDO xfer
  113. datagram->data + EC_SYNC_PAGE_SIZE);
  114. slave->configured_tx_mailbox_offset =
  115. slave->sii.std_tx_mailbox_offset;
  116. slave->configured_tx_mailbox_size =
  117. slave->sii.std_tx_mailbox_size;
  118. }
  119. fsm->take_time = 1;
  120. fsm->retries = EC_FSM_RETRIES;
  121. fsm->state = ec_fsm_slave_config_state_mbox_sync;
  122. }

总结一句:ENI很重要,ENI很重要,ENI很重要。

唠叨一句:大多数Slave厂商用TwinCat配ESI跑测试,他才不管从站的SII对不对呢。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/506463
推荐阅读
相关标签
  

闽ICP备14008679号