【源码】基于I.MX6ull驱动移植ds18b20的实验详解


文章目录

  • 前言
  • 一、硬件连接
  • 二、代码移植
    • 1.驱动代码
    • 2.编译程序
  • 三、移植到开发板
  • 参考连接


前言

提示:基于I.MX6ull驱动移植ds18b20的实验:

实验平台:正点原子alpha开发板V2.2
传感器:ds18b20模块


一、硬件连接

ds18b20的VCC:连接开发板3.3V;
ds18b20的GND:连接开发板GND;
ds18b20的信号线:连接alpha开发板的CSI_VSYNC引脚,查手册可知。
在这里插入图片描述

在这里插入图片描述

请添加图片描述

二、代码移植

1.驱动代码

1.创建一个ds18b20.c文件,在文件中输入以下代码:

代码如下(示例):

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : ds18b20.c
作者       : wj learn tangmingfei2013@126.com
版本       : V1.0
描述       : ds18b20 驱动程序, GPIO4_PIN19-----ds18b20 port
其他       : 无
日志       : 初版V1.0 2024/4/20
使用方法:
    typedef struct{
        unsigned short  temperatureVal; //温度数据, 真实值val = temperatureVal *0.0625
        int sign;                       //符号位,1: 负数, 0: 正数
    }Ds18b20Struc;
    Ds18b20Struc ds_struc;
    
    void convert_temp()
    {
        read(fd, &ds_struc, sizeof(Ds18b20Struc));
        printf("ds18b20 Value: %.02f, sign: %d \n", ds_struc.temperatureVal*0.0625, 
                                                    ds_struc.sign);
    }
***************************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.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.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
 
#define MODE_NAME      "ds18b20"     // dev/ds18b20
#define DEVICE_CNT     1
 
#define CCM_CCGR3_BASE                          (0x20C4074)   // GPIO端口时钟配置
#define IOMUXC_SW_MUX_CTL_PAD_CSI_VSYNC_BASE    (0x20E01DC)   // IO_PIN 功能属性配置
#define IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC_BASE    (0x20E0468)   // IO_PIN 电平属性配置
 
#define GPIO4_GDIR_BASE                         (0x20A8004)   // GPIO 方向属性配置
#define GPIO4_DR_BASE                           (0x20A8000)   // GPIO 数据寄存器
 
#define PIN_SEC                                 19            // GPIO - 19 
 
 
static void __iomem *CCM_CCGR;
static void __iomem *IOMUXC_SW_MUX_CTL ;
static void __iomem *IOMUXC_SW_PAD_CTL ;
 
static void __iomem *GPIO_GDIR;
static void __iomem *GPIO_DR;
 
typedef struct{
    unsigned short   temperatureVal;    // 温度数据, 真实值val = temperatureVal *0.0625
    int sign;                           // 符号位,1: 负数, 0: 正数
}Ds18b20Struc;
 
/* 1.  struds18b20设备结构体 */
struct struds18b20_dev
{
    dev_t devid;            /* 设备号      */
    struct cdev cdev;       /* cdev     */
    struct class *class;    /* 类        */
    struct device *device;  /* 设备 	    */
    int major;              /* 主设备号     */
    int minor;              /* 次设备号     */
    struct device_node *nd; /* 设备节点 */
};
 
 
static struct struds18b20_dev struds18b20; /* structure ds18b20 设备 */

static void gpio_init( void )
{
    unsigned int temp;
 
    /*
        gpio port: GPIO4-PIN-19
    */
 
    // remap the register address to MCU
    CCM_CCGR = ioremap(CCM_CCGR3_BASE, 4);  
    IOMUXC_SW_MUX_CTL = ioremap(IOMUXC_SW_MUX_CTL_PAD_CSI_VSYNC_BASE, 4);
    IOMUXC_SW_PAD_CTL = ioremap(IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC_BASE, 4); 
 
    GPIO_GDIR = ioremap(GPIO4_GDIR_BASE, 4);
    GPIO_DR = ioremap(GPIO4_DR_BASE, 4);
 
    /*
    step-1:
        使能GPIO-4 CLOCK
        register CCM_CCGR3  
        Address: 20C_4000h base + 74h offset = 20C_4074h
        CG6: bit[13:12] = 0b11
    */
    temp = readl(CCM_CCGR);
    temp &= ~(3 << 12);             /* 清除以前的设置 */
    temp |= (3<<12);                /* 设置新值 */
    writel(temp, CCM_CCGR);
 
    /*
     step-2:
       设置GPIO4_PIN20为IO
       register IOMUXC_SW_MUX_CTL_PAD_CSI_HSYNC
       Address: 20E_0000h base + 1E0h offset = 20E_01E0h
       MUX_MODE: bit[3:0] = 0b0101
       */
    temp = readl(IOMUXC_SW_MUX_CTL); 
    temp &=~(0xf);
    temp |= 0x05;
    writel(temp, IOMUXC_SW_MUX_CTL);
 
    /*
    step-3:
        设置GPIO_GDIR,配置该IO为 in/out
        Address: Base address + 4h offset
        0 INPUT — GPIO is configured as input.
        1 OUTPUT — GPIO is configured as output.
    */
    temp = readl(GPIO_GDIR); 
    temp |= (1<<PIN_SEC);     //设置IO为输出
    writel(temp, GPIO_GDIR);
 
}
 
static void gpio_uninit( void )
{
    iounmap(CCM_CCGR);
    iounmap(IOMUXC_SW_MUX_CTL);
    iounmap(IOMUXC_SW_PAD_CTL);
    iounmap(GPIO_GDIR);
    iounmap(GPIO_DR);
}
 
static void gpio_set_input(void)
{
    unsigned int temp;
 
    /*
    step-3:
        设置GPIO_GDIR,配置该IO为 in/out
        Address: Base address + 4h offset
        0 INPUT — GPIO is configured as input.
        1 OUTPUT — GPIO is configured as output.
    */
    temp = readl(GPIO_GDIR); 
    temp &= ~(1<<PIN_SEC);       //设置IO为输入
    writel(temp, GPIO_GDIR);    
}
 
static void gpio_set_output(void)
{
    unsigned int temp;
 
    /*
        step-3:
            设置GPIO_GDIR,配置该IO为 in/out
            Address: Base address + 4h offset
            0 INPUT — GPIO is configured as input.
            1 OUTPUT — GPIO is configured as output.
    */
    temp = readl(GPIO_GDIR); 
    temp |= (1<<PIN_SEC);        //设置IO为输入
    writel(temp, GPIO_GDIR);    
}
 
 
static void set_pin_data( int val )
{
    unsigned int temp;
 
    temp = readl(GPIO_DR);
    if( val ){
        temp |= (1<<PIN_SEC);
        writel(temp, GPIO_DR);
    }
    else{
        temp &= ~(1<<PIN_SEC);
        writel(temp, GPIO_DR);
    }
}
 
static bool get_pin_data(void)
{
    unsigned int temp;
    
    temp = readl(GPIO_DR);
    
    return (temp & (1<<PIN_SEC))> 0 ? true:false;
}

static void tempudelay(int usecs) 
{
    int pre,last;
 
    pre = ktime_get_boot_ns();
    
    while(1){
        last = ktime_get_boot_ns();
        if( (last - pre) >= (usecs*1000))
            break;
    }
}
 
static int ds18b20_wait_for_ack(void)
{
    int timeout_count = 500;
 
    gpio_set_input();
 
    /* 如果是高电平,等待 */
    while (get_pin_data() && --timeout_count)
    {
        udelay(1);
    }
 
    if (!timeout_count)
    return -1;
 
    /* 现在是低电平 */
    timeout_count = 500;
    while (!get_pin_data() && --timeout_count)
    {
        udelay(1);
    }
 
    if (!timeout_count)
        return -1;
 
    return 0;
}
 
 
static int ds18b20_check( void )
{
    gpio_set_output();
    set_pin_data(0);
 
    tempudelay(480);
 
    if (ds18b20_wait_for_ack())
        return -1;
    else
        return 0;
 
}
 
static void ds18b20_write_byte( unsigned char byte)
{
    unsigned char k;
 
    // write data bit
    gpio_set_output(); 
 
    for ( k = 0; k < 8; k++ )
    {
        if (byte & (1<<k))
        {
            set_pin_data(0);
            tempudelay(2); 
 
            set_pin_data(1);
            tempudelay(65); 
        }
        else
        {
            set_pin_data(0);
            tempudelay(65); 
 
            set_pin_data(1);
            tempudelay(2); 
        }
    }
}
 
 
static unsigned char ds18b20_read_byte( void )
{
    unsigned char byteVal = 0;
    unsigned char i;
 
    for ( i = 0; i < 8; i++ )
    {
        gpio_set_output();
        set_pin_data(0);
        tempudelay(2);
 
        gpio_set_input();
        tempudelay(7);
 
        if( get_pin_data() )
            byteVal |= (1<<i);
 
        tempudelay(60); 
    }
 
    return  byteVal;
}
 
static bool ds18b20_process( Ds18b20Struc *pdsStruc)
{
    unsigned long tempval;
    unsigned long flags;
    unsigned char temp1, temp2;
 
    local_irq_save(flags);
 
    if (ds18b20_check() == -1)
    {
        gpio_set_output();
        local_irq_restore(flags); 
        return false;
    }
    tempudelay(580);
 
    ds18b20_write_byte(0xcc);
    ds18b20_write_byte(0x44);
 
    gpio_set_output();
    set_pin_data(1);
 
    local_irq_restore(flags); 
 
    set_current_state(TASK_INTERRUPTIBLE);
    schedule_timeout(HZ); 
 
    local_irq_save(flags);
    if (ds18b20_check() == -1)
    {
        gpio_set_output();
        local_irq_restore(flags); 
        return false;
    }
    tempudelay(580);
 
    ds18b20_write_byte(0xcc);
    ds18b20_write_byte(0xbe);
 
    temp1 = ds18b20_read_byte();
    temp2 = ds18b20_read_byte();
 
    pdsStruc->sign = 0;
    if (temp2 > 0x7f)           //最高位为1时温度是负
    {
        temp1    = ~temp1;      //补码转换,取反加一
        temp2    = ~temp2+1; 
        pdsStruc->sign = 1;
    }
 
    //read tempeture value 
    tempval =  ((temp2 << 8) | temp1);
    printk(" [%s line %d ] read value: 0x%04x \r\n",
           __FUNCTION__, __LINE__, (u16)tempval);
 
    pdsStruc->temperatureVal = tempval;
 
    local_irq_restore(flags);
 
    return true;
}


static ssize_t ds18b20_read_val (struct file *file, char __user *buf, 
                                          size_t size, loff_t *offset)
{
    Ds18b20Struc dsStruc;
    bool result;
 
    result = ds18b20_process( &dsStruc );
    if( result ){
        result = copy_to_user(buf, &dsStruc, sizeof(Ds18b20Struc));
    }
 
    return sizeof(Ds18b20Struc);
}
 
 
static int ds18b20_drv_open(struct inode *node, struct file *file)
{
    //init device hardware 
    printk(" [%s line %d ] open the devices! \r\n",__FUNCTION__, __LINE__);
 
    return 0;
}
 
static int ds18b20_drv_close(struct inode *node, struct file *file)
{
    printk(" %s line %d \r\n",  __FUNCTION__, __LINE__);
 
    return 0;
}
 
/* 2. 定义自己的 file_operations 结构体 */
static struct file_operations ds18b20drv_drv = {
    .owner = THIS_MODULE,
    .open  = ds18b20_drv_open,
    .read = ds18b20_read_val,
    .release = ds18b20_drv_close,
};
 
/* 4. 把 file_operations 结构体告诉内核:注册驱动程序 */
/* 5. 安装驱动程序时,就会去调用这个入口函数 */
static int __init ds18b20_drv_init(void)
{
    printk("[%s line %d ] initial ds18b20 devices! \r\n",__FUNCTION__, __LINE__);
 
    /* 初始化硬件资源 */
    gpio_init();
 
    /* 注册字符设备驱动 */
 
    /* 1、创建设备号 */
    if (struds18b20.major)
    {
        /*  定义了设备号 */
        struds18b20.devid = MKDEV(struds18b20.major, 0);
        register_chrdev_region(struds18b20.devid, DEVICE_CNT, MODE_NAME);
    }
    else
    {
        /* 没有定义设备号,自动申请设备号 */
        alloc_chrdev_region(&struds18b20.devid, 0, DEVICE_CNT, MODE_NAME);
 
        struds18b20.major = MAJOR(struds18b20.devid); /* 获取分配号的主设备号 */
        struds18b20.minor = MINOR(struds18b20.devid); /* 获取分配号的次设备号 */
    }
 
    printk("struds18b20 major=%d,minor=%d \r\n", struds18b20.major, struds18b20.minor);
 
    /* 2、初始化cdev */
    struds18b20.cdev.owner = THIS_MODULE;
    cdev_init(&struds18b20.cdev, &ds18b20drv_drv);
 
    /* 3、添加一个cdev */
    cdev_add(&struds18b20.cdev, struds18b20.devid, DEVICE_CNT);
 
    /* 4、创建类 */
    struds18b20.class = class_create(THIS_MODULE, MODE_NAME);
    if (IS_ERR(struds18b20.class))
    {
        return PTR_ERR(struds18b20.class);
    }
 
    /* 5、创建设备 */
    struds18b20.device = device_create(struds18b20.class, NULL, 
                                       struds18b20.devid, NULL,
                                       MODE_NAME);
    if (IS_ERR(struds18b20.device))
    {
        return PTR_ERR(struds18b20.device);
    }
 
    return 0;
}
 
/* 6. 有入口函数就有出口函数:卸载驱动程序时就会去调用这个出口函数 */
static void __exit ds18b20_drv_exit(void)
{
    printk("[%s line %d ] exit ds18b20 drvices driver!\r\n",__FUNCTION__, __LINE__);
 
    gpio_uninit();
 
    /* 注销字符设备驱动 */
    cdev_del(&struds18b20.cdev);                             /*  删除cdev */
    unregister_chrdev_region(struds18b20.devid, DEVICE_CNT); /* 注销设备号 */
 
    device_destroy(struds18b20.class, struds18b20.devid);
    class_destroy(struds18b20.class);
}
 
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
 
module_init(ds18b20_drv_init);
module_exit(ds18b20_drv_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wj");

2.创建一个ds18b20App.c文件,在文件中输入以下代码:

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名      : ds18b20App.c
作者        : wj learn tangmingfei2013@126.com
版本        : V1.0
描述        : ds18b20 driver 测试程序,用于测试 drv_03_ds18b20
日志        : 初版V1.0 2024/4/20 
使用方法    : ./test_03_ds18b20
***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
 
#define devices   "/dev/ds18b20"
 
typedef struct{
    unsigned short  temperatureVal;     //温度数据, 真实值val = temperatureVal *0.0625
    int sign;                           //符号位,1: 负数, 0: 正数
}Ds18b20Struc;
 
 
void convert_temp( Ds18b20Struc *pStru )
{
    printf("ds18b20 Value: %.02f, sign: %d \n", pStru->temperatureVal*0.0625, 
                                                pStru->sign);
}
 
 
int main(void)
{
    int fd;
    int count_run = 10000;
    int len; 
    Ds18b20Struc ds_struc;
 
    fd = open(devices, 0);
    if (fd == -1){
        printf("can not open file: %s \n", devices);
        return -1;
    }
 
    while( count_run > 0)
    {
        len = read(fd, &ds_struc, sizeof(Ds18b20Struc));
        count_run--;
        if (len > 0) {
           convert_temp(&ds_struc);
        } 
        else {
           perror("read ds18b20 device fail!\n");
        }
        sleep(1);
    }
 
    close(fd);
 
    return 0;
}

3.创建一个Makefile文件,在文件中输入以下代码:
其中KERNELDIR 路径改为自己的交叉编译器

KERNELDIR := /home/wj/linux/alientek-linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)

obj-m := ds18b20.o

build: kernel_modules

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

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

2.编译程序

1.在文件夹下输入make,之后输出ds28b20.ko内核模块
在这里插入图片描述
2.交叉编译器编译可执行文件

//生成可执行文件
arm-linux-gnueabihf-gcc -o ds18b20App ds18b20App.c
//查看文件类型
file ds18b20App 

在这里插入图片描述

三、移植到开发板

本方法使用nfs挂载的方法加载根文件系统,将上述生成的ds28b20.ko 文件和ds18b20App文件拷贝到自己为根文件夹下面;

加载内核模块

//使用insmod方法有效,测试使用modprobe无效
insmod ds18b20.ko

在这里插入图片描述

//查看设备是否存在
ls /dev/sd*

在这里插入图片描述

//执行可执行程序
./ds18b20

在这里插入图片描述
在这里插入图片描述

参考连接

https://blog.csdn.net/mftang/article/details/135882692

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

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

相关文章

在瑞芯微RV1126 Linux系统上调试WiFi的详细指南

目录标题 1. **系统和环境准备**2. **检查WiFi设备状态**3. **启用和禁用WiFi接口**4. **扫描可用的WiFi网络**5. **连接到WiFi网络**6. **查看当前的WiFi连接状态**7. **断开和重新连接WiFi**8. **管理WiFi网络配置**9. **使用iw工具进行高级WiFi调试**10. **故障排除和日志获…

Kafka安装Windows版

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 Kafka 是一个由 LinkedIn 开发的分布式消息系统,它于2011年年初开源,现…

【JavaSE】异常

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 认识异常 异常分类 举例 栈溢出错误 空指针异常&#xff08;运行时异常&#xff09; 编译时异常 处理异常 抛出 异常 程序本身触发异常 手动抛出异常 举例 利用try ca…

1096 大美数

solution B被A整除&#xff0c;B是A的倍数。B / AA整除B A把B整除 &#xff22;被A整除 B / A >n可以整除不同的四个因数之和 等价于 (a b c d) % n 0 #include<iostream> #include<cmath> using namespace std; int main(){int k, n, t;scanf("%d&q…

【MATLAB】App 设计 (入门)

设计APP 主界面 函数方法 定时器 classdef MemoryMonitorAppExample < matlab.apps.AppBase% Properties that correspond to app componentsproperties (Access public)UIFigure matlab.ui.FigureStopButton matlab.ui.control.ButtonStartButton matlab.ui.cont…

openAI tts Java文本转语音完整前后端代码 html

Java后端代码 maven 仓库&#xff1a; <!--openAI 请求工具--> <dependency><groupId>com.unfbx</groupId><artifactId>chatgpt-java</artifactId><version>1.1.5</version> </dependency>maven 仓库官方 tts 使用案例…

【论文源码实战】轻量化MobileSAM,分割一切大模型出现,模型缩小60倍,速度提高40倍

前言 MobileSAM模型是在2023年发布的&#xff0c;其对之前的SAM分割一切大模型进行了轻量化的优化处理&#xff0c;模型整体体积缩小了60倍&#xff0c;运行速度提高40倍&#xff0c;但分割效果却依旧很好。 MobileSAM在使用方法上沿用了SAM模型的接口&#xff0c;因此可以与…

超越GPT-4V,苹果多模态大模型上新,神经形态计算加速MLLM(一)

4月8日&#xff0c;苹果发布了其最新的多模态大语言模型&#xff08;MLLM &#xff09;——Ferret-UI&#xff0c;能够更有效地理解和与屏幕信息进行交互&#xff0c;在所有基本UI任务上都超过了GPT-4V&#xff01; 苹果开发的多模态模型Ferret-UI增强了对屏幕的理解和交互&am…

【YOLOv8改进[Backbone]】使用MobileNetV3助力YOLOv8网络结构轻量化并助力涨点

目录 一 MobileNetV3 1 面向块搜索的平台感知NAS和NetAdapt 2 反向残差和线性瓶颈 二 使用MobileNetV3助力YOLOv8 1 整体修改 ① 添加MobileNetV3.py文件 ② 修改ultralytics/nn/tasks.py文件 ③ 修改ultralytics/utils/torch_utils.py文件 2 配置文件 3 训练 其他 …

内置管线升级到SBP,如何复用之前打包的AssetBundle

1&#xff09;内置管线升级到SBP&#xff0c;如何复用之前打包的AssetBundle 2&#xff09;安卓真机&#xff0c;在Unity 2021.3.31版本下Buffer数据异常 3&#xff09;URP里CullResults.CreateSharedRendererScene下面的消耗 4&#xff09;移动端是否支持曲面细分着色 这是第3…

C#基础|Debug程序调试学习和技巧总结

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 在程序的开发过程中&#xff0c;可能绝大部分时间是用来调试程序&#xff0c; 当完成了某个功能的编程&#xff0c;都需要调试一下程序&#xff0c;看编程是否存在问题。 01 为什么需要程序调试 无论是电气工程师还…

Zed,有望打败 VS Code 吗?

大家好&#xff0c;我是楷鹏。 先说结论&#xff0c;不行。 Zed&#xff0c;又一款新起的文本代码编辑器 &#x1f449; https://zed.dev 今年一月二十四号正式开源&#xff0c;短短不到三个月&#xff0c;GitHub 上已经冲上 3 万 star 正如 Zed 的口号所说「Code at the spe…

win11家庭中文版安装docker遇到Hyper-V启用失败,如何解决??

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

统一所有 LLM API:支持预算与速率限制 | 开源日报 No.229

BerriAI/litellm Stars: 6.7k License: NOASSERTION litellm 是一个使用 OpenAI 格式调用所有 LLM API 的工具。它支持 Bedrock、Azure、OpenAI、Cohere、Anthropic 等 100 多种 LLMs&#xff0c;提供企业级代理服务器和稳定版本 v1.30.2。 主要功能和优势包括&#xff1a; 将…

Jenkins的安装和部署

文章目录 概述Jenkins部署项目的流程jenkins的安装启动创建容器进入容器浏览器访问8085端口 Jenkins创建项目创建example项目 概述 Jenkins&#xff1a;是一个开源的、提供友好操作界面的持续集成&#xff08;CLI&#xff09;工具&#xff0c;主要用于持续、自动构建的一些定时…

nVisual在线网络规划设计软件

●01● nVisual在线网络规划设计软件 在信息化快速发展的今天&#xff0c;网络基础设施的建设与优化变得尤为关键。为了满足现代通信行业对高效、精准的网络规划需求&#xff0c;nVisual在线网络规划设计软件应运而生&#xff0c;它通过集成先进的GIS技术和网络规划工具&#…

如何快速学习盲打键盘的指法

学习盲打键盘的指法需要一定的时间和练习&#xff0c;但是以下几个方法可以帮助你加快学习的速度&#xff1a; 掌握正确的手位&#xff1a;了解标准的键盘布局以及手指应该放置的位置是学习盲打的第一步。在QWERTY键盘上&#xff0c;你的左手应该放在ASDF键上&#xff0c;右手应…

基于开源项目改造,我制作了15个酷炫的数据大屏(附 Python 源码)

数据可视化大屏在许多领域都有广泛的应用&#xff0c;其带来了好处也是显而易见的&#xff1a; 直观展示数据&#xff1a; 大屏幕数据可视化能够将庞大的数据集以图形化的方式展示出来&#xff0c;使人们能够更容易地理解和分析数据。这种可视化形式使信息更加直观&#xff0c;…

SSM项目前后端分离详细说明

1.后端 1.1打包 说明&#xff1a;使用idea打开项目&#xff0c;然后进行打包。 1.2tomcat 说明&#xff1a;把后端打成war包后放入tomcat启动。 1.3启动tomcat 说明&#xff1a; 找到tomcat中bin目录中的startup.bat文件&#xff0c;进行启动。如果启动失败&#xff0c;可以…

Stream 流常见基本操作

文章目录 概述一、Stream 流的常见生成方式二、Stream 流中间操作方法1、常用中间操作方法2、使用示例13、使用示例24、使用示例35、使用示例46、使用示例57、Stream 流使用注意事项 三、Stream 流终结操作方法1、常用终结方法2、使用示例13、使用示例24、使用示例35、Stream 基…