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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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; ① 通过用户…