RT-Thread使用DP83640 实现IEEE1588 协议笔记

       本文记录RT-Thread OS 下,使用DP83640 Phy 芯片实现IEEE1588 协议的过程。

硬件

        为了测试软件,我们专门布了一块板,SOC 采用了STM32f429 实现。外接了DP83640 PHY 和GPS 模块。

要点:

  1. DP83640 外接50M 晶振。
  2. OUT_CLK 连接到STM3 的TIM1_ETR 作为外部时钟计数
  3. 接口使用RMII 接口

DP83640 与普通PHY 芯片的主要差别是几个GPIO 引脚

      上面的GPIO 信号中,GPIO12 (CLK_OUT )是一个比较重要的信号,相当于PPS 输出信号。以内部250MHz 为基础,可以分频为10M 输出。将它连接到TIM4 的TIM1 ETR(PF2) 输入。用作内部时钟的节拍。 

参考代码

DP83640 的驱动代码

IT 原始的代码: snlc036

我参考了这个项目的代码

Github 上的代码https://github.com/j123b567/ti-epl 是为STM32Fxx7 上移植的代码

PTP 协议栈

我参考了这个项目的代码

GitHub - hasseb/stm32h7_atsame70_ptpd: IEEE 1588 PTP daemon for STM32H7 and ATSAME70

软件实现要点

步骤

1 实现Ethernet 通信。

主要修改drv_eth中的phy_monitor_thread_entry 程序

     尽管我们将原来的LAN8742 改成了DP83640 但是由于PHY_ID1_REGPHY_BASIC_CONTROL_REG等寄存器的地址是相同的,所以不需要什么修改。

2 实现 PTP 协议

MDIO 的接口驱动

STM32F 的MAC 控制器支持MDIO接口。使用下面的函数实现:

HAL_StatusTypeDef HAL_ETH_ReadPHYRegister(ETH_HandleTypeDef *heth, uint16_t PHYReg, uint32_t *RegValue);

HAL_StatusTypeDef HAL_ETH_WritePHYRegister(ETH_HandleTypeDef *heth, uint16_t PHYReg, uint32_t RegValue);

DP83640 的地址

        在RT-Thread 的drv_eth.c中,Phy addrerss是自动扫描的,找到的地址为 0x19 (可能与上电Strap 有关)。

DP83640 底层驱动

DP83640 通过MDIO 接口访问,DP83640 的底层接口改为:

1 在 drv_eth 中添加了:

uint32_t ReadPHYRegister( uint16_t PHYReg){

    uint32_t data;

  HAL_ETH_ReadPHYRegister(&EthHandle,PHYReg, &data);

  return data;

}

 void WritePHYRegister( uint16_t PHYReg, uint32_t RegValue){

  HAL_ETH_WritePHYRegister(&EthHandle,PHYReg,RegValue);

 }

2 epl_core.c 中,替换了IntWriteRegEPLReadReg中的读写子程序。

LED 灯

        按照DP83640 的数据手册,LED应该为mode1 .但是绿灯却不闪烁。 在drv_eth.c 中添加了

HAL_ETH_WritePHYRegister(&EthHandle, PHY_PHYCTRL, 0x8019);

绿灯闪了, 这却是mode2!

DP83640 的初始化

Pdp_dep.c 中添加了

void PHY_PTPStart(void) {

    RX_CFG_ITEMS rx_cfg_items = {
        .ptpVersion = 0x02,
        .ptpFirstByteMask = 0x00,
        .ptpFirstByteData = 0x00,
        .ipAddrData = 0,
        .tsMinIFG = 0x00,
        .srcIdHash = 0,
        .ptpDomain = 0,
        .tsSecLen = 0, //0; // DRs option
        .rxTsNanoSecOffset = 0, //0x24; // DRs option

        .rxTsSecondsOffset = 0, //0x21; // DRs option

    };

    PTPEnable(pEPL_HANDLE, TRUE);
    PTPSetClockConfig(pEPL_HANDLE,0x0007,0x19,0,0);//OUT_CLK=10M 0x19
    PTPSetTransmitConfig(pEPL_HANDLE, TXOPT_TS_EN | TXOPT_IPV4_EN, 2, 0, 0);
    PTPSetReceiveConfig(pEPL_HANDLE, RXOPT_RX_TS_EN | RXOPT_RX_IPV4_EN, &rx_cfg_items);
};

PTPSetClockConfig 是我添加的,为了在OUT_CLK 输出 10M 时钟。

0x19=25,

250M/25=10MHz.

DP83640 读写的互斥保护

        如果多个线程访问DP83640 寄存器的话,可能引起出错。需要使用操作系统的互斥机制

Mutex 。这是与OS 有关的,需要在epl_oai.c 程序中修改。

允许组播

          在STM32f4xx_eth.c 中的ETH_MACDMAConfig程序中。改为:

macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;

这有点简单粗暴,如果改为过滤方式,网络通信更有效。

Sys_time.c 的修改

        时钟都在DP83640 芯片中,所以PTP 协议主要修改这部分程序:

void getTime(TimeInternal *time)
{
    NS_UINT32 seconds,nanoseconds;
//	ETH_PTPTime_GetTime(&timestamp);
	 PTPClockReadCurrent( pEPL_HANDLE, &seconds,  &nanoseconds);

 time->seconds = (int32_t)seconds;
  time->nanoseconds = (int32_t) nanoseconds;


}

void setTime(const TimeInternal *time)
{

	  PTPClockSet(pEPL_HANDLE, time->seconds, time->nanoseconds);
	DBG("resetting system clock to %d sec %d nsec\n", time->seconds, time->nanoseconds);
}

void updateTime(const TimeInternal *time)
{
	struct ptptime_t timeoffset;

	DBGV("updateTime: %d sec %d nsec\n", time->seconds, time->nanoseconds);

	timeoffset.tv_sec = -time->seconds;
	timeoffset.tv_nsec = -time->nanoseconds;

	/* Coarse update method */
	ETH_PTPTime_UpdateOffset(&timeoffset);
	DBGV("updateTime: updated\n");
}

uint32_t getRand(uint32_t randMax)
{
	return rand() % randMax;
}

bool  adjFreq(int32_t adj1)
{
    if (adj1!=0){
    int64_t adj=adj1-16;
	 if (adj > ADJ_FREQ_MAX)
		adj = ADJ_FREQ_MAX;
	else if (adj < -ADJ_FREQ_MAX)
		adj = -ADJ_FREQ_MAX;

   int32_t seconds=(int)(adj /0x1000000000);
   int32_t nanoseconds=(int)(adj %1000000000);
  bool negAdj = TRUE;
	        if ( adj > 0)
	            negAdj = FALSE;
 	PTPClockStepAdjustment (pEPL_HANDLE,abs(seconds),abs(nanoseconds),negAdj);

    }
	return TRUE;
}

drv_hwtimer.c 的修改

平心而论,RT-Thread 的dvr_timer 的实现是比较不完整的。需要自己修改。实现要点

1 使用TIM1 External1 模式

2 Prescale 为 10,分频为 1MHz。

static void timer_init(struct rt_hwtimer_device *timer, rt_uint32_t state)
{
    TIM_HandleTypeDef *tim = RT_NULL;
    TIM_SlaveConfigTypeDef sSlaveConfig = {0};
    struct stm32_hwtimer *tim_device = RT_NULL;

    RT_ASSERT(timer != RT_NULL);
    if (state)
    {
        tim = (TIM_HandleTypeDef *)timer->parent.user_data;
        tim_device = (struct stm32_hwtimer *)timer;

        tim->Init.Period            = 10000-1;
        tim->Init.Prescaler         =9;//div 10 1MHz
        tim->Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
        tim->Init.CounterMode   = TIM_COUNTERMODE_DOWN;
        tim->Init.RepetitionCounter = 0;
        tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
        if (HAL_TIM_Base_Init(tim) != HAL_OK)
        {
            LOG_E("%s init failed", tim_device->name);
            return;
        }
        sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
         sSlaveConfig.InputTrigger = TIM_TS_ETRF;
         sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_NONINVERTED;
         sSlaveConfig.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1;
         sSlaveConfig.TriggerFilter = 0;
         if (HAL_TIM_SlaveConfigSynchro(tim, &sSlaveConfig) != HAL_OK)
         {
           Error_Handler();
         }
            /* set the TIMx priority */
          HAL_NVIC_SetPriority(tim_device->tim_irqn, 3, 0);
            /* enable the TIMx global Interrupt */
            HAL_NVIC_EnableIRQ(tim_device->tim_irqn);
            /* clear update flag */
           __HAL_TIM_CLEAR_FLAG(tim, TIM_FLAG_UPDATE);
            /* enable update request source */
            __HAL_TIM_URS_ENABLE(tim);
            LOG_D("%s init success", tim_device->name);

    }
}

结果

目前代码已经调试出来了。从ppm 看,好像还有点问题。周期性地(每隔50秒左右)会突跳到100ppm 。不知道什么原因,也可能是MASTER 的问题。下面要进一步调试。

 将Master PTP 的SYNC INTERVAL 改小,有明显改善。

   #define DEFAULT_SYNC_INTERVAL           -7/* -7 in 802.1AS */

 

MASTER 使用了STM32F429 MAC 1588 的程序。下一步要使用DP83640 的版本也测试一下。

版权声明:本文为CSDN博主「姚家湾」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yaojiawan/article/details/122434783

       本文记录RT-Thread OS 下,使用DP83640 Phy 芯片实现IEEE1588 协议的过程。

硬件

        为了测试软件,我们专门布了一块板,SOC 采用了STM32f429 实现。外接了DP83640 PHY 和GPS 模块。

要点:

  1. DP83640 外接50M 晶振。
  2. OUT_CLK 连接到STM3 的TIM1_ETR 作为外部时钟计数
  3. 接口使用RMII 接口

DP83640 与普通PHY 芯片的主要差别是几个GPIO 引脚

      上面的GPIO 信号中,GPIO12 (CLK_OUT )是一个比较重要的信号,相当于PPS 输出信号。以内部250MHz 为基础,可以分频为10M 输出。将它连接到TIM4 的TIM1 ETR(PF2) 输入。用作内部时钟的节拍。 

参考代码

DP83640 的驱动代码

IT 原始的代码: snlc036

我参考了这个项目的代码

Github 上的代码https://github.com/j123b567/ti-epl 是为STM32Fxx7 上移植的代码

PTP 协议栈

我参考了这个项目的代码

GitHub - hasseb/stm32h7_atsame70_ptpd: IEEE 1588 PTP daemon for STM32H7 and ATSAME70

软件实现要点

步骤

1 实现Ethernet 通信。

主要修改drv_eth中的phy_monitor_thread_entry 程序

     尽管我们将原来的LAN8742 改成了DP83640 但是由于PHY_ID1_REGPHY_BASIC_CONTROL_REG等寄存器的地址是相同的,所以不需要什么修改。

2 实现 PTP 协议

MDIO 的接口驱动

STM32F 的MAC 控制器支持MDIO接口。使用下面的函数实现:

HAL_StatusTypeDef HAL_ETH_ReadPHYRegister(ETH_HandleTypeDef *heth, uint16_t PHYReg, uint32_t *RegValue);

HAL_StatusTypeDef HAL_ETH_WritePHYRegister(ETH_HandleTypeDef *heth, uint16_t PHYReg, uint32_t RegValue);

DP83640 的地址

        在RT-Thread 的drv_eth.c中,Phy addrerss是自动扫描的,找到的地址为 0x19 (可能与上电Strap 有关)。

DP83640 底层驱动

DP83640 通过MDIO 接口访问,DP83640 的底层接口改为:

1 在 drv_eth 中添加了:

uint32_t ReadPHYRegister( uint16_t PHYReg){

    uint32_t data;

  HAL_ETH_ReadPHYRegister(&EthHandle,PHYReg, &data);

  return data;

}

 void WritePHYRegister( uint16_t PHYReg, uint32_t RegValue){

  HAL_ETH_WritePHYRegister(&EthHandle,PHYReg,RegValue);

 }

2 epl_core.c 中,替换了IntWriteRegEPLReadReg中的读写子程序。

LED 灯

        按照DP83640 的数据手册,LED应该为mode1 .但是绿灯却不闪烁。 在drv_eth.c 中添加了

HAL_ETH_WritePHYRegister(&EthHandle, PHY_PHYCTRL, 0x8019);

绿灯闪了, 这却是mode2!

DP83640 的初始化

Pdp_dep.c 中添加了

void PHY_PTPStart(void) {

    RX_CFG_ITEMS rx_cfg_items = {
        .ptpVersion = 0x02,
        .ptpFirstByteMask = 0x00,
        .ptpFirstByteData = 0x00,
        .ipAddrData = 0,
        .tsMinIFG = 0x00,
        .srcIdHash = 0,
        .ptpDomain = 0,
        .tsSecLen = 0, //0; // DRs option
        .rxTsNanoSecOffset = 0, //0x24; // DRs option

        .rxTsSecondsOffset = 0, //0x21; // DRs option

    };

    PTPEnable(pEPL_HANDLE, TRUE);
    PTPSetClockConfig(pEPL_HANDLE,0x0007,0x19,0,0);//OUT_CLK=10M 0x19
    PTPSetTransmitConfig(pEPL_HANDLE, TXOPT_TS_EN | TXOPT_IPV4_EN, 2, 0, 0);
    PTPSetReceiveConfig(pEPL_HANDLE, RXOPT_RX_TS_EN | RXOPT_RX_IPV4_EN, &rx_cfg_items);
};

PTPSetClockConfig 是我添加的,为了在OUT_CLK 输出 10M 时钟。

0x19=25,

250M/25=10MHz.

DP83640 读写的互斥保护

        如果多个线程访问DP83640 寄存器的话,可能引起出错。需要使用操作系统的互斥机制

Mutex 。这是与OS 有关的,需要在epl_oai.c 程序中修改。

允许组播

          在STM32f4xx_eth.c 中的ETH_MACDMAConfig程序中。改为:

macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;

这有点简单粗暴,如果改为过滤方式,网络通信更有效。

Sys_time.c 的修改

        时钟都在DP83640 芯片中,所以PTP 协议主要修改这部分程序:

void getTime(TimeInternal *time)
{
    NS_UINT32 seconds,nanoseconds;
//	ETH_PTPTime_GetTime(&timestamp);
	 PTPClockReadCurrent( pEPL_HANDLE, &seconds,  &nanoseconds);

 time->seconds = (int32_t)seconds;
  time->nanoseconds = (int32_t) nanoseconds;


}

void setTime(const TimeInternal *time)
{

	  PTPClockSet(pEPL_HANDLE, time->seconds, time->nanoseconds);
	DBG("resetting system clock to %d sec %d nsec\n", time->seconds, time->nanoseconds);
}

void updateTime(const TimeInternal *time)
{
	struct ptptime_t timeoffset;

	DBGV("updateTime: %d sec %d nsec\n", time->seconds, time->nanoseconds);

	timeoffset.tv_sec = -time->seconds;
	timeoffset.tv_nsec = -time->nanoseconds;

	/* Coarse update method */
	ETH_PTPTime_UpdateOffset(&timeoffset);
	DBGV("updateTime: updated\n");
}

uint32_t getRand(uint32_t randMax)
{
	return rand() % randMax;
}

bool  adjFreq(int32_t adj1)
{
    if (adj1!=0){
    int64_t adj=adj1-16;
	 if (adj > ADJ_FREQ_MAX)
		adj = ADJ_FREQ_MAX;
	else if (adj < -ADJ_FREQ_MAX)
		adj = -ADJ_FREQ_MAX;

   int32_t seconds=(int)(adj /0x1000000000);
   int32_t nanoseconds=(int)(adj %1000000000);
  bool negAdj = TRUE;
	        if ( adj > 0)
	            negAdj = FALSE;
 	PTPClockStepAdjustment (pEPL_HANDLE,abs(seconds),abs(nanoseconds),negAdj);

    }
	return TRUE;
}

drv_hwtimer.c 的修改

平心而论,RT-Thread 的dvr_timer 的实现是比较不完整的。需要自己修改。实现要点

1 使用TIM1 External1 模式

2 Prescale 为 10,分频为 1MHz。

static void timer_init(struct rt_hwtimer_device *timer, rt_uint32_t state)
{
    TIM_HandleTypeDef *tim = RT_NULL;
    TIM_SlaveConfigTypeDef sSlaveConfig = {0};
    struct stm32_hwtimer *tim_device = RT_NULL;

    RT_ASSERT(timer != RT_NULL);
    if (state)
    {
        tim = (TIM_HandleTypeDef *)timer->parent.user_data;
        tim_device = (struct stm32_hwtimer *)timer;

        tim->Init.Period            = 10000-1;
        tim->Init.Prescaler         =9;//div 10 1MHz
        tim->Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
        tim->Init.CounterMode   = TIM_COUNTERMODE_DOWN;
        tim->Init.RepetitionCounter = 0;
        tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
        if (HAL_TIM_Base_Init(tim) != HAL_OK)
        {
            LOG_E("%s init failed", tim_device->name);
            return;
        }
        sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
         sSlaveConfig.InputTrigger = TIM_TS_ETRF;
         sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_NONINVERTED;
         sSlaveConfig.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1;
         sSlaveConfig.TriggerFilter = 0;
         if (HAL_TIM_SlaveConfigSynchro(tim, &sSlaveConfig) != HAL_OK)
         {
           Error_Handler();
         }
            /* set the TIMx priority */
          HAL_NVIC_SetPriority(tim_device->tim_irqn, 3, 0);
            /* enable the TIMx global Interrupt */
            HAL_NVIC_EnableIRQ(tim_device->tim_irqn);
            /* clear update flag */
           __HAL_TIM_CLEAR_FLAG(tim, TIM_FLAG_UPDATE);
            /* enable update request source */
            __HAL_TIM_URS_ENABLE(tim);
            LOG_D("%s init success", tim_device->name);

    }
}

结果

目前代码已经调试出来了。从ppm 看,好像还有点问题。周期性地(每隔50秒左右)会突跳到100ppm 。不知道什么原因,也可能是MASTER 的问题。下面要进一步调试。

 将Master PTP 的SYNC INTERVAL 改小,有明显改善。

   #define DEFAULT_SYNC_INTERVAL           -7/* -7 in 802.1AS */

 

MASTER 使用了STM32F429 MAC 1588 的程序。下一步要使用DP83640 的版本也测试一下。

版权声明:本文为CSDN博主「姚家湾」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yaojiawan/article/details/122434783

生成海报
点赞 0

姚家湾

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐