基於tiny4412的Linux內核移植 -- DM9621NP網卡驅動移植(四)

来源:http://www.cnblogs.com/pengdonglin137/archive/2016/01/23/5153794.html
-Advertisement-
Play Games


作者信息

作者: 彭東林

郵箱:[email protected]

QQ:405728433

平臺簡介

開發板:tiny4412ADK + S700 + 4GB Flash

要移植的內核版本:Linux-4.4.0 (支持device tree)

u-boot版本:友善之臂自帶的 U-Boot 2010.12 (為支持uImage啟動,做了少許改動)

busybox版本:busybox 1.25

網卡晶元:DM9621NP

交叉編譯工具鏈: arm-none-linux-gnueabi-gcc

      (gcc version 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29))

概述

    tiny4412的網卡部分跟我之前的tq2440差別很大,在tq2440中使用網卡晶元是DM9000,使用的是記憶體介面,移植起來很容易,但是到了tiny4412就不一樣了,tiny4412使用的網卡晶元是DM9621,它是usb介面的,而且並沒有直接連接到exynos4412上,中間通過一個hub晶元usb4640,然後usb4640通過HSIC介面(XhsicSTROBE0和XhsicDATA0)連接到exynos4412上,下麵是一些參考資料:

  • HSI介紹

         http://www.cnblogs.com/pengdonglin137/p/5151331.html

         http://www.cnblogs.com/pengdonglin137/p/5151006.html

  • exynos4412手冊的第31章USB 2.0 Host Controller

image

 

image

 

  • 原理圖

image

上面這張圖就是tiny4412板子上的USB連接圖,我們重點關註DM9621和USB4640。

移植

    通過閱讀usb4640的晶元手冊,同時結合tiny4412的原理圖發現,usb4640的部分功能在tiny4412上是沒有用的,沒有外接的SPI Flash和I2C設備,大部分引腳都懸空了,除了數據傳輸的埠外,我們可以控制的就只剩下USB4640的複位引腳了,並且板子起來後,我用萬用表測量了usb4640的複位引腳的電壓,發現是0.7v左右,說明usb4640一直處於複位狀態,最終針對usb4640我們要做的就是只是板子起來後,將控制usb4640複位的引腳電平拉高即可,當然需要在設備樹中添加usb4640用到的GPIO資源。

    通過對比tiny4412自帶的linux-3.0.86版本的內核,對於DM9621的驅動在Linux 4.4中全部在driver/net/usb/dm9601.c中實現了,並且對於dm9601這樣的usb設備不需要出現在設備樹中。所以針對dm9601我們要做的就是:在內核配置時將DM9601的驅動選中即可,同時發現,為了支持識別bootargs中的mac地址的功能,需要稍微修改dm9601.c,添加解析mac地址的功能,並且還需要將解析到的有效的mac地址設置到dm9621中,否則網路沒法用(發現這個問題也廢了一些時間)。

    此外,最關鍵的是一定要在設備樹中將用到的片內外設使能:如hsi、ehci、ohci、otg等等。

下麵是patch:

   1: diff --git a/Makefile b/Makefile
   2: index 70dea02..5d96411 100644
   3: --- a/Makefile
   4: +++ b/Makefile
   5: @@ -248,8 +248,8 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
   6:  # "make" in the configured kernel build directory always uses that.
   7:  # Default value for CROSS_COMPILE is not to prefix executables
   8:  # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
   9: -ARCH        ?= $(SUBARCH)
  10: -CROSS_COMPILE    ?= $(CONFIG_CROSS_COMPILE:"%"=%)
  11: +ARCH        ?= arm
  12: +CROSS_COMPILE    ?= /root/tiny4412_android5/SysPort/cross_compile/arm-2014.05/bin/arm-none-linux-gnueabi-
  13:  
  14:  # Architecture as present in compile.h
  15:  UTS_MACHINE     := $(ARCH)
  16: diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts
  17: index 4840bbd..69a0d5d 100644
  18: --- a/arch/arm/boot/dts/exynos4412-tiny4412.dts
  19: +++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts
  20: @@ -14,6 +14,7 @@
  21:  /dts-v1/;
  22:  #include "exynos4412.dtsi"
  23:  #include <;dt-bindings/gpio/gpio.h>
  24: +#include <;dt-bindings/usb4640/usb4640.h>
  25:  
  26:  / {
  27:      model = "FriendlyARM TINY4412 board based on Exynos4412";
  28: @@ -21,6 +22,7 @@
  29:  
  30:      chosen {
  31:          stdout-path = &;serial_0;
  32: +        bootargs = "root=/dev/ram0 rw rootfstype=ext4 console=ttySAC0,115200 ethmac=1C:6F:65:34:51:7E init=/linuxrc";
  33:      };
  34:  
  35:      memory {
  36: @@ -68,6 +70,12 @@
  37:              clock-frequency = <;24000000>;
  38:          };
  39:      };
  40: +
  41: +    usb-hub {
  42: +        compatible = "smsc,usb4640";
  43: +        reset-gpios = <&gpm2 4 GPIO_ACTIVE_LOW>;
  44: +        initial-mode = <;USB4640_MODE_HUB>;
  45: +    };
  46:  };
  47:  
  48:  &;rtc {
  49: @@ -78,7 +86,7 @@
  50:      bus-width = <;4>;
  51:      pinctrl-0 = <;&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
  52:      pinctrl-names = "default";
  53: -    status = "okay";
  54: +    status = "disabled";
  55:  };
  56:  
  57:  &;serial_0 {
  58: @@ -96,3 +104,32 @@
  59:  &;serial_3 {
  60:      status = "okay";
  61:  };
  62: +
  63: +&;exynos_usbphy {
  64: +    status = "okay";
  65: +};
  66: +
  67: +&;ehci {
  68: +    status = "okay";
  69: +    port@0 {
  70: +        status = "okay";
  71: +    };
  72: +    port@1 {
  73: +        status = "okay";
  74: +    };
  75: +    port@2 {
  76: +        status = "okay";
  77: +    };
  78: +};
  79: +
  80: +&;ohci {
  81: +    status = "okay";
  82: +    port@0 {
  83: +        status = "okay";
  84: +    };
  85: +};
  86: +
  87: +&;hsotg {
  88: +    status = "okay";
  89: +};
  90: +
  91: diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
  92: index 0b4bdd3..4361385 100644
  93: --- a/drivers/net/usb/dm9601.c
  94: +++ b/drivers/net/usb/dm9601.c
  95: @@ -58,6 +58,39 @@
  96:  #define DM_RX_OVERHEAD    7    /* 3 byte header + 4 byte crc tail */
  97:  #define DM_TIMEOUT    1000
  98:  
  99: +/* Setup ethernet address */
 100: +static u8 param_addr[ETH_ALEN];
 101: +
 102: +static int __init dm9601_set_mac(char *str) {
 103: +    u8 addr[ETH_ALEN];
 104: +    uint val;
 105: +    int idx = 0;
 106: +    char *p = str, *end;
 107: +
 108: +    while (*p &;& idx < ETH_ALEN) {
 109: +        val = simple_strtoul(p, &;end, 16);
 110: +        if (end <;= p) {
 111: +            break;
 112: +        } else {
 113: +            addr[idx++] = val;
 114: +            p = end;
 115: +            if (*p == ':'|| *p == '-') {
 116: +                p++;
 117: +            } else {
 118: +                break;
 119: +            }
 120: +        }
 121: +    }
 122: +
 123: +    if (idx == ETH_ALEN) {
 124: +        printk("Setup ethernet address to %pM\n", addr);
 125: +        memcpy(param_addr, addr, ETH_ALEN);
 126: +    }
 127: +
 128: +    return 1;
 129: +}
 130: +__setup("ethmac=", dm9601_set_mac);
 131: +
 132:  static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
 133:  {
 134:      int err;
 135: @@ -190,8 +223,6 @@ static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value)
 136:      return dm_read_shared_word(dev, 0, offset, value);
 137:  }
 138:  
 139: -
 140: -
 141:  static int dm9601_get_eeprom_len(struct net_device *dev)
 142:  {
 143:      return DM_EEPROM_LEN;
 144: @@ -389,7 +420,11 @@ static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)
 145:      /*
 146:       * Overwrite the auto-generated address only with good ones.
 147:       */
 148: -    if (is_valid_ether_addr(mac))
 149: +    if (is_valid_ether_addr(param_addr)) {
 150: +        /* write MAC to dm9621 */
 151: +        memcpy(dev->;net->dev_addr, param_addr, ETH_ALEN);
 152: +        __dm9601_set_mac_address(dev);
 153: +    } else if (is_valid_ether_addr(mac))
 154:          memcpy(dev->;net->dev_addr, mac, ETH_ALEN);
 155:      else {
 156:          printk(KERN_WARNING
 157: diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
 158: index f7a7fc2..c1fcc2f 100644
 159: --- a/drivers/usb/misc/Kconfig
 160: +++ b/drivers/usb/misc/Kconfig
 161: @@ -249,6 +249,11 @@ config USB_HSIC_USB3503
 162:         help
 163:           This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver.
 164:  
 165: +config USB_HSIC_USB4640
 166: +       tristate "USB4640 HSIC to USB20 Driver"
 167: +       help
 168: +         This option enables support for SMSC USB4640 HSIC to USB 2.0 Driver.
 169: +
 170:  config USB_LINK_LAYER_TEST
 171:      tristate "USB Link Layer Test driver"
 172:      help
 173: diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
 174: index 45fd4ac..05b6344 100644
 175: --- a/drivers/usb/misc/Makefile
 176: +++ b/drivers/usb/misc/Makefile
 177: @@ -25,6 +25,7 @@ obj-$(CONFIG_USB_USS720)        += uss720.o
 178:  obj-$(CONFIG_USB_SEVSEG)        += usbsevseg.o
 179:  obj-$(CONFIG_USB_YUREX)            += yurex.o
 180:  obj-$(CONFIG_USB_HSIC_USB3503)        += usb3503.o
 181: +obj-$(CONFIG_USB_HSIC_USB4640)        += usb4640.o
 182:  obj-$(CONFIG_USB_CHAOSKEY)        += chaoskey.o
 183:  
 184:  obj-$(CONFIG_USB_SISUSBVGA)        += sisusbvga/
 185: diff --git a/drivers/usb/misc/usb4640.c b/drivers/usb/misc/usb4640.c
 186: new file mode 100644
 187: index 0000000..9c9c01b
 188: --- /dev/null
 189: +++ b/drivers/usb/misc/usb4640.c
 190: @@ -0,0 +1,154 @@
 191: +/*
 192: + * Driver for SMSC USB4640 USB 2.0 hub controller driver
 193: + *
 194: + */
 195: +
 196: +#include <;linux/gpio.h>
 197: +#include <;linux/delay.h>
 198: +#include <;linux/slab.h>
 199: +#include <;linux/module.h>
 200: +#include <;linux/of_gpio.h>
 201: +#include <;linux/platform_device.h>
 202: +#include <;linux/platform_data/usb4640.h>
 203: +
 204: +struct usb4640 {
 205: +    enum usb4640_mode    mode;
 206: +    struct device        *dev;
 207: +    int    gpio_reset;
 208: +};
 209: +
 210: +static int usb4640_reset(struct usb4640 *hub, int state)
 211: +{
 212: +    if (gpio_is_valid(hub->;gpio_reset))
 213: +        gpio_set_value_cansleep(hub->;gpio_reset, state);
 214: +
 215: +    /* Wait 1ms for hub logic to stabilize */
 216: +    if (state)
 217: +        usleep_range(1, 10);
 218: +
 219: +    return 0;
 220: +}
 221: +
 222: +static int usb4640_connect(struct usb4640 *hub)
 223: +{
 224: +    struct device *dev = hub->;dev;
 225: +
 226: +    usb4640_reset(hub, 1);
 227: +    hub->mode = USB4640_MODE_HUB;
 228: +    dev_info(dev, "switched to HUB mode\n");
 229: +
 230: +    return 0;
 231: +}
 232: +
 233: +static int usb4640_switch_mode(struct usb4640 *hub, enum usb4640_mode mode)
 234: +{
 235: +    struct device *dev = hub->;dev;
 236: +    int err = 0;
 237: +
 238: +    switch (mode) {
 239: +    case USB4640_MODE_HUB:
 240: +        err = usb4640_connect(hub);
 241: +        break;
 242: +
 243: +    case USB4640_MODE_STANDBY:
 244: +        usb4640_reset(hub, 0);
 245: +        dev_info(dev, "switched to STANDBY mode\n");
 246: +        break;
 247: +
 248: +    default:
 249: +        dev_err(dev, "unknown mode is requested\n");
 250: +        err = -EINVAL;
 251: +        break;
 252: +    }
 253: +
 254: +    return err;
 255: +}
 256: +
 257: +
 258: +static int usb4640_probe(struct usb4640 *hub)
 259: +{
 260: +    struct device *dev = hub->;dev;
 261: +    struct usb4640_platform_data *pdata = dev_get_platdata(dev);
 262: +    struct device_node *np = dev->of_node;
 263: +    int err;
 264: +    u32 mode = USB4640_MODE_HUB;
 265: +
 266: +    if (pdata) {
 267: +        hub->;gpio_reset        = pdata->gpio_reset;
 268: +        hub->;mode        = pdata->initial_mode;
 269: +    } else if (np) {
 270: +        hub->;gpio_reset = of_get_named_gpio(np, "reset-gpios", 0);
 271: +        if (hub->;gpio_reset == -EPROBE_DEFER)
 272: +            return -EPROBE_DEFER;
 273: +        of_property_read_u32(np, "initial-mode", &mode);
 274: +        hub->;mode = mode;
 275: +    }
 276: +
 277: +    if (gpio_is_valid(hub->;gpio_reset)) {
 278: +        err = devm_gpio_request_one(dev, hub->;gpio_reset,
 279: +                GPIOF_OUT_INIT_LOW, "usb4640 reset");
 280: +        if (err) {
 281: +            dev_err(dev,
 282: +                "unable to request GPIO %d as reset pin (%d)\n",
 283: +                hub->;gpio_reset, err);
 284: +            return err;
 285: +        }
 286: +    }
 287: +
 288: +    usb4640_switch_mode(hub, hub->;mode);
 289: +
 290: +    dev_info(dev, "%s: probed in %s mode\n", __func__,
 291: +            (hub->;mode == USB4640_MODE_HUB) ? "hub" : "standby");
 292: +
 293: +    return 0;
 294: +}
 295: +
 296: +static int usb4640_platform_probe(struct platform_device *pdev)
 297: +{
 298: +    struct usb4640 *hub;
 299: +
 300: +    hub = devm_kzalloc(&;pdev->dev, sizeof(struct usb4640), GFP_KERNEL);
 301: +    if (!hub)
 302: +        return -ENOMEM;
 303: +    hub->dev = &pdev->dev;
 304: +
 305: +    return usb4640_probe(hub);
 306: +}
 307: +
 308: +#ifdef CONFIG_OF
 309: +static const struct of_device_id usb4640_of_match[] = {
 310: +    { .compatible = "smsc,usb4640", },
 311: +    {},
 312: +};
 313: +MODULE_DEVICE_TABLE(of, usb4640_of_match);
 314: +#endif
 315: +
 316: +static struct platform_driver usb4640_platform_driver = {
 317: +    .driver = {
 318: +        .name = USB4640_NAME,
 319: +        .of_match_table = of_match_ptr(usb4640_of_match),
 320: +    },
 321: +    .probe        = usb4640_platform_probe,
 322: +};
 323: +
 324: +static int __init usb4640_init(void)
 325: +{
 326: +    int err;
 327: +
 328: +    err = platform_driver_register(&;usb4640_platform_driver);
 329: +    if (err != 0)
 330: +        pr_err("usb4640: Failed to register platform driver: %d\n",
 331: +               err);
 332: +
 333: +    return 0;
 334: +}
 335: +module_init(usb4640_init);
 336: +
 337: +static void __exit usb4640_exit(void)
 338: +{
 339: +    platform_driver_unregister(&;usb4640_platform_driver);
 340: +}
 341: +module_exit(usb4640_exit);
 342: +
 343: +MODULE_DESCRIPTION("USB4640 USB HUB driver");
 344: +MODULE_LICENSE("GPL");
 345: diff --git a/include/dt-bindings/usb4640/usb4640.h b/include/dt-bindings/usb4640/usb4640.h
 346: new file mode 100644
 347: index 0000000..ef3e1e1
 348: --- /dev/null
 349: +++ b/include/dt-bindings/usb4640/usb4640.h
 350: @@ -0,0 +1,7 @@
 351: +#ifndef _DT_BINDINGS_USB4640
 352: +#define _DT_BINDINGS_USB4640
 353: +
 354: +#define USB4640_MODE_UNKNOWN   1
 355: +#define USB4640_MODE_HUB       2
 356: +#define USB4640_MODE_STANDBY   3
 357: +#endif
 358: diff --git a/include/linux/platform_data/usb4640.h b/include/linux/platform_data/usb4640.h
 359: new file mode 100644
 360: index 0000000..5a416ab
 361: --- /dev/null
 362: +++ b/include/linux/platform_data/usb4640.h
 363: @@ -0,0 +1,17 @@
 364: +#ifndef __USB4640_H__
 365: +#define __USB4640_H__
 366: +
 367: +#define USB4640_NAME    "usb4640"
 368: +
 369: +enum usb4640_mode {
 370: +    USB4640_MODE_UNKNOWN = 1,
 371: +    USB4640_MODE_HUB,
 372: +    USB4640_MODE_STANDBY,
 373: +};
 374: +
 375: +struct usb4640_platform_data {
 376: +    enum usb4640_mode    initial_mode;
 377: +    int    gpio_reset;
 378: +};
 379: +
 380: +#endif

 

測試

用ramdisk啟動內核,可以看到如下log

[    2.690100] usb 2-2.2: new full-speed USB device number 4 using exynos-ehci
[    2.795595] usb 2-2.2: not running at top speed; connect to a high speed hub
[    2.869980] usb 2-2.2: New USB device found, idVendor=0a46, idProduct=9621
[    2.870147] usb 2-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[    2.883960] dm9601 2-2.2:1.0 eth0: register 'dm9601' at usb-12580000.ehci-2.2, Davicom DM96xx USB 10/100 Ethernet, 1c:6f:65:34:51:7e

執行如下命令:

ifconfig eth0 192.168.1.8

我的開發機使用橋接的方式連接到物理網卡上,ip是192.168.1.10,在板子上執行如下命令:

ping 192.168.1.10

看到如下log:

   1: [root@tiny4412 ]# ping 192.168.1.10
   2: PING 192.168.1.10 (192.168.1.10): 56 data bytes
   3: 64 bytes from 192.168.1.10: seq=0 ttl=64 time=1.354 ms
   4: 64 bytes from 192.168.1.10: seq=1 ttl=64 time=1.210 ms
   5: 64 bytes from 192.168.1.10: seq=2 ttl=64 time=1.550 ms
   6: 64 bytes from 192.168.1.10: seq=3 ttl=64 time=2.336 ms
   7: 64 bytes from 192.168.1.10: seq=4 ttl=64 time=1.612 ms
   8: ^C
   9: --- 192.168.1.10 ping statistics ---
  10: 5 packets transmitted, 5 packets received, 0% packet loss
  11: round-trip min/avg/max = 1.210/1.612/2.336 ms

可以將配置IP的命令寫到/etc/profile中,就不用每次都配置ip了。

    下麵我們看看系統的GPIO使用情況,再次之前需要掛載debugfs文件系統到/sys/kernel/debug下,修改配置文件/etc/fstab (需要在開發機上修改,然後重新製作ramdisk鏡像),添加一行:

debugfs        /sys/kernel/debug    debugfs        defaults    0    0

然後重新製作生成ramdisk.img,啟動系統,執行如下命令:

   1:  [root@tiny4412 ]# mount
   2:  /dev/root on / type ext4 (rw,relatime,data=ordered)
   3:  devtmpfs on /dev type devtmpfs (rw,relatime,size=480388k,nr_inodes=120097,mode=755)
   4:  proc on /proc type proc (rw,relatime)
   5:  tmpfs on /tmp type tmpfs (rw,relatime)
   6:  sysfs on /sys type sysfs (rw,relatime)
   7:  debugfs on /sys/kernel/debug type debugfs (rw,relatime)
   8:  devpts on /dev/pts type devpts (rw,relatime,mode=600)
   9:   
  10:  [root@tiny4412 ]# cat /sys/kernel/debug/gpio 
  11:  ......
  12:   
  13:  GPIOs 128-135, platform/11000000.pinctrl, gpm0:
  14:   
  15:  GPIOs 136-142, platform/11000000.pinctrl, gpm1:
  16:   
  17:  GPIOs 143-147, platform/11000000.pinctrl, gpm2:
  18:   gpio-147 (                    |usb4640 reset       ) out hi    
  19:   
  20:  GPIOs 148-155, platform/11000000.pinctrl, gpm3:
  21:   
  22:  GPIOs 156-163, platform/11000000.pinctrl, gpm4:
  23:   gpio-156 (                    |?                   ) out hi    
  24:   gpio-157 (                    |?                   ) out hi    
  25:   gpio-158 (                    |?                   ) out hi    
  26:   gpio-159 (                    |?                   ) out hi    
  27:   
  28:  GPIOs 164-169, platform/11000000.pinctrl, gpy0:
  29:   
  30:  ......

可以看到控制usb4640的gpio資源狀態以及控制led燈的gpio狀態。

    下麵我們執行命令,將開發機上的一個共用目錄用nfs掛載到tiny4412上:

   1:  [root@tiny4412 ]# ls /mnt/
   2:  [root@tiny4412 ]# mount -t nfs -o nolock 192.168.1.10:/nfs_rootfs/rootfs /mnt
   3:

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本篇體驗使用AngularJS中的$http對MongoLab數據表進行增刪改查。主頁面:Load CourseAdd New Course以上,頁面上顯示course_list.html,add_course.html和edit_course.html的內容顯示與toggleAddCourseVi...
  • 通過一個例子介紹如何將前端模塊管理利器Webpack用在Angular項目中。
  • 快速創建對象的時候系統會自動釋放一次,如:NSDictionary *dic =[NSDictionary dictionary]; //此時系統自動自動釋放一次記憶體,對象引用計數就 ' -1 '創建(複製)不可變的對象用 copy;創建(複製)可變的對象用 MutableCopy;frame:是以...
  • 現象:The identity used to sign the executable is no longer validPlease verify that your device’s clock is properly set, and that your signing certificat...
  • 一直以來被Linux的hostname和fqdn(Fully Qualified Domain Name)困惑了好久,今天專門抽時間把它們的使用細節弄清了。 一、設置hostname/fqdn 在Linux系統內設置hostname很簡單,如: $ hostname florian 如果...
  • Exynos4412時鐘域分類圖1:
  • 一、關閉SELinux安全增強型Linux(SELinux)的是一個Linux內核的功能,它提供支持訪問控制的安全政策保護機制。但是,SELinux帶來的附加安全性和使用複雜性上不成比例,性價比不高sed -i /SELINUX=enforcing/SELINUX=disabled/ /etc/se...
  • 前言只是作為一個shell的小小練習和日常統計用,瞎折騰的過程中也是摸到了獲取子shell返回值的幾種方法;肯定還有別的方法,跟進程間的通信相關,希望你能提出建議和補充,謝謝~ 完整程式: bash ! /bin/bash 檢查參數數量是否符合要求if [ $ ne 1 ]then echo Ho....
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...