36.2Linux單總線驅動DS18B20實驗(詳細講解代碼)_csdn

想必看過我很多次博客的同學,都知道了編寫驅動的流程!
這里我們還是按照以前的習慣來一步一步講解!
單總線驅動,在F103和51單片機的裸機開發中是經常見的。
linux驅動代碼編寫實際上就是,端對端的編程!
就是 硬件-連接-軟件
一開始是主芯片的設備樹和鏡像,配置硬件,該執行哪條總線,端口。
二就是編寫外設寫入或者讀取數據的文件,還有類似QT的代碼執行。
三就是需要把需要驅動的硬件目標和軟件操作文件進行匹配。也就是將第一步和第二步相匹配。

1、配置設備樹

打開 stm32mp157d-atk.dts 文件,把以下的內容添加到此文件中
Pasted image 20250904160715.png
與之前大多數不同的是,這里并不是節點追加的方式。是新創建的。
Pasted image 20250904160937.png
這里就不用配置鏡像了,因為沒有用到追加節點。就用到一個GPIO口。
一般來說下面我們這個代碼用到了platform框架,那么就需要用到pinctrl用來配置電氣屬性的,但是這里正點原子并沒有加pinctrl,可能是因為該引腳復位后默認就是 GPIO 功能,就無需 pinctrl 配置 “復用為 GPIO”,但是這樣并不規范!
編譯:

	make dtbs

Pasted image 20250428095407.png
復制到開發板中:

	sudo cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/chensir/linux/tftpboot/ -f

Pasted image 20250428095536.png
發現可以在設備樹下發現我們剛剛創建的節點;
Pasted image 20250904163310.png

2、DS18B20驅動編寫

之前的博客也是跟大家按照肌肉記憶來編寫程序!一步一步按照思路來編寫!
總代碼會放在最后。
為了讓大家更能明白,可以先對著總代碼,進行對我的寫代碼流程更加詳細得當!

2.1、頭文件

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/errno.h>

可以看出頭文件用到了miscdevice.h,說明這個例程代碼用到了MISC子系統。

2.2、驅動注冊和注銷

因為我這個DS18B20外設的單總線是在linux沒有總線概念的,所以用platform總線來實現總線驅動框架,這個是前面已經講過的!這個僅僅為了簡化代碼量!
Pasted image 20250904165935.png
注冊和注銷一體化:這個意思是init和exit不用開發人員寫了!
可以看以下舉例代碼:
Pasted image 20250904170607.png

2.2.1、編寫platform_driver驅動結構體

Pasted image 20250904171055.png
其中流程是:
16行代碼:設備樹中的compatible值會與ds18b20_of_match下的compatible相匹配。
如下圖ds18b20_of_match的代碼:
Pasted image 20250904171625.png
其中MODULE_DEVICE_TABLE是聲明一下而已!
15行代碼:會在driver目錄下生成ds18b20。這個是驅動開發者自己編寫的!
和設備樹中的compatible沒有關系。
18~19行代碼:compatible值一旦匹配成功,就會執行probe和remove。(這些其實之前的驅動程序講解已經講過很多遍了)

2.2.2、編寫probe和remove函數

Pasted image 20250904173259.png

2.2.3、注冊和注銷字符驅動設備

我們這里用到的MISC子系統和platform框架,所以可以回顧。
Pasted image 20250904173832.png
Pasted image 20250904173924.png
Pasted image 20250904174001.png
我們用這個的同時需要在設備結構體中定義MISC設備。
雖然說MISC子系統幫開發人員自動設置了主設備號為10,但是子設備號、設備名、字符操作集還是得開發人員自己創建。

1、定義設備結構體

struct miscdevice mdev;
這里可以放在probe函數內,但是如果需要適應創建多個MISC設備,那么就放在設備結構體中;
Pasted image 20250906192028.png
這里我們要強調一下
Pasted image 20250906193706.png
這兩種寫法要好好記一下!

2、配置probe和remove函數

這里我們先編譯測試一下;
Pasted image 20250906202001.png
發現并沒有問題!
Pasted image 20250906202028.png
接下來繼續完善probe和remove函數:
Pasted image 20250906202153.png
可以看到我們添加了名字、次設備號、字符操作集函數。
接下來注冊和注銷MISC設備:
Pasted image 20250906203450.png
Pasted image 20250906203501.png
==重點來了!!!==我們發現,只有在probe函數內,才動態分配了內存,在remove是沒有分配,不能傻傻的再用devm_kmallo函數了,在 probe 函數中動態分配的,remove 函數無法直接訪問,需要通過 platform_set_drvdataplatform_get_drvdata 傳遞指針。
接下來繼續完善:
在probe內使用 platform_set_drvdata(pdev, ds18b20_dev);
在remove內使用 ds18b20_dev = platform_get_drvdata(pdev);
Pasted image 20250906204142.png
Pasted image 20250906204201.png

3、配置字符操作集

目前這個單總線DS18B20,功能實現只需要讀數據就行,所以字符操作集只涉及,在本模塊下執行、open、release、read即可!
Pasted image 20250907111518.png
在完成寫完字符操作集之前,我們先來回顧DS18B20的時序,需要嚴格特定的時序,還有數據判定。然后再上傳數據。

4、獲取設備節點(設備樹屬性)

4.1、配置設備樹結構體

Pasted image 20250907124351.png

4.2、GPIO初始化

Pasted image 20250908182729.png
使用了 devm_gpio_request(帶 devm_ 前綴的資源申請函數)—— 這類函數申請的 GPIO 會與「設備生命周期」自動綁定,無需手動調用 gpio_free,內核會在設備卸載時自動釋放 GPIO。
這里知道了有關驅動的gpio信息,僅僅是能知道信息,并沒有驅動能力,所以要向內核申請權限來驅動gpio口。

5、配置DS18B20時序

熟悉DS18B20配置的同學就知道,需要嚴格的時序,還有高低電平轉換!所以接下來我們需要配置高低電平、定時器、還有可以把這個讀寫時序放到隊列里面!

5.1、配置輸入輸出

需要配置輸入輸出!
因為需要獲取DS18B20的溫度數據,所以需要判定GPIO的值。
Pasted image 20250908182803.png

5.2、獲取GPIO的值

Pasted image 20250908182825.png

5.3、設置定時器

Pasted image 20250907163750.png
Pasted image 20250907172045.png
Pasted image 20250907172053.png

5.4、配置工作隊列

Pasted image 20250907182702.png
Pasted image 20250907171737.png
Pasted image 20250907172156.png
Pasted image 20250907172221.png
其中:

   struct ds18b20_dev *ds18b20_dev = container_of(work, struct ds18b20_dev, work);container_of需要放在處理函數中,通過工作隊列(work)反向找到設備結構體(匹配設備)

Pasted image 20250908182935.png

5.5、完善定時任務

Pasted image 20250908183004.png

5.6、完成DS18B20的時序

5.6.1、初始化DS18B20

Pasted image 20250908183036.png

5.6.2、寫入一位數據

Pasted image 20250908183055.png

5.6.3、讀取一位數據

Pasted image 20250908183105.png

5.6.4、寫一個字節到DS18B20

Pasted image 20250908183121.png

5.6.5、從DS18B20讀取一個字節

Pasted image 20250908183135.png

5.7、完善字符操作集

Pasted image 20250907190529.png
Pasted image 20250908183205.png
Pasted image 20250908183215.png

5.8、完善工作隊列

Pasted image 20250908183241.png
編譯生成ko文件:

make

復制到

sudo cp ds18b20.ko /home/chensir/linux/nfs/rootfs/lib/modules/5.4.31/

6、編寫測試 APP

這里其實也簡單,就是傳遞2個數據!
核心代碼:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"int main()
{int fd, ret;unsigned char result[2];int TH, TL;short tmp = 0;float temperature;int flag = 0;fd = open("/dev/ds18b20", 0);if(fd < 0){perror("open device failed\n");exit(1);}elseprintf("Open success!\n");while(1){ret = read(fd, &result, sizeof(result)); if(ret == 0) {	/* 讀取到數據 */TL = result[0];TH = result[1];if((TH == 0XFF) && (TL == 0XFF))/* 如果讀取到數據為0XFFFF就跳出本次循序 */continue;if(TH > 7) {	/* 負數處理 */TH = ~TH;TL = ~TL;flag = 1;	/* 標記為負數 */}tmp = TH;tmp <<= 8;tmp += TL;if(flag == 1) {temperature = (float)(tmp+1)*0.0625; /* 計算負數的溫度 */temperature = -temperature;}else {temperature = (float)tmp *0.0625;	/* 計算正數的溫度 */}            if(temperature < 125 && temperature > -55) {	/* 溫度范圍 */printf("Current Temperature: %f\n", temperature);}}else if(ret == -1){perror("read"); break;}flag = 0;sleep(1);}close(fd);	/* 關閉文件 */
}

編譯:

arm-none-linux-gnueabihf-gcc ds18b20App.c -o ds18b20App

復制到

sudo cp ds18b20App /home/chensir/linux/nfs/rootfs/lib/modules/5.4.31/

Pasted image 20250907192925.png

8、效果

Pasted image 20250908183626.png

9、總代碼:

ds18b20.c:

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
/* #include <linux/ide.h> */
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/timer.h>
#include <linux/workqueue.h>/*ds18b20設備結構體*/
struct ds18b20_dev{struct miscdevice mdev;	/* MISC設備 */struct device_node *nd; //設備樹節點指針int    ds18b20_gpio;    //GPIO編號unsigned char data[2]; /* 接收原始數據的BUFF */struct timer_list timer;   /* 定時器 */struct work_struct work;   /* 工作隊列 */
};#define HIGH 1	
#define LOW 0/** @description	 : 	設置GPIO的輸出值* @param - value: 	輸出value的值 * @return 	     :  無*/
static void ds18b20_set_output(struct ds18b20_dev *dev, int value)
{if(value)gpio_direction_output(dev->ds18b20_gpio, 1);elsegpio_direction_output(dev->ds18b20_gpio, 0);
}/** @description	: 	設置GPIO為輸入模式* @param 		:	無* @return 	  	:   無*/
static void ds18b20_set_input(struct ds18b20_dev *dev)
{gpio_direction_input(dev->ds18b20_gpio);
}/** @description	: 	獲取GPIO的值* @param 		:	無 * @return 	  	:   GPIO的電平*/
static int ds18b20_get_io(struct ds18b20_dev *dev)
{return gpio_get_value(dev->ds18b20_gpio); 
}/** @description	: 	寫一位數據* @param 	bit	: 	要寫入的位數* @return 	  	:   無*/
static void ds18b20_write_bit(struct ds18b20_dev *dev, int bit)
{local_irq_disable();if(bit) {ds18b20_set_output(dev, LOW);udelay(5);ds18b20_set_input(dev);    /* 釋放為高阻 */udelay(55);                /* 補足到 ~60us */} else {ds18b20_set_output(dev, LOW);udelay(60);                /* 寫0保持低 ~60us */ds18b20_set_input(dev);    /* 釋放為高阻 */udelay(5);}local_irq_enable();
}/** @description	: 	讀一位數據* @param 		: 	無* @return 	  	:   返回讀取一位的數據*/
static int ds18b20_read_bit(struct ds18b20_dev *dev)
{u8 bit = 0;local_irq_disable();ds18b20_set_output(dev, LOW);udelay(2);ds18b20_set_input(dev);udelay(12);bit = ds18b20_get_io(dev) ? 1 : 0;udelay(45);local_irq_enable();return bit;
}/** @description	: 	寫一個字節到DS18B20* @param byte  : 	要寫入的字節* @return 	  	:   無*/
static void ds18b20_write_byte(struct ds18b20_dev *dev, u8 byte)
{int i;for(i = 0; i < 8; i++) {if(byte & 0x01)ds18b20_write_bit(dev,1); /* write 1 */elseds18b20_write_bit(dev,0); /* write 0 */byte >>= 1;	/* 右移一位獲取高一位的數據 */}
}/** @description	: 	讀取一個字節的數據* @param 		: 	無* @return 	  	:   讀取到的數據*/
static char ds18b20_read_byte(struct ds18b20_dev *dev)
{int i;u8 byte = 0;for(i = 0; i < 8; i++) {	/* DS18B20先輸出低位數據 ,高位數據后輸出 */if(ds18b20_read_bit(dev))byte |= (1 << i);elsebyte &= ~(1 << i);}return byte;
}/** @description	: 	GPIO的初始化函數* @param pdev	:	platform設備 	* @return 	  	:   0表示轉換成功,其它值表示轉換失敗*/
static int ds18b20_request_gpio(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct ds18b20_dev *ds18b20_dev = platform_get_drvdata(pdev);int ret;ds18b20_dev->nd = dev->of_node;if (!ds18b20_dev->nd)return -EINVAL;ds18b20_dev->ds18b20_gpio = of_get_named_gpio(ds18b20_dev->nd, "ds18b20-gpio", 0);if (!gpio_is_valid(ds18b20_dev->ds18b20_gpio))return -EINVAL;ret = devm_gpio_request(dev, ds18b20_dev->ds18b20_gpio, "DS18B20 Gpio");if (ret)return ret;ds18b20_set_input(ds18b20_dev);return 0;
}/** @description	: 	初始化DS18B20* @param 		: 	無* @return 	  	:   0,初始化成功,1,失敗*/
static int ds18b20_init(struct ds18b20_dev *dev)
{int ret = 1;  // 默認失敗int i;ds18b20_set_input(dev);udelay(10);    // 總線穩定時間ds18b20_set_output(dev, LOW);  // 拉低復位udelay(500);     // >=480usds18b20_set_input(dev);        // 釋放總線(高阻)/* 在 15~300us 窗口內輪詢檢測存在脈沖(低電平) */for (i = 0; i < 60; i++) {     // 60 * 5us = 300usudelay(5);if (ds18b20_get_io(dev) == LOW) {ret = 0;  // 初始化成功,檢測到存在脈沖break;}}/* 等待存在脈沖結束 */udelay(240);ds18b20_set_input(dev);  // 保持釋放return ret;
}/** @description		: 打開設備* @param - inode 	: 傳遞給驅動的inode* @param - filp 	: 設備文件,file結構體有個叫做pr似有ate_data的成員變量* 					  一般在open的時候將private_data似有向設備結構體。* @return 			: 0 成功;其他 失敗*/
static int ds18b20_open(struct inode *inode, struct file *filp)
{struct miscdevice *mdev = filp->private_data; /* 由 misc_open 預先設置 */struct ds18b20_dev *ds18b20 = dev_get_drvdata(mdev->this_device);filp->private_data = ds18b20;return 0;
}
/** @description		: 從設備讀取數據 * @param - filp 	: 要打開的設備文件(文件描述符)* @param - buf 	: 返回給用戶空間的數據緩沖區* @param - cnt 	: 要讀取的數據長度* @param - offt 	: 相對于文件首地址的偏移* @return 			: 讀取的字節數,如果為負值,表示讀取失敗*/
static ssize_t ds18b20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) 
{struct ds18b20_dev *ds18b20 = filp->private_data;size_t n = 2;if (!ds18b20)return -ENODEV;if (cnt < n)n = cnt;if (copy_to_user(buf, &ds18b20->data[0], n))return -EFAULT;return 0; 
}static int ds18b20_release(struct inode *inode, struct file *filp)
{return 0;
}static struct file_operations ds18b20_fops = {.owner	= THIS_MODULE,.open = ds18b20_open,.read	= ds18b20_read,.release = ds18b20_release,
};/** @description     : 使用內核的工作隊列,獲取溫度的原始數據* @param - work 	: work的結構體* @return          : 無*/
static void ds18b20_work_callback(struct work_struct *work)
{int ret;struct ds18b20_dev *dev = container_of(work, struct ds18b20_dev, work);ret = ds18b20_init(dev);if (ret)return;ds18b20_write_byte(dev, 0XCC);ds18b20_write_byte(dev, 0X44);msleep(750);ds18b20_set_input(dev);ret = ds18b20_init(dev);if (ret)return;ds18b20_write_byte(dev, 0XCC);ds18b20_write_byte(dev, 0XBE);dev->data[0] = ds18b20_read_byte(dev);dev->data[1] = ds18b20_read_byte(dev);
}/** @description     : 定時器的操作函數,每1s去獲取一次數據* @param - asg 	: 定時器的結構體* @return          : 無*/
/* 定時器回調:每秒觸發一次采集 */
static void ds18b20_timer_callback(struct timer_list *arg)
{struct ds18b20_dev *dev = from_timer(dev, arg, timer);schedule_work(&dev->work);mod_timer(&dev->timer, jiffies + msecs_to_jiffies(1000));
}/*驅動的probe函數,當驅動與設備匹配以后此函數就會執行*/
static int ds18b20_probe(struct platform_device *pdev)
{int ret;struct miscdevice *mdev;struct ds18b20_dev *ds18b20_dev;dev_info(&pdev->dev, "ds18b20 device and driver matched successfully!\n");ds18b20_dev = devm_kzalloc(&pdev->dev, sizeof(*ds18b20_dev), GFP_KERNEL);if (!ds18b20_dev) {return -ENOMEM;}platform_set_drvdata(pdev, ds18b20_dev);/* GPIO的初始化 */ret = ds18b20_request_gpio(pdev);if(ret) {return ret;}mdev = &ds18b20_dev->mdev;mdev->name = "ds18b20";mdev->minor = MISC_DYNAMIC_MINOR;mdev->fops = &ds18b20_fops;ret=misc_register(mdev);if(ret < 0){dev_info(&pdev->dev, "ds18b20 MISC match fail!\n");return -ENODEV;}/* 綁定 drvdata,供 open 通過 mdev->this_device 找回 */if (mdev->this_device)dev_set_drvdata(mdev->this_device, ds18b20_dev);/* 初始化定時器 */timer_setup(&ds18b20_dev->timer, ds18b20_timer_callback, 0);ds18b20_dev->timer.expires=jiffies + msecs_to_jiffies(1000);add_timer(&ds18b20_dev->timer);/* 初始化工作隊列 */INIT_WORK(&ds18b20_dev->work, ds18b20_work_callback);return 0;
}/*驅動的remove函數,移除驅動的時候此函數會執行*/
static int ds18b20_remove(struct platform_device *pdev)
{int ret;struct miscdevice *mdev;struct ds18b20_dev *ds18b20_dev;dev_info(&pdev->dev, "DS18B20 driver has been removed!\n");ds18b20_dev = platform_get_drvdata(pdev);mdev = &ds18b20_dev->mdev;misc_deregister(mdev);   /* 卸載定時器 */del_timer(&ds18b20_dev->timer); /* 卸載工作隊列 */cancel_work_sync(&ds18b20_dev->work);return 0;
}static const struct of_device_id ds18b20_of_match[] = {{ .compatible = "alientek,ds18b20" },{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of,ds18b20_of_match);/*platform驅動結構體*/
static struct platform_driver ds18b20_driver = {.driver = {.name			= "ds18b20",.of_match_table	= ds18b20_of_match,},.probe		= ds18b20_probe,.remove		= ds18b20_remove,
};/*注冊和注銷一體化*/
module_platform_driver(ds18b20_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("chensir");
MODULE_INFO(intree, "Y");

ds18b20App.c:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"int main()
{int fd, ret;unsigned char result[2];int TH, TL;short tmp = 0;float temperature;int flag = 0;fd = open("/dev/ds18b20", 0);if(fd < 0){perror("open device failed\n");exit(1);}elseprintf("Open success!\n");while(1){ret = read(fd, &result, sizeof(result)); if(ret == 0) {	/* 讀取到數據 */TL = result[0];TH = result[1];if((TH == 0XFF) && (TL == 0XFF))/* 如果讀取到數據為0XFFFF就跳出本次循序 */continue;if(TH > 7) {	/* 負數處理 */TH = ~TH;TL = ~TL;flag = 1;	/* 標記為負數 */}tmp = TH;tmp <<= 8;tmp += TL;if(flag == 1) {temperature = (float)(tmp+1)*0.0625; /* 計算負數的溫度 */temperature = -temperature;}else {temperature = (float)tmp *0.0625;	/* 計算正數的溫度 */}            if(temperature < 125 && temperature > -55) {	/* 溫度范圍 */printf("Current Temperature: %f\n", temperature);}}else if(ret == -1){perror("read"); break;}flag = 0;sleep(1);}close(fd);	/* 關閉文件 */
}

makefile:

KERNELDIR := /home/chensir/linux/atk-mp1/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd) 
obj-m := ds18b20.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

在全部完成之后呢,我又把單總線的GPIO口換成別的地方了,是STM32MP157的PZ6。
需要做以下改動;
在pinctrl-z下添加:

&pinctrl_z {ds18b20_pins: ds18b20-0 {pins1 {pinmux = <STM32_PINMUX('Z', 6, GPIO)>;  // PZ6 設為 GPIOdrive-open-drain;          // 開漏輸出(釋放=高阻)bias-pull-up;              // 上拉(仍建議外部4.7k)slew-rate = <0>;};};

在根節點“/”下追加:

	 	ds18b20@0 {compatible = "alientek,ds18b20";pinctrl-names = "default";pinctrl-0 = <&ds18b20_pins>;ds18b20-gpio = <&gpioz 6 GPIO_ACTIVE_HIGH>;status = "okay";

即可,配置電氣屬性,大家如果想換成別的IO口,就需要知道IO口有沒有被占用,被占用就要解除占用噢!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/96340.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/96340.shtml
英文地址,請注明出處:http://en.pswp.cn/web/96340.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【雜類】應對 MySQL 處理短時間高并發的請求:緩存預熱

一、什么是緩存預熱&#xff1f;1. 核心概念??緩存預熱&#xff08;Cache Warm-up&#xff09;?? 是指在系統??正式對外提供服務之前??&#xff0c;或??某個高并發場景來臨之前??&#xff0c;??主動??將后續極有可能被訪問的熱點數據從數據庫&#xff08;MySQL…

點評項目(Redis中間件)第三部分短信登錄,查詢緩存

可以直接看后面Redis實現功能的部分基于session實現短信登錄發送短信驗證碼前端請求樣式業務層代碼Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {Overridepublic Result sendCode(String phone, HttpSession se…

線性方程求解器的矩陣分裂

大概思路是對的&#xff0c;但是查老師可能會出現幻覺&#xff0c;小心食用 &#x1f603;這段代碼是在初始化迭代法求解器&#xff0c;構建迭代矩陣和分裂矩陣。以下是詳細解釋&#xff1a; if init_from_func or init_from_input:# 1. 存儲剛度矩陣self.stiff_p stiff_p# 2.…

【Beetle RP2350】雷達模塊 CEM5861G-M11 開發使用指南

一、硬件介紹 1、產品特點 Beetle RP2350【RP2350A_QFN60】是一款基于RP2350微控制器的高性能迷你開發板&#xff0c;雙核雙架構設計&#xff08;支持 Arm Cortex-M33或Hazard3 RISC-V內核&#xff09;為開發者提供靈活的性能配置。 雙核雙架構&#xff0c;性能自由切換 采…

高通Android 13 開機黑屏問題深度剖析與解決方案

1. 問題概述 在 Android 13 系統定制化開發過程中&#xff0c;開機流程的調試與優化頗具挑戰性。一個典型問題是&#xff1a;當開機動畫播放完畢后&#xff0c;設備會先出現數秒黑屏&#xff0c;然后才進入鎖屏界面。本文基于開機日志分析&#xff0c;結合實際項目經驗&#x…

騰訊推出AI CLI工具CodeBuddy,國內首家同時支持插件、IDE和CLI三種形態的AI編程工具廠商

2025年9月9日&#xff0c;騰訊正式推出自研AI CLI工具CodeBuddy code&#xff0c;成為國內首家同時支持插件、IDE和CLI三種形態的AI編程工具廠商。這一創新不僅填補了國內市場在全形態AI編程工具領域的空白&#xff0c;更以編碼時間縮短40%、AI生成代碼占比超50%的硬核數據&…

零基礎學習QT的第二天-組件基礎知識

組件聲明以及設置屬性 所有的組件的基類為&#xff1a;QtObject&#xff0c;在c中名稱為&#xff1a;QObject。 在qml和c名稱有所區別&#xff0c;例如在Qml中QtObject&#xff0c;在C會省略一個t(QObject) 聲明組件的方式&#xff1a; 組件名 {屬性名:值}在實際應用中&#xf…

像素圖生成小程序開發全解析:從圖片上傳到Excel圖紙

像素圖生成小程序開發全解析&#xff1a;從圖片上傳到Excel圖紙 前言 在數字化創作和工藝設計領域&#xff0c;像素圖生成工具具有廣泛的應用價值&#xff0c;無論是十字繡設計、LED燈陣布置還是復古游戲美術創作。本文將詳細解析一個功能完整的像素圖生成小程序的開發過程&…

mac-intel操作系統go-stock項目(股票分析工具)安裝與配置指南

1. 項目基礎介紹 go-stock 是一個基于Wails和NaiveUI開發的AI賦能股票分析工具。旨在為用戶提供自選股行情獲取、成本盈虧展示、漲跌報警推送等功能。它支持A股、港股、美股等市場&#xff0c;能夠進行市場整體或個股的情緒分析、K線技術指標分析等功能。所有數據均保存在本地…

spring-單例bean是線程安全的嗎

其中可修改的成員變量有線程不安全問題&#xff0c;不可修改的無狀態的 userService是沒有線程安全問題的 spring框架中有一個 Scope注解&#xff0c;默認的值就是singleton&#xff0c;單例的。 不是線程安全的&#xff0c;一般來說&#xff0c;我們在bean中注入的對象都是無狀…

CM1033系列 3串鋰電池保護IC - 高精度±25mV 內置延時 多型號可選(含鐵鋰)

1. 核心亮點 高精度多重保護&#xff1a;專為3串電池組設計&#xff0c;提供過充、過放、三級過流&#xff08;含短路&#xff09;、充電過流及斷線檢測等全方位保護&#xff0c;電壓檢測精度高達25mV。超低功耗&#xff1a;工作電流典型值僅7μA&#xff0c;休眠電流低至4μA&…

【第23話:定位建圖】SLAM后端優化方法詳解

SLAM 后端優化方法詳解 SLAM&#xff08;Simultaneous Localization and Mapping&#xff09;后端優化是SLAM系統中的關鍵環節&#xff0c;負責對前端輸出的傳感器數據進行全局一致性優化&#xff0c;消除累積誤差。后端通常基于圖優化框架&#xff08;如g2o、GTSAM&#xff09…

MongoDB 備份與恢復終極指南:mongodump 和 mongorestore 深度實戰

MongoDB 備份與恢復終極指南&#xff1a;mongodump 和 mongorestore 深度實戰引言&#xff1a;數據守護者的使命第一部分&#xff1a;基礎概念與核心原理1.1 邏輯備份 vs. 物理備份&#xff1a;根本性的區別1.2 核心工具介紹第二部分&#xff1a;mongodump 備份實戰詳解2.1 基礎…

鴻蒙的“分布式架構”理念:未來操作系統的關鍵突破

一、引言&#xff1a;為什么需要分布式架構&#xff1f; 隨著移動互聯網的發展&#xff0c;智能設備不斷普及。用戶身邊可能同時擁有 手機、平板、PC、電視、手表、耳機、智能音箱、車機 等多種終端設備。 但現實中&#xff0c;我們常遇到以下問題&#xff1a; 不同設備系統割…

MySQL 事務管理與鎖機制:解決并發場景下的數據一致性問題

前言在電商下單、金融轉賬、庫存扣減等并發業務場景中&#xff0c;若不控制數據操作的原子性與隔離性&#xff0c;極易出現 “超賣”“重復扣款”“臟讀數據” 等問題。MySQL 的事務管理與鎖機制是解決這些問題的核心技術&#xff0c;也是后端開發者必須掌握的生產環境能力。本…

MySQL集群高可用架構

一、MySQL高可用之組復制&#xff08;MGR&#xff09;1.1 組復制核心特性與優勢MySQL Group Replication&#xff08;MGR&#xff09;是基于分布式一致性協議&#xff08;Paxos&#xff09;實現的高可用集群方案&#xff0c;核心特性包括&#xff1a;自動故障檢測與恢復&#x…

判別模型 VS 生成模型

1. 判別模型&#xff08;Discriminative Models&#xff09;判別模型直接學習輸入特征&#xff08;X&#xff09;與輸出標簽&#xff08;Y&#xff09;之間的映射關系&#xff0c;即直接對條件概率P(Y|X)進行建模。判別模型關注于如何區分不同類別的數據。特點&#xff1a;直接…

代碼隨想錄算法訓練營第三十一天 | 合并區間、單調遞增的數字

合并區間&#xff1a; 這里還是先對左區間進行排序&#xff0c;判斷重疊區間&#xff0c;首先判斷是否存在元素&#xff0c;存在那么就將元素的第一個放到結果中&#xff0c;那么判斷重疊就是當前元素的左區間和結果集里的最后元素的右區間進行判斷&#xff0c;如果重疊&#x…

EXCEL VBA 清空Excel工作表(Sheet)的方法

1. 刪除所有內容&#xff0c;但保留格式和對象 這種方法只會清除單元格的內容&#xff0c;不會影響格式和嵌入的圖表或對象。 Sub ClearSheetContents()Worksheets("Sheet1").Cells.ClearContents End Sub2. 刪除所有內容和格式&#xff0c;但保留對象 這種方法會刪除…

智能客戶服務支持智能體

超越傳統客服機器人。智能體可以深度查詢知識庫、調用訂單系統API、甚至根據客戶情緒靈活處理退貨、退款、升級投訴等復雜流程。 案例&#xff1a; 客戶說&#xff1a;“我上周買的鞋子尺碼不對&#xff0c;想換貨但是找不到訂單頁面了。” 智能體行動&#xff1a; ① 通過用戶…