Ethercat学习-从站源码移植

article/2025/7/4 7:46:52

文章目录

      • 简介
        • 移植源码
        • 1.源码结构
        • 2.GD32硬件接口准备
          • 1.SPI接口
          • 2.PDI中断配置
          • 3.Sync0中断配置
          • 4.Sync1中断配置
          • 5.定时器中断配置
        • 3.移植准备
        • 4.源码移植
          • 1.修改头文件名
          • 2.ecatport.c文件修改
            • 1.SPI部分修改
            • 2.中断部分
            • 3.修改HW_Init()
            • 4.报错修改
          • 3.myapp.c文件修改
        • 5.其他

简介

移植平台GD32F450,从站芯片AX58100,从站源码版本V5.12

移植源码

1.源码结构

源码使用之前SSC生成的源码,如下图所示

在这里插入图片描述

因为配置SSC的时候只选择了COE的功能,所以源码比较少。移植过程中重点关注红笔圈出的几个文件。其中ecat_def.h就是SSC中的配置项。el9800.c和el9800.h文件是根据EL9800的硬件生成的硬件接口文档,我们需要将它修改为GD32的接口。myapp.c、myapp.h、myappObjects.h是根据我之前定义的Excel文件生成的。

2.GD32硬件接口准备

1.SPI接口

ESI文件的ConfigData中,PDI的配置选择的SPI,另外SPI的极性也在PDI的配置中。下面是部分代码

/* SPI3初始化 */
void bsp_spi3_init(void)
{spi_parameter_struct spi_init_struct;rcu_periph_clock_enable(SPI3_CS_GPIO_CLK);rcu_periph_clock_enable(SPI3_MISO_GPIO_CLK);rcu_periph_clock_enable(SPI3_MOSI_GPIO_CLK);rcu_periph_clock_enable(SPI3_SCK_GPIO_CLK);rcu_periph_clock_enable(RCU_SPI3);/* MISO 引脚配置*/gpio_mode_set(SPI3_MISO_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, SPI3_MISO_GPIO);gpio_output_options_set(SPI3_MISO_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, SPI3_MISO_GPIO);gpio_af_set(SPI3_MISO_GPIO_PORT, GPIO_AF_5, SPI3_MISO_GPIO);/* MOSI 引脚配置 */gpio_mode_set(SPI3_MOSI_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, SPI3_MOSI_GPIO);gpio_output_options_set(SPI3_MOSI_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, SPI3_MOSI_GPIO);gpio_af_set(SPI3_MOSI_GPIO_PORT, GPIO_AF_5, SPI3_MOSI_GPIO);/* SCK 引脚配置*/gpio_mode_set(SPI3_SCK_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, SPI3_SCK_GPIO);gpio_output_options_set(SPI3_SCK_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, SPI3_SCK_GPIO);gpio_af_set(SPI3_SCK_GPIO_PORT, GPIO_AF_5, SPI3_SCK_GPIO);/* CS 引脚配置 */gpio_mode_set(SPI3_CS_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SPI3_CS_GPIO);gpio_output_options_set(SPI3_CS_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SPI3_CS_GPIO);gpio_output_options_set(SPI3_CS_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SPI3_CS_GPIO);gpio_bit_set(SPI3_CS_GPIO_PORT,SPI3_CS_GPIO);spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;       /* 全双工*/spi_init_struct.device_mode          = SPI_MASTER;                     /* 主机模式*/spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;             /* 8位帧格式*/spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;         /* 时钟相位极性,空闲时位低电平,第一个时钟边沿进行采样 */spi_init_struct.nss                  = SPI_NSS_SOFT;                   /* 软件NSS */ spi_init_struct.prescale             = SPI_PSC_32;                     /* 时钟32分频 */spi_init_struct.endian               = SPI_ENDIAN_MSB;                 /* 高位在前 */spi_init(SPI3, &spi_init_struct);/* 使能SPI */spi_enable(SPI3);
}
/* SPI的单字节收发 */
u8 spi_data_rw(u8 data)
{u16 tmp;/* 等待发送缓冲区清空 */while(RESET == spi_i2s_flag_get(Ethercat_SPI,SPI_FLAG_TBE));/* 发送要写的寄存器地址 */spi_i2s_data_transmit(Ethercat_SPI,data);/* 等待接收完成 SPI收发一体的,必须等到接收完成才代表一次完整的发送完成*/while(RESET == spi_i2s_flag_get(Ethercat_SPI,SPI_FLAG_RBNE));/* 读取缓存取得值,清空缓存区,准备发送 */tmp = spi_i2s_data_receive(Ethercat_SPI);   return (u8)tmp;
}
2.PDI中断配置

PDI中断是一个外部引脚中断

void ethercat_pdi_init(void)
{rcu_periph_clock_enable(RCU_SYSCFG);rcu_periph_clock_enable(RCU_GPIOE);gpio_mode_set(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE,GPIO_PIN_3);/* 外部中断配置 */syscfg_exti_line_config(EXTI_SOURCE_GPIOE, EXTI_SOURCE_PIN3);exti_init(EXTI_3, EXTI_INTERRUPT, EXTI_TRIG_FALLING);exti_interrupt_flag_clear(EXTI_3); 
}
3.Sync0中断配置

Sync0中断是一个外部引脚中断

void ethercat_sync0_init(void)
{rcu_periph_clock_enable(RCU_SYSCFG);rcu_periph_clock_enable(RCU_GPIOC);gpio_mode_set(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE,GPIO_PIN_13);/* 外部中断配置 */syscfg_exti_line_config(EXTI_SOURCE_GPIOC, EXTI_SOURCE_PIN13);exti_init(EXTI_13, EXTI_INTERRUPT, EXTI_TRIG_FALLING);exti_interrupt_flag_clear(EXTI_13); 
}
4.Sync1中断配置

Sync1中断是一个外部引脚中断

void ethercat_sync1_init(void)
{rcu_periph_clock_enable(RCU_SYSCFG);rcu_periph_clock_enable(RCU_GPIOF);gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE,GPIO_PIN_1);/* 外部中断配置 */syscfg_exti_line_config(EXTI_SOURCE_GPIOF, EXTI_SOURCE_PIN1);exti_init(EXTI_1, EXTI_INTERRUPT, EXTI_TRIG_FALLING);exti_interrupt_flag_clear(EXTI_1); 
}
5.定时器中断配置

因为我们在配置SSC的时候选择了ECAT_TIMER_INT选项,所以需要配置一个1ms的定时器中断,用来喂看门狗

void bsp_time4_init(void)
{timer_parameter_struct timer_initpara;rcu_periph_clock_enable(RCU_TIMER4);rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4); //四倍频,定时器1的时钟来自 APB1 = AHB/4 AHB=SYS,所以定时器的时钟就是200Mtimer_deinit(TIMER4);/* TIMER1 配置 */timer_initpara.prescaler         = TIM4_PSC;             /* 预分频值,计数时钟=定时器时钟/(PSC+1) */timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;   /* 无对齐模式*/timer_initpara.counterdirection  = TIMER_COUNTER_UP;     /* 向上计数 */timer_initpara.period            = TIM4_CAR;             /* 自动重装值 */timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;     /* 时钟分频,规定定时器时钟与死区时间和数字滤波采样时钟之间的系数*/timer_initpara.repetitioncounter = 0;                    /* 中断的产生频率,计数器溢出N+1次后产生中断*/timer_init(TIMER4,&timer_initpara); timer_interrupt_enable(TIMER4,TIMER_INT_UP);   timer_enable(TIMER4); 						 
}

3.移植准备

在GD32的工程下面新建一个Ethercat文件夹,在Ethercat文件夹下面新建一个src文件夹和inc文件夹。将源码中的头文件(.h文件)都复制到inc文件夹下面,将源码中的源文件(.c文件)都复制到src文件夹下面。

在这里插入图片描述

在这里插入图片描述

修改文件名字(个人喜好)将el9800hw.c和el9800.h改为ecatport.c和ecatport.h ,将源码加入到Keil的工程中。

4.源码移植

1.修改头文件名

因为我将源码的文件名字从el9800.h改为了ecatport.h,所以我需要将源码中调用el9800.h都改为ecatport.h,好像就两处。

2.ecatport.c文件修改
1.SPI部分修改

这里面的读写函数与SPI相关的有三个,RxTxSpiData、SELECT_SPI、DESELECT_SPI。

RxTxSpiData是SPI单字节读写,原先的RxTxSpiData函数是基于PIC单片机硬件的,我们将原先的RxTxSpiData函数删除掉,通过宏定义替换为我们自己定义的。

SELECT_SPI和DESELECT_SPI是对SPI CS引脚进行控制,我们也通过宏定义实现。

/*-----------------------------------------------------------------------------------------
------
------    SPI defines/macros
------
-----------------------------------------------------------------------------------------*/
#define RxTxSpiData         spi_data_rw
#define SELECT_SPI          gpio_bit_reset(GPIOE, GPIO_PIN_4);  
#define DESELECT_SPI        gpio_bit_set(GPIOE, GPIO_PIN_4);
2.中断部分

首先修改中断函数的写法,并增加一个定时器中断函数,修改如下

/* 修改前 __attribute__ ((__interrupt__, no_auto_psv))是PIC单片机的中断的写法,在GD32中不需要 */
void __attribute__ ((__interrupt__, no_auto_psv)) EscIsr(void)
{PDI_Isr();/* reset the interrupt flag */ACK_ESC_INT;}void __attribute__((__interrupt__, no_auto_psv)) Sync0Isr(void)
{Sync0_Isr();/* reset the interrupt flag */ACK_SYNC0_INT;
}void __attribute__((__interrupt__, no_auto_psv)) Sync1Isr(void)
{Sync1_Isr();/* reset the interrupt flag */ACK_SYNC1_INT;
}/*******************************修改后***********************************************/
void EscIsr(void)
{PDI_Isr();/* reset the interrupt flag */ACK_ESC_INT;}void Sync0Isr(void)
{Sync0_Isr();/* reset the interrupt flag */ACK_SYNC0_INT;
}void Sync1Isr(void)
{Sync1_Isr();/* reset the interrupt flag */ACK_SYNC1_INT;
}void ethercat_timer_isr(void)
{ECAT_CheckTimer();ACK_TIMER_INT;
}

将函数通过宏定义映射一下

/*PDI 映射 */
#define ACK_ESC_INT         exti_interrupt_flag_clear(EXTI_3);
#define EscIsr              EXTI3_IRQHandler
/*Sync0 映射 */
#define ACK_SYNC0_INT      exti_interrupt_flag_clear(EXTI_13)
#define Sync0Isr           EXTI10_15_IRQHandler
/*Sync1 映射 */
#define ACK_SYNC1_INT      exti_interrupt_flag_clear(EXTI_1)
#define Sync1Isr           EXTI1_IRQHandler
/*定时器 映射 */
#define ethercat_timer_isr TIMER4_IRQHandler
#define ACK_TIMER_INT      timer_interrupt_flag_clear(TIMER4, TIMER_INT_UP)

除了这几个中断,可以看到程序里还用到了全局中断的使能与失能。也需要宏定义一下

#define DISABLE_GLOBAL_INT      __disable_irq()			
#define ENABLE_GLOBAL_INT       __enable_irq()
3.修改HW_Init()
UINT8 HW_Init(void)
{UINT32 intMask;sysTick_init();nvic_configuration();	ethercat_gpio_init();bsp_spi3_init();do{intMask = 0x93;HW_EscWriteDWord(intMask, ESC_AL_EVENTMASK_OFFSET);intMask = 0;HW_EscReadDWord(intMask, ESC_AL_EVENTMASK_OFFSET);} while (intMask != 0x93);intMask = 0x00;HW_EscWriteDWord(intMask, ESC_AL_EVENTMASK_OFFSET);INIT_ESC_INT;INIT_SYNC0_INT;INIT_SYNC1_INT;INIT_ECAT_TIMER;/* enable all interrupts */ENABLE_GLOBAL_INT;return 0;
}

HW_Init初始化中的有些宏定义我没有去实现,直接删去了。因为外设在初始化的时候已经使能,不需要额外进行使能了。我只是先了如下几个宏定义

#define INIT_ESC_INT       ethercat_pdi_init()
#define INIT_SYNC0_INT     ethercat_sync0_init()
#define INIT_SYNC1_INT     ethercat_sync1_init()
#define INIT_ECAT_TIMER    ethercat_timer_init()

暂时不管应用层逻辑,先编译一下,看看硬件部分修改是否还有问题。

4.报错修改

提示没有 Nop ,我直接把Nop删除掉了。

提示没有定义DISABLE_ESC_INT和ENABLE_ESC_INT,因为这定义要在多个文件中引用,所以定义到ecatport.h文件中

#define DISABLE_ESC_INT()     exti_interrupt_disable(EXTI_3)
#define ENABLE_ESC_INT()      exti_interrupt_enable(EXTI_3)
3.myapp.c文件修改

这个文件主要实现我们的应用逻辑。需要修改的函数有三个

void APPL_InputMapping(UINT16* pData);
void APPL_OutputMapping(UINT16* pData);
void APPL_Application(void);

函数APPL_InputMapping是将TPDO数据从单片机中拷贝到ESC中;函数APPL_OutputMapping是将ESC中的RPDO数据拷贝到单片机的内存中。APPL_Application是本地应用逻辑,处理APPL_OutputMapping中得到的数据,打包APPL_InputMapping中需要的数据。

修改如下:

/************myapp.h ************/
typedef struct{unsigned int  data1;unsigned int  data2;
} __attribute__((__packed__)) _tpdo1A00_t;typedef struct{unsigned int  data1;unsigned int  data2;
} __attribute__((__packed__)) _rpdo1600_t;/***************myapp.c ***********/
static volatile _tpdo1A00_t tpdodata;
static volatile _rpdo1600_t rpdodata;

在myapp.h定义了两个结构体用来存放TPDO和RPDO的数据。结构体的定义需要和PDO的映射对应。之前配置的时候我们的TPDO映射了两个32位的数,所以这里结构体里也是定义了两个32位的元素,另外需要取消字节对齐。

void APPL_InputMapping(UINT16* pData)
{UINT16 j = 0;UINT16 *pTmpData = (UINT16 *)pData;for (j = 0; j < sTxPDOassign.u16SubIndex0; j++){switch (sTxPDOassign.aEntries[j]){/* TxPDO 1 */case 0x1A00:memcpy(pTmpData,(void *)&tpdodata,sizeof(_tpdo1A00_t));pTmpData+=sizeof(_tpdo1A00_t);break;case 0x1A01:break;default:break;}}
}

APPL_InputMapping参数中pData就是传入进来的单片机本地的内存地址,在PDO_InputMappings函数中会将该内存地址的数据拷贝到ESC中,拷贝长度为总的映射长度。

TxPDOassign的aEntries中记录了0x1C13中所包含的对象字典。因为目前我们值映射了0x1A00,所以相当于只执行了memcpy这句话,将tpdodata的数据拷贝到pData的地址中。

如果0x1C13映射了0x1A00、0x1A01,那么在执行了memcpy这句话,可以将地址指针移位sizeof(_tpdo1A00_t)个长度,在继续拷贝0x1A01所对应的TPDO数据。

void APPL_OutputMapping(UINT16* pData)
{UINT16 j = 0;UINT16 *pTmpData = (UINT16 *)pData;for (j = 0; j < sRxPDOassign.u16SubIndex0; j++){switch (sRxPDOassign.aEntries[j]){/* RxPDO 1 */case 0x1600:memcpy(&rpdodata,pTmpData,sizeof(_rpdo1600_t));pTmpData = pTmpData+sizeof(_rpdo1600_t);break;default:break;}}
}

APPL_OutputMapping的修改与APPL_InputMapping的思路差不多,不同的是memcpy的方向,是将传入地址的数据拷贝到rpdodata中。

void APPL_Application(void)
{}

APPL_Application暂时没有修改,因为是简单的测试程序,我只通过debug观察rpdodata和tpdodata这两个变量就可以知道通信是否正常, 没有别的应用逻辑。

移植主要需要修改的源码有两个文件一个是myapp.c这个是SSC根据excel表格自动生成的文件,每个人的excel表格的名字不一样,所以文件名也不一样。另一个文件就是ecatport.c。其中myapp.c是我们实现应用逻辑的文件,而ecatport.c是实现我们硬件接口的地方。

修改 static UINT8 RxTxSpiData(UINT8 MosiByte) 这个是EL9800的SPI收发接口,将它改为我们自己的

5.其他

32位的单片机还需要修改ecat_def.h中的两个宏定义,改为如下:

/** 
OBJ_DWORD_ALIGN: Shall be set if the object structures are not Byte aligned and 32bit entries are implicitly padded to even 32bit memory addresses. */
#ifndef OBJ_DWORD_ALIGN
#define OBJ_DWORD_ALIGN                           1
#endif/** 
OBJ_WORD_ALIGN: Shall be set if the object structures are not Byte aligned and 16bit entries are implicitly padded to even 16bit memory addresses. */
#ifndef OBJ_WORD_ALIGN
#define OBJ_WORD_ALIGN                            0
#endif

如果不修改,在初始化状态跳转的时候可能会报0x001E或0x001D的错误。该错误是因为CheckSmSettings函数在检查邮箱配置的时候发现ESC中配置的SM2或者SM3的长度与从站代码中的nPdInputSize变量或nPdOutputSize变量的值不相同所返回的。理论上主站在配置ESC的SM2和SM3长度的时候是先读取从站代码中的TPDO和RPDO的映射内容,然后根据映射内容来计算映射的数据长度,再将所计算的长度填写到ESC中。而从站代码的nPdInputSize和nPdOutputSize这两个变量的值也是根据TPDO和RPDO的映射内容计算得到的,所以ESC所配置的值应该和代码中的值一样才对。而在调用APPL_GenerateMapping获取nPdInputSize和nPdOutputSize的值的时候会调用OBJ_GetEntryOffset 来计算结构体中元素的地址的偏移量,如果OBJ_DWORD_ALIGN = 0 ,OBJ_WORD_ALIGN =1就会按照16位对齐来计算,而GD32是32位对齐的,所以计算后得到的地址是错误的,所得出的长度也是错误的。因此要设置OBJ_DWORD_ALIGN = 1 ,OBJ_WORD_ALIGN =0。

UINT16 APPL_GenerateMapping(UINT16 *pInputSize,UINT16 *pOutputSize)
{.....pPDOEntry = (UINT32 *)((UINT16 *)pPDO->pVarPtr + (OBJ_GetEntryOffset((PDOEntryCnt+1),pPDO)>>3)/2);    .....
}

需要注意的是OBJ_GetEntryOffset得到的值单位是bit,右移3位相当于除以8,转换位字节;后面又除了2是因为前面的pPDO->pVarPtr被强制转换位了(UINT16 *)类型,那么pPDO->pVarPtr+1 就相当于跨了两个字节。

移植工程链接:https://github.com/IJustLoveMyself/csdn-example


http://chatgpt.dhexx.cn/article/kQGIWfSC.shtml

相关文章

小说php 站点源码下载,PTCMS小说站源码

必装环境&#xff1a;nginx(apache.iis 也可)&#xff0c;mysql,php5.6,memcached php5.6 安装扩展 memcache 新建站点&#xff0c;注意新建时&#xff0c;PHP 版本必须选择 PHP5.6&#xff0c;不然程序会报错 1.上传网站文件到网站目录&#xff0c;新建网站伪静态选择 thinkph…

PHP是什么

PHP 是服务器端脚本语言。 您应当具备的基础知识 在继续学习之前&#xff0c;您需要对以下知识有基本的了解&#xff1a; HTMLCSS 如果您希望首先学习这些项目&#xff0c;请在我们的 首页 访问这些教程。 PHP 是什么&#xff1f; PHP 代表 PHP: Hypertext PreprocessorPHP 是一…

国家地表水水质自动监测站坐标每四小时数据(共1952个监测站,含省份、城市、河流、流域、断面名称、监测时间、水温、pH、DO、CODMn、TP、TN、NH3-N、浊度等)

1.监测范围 国家地表水水质自动监测网1952 个水质自动监测站。2.监测项目 监测项目为国家水质自动监测站配备的监测指标&#xff0c;主要包括五参数(水温、pH、溶解氧、电导率和浊度)、氨氮、高锰酸盐指数、总氮、总磷&#xff0c;部分水站增测总有机碳、叶绿素a、藻密度、VOCs…

php 跨站脚本,Piwigo register.php页面多个跨站脚本漏洞

发布日期&#xff1a;2010-05-06 更新日期&#xff1a;2010-05-11 受影响系统&#xff1a; Piwigo project Piwigo 2.0.9 描述&#xff1a; -------------------------------------------------------------------------------- BUGTRAQ ID: 39958 CVE(CAN) ID: CVE-2010-1707…

基于Modbus RTU 485通信协议实现对PH、溶解氧传感器的数据采集

modbus rtu 485协议采用的是一主多从方式通信&#xff0c;主机是普中的stm32f103zet6开发板&#xff0c;从机是传感器。代码已经在实物上测试通过&#xff0c;并且也用modbus精灵测试通过了。如果你没有stm32基础的话&#xff0c;建议先去B站搜索“正点原子”了解一下485串口通…

3、基于51单片机的智能水箱控制系统-温度-PH值-水位(仿真+程序+原理图)

目录 基于51单片机的智能水箱控制系统1、主要功能2、实验结果3、仿真工程4、原理图5、程序源码6、资源获取 基于51单片机的智能水箱控制系统 1、主要功能 51单片机检测水箱内温度&#xff0c;ph值&#xff1b;使用pid算法控制温度到设置值&#xff1b;普通控制ph值到设定值&a…

如何下载y站视频

今天看到了一篇B站视频的下载方法&#xff0c;学习了下&#xff0c;然后去看了下y站是不是也能下下来&#xff0c;居然被我试出来了&#xff0c;嘿嘿 B站文章链接&#xff1a;https://blog.csdn.net/Enderman_xiaohei/article/details/94718494 然后看一下y站的&#xff0c;打…

基于STM32(HAL库)的水质检测(浑浊度、PH值、温度、手机APP显示、wifi上云)

本系统由通过wifi将浑浊度、PH值、温度采集的数据发送到手机APP&#xff0c;超过设定的阈值报警。 一、硬件材料清单&#xff1a; 1、STM32C8T6&#xff1a;控制器 2、OLED显示屏&#xff1a;显示传感器采集的数据 3、PH传感器&#xff1a;检测PH值 4、TDS传感器&#xff1…

JEB动态调试与篡改攻防世界Ph0en1x-100

文章目录 题目APK静态分析jadx反编译IDA反汇编 JEB动态调试工具的使用操作内存值 AndroidKiller工具的使用篡改软件包 总结 题目 攻防世界 Mobile 新手区题目链接 Ph0en1x-100&#xff0c;如下&#xff1a; 下载附件得到一个 apk&#xff0c;安装后如下&#xff1a; 要求输入…

php 抓站,如何跨站抓取别的站点的页面的补充

如何跨站抓取别的站点的页面的补充 更新时间&#xff1a;2006年10月09日 00:00:00 作者&#xff1a; 在实际的应用中&#xff0c;经常会遇到一些特殊的情况&#xff0c;比如需要新闻&#xff0c;天气预报&#xff0c;等等&#xff0c;但是作为个人站点或者实力小的站点 我们不…

用python计算ph,用python下载PH上的学习视频

努力学习&#xff0c;天天向上 闲来无事&#xff0c;用python写个脚本下载PH上的学习视频 环境&#xff1a;python3 用法&#xff1a;python ph_downloader.py viewkey viewkey是PH上的一串字符 代码在 https://paste.ubuntu.com/p/jXVYD3NGP9 多线程下载&#xff0c;网络不好时…

浮标水质监测站--河流湖泊水库现场水质自动监测的解决方案

浮标水质监测站--河流湖泊水库现场水质自动监测的解决方案 什么是浮标水质监测站&#xff1f; 浮标水质监测站是设立在河流、湖泊、水库、近岸海域等流域内进行现场水质自动监测的监测仪器&#xff0c;是以水质监测仪为核心&#xff0c;运用传感器技术&#xff0c;结合浮标体…

html全屏背景视频特效,HTML5全屏背景视频特效插件Vidage.js源码

下面我们对HTML5全屏背景视频特效插件Vidage.js源码文件阐述相关使用资料和HTML5全屏背景视频特效插件Vidage.js源码文件的更新信息。 HTML5全屏背景视频特效插件Vidage.js源码 本特效是一款移动友好的基于HTML5插件Vidage.js实现全屏背景视频特效的代码。Vidage.js可以在手机或…

html背景图片只显示一张图片,img只显示图片一部分 或 css设置背景图片只显示图片指定区域(示例代码)...

17:14 2016/3/22 img只显示图片一部分 或 css设置背景图片只显示图片指定区域 background-position: 100% 56%; 设置背景图片显示图片的哪个坐标区域&#xff0c;图片左上角为0&#xff0c;0或0%&#xff0c;0%&#xff0c;右下角为高度和宽度&#xff0c;或100%&#xff0c;10…

html的背景图片设置

背景颜色&#xff1a;background-color&#xff0c;可用rgb、rgba、#off等进行设置 背景图片&#xff1a;background-img&#xff0c;可用网址或图片存放地址进行设置 平铺方式&#xff1a; background-repeat:repeat&#xff0c;在背景上只出现一张图片&#xff0c;没有设置…

Html手机web背景全屏,使用全屏背景的网页设计欣赏 全屏网页背景教程

使用全屏背景的网页设计欣赏 & 全屏网页背景教程 Sponsor 全屏的大背景网站之前分享过不少&#xff0c;这是2012年以及2013年的设计趋势&#xff0c;适合用全屏背景的网站有很多&#xff0c;比如摄影类、建筑类、餐厅美食行业等等。全屏背景不仅仅是一些插画及设计图&#…

html背景全屏,23个使用大背景的全屏网页设计作品

23个使用大背景的全屏网页设计作品 7月 20, 2015 评论 (2) Sponsor 在2013年的时候使用全屏背景或者大图片的网页开始流行起来&#xff0c;到了今年&#xff0c;还是经常看到这类风格的页面出现&#xff0c;可见这个趋势还是很流行的&#xff0c;值得运用。 一张大图再配合一个…

html点击图片可以放全屏,html:点击图片放大到全屏,再次点击缩回

做手机网页开发时,经常遇到的效果:点击某个不加链接的图片时,图片放大,当再次点击放大的图片时,图片缩回(实质上将层隐藏掉了)。 实现思路: 准备原材料:一个遮档整个屏幕的不透明接近黑色的背景层( ),一个位于背景层之上的图片容器( ) ; 加工过程:当点击不加链接的图…

利用css 把背景图片全屏铺满

1.html代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><!--引入我自己写的css文件--><link text"text/css" rel"stylesheet" href"../cs…

html5设置全屏背景图,HTML5 body设置全屏背景图片 如何让body的背景图片自适应整个屏----实战经验...

用什么代码实现&#xff1f;不允许有白色底色产生&#xff0c;因为手机高度不一样 错误的写法&#xff1a;加到div中结合图片设置min-height&#xff0c;但是页面不会回弹 话不多说直接上代码 终极方案 html,body{ width:100%; height:100% } 再加一段 body{ font-family: &quo…