SOEM裸机移植
源码地址
https://gitee.com/rathon/apollof429-v2.git
还有一些移植细节可以参考我之前写的一些博客
硬件平台:
正点原子APOLLOF429V2开发板
开发环境
stm32cubemx6.2.0版本,用的库为STM32Cube_FW_F4_V1.26.2,开发软件为STM32cubeide
时钟配置
串口配置
非gnu版串口重定向
在main.c或者.h中添加以下代码进行重定向
#include <stdio.h>
/* USER CODE BEGIN PFP */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
gnu版串口重定向
当配置了–gnu时,需要用以下的重定向代码
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
//#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
//comment_20190422: soem needs --gnu compile option,
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
定时器配置
主站系统时钟
将TIM2定时器配置成1s周期中断,主频1Mhz,这样CNT对应的单位刚好是1us。
打开定时器中断
HAL_TIM_Base_Start_IT(&htim2);
配置一个IO用于示波器抓取定时器时钟是否准确,这里选用PB0。(非必须)
uint32_t time_s ,time_ms, time_us;
extern int dorun;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
time_s++;
}
}
uint32_t GetSec(void)
{
return time_s;
}
uint32_t GetUSec(void)
{
time_us = (TIM2->CNT)%1000000;
return time_us;
}
主站DC定时器中断函数
将TIM5定时器配置成1ms周期中断,主频1Mhz,这样CNT对应的单位刚好是1us。
打开定时器中断
HAL_TIM_Base_Start_IT(&htim5);
配置一个IO用于示波器抓取定时器时钟是否准确,这里选用PB0。(非必须)
uint32_t time_s ,time_ms, time_us;
extern int dorun;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
time_s++;
}
else if (htim->Instance == TIM5)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
if(dorun==1)
{
ecat_loop();
}
}
else
{
}
}
uint32_t GetSec(void)
{
return time_s;
}
uint32_t GetUSec(void)
{
time_us = (TIM2->CNT)%1000000;
return time_us;
}
osal相关文件
osal是soem里的文件,里面有个延时相关的函数(因为我不喜欢正点原子提供的SYSTEM/delay文件夹里的函数),由于后面的PCF8574和IIC驱动里有延时调用,因此在这里我就先移植“osal”。
osal_def.h
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#ifndef _osal_defs_
#define _osal_defs_
#ifdef __cplusplus
extern "C"
{
#endif
// define if debug printf is needed
//#define EC_DEBUG
#ifdef EC_DEBUG
#define EC_PRINT printf
#else
#define EC_PRINT(...) do {} while (0)
#endif
#ifndef PACKED
#define PACKED_BEGIN
#define PACKED __attribute__((__packed__))
#define PACKED_END
#endif
#define OSAL_THREAD_HANDLE task_t *
#define OSAL_THREAD_FUNC void
#define OSAL_THREAD_FUNC_RT void
#ifdef __cplusplus
}
#endif
#endif
osal.h
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#ifndef _osal_
#define _osal_
#ifdef __cplusplus
extern "C"
{
#endif
#include "osal_defs.h"
#include <stdint.h>
#include <stdbool.h>
/* General types */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
typedef uint8_t boolean;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef int64_t int64;
typedef uint64_t uint64;
typedef float float32;
typedef double float64;
typedef struct
{
uint32 sec; /*< Seconds elapsed since the Epoch (Jan 1, 1970) */
uint32 usec; /*< Microseconds elapsed since last second boundary */
} ec_timet;
typedef struct
{
uint32 tv_sec;
uint32 tv_usec;
}timeval;
typedef struct
{
int tz_minuteswest; /* 格林尼治时间往西方的时差,以分钟为单位 */
int tz_dsttime; /* 日光节约时间的修正方式 */
}timezone;
typedef struct osal_timer
{
ec_timet stop_time;
} osal_timert;
void osal_timer_start(osal_timert * self, uint32 timeout_us);
boolean osal_timer_is_expired(osal_timert * self);
int osal_usleep(uint32 usec);
ec_timet osal_current_time(void);
void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff);
int osal_thread_create(void *thandle, int stacksize, void *func, void *param);
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param);
#ifdef __cplusplus
}
#endif
#endif
osal.c
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#include "osal.h"
#include "tim.h"
#include <stdlib.h>
#define timercmp(a, b, CMP) \
(((a)->tv_sec == (b)->tv_sec) ? \
((a)->tv_usec CMP (b)->tv_usec) : \
((a)->tv_sec CMP (b)->tv_sec))
#define timeradd(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
if ((result)->tv_usec >= 1000000) \
{ \
++(result)->tv_sec; \
(result)->tv_usec -= 1000000; \
} \
} while (0)
#define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
#define USECS_PER_SEC 1000000
#define USECS_PER_TICK (USECS_PER_SEC / CFG_TICKS_PER_SECOND)
/* Workaround for rt-labs defect 776.
* Default implementation of udelay() didn't work correctly when tick was
* shorter than one millisecond.
*/
void udelay (uint32_t us)
{
osal_timert qtime;
osal_timer_start(&qtime, us);
while(!osal_timer_is_expired(&qtime));
}
int gettimeofday(timeval *tp, void *tzp)
{
uint32_t sec = GetSec();
uint32_t us = GetUSec();
tp->tv_usec = us;
tp->tv_sec = sec;
return 0;
}
int osal_usleep (uint32 usec)
{
udelay(usec);
return 0;
}
int osal_gettimeofday(timeval *tv, timezone *tz)
{
return gettimeofday(tv, tz);
}
ec_timet osal_current_time (void)
{
timeval current_time;
ec_timet return_value;
gettimeofday (¤t_time, 0);
return_value.sec = current_time.tv_sec;
return_value.usec = current_time.tv_usec;
return return_value;
}
void osal_timer_start (osal_timert * self, uint32 timeout_usec)
{
timeval start_time;
timeval timeout;
timeval stop_time;
gettimeofday (&start_time, 0);
timeout.tv_sec = timeout_usec / USECS_PER_SEC;
timeout.tv_usec = timeout_usec % USECS_PER_SEC;
timeradd (&start_time, &timeout, &stop_time);
self->stop_time.sec = stop_time.tv_sec;
self->stop_time.usec = stop_time.tv_usec;
}
boolean osal_timer_is_expired (osal_timert * self)
{
timeval current_time;
timeval stop_time;
int is_not_yet_expired;
gettimeofday (¤t_time, 0);
stop_time.tv_sec = self->stop_time.sec;
stop_time.tv_usec = self->stop_time.usec;
is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
return is_not_yet_expired == false;
}
void *osal_malloc(size_t size)
{
return malloc(size);
}
void osal_free(void *ptr)
{
free(ptr);
}
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
{
// thandle = task_spawn ("worker", func, 6,stacksize, param);
// if(!thandle)
// {
// return 0;
// }
return 1;
}
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
{
// thandle = task_spawn ("worker_rt", func, 15 ,stacksize, param);
// if(!thandle)
// {
// return 0;
// }
return 1;
}
添加PCF8574和IIC驱动
由于正点原子的阿波罗开发板中,phy芯片的ETH_RESET引脚由PCF8574芯片控制,因此得从正点原子的代码中copy来以下驱动文件。
由于正点原子这种独特的设计,导致phy的复位引脚的代码变得很复杂。
myiic.h
/**
****************************************************************************************************
* @file myiic.h
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2022-04-20
* @brief IIC 驱动代码
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 阿波罗 F429开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
* 修改说明
* V1.0 20220420
* 第一次发布
*
****************************************************************************************************
*/
#ifndef __MYIIC_H
#define __MYIIC_H
#include "stm32f4xx_hal.h"
#include "osal.h"
/******************************************************************************************/
/* 引脚 定义 */
#define IIC_SCL_GPIO_PORT GPIOH
#define IIC_SCL_GPIO_PIN GPIO_PIN_4
#define IIC_SCL_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0) /* PH口时钟使能 */
#define IIC_SDA_GPIO_PORT GPIOH
#define IIC_SDA_GPIO_PIN GPIO_PIN_5
#define IIC_SDA_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0) /* PH口时钟使能 */
/******************************************************************************************/
/* IO操作 */
#define IIC_SCL(x) do{ x ? \
HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* SCL */
#define IIC_SDA(x) do{ x ? \
HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* SDA */
#define IIC_READ_SDA HAL_GPIO_ReadPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN) /* 读取SDA */
/******************************************************************************************/
/* IIC所有操作函数 */
void iic_init(void); /* 初始化IIC的IO口 */
void iic_start(void); /* 发送IIC开始信号 */
void iic_stop(void); /* 发送IIC停止信号 */
void iic_ack(void); /* IIC发送ACK信号 */
void iic_nack(void); /* IIC不发送ACK信号 */
uint8_t iic_wait_ack(void); /* IIC等待ACK信号 */
void iic_send_byte(uint8_t data); /* IIC发送一个字节 */
uint8_t iic_read_byte(unsigned char ack); /* IIC读取一个字节 */
#endif
myiic.c
/**
****************************************************************************************************
* @file myiic.c
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2022-04-20
* @brief IIC 驱动代码
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 阿波罗 F429开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
* 修改说明
* V1.0 20220420
* 第一次发布
*
****************************************************************************************************
*/
#include "myiic.h"
/**
* @brief 初始化IIC
* @param 无
* @retval 无
*/
void iic_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
IIC_SCL_GPIO_CLK_ENABLE(); /* SCL引脚时钟使能 */
IIC_SDA_GPIO_CLK_ENABLE(); /* SDA引脚时钟使能 */
gpio_init_struct.Pin = IIC_SCL_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 快速 */
HAL_GPIO_Init(IIC_SCL_GPIO_PORT, &gpio_init_struct); /* SCL引脚初始化 */
/* SDA引脚开漏输出,上拉, 这样就不用再设置IO方向了,开漏输出的时候(=1), 也可以读取外部信号的高低电平 */
gpio_init_struct.Pin = IIC_SDA_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD; /* 开漏输出 */
HAL_GPIO_Init(IIC_SDA_GPIO_PORT, &gpio_init_struct); /* SDA引脚初始化 */
iic_stop(); /* 停止总线上所有设备 */
}
/**
* @brief IIC延时函数,用于控制IIC读写速度
* @param 无
* @retval 无
*/
static void iic_delay(void)
{
osal_usleep(2); /* 2us的延时, 读写速度在250Khz以内 */
}
/**
* @brief 产生IIC起始信号
* @param 无
* @retval 无
*/
void iic_start(void)
{
IIC_SDA(1);
IIC_SCL(1);
iic_delay();
IIC_SDA(0); /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */
iic_delay();
IIC_SCL(0); /* 钳住I2C总线,准备发送或接收数据 */
iic_delay();
}
/**
* @brief 产生IIC停止信号
* @param 无
* @retval 无
*/
void iic_stop(void)
{
IIC_SDA(0); /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
iic_delay();
IIC_SCL(1);
iic_delay();
IIC_SDA(1); /* 发送I2C总线结束信号 */
iic_delay();
}
/**
* @brief 等待应答信号到来
* @param 无
* @retval 1,接收应答失败
* 0,接收应答成功
*/
uint8_t iic_wait_ack(void)
{
uint8_t waittime = 0;
uint8_t rack = 0;
IIC_SDA(1); /* 主机释放SDA线(此时外部器件可以拉低SDA线) */
iic_delay();
IIC_SCL(1); /* SCL=1, 此时从机可以返回ACK */
iic_delay();
while (IIC_READ_SDA) /* 等待应答 */
{
waittime++;
if (waittime > 250)
{
iic_stop();
rack = 1;
break;
}
}
IIC_SCL(0); /* SCL=0, 结束ACK检查 */
iic_delay();
return rack;
}
/**
* @brief 产生ACK应答
* @param 无
* @retval 无
*/
void iic_ack(void)
{
IIC_SDA(0); /* SCL 0 -> 1 时 SDA = 0,表示应答 */
iic_delay();
IIC_SCL(1); /* 产生一个时钟 */
iic_delay();
IIC_SCL(0);
iic_delay();
IIC_SDA(1); /* 主机释放SDA线 */
iic_delay();
}
/**
* @brief 不产生ACK应答
* @param 无
* @retval 无
*/
void iic_nack(void)
{
IIC_SDA(1); /* SCL 0 -> 1 时 SDA = 1,表示不应答 */
iic_delay();
IIC_SCL(1); /* 产生一个时钟 */
iic_delay();
IIC_SCL(0);
iic_delay();
}
/**
* @brief IIC发送一个字节
* @param data: 要发送的数据
* @retval 无
*/
void iic_send_byte(uint8_t data)
{
uint8_t t;
for (t = 0; t < 8; t++)
{
IIC_SDA((data & 0x80) >> 7); /* 高位先发送 */
iic_delay();
IIC_SCL(1);
iic_delay();
IIC_SCL(0);
data <<= 1; /* 左移1位,用于下一次发送 */
}
IIC_SDA(1); /* 发送完成, 主机释放SDA线 */
}
/**
* @brief IIC读取一个字节
* @param ack: ack=1时,发送ack; ack=0时,发送nack
* @retval 接收到的数据
*/
uint8_t iic_read_byte(uint8_t ack)
{
uint8_t i, receive = 0;
for (i = 0; i < 8; i++ ) /* 接收1个字节数据 */
{
receive <<= 1; /* 高位先输出,所以先收到的数据位要左移 */
IIC_SCL(1);
iic_delay();
if (IIC_READ_SDA)
{
receive++;
}
IIC_SCL(0);
iic_delay();
}
if (!ack)
{
iic_nack(); /* 发送nACK */
}
else
{
iic_ack(); /* 发送ACK */
}
return receive;
}
pcf8574.h
/**
****************************************************************************************************
* @file pcf8574.h
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2022-04-20
* @brief PCF8574 驱动代码
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 阿波罗 F429开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
* 修改说明
* V1.0 20220420
* 第一次发布
*
****************************************************************************************************
*/
#ifndef __PCF8574_H
#define __PCF8574_H
#include "myiic.h"
/******************************************************************************************/
/* 引脚 定义 */
#define PCF8574_GPIO_PORT GPIOB
#define PCF8574_GPIO_PIN GPIO_PIN_12
#define PCF8574_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */
/******************************************************************************************/
#define PCF8574_INT HAL_GPIO_ReadPin(PCF8574_GPIO_PORT, PCF8574_GPIO_PIN) /* PCF8574 INT脚 */
#define PCF8574_ADDR 0X40 /* PCF8574地址(左移了一位) */
/* PCF8574各个IO的功能 */
#define BEEP_IO 0 /* 蜂鸣器控制引脚 P0 */
#define AP_INT_IO 1 /* AP3216C中断引脚 P1 */
#define DCMI_PWDN_IO 2 /* DCMI的电源控制引脚 P2 */
#define USB_PWR_IO 3 /* USB电源控制引脚 P3 */
#define EX_IO 4 /* 扩展IO,自定义使用 P4 */
#define MPU_INT_IO 5 /* SH3001中断引脚 P5 */
#define RS485_RE_IO 6 /* RS485_RE引脚 P6 */
#define ETH_RESET_IO 7 /* 以太网复位引脚 P7 */
/******************************************************************************************/
uint8_t pcf8574_init(void);
uint8_t pcf8574_read_byte(void);
void pcf8574_write_byte(uint8_t data);
void pcf8574_write_bit(uint8_t bit, uint8_t sta);
uint8_t pcf8574_read_bit(uint8_t bit);
#endif
pcf8574.c
/**
****************************************************************************************************
* @file pcf8574.c
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2022-04-20
* @brief PCF8574 驱动代码
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 阿波罗 F429开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
* 修改说明
* V1.0 20220420
* 第一次发布
*
****************************************************************************************************
*/
#include "pcf8574.h"
/**
* @brief 初始化PCF8574
* @param 无
* @retval 0, 成功;
1, 失败;
*/
uint8_t pcf8574_init(void)
{
uint8_t temp = 0;
GPIO_InitTypeDef gpio_init_struct;
PCF8574_GPIO_CLK_ENABLE(); /* 使能GPIOB时钟 */
gpio_init_struct.Pin = PCF8574_GPIO_PIN; /* PB12 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_HIGH; /* 高速 */
HAL_GPIO_Init(PCF8574_GPIO_PORT, &gpio_init_struct); /* 初始化IO */
iic_init(); /* IIC初始化 */
/* 检查PCF8574是否在位 */
iic_start();
iic_send_byte(PCF8574_ADDR); /* 写地址 */
temp = iic_wait_ack(); /* 等待应答,通过判断是否有ACK应答,来判断PCF8574的状态 */
iic_stop(); /* 产生一个停止条件 */
pcf8574_write_byte(0XFF); /* 默认情况下所有IO输出高电平 */
return temp;
}
/**
* @brief 读取PCF8574的8位IO值
* @param 无
* @retval 读到的数据
*/
uint8_t pcf8574_read_byte(void)
{
uint8_t temp = 0;
iic_start();
iic_send_byte(PCF8574_ADDR | 0X01); /* 进入接收模式 */
iic_wait_ack();
temp = iic_read_byte(0);
iic_stop(); /* 产生一个停止条件 */
return temp;
}
/**
* @brief 向PCF8574写入8位IO值
* @param data : 要写入的数据
* @retval 无
*/
void pcf8574_write_byte(uint8_t data)
{
iic_start();
iic_send_byte(PCF8574_ADDR | 0X00); /* 发送器件地址0X40,写数据 */
iic_wait_ack();
iic_send_byte(data); /* 发送字节 */
iic_wait_ack();
iic_stop(); /* 产生一个停止条件 */
osal_usleep(10000);
}
/**
* @brief 设置PCF8574某个IO的高低电平
* @param bit : 要设置的IO编号,0~7
* @param sta : IO的状态;0或1
* @retval 无
*/
void pcf8574_write_bit(uint8_t bit, uint8_t sta)
{
uint8_t data;
data = pcf8574_read_byte(); /* 先读出原来的设置 */
if (sta == 0)
{
data &= ~(1 << bit);
}
else
{
data |= 1 << bit;
}
pcf8574_write_byte(data); /* 写入新的数据 */
}
/**
* @brief 读取PCF8574的某个IO的值
* @param bit : 要读取的IO编号, 0~7
* @retval 此IO口的值(状态, 0/1)
*/
uint8_t pcf8574_read_bit(uint8_t bit)
{
uint8_t data;
data = pcf8574_read_byte(); /* 先读取这个8位IO的值 */
if (data & (1 << bit))
{
return 1;
}
else
{
return 0;
}
}
以太网外设
STM32CUBEMX配置
此时会自动生成eth.c文件
eth.h
/**
******************************************************************************
* @file eth.h
* @brief This file contains all the function prototypes for
* the eth.c file
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2024 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __ETH_H__
#define __ETH_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern ETH_HandleTypeDef heth;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_ETH_Init(void);
/* USER CODE BEGIN Prototypes */
/* PHY芯片寄存器映射表 */
#define ETH_CHIP_BCR ((uint16_t)0x0000U)
#define ETH_CHIP_BSR ((uint16_t)0x0001U)
#define PHY_REGISTER2 ((uint16_t)0x0002U)
#define PHY_REGISTER3 ((uint16_t)0x0003U)
/* 操作SCR寄存器的值(一般不需要修改) */
#define ETH_CHIP_BCR_SOFT_RESET ((uint16_t)0x8000U)
#define ETH_CHIP_BCR_LOOPBACK ((uint16_t)0x4000U)
#define ETH_CHIP_BCR_SPEED_SELECT ((uint16_t)0x2000U)
#define ETH_CHIP_BCR_AUTONEGO_EN ((uint16_t)0x1000U)
#define ETH_CHIP_BCR_POWER_DOWN ((uint16_t)0x0800U)
#define ETH_CHIP_BCR_ISOLATE ((uint16_t)0x0400U)
#define ETH_CHIP_BCR_RESTART_AUTONEGO ((uint16_t)0x0200U)
#define ETH_CHIP_BCR_DUPLEX_MODE ((uint16_t)0x0100U)
/* 操作BSR寄存器的值(一般不需要修改) */
#define ETH_CHIP_BSR_100BASE_T4 ((uint16_t)0x8000U)
#define ETH_CHIP_BSR_100BASE_TX_FD ((uint16_t)0x4000U)
#define ETH_CHIP_BSR_100BASE_TX_HD ((uint16_t)0x2000U)
#define ETH_CHIP_BSR_10BASE_T_FD ((uint16_t)0x1000U)
#define ETH_CHIP_BSR_10BASE_T_HD ((uint16_t)0x0800U)
#define ETH_CHIP_BSR_100BASE_T2_FD ((uint16_t)0x0400U)
#define ETH_CHIP_BSR_100BASE_T2_HD ((uint16_t)0x0200U)
#define ETH_CHIP_BSR_EXTENDED_STATUS ((uint16_t)0x0100U)
#define ETH_CHIP_BSR_AUTONEGO_CPLT ((uint16_t)0x0020U)
#define ETH_CHIP_BSR_REMOTE_FAULT ((uint16_t)0x0010U)
#define ETH_CHIP_BSR_AUTONEGO_ABILITY ((uint16_t)0x0008U)
#define ETH_CHIP_BSR_LINK_STATUS ((uint16_t)0x0004U)
#define ETH_CHIP_BSR_JABBER_DETECT ((uint16_t)0x0002U)
#define ETH_CHIP_BSR_EXTENDED_CAP ((uint16_t)0x0001U)
/* PHY芯片进程状态 */
#define ETH_CHIP_STATUS_READ_ERROR ((int32_t)-5)
#define ETH_CHIP_STATUS_WRITE_ERROR ((int32_t)-4)
#define ETH_CHIP_STATUS_ADDRESS_ERROR ((int32_t)-3)
#define ETH_CHIP_STATUS_RESET_TIMEOUT ((int32_t)-2)
#define ETH_CHIP_STATUS_ERROR ((int32_t)-1)
#define ETH_CHIP_STATUS_OK ((int32_t) 0)
#define ETH_CHIP_STATUS_LINK_DOWN ((int32_t) 1)
#define ETH_CHIP_STATUS_100MBITS_FULLDUPLEX ((int32_t) 2)
#define ETH_CHIP_STATUS_100MBITS_HALFDUPLEX ((int32_t) 3)
#define ETH_CHIP_STATUS_10MBITS_FULLDUPLEX ((int32_t) 4)
#define ETH_CHIP_STATUS_10MBITS_HALFDUPLEX ((int32_t) 5)
#define ETH_CHIP_STATUS_AUTONEGO_NOTDONE ((int32_t) 6)
void PhyReset(void);
void PhyEventHandler(void);
void PhyTick(void);
#define LAN8720 0
#define SR8201F 1
#define YT8512C 2
#define RTL8201 3
int EthRdPacket(void* pBuff);
int EthWrPacket(void* pBuff, int Len);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __ETH_H__ */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
eth.c
/**
******************************************************************************
* @file eth.c
* @brief This file provides code for the configuration
* of the ETH instances.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2024 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "eth.h"
/* USER CODE BEGIN 0 */
#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "pcf8574.h"
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma data_alignment=4
#endif
__ALIGN_BEGIN ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB] __ALIGN_END;/* Ethernet Rx MA Descriptor */
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma data_alignment=4
#endif
__ALIGN_BEGIN ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB] __ALIGN_END;/* Ethernet Tx DMA Descriptor */
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma data_alignment=4
#endif
__ALIGN_BEGIN uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __ALIGN_END; /* Ethernet Receive Buffer */
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma data_alignment=4
#endif
__ALIGN_BEGIN uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __ALIGN_END; /* Ethernet Transmit Buffer */
#define YT8512C_AND_RTL8201BL_PHYREGISTER2 0x0000
#define SR8201F_PHYREGISTER2 0x001C
#define LAN8720A_PHYREGISTER2 0x0007
uint32_t PHY_TYPE;
uint16_t ETH_CHIP_PHYSCSR;
uint16_t ETH_CHIP_SPEED_STATUS;
uint16_t ETH_CHIP_DUPLEX_STATUS;
/* USER CODE END 0 */
ETH_HandleTypeDef heth;
/* ETH init function */
void MX_ETH_Init(void)
{
/* USER CODE BEGIN ETH_Init 0 */
/* USER CODE END ETH_Init 0 */
/* USER CODE BEGIN ETH_Init 1 */
/* USER CODE END ETH_Init 1 */
heth.Instance = ETH;
heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
heth.Init.Speed = ETH_SPEED_100M;
heth.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
heth.Init.PhyAddress = LAN8742A_PHY_ADDRESS;
heth.Init.MACAddr[0] = 0x00;
heth.Init.MACAddr[1] = 0x80;
heth.Init.MACAddr[2] = 0xE1;
heth.Init.MACAddr[3] = 0x00;
heth.Init.MACAddr[4] = 0x00;
heth.Init.MACAddr[5] = 0x00;
heth.Init.RxMode = ETH_RXPOLLING_MODE;
heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
/* USER CODE BEGIN MACADDRESS */
heth.Init.PhyAddress = 0U;
/* USER CODE END MACADDRESS */
if (HAL_ETH_Init(&heth) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ETH_Init 2 */
/* Initialize Tx Descriptors list: Chain Mode */
HAL_ETH_DMATxDescListInit(&heth, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
/* Initialize Rx Descriptors list: Chain Mode */
HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
/* Enable MAC and DMA transmission and reception */
HAL_ETH_Start(&heth);
/* USER CODE END ETH_Init 2 */
}
void HAL_ETH_MspInit(ETH_HandleTypeDef* ethHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(ethHandle->Instance==ETH)
{
/* USER CODE BEGIN ETH_MspInit 0 */
/* USER CODE END ETH_MspInit 0 */
/* ETH clock enable */
__HAL_RCC_ETH_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/**ETH GPIO Configuration
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PB11 ------> ETH_TX_EN
PG13 ------> ETH_TXD0
PG14 ------> ETH_TXD1
*/
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/* USER CODE BEGIN ETH_MspInit 1 */
PhyReset();
/* USER CODE END ETH_MspInit 1 */
}
}
void HAL_ETH_MspDeInit(ETH_HandleTypeDef* ethHandle)
{
if(ethHandle->Instance==ETH)
{
/* USER CODE BEGIN ETH_MspDeInit 0 */
/* USER CODE END ETH_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ETH_CLK_DISABLE();
/**ETH GPIO Configuration
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PB11 ------> ETH_TX_EN
PG13 ------> ETH_TXD0
PG14 ------> ETH_TXD1
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_11);
HAL_GPIO_DeInit(GPIOG, GPIO_PIN_13|GPIO_PIN_14);
/* USER CODE BEGIN ETH_MspDeInit 1 */
/* USER CODE END ETH_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* SR8201F Register 2 0x001C
Register 3 0xC016
YT8512C Register 2 0x0000
Register 3 0x0128
LAN8720A Register 2 0x0007
Register 3 0xC0F0
RTL8201BL Register 2 0x0000
Register 3 0x8201 */
void PhyReset(void)
{
uint32_t regvalue;
HAL_ETH_ReadPHYRegister(&heth,PHY_REGISTER2, ®value);//get phy id
PHY_TYPE = regvalue;
printf("phy id is %d\n",PHY_TYPE);
if (PHY_TYPE && 0xFFF == 0xFFF) /*LAN8720A*/
{
pcf8574_write_bit(ETH_RESET_IO,1);
osal_usleep(10000);
pcf8574_write_bit(ETH_RESET_IO,0);
osal_usleep(10000);
}
else /*YT8512C*/
{
pcf8574_write_bit(ETH_RESET_IO,0);
osal_usleep(10000);
pcf8574_write_bit(ETH_RESET_IO,1);
osal_usleep(10000);
}
HAL_ETH_ReadPHYRegister(&heth,PHY_REGISTER2, ®value);//get phy id
switch (regvalue)
{
case YT8512C_AND_RTL8201BL_PHYREGISTER2:
HAL_ETH_ReadPHYRegister(&heth,PHY_REGISTER3, ®value);
if (regvalue == 0x128)
{
ETH_CHIP_PHYSCSR = ((uint16_t)0x11);
ETH_CHIP_SPEED_STATUS = ((uint16_t)0x4010);
ETH_CHIP_DUPLEX_STATUS = ((uint16_t)0x2000);
PHY_TYPE = YT8512C;
}
else
{
ETH_CHIP_PHYSCSR = ((uint16_t)0x10);
ETH_CHIP_SPEED_STATUS = ((uint16_t)0x0022);
ETH_CHIP_DUPLEX_STATUS = ((uint16_t)0x0004);
PHY_TYPE = RTL8201;
}
break;
case SR8201F_PHYREGISTER2:
ETH_CHIP_PHYSCSR = ((uint16_t)0x00);
ETH_CHIP_SPEED_STATUS = ((uint16_t)0x2020);
ETH_CHIP_DUPLEX_STATUS = ((uint16_t)0x0100);
PHY_TYPE = SR8201F;
break;
case LAN8720A_PHYREGISTER2:
ETH_CHIP_PHYSCSR = ((uint16_t)0x1F);
ETH_CHIP_SPEED_STATUS = ((uint16_t)0x0004);
ETH_CHIP_DUPLEX_STATUS = ((uint16_t)0x0010);
PHY_TYPE = LAN8720;
break;
}
}
int32_t eth_chip_get_link_state(void)
{
uint32_t readval = 0;
if (HAL_ETH_ReadPHYRegister(&heth,ETH_CHIP_PHYSCSR, &readval) != HAL_OK)
{
return ETH_CHIP_STATUS_READ_ERROR;
}
if (((readval & ETH_CHIP_SPEED_STATUS) != ETH_CHIP_SPEED_STATUS) && ((readval & ETH_CHIP_DUPLEX_STATUS) != 0))
{
return ETH_CHIP_STATUS_100MBITS_FULLDUPLEX;
}
else if (((readval & ETH_CHIP_SPEED_STATUS) != ETH_CHIP_SPEED_STATUS))
{
return ETH_CHIP_STATUS_100MBITS_HALFDUPLEX;
}
else if (((readval & ETH_CHIP_BCR_DUPLEX_MODE) != ETH_CHIP_BCR_DUPLEX_MODE))
{
return ETH_CHIP_STATUS_10MBITS_FULLDUPLEX;
}
else
{
return ETH_CHIP_STATUS_10MBITS_HALFDUPLEX;
}
}
int linkState;
int phyEvent;
void PhyEventHandler(void)
{
uint32_t value;
//Any link failure condition is latched in the BMSR register. Reading
//the register twice will always return the actual link status
HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, &value);/* Read PHY_BSR*/
HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, &value);/* Read PHY_BSR*/
//Link is up?
if((value & PHY_LINKED_STATUS) != 0)
{
int32_t readval = 0;
uint32_t duplex = 0;
uint32_t speed = 0;
readval = eth_chip_get_link_state();
if (readval == ETH_CHIP_STATUS_READ_ERROR)
{
//Update link state
linkState = FALSE;
}
else
{
switch (readval)
{
case ETH_CHIP_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_MODE_FULLDUPLEX;
speed = ETH_SPEED_100M;
printf("ETH_CHIP_STATUS_100MBITS_FULLDUPLEX\n");
break;
case ETH_CHIP_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_MODE_HALFDUPLEX;
speed = ETH_SPEED_100M;
printf("ETH_CHIP_STATUS_100MBITS_HALFDUPLEX\n");
break;
case ETH_CHIP_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_MODE_FULLDUPLEX;
speed = ETH_SPEED_10M;
printf("ETH_CHIP_STATUS_10MBITS_FULLDUPLEX\n");
break;
case ETH_CHIP_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_MODE_HALFDUPLEX;
speed = ETH_SPEED_10M;
printf("ETH_CHIP_STATUS_10MBITS_HALFDUPLEX\n");
break;
default:
duplex = ETH_MODE_FULLDUPLEX;
speed = ETH_SPEED_100M;
printf("ETH_CHIP_STATUS_100MBITS_FULLDUPLEX\n");
break;
}
heth.Init.Speed = speed;
heth.Init.DuplexMode = duplex;
//Update link state
linkState = TRUE;
/* ETHERNET MAC Re-Configuration */
HAL_ETH_ConfigMAC(&heth, (ETH_MACInitTypeDef *) NULL);
/* Restart MAC interface */
HAL_ETH_Start(&heth);
}
}
else
{
//Update link state
linkState = FALSE;
}
}
extern int dorun;
void PhyTick(void)
{
uint32_t value;
int link;
//Any link failure condition is latched in the BMSR register. Reading
//the register twice will always return the actual link status
HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, &value);/* Read PHY_BSR*/
HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, &value);/* Read PHY_BSR*/
//Retrieve current link state
link= (value & PHY_LINKED_STATUS) ? TRUE : FALSE;
//Link up event?
if(link && !linkState)
{
//Set event flag
phyEvent = TRUE;
printf("Link up event\n");
}
//Link down event?
else if(!link && linkState)
{
//Set event flag
phyEvent = TRUE;
dorun = 0;
printf("Link down event\n");
}
}
int EthRdPacket(void* pBuff)
{
int Len;
uint8_t* pDmaBuff;
HAL_StatusTypeDef HalStatus;
__IO ETH_DMADescTypeDef *dmarxdesc;
HalStatus = HAL_ETH_GetReceivedFrame(&heth); // check if a packet has been received
/* Clean and Invalidate data cache */
//SCB_CleanInvalidateDCache();
if (HalStatus == HAL_OK) // packet received
{
Len = heth.RxFrameInfos.length; // packet lenght
pDmaBuff = (uint8_t*)heth.RxFrameInfos.buffer; // DMA buffer pointe
memcpy (pBuff, pDmaBuff, Len); // read the data
/* Release descriptors to DMA */
/* Point to first descriptor */
dmarxdesc = heth.RxFrameInfos.FSRxDesc;
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
for (int i=0; i< heth.RxFrameInfos.SegCount; i++)
{
dmarxdesc->Status |= ETH_DMARXDESC_OWN;
dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
}
/* Clear Segment_Count */
heth.RxFrameInfos.SegCount =0;
return Len; // return the number of bytes read
}
else
{
return 0; // no packet received
}
}
int EthWrPacket(void* pBuff, int Len)
{
uint8_t* pDmaBuff;
HAL_StatusTypeDef HalStatus;
/* Clean and Invalidate data cache */
//SCB_CleanInvalidateDCache();
if ((heth.TxDesc->Status & ETH_DMATXDESC_OWN) == (uint32_t)RESET)
{
pDmaBuff = (uint8_t*)(heth.TxDesc->Buffer1Addr);
memcpy (pDmaBuff, pBuff, Len);
HalStatus = HAL_ETH_TransmitFrame(&heth, Len);
if (HalStatus != HAL_OK)
{
printf ("HAL_ETH_TransmitFrame err %d\n", HalStatus);
return 0;
}
return Len;
}
else
{
return 0;
}
}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
SOEM例程
main.c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
time_s = 0;
time_ms = 0;
time_us = 0;
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM5_Init();
MX_USART1_UART_Init();
//MX_ETH_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim5);
while (pcf8574_init()) /* ��ⲻ��PCF8574 */
{
printf("PCF8574 Check Failed!\n");
osal_usleep(1000);
printf("Please Check! \n");
}
printf("PCF8574 Check Success!\n");
MX_ETH_Init();
linkState = FALSE;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
PhyTick();
if(phyEvent)
{
phyEvent = FALSE;
PhyEventHandler();
simpletest("hello");
//slaveinfo("hello");
//servotest("hello");
}
else
{
if(time_us%100000 == 0)
{
//ecatcheck();
}
}
}
/* USER CODE END 3 */
}
slaveinfo.c
/** \file
* \brief Example code for Simple Open EtherCAT master
*
* Usage : slaveinfo [ifname] [-sdo] [-map]
* Ifname is NIC interface, f.e. eth0.
* Optional -sdo to display CoE object dictionary.
* Optional -map to display slave PDO mapping
*
* This shows the configured slave data.
*
* (c)Arthur Ketels 2010 - 2011
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "ethercat.h"
#include "slaveinfo.h"
extern char IOmap[4096];
ec_ODlistt ODlist;
ec_OElistt OElist;
boolean printSDO = FALSE;
boolean printMAP = FALSE;
char usdo[128];
char hstr[1024];
char* dtype2string(uint16 dtype)
{
switch(dtype)
{
case ECT_BOOLEAN:
sprintf(hstr, "BOOLEAN");
break;
case ECT_INTEGER8:
sprintf(hstr, "INTEGER8");
break;
case ECT_INTEGER16:
sprintf(hstr, "INTEGER16");
break;
case ECT_INTEGER32:
sprintf(hstr, "INTEGER32");
break;
case ECT_INTEGER24:
sprintf(hstr, "INTEGER24");
break;
case ECT_INTEGER64:
sprintf(hstr, "INTEGER64");
break;
case ECT_UNSIGNED8:
sprintf(hstr, "UNSIGNED8");
break;
case ECT_UNSIGNED16:
sprintf(hstr, "UNSIGNED16");
break;
case ECT_UNSIGNED32:
sprintf(hstr, "UNSIGNED32");
break;
case ECT_UNSIGNED24:
sprintf(hstr, "UNSIGNED24");
break;
case ECT_UNSIGNED64:
sprintf(hstr, "UNSIGNED64");
break;
case ECT_REAL32:
sprintf(hstr, "REAL32");
break;
case ECT_REAL64:
sprintf(hstr, "REAL64");
break;
case ECT_BIT1:
sprintf(hstr, "BIT1");
break;
case ECT_BIT2:
sprintf(hstr, "BIT2");
break;
case ECT_BIT3:
sprintf(hstr, "BIT3");
break;
case ECT_BIT4:
sprintf(hstr, "BIT4");
break;
case ECT_BIT5:
sprintf(hstr, "BIT5");
break;
case ECT_BIT6:
sprintf(hstr, "BIT6");
break;
case ECT_BIT7:
sprintf(hstr, "BIT7");
break;
case ECT_BIT8:
sprintf(hstr, "BIT8");
break;
case ECT_VISIBLE_STRING:
sprintf(hstr, "VISIBLE_STRING");
break;
case ECT_OCTET_STRING:
sprintf(hstr, "OCTET_STRING");
break;
default:
sprintf(hstr, "Type 0x%4.4X", dtype);
}
return hstr;
}
char* SDO2string(uint16 slave, uint16 index, uint8 subidx, uint16 dtype)
{
int l = sizeof(usdo) - 1, i;
uint8 *u8;
int8 *i8;
uint16 *u16;
int16 *i16;
uint32 *u32;
int32 *i32;
uint64 *u64;
int64 *i64;
float *sr;
double *dr;
char es[32];
memset(&usdo, 0, 128);
ec_SDOread(slave, index, subidx, FALSE, &l, &usdo, EC_TIMEOUTRXM);
if (EcatError)
{
return ec_elist2string();
}
else
{
switch(dtype)
{
case ECT_BOOLEAN:
u8 = (uint8*) &usdo[0];
if (*u8) sprintf(hstr, "TRUE");
else sprintf(hstr, "FALSE");
break;
case ECT_INTEGER8:
i8 = (int8*) &usdo[0];
sprintf(hstr, "0x%2.2x %d", *i8, *i8);
break;
case ECT_INTEGER16:
i16 = (int16*) &usdo[0];
sprintf(hstr, "0x%4.4x %d", *i16, *i16);
break;
case ECT_INTEGER32:
case ECT_INTEGER24:
i32 = (int32*) &usdo[0];
sprintf(hstr, "0x%8.8x %d", *i32, *i32);
break;
case ECT_INTEGER64:
i64 = (int64*) &usdo[0];
sprintf(hstr, "0x%16.16"PRIx64" %"PRId64, *i64, *i64);
break;
case ECT_UNSIGNED8:
u8 = (uint8*) &usdo[0];
sprintf(hstr, "0x%2.2x %u", *u8, *u8);
break;
case ECT_UNSIGNED16:
u16 = (uint16*) &usdo[0];
sprintf(hstr, "0x%4.4x %u", *u16, *u16);
break;
case ECT_UNSIGNED32:
case ECT_UNSIGNED24:
u32 = (uint32*) &usdo[0];
sprintf(hstr, "0x%8.8x %u", *u32, *u32);
break;
case ECT_UNSIGNED64:
u64 = (uint64*) &usdo[0];
sprintf(hstr, "0x%16.16"PRIx64" %"PRIu64, *u64, *u64);
break;
case ECT_REAL32:
sr = (float*) &usdo[0];
sprintf(hstr, "%f", *sr);
break;
case ECT_REAL64:
dr = (double*) &usdo[0];
sprintf(hstr, "%f", *dr);
break;
case ECT_BIT1:
case ECT_BIT2:
case ECT_BIT3:
case ECT_BIT4:
case ECT_BIT5:
case ECT_BIT6:
case ECT_BIT7:
case ECT_BIT8:
u8 = (uint8*) &usdo[0];
sprintf(hstr, "0x%x", *u8);
break;
case ECT_VISIBLE_STRING:
strcpy(hstr, usdo);
break;
case ECT_OCTET_STRING:
hstr[0] = 0x00;
for (i = 0 ; i < l ; i++)
{
sprintf(es, "0x%2.2x ", usdo[i]);
strcat( hstr, es);
}
break;
default:
sprintf(hstr, "Unknown type");
}
return hstr;
}
}
/** Read PDO assign structure */
int si_PDOassign(uint16 slave, uint16 PDOassign, int mapoffset, int bitoffset)
{
uint16 idxloop, nidx, subidxloop, rdat, idx, subidx;
uint8 subcnt;
int wkc, bsize = 0, rdl;
int32 rdat2;
uint8 bitlen, obj_subidx;
uint16 obj_idx;
int abs_offset, abs_bit;
rdl = sizeof(rdat); rdat = 0;
/* read PDO assign subindex 0 ( = number of PDO's) */
wkc = ec_SDOread(slave, PDOassign, 0x00, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
rdat = etohs(rdat);
/* positive result from slave ? */
if ((wkc > 0) && (rdat > 0))
{
/* number of available sub indexes */
nidx = rdat;
bsize = 0;
/* read all PDO's */
for (idxloop = 1; idxloop <= nidx; idxloop++)
{
rdl = sizeof(rdat); rdat = 0;
/* read PDO assign */
wkc = ec_SDOread(slave, PDOassign, (uint8)idxloop, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
/* result is index of PDO */
idx = etohs(rdat);
if (idx > 0)
{
rdl = sizeof(subcnt); subcnt = 0;
/* read number of subindexes of PDO */
wkc = ec_SDOread(slave,idx, 0x00, FALSE, &rdl, &subcnt, EC_TIMEOUTRXM);
subidx = subcnt;
/* for each subindex */
for (subidxloop = 1; subidxloop <= subidx; subidxloop++)
{
rdl = sizeof(rdat2); rdat2 = 0;
/* read SDO that is mapped in PDO */
wkc = ec_SDOread(slave, idx, (uint8)subidxloop, FALSE, &rdl, &rdat2, EC_TIMEOUTRXM);
rdat2 = etohl(rdat2);
/* extract bitlength of SDO */
bitlen = LO_BYTE(rdat2);
bsize += bitlen;
obj_idx = (uint16)(rdat2 >> 16);
obj_subidx = (uint8)((rdat2 >> 8) & 0x000000ff);
abs_offset = mapoffset + (bitoffset / 8);
abs_bit = bitoffset % 8;
ODlist.Slave = slave;
ODlist.Index[0] = obj_idx;
OElist.Entries = 0;
wkc = 0;
/* read object entry from dictionary if not a filler (0x0000:0x00) */
if(obj_idx || obj_subidx)
wkc = ec_readOEsingle(0, obj_subidx, &ODlist, &OElist);
printf(" [0x%4.4X.%1d] 0x%4.4X:0x%2.2X 0x%2.2X", abs_offset, abs_bit, obj_idx, obj_subidx, bitlen);
if((wkc > 0) && OElist.Entries)
{
printf(" %-12s %s\n", dtype2string(OElist.DataType[obj_subidx]), OElist.Name[obj_subidx]);
}
else
printf("\n");
bitoffset += bitlen;
};
};
};
};
/* return total found bitlength (PDO) */
return bsize;
}
int si_map_sdo(int slave)
{
int wkc, rdl;
int retVal = 0;
uint8 nSM, iSM, tSM;
int Tsize, outputs_bo, inputs_bo;
uint8 SMt_bug_add;
printf("PDO mapping according to CoE :\n");
SMt_bug_add = 0;
outputs_bo = 0;
inputs_bo = 0;
rdl = sizeof(nSM); nSM = 0;
/* read SyncManager Communication Type object count */
wkc = ec_SDOread(slave, ECT_SDO_SMCOMMTYPE, 0x00, FALSE, &rdl, &nSM, EC_TIMEOUTRXM);
/* positive result from slave ? */
if ((wkc > 0) && (nSM > 2))
{
/* make nSM equal to number of defined SM */
nSM--;
/* limit to maximum number of SM defined, if true the slave can't be configured */
if (nSM > EC_MAXSM)
nSM = EC_MAXSM;
/* iterate for every SM type defined */
for (iSM = 2 ; iSM <= nSM ; iSM++)
{
rdl = sizeof(tSM); tSM = 0;
/* read SyncManager Communication Type */
wkc = ec_SDOread(slave, ECT_SDO_SMCOMMTYPE, iSM + 1, FALSE, &rdl, &tSM, EC_TIMEOUTRXM);
if (wkc > 0)
{
if((iSM == 2) && (tSM == 2)) // SM2 has type 2 == mailbox out, this is a bug in the slave!
{
SMt_bug_add = 1; // try to correct, this works if the types are 0 1 2 3 and should be 1 2 3 4
printf("Activated SM type workaround, possible incorrect mapping.\n");
}
if(tSM)
tSM += SMt_bug_add; // only add if SMt > 0
if (tSM == 3) // outputs
{
/* read the assign RXPDO */
printf(" SM%1d outputs\n addr b index: sub bitl data_type name\n", iSM);
Tsize = si_PDOassign(slave, ECT_SDO_PDOASSIGN + iSM, (int)(ec_slave[slave].outputs - (uint8 *)&IOmap[0]), outputs_bo );
outputs_bo += Tsize;
}
if (tSM == 4) // inputs
{
/* read the assign TXPDO */
printf(" SM%1d inputs\n addr b index: sub bitl data_type name\n", iSM);
Tsize = si_PDOassign(slave, ECT_SDO_PDOASSIGN + iSM, (int)(ec_slave[slave].inputs - (uint8 *)&IOmap[0]), inputs_bo );
inputs_bo += Tsize;
}
}
}
}
/* found some I/O bits ? */
if ((outputs_bo > 0) || (inputs_bo > 0))
retVal = 1;
return retVal;
}
int si_siiPDO(uint16 slave, uint8 t, int mapoffset, int bitoffset)
{
uint16 a , w, c, e, er, Size;
uint8 eectl;
uint16 obj_idx;
uint8 obj_subidx;
uint8 obj_name;
uint8 obj_datatype;
uint8 bitlen;
int totalsize;
ec_eepromPDOt eepPDO;
ec_eepromPDOt *PDO;
int abs_offset, abs_bit;
char str_name[EC_MAXNAME + 1];
eectl = ec_slave[slave].eep_pdi;
Size = 0;
totalsize = 0;
PDO = &eepPDO;
PDO->nPDO = 0;
PDO->Length = 0;
PDO->Index[1] = 0;
for (c = 0 ; c < EC_MAXSM ; c++) PDO->SMbitsize[c] = 0;
if (t > 1)
t = 1;
PDO->Startpos = ec_siifind(slave, ECT_SII_PDO + t);
if (PDO->Startpos > 0)
{
a = PDO->Startpos;
w = ec_siigetbyte(slave, a++);
w += (ec_siigetbyte(slave, a++) << 8);
PDO->Length = w;
c = 1;
/* traverse through all PDOs */
do
{
PDO->nPDO++;
PDO->Index[PDO->nPDO] = ec_siigetbyte(slave, a++);
PDO->Index[PDO->nPDO] += (ec_siigetbyte(slave, a++) << 8);
PDO->BitSize[PDO->nPDO] = 0;
c++;
/* number of entries in PDO */
e = ec_siigetbyte(slave, a++);
PDO->SyncM[PDO->nPDO] = ec_siigetbyte(slave, a++);
a++;
obj_name = ec_siigetbyte(slave, a++);
a += 2;
c += 2;
if (PDO->SyncM[PDO->nPDO] < EC_MAXSM) /* active and in range SM? */
{
str_name[0] = 0;
if(obj_name)
ec_siistring(str_name, slave, obj_name);
if (t)
printf(" SM%1d RXPDO 0x%4.4X %s\n", PDO->SyncM[PDO->nPDO], PDO->Index[PDO->nPDO], str_name);
else
printf(" SM%1d TXPDO 0x%4.4X %s\n", PDO->SyncM[PDO->nPDO], PDO->Index[PDO->nPDO], str_name);
printf(" addr b index: sub bitl data_type name\n");
/* read all entries defined in PDO */
for (er = 1; er <= e; er++)
{
c += 4;
obj_idx = ec_siigetbyte(slave, a++);
obj_idx += (ec_siigetbyte(slave, a++) << 8);
obj_subidx = ec_siigetbyte(slave, a++);
obj_name = ec_siigetbyte(slave, a++);
obj_datatype = ec_siigetbyte(slave, a++);
bitlen = ec_siigetbyte(slave, a++);
abs_offset = mapoffset + (bitoffset / 8);
abs_bit = bitoffset % 8;
PDO->BitSize[PDO->nPDO] += bitlen;
a += 2;
/* skip entry if filler (0x0000:0x00) */
if(obj_idx || obj_subidx)
{
str_name[0] = 0;
if(obj_name)
ec_siistring(str_name, slave, obj_name);
printf(" [0x%4.4X.%1d] 0x%4.4X:0x%2.2X 0x%2.2X", abs_offset, abs_bit, obj_idx, obj_subidx, bitlen);
printf(" %-12s %s\n", dtype2string(obj_datatype), str_name);
}
bitoffset += bitlen;
totalsize += bitlen;
}
PDO->SMbitsize[ PDO->SyncM[PDO->nPDO] ] += PDO->BitSize[PDO->nPDO];
Size += PDO->BitSize[PDO->nPDO];
c++;
}
else /* PDO deactivated because SM is 0xff or > EC_MAXSM */
{
c += 4 * e;
a += 8 * e;
c++;
}
if (PDO->nPDO >= (EC_MAXEEPDO - 1)) c = PDO->Length; /* limit number of PDO entries in buffer */
}
while (c < PDO->Length);
}
if (eectl) ec_eeprom2pdi(slave); /* if eeprom control was previously pdi then restore */
return totalsize;
}
int si_map_sii(int slave)
{
int retVal = 0;
int Tsize, outputs_bo, inputs_bo;
printf("PDO mapping according to SII :\n");
outputs_bo = 0;
inputs_bo = 0;
/* read the assign RXPDOs */
Tsize = si_siiPDO(slave, 1, (int)(ec_slave[slave].outputs - (uint8*)&IOmap), outputs_bo );
outputs_bo += Tsize;
/* read the assign TXPDOs */
Tsize = si_siiPDO(slave, 0, (int)(ec_slave[slave].inputs - (uint8*)&IOmap), inputs_bo );
inputs_bo += Tsize;
/* found some I/O bits ? */
if ((outputs_bo > 0) || (inputs_bo > 0))
retVal = 1;
return retVal;
}
void si_sdo(int cnt)
{
int i, j;
ODlist.Entries = 0;
memset(&ODlist, 0, sizeof(ODlist));
if( ec_readODlist(cnt, &ODlist))
{
printf(" CoE Object Description found, %d entries.\n",ODlist.Entries);
for( i = 0 ; i < ODlist.Entries ; i++)
{
ec_readODdescription(i, &ODlist);
while(EcatError) printf("%s", ec_elist2string());
printf(" Index: %4.4x Datatype: %4.4x Objectcode: %2.2x Name: %s\n",
ODlist.Index[i], ODlist.DataType[i], ODlist.ObjectCode[i], ODlist.Name[i]);
memset(&OElist, 0, sizeof(OElist));
ec_readOE(i, &ODlist, &OElist);
while(EcatError) printf("%s", ec_elist2string());
for( j = 0 ; j < ODlist.MaxSub[i]+1 ; j++)
{
if ((OElist.DataType[j] > 0) && (OElist.BitLength[j] > 0))
{
printf(" Sub: %2.2x Datatype: %4.4x Bitlength: %4.4x Obj.access: %4.4x Name: %s\n",
j, OElist.DataType[j], OElist.BitLength[j], OElist.ObjAccess[j], OElist.Name[j]);
if ((OElist.ObjAccess[j] & 0x0007))
{
printf(" Value :%s\n", SDO2string(cnt, ODlist.Index[i], j, OElist.DataType[j]));
}
}
}
}
}
else
{
while(EcatError) printf("%s", ec_elist2string());
}
}
void slaveinfo(char *ifname)
{
printSDO = TRUE;
printMAP = TRUE;
int cnt, i, j, nSM;
uint16 ssigen;
int expectedWKC;
printf("Starting slaveinfo\n");
/* initialise SOEM, bind socket to ifname */
if (ec_init(ifname))
{
printf("ec_init on %s succeeded.\n",ifname);
/* find and auto-config slaves */
if ( ec_config_init(FALSE) > 0 )
{
printf("%d slaves found and configured.\n",ec_slavecount);
ec_config_map(&IOmap);
ec_configdc();
while(EcatError) printf("%s", ec_elist2string());
printf("%d slaves found and configured.\n",ec_slavecount);
expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
printf("Calculated workcounter %d\n", expectedWKC);
/* wait for all slaves to reach SAFE_OP state */
ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 3);
if (ec_slave[0].state != EC_STATE_SAFE_OP )
{
printf("Not all slaves reached safe operational state.\n");
ec_readstate();
for(i = 1; i<=ec_slavecount ; i++)
{
if(ec_slave[i].state != EC_STATE_SAFE_OP)
{
printf("Slave %d State=%2x StatusCode=%4x : %s\n",
i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
}
}
}
ec_readstate();
for( cnt = 1 ; cnt <= ec_slavecount ; cnt++)
{
printf("\nSlave:%d\n Name:%s\n Output size: %dbits\n Input size: %dbits\n State: %d\n Delay: %d[ns]\n Has DC: %d\n",
cnt, ec_slave[cnt].name, ec_slave[cnt].Obits, ec_slave[cnt].Ibits,
ec_slave[cnt].state, ec_slave[cnt].pdelay, ec_slave[cnt].hasdc);
if (ec_slave[cnt].hasdc) printf(" DCParentport:%d\n", ec_slave[cnt].parentport);
printf(" Activeports:%d.%d.%d.%d\n", (ec_slave[cnt].activeports & 0x01) > 0 ,
(ec_slave[cnt].activeports & 0x02) > 0 ,
(ec_slave[cnt].activeports & 0x04) > 0 ,
(ec_slave[cnt].activeports & 0x08) > 0 );
printf(" Configured address: %4.4x\n", ec_slave[cnt].configadr);
printf(" Man: %8.8x ID: %8.8x Rev: %8.8x\n", (int)ec_slave[cnt].eep_man, (int)ec_slave[cnt].eep_id, (int)ec_slave[cnt].eep_rev);
for(nSM = 0 ; nSM < EC_MAXSM ; nSM++)
{
if(ec_slave[cnt].SM[nSM].StartAddr > 0)
printf(" SM%1d A:%4.4x L:%4d F:%8.8x Type:%d\n",nSM, etohs(ec_slave[cnt].SM[nSM].StartAddr), etohs(ec_slave[cnt].SM[nSM].SMlength),
etohl(ec_slave[cnt].SM[nSM].SMflags), ec_slave[cnt].SMtype[nSM]);
}
for(j = 0 ; j < ec_slave[cnt].FMMUunused ; j++)
{
printf(" FMMU%1d Ls:%8.8x Ll:%4d Lsb:%d Leb:%d Ps:%4.4x Psb:%d Ty:%2.2x Act:%2.2x\n", j,
etohl(ec_slave[cnt].FMMU[j].LogStart), etohs(ec_slave[cnt].FMMU[j].LogLength), ec_slave[cnt].FMMU[j].LogStartbit,
ec_slave[cnt].FMMU[j].LogEndbit, etohs(ec_slave[cnt].FMMU[j].PhysStart), ec_slave[cnt].FMMU[j].PhysStartBit,
ec_slave[cnt].FMMU[j].FMMUtype, ec_slave[cnt].FMMU[j].FMMUactive);
}
printf(" FMMUfunc 0:%d 1:%d 2:%d 3:%d\n",
ec_slave[cnt].FMMU0func, ec_slave[cnt].FMMU1func, ec_slave[cnt].FMMU2func, ec_slave[cnt].FMMU3func);
printf(" MBX length wr: %d rd: %d MBX protocols : %2.2x\n", ec_slave[cnt].mbx_l, ec_slave[cnt].mbx_rl, ec_slave[cnt].mbx_proto);
ssigen = ec_siifind(cnt, ECT_SII_GENERAL);
/* SII general section */
if (ssigen)
{
ec_slave[cnt].CoEdetails = ec_siigetbyte(cnt, ssigen + 0x07);
ec_slave[cnt].FoEdetails = ec_siigetbyte(cnt, ssigen + 0x08);
ec_slave[cnt].EoEdetails = ec_siigetbyte(cnt, ssigen + 0x09);
ec_slave[cnt].SoEdetails = ec_siigetbyte(cnt, ssigen + 0x0a);
if((ec_siigetbyte(cnt, ssigen + 0x0d) & 0x02) > 0)
{
ec_slave[cnt].blockLRW = 1;
ec_slave[0].blockLRW++;
}
ec_slave[cnt].Ebuscurrent = ec_siigetbyte(cnt, ssigen + 0x0e);
ec_slave[cnt].Ebuscurrent += ec_siigetbyte(cnt, ssigen + 0x0f) << 8;
ec_slave[0].Ebuscurrent += ec_slave[cnt].Ebuscurrent;
}
printf(" CoE details: %2.2x FoE details: %2.2x EoE details: %2.2x SoE details: %2.2x\n",
ec_slave[cnt].CoEdetails, ec_slave[cnt].FoEdetails, ec_slave[cnt].EoEdetails, ec_slave[cnt].SoEdetails);
printf(" Ebus current: %d[mA]\n only LRD/LWR:%d\n",
ec_slave[cnt].Ebuscurrent, ec_slave[cnt].blockLRW);
if ((ec_slave[cnt].mbx_proto & ECT_MBXPROT_COE) && printSDO)
si_sdo(cnt);
if(printMAP)
{
if (ec_slave[cnt].mbx_proto & ECT_MBXPROT_COE)
si_map_sdo(cnt);
else
si_map_sii(cnt);
}
}
}
else
{
printf("No slaves found!\n");
}
printf("End slaveinfo, close socket\n");
/* stop SOEM, close socket */
ec_close();
}
else
{
printf("No socket connection on %s\nExcecute as root\n",ifname);
}
}
simpletest.c
#include "simpletest.h"
#include "ethercat.h"
#include <stdio.h>
#define EC_TIMEOUTMON 500
extern char IOmap[4096];
boolean needlf;
boolean inOP;
int wkc;
int expectedWKC;
uint8 currentgroup = 0;
void simpletest(char *ifname)
{
int i, j, oloop, iloop, chk;
needlf = FALSE;
inOP = FALSE;
printf("Starting simple test\n");
/* initialise SOEM, bind socket to ifname */
if (ec_init(ifname))
{
printf("ec_init on %s succeeded.\n",ifname);
/* find and auto-config slaves */
if ( ec_config_init(FALSE) > 0 )
{
printf("%d slaves found and configured.\n",ec_slavecount);
ec_config_map(&IOmap);
ec_configdc();
printf("Slaves mapped, state to SAFE_OP.\n");
/* wait for all slaves to reach SAFE_OP state */
ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4);
oloop = ec_slave[0].Obytes;
if ((oloop == 0) && (ec_slave[0].Obits > 0)) oloop = 1;
if (oloop > 8) oloop = 8;
iloop = ec_slave[0].Ibytes;
if ((iloop == 0) && (ec_slave[0].Ibits > 0)) iloop = 1;
if (iloop > 8) iloop = 8;
printf("segments : %d : %d %d %d %d\n",ec_group[0].nsegments ,ec_group[0].IOsegment[0],ec_group[0].IOsegment[1],ec_group[0].IOsegment[2],ec_group[0].IOsegment[3]);
printf("Request operational state for all slaves\n");
expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
printf("Calculated workcounter %d\n", expectedWKC);
ec_slave[0].state = EC_STATE_OPERATIONAL;
/* send one valid process data to make outputs in slaves happy*/
ec_send_processdata();
ec_receive_processdata(EC_TIMEOUTRET);
/* request OP state for all slaves */
ec_writestate(0);
chk = 200;
/* wait for all slaves to reach OP state */
do
{
ec_send_processdata();
ec_receive_processdata(EC_TIMEOUTRET);
ec_statecheck(0, EC_STATE_OPERATIONAL, 50000);
}
while (chk-- && (ec_slave[0].state != EC_STATE_OPERATIONAL));
if (ec_slave[0].state == EC_STATE_OPERATIONAL )
{
printf("Operational state reached for all slaves.\n");
inOP = TRUE;
/* cyclic loop */
for(i = 1; i <= 1000; i++)
{
ec_send_processdata();
wkc = ec_receive_processdata(EC_TIMEOUTRET);
if(wkc >= expectedWKC)
{
printf("Processdata cycle %4d, WKC %d , O:", i, wkc);
for(j = 0 ; j < oloop; j++)
{
printf(" %2.2x", *(ec_slave[0].outputs + j));
}
printf(" I:");
for(j = 0 ; j < iloop; j++)
{
printf(" %2.2x", *(ec_slave[0].inputs + j));
}
printf(" T:%lld\n",ec_DCtime);
needlf = TRUE;
}
osal_usleep(5000);
}
inOP = FALSE;
}
else
{
printf("Not all slaves reached operational state.\n");
ec_readstate();
for(i = 1; i<=ec_slavecount ; i++)
{
if(ec_slave[i].state != EC_STATE_OPERATIONAL)
{
printf("Slave %d State=0x%2.2x StatusCode=0x%4.4x : %s\n",
i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
}
}
}
printf("\nRequest init state for all slaves\n");
ec_slave[0].state = EC_STATE_INIT;
/* request INIT state for all slaves */
ec_writestate(0);
}
else
{
printf("No slaves found!\n");
}
printf("End simple test, close socket\n");
}
else
{
printf("No socket connection on %s\nExcecute as root\n",ifname);
}
}
void ecatcheck(void)
{
int slave;
if( inOP && ((wkc < expectedWKC) || ec_group[currentgroup].docheckstate))
{
if (needlf)
{
needlf = FALSE;
printf("\n");
}
/* one ore more slaves are not responding */
ec_group[currentgroup].docheckstate = FALSE;
ec_readstate();
for (slave = 1; slave <= ec_slavecount; slave++)
{
if ((ec_slave[slave].group == currentgroup) && (ec_slave[slave].state != EC_STATE_OPERATIONAL))
{
ec_group[currentgroup].docheckstate = TRUE;
if (ec_slave[slave].state == (EC_STATE_SAFE_OP + EC_STATE_ERROR))
{
printf("ERROR : slave %d is in SAFE_OP + ERROR, attempting ack.\n", slave);
ec_slave[slave].state = (EC_STATE_SAFE_OP + EC_STATE_ACK);
ec_writestate(slave);
}
else if(ec_slave[slave].state == EC_STATE_SAFE_OP)
{
printf("WARNING : slave %d is in SAFE_OP, change to OPERATIONAL.\n", slave);
ec_slave[slave].state = EC_STATE_OPERATIONAL;
ec_writestate(slave);
}
else if(ec_slave[slave].state > EC_STATE_NONE)
{
if (ec_reconfig_slave(slave, EC_TIMEOUTMON))
{
ec_slave[slave].islost = FALSE;
printf("MESSAGE : slave %d reconfigured\n",slave);
}
}
else if(!ec_slave[slave].islost)
{
/* re-check state */
ec_statecheck(slave, EC_STATE_OPERATIONAL, EC_TIMEOUTRET);
if (ec_slave[slave].state == EC_STATE_NONE)
{
ec_slave[slave].islost = TRUE;
printf("ERROR : slave %d lost\n",slave);
}
}
}
if (ec_slave[slave].islost)
{
if(ec_slave[slave].state == EC_STATE_NONE)
{
if (ec_recover_slave(slave, EC_TIMEOUTMON))
{
ec_slave[slave].islost = FALSE;
printf("MESSAGE : slave %d recovered\n",slave);
}
}
else
{
ec_slave[slave].islost = FALSE;
printf("MESSAGE : slave %d found\n",slave);
}
}
}
if(!ec_group[currentgroup].docheckstate)
printf("OK : all slaves resumed OPERATIONAL.\n");
}
}