ARM Linux驅動篇 學習溫度感測器ds18b20的驅動編寫過程

来源:https://www.cnblogs.com/NickQ/archive/2018/05/12/9026545.html
-Advertisement-
Play Games

ARM Linux驅動篇 學習溫度感測器ds18b20的驅動編寫過程 原文地址: "http://www.cnblogs.com/NickQ/p/9026545.html " 一、開發板與ds18b20的入門 ds18B20是常用的數字溫度感測器,具有體積小,硬體開銷低,抗干擾能力強,精度高的特點。 ...


ARM Linux驅動篇 學習溫度感測器ds18b20的驅動編寫過程

原文地址:http://www.cnblogs.com/NickQ/p/9026545.html

一、開發板與ds18b20的入門

ds18B20是常用的數字溫度感測器,具有體積小,硬體開銷低,抗干擾能力強,精度高的特點。但樓主在使用過程中發現,ds18b20測量的溫度還是需要進行一定的軟體校準的。後面我們會談論到。

除了上面提到的,ds18b20還有很多可圈可點的有點。下麵說樓主所關註到的幾個。

  1. 單匯流排協議,稱為匯流排,必然可以掛載很多設備,但卻只占用一個IO口。這對於缺乏IO資源的設備來說,就像是救命稻草。
  2. 可以由用戶自己權衡測量精度和測量時間。根據Datasheet,18B20控制寄存器有兩位是用來控制精度和測量時間的。如下圖。
    下圖表明:用戶可以在精度9bit-12bit中,自由切換,這也對應著93.75ms,187.5ms,375ms,750ms四個最大測量時間。也就是說9bit精度意味著,最大測量時間最有93.75ms(對於緩慢變化的溫度來說,這已經很快了),但只可以精確到0.25攝氏度。12bit精度意味著,雖然最大測量時間有750ms,但精度卻能達到0.0625(750ms對於溫度測量不能算很慢,但換來的這個精度卻是不低的)。
    測量精度與測量時間
  3. 可以使用寄生電源供電。這也是這個晶元突出的地方。這意味著可以不連接電源線,也為PCB布板,多設備走線省去了很多方便。

二、開發板的硬體電路和寄存器

樓主這裡使用的是飛凌2440開發板,做學習之用。
電路連接圖如下
電路連接圖
這個板子上是接了電源和外部上拉。事實上這個電源可以由寄生電源,即由信號線DQ上的外部上拉提供。
圖中,也可以看出信號線DQ是連接在了GPG0口。

寄存器說明圖
寄存器物理地址:
reg_addr
寄存器配置說明
GPGCON-GPG0
reg2
GPGDAT AND GPGUP
reg3

三、 驅動實現

/*********************************************************************************
*      Copyright:  (C)2018. Xu Qiang <[email protected]>
*                  All rights reserved.
*
*      Filename:     
*      Description:  
*
*      Version:      1.0.0 (2018/05/10)
*      Author:       Xu Qiang <[email protected]>
*      ChangeLog:    Release version on "2018/05/10 00:14:26"
* 
********************************************************************************/
#include <linux/module.h>   /* Every Linux kernel module must include this head */
#include <linux/init.h>     /* Every Linux kernel module must include this head */
#include <linux/kernel.h>   /* printk() */
#include <linux/fs.h>       /* struct fops */
#include <linux/errno.h>    /* error codes */
#include <linux/cdev.h>     /* cdev_alloc()  */
#include <asm/io.h>         /* ioremap()  */
#include <linux/ioport.h>   /* request_mem_region() */
#include <linux/delay.h>     
#include <linux/moduleparam.h>      
#include <linux/types.h>     
#include <mach/regs-gpio.h>     
#include <mach/hardware.h>     
#include <asm/uaccess.h>     
#include <linux/gpio.h>     
#include <linux/device.h>    

//定義驅動模塊信息
//模塊作者和描述
#define DRV_AUTHOR                "Nick <[email protected]>"
#define DRV_DESC                  "S3C24XX 18B20 driver"

//模塊名
#define DEV_NAME                  "s3c18b20"

//模塊版本信息(只作用於安裝和卸載的列印信息中)
#define DRV_MAJOR_VER             1
#define DRV_MINOR_VER             0
#define DRV_REVER_VER             0


//定義GPG口的寄存器地址,註意偏移地址在程式中的使用
#define S3C_GPG_BASE              0x56000060           //寄存器物理地址基地址
#define GPGCON_OFFSET             0
#define GPGDAT_OFFSET             4
#define GPGUP_OFFSET              8
#define S3C_GPG_LEN               0x10   /* 0x56000060~0x56000070  */ //此處定義的大小包括了四個寄存器地址空間,即4*4位元組(包括保留的寄存器地址)

//定義ds18b20 DQ線對應的埠的GPIO編號。例如:GPG0 GPIO編號為0
#define GPIO_NUM_18B20            0      //18B20 PORT is PG0 

//定義函數操作的參數
//GPIO_Mode 
#define GPIO_MODE_INPUT           0x00
#define GPIO_MODE_OUTPUT          0x01
#define GPIO_MODE_EINT            0x10

//GPIO_STATUS
#define GPIO_STATUS_LOW           0
#define GPIO_STATUS_HIGH          1

//GPIO_PULLUP
#define GPIO_PULLUP_ENABLE        0
#define GPIO_PULLUP_DISABLE       1

#define DISABLE                   0
#define ENABLE                    1

//定義函數巨集
#define s3c_gpio_read(reg)      __raw_readl((reg)+s3c_gpg_membase)
#define s3c_gpio_write(val,reg) __raw_writel((val),(reg)+s3c_gpg_membase)

//設置GPIO模式 參數:操作寄存器的偏移地址、GPIO編號、設置的狀態(取值應為上述的巨集)
#define s3c_18b20_gpio_mode(gpio_mode)       s3c2440_gpio_cfgpin_mode(GPGCON_OFFSET,GPIO_NUM_18B20, gpio_mode)
#define s3c_18b20_gpio_setsta(gpio_status)   s3c2440_gpio_cfgpin_status(GPGDAT_OFFSET,GPIO_NUM_18B20, gpio_status)
#define s3c_18b20_gpio_getsta()              s3c2440_gpio_getpin_status(GPGDAT_OFFSET,GPIO_NUM_18B20)
#define s3c_18b20_gpio_pullup(gpio_pullup)   s3c2440_gpio_cfgpin_pullup(GPGUP_OFFSET,GPIO_NUM_18B20, gpio_pullup)

//全局變數的定義
//設備數量、主設備好、次設備號(此處主設備號可由靜態給定,只要不為0即可。若主設備號為0,則動態申請)
int dev_count = 1;
int dev_major = 0;
int dev_minor = 0;

int debug = DISABLE;

//定義存儲 映射的虛擬地址空間地址的起始地址 變數
static void __iomem   *s3c_gpg_membase;
static struct cdev    *s3c_18b20_cdev;

//設置GPIO模式,此函數由上述的帶參數巨集調用
static int s3c2440_gpio_cfgpin_mode(unsigned long gpio_addr,unsigned char gpio_num, unsigned char gpio_mode)
{
    volatile unsigned long  gpg_con;
    
    if((GPIO_MODE_INPUT != gpio_mode) && (GPIO_MODE_OUTPUT != gpio_mode) && (GPIO_MODE_EINT != gpio_mode))
    {
        return -1;
    }
    
    /* Set GPxCON register, set correspond GPIO port as input or output mode  */
    gpg_con =  s3c_gpio_read(gpio_addr);  //此處的gpio_addr是偏移地址,調用此巨集後會根據s3c_gpg_membase轉換為絕對虛擬地址
    gpg_con &= ~(0x3<<(2*gpio_num));      /* Clear the currespond GPIO configure register */
    gpg_con |= gpio_mode<<(2*gpio_num);   /* Set the currespond GPIO as output mode */

    //帶參數巨集,實現將gpgdat寫入gpio_addr。
    s3c_gpio_write(gpg_con,gpio_addr);    //此處的gpio_addr是偏移地址,調用此巨集後會根據s3c_gpg_membase轉換為絕對虛擬地址
    
    return 0;
}

//設置GPIO引腳電平狀態,此函數由上述的帶參數巨集調用
static int s3c2440_gpio_cfgpin_status(unsigned long gpio_addr,unsigned char gpio_num, unsigned char gpio_status)
{
    volatile unsigned long  gpg_dat;
    
    if((GPIO_STATUS_LOW != gpio_status) && (GPIO_STATUS_HIGH != gpio_status))
    {
        return -1;
    }

    /* Set GPxDAT register, set correspond GPIO port power level as high level or low level */
    gpg_dat = s3c_gpio_read(gpio_addr); //此處的gpio_addr是偏移地址,調用此巨集後會根據s3c_gpg_membase轉換為絕對虛擬地址
    
    if(GPIO_STATUS_LOW == gpio_status)
    {
        gpg_dat &= ~(0x1<<gpio_num); /* This port set to low level */
    }
    else
    {
        gpg_dat |= (0x1<<gpio_num); /* This port set to high level*/
    }
    
    //帶參數巨集,實現將gpgdat寫入gpio_addr。
    s3c_gpio_write(gpg_dat,gpio_addr);  //此處的gpio_addr是偏移地址,調用此巨集後會根據s3c_gpg_membase轉換為絕對虛擬地址
    
    return 0;
}

//設置GPIO上拉狀態,此函數由上述的帶參數巨集調用
static int s3c2440_gpio_cfgpin_pullup(unsigned long gpio_addr,unsigned char gpio_num, unsigned char gpio_pullup)
{
    volatile unsigned long  gpg_up;
    
    if((GPIO_PULLUP_ENABLE != gpio_pullup) && (GPIO_PULLUP_DISABLE != gpio_pullup))
    {
        return -1;
    }
    
    /* Set GPxUP register, set correspond GPIO port pull up resister as enable or disable  */
    gpg_up = s3c_gpio_read(gpio_addr);
    
    if(GPIO_PULLUP_ENABLE == gpio_pullup)
    {
        gpg_up &= ~(0x1<<gpio_num); /* Enable pull up resister */
    }
    else
    {
        gpg_up |= (0x1<<gpio_num);  /* Disable pull up resister */
    }
    
    s3c_gpio_write(gpg_up,gpio_addr);
    
    return 0;
}

//讀取GPIO引腳電平狀態,此函數由上述的帶參數巨集調用
static int s3c2440_gpio_getpin_status(unsigned long gpio_addr,unsigned char gpio_num)
{
    volatile unsigned long  gpg_dat;
    
    /* Get GPxDAT register, get correspond GPIO port power level as high level or low level */
    gpg_dat = s3c_gpio_read(gpio_addr);
    
    gpg_dat &= (0x1<<gpio_num);  
    
    if(gpg_dat)
    {
        return GPIO_STATUS_HIGH;
    }
    else
    {
        return GPIO_STATUS_LOW;
    }
}

//向內核申請4*4個位元組的虛擬地址空間,並與寄存器物理地址綁定映射
static int s3c_18b20_addr_init(void)
{
    
    if(!request_mem_region(S3C_GPG_BASE, S3C_GPG_LEN, "s3c2440 18b20"))
    {
        return -EBUSY;
    }

    if( !(s3c_gpg_membase=ioremap(S3C_GPG_BASE, S3C_GPG_LEN)) )
    {
        release_mem_region(S3C_GPG_BASE, S3C_GPG_LEN);
        return -ENOMEM;
    }

    return 0;
}

//釋放虛擬地址,解除地址映射
static void s3c_18b20_addr_release(void)
{
    release_mem_region(S3C_GPG_BASE, S3C_GPG_LEN);
    
    iounmap(s3c_gpg_membase);
}

//寫ds18b20複位時序
static int s3c_ds18b20_clk_reset(void)    
{    
    int retval = 0;    

    s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);    
    s3c_18b20_gpio_pullup(GPIO_PULLUP_ENABLE);    

    s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH);    
    udelay(2);    
    s3c_18b20_gpio_setsta(GPIO_STATUS_LOW); // 拉低ds18b20匯流排,複位ds18b20     
    udelay(500);                // 保持複位電平500us     

    s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH); // 釋放ds18b20匯流排     
    udelay(60);    

    // 若複位成功,ds18b20發出存在脈衝(低電平,持續60~240us)     
    s3c_18b20_gpio_mode(GPIO_MODE_INPUT);     
    retval = s3c_18b20_gpio_getsta();

    udelay(500);    
    s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);     
    s3c_18b20_gpio_pullup(GPIO_PULLUP_ENABLE);     
    s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH); // 釋放匯流排     

    return retval;    
}  

//ds18b20寫數據時序
static void s3c_ds18b20_clk_write_byte(unsigned char data)    
{    
    int i = 0,flag = 0;    

    s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);    
    s3c_18b20_gpio_pullup(GPIO_PULLUP_DISABLE);    

    for (i = 0; i < 8; i++)    
    {    
        // 匯流排從高拉至低電平時,就產生寫時隙     
        s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH);     
        udelay(2);    
        s3c_18b20_gpio_setsta(GPIO_STATUS_LOW);   
        flag = data & 0x01;
        if(flag)        
        {
            s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH); 
        }
        else
        {
            s3c_18b20_gpio_setsta(GPIO_STATUS_LOW); 
        }  
        udelay(60);    
        data >>= 1;    
    }    
    s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH); // 重新釋放ds18b20匯流排     
}   

//ds18b20讀數據時序
static unsigned char s3c_ds18b20_clk_read_byte(void)    
{    
    int i;    
    unsigned char data = 0;    

    for (i = 0; i < 8; i++)    
    {    
        // 匯流排從高拉至低,只需維持低電平17ts,再把匯流排拉高,就產生讀時隙     
        s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);   
        s3c_18b20_gpio_pullup(GPIO_PULLUP_ENABLE);   
        s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH);  
        udelay(2);    
        s3c_18b20_gpio_setsta(GPIO_STATUS_LOW);   
        udelay(2);    
        s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH);   
        udelay(8);    
        data >>= 1;    
        s3c_18b20_gpio_mode(GPIO_MODE_INPUT);    
        if (s3c_18b20_gpio_getsta())    
            data |= 0x80;    
        udelay(50);    
    }    
    s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);   
    s3c_18b20_gpio_pullup(GPIO_PULLUP_ENABLE);   
    s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH); // 釋放ds18b20匯流排     
    return data;    
}  


//內核的調用介面
static int s3c_18b20_open(struct inode *inode, struct file *file)
{
    int flag = 0;    

    printk(KERN_ERR "open start\n");

    flag = s3c_ds18b20_clk_reset();    
    
    printk(KERN_ERR "ds18b20 reset is %d\n",flag);
    
    if (flag & 0x01)    
    {    
        printk(KERN_WARNING "open ds18b20 failed\n");    
        return -1;    
    }    
    printk(KERN_NOTICE "open ds18b20 successful\n");    
    return 0;    
}

//內核的調用介面
static int s3c_18b20_release(struct inode *inode, struct file *file)
{
    printk(KERN_DEBUG "/dev/s3c_18b20%d closed.\n", iminor(inode));

    return 0;
}

//內核的調用介面
static ssize_t s3c_18b20_read(struct file *filp, char __user * buf, size_t count, loff_t * f_pos) 
{
    int flag;    
    unsigned long err;    
    unsigned char result[2] = { 0x00, 0x00 };    
    
    flag = s3c_ds18b20_clk_reset(); 
    
    if (flag & 0x01)    
    {    
        printk(KERN_WARNING "ds18b20 init failed\n");    
        return -1;    
    }    

    s3c_ds18b20_clk_write_byte(0xcc);    
    s3c_ds18b20_clk_write_byte(0x44);    

    flag = s3c_ds18b20_clk_reset();    
    if (flag & 0x01)    
        return -1;    

    s3c_ds18b20_clk_write_byte(0xcc);    
    s3c_ds18b20_clk_write_byte(0xbe);    

    result[0] = s3c_ds18b20_clk_read_byte();    // 溫度低八位     
    result[1] = s3c_ds18b20_clk_read_byte();    // 溫度高八位     

    err = copy_to_user(buf, &result, sizeof(result));    
    return err ? -EFAULT : min(sizeof(result), count);    
}    

//定義的ds18b20文件操作的數據結構
static struct file_operations s3c_18b20_fops = 
{
    .owner   = THIS_MODULE,
    .open    = s3c_18b20_open,
    .read    = s3c_18b20_read,
    .release = s3c_18b20_release,
};

//模塊安裝調用的初始化
static int __init s3c_18b20_init(void)
{
    int                    result;
    dev_t                  devno;

    //申請並映射虛擬地址
    if( 0 != s3c_18b20_addr_init() )
    {
        printk(KERN_ERR "s3c2440 18B20 addr initialize failure.\n");
        return -ENODEV;
    }

    //為設備註冊設備號。如果dev_major不為0,則動態申請主設備號。否者使用dev_major為主設備號
    if (0 != dev_major) /*  Static */
    {
        devno = MKDEV(dev_major, dev_minor);
        result = register_chrdev_region(devno, dev_count, DEV_NAME);
    }
    else
    {
        result = alloc_chrdev_region(&devno, dev_minor, dev_count, DEV_NAME);
        dev_major = MAJOR(devno);
    }

    /*  Alloc for device major failure */
    if (result < 0)
    {
        printk(KERN_ERR "S3C %s driver can't use major %d\n", DEV_NAME, dev_major);
        return -ENODEV;
    } 
    
    printk(KERN_DEBUG "S3C %s driver use major %d\n", DEV_NAME, dev_major);

    //為s3c_18b20_cdev數據結構申請空間
    if(NULL == (s3c_18b20_cdev=cdev_alloc()) )
    {
        printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
        unregister_chrdev_region(devno, dev_count);
        return -ENOMEM;
    }
    
    //綁定字元設備數據結構
    s3c_18b20_cdev->owner = THIS_MODULE;
    cdev_init(s3c_18b20_cdev, &s3c_18b20_fops);

    //註冊cdev到內核
    result = cdev_add(s3c_18b20_cdev, devno, dev_count);
    
    if (0 != result)
    {   
        printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result); 
        goto ERROR;
    }

            
    printk(KERN_ERR "S3C %s driver[major=%d] version %d.%d.%d installed successfully!\n", 
            DEV_NAME, dev_major, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);
    return 0;


ERROR:
    printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME);
    cdev_del(s3c_18b20_cdev);
    unregister_chrdev_region(devno, dev_count);
    return result;
}

static void __exit s3c_18b20_exit(void)
{
    dev_t devno = MKDEV(dev_major, dev_minor);

    s3c_18b20_addr_release();

    cdev_del(s3c_18b20_cdev);
    unregister_chrdev_region(devno, dev_count);

    printk(KERN_ERR "S3C %s driver version %d.%d.%d removed!\n", 
            DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);

    return ;
}

/* These two functions defined in <linux/init.h> */
module_init(s3c_18b20_init);
module_exit(s3c_18b20_exit);

module_param(debug, int, S_IRUGO);
module_param(dev_major, int, S_IRUGO);

MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");

四、編譯的Makefile

LINUX_SRC = ${shell pwd}/../kernel/linux-3.0
CROSS_COMPILE=/opt/xtools/arm920t/bin/arm-linux-
INST_PATH=${shell pwd}/
PWD := $(shell pwd)
EXTRA_CFLAGS+=-DMODULE
obj-m += kernel_18b20.o

modules:
        @make -C $(LINUX_SRC) M=$(PWD) modules
        @make clear

uninstall:
        rm -f ${INST_PATH}/*.ko

install: uninstall
        cp -af *.ko ${INST_PATH}

clear:
        @rm -f *.o *.cmd *.mod.c
        @rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
        @rm -f .*ko.cmd .*.o.cmd .*.o.d

clean: clear
        @rm -f *.ko

解釋說明:
LINUX_SRC 指定開發板已編譯過得內核路徑
CROSS_COMPILE 指定交叉編譯器
INST_PATH 安裝路徑
obj-m += kernel_18b20.o 編譯成模塊,輸出文件名為kernel_18b20.o

編譯運行。
修改驅動文件名為kernel_18b20.c
使用make編譯後,將kernel_18b20.ko傳輸至開發板。
使用insmod安裝。可以使用dmesg查看安裝列印信息,也可以使用lsmod查看設備信息

[root@NickQ_fl2440 driver]# insmod kernel_18b20.ko 
[root@NickQ_fl2440 driver]# dmesg
S3C s3c18b20 driver use major 253
S3C s3c18b20 driver[major=253] version 1.0.0 installed successfully!
[root@NickQ_fl2440 driver]# lsmod
kernel_18b20 3250 0 - Live 0xbf000000

然後查看主設備號cat /proc/devices,使用moknod創建設備節點

[root@NickQ_fl2440 driver]# cat /proc/devices | grep s3c18b20
253 s3c18b20
[root@NickQ_fl2440 driver]# mknod -m 755 /dev/s3c18b20 c 253 0
[root@NickQ_fl2440 driver]# ls /dev/s3c18b20 
/dev/s3c18b20

mknod 用法

[root@NickQ_fl2440 driver]# mknod --help
BusyBox v1.27.1 (2017-11-20 21:14:15 CST) multi-call binary.

Usage: mknod [-m MODE] NAME TYPE MAJOR MINOR

Create a special file (block, character, or pipe)

        -m MODE Creation mode (default a=rw)
TYPE:
        b       Block device
        c or u  Character device
        p       Named pipe (MAJOR and MINOR are ignored)

五、編寫測試程式

[nick@XQLY driver]$ vim ~/s3c2440/linux/drivers/test_18b20.c 
/*********************************************************************************
*      Copyright:  (C)2018. Xu Qiang <[email protected]>
*                  All rights reserved.
*
*      Filename:
*      Description:
*
*      Version:      1.0.0 (2018/05/10)
*      Author:       Xu Qiang <[email protected]>
*      ChangeLog:    Release version on "2018/05/10 18:25:02"
*
********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/ioctl.h>


int main()
{
    int fd;
    unsigned char result[2];
    unsigned char integer_value = 0;
    float decimal_value = 0.0;
    float temperature   = 0.0;

    fd = open("/dev/s3c18b20", 0);

    if(fd < 0)
    {
        perror("open device failed\n");
        exit(1);
    }
    else
        printf("Open success!\n");

    while(1)
    {
        read(fd, &result, sizeof(result));

        integer_value = ((result[0] & 0xf0) >> 4) | ((result[1] & 0x08) << 4);

        decimal_value = (result[0] & 0x0f) * 0.0625;

        temperature = (float)integer_value + decimal_value;

        printf("Current Temperature:%6.4f\n", temperature);

        sleep(1);
    }
}
   

解析溫度的說明:
溫度格式

我們讀取出來的數據放置在result[2]里。
result[0]對應從LS Byte里讀回來的值
result[1]對應從MS Byte里讀回來的值

所以integer_value = ((result[0] & 0xf0) >> 4) | ((result[1] & 0x08) << 4);是將LS的高四位和MS的低四位取出(其中有一位符號位S),併合成一個數,即為整數部分。decimal_value = (result[0] & 0x0f) * 0.0625;是提取小數部分

六、測試現象

[root@NickQ_fl2440 driver]# ./start_s3c18b20 
Open success!
Current Temperature:26.5625
Current Temperature:26.6250
Current Temperature:26.5625
Current Temperature:26.6250
Current Temperature:26.6250
^C

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

-Advertisement-
Play Games
更多相關文章
  • 近期接手一個winform 項目,雖然之前有.net 的經驗,但是對一些控制項的用法還不是很熟悉。 這段時間將會記錄一些在工作中遇到的坎坷以及對應的解決辦法,寫出來與大家分享並希望大神提出更好解決方法來促進進步。 我也會儘可能把我查找到資料的出處引出來,以此來感恩對我提供幫助的人們。 正題如下 一、關 ...
  • .net捕捉全局未處理異常的3種方式:方式一、Page_Error處理頁面級未處理異常、方式二、通過HttpModule來捕獲未處理的異常、方式三、通過Global中捕獲未處理的異常 ...
  • 官方文檔:https://cloud.tencent.com/document/product/641/12422 請求官方API及簽名的生成代碼如下: ...
  • 有些項目尤其是WinForm或者是WPF項目,針對一些工具形式的小項目,不想軟體流出去之後,懂程式的的拿到手之後一看配置文件就知道了我們資料庫的用戶名和密碼,如果外網能訪問的話,那就麻煩大了。所以這裡為了防止項目外泄之後這些信息不被別人看到,我們就需要對鏈接字元串或者其他重要信息進行加密,用的時候在 ...
  • 比如Json.NET的JObject明明實現了IEnumerable<T>,具體來說是IEnumerable<KeyValuePair<string, JToken>>,按說JObject類型的對象是可以直接調用Select、Where等linq擴展方法的,但偏偏就是不行,代碼如下: 究竟是人性的扭 ...
  • 經緯度轉換為詳細地址信息 參考文檔:http://lbs.qq.com/webservice_v1/guide-gcoder.html 首先申請key,如果使用的是服務端請求webservice API ,申請密鑰的時候要選擇“服務端”,創建成功之後設置ip白名單,否則預設全部ip都可以使用的你的k ...
  • 本篇承接上篇內容,如果你不小心點擊進來,建議從第一篇開始完整閱讀,文章內容繼承性連貫性。 構建NetCore應用框架之實戰篇系列 一、簡介 1、登錄功能完成後,框架的雛形已經形成,有必要進行複習。 2、本篇簡單對框架代碼進行一些解釋。同時可以簡單理解框架的規範。 二、目錄結構規範 1、直接上圖,目錄 ...
  • C# 虹軟SDK視頻人臉識別和註冊 居然收到10元打賞,還不知道誰乾的,其實更好(自認為)的demo在https://github.com/catzhou2002/ArcFaceDemo 截屏留念: 當然,謝謝!謝謝鼓勵! ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...