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

news2024/11/20 9:46:00

文章目录

  • 前言
  • 一、硬件连接
  • 二、代码移植
    • 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.coloradmin.cn/o/1609828.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将windows作为网关

开启转发 reg add HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters /v IPEnableRouter /D 1 /f开启routing and remote access服务 这样局域网里面别的设备能通过windows进行上网 参考&#xff1a;https://www.cnblogs.com/chrishg/articles/12861053.html

Android的一些总结

先打开自定义的app显示欢迎->消失 打开桌面应用程序->在桌面应用程序中也要能一键启动打开视频播放的app 桌面应用程序广播接收者进行监听&#xff0c;然后打开服务/activity是可行的。 ########################## 日志&#xff0c;调试&#xff1a; Usb 无线 串口…

全新Linux教程-驱动大全-PCI和PCIe子系统-P1-从软件角度看PCI和PCIE

主题&#xff1a;为什么要引入PCI&#xff0c; 为什么要引入PCIE&#xff0c;PCIE和PCI的硬件接口大概如何&#xff1f;理解一下CPU地址和PCI地址空间的概念。 参考资料&#xff1a; * 《PCI Express Technology》&#xff0c;Mike Jackson, Ravi Budruk; MindShare, Inc. *…

比较转录组学方法推断基因共表达网络及其在玉米和水稻叶片转录组中的应用 TO-GCN时序分析-文献精读-8

Comparative transcriptomics method to infer gene coexpression networks and its applications to maize and rice leaf transcriptomes 比较转录组学方法推断基因共表达网络及其在玉米和水稻叶片转录组中的应用 TO-GCN时序分析&#xff0c;媲美加权基因共表达网络分析-WG…

免费证书和付费证书的区别

免费SSL证书和付费SSL证书都是用来实现HTTPS加密通信的安全工具&#xff0c;旨在为网站提供数据传输的机密性和完整性保护。尽管它们的基本功能相似&#xff0c;即通过公钥加密技术建立安全连接&#xff0c;但两者在以下几个方面存在显著区别&#xff1a; 1. 类型与验证级别 免…

项目二:学会使用python爬虫请求库(小白入门级)

上一章已经了解python爬虫的基本知识&#xff0c;这一次让我们一起来学会如何使用python请求库爬取目标网站的信息。当然这次爬虫之旅相信我能给你带来不一样的体验。 目录 一、安装requests 库 简介 安装 步骤 1.requests的基本使用3步骤 2.查看所使用编码 3.设置编码…

Spring Boot入门(16):让你的API文档更亮眼:Spring Boot与Swagger-UI完美整合!

1. 前言 在实际开发过程中&#xff0c;我们经常需要编写API文档来描述接口的调用方法、参数、返回值等信息。为了提高开发效率和维护便利性&#xff0c;Swagger-UI成为了API文档自动生成的一种流行方案。本文将介绍如何利用Spring Boot和Swagger-UI实现在线API文档。 2. 摘要…

应用编程之进程(三-通信篇)

所谓进程间通信指的是系统中两个进程之间的通信&#xff0c;不同的进程都在各自的地址空间中、相互独立、隔离&#xff0c;所以它们是处在于不同的地址空间中&#xff0c;因此相互通信比较难&#xff0c;Linux 内核提供了多种进程间通信的机制。 大部分的程序是不要考虑进程间…

unity学习(86)——细节优化

东西已经做出来了&#xff0c;现在需要的是优化&#xff0c;说得简单&#xff0c;做起来难。 1.122包的优化&#xff0c;避免重复创建&#xff01; 2.为何会出现一边动&#xff0c;一边不动的情况。重复登录后依旧是unity可以看到移动&#xff0c;但是exe那边看不到移动&#…

NAT基本配置

配置IP完成及缺省的路由如下&#xff1b; 此时R1pingISP是ping不通的&#xff0c;因为缺省是可以将数据传给R3&#xff0c;但是R3传不回去&#xff0c;知道目标IP地址但因其是私有内部IP&#xff0c;而自己的是公有IP&#xff0c;所以传不过去&#xff0c;此时就需要R2这个边界…

Linux 基于 TCP 协议的简单服务器-客户端应用

目录 一、相关函数 1、listen() 2、accept() 3、connect() 4、两种IP地址转换方式 5、TCP和UDP数据发送和接收函数对比 5、log.hpp自定义记录日志 二、udp_server.hpp单进程版本 三、tcp_server.cc 四、Telnet客户端&#xff08;代替tcp_client.cc&#xff09; 五…

TorchEEG文档_学习笔记1【代码详解】

文章目录 一、用户文档1.安装Pytorch2.安装TorchEEG3.安装与图算法的插件 二、教程1.使用TorchEEG完成深度学习工作流程2datasets模块3.transforms模块4.models模块5.trainer模块6.使用Vanilla PyTorch训练模型 一、用户文档 1.安装Pytorch TorchEEG依赖于PyTorch,根据系统、…

PTA L2-047 锦标赛

题目 解析 把每一场比赛看作满二叉树的一个节点&#xff0c;父节点递归遍历子节点的结果&#xff0c;进行试填。 代码 #include <bits/stdc.h>using i64 long long;struct Node {int win, lose; };void solve() {int k;std::cin >> k;int siz (1 << k);…

常见UI组件(二)

一、文本输入 1.1 概述 TextInput为文本输入组件&#xff0c;用于接收用户输入的文本内容 1.2 参数 Entry Component struct Index {build() {Column({space : 50}) {TextInput({placeholder:请输入用户名}).width(70%)TextInput({text:当前内容}).width(70%)}.width(100%).…

基于springboot+vue+Mysql的广场舞团管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Java | Leetcode Java题解之第38题外观数列

题目&#xff1a; 题解&#xff1a; class Solution {public String countAndSay(int n) {String[] arr {"","1","11","21","1211","111221","312211","13112221","1113213211",…

六、项目发布 -- 4. 电子书详情页API开发、电子书列表API开发

电子书详情页API的编写 同理如下app.get中路由、回调&#xff1b;回调中要连接数据库、接收前端传过来的值、到数据库中做查询&#xff0c;然后回调&#xff08;如果回调失败返回什么JSON&#xff0c;如果回调成功返回什么JSON&#xff09;&#xff1b;最后千万别忘记了关闭数…

面试:java中常见的锁

文章目录 概述并发编程三大特性java中常见的锁synchronized关键字&#xff08;隐式锁&#xff09;ReentrantLock 类ReadWriteLock 接口StampedLock 类 小结 概述 在Java中&#xff0c;锁是用于控制多个线程对共享资源的访问的一种机制。它有两个主要作用&#xff1a; 保证线程…

Pyqt5中设置matplotlib绘图区背景透明

Pyqt5中设置matplotlib绘图区背景透明 一、前言 最近组里接了学校的某项目&#xff0c;作者是团队里面负责做前端展示的&#xff0c;但是说实话&#xff0c;感觉QT做前端展示真不是很方便&#xff0c;开发过程中遇到不少棘手的问题。 其中一个卡了我一段时间的是界面画图表时…

ASP.NET基于Web的招投标系统的设计与实现

摘 要 招标拍卖的历史悠久&#xff0c;在近两千年的发展历程中&#xff0c;人们对拍卖的理论和技术做了大量的探讨。随着计算机网络技术的迅猛发展和日益成熟&#xff0c;为了提高招投标及采购工作的效率&#xff0c;为廉政建设和防止腐败提供技术保障&#xff0c;传统的拍…