当前位置: 首页 > news >正文

别人做的网站需要提供些什么给我们云邦北京网站建设

别人做的网站需要提供些什么给我们,云邦北京网站建设,网站建设编辑工作总结,wordpress 注册会员默认权限目录 概述 1 硬件接口 2 添加ICM20608的设备树信息 2.1 使用i.MX Pins Tool v6配置SPI引脚 2.2 配置设备树 2.3 编译.dts 2.4 在板卡中更新设备树 3 编写驱动程序 3.1 创建设备匹配表 3.2 重要函数介绍 3.2.1 初始化函数 3.2.2 写寄存器函数 3.2.3 读寄存器函数 …目录 概述 1 硬件接口 2 添加ICM20608的设备树信息 2.1 使用i.MX Pins Tool v6配置SPI引脚 2.2 配置设备树 2.3 编译.dts  2.4 在板卡中更新设备树 3 编写驱动程序 3.1 创建设备匹配表 3.2 重要函数介绍 3.2.1 初始化函数 3.2.2 写寄存器函数 3.2.3 读寄存器函数 3.2.3 完整驱动代码 3.2.3.1 驱动代码程序 3.2.3.1 驱动代码程序Makefile 4 测试驱动安装 4.1 编译驱动程序 4.2 在板卡上安装驱动 5 验证和测试 5.1 编写测试程序 5.1.1 测试程序源代码 5.1.2 测试程序Makefile  5.2 运行测试代码 5.3 结论 概述 本文详细介绍了如何在Linux platform tree框架下编写一个基于SPI接口的驱动程序。笔者使用ICM20606芯片应用SPI工作方式操作该芯片实现其驱动程序。还编写了一个测试程序并在板卡上运行测试程序。验证驱动的可靠性。 源代码地址 ICM20608linux环境下的驱动程序资源-CSDN文库 ICM20608linux环境下的测试程序资源-CSDN文库 1 硬件接口 本文主要使用SPI总线驱动ICM20608芯片如下内容会详细介绍设备树更新驱动代码的实现等内容。由原理图可得ICM20608与MCU的对应关系如下 ICM20608MCU注释SDO MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 主设备输入、从设备输出线SDI MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 主设备输出、从设备输入线SCLK MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 时钟线CS MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 从设备选择线INTJTAG_MOD_SP_TX中断引脚 板卡上ICM-20608的原理图 2 添加ICM20608的设备树信息 2.1 使用i.MX Pins Tool v6配置SPI引脚 使用i.MX Pins Tool v6配置引脚配置对应的引脚为SPI,配置参数如下 pinctrl_ecspi3: ecspi3grp {fsl,pins MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x100b1 /* MISO*/MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x100b1 /* MOSI*/MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x100b1 /* CLK */MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x100b0 /* CS */;}; 在.dts文件中的位置为: 2.2 配置设备树 配置设备树的目的是将SPI的工作IO引脚和外围的设备对应起来方便在驱动程序中匹配该参数信息。在.dts文件中添加如下代码 ecspi3 {fsl,spi-num-chipselects 1;cs-gpio gpio1 20 GPIO_ACTIVE_LOW;pinctrl-names default;pinctrl-0 pinctrl_ecspi3;status okay;spidev: icm206080 {compatible atk,icm20608;spi-max-frequency 8000000;reg 0;}; }; spi的配置参数在.dts文件中的位置为: 2.3 编译.dts  使用命令编译代码。然后将生成的.dtb文件发送到NFS的共享目录下方便在板卡使用该文件 make dtbs 编译后得到了.dtb 2.4 在板卡中更新设备树 1 将NFS中共享的设备树文件copy到运行目录中 2reboot板卡后内核会重新读取.dtb文件。然后在/sys/bus/spi/devices/spi2.0目录下查看板卡device tree使用如下命令 cd /sys/bus/spi/devices/spi2.0/ ls -l 得到节点信息如下 3 编写驱动程序 3.1 创建设备匹配表 其目的是和设备树中参数对应起来 static const struct of_device_id atk_dl6y2c_icm20608[] {{ .compatible atk,icm20608 },{ }, };static const struct spi_device_id icm20608_id[] {{ xxxxyyy, (kernel_ulong_t)NULL },{ /* END OF LIST */ } };/* 1. 定义platform_driver */ static struct i2c_driver icm20608_driver {.probe icm20608_probe,.remove icm20608_remove,.driver {.name atk_icm20608,.of_match_table atk_dl6y2c_icm20608,},.id_table icm20608_id, }; 在.c 文件中的代码信息如下  3.2 重要函数介绍 3.2.1 初始化函数 在初始化函数中通过调用spi_setup 来初始化spi接口该函数放在prope接口中完成 3.2.2 写寄存器函数 第148行写寄存器数据时寄存器地址信息bit-7必须为0 3.2.3 读寄存器函数 第111行读寄存器数据时必须传递一个寄存器地址信息且bit-7必须为1 3.2.3 完整驱动代码 3.2.3.1 驱动代码程序 /*************************************************************** Copyright 2024-2029. All rights reserved. 文件名 : drv_17_icm20608.c 作者 : tangmingfei2013126.com 版本 : V1.0 描述 : icm20608 驱动程序 其他 : 无 日志 : 初版V1.0 2024/1/30 使用方法 1) 在.dts文件中定义节点信息 ecspi3 {fsl,spi-num-chipselects 1;cs-gpio gpio1 20 GPIO_ACTIVE_LOW;pinctrl-names default;pinctrl-0 pinctrl_ecspi3;status okay;spidev: icm206080 {compatible atk,icm20608;spi-max-frequency 8000000;reg 0;}; };2) 在驱动匹配列表 static const struct of_device_id icm20608_of_match[] {{ .compatible atk,icm20608 },{ } // Sentinel }; ***************************************************************/ #include linux/types.h #include linux/kernel.h #include linux/delay.h #include linux/ktime.h #include linux/ide.h #include linux/init.h #include linux/module.h #include linux/errno.h #include linux/gpio.h #include linux/cdev.h #include linux/device.h #include linux/of_gpio.h #include linux/semaphore.h #include linux/timer.h #include linux/irq.h #include linux/wait.h #include linux/poll.h #include linux/fs.h #include linux/fcntl.h #include asm/mach/map.h #include asm/uaccess.h #include asm/io.h #include linux/spi/spi.h #include linux/of.h #include linux/of_address.h#define ICM20_SELF_TEST_X_GYRO 0x00 #define ICM20_SELF_TEST_Y_GYRO 0x01 #define ICM20_SELF_TEST_Z_GYRO 0x02 #define ICM20_SELF_TEST_X_ACCEL 0x0D #define ICM20_SELF_TEST_Y_ACCEL 0x0E #define ICM20_SELF_TEST_Z_ACCEL 0x0F/* 陀螺仪静态偏移 */ #define ICM20_XG_OFFS_USRH 0x13 #define ICM20_XG_OFFS_USRL 0x14 #define ICM20_YG_OFFS_USRH 0x15 #define ICM20_YG_OFFS_USRL 0x16 #define ICM20_ZG_OFFS_USRH 0x17 #define ICM20_ZG_OFFS_USRL 0x18#define ICM20_SMPLRT_DIV 0x19 #define ICM20_CONFIG 0x1A #define ICM20_GYRO_CONFIG 0x1B #define ICM20_ACCEL_CONFIG 0x1C #define ICM20_ACCEL_CONFIG2 0x1D #define ICM20_LP_MODE_CFG 0x1E #define ICM20_ACCEL_WOM_THR 0x1F #define ICM20_FIFO_EN 0x23 #define ICM20_FSYNC_INT 0x36 #define ICM20_INT_PIN_CFG 0x37 #define ICM20_INT_ENABLE 0x38 #define ICM20_INT_STATUS 0x3A/* 加速度输出 */ #define ICM20_ACCEL_XOUT_H 0x3B #define ICM20_ACCEL_XOUT_L 0x3C #define ICM20_ACCEL_YOUT_H 0x3D #define ICM20_ACCEL_YOUT_L 0x3E #define ICM20_ACCEL_ZOUT_H 0x3F #define ICM20_ACCEL_ZOUT_L 0x40/* 温度输出 */ #define ICM20_TEMP_OUT_H 0x41 #define ICM20_TEMP_OUT_L 0x42/* 陀螺仪输出 */ #define ICM20_GYRO_XOUT_H 0x43 #define ICM20_GYRO_XOUT_L 0x44 #define ICM20_GYRO_YOUT_H 0x45 #define ICM20_GYRO_YOUT_L 0x46 #define ICM20_GYRO_ZOUT_H 0x47 #define ICM20_GYRO_ZOUT_L 0x48#define ICM20_SIGNAL_PATH_RESET 0x68 #define ICM20_ACCEL_INTEL_CTRL 0x69 #define ICM20_USER_CTRL 0x6A #define ICM20_PWR_MGMT_1 0x6B #define ICM20_PWR_MGMT_2 0x6C #define ICM20_FIFO_COUNTH 0x72 #define ICM20_FIFO_COUNTL 0x73 #define ICM20_FIFO_R_W 0x74 #define ICM20_WHO_AM_I 0x75/* 加速度静态偏移 */ #define ICM20_XA_OFFSET_H 0x77 #define ICM20_XA_OFFSET_L 0x78 #define ICM20_YA_OFFSET_H 0x7A #define ICM20_YA_OFFSET_L 0x7B #define ICM20_ZA_OFFSET_H 0x7D #define ICM20_ZA_OFFSET_L 0x7E#define DEVICE_NAME icm20608 // dev/icm20608/* icm20608dev设备结构体 */ struct icm20608stru_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */struct spi_device *spi; };struct icm20608stru_value{signed int gyro_x_adc; /* 陀螺仪X轴原始值 */signed int gyro_y_adc; /* 陀螺仪Y轴原始值 */signed int gyro_z_adc; /* 陀螺仪Z轴原始值 */signed int accel_x_adc; /* 加速度计X轴原始值 */signed int accel_y_adc; /* 加速度计Y轴原始值 */signed int accel_z_adc; /* 加速度计Z轴原始值 */signed int temp_adc; /* 温度原始值 */ };/* read or write icm20608 structure */static struct icm20608stru_dev icm20608dev;/*icm20608 driver */ static int icm20608_read( unsigned char reg, unsigned char *buff, unsigned int len) {int ret -1;unsigned char txdata[1];unsigned char * rxdata;struct spi_message spi_msg;struct spi_transfer *spi_t;struct spi_device *spi icm20608dev.spi;spi_t kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);if(!spi_t) {return -ENOMEM;}rxdata kzalloc(sizeof(char) * len, GFP_KERNEL);if(!rxdata) {goto ERROR1;}txdata[0] reg | 0x80; // set bit-7 as 1 for read mode spi_t-tx_buf txdata; spi_t-rx_buf rxdata; spi_t-len len1; spi_message_init(spi_msg); spi_message_add_tail(spi_t, spi_msg);ret spi_sync(spi, spi_msg);if(ret) {goto ERROR2;}memcpy(buff , rxdata1, len);ERROR2:kfree(rxdata); ERROR1: kfree(spi_t);return ret; }static int icm20608_write( unsigned char reg, unsigned char *buff, unsigned int len) {int ret -1;unsigned char *txdata;struct spi_message spi_msg;struct spi_transfer *spi_t;struct spi_device *spi icm20608dev.spi;spi_t kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); if(!spi_t) {return -ENOMEM;}txdata kzalloc(sizeof(char)len, GFP_KERNEL);if(!txdata) {goto ERROR1;}*txdata reg ~0x80; memcpy(txdata1, buff, len); spi_t-tx_buf txdata; spi_t-len len1;spi_message_init(spi_msg);spi_message_add_tail(spi_t, spi_msg);ret spi_sync(spi, spi_msg);if(ret) {goto ERROR2;}ERROR2:kfree(txdata); ERROR1:kfree(spi_t);return ret; }static void icm20608_readdata(struct icm20608stru_value *dev) {unsigned char data[14] { 0 };icm20608_read(ICM20_ACCEL_XOUT_H, data, 14);dev-accel_x_adc (signed short)((data[0] 8) | data[1]); dev-accel_y_adc (signed short)((data[2] 8) | data[3]); dev-accel_z_adc (signed short)((data[4] 8) | data[5]); dev-temp_adc (signed short)((data[6] 8) | data[7]); dev-gyro_x_adc (signed short)((data[8] 8) | data[9]); dev-gyro_y_adc (signed short)((data[10] 8) | data[11]);dev-gyro_z_adc (signed short)((data[12] 8) | data[13]); }static void icm20608_reginit(void) {unsigned char value 0;unsigned char buff[1];buff[0] 0x80;icm20608_write( ICM20_PWR_MGMT_1, buff , 1);mdelay(50);buff[0] 0x01;icm20608_write( ICM20_PWR_MGMT_1, buff , 1);mdelay(50);icm20608_read(ICM20_WHO_AM_I, value, 1);printk(ICM20608 ID %#X\r\n, value); buff[0] 0x00;icm20608_write( ICM20_SMPLRT_DIV, buff, 1);buff[0] 0x18;icm20608_write( ICM20_GYRO_CONFIG, buff, 1);buff[0] 0x18;icm20608_write( ICM20_ACCEL_CONFIG, buff, 1);buff[0] 0x04;icm20608_write( ICM20_CONFIG, buff, 1);buff[0] 0x04;icm20608_write( ICM20_ACCEL_CONFIG2, buff, 1);buff[0] 0x00;icm20608_write( ICM20_PWR_MGMT_2, buff, 1);buff[0] 0x00;icm20608_write( ICM20_LP_MODE_CFG, buff, 1);buff[0] 0x00;icm20608_write( ICM20_FIFO_EN, buff, 1); }/*linux driver 驱动接口 实现对应的open/read/write等函数填入file_operations结构体 */ static ssize_t icm20608_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset) {int err;signed int data[7];struct icm20608stru_value stru_dev;icm20608_readdata( stru_dev );data[0] stru_dev.gyro_x_adc;data[1] stru_dev.gyro_y_adc;data[2] stru_dev.gyro_z_adc;data[3] stru_dev.accel_x_adc;data[4] stru_dev.accel_y_adc;data[5] stru_dev.accel_z_adc;data[6] stru_dev.temp_adc;err copy_to_user(buf, data, sizeof(data));return err; }static int icm20608_drv_close(struct inode *node, struct file *file) {printk( %s line %d \r\n, __FUNCTION__, __LINE__);return 0; }static int icm20608_drv_open(struct inode *inode, struct file *filp) {icm20608_reginit();return 0; }/* 定义driver的file_operations结构体 */ static struct file_operations icm20608_fops {.owner THIS_MODULE,.read icm20608_drv_read,.open icm20608_drv_open,.release icm20608_drv_close, };/* 1. 从platform_device获得GPIO* 2. gpioirq* 3. request_irq*/ static int icm20608_probe( struct spi_device *spi ) {printk(icm20608 driver and device was matched!\r\n);/* initial spi port */spi-mode SPI_MODE_0; /*MODE0CPOL0CPHA0*/spi_setup(spi);icm20608dev.spi spi;/* register file_operations */icm20608dev.major register_chrdev( 0, DEVICE_NAME, /* device name */icm20608_fops); /* create the device class */icm20608dev.class class_create(THIS_MODULE, icm20608_class);if (IS_ERR(icm20608dev.class)) {printk(%s line %d\n, __FUNCTION__, __LINE__);unregister_chrdev( icm20608dev.major, DEVICE_NAME);return PTR_ERR( icm20608dev.class );}/* create device */device_create( icm20608dev.class, NULL, MKDEV( icm20608dev.major, 0 ), NULL, DEVICE_NAME); // device name return 0; }static int icm20608_remove(struct spi_device *spi) {printk(%s line %d\n, __FUNCTION__, __LINE__);device_destroy( icm20608dev.class, MKDEV( icm20608dev.major, 0));class_destroy(icm20608dev.class);unregister_chrdev(icm20608dev.major, DEVICE_NAME);return 0; }static const struct of_device_id atk_dl6y2c_icm20608[] {{ .compatible atk,icm20608 },{ }, };static const struct spi_device_id icm20608_id[] {{ xxxxyyy, (kernel_ulong_t)NULL },{ /* END OF LIST */ } };/* 1. 定义platform_driver */ static struct spi_driver icm20608_driver {.probe icm20608_probe,.remove icm20608_remove,.driver {.name atk_icm20608,.of_match_table atk_dl6y2c_icm20608,},.id_table icm20608_id, };/* 2. 入口函数: 注册driver */ static int __init icm20608_init(void) {printk(%s line %d\n,__FUNCTION__, __LINE__);return spi_register_driver(icm20608_driver); }/* 3. 出口函数卸载driver*/ static void __exit icm20608_exit(void) {printk(%s line %d\n, __FUNCTION__, __LINE__);spi_unregister_driver(icm20608_driver); }/*4. 驱动入口和出口函数 */ module_init(icm20608_init); module_exit(icm20608_exit);MODULE_LICENSE(GPL); MODULE_AUTHOR(tangmingfei2013126.com);3.2.3.1 驱动代码程序Makefile PWD : $(shell pwd)KERNEL_DIR/home/mftang/linux_workspace/study_atk_dl6y2c/kernel/atk-dl6u2c ARCHarm CROSS_COMPILE/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-export ARCH CROSS_COMPILEobj-m: drv_17_icm20608.oall:$(MAKE) -C $(KERNEL_DIR) M$(PWD) modulesclean:rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.order *.symvers 4 测试驱动安装 4.1 编译驱动程序 在服务器上编译驱动程序执行make命令后会生成的.ko复制该文件到NFS的共享目中方便在板卡中使用 4.2 在板卡上安装驱动 在板卡上执行  insmod drv_17_icm20608.ko可以看见打印如下信息说明驱动程序已经安装到内核  在dev目录下查看该设备驱动 5 验证和测试 完成驱动程序编写后并不能保证驱动程序是否能够正常工作这时需要编程一个测试程序来验证其功能是否可靠。 5.1 编写测试程序 5.1.1 测试程序源代码 /*************************************************************** Copyright 2024-2029. All rights reserved. 文件名 : test_17_icm20608.c 作者 : tangmingfei2013126.com 版本 : V1.0 描述 : 测试icm20608驱动程序 其他 : 无 日志 : 初版V1.0 2024/02/22 ***************************************************************/ #include sys/types.h #include sys/stat.h #include linux/types.h #include linux/i2c-dev.h #include linux/i2c.h #include sys/ioctl.h #include unistd.h #include stdio.h #include string.h #include fcntl.h #include stdlib.h #include linux/fs.h #include unistd.h #include errno.h #include assert.h #include string.h #include time.h#define DEV_FILE /dev/icm20608struct icm20608stru_value{signed int gyro_x_adc; /* 陀螺仪X轴原始值 */signed int gyro_y_adc; /* 陀螺仪Y轴原始值 */signed int gyro_z_adc; /* 陀螺仪Z轴原始值 */signed int accel_x_adc; /* 加速度计X轴原始值 */signed int accel_y_adc; /* 加速度计Y轴原始值 */signed int accel_z_adc; /* 加速度计Z轴原始值 */signed int temp_adc; /* 温度原始值 */ };int main(void) {int count 0;int fd, ret;unsigned char databuf[7];struct icm20608stru_value stru_value;float gyro_x_act, gyro_y_act, gyro_z_act;float accel_x_act, accel_y_act, accel_z_act;float temp_act;fd open(DEV_FILE, O_RDWR);if (fd -1){printf(can not open file: %s \n, DEV_FILE);return -1;}while (count 10000 ) {ret read(fd, databuf, sizeof(databuf));if(ret 0) {stru_value.gyro_x_adc databuf[0];stru_value.gyro_y_adc databuf[1];stru_value.gyro_z_adc databuf[2];stru_value.accel_x_adc databuf[3];stru_value.accel_y_adc databuf[4];stru_value.accel_z_adc databuf[5];stru_value.temp_adc databuf[6];/* 计算实际值 */gyro_x_act (float)(stru_value.gyro_x_adc) / 16.4;gyro_y_act (float)(stru_value.gyro_y_adc) / 16.4;gyro_z_act (float)(stru_value.gyro_z_adc) / 16.4;accel_x_act (float)(stru_value.accel_x_adc) / 2048;accel_y_act (float)(stru_value.accel_y_adc) / 2048;accel_z_act (float)(stru_value.accel_z_adc) / 2048;temp_act ((float)(stru_value.temp_adc) - 25 ) / 326.8 25;printf(\r\n %04d count value:\r\n, count);printf(gx %d, gy %d, gz %d\r\n, stru_value.gyro_x_adc,stru_value.gyro_y_adc, stru_value.gyro_z_adc);printf(ax %d, ay %d, az %d\r\n, stru_value.accel_x_adc, stru_value.accel_y_adc, stru_value.accel_z_adc);printf(temp %d\r\n, stru_value.temp_adc);printf( %04d user value: , count);printf(gx %.2f°/S, gy %.2f°/S, gz %.2f°/S\r\n, gyro_x_act, gyro_y_act, gyro_z_act);printf(ax %.2fg, ay %.2fg, az %.2fg\r\n, accel_x_act, accel_y_act, accel_z_act);printf(temp %.2f°C\r\n, temp_act);}usleep(100000); /*100ms */count;}close(fd);return 0; }5.1.2 测试程序Makefile  CFLAGS -Wall -O2 CC/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc STRIP/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-striptest_17_icm20608: test_17_icm20608.o$(CC) $(CFLAGS) -o test_17_icm20608 test_17_icm20608.o$(STRIP) -s test_17_icm20608clean:rm -f test_17_icm20608 test_17_icm20608.o 5.2 运行测试代码 编译测试代码将生成的可执行文件共享在NF目录中方便在板卡中执行程序 转动板卡观察sensor的数据变化 5.3 结论 通过运行测试程序App调用驱动程序能正确的从sensor中读取数据
http://www.dnsts.com.cn/news/197514.html

相关文章:

  • 延庆网站制作织梦是怎么做网站
  • 网站开发 架构第三方做农产品价格数据的网站
  • 腾讯免费网站空间做同城特价的网站
  • 长春网站建设服务扶贫网站建设优势
  • wordpress网站打开慢商城网站有什么好处
  • 网站开发.networdpress 相邻文章
  • 社区门户网站模板网站建站的步骤流程
  • 怎么选择网站模板北京网站开发公司一网天行
  • 网站怎么制作 优帮云企业网站搜索优化
  • 康定网站建设营销软文100字
  • wordpress15天教程网站改名 seo
  • 建立一个网站需要多久wordpress 糗百
  • 旅游网站建设的目标建设视频网站多少钱
  • 休闲食品网站建设策划书钓鱼转转网站在线生成
  • 建设积分兑换官方网站wordpress去版权插件
  • 网站开发代理合同制作微信网页
  • 梧州单身相亲网站erp网站建设
  • 网站手机端和电脑端禁止百度收录的网站
  • 盐城百度推广公司上海seo优化培训机构
  • dede网站百度统计怎么做Wordpress iPhone 上传
  • 昆明猫咪网站建设公司代刷网站推广链接免费
  • 网站建设论文二稿网站对于一个企业的优势
  • wordpress做网站优点wordpress智能小程序
  • 服务器怎么发布网站徐州网站建设公司哪个好
  • 寺庙网站开发建设方案wordpress留言板设置
  • 多媒体网站设计开发是指什么建网站做商城个体户资质可以
  • 免费网站建设能做吗网站设计流程的步骤
  • 贵阳市城乡建设部网站公司做一个静态网站多少钱
  • 个人网站开发是学什么语言做视频网站盈利模式
  • 做销售在哪个网站找客户端廊坊seo建站