用USB代替网络通讯,实现Cartographer上位机与下位机之间的消息传递_usb_f_ecm_subset-程序员宅基地

技术标签: 算法  linux  内核  

任务动机:用USB代替网络通讯,实现Cartographer上位机与下位机之间的消息传递。

任务描述:根据任务动机,研发实现用USB代替网络通讯,形成文档。

1. 在Toybrick-RK3399 ProD上编译Linux Kernel

        这是个漫长且无聊的过程,因为有关这方面的教程少之又少,在本人无数次踩坑下终于搞定。感兴趣的小伙伴可以打开官方教程链接体验下想找教程却没有教程然后砸电脑未遂的心情。

1.1 编译原理

        在RK3399 ProD里面,Linux的Kernel和rootfs(根文件系统)是分开地址存放的,rootfs是在Kernel之上运行的,所有的驱动和最基础的指令都存放在Kernel里面,各个厂商根据自己的特性在Kernel上开发自己的操作系统(例如Fedora和Ubuntu)。板子可以在不用重新编译内核的情况下更换操作系统,在本文档中,我们主要讲如何编译驱动到kernel里面,并支持多种操作系统。我用的rootfs链接(Ubuntu):百度云网盘提取码:q9my

1.2 编译过程

        首先,RK3399 ProD是A53+A7的芯片,你需要特殊的gcc编译器,然后再编译。

git clone https://github.com/friendlyarm/prebuilts.git -b master --depth 1
cd prebuilts/gcc-x64
cat toolchain-6.4-aarch64.tar.gz* | sudo tar xz -C /

        然后再编辑你的.bashrc,把下面的东西加到bashrc的最底部。

export PATH=/opt/FriendlyARM/toolchain/6.4-aarch64/bin:$PATH
export GCC_COLORS=auto

        接下来,你需要下载Linux Kernel源码,由于官方给的源码太多坑,我把我修改后的直接上传到gitee供国人下载,方便快捷。

git clone -b kernel-joybrick https://gitee.com/harryzhangabc/ros_bridge-and-catographer-setup.git

        这里介绍下如何给驱动添加USB RNIDS/gagnet网络功能。

cd /path/to/your/ros_bridge-and-catographer-setup/dir/
make menuconfig

        然后把下面的几个选项操作成如下方式

<M>   USB Gadget Drivers
<M>     USB functions configurable through configfs
<M>     Ethernet Gadget (with CDC Ethernet support)
[*]       RNDIS support (NEW)

接着在该目录下编译内核。

./make.sh linux prod

        编译完成后会生成boot_linux.img文件,把这个文件烧入到3399pro对应的区域中烧写教程,只要烧这个文件,其他的都不用管。当然你想偷懒的话可以用我编译好的:D百度网盘提取码:pt7p
        最后看下你编译好的文件,有几个驱动模块文件需要手动复制到RK3399 ProD里面手动加载

drivers/usb/gadget/function/u_ether.ko
drivers/usb/gadget/function/usb_f_ecm_subset.ko
drivers/usb/gadget/function/usb_f_ecm.ko
drivers/usb/gadget/function/usb_f_rndis.ko
drivers/usb/gadget/legacy/g_ether.ko
drivers/usb/gadget/libcomposite.ko

        然后在设备上,依次加载上述模块

insmod libcomposite.ko
insmod u_ether.ko
insmod usb_f_rndis.ko
insmod usb_f_ecm.ko
insmod usb_f_ecm_subset.ko
insmod g_ether.ko

        注意: 要先加载 libcomposite.ko 和 u_ether.ko,后面的模块才可以加载进去。

1.3 IP设置

  • IP 地址设置

        用数据线连接 PC 机和设备的 OTG 接口,在 PC 机中执行 lsusb 命令可以查看到 USB 以太网设备,即说明连接成功。

firefly@Desktop:~$ lsusb
Bus 002 Device 003: ID 09da:5814 A4Tech Co., Ltd.
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 005: ID 04f2:b2ea Chicony Electronics Co., Ltd Integrated Camera [ThinkPad]
Bus 001 Device 004: ID 0a5c:21e6 Broadcom Corp. BCM20702 Bluetooth 4.0 [ThinkPad]
Bus 001 Device 003: ID 147e:1002 Upek Biometric Touchchip/Touchstrip Fingerprint Sensor
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 003: ID 0525:a4a2 Netchip Technology, Inc. Linux-USB Ethernet/RNDIS Gadget
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
  • 在设备中 IP 的设置

        输入执行 ifconfig -a 命令,可以查看到以下信息:

root@firefly:~# ifconfig -a

# eth0 是有线网卡
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 168.168.100.48  netmask 255.255.0.0  broadcast 168.168.255.255
        inet6 fe80::1351:ae2f:442e:e436  prefixlen 64  scopeid 0x20<link>
        ether 8a:4f:c3:77:94:ac  txqueuelen 1000  (Ethernet)
        RX packets 9759  bytes 897943 (897.9 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 236  bytes 35172 (35.1 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 42  base 0x8000

        ...

# usb0 是虚拟的 usb 网卡
usb0: flags=4098<BROADCAST,MULTICAST>  mtu 1500
        ether 4a:81:b1:34:d2:ad  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

        然后给 usb0 网卡自定义一个适当的 IP,注意要设置 usb0 的 IP 和有线网卡 eth0 的 IP 不在同一网段!!

sudo ifconfig usb0 192.168.2.222 up
  • 在 PC 机中 IP 的设置

        首先,需要在设置里设置板子的ip,由于我设置的是192.168.2.222,那么直接在系统设置里面设置ip地址为手动,填入ip就行。(系统不同,这里就不放图了!!!)

# 设置 USB 网卡的 IP 地址和设备的 usb0 的 IP 地址在同一网段
firefly@Desktop:~$ sudo ifconfig enp0s20u2i1 192.168.2.95
#设置默认网关:要设置为设备 usb0 的 ip 地址,因为后面要通过 usb0 来进行流量的转发
firefly@Desktop:~$ sudo route add default gw 192.168.2.222

        设置完设备和 PC 机的 IP 后,使用USB-C/USB-A线连接到电脑,其中,电脑对板子的ip为192.168.2.222,板子对电脑的ip地址是192.168.2.95,他们是可以互相 ping 通的,PC 机也可以用 ssh 命令登录到设备。

        然后,我们使用性能测试工具ipref测试下带宽。在USB3.0接口下,带宽约为250Mbit/s左右(30MByte/s),满足日常传输使用。

        配置过程中要注意以下几点:

  1. 对应好上述步骤中自己设备上的各个 IP 地址,注意设备上的 USB 虚拟网卡 IP 和有线网络 IP 不在同一网段;
  2. PC 机虚拟 USB 网卡的网关要设置为设备虚拟 USB 网卡的 IP 地址;
  3. 设备上的虚拟网卡 IP 地址、IP 转发功能、流量转发规则等设置会在设备重启后恢复,然后大家可以自行查找资料如何开机自动设置这些配置。

1.4 配置ROS

  • 在RK3399上(从机)

        首先编辑/etc/hosts

sudo vi /etc/hosts

        然后在中间加入以下内容

192.168.2.95 server-HB

        名字自取啥都行,IP根据你自己情况来定 然后编辑.bashrc

sudo vi ~/.bashrc

        加入以下内容

export ROS_HOSTNAME=server-HB
export ROS_MASTER_URI=http://192.168.2.95:11311
export ROS_IP=192.168.2.222
  • 在电脑上

        编辑.bashrc

sudo gedit ~/.bashrc

        加入下面内容

export ROS_MASTER_URI=http://192.168.2.95:11311

        ROS连接大功告成!

1.5 测试

        在电脑上输入以下命令

roscore
rosrun turtlesim turtlesim_node

        然后在RK3399上打开一个终端

rosrun turtlesim turtle_teleop_key

        然后你就可以控制小乌龟行走了!USBC到此配置完成!到目前为止ROS传输demo均已配置完成,Cartographer也能跑通,上位机可以直接获取到Cartographer的话题信息(这里就不放图了),剩余的硬件驱动/适配上位机程序部分需要一些时间,预计下周完成。

 

2. 如何在任意板子上编译RK3399/RK3399 Pro的Kernel

2.1 不同板子所对应的设备树

        一般来说,不同板子都有对应的特制功能从而设置了对应的特殊硬件(比如I2C、SPI设备),这些硬件需要在内核注册相关dts节点才能使用。因此,我们在购买板子的时候商家已经注册好了这些dts节点供我们使用,我们只要直接编译内核就好,如果是我们自己的板子,就需要自己去编写相对应的节点和驱动。
Linux设备树详解

2.2 RK3399/RK3399 Pro的原始设备树

        对于这款芯片而言,rockchip官方已经给出了相关的基础设备树,其芯片所有的外设的功能已经预制在/kernel/arch/arm64/boot/dts/rockchip中的rk3399.dtsi和rk3399pro.dtsi中,你只需要创建一个你自己的.dtsi,然后在里面加入你想使用的外设设备,然后再写一个驱动.c文件放到/kernel/drivers里面即可。以下是自行编写并实现PWM驱动的一个示例:

//为了省时间,我直接在原厂板子上提供的.dtsi进行修改,文件位于/kernel/arch/arm64/boot/dts/rockchip/rk3399pro-toybrick.dtsi,同时,你要自己画板子的话,可以参考这个文件的写法,写一个自己的板级dtsi文件。
// #include <dt-bindings/pwm/pwm.h>
// #include <dt-bindings/gpio/gpio.h>
// #include <dt-bindings/pinctrl/rockchip.h>
// #include <dt-bindings/input/input.h>
// #include <dt-bindings/display/drm_mipi_dsi.h>
// #include <dt-bindings/sensor-dev.h>

// #include "rk3399pro.dtsi"
// #include "rk3399-opp.dtsi"
//注:如果是自己写文件的话,请手动加入以上这些库文件,然后再继续进行编写

        pwm1: pwm_1 {
            status = "okay";
            compatible = "pwm_1";
            pwm_id = <1>;
        };          //添加一个pwm节点

        backlight: backlight {        //在这个上面添加
		compatible = "pwm-backlight";
        ......
编写驱动文件

#include <linux/module.h>
#include <linux/pwm.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>

#include <dt-bindings/pwm/pwm.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/init.h>

#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>

#define SYS_DEV_CONFIG 1

#define PWM_1_SET_PERIOD        _IOW('A', 0x11, unsigned int)
#define PWM_1_GET_PERIOD        _IOR('A', 0x12, unsigned int)
#define PWM_1_SET_DUTY          _IOW('A', 0x13, unsigned int)
#define PWM_1_GET_DUTY          _IOR('A', 0x14, unsigned int)
#define PWM_1_ENABLE            _IO('A', 0x15)
#define PWM_1_DISABLE           _IO('A', 0x16)

static int gval = 0;
struct pwm_data {
	int     pwm_id;
	struct pwm_device       *pwm;
	unsigned int            period;
	unsigned int pwm_period_ns;
	unsigned int max_period;
	unsigned int min_period;
	unsigned int duty_ns;
	bool	enabled;
};
struct pwm_data g_pdata;

#ifdef SYS_DEV_CONFIG
static ssize_t pwm_store(struct device *dev, \
		struct device_attribute *attr, const char *buf,size_t count)
{
	unsigned long state;
	int c,ret;
	ret = kstrtoul(buf, 10, &state);
	if (state > g_pdata.max_period)
		state = g_pdata.max_period;
	else if (state < g_pdata.min_period)
		state = g_pdata.min_period;
	c = state;
	if (ret)
		return ret;

	g_pdata.enabled = true;
	pwm_config(g_pdata.pwm, c, g_pdata.pwm_period_ns);
	pwm_enable(g_pdata.pwm);
	gval = c;

	return count;
}
static ssize_t pwm_show(struct device *dev, \
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%u\n", gval);
}

static struct kobject *pwm_kobj;
static DEVICE_ATTR(pwm, (S_IWUSR|S_IRUSR|S_IWGRP|S_IRGRP), pwm_show, pwm_store);
#endif

static int pwm_status_update(struct pwm_data *pdata)	//控制占空比、周期
{
	if (pdata->enabled)
		return 0;
		
	pdata->duty_ns = pdata->duty_ns * pdata->period / 100;	
	pwm_enable(pdata->pwm);
	pwm_config(pdata->pwm, pdata->duty_ns, g_pdata.pwm_period_ns);
	pdata->enabled = true;
	gval = pdata->duty_ns;
	return 0;
}
ssize_t pwm_parse_dt(struct pwm_data *pdata, struct platform_device *pdev)	//寻找已经注册的参数
{
	struct device_node *np = pdev->dev.of_node;
	const __be32 *id, *min_period, *max_period, *duty_ns;
	int  len;

	id = of_get_property(np, "pwm_id", &len);
	if (id)
		pdata->pwm_id = be32_to_cpu(*id);

	min_period = of_get_property(np, "min_period", &len);
	if (min_period)
		pdata->min_period = be32_to_cpu(*min_period);

	max_period = of_get_property(np, "max_period", &len);
	if (max_period)
		pdata->max_period = be32_to_cpu(*max_period);

	pdata->pwm_period_ns = pdata->max_period - pdata->min_period;

	duty_ns = of_get_property(np, "duty_ns", &len);
	if (duty_ns)
		pdata->duty_ns = be32_to_cpu(*duty_ns);

	return 0;
}

static void update_parameter(struct pwm_data data)
{
	struct pwm_data *pw_data = &data;
	pwm_status_update(pw_data);
}

static int pwm_1_open(struct inode *inode, struct file *filp)	
{
	g_pdata.enabled = true;
    update_parameter(g_pdata);
    
    return 0;
}

static int pwm_1_release(struct inode *inode, struct file *filp)
{	
    g_pdata.enabled = false;
    update_parameter(g_pdata);
    
    return 0;
}

static ssize_t pwm_1_read(struct file *filp, char __user *buf, size_t len, loff_t *pos)
{ 
    return 0;
}

static ssize_t pwm_1_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos)
{
    return 0;
}

static unsigned int pwm_1_get_period(void)
{
    return (g_pdata.period / 1000000);
}

static long pwm_1_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)	//驱动注册成功后,会在/dev下创建一个/pwm1的接口
{
    unsigned int period;
    unsigned int duty;
    
    void __user *argp = (void __user *)arg;
    
    switch (cmd)
    {
        case PWM_1_SET_PERIOD:
            if (argp == NULL) {
                printk("1: invalid argument.");
                return -EINVAL;
            }

            if (copy_from_user(&period, argp, sizeof(unsigned int))) {
                printk("copy_from_user failed.");
                return -EFAULT;
            }
            g_pdata.period = period;
            update_parameter(g_pdata);
            break;
            
        case PWM_1_GET_PERIOD:
            period = pwm_1_get_period();
        
            if (copy_to_user(argp, &period, sizeof(unsigned int))) {
                printk("copy_to_user failed.");
                return -EFAULT;
            }
            break;
            
        case PWM_1_SET_DUTY    :
            if (argp == NULL) {
                printk("1: invalid argument.");
                return -EINVAL;
            }

            if (copy_from_user(&duty, argp, sizeof(unsigned int))) {
                printk("copy_from_user failed.");
                return -EFAULT;
            }

            if ((duty < 0) || (duty > 100)) {
                printk("1: invalid argument.");
                return -EINVAL;
            }

            g_pdata.duty_ns = duty;
            update_parameter(g_pdata);
            break;
            
        case PWM_1_GET_DUTY    :
            if (copy_to_user(argp, &g_pdata.duty_ns, sizeof(unsigned int))) {
                printk("copy_to_user failed.");
                return -EFAULT;
            }
            break;
            
        case PWM_1_ENABLE:
            g_pdata.enabled = true;
			update_parameter(g_pdata);
            break;
            
        case PWM_1_DISABLE:
            g_pdata.enabled = false;
			update_parameter(g_pdata);
            break;
            
        default:
            printk("pwm: cmd error!\n");
            return -EFAULT;
    }
    
    return 0;
}

struct file_operations pwm_1_fops = {	//PWM1操作函数入口
    .owner = THIS_MODULE,
    .open = pwm_1_open,
    .release = pwm_1_release,
    .write = pwm_1_write,
    .read = pwm_1_read,
    .unlocked_ioctl = pwm_1_ioctl
};

struct miscdevice pwm_1_dev =
{  
    .minor  =   MISC_DYNAMIC_MINOR,
    .fops   =   &pwm_1_fops,
    .name   =   "pwm",
};

static int pwm_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct pwm_data *pdata = pdev->dev.platform_data;
	int ret;

	if (!np) {
		dev_err(&pdev->dev, "Device Tree node missing\n");
		return -EINVAL;
	}

	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;

	if (np)
		ret = pwm_parse_dt(pdata, pdev);

	pdata->enabled = false;

	pdata->pwm = pwm_request(pdata->pwm_id, "pwm_1");
	if (IS_ERR(pdata->pwm)) {
		dev_err(&pdev->dev, "unable to request legacy PWM\n");
		ret = PTR_ERR(pdata->pwm);
		goto err;
	}
	
	if (pdata->pwm_period_ns > 0)
		pwm_set_period(pdata->pwm, pdata->pwm_period_ns);
	pdata->period = pwm_get_period(pdata->pwm);

	g_pdata = *pdata;
	pwm_status_update(pdata);
	printk("%s: Toybrick PWM by harry Demo !\n", __func__);
	
	misc_register(&pwm_1_dev);

#ifdef SYS_DEV_CONFIG
	pwm_kobj = kobject_create_and_add("pwm", NULL);
	if (pwm_kobj == NULL) {
		printk("create kobject fail \n");
		ret = -ENOMEM;
		goto err;
	}

	ret = sysfs_create_file(pwm_kobj, &dev_attr_pwm.attr);
	if (ret) {
	printk("pwm sysfs_init: sysfs_create_group failed\n");
	goto err;
	}
#endif
	return 0;
err:
#ifdef SYS_DEV_CONFIG
	kobject_del(pwm_kobj);
#endif
	pwm_free(pdata->pwm);
	return ret;
}

static int pwm_remove(struct platform_device *pdev)
{
	struct pwm_data *pdata = pdev->dev.platform_data;
#ifdef SYS_DEV_CONFIG
	kobject_del(pwm_kobj);
#endif
	pwm_free(pdata->pwm);
	return 0;
}

static const struct of_device_id pwm_dt_ids[] = {
        { .compatible = "pwm_1"},
        {  }
};
MODULE_DEVICE_TABLE(of, pwm_dt_ids);

static struct platform_driver pwm_driver = {
	.driver = {
		.name = "pwm",
		.of_match_table = pwm_dt_ids,
	},
	.probe = pwm_probe,
	.remove = pwm_remove,
};

module_platform_driver(pwm_driver);
MODULE_AUTHOR("Harry <[email protected]>");
MODULE_LICENSE("GPL");

        然后添加到menuconfig

config PWM_RK3399_1
	tristate "RK3399 PWM1 support"
	default y
	help
	  Generic PWM framework driver for RK3399.

	  To compile this driver as a module, choose M here: the module
	  will be called pwm1.

        添加到makefile

obj-$(CONFIG_PWM_RK3399_1)	+= pwm-rk3399-1.o

        最后编译

./make.sh linux prod

注意,如果是自己制作的板子的话,可以参考make.sh里面的写法,先编译.dts/.dtsi文件,然后再编译内核

3. Cartographer STM32下位机

3.1 介绍

这是一个基于STM32的智能小车下位机(底盘控制器),兼容ROS操作系统,和cartographer项目的上位机进行适配,上位机通过STM32虚拟串口与下位机透传,波特率自适应不丢包。

3.2 软件架构

本软件基于C编写,支持一路SBUS接收机、一路GPS、一路IMU、一路编码器,支持速度闭环控制、方向控制、路径规划自动驾驶(基于GPS、测试中)、颠簸路况补偿。

支持上传GPS/IMU等信息并加入时间戳供上位机使用和参考。

3.3 任务列表

  •  完成SUBS解析。
  •  完成编码器读取和滤波。
  •  完成智能非线性PID设计与部署。
  •  完成IMU的去重力和yaw磁力计融合。
  •  完成GPS解析与自动授时。
  •  完成颠簸路段补偿设计。
  •  所有串口都走DMA以避免丢包。
  •  加入对INA219功率模块的支持。
  •  把所有接口换成紧接口,自带锁定。
  •  重新制板,所有东西包括电源全部采用模块化设计。
  •  加入对SK6812的支持。
  •  加入12VLED车灯功能。
  •  加入电压监测。
  •  完善上下位机通讯协议。
  •  加入在线调参功能。
  •  完成GPS路径规划设计(通过IMU补偿GPS精度)。

4. 参考链接

http://wiki.t-firefly.com/zh_CN/Firefly-RK3399/ubuntu_manual.html#usb-yi-tai-wang USB 以太网设置
http://t.rock-chips.com/wiki.php?mod=view&id=14 如何烧写固件
http://wiki.friendlyarm.com/wiki/index.php/NanoPC-T4/zh 12.6节 编译 FriendlyCore/FriendlyDesktop/Lubuntu/EFlasher的内核源代码

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhzwang/article/details/109304765

智能推荐

Redis 主从复制-哨兵-集群 相关部署_windows redis 主从复制部署-程序员宅基地

文章浏览阅读175次。Redis 主从复制+哨兵+集群一.主从复制-哨兵-集群二.主从复制1.主从复制的作用2.主从复制流程3.部署Redis 主从复制三.哨兵模式1.哨兵模式的原理2.哨兵模式的作用3.哨兵结构由两部分组成,哨兵节点和数据节点部署哨兵模式四.集群模式1.集群的作用,可以归纳为两点2.Redis集群的数据分片3.以3个节点组成的集群为例4.Redis集群的主从复制模型5.Redis集群部署一.主从复制-哨兵-集群1.主从复制:主从复制是高可用Redis的基础,哨兵和集群都是在主从复制基础上实现高可用的。主从复_windows redis 主从复制部署

看完还不懂HashMap算我输(附职场面试常见问题)_hashmap原理详解,看不懂算我输(附面试题)-程序员宅基地

文章浏览阅读1.3w次,点赞181次,收藏596次。HashMap的原理与实现版本之更迭:–》JDK 1.7 : Table数组+ Entry链表;–》JDK1.8 : Table数组+ Entry链表/红黑树;(为什么要使用红黑树?)一问HashMap的实现原理你看过HashMap源码吗,知道底层的原理吗为什么使用数组+链表用LinkedList代替数组可以吗既然是可以的,为什么不用反而用数组。重要变量介绍:..._hashmap原理详解,看不懂算我输(附面试题)

PHP实现跨域多图上传_php 上传图片流跨域怎么做-程序员宅基地

文章浏览阅读959次。干货:问题:在a.com上上传图片至b.comhtml(a.com)<div class="box_03"> <iframe style="width:100%;height:100%;" frameborder="0" scrolling="no" src="/picupload/ming?imgurl=&shuiyin=&uploadUr..._php 上传图片流跨域怎么做

Cisco(46)——HSRP和VRRP_hsrp 可以在svi口配置么-程序员宅基地

文章浏览阅读1.2w次,点赞4次,收藏2次。实验拓扑:实验说明:1.把SW3模拟成三台PC,配置三个VLAN对应的SVI地址。2.HSRP:SW1做vlan 10的活动设备,SW2做vlan 20的备份设备;SW1做vlan 10的备份设备,SW2做vlan 20的活动设备;3.VRRP:SW1做vlan 30的Master,SW2做vlan 30的备份设备。4.down掉环回口,检查数据包的路径。实..._hsrp 可以在svi口配置么

kafka-0.8.2.1-src编译报错解决办法_to honour the jvm settings for this build a new jv-程序员宅基地

文章浏览阅读8k次。今天编译kafka-0.8.2.1-src源代码,发现一个问题。编译始终报错,错误如下:lizhitao@users-MacBook-Pro-2:~/mt_wp/open_source/kafka-platform/kafka-0.8.2.1-src$ gradle jar_core_2_10_4 --stacktraceTo honour the JVM settings for this bu_to honour the jvm settings for this build a new jvm will be forked. please c

微信支付退款 java_Java后台实现微信支付和微信退款-程序员宅基地

文章浏览阅读600次。微信支付流程都是我自己工作中开发的,亲测可用,不喜勿喷。controller中我是这么写的,你们需要根据自己的业务需求改动。ResponseBean是我自己封装的,你们可以改成你们想要的形式。/*** 微信统一下单接口* @return*/@RequestMapping(value = "/doUnifiedOrder", method = RequestMethod.POST)public Re..._java+微信支付 dounifiedrefund

随便推点

PDO / MySQL:如何优化内存消耗大的结果集查询-程序员宅基地

文章浏览阅读140次。I'm having a strange time dealing with selecting from a table with about 30,000 rows.It seems my script is using an outrageous amount of memory for what is a simple, forward only walk over a query res..._pdo_mysql.cache_size

layui实现表格合并单元格,设置不同背景色_layui tablemerge 合并背景-程序员宅基地

文章浏览阅读1.0k次。最近在使用layui的过程中,遇到了表格合并单元格,设置不同底色的问https://www.hixiaoe.com/题。在此总结,大家一起学习。效果如下:同一组新闻的底色相同实现代码:<script> layui.config({ base: '/static/' //静态资源所在路径 }).extend({ index: 'admin/lib/index' //主入口模块 .._layui tablemerge 合并背景

kali2020.3详细安装教程|避免踩坑版_kali2020.3安装-程序员宅基地

文章浏览阅读3.8k次,点赞10次,收藏18次。Kali Linux系统作为白帽、黑帽最受欢迎的渗透测试系统,你如果是一个安全渗透专家或者网络安全管理员,必须要学会慎重并且合理地利用这个系统,因为对目标系统造成的实质伤害会带来法律的约束以及制裁!1、Kali Linux下载官网下载镜像:下载链接:https://www.kali.org2、安装配置我这里使用的虚拟机软件是 VMware 15,名字随便看需要,也可按默认配置建议选4G,也可以选2G内存默认,下一步默认,下一步默认,下一步默认,下一步这里建议将磁_kali2020.3安装

消息队列mysql redis那个好,redis可以做消息队列吗-程序员宅基地

文章浏览阅读131次。应用场景:例如秒杀。瞬时大量写入订单到数据库,导致数据库无法及时响应。此时可以采用Redis做消息队列,把所有需要写入的数据先写入Redis消息队列中,然后同时在服务器开启php-cli进程循环读取队列中的数据,异步写入数据库。使用redis做消息队列可能会出现消息丢失的情况,因为没有消息接收的确认机制。大型程序,应该使用类似RabitMQ来做专业消息队列。1、使用publish/subscrib..._mysql 做队列好还是redis做队列

linux环境安装apache服务器_no such command: nginx. please use /usr/bin/yum ---程序员宅基地

文章浏览阅读1.3k次。本文转自:https://www.cnblogs.com/wcwnina/p/8029156.htmlApache简介 Apache HTTP Server(简称Apache)是Apache软件基金会的一个开放源码的网页服务器,可以在大多数计算机操作系统中运行,由于其多平台和安全性被广泛使用,是最流行的Web服务器端软件之一。它快速、可靠并且可通过简单的API扩展,将..._no such command: nginx. please use /usr/bin/yum --help

LINUX-基础回顾_linux 粘贴之后 只做提示-程序员宅基地

文章浏览阅读560次。xShell:最常用的软件!远程操作linux,打开命令行终端!终端模拟软件。export LANG=“zh_CN.UTF-8” #中文export LANG=“en_US.UTF-8” #英文查看linux的ip地址:ifconfig;Linux无法使用ifconfig命令查看ip地址是因为没有安装net-tool所以执行下面的语句进行安装。_linux 粘贴之后 只做提示