Linux驱动之platform设备驱动

目录

前言

一、Linux驱动的分离与分层

二、开发环境

三、驱动程序编写

3.2 platform 驱动模块程序

3.3 测试app程序

四、运行测试

4.1 编译

4.2 运行测试


前言

        前面几章编写的设备驱动都非常的简单,都是对 IO进行最简单的读写操作。像 I2C、SPI、 LCD 等这些复杂外设的驱动就不能这么去写了, Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路,在这个思路下诞生了我们将来最常打交道的platform 设备驱动,也叫做平台设备驱动。本章我们就来学习一下 Linux 下的驱动分离与分层,以及 platform 框架下的设备驱动该如何编写。

一、Linux驱动的分离与分层

Linux驱动的分离与分层在这就不再进行详细讲解了,如果还不了解的,可以去查看之前的文章《Linux驱动之软件架构思想》,链接如下:

Linux驱动之软件架构思想

二、开发环境

  • CPU:IMX6ULL

  • 内核版本:Linux-5.19

三、驱动程序编写

        本次我们需要编写一个驱动模块和一个设备模块,其中驱动模块是 platform 驱动程序,设备模块是 platform 的设备信息。当这两个模块都加载成功以后就会匹配成功,然后 platform驱动模块中的 probe 函数就会执行, probe 函数中就是传统的字符设备驱动那一套。

3.1 platform 设备模块程序

        在 leddevice.c 中输入如下所示内容:

/************************************************************
 * Copyright © toto Co., Ltd. 1998-2029. All rights reserved.
 * Description: 
 * Version: 1.0
 * Autor: toto
 * Date: Do not edit
 * LastEditors: Seven
 * LastEditTime: Do not edit
*************************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/irq.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>

#define LEDDEV_CNT  1               /* 设备号数量 */
#define LEDDEV_NAME "platform_led"  /* 设备名字 */
#define LED_ON      1
#define LED_OFF     0

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *VM_CCM_CCGR1;
static void __iomem *VM_SW_MUX_GPIO1_IO03;
static void __iomem *VM_SW_PAD_GPIO1_IO03;
static void __iomem *VM_GPIO1_DR;
static void __iomem *VM_GPIO1_GDIR;

/* led_dev 设备结构体 */
struct led_dev {
    dev_t devid;            /* 设备号 */
    struct cdev cdev;       /* cdev */
    struct class *class;    /* 类 */
    struct device *device;  /* 设备 */
    int major;              /* 主设备号 */
};

struct led_dev leddev; /* led 设备 */

/*
 * @Brief   led 打开、关闭接口
 * @Param   sta:1打开,0关闭
 * @Note    NOne
 * @RetVal  NOne
 */
void led_switch(u8 sta)
{
    u32 val = 0;

    if (sta == LED_ON) {
        val = readl(VM_GPIO1_DR);
        val &= ~(1 << 3);
        writel(val, VM_GPIO1_DR);
    } else if (sta == LED_OFF) {
        val |= (1 << 3);
        writel(val, VM_GPIO1_DR);
    }
}

/*
 * @Brief   打开设备
 * @Param   inode:传递给驱动的inode
 * @Param   filp:设备文件
 * @Note    NOne
 * @RetVal  NOne
 */
static int led_open(struct inode *inode, struct file *filp)
{
    /* 设置私有数据 */
    filp->private_data = &leddev;

    return 0;
}

/*
 * @Brief   向设备写数据
 * @Param   filp:设备文件
 * @Param   buf:要写入设备的数据
 * @Param   cnt:要写入的数据长度
 * @Param   offt:相对于文件首地址的偏移
 * @Note    NOne
 * @RetVal  写入的字节数,如果为负值,表示写入失败
 */
static ssize_t led_write(struct file *filp, const char __user *buf,
                        size_t cnt, loff_t *offt)
{
    int ret;
    unsigned char databuf[1];
    unsigned char ledstat;

    ret = copy_from_user(databuf, buf, cnt);
    if (ret < 0) {
        return -EFAULT;
    }

    ledstat = databuf[0];
    led_switch(ledstat);

    return 0;
}

/* 设备操作函数 */
static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open  = led_open,
    .write = led_write, 
};

/*
 * @Brief   platform 驱动的probe函数,当驱动与设备
            匹配后此函数就会执行
 * @Param   platform 设备
 * @Note    NOne
 * @RetVal  0成功,其他值失败
 */
static int led_probe(struct platform_device *dev)
{
    int i = 0;
    int ressize[5];
    u32 val = 0;
    struct  resource *ledsource[5];
  
    printk(KERN_INFO "led driver and device has matched\n");

    for (i = 0; i < 5; i++) {
        ledsource[i] = platform_get_resource(dev, IORESOURCE_MEM, i);
        if (!ledsource[i]) {
            dev_err(&dev->dev, "NO MEM\n");
            return -ENXIO;
        }
        ressize[i] = resource_size(ledsource[i]);
    }

    /* 初始化 led */
    /* 寄存器映射 */
    VM_CCM_CCGR1 = ioremap(ledsource[0]->start, ressize[0]);
    VM_SW_MUX_GPIO1_IO03 = ioremap(ledsource[1]->start, ressize[1]);
    VM_SW_PAD_GPIO1_IO03 = ioremap(ledsource[2]->start, ressize[2]);
    VM_GPIO1_DR = ioremap(ledsource[3]->start, ressize[3]);
    VM_GPIO1_GDIR = ioremap(ledsource[4]->start, ressize[4]);

    val = readl(VM_CCM_CCGR1);
    val |= (1 << 3);
    writel(val, VM_CCM_CCGR1);

    /* 设置GPIO1_IO03 复用功能 */
    writel(5, VM_SW_MUX_GPIO1_IO03);
    writel(0x10B0, VM_SW_PAD_GPIO1_IO03);

    /* 默认关闭 led */
    val = readl(VM_GPIO1_GDIR);
    val |= (1 << 3);
    writel(val, VM_GPIO1_GDIR);

    /* 注册字符设备驱动 */
    /* 1.创建设备号 */
    if (leddev.major) {
        leddev.devid = MKDEV(leddev.major, 0);
        register_chrdev_region(leddev.devid, LEDDEV_CNT,
                                LEDDEV_NAME);
    } else {
        alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT,
                            LEDDEV_NAME);
        leddev.major = MAJOR(leddev.devid);
    }

    /* 2.初始化cdev */
    leddev.cdev.owner = THIS_MODULE;
    cdev_init(&leddev.cdev, &led_fops);

    /* 3.添加cdev */
    cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);

    /* 4.创建类 */
    leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
    if (IS_ERR(leddev.class)) {
        return PTR_ERR(leddev.class);
    }

    /* 5.创建设备 */
    leddev.device = device_create(leddev.class, NULL, leddev.devid,
                                    NULL, LEDDEV_NAME);
    if (IS_ERR(leddev.device)) {
        return PTR_ERR(leddev.device);
    }

    return 0;
}

/*
 * @Brief   移除 platform 驱动函数
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static int led_remove(struct platform_device *dev)
{
    iounmap(VM_CCM_CCGR1);
    iounmap(VM_SW_MUX_GPIO1_IO03);
    iounmap(VM_SW_PAD_GPIO1_IO03);
    iounmap(VM_GPIO1_DR);
    iounmap(VM_GPIO1_GDIR);

    cdev_del(&leddev.cdev);
    unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
    device_destroy(leddev.class, leddev.devid);
    class_destroy(leddev.class);

    return 0;
}

/* platform 驱动结构体 */
static struct platform_driver led_driver = {
    .driver = {
        .name = "imx6ull-led", /* 驱动名字,用于匹配设备 */
    },
    .probe = led_probe,
    .remove = led_remove,
};

/*
 * @Brief   驱动模块加载函数
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static int __init leddriver_init(void)
{
    return platform_driver_register(&led_driver);
}

/*
 * @Brief   驱动模块卸载函数
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static void __exit leddriver_exit(void)
{
    platform_driver_unregister(&led_driver);
}

module_init(leddriver_init);
module_exit(leddriver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("toto");
  • 第 56~82 行, led_resources 数组,也就是设备资源,描述了 LED 所要使用到的寄存器信息,也就是 IORESOURCE_MEM 资源。

  • 第 88~96, platform 设备结构体变量 leddevice,这里要注意 name 字段为“imx6ul-led”,所以稍后编写 platform 驱动中的 name 字段也要为“imx6ul-led”,否则设备和驱动匹配失败。

  • 第 103~106 行,设备模块加载函数,在此函数里面通过 platform_device_register 向 Linux 内核注册 leddevice 这个 platform 设备。

  • 第 113~116 行,设备模块卸载函数,在此函数里面通过 platform_device_unregister 从 Linux内核中删除掉 leddevice 这个 platform 设备。

3.2 platform 驱动模块程序

        在 leddriver.c 里面输入如下内容:

/***********************************************************
 * Copyright © toto Co., Ltd. 1998-2029. All rights reserved.
 * Description: 
 * Version: 1.0
 * Autor: toto
 * Date: Do not edit
 * LastEditors: Seven
 * LastEditTime: Do not edit
***********************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/irq.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>

#define CCM_CCGR1_REGBASE           (0x020C406C)
#define SW_MUX_GPIO1_IO03_REGBASE   (0x020E0068)
#define SW_PAD_GPIO1_IO03_REGBASE   (0x020E02F4)
#define GPIO1_DR_REGBASE            (0x0209C000)
#define GPIO1_GDIR_REGBASE          (0x0209C004)
#define REGISTER_LENGTH             4

/*
 * @Brief   释放platform设备模块的函数
 * @Param   dev:要释放的设备
 * @Note    NOne
 * @RetVal  NOne
 */
static void led_release(struct device *dev)
{
    printk("led device released\n");
}

/*
*设备资源信息,LED使用的所有寄存器
*/
static struct resource led_resource[] = {
    [0] = {
        .start = CCM_CCGR1_REGBASE,
        .end   = (CCM_CCGR1_REGBASE + REGISTER_LENGTH - 1),
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = SW_MUX_GPIO1_IO03_REGBASE,
        .end   = (SW_MUX_GPIO1_IO03_REGBASE + REGISTER_LENGTH -1),
        .flags = IORESOURCE_MEM,
    },
    [2] = {
        .start = SW_PAD_GPIO1_IO03_REGBASE,
        .end   = (SW_PAD_GPIO1_IO03_REGBASE + REGISTER_LENGTH - 1),
        .flags = IORESOURCE_MEM,
    },
    [3] = {
        .start = GPIO1_DR_REGBASE,
        .end   = (GPIO1_DR_REGBASE + REGISTER_LENGTH - 1),
        .flags = IORESOURCE_MEM,
    },
    [4] = {
        .start = GPIO1_GDIR_REGBASE,
        .end   = (GPIO1_GDIR_REGBASE + REGISTER_LENGTH -1),
        .flags = IORESOURCE_MEM,
    },
};

/*
 * platform设备结构体
 */
static struct platform_device leddev = {
    .name = "imx6ull-led",
    .id = -1,
    .dev = {
        .release = led_release,
    },
    .num_resources = ARRAY_SIZE(led_resource),
    .resource = led_resource,
};

/*
 * @Brief   设备模块加载
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static int __init leddev_init(void)
{
    return platform_device_register(&leddev);
}

/*
 * @Brief   设备模块注销
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static void __exit leddev_exit(void)
{
    platform_device_unregister(&leddev);
}

module_init(leddev_init);
module_exit(leddev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("toto");
  • 第 34~122 行,传统的字符设备驱动。

  • 第 130~206 行, probe 函数,当设备和驱动匹配以后此函数就会执行,当匹配成功以后会在终端上输出“led driver and device has matched!”这样语句。在 probe 函数里面初始化 LED、注册字符设备驱动。也就是将原来在驱动加载函数里面做的工作全部放到 probe 函数里面完成。

  • 第 213~226 行, remobe 函数,当卸载 platform 驱动的时候此函数就会执行。在此函数里面释放内存、注销字符设备等。也就是将原来驱动卸载函数里面的工作全部都放到 remove 函数中完成。

  • 第 229~235 行, platform_driver 驱动结构体,注意 name 字段为"imx6ul-led",和我们在leddevice.c 文件里面设置的设备 name 字段一致。

  • 第 242~245 行,驱动模块加载函数,在此函数里面通过 platform_driver_register 向 Linux 内核注册 led_driver 驱动。

  • 第 252~255 行,驱动模块卸载函数,在此函数里面通过 platform_driver_unregister 从 Linux内核卸载 led_driver 驱动。

3.3 测试app程序

        在 platform_app.c 里面输入如下内容:

/********************************************
 * Copyright © toto Co., Ltd. 1998-2029. All rights reserved.
 * Description: 
 * Version: 1.0
 * Autor: toto
 * Date: Do not edit
 * LastEditors: Seven
 * LastEditTime: Do not edit
********************************************/
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

#define LEDON   1
#define LEDOFF  0 

/*
 * @Brief
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
int main(int argc, char *argv[])
{
    int fd, retval;
    char *filename;
    unsigned char databuf[1];

    if (argc != 3) {
        printf("Error argc par cnt\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("file %s open failed\n", filename);
        return -1;
    }

    databuf[0] = atoi(argv[2]);
    retval = write(fd, databuf, sizeof(databuf));
    if (retval < 0) {
        printf("led control failed\n");
        close(fd);
        return -1;
    }

    close(fd);

    return 0;
}

四、运行测试

4.1 编译

1.编译驱动程序 编写 Makefile 文件,Makefile 内容如下所示:

KERNELDIR := /home/toto/workspace/linux/linux-5.19
CURRENT_PATH := $(shell pwd)
obj-m := leddriver.o
obj-m += leddevice.o

build: kernel_modules

kernel_modules:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

编译命令:

make -j8

编译成功以后就会生成一个名为“leddevice.ko leddriver.ko”的驱动模块文件。

2. 编译测试app 编译命令:

arm-linux-gnueabihf-gcc platform_app.c -o platform_app

编译成功以后就会生成 platform_app 这个应用程序。

4.2 运行测试

        开发板上电,将leddevice.ko、leddriver.ko 和 key_app 这三个文件拷贝到 /lib/modules/5.19.0-g794a2f7be62d-dirty/ 目录中,输入如下命令加载 leddevice.ko 设备模块和 leddriver.ko 这个驱动模块:

insmod leddevice.ko 
insmod leddriver.ko

        根文件系统中/sys/bus/platform/目录下保存着当前板子 platform 总线下的设备和驱动,其中devices 子目录为 platform 设备, drivers 子目录为 plartofm 驱动。查看/sys/bus/platform/devices/目录,看看我们的设备是否存在,我们在 leddevice.c 中设置 leddevice(platform_device 类型)的name 字段为“imx6ull-led”,也就是设备名字为 imx6ull-led,因此肯定在/sys/bus/platform/devices/目录下存在一个名字“imx6ull-led”的文件,否则说明我们的设备模块加载失败,结果如下所示:

        同理,查看/sys/bus/platform/drivers/目录,看一下驱动是否存在,我们在 leddriver.c 中设置led_driver (platform_driver 类型)的 name 字段为“imx6ull-led”,因此会在/sys/bus/platform/drivers/目录下存在名为“imx6ull-led”这个文件,结果如下所示:

        驱动模块和设备模块加载成功以后 platform 总线就会进行匹配,当驱动和设备匹配成功以后就会输出如下所示一行语句:

/lib/modules/5.19.0-g794a2f7be62d-dirty # insmod leddevice.ko
/lib/modules/5.19.0-g794a2f7be62d-dirty # insmod leddriver.ko
[   45.657342] led driver and device has matched

驱动和设备匹配成功以后就可以测试 LED 灯驱动了,输入如下命令打开 LED 灯:

./platform_app /dev/platform_led 1

在输入如下命令关闭 LED 灯:

./platform_app /dev/platform_led 0

卸载驱动命令如下:

rmmod leddevice.ko
rmmod leddriver.ko

        关于更多嵌入式C语言、FreeRTOS、RT-Thread、Linux应用编程、linux驱动等相关知识,关注公众号【嵌入式Linux知识共享】,后续精彩内容及时收看了解。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/91923.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ansible 创建使用角色

使用 Ansible Galaxy 和要求文件 /ansible/roles/requirements.yml 。从以下 URL 下载角色并安装到 /ansible/roles &#xff1a; http://materials/haproxy.tar 此角色的名称应当为 balancer http://materials/phpinfo.tar 此角色的名称应当为 phpinfo #创建 vim /ansible/r…

leetcode3. 无重复字符的最长子串(滑动窗口 - java)

滑动窗口 无重复字符的最长子串滑动窗口 上期经典 无重复字符的最长子串 难度 - 中等 3. 无重复字符的最长子串 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc…

MySQL详细安装与配置

免安装版的Mysql MySQL关是一种关系数据库管理系统&#xff0c;所使用的 SQL 语言是用于访问数据库的最常用的 标准化语言&#xff0c;其特点为体积小、速度快、总体拥有成本低&#xff0c;尤其是开放源码这一特点&#xff0c;在 Web 应用方面 MySQL 是最好的 RDBMS(Relation…

postman访问ruoyi后台接口

打开若依页面&#xff0c;登录进去&#xff0c;F12打开控制台&#xff0c;选一个后台服务&#xff0c;把下图两个节点&#xff0c;补到postman请求header里面即可

Day46|leetcode 139.单词拆分

leetcode 139.单词拆分 题目链接&#xff1a;139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; 视频链接&#xff1a;动态规划之完全背包&#xff0c;你的背包如何装满&#xff1f;| LeetCode&#xff1a;139.单词拆分_哔哩哔哩_bilibili 题目概述 给你一个字符串 s 和一…

关于ios Universal Links apple-app-site-association文件 Not Found的问题

1. 背景说明 1.1 Universal Links 是什么 Support Universal Links 里面有说到 Universal Links 是什么、注意点、以及如何配置的。简单来说就是 当您支持通用链接时&#xff0c;iOS 用户可以点击指向您网站的链接&#xff0c;并无缝重定向到您安装的应用程序 大白话就是说&am…

【算法训练-链表】反转链表、区间反转链表、K个一组反转链表

从今天开始进行高频算法的训练&#xff0c;一方面训练自己的逻辑思维&#xff0c;一方面保持自己的竞争力。训练过程有这么两个基准原则&#xff1a; 首先训练题的来源呢有三个&#xff0c;首选的是三个都出现过的高频题&#xff0c;以&#xff1a;牛客101为基准分类&#xff…

MAYA粒子基础_场

重力场 牛顿场 径向场 均匀场和重力场的区别 空气场 推动物体 阻力场 推动物体 涡流场 湍流场 体积轴场

研磨设计模式day12命令模式

目录 定义 几个参数 场景描述 代码示例 参数化设置 命令模式的优点 本质 何时选用 定义 几个参数 Command&#xff1a;定义命令的接口。 ConcreteCommand:命令接口的实现对象。但不是真正实现&#xff0c;是通过接收者的功能来完成命令要执行的操作 Receiver&#x…

kafka-python 消费者消费不到消息

排除步骤1&#xff1a; 使用group_id”consumer_group_id_001“ 和 auto_offset_reset"earliest" from kafka import KafkaConsumerconsumer KafkaConsumer(bootstrap_servers["dev-kafka01.test.xxx.cloud:9092"],enable_auto_commitTrue, auto_commit…

计算机安全学习笔记(I):访问控制安全原理

访问控制原理 从广义上来讲&#xff0c;所有的计算机安全都与访问控制有关。 RFC 4949: Internet Security Glossary, Version 2 (rfc-editor.org) RFC 4949 定义的计算机安全&#xff1a;用来实现和保证计算机系统的安全服务的措施&#xff0c;特别是保证访问控制服务的措施…

十几款拿来就能用的炫酷表白代码

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 表白代码 1、坐我女朋友好吗&#xff0c;不同意就关机.vbs2、坐我女朋友好吗&…

使用 Transformer 和 Amazon OpenSearch Service 构建基于列的语义搜索引擎

在数据湖中&#xff0c;对于数据清理和注释、架构匹配、数据发现和跨多个数据来源进行分析等许多操作&#xff0c;查找相似的列有着重要的应用。如果不能从多个不同的来源准确查找和分析数据&#xff0c;就会严重拉低效率&#xff0c;不论是数据科学家、医学研究人员、学者&…

C# 实现 国密SM4/ECB/PKCS7Padding对称加密解密

C# 实现 国密SM4/ECB/PKCS7Padding对称加密解密&#xff0c;为了演示方便本问使用的是Visual Studio 2022 来构建代码的 1、新建项目&#xff0c;之后选择 项目 鼠标右键选择 管理NuGet程序包管理&#xff0c;输入 BouncyCastle 回车 添加BouncyCastle程序包 2、代码如下&am…

npm和yarn的区别?

文章目录 前言npm和yarn的作用和特点npm和yarn的安装的机制npm安装机制yarn安装机制检测包解析包获取包链接包构建包 总结后言 前言 这一期给大家讲解npm和yarn的一些区别 npm和yarn的作用和特点 包管理&#xff1a;npm 和 yarn 可以用于安装、更新和删除 JavaScript 包。它们提…

识别图片中的文字

前言 PearOCR 是一款免费无限制网页版文字识别工具。 优点如下&#xff1a; 免费&#xff1a;完全免费&#xff0c;没有任何次数、大小限制&#xff0c;可以无限使用&#xff1b; 安全&#xff1a;全部数据本地运算&#xff0c;所有图片均不会被上传&#xff1b; 智能&#xf…

市场的新宠:4G智能手表

现在人们提到智能手表&#xff0c;健康监测、运动记录、接打电话等定是他不可或缺的功能&#xff0c;而其中通讯功能在绝大数多的智能手表上都是通过蓝牙实现的&#xff0c;需要让手表通过蓝牙连接到手机端来进行。在没有手机的情况下&#xff0c;配置再高的蓝牙智能手表也是“…

Kubernetes入门 十、HPA 自动扩/缩容

目录 概述安装metrics-server使用HPA 概述 我们已经可以通过手动执行 kubectl scale 命令实现Pod的扩缩容&#xff0c;但是这显然不符合 Kubernetes 的定位目标–自动化和智能化。Kubernetes 期望可以通过监测Pod的使用情况&#xff0c;实现 Pod 数量的自动调整&#xff0c;于…

成都瀚网科技:抖店如何经营?

作为热门的短视频分享平台&#xff0c;抖音不仅是一种娱乐工具&#xff0c;更是一个蕴藏着无限商机的电商平台。开店、抖音下单成为很多人的选择。那么&#xff0c;抖音如何开店、下单呢&#xff1f; 1、如何在抖音上开店和下单&#xff1f; 注册账号&#xff1a;首先&#xff…

5.网络原理之初识

文章目录 1.网络发展史1.1独立模式1.2网络互连1.3局域网LAN1.3.1基于网线直连1.3.2基于集线器组建1.3.3基于交换机组建1.3.4基于交换机和路由器组建1.3.4.1路由器和交换机区别 1.4广域网WAN 2.网络通信基础2.1IP地址2.2端口号2.3认识协议2.4五元组2.5 协议分层2.5.1 分层的作用…