stm32实现1588协议

1.引言

本文章基于stm32官方例程实现,详细代码可下载例程。

在RT-thread系统上实现1588协议,网络驱动上需要做较多的修改。

2.代码修改

2.1驱动分析

当Accumulator register,溢出之后,会增加subsecond register一个步长,这个步长为constant value;每个时钟周期Accumulator register会加上addend register的值,这样做的目的是调整subsecond register 增加的周期。

为Accumulator register提供clock的位系统时钟 HCLK,一般为72MHz,而subsecond register 增加的频率需要为50MHz。

subsecond register的最大值为2^31,把2^31作为1秒钟,那么每20nssubsecond register需要增加 20ns * (2^31) / 10^9ns = 43 ,即constant value 为43.。

Accumulator register 溢出时间为 49 *10^9 / (2^31) ~= 20.023ns

Addend / 2^32 = (1 / 20ns) /72mhz 换算单位之后Addend =2979125334.90

2.2驱动层的修改

原代码的DMA描述符如下:

static ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];//STM32的MAC加入时间戳时,会将描述符的地址覆盖,//所以需要将地址提前保存起来,MAC的时间戳读出来之后,再将地址写回去。 //再创建两个描述符用于保存地址 ETH_DMADESCTypeDef DMAPTPRxDscrTab[ETH_RXBUFNB], DMAPTPTxDscrTab[ETH_TXBUFNB];/* Ethernet Rx & Tx PTP Helper Descriptors */

2.3发送函数的更改rt_err_t rt_stm32_eth_tx( rt_device_t dev, struct pbuf* p){struct pbuf* q;rt_uint32_t offset = 0;unsigned char * buffer;/* get free tx buffer */{rt_err_t result;result = rt_sem_take(&tx_buf_free, 2);if (result != RT_EOK) return -RT_ERROR;}#if LWIP_PTPunsigned int timeout = 0;struct ptptime_t timestamp;DMATxDescToSet->Buffer1Addr = DMAPTPTxDescToSet->Buffer1Addr; //保存地址DMATxDescToSet->Buffer2NextDescAddr = DMAPTPTxDescToSet->Buffer2NextDescAddr;#endifbuffer = (unsigned char *)DMATxDescToSet->Buffer1Addr;for (q = p; q != NULL; q = q->next){rt_uint8_t* ptr;rt_uint32_t len;len = q->len;ptr = q->payload;/**Copy the frame to be sent into memory pointed by the current ETHERNET DMA Tx descriptor*/memcpy((void *)(&buffer[offset] ), ptr, len);offset += len;}#ifdef ETH_TX_DUMP…….#endif/* Setting the Frame Length: bits[12:0] */DMATxDescToSet->ControlBufferSize = (p->tot_len & ETH_DMATxDesc_TBS1);/* Setting the last segment and first segment bits (in this case a frame is transmitted in one descriptor) */DMATxDescToSet->Status |= ETH_DMATxDesc_LS | ETH_DMATxDesc_FS;/* Enable TX Completion Interrupt */DMATxDescToSet->Status |= ETH_DMATxDesc_IC;#ifdef CHECKSUM_BY_HARDWARE……..#endif/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */DMATxDescToSet->Status |= ETH_DMATxDesc_OWN;/* When Tx Buffer unavailable flag is set: clear it and resume transmission */if ((ETH->DMASR & ETH_DMASR_TBUS) != (uint32_t)RESET){/* Clear TBUS ETHERNET DMA flag */ETH->DMASR = ETH_DMASR_TBUS;/* Transmit Poll Demand to resume DMA transmission*/ETH->DMATPDR = 0;}#if LWIP_PTP/* Wait for ETH_DMATxDesc_TTSS flag to be set */do{timeout++;} while (!(DMATxDescToSet->Status & ETH_DMATxDesc_TTSS) && (timeout < PHY_READ_TO)); //等待加入时间戳/* Return ERROR in case of timeout */if(timeout == PHY_READ_TO){return ETH_ERROR;}timestamp.tv_nsec = ETH_PTPSubSecond2NanoSecond(DMATxDescToSet->Buffer1Addr);timestamp.tv_sec = DMATxDescToSet->Buffer2NextDescAddr;/* Clear the DMATxDescToSet status register TTSS flag */DMATxDescToSet->Status &= ~ETH_DMATxDesc_TTSS;DMATxDescToSet->Buffer1Addr = DMAPTPTxDescToSet->Buffer1Addr;DMATxDescToSet->Buffer2NextDescAddr = DMAPTPTxDescToSet->Buffer2NextDescAddr; //还原地址/* Update the ETHERNET DMA global Tx descriptor with next Tx decriptor *//* Chained Mode *//* Selects the next DMA Tx descriptor list for next buffer to send */DMATxDescToSet = (ETH_DMADESCTypeDef*) (DMATxDescToSet->Buffer2NextDescAddr);if(DMAPTPTxDescToSet->Status != 0){DMAPTPTxDescToSet = (ETH_DMADESCTypeDef*) (DMAPTPTxDescToSet->Status);}else{DMAPTPTxDescToSet++;}#else/* Update the ETHERNET DMA global Tx descriptor with next Tx decriptor *//* Chained Mode *//* Selects the next DMA Tx descriptor list for next buffer to send */DMATxDescToSet = (ETH_DMADESCTypeDef*) (DMATxDescToSet->Buffer2NextDescAddr);#endif#if LWIP_PTPp->time_sec = timestamp.tv_sec;p->time_nsec = timestamp.tv_nsec;#endif/* Return SUCCESS */return RT_EOK;}2.4其他

接收函数也做类似的更改,还有中断函数直接参照例程修改就可以。

3.协议分析

1588协议中,定义了两种报文,事件报文和通用报文;

事件报文时间概念报文,进出设备端口时打上精确的时间戳,PTP根据事件报文携带的时间戳,计算链路延迟。事件报文包含以下4种:Sync、Delay_Req、Pdelay_Req和Pdelay_Resp。

爱情使人忘记时间,时间也使人忘记爱情。

stm32实现1588协议

相关文章:

你感兴趣的文章:

标签云: