自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(92)
  • 收藏
  • 关注

原创 软件I2C

这一限制在复杂的嵌入式系统中可能导致布线困难,特别是当需要连接多个 I2C 设备或在 PCB 布局上有特殊要求时,固定的引脚分配可能会增加设计复杂度。此外,当需要多个独立的 I2C 总线时,可用的硬件 I2C 控制器数量将成为系统扩展的瓶颈。在使用硬件I2C时,我们将SDA引脚与SCL引脚都设置为复用开漏输出,而在软件实现I2C时,我们要把引脚模式设置为通用开漏输出,初始化完成后,把SDA和SCL都写一,保持高电平,我们选择PB8作为SCL,PB9作为SDA。因此本节我们使用软件来模拟实现I2C的通信。

2025-11-12 10:46:09 369

原创 I2C编程

与GPIO和USART一样,I2C也是STM32F103C8T6 的一个片上外设。STM32F103C8T6 的 I2C 模块是一款兼容 I2C v2.1 规范的高性能通信接口,支持标准模式 (100kbps) 和快速模式 (400kbps),可配置为主机或从机模式,具备 7 位 / 10 位地址识别、PEC 错误检测、DMA 传输和中断驱动等功能。

2025-11-12 10:42:55 1031

原创 I2C通信

I2C(Inter-Integrated Circuit,集成电路间总线)是由 Philips(现 NXP)开发的 短距离、同步串行通信总线,核心特点是 仅需 2 根线(SDA 数据线 + SCL 时钟线) 即可实现多设备间通信,广泛应用于嵌入式系统(如 STM32 与传感器、EEPROM、LCD 模块的连接“逻辑线与” 是指多根信号线通过硬件连接到同一条总线时,总线的最终电平由 “所有设备的输出电平” 通过 “与逻辑” 决定逻辑与规则:只要有一个设备输出低电平(0),总线最终电平就是低电平(0)

2025-11-09 21:14:33 1292

原创 USART编程

USART模块中有若干个状态寄存器,使用USART接收数据时就需要读取其中**RXNE(Read Data Register Not Empty ,接收数据寄存器非空)**中的值。发送数据函数需要传入三个参数,第一个参数表明需要哪个USART模块,第二个参数指向我们需要传送的数据的数组首地址,第三个参数表明发送数据的个数。在上一章我们提到USART模块中有若干个状态寄存器,使用USART发送数据时就需要读取其中**TXE(发送数据寄存器空,可写新数据),意味着它可以被重写,当我们通过串口把数据发送出去时,

2025-11-08 14:57:59 825

原创 单片机通信协议--USART(串口通信)

USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里自带波特率发生器,最高达4.5Mbits/s可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)可选校验位(无校验/奇校验/偶校验)

2025-11-08 14:51:08 1172 1

原创 GPIO及LED闪灯实验

在 STM32 开发中,GPIO(General-Purpose Input/Output,通用输入输出)是最基础且常用的外设,用于控制 LED、按键、传感器等外部设备。

2025-11-03 09:50:22 1331

原创 STM32F103C8T6简介

STM32 是意法半导体(STMicroelectronics)推出的一系列基于ARM Cortex-M 内核的 32 位微控制器(MCU),广泛应用于嵌入式系统开发,是目前业界最流行的单片机系列之一。

2025-11-03 09:48:53 1388

原创 Linux高级IO

I/O(input/output)是指输入和输出,在著名的冯·诺依曼体系结构当中,将数据从输入设备拷贝到内存就叫做输入,将数据从内存拷贝到输出设备就叫做输出OS如何得知外设当中有数据可读取?输入就是操作系统将数据从外设拷贝到内存的过程,操作系统一定要通过某种方法得知特定外设上是否有数据就绪需要注意的是,CPU不直接和外设打交道指的是在数据层面上,而外设其实是可以直接将某些控制信号发送给CPU当中的某些控制器的OS如何处理从网卡中读取到的数据包?操作系统任何时刻都可能会收到大量的数据包,因此操作系统必须将这些

2025-07-16 17:45:43 1001

原创 Linux线程池

线程池(Thread Pool)是一种基于池化思想的并发编程技术,其核心概念是通过预先创建并维护一组可重用的线程,以高效地处理并发任务。

2025-07-16 17:40:23 989

原创 Linux信号量

多个执行流为了访问临界资源会竞争式的申请信号量,因此信号量是会被多个执行流同时访问的,也就是说信号量本质也是临界资源。但实际我们可以将这块临界资源再分割为多个区域,当多个执行流需要访问临界资源时,如果这些执行流访问的是临界资源的不同区域,那么我们可以让这些执行流同时访问临界资源的不同区域,此时不会出现数据不一致等问题。当执行流在申请信号量时,可能此时信号量的值为0,也就是说信号量描述的临界资源已经全部被申请了,此时该执行流就应该在该信号量的等待队列当中进行等待,直到有信号量被释放时再被唤醒。

2025-07-11 14:45:40 803

原创 Linux生产者消费者模型

生产者和消费者彼此之间不直接通讯,而通过这个容器来通讯,所以生产者生产完数据之后不用等待消费者处理,直接将生产的数据放到这个容器当中,消费者也不用找生产者要数据,而是直接从这个容器里取数据,这个容器就相当于一个缓冲区,平衡了生产者和消费者的处理能力,这个容器实际上就是用来给生产者和消费者解耦的。代码中生产者生产数据就是将获取到的随机数Push到阻塞队列,而消费者消费数据就是从阻塞队列Pop数据,为了便于观察,我们可以将生产者生产的数据和消费者消费的数据进行打印输出。

2025-07-11 14:42:28 1106

原创 Git(五):标签管理与多人协作(一)

相较于难以记住的 commit id ,tag 很好的解决这个问题,因为tag⼀定是⼀个容易记住,且有意义的名字。⽬前,我们的仓库中只有⼀个master主分⽀,但在实际的项⽬开发中,在任何情况下其实都是不允许 直接在master分⽀上修改代码的,这是为了保证主分⽀的稳定。最后不要忘记,虽然我们是在分支上进行多人协作开发,但最终的目的是要将开发后的代码合并到 master上去,让我们的项目运行最新的代码。如果标签已经推送到远程,要删除远程标签就麻烦⼀点,先从本地删除,然后,从远程删除。

2025-07-04 16:21:55 1159

原创 C++类型转换

但对于单参数的自定义类型来说,A a2 = 1这种代码的可读性不是很好,因此可以用explicit修饰单参数的构造函数,从而禁止单参数构造函数的隐式转换。C语言和C++都是强类型语言,如果赋值运算符左右两侧变量的类型不同,或形参与实参的类型不匹配,或返回值类型与接收返回值的变量类型不一致,那么就需要进行类型转换。其中,向上转型就是所说的切割/切片,是语法天然支持的,不需要进行转换,而向下转型是语法不支持的,需要进行强制类型转换。dynamic_cast用于将父类的指针(或引用)转换成子类的指针(或引用)

2025-07-03 13:35:04 1048

原创 C++智能指针

而如果连接结点时只进行一个连接操作,那么当node1和node2的生命周期结束时,就会有一个资源对应的引用计数被减为0,此时这个资源就会被释放,这个释放后另一个资源的引用计数也会被减为0,最终两个资源就都被释放了,这就是为什么只进行一个连接操作时这两个结点就都能够正确释放的原因。其次,shared_ptr中的引用计数count也不能定义成一个静态的成员变量,因为静态成员变量是所有类型对象共享的,这会导致管理相同资源的对象和管理不同资源的对象用到的都是同一个引用计数。

2025-07-03 11:50:09 849

原创 Linux线程安全

例如,现在有两个线程访问一块临界区,一个线程往临界区写入数据,另一个线程从临界区读取数据,但负责数据写入的线程的竞争力特别强,该线程每次都能竞争到锁,那么此时该线程就一直在执行写入操作,直到临界区被写满,此后该线程就一直在进行申请锁和释放锁。多个线程并发的操作共享变量,就会带来一些问题。临界区内的线程完全可能进行线程切换,但即便该线程被切走,其他线程也无法进入临界区进行资源访问,因为此时该线程是拿着锁被切走的,锁没有被释放也就意味着其他线程无法申请到锁,也就无法进入临界区进行资源访问了。

2025-06-30 14:09:11 1108

原创 C++异常

实际中很多公司都会自定义自己的异常体系进行规范的异常管理。公司中的项目一般会进行模块划分,让不同的程序员或小组完成不同的模块,如果不对抛异常这件事进行规范,那么负责最外层捕获异常的程序员就非常难受了,因为他需要捕获大家抛出的各种类型的异常对象因此实际中都会定义一套继承的规范体系,先定义一个最基础的异常类,所有人抛出的异常对象都必须是继承于该异常类的派生类对象,因为异常语法规定可以用基类捕获抛出的派生类对象,因此最外层就只需捕获基类就行了。

2025-06-30 14:01:20 919

原创 C++11--线程库

因此可能当线程1刚将n的值加载到寄存器中就被切走了,也就是只完成了++操作的第一步,而线程2可能顺利完成了一次完整的++操作才被切走,而这时线程1继续用之前加载到寄存器中的值完成剩余的两步操作,最终就会导致两个线程分别对共享变量n进行了一次++操作,但最终n的值却只被++了一次。多线程最主要的问题是共享数据带来的问题(即线程安全)。线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的,就算线程函数的参数为引用类型,在线程函数中修改后也不会影响到外部实参,因为其实际引用的是线程栈中的拷贝,而不是外部实参。

2025-06-23 17:13:46 929

原创 Git(四):远程操作

我们⽬前所说的所有内容(⼯作区,暂存区,版本库等等),都是在本地!也就是在你的笔记本或者计算机上。⽽我们的Git其实是**分布式版本控制系统!**什么意思呢?可以简单理解为,我们每个⼈的电脑上都是⼀个完整的版本库,这样你⼯作的时候,就不需要联⽹了,因为版本库就在你⾃⼰的电脑上。既然每个⼈电脑上都有⼀个完整的版本库,那多个⼈如何协作呢?⽐⽅说你在⾃⼰电脑上改了⽂件A,你的同事也在他的电脑上改了⽂件A,这时,你们俩之间只需把各⾃的修改推送给对⽅,就可以互相看到对⽅的修改了分布式版本控制系统的安全性要⾼很多,

2025-06-23 17:10:52 716

原创 Git(三):分支管理

本章介绍Git的杀手级功能之一:分支分支就 是科幻电影里面的平行宇宙,当你正在电脑前努力学习C++的时候,另⼀个你正在另⼀个平行宇宙里 努力学习JAVA。如果两个平行宇宙互不⼲扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并 了,结果,你既学会了C++又学会了JAVA!在版本回退里,你已经知道,每次提交,Git都把它们串成⼀条时间线,这条时间线就可以理解为是一个分支。截止到目前,只有⼀条时间线,在Git里,这个分支叫主分支,即master分支。

2025-06-19 20:20:42 1006

原创 Linux多线程

在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”一切进程至少都有一个执行线程线程在进程内部运行,本质是在进程地址空间内运行在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更轻量化透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。

2025-06-19 20:07:28 1431

原创 Git(二):基本操作

如果有⼀天你发现之前的⼯作做的出现了很⼤的问题,需要在某个特定的历史版本重新开始,这个时候,就需要版本回退的功能。比如你新增了一行,这就是⼀个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚⾄创建一个新文件,也算一个修改。让我们将 ReadMe 文件进行一次修改,此时,仓库中的 ReadMe 和我们⼯作区的 ReadMe 是不同的, 使用。如果我们在我们的⼯作区写了很⻓时间代码,越写越写不下去,觉得⾃⼰写的实在是垃圾,想恢复到 上⼀个版本。

2025-06-18 15:14:10 717

原创 Git(一):初识Git

Git 是一个分布式版本控制系统,由 Linus Torvalds 于 2005 年为管理 Linux 内核开发而创建。其核心功能是跟踪文件的修改历史,并支持多人协作开发,使团队成员能够高效地管理代码变更、回溯版本、合并修改等。

2025-06-17 15:33:18 1048

原创 C++11--包装器

function是一种函数包装器,也叫做适配器。Ret:被包装的可调用对象的返回值类型Args…:被包装的可调用对象的形参类型public:class Pluspublic:int main()//1、包装函数指针(函数名)//2、包装仿函数(函数对象)//3、包装lambda表达式//4、类的静态成员函数//&可省略//5、类的非静态成员函数//&不可省略return 0;

2025-06-17 15:27:34 964

原创 Linux任务管理与守护进程

这些进程组的控制终端相同,它们同属于一个会话,当用户在控制终端输入特殊的控制键(如Ctrl+C产生SIGINT,Ctrl+\产生SIGQUIT,Ctrl+Z产生SIGTSTP),内核就会发送相应的信号给前台进程组中的所有进程。当我们用Xshell或是终端登录时,本质都是先创建一个bash进程,整体称之为一个会话(所有的命令行的进程都是bash的子进程),所有的命令行启动的任务都是在对应的会话内运行的。需要注意的是,只要在某个进程组中有一个进程存在,则该进程组就存在,这与其组长进程是否终止无关。

2025-06-16 17:00:02 865

原创 C++11--Lambda表达式

现在要对若干商品分别按照价格和数量进行升序、降序排序。仿函数确实能够解决这里的问题,但可能仿函数的定义位置可能和使用仿函数的地方隔得比较远,这就要求仿函数的命名必须要通俗易懂,否则会降低代码的可读性这样一来,每次调用sort函数时只需要传入一个lambda表达式指明比较方式即可,阅读代码的人一看到lambda表达式就知道本次排序的比较方式是怎样的,大大提高代码的可读性。

2025-06-16 16:55:06 945

原创 C++11--可变模板参数

返回类型 函数名(Args… args)//函数体{}模板参数Args前面有省略号,代表它是一个可变模板参数,我们把带省略号的参数称为参数包,参数包里面可以包含0到N ( N ≥ 0 ) N(N\geq 0)N(N≥0)个模板参数,而args则是一个函数形参参数包模板参数包Args和函数形参参数包args的名字可以任意指定,并不是说必须叫做Args和argsint main()ShowList();return 0;//获取参数包中参数的个数。

2025-06-13 10:07:15 1271

原创 C++11--类的新功能

上述代码中用一个右值去构造s2对象,但由于Person类没有生成默认的移动构造函数,因此这里会调用Person的拷贝构造函数(拷贝构造既能接收左值也能接收右值),这时在Person的拷贝构造函数中就会调用string的拷贝构造函数对name成员进行深拷贝。如果要让Person类生成默认的移动构造函数,就必须将Person类中的拷贝构造、拷贝赋值和析构函数全部注释掉,这时用右值去构造s2对象时就会调用Person默认生成的移动构造函数。

2025-06-11 15:02:05 847

原创 Linux进程信号(二)

理论上来说是可以的,因为内核态是一种权限非常高的状态,但是绝对不能这样设计如果允许在内核态直接执行用户空间的代码,那么用户就可以在代码中设计一些非法操作,比如清空数据库等,虽然在用户态时没有足够的权限做到清空数据库,但是如果是在内核态时执行了这种非法代码,那么数据库就真的被清空了,因为内核态是有足够权限清空数据库的也就是说,不能让操作系统直接去执行用户的代码,因为操作系统无法保证用户的代码是合法代码,即操作系统不信任任何用户。

2025-06-11 14:58:31 731

原创 Linux进程信号(一)

核心转储是Linux系统中用于调试程序崩溃的关键机制,通过记录进程终止时的内存状态,帮助开发者快速定位问题根源。其配置涉及信号触发、路径设置、存储管理及分析工具的使用,需根据具体需求平衡调试便利性与系统资源占用在云服务器中,核心转储是默认被关掉的,我们可以通过使用ulimit -a命令查看当前资源限制的设定我们可以通过。

2025-06-09 17:19:32 801

原创 Linux进程间通信(二)

管道通信本质是基于文件的,也就是说操作系统并没有为此做过多的设计工作,而system V IPC是操作系统特地设计的一种通信方式。但是不管怎么样,它们的本质都是一样的,都是在想尽办法让不同的进程看到同一份由操作系统提供的资源其中,system V共享内存和system V消息队列是以传送数据为目的的,而system V信号量是为了保证进程间的同步与互斥而设计的,虽然system V信号量和通信好像没有直接关系,但属于通信范畴。

2025-06-09 17:11:41 963

原创 Linux进程间通信(一)

进程间通信简称IPC(Interprocess communication),进程间通信就是在不同进程之间传播或交换信息。

2025-06-04 09:46:19 964

原创 Linux中的31个普通信号

2025-06-04 09:39:04 338

原创 C++11 -- 右值引用和移动语义

上述代码中的插入第一个元素时就会匹配到push_back的左值引用版本,在push_back函数内部就会调用string的拷贝构造函数进行深拷贝,而插入后面三个元素时由于传入的是右值,因此会匹配到push_back的右值引用版本,此时在push_back函数内部就会调用string的移动构造函数进行资源的转移。移动赋值是一个赋值运算符重载函数,该函数的参数是右值引用类型的,移动赋值也是将传入右值的资源窃取过来,占为己有,这样就避免了深拷贝,所以它叫移动赋值,就是窃取别人的资源来赋值给自己的意思。

2025-05-27 11:47:52 1037

原创 Linux 动态库与静态库

对于可能频繁用到的源文件,比如这里的test1.c、test2.c、test3.c、test4.c,我们可以将它们的目标文件test1.o、test2.o、test3.o、test4.o进行打包,之后需要用到这四个目标文件时就可以之间链接这个包当中的目标文件了,而这个包实际上就可以称之为一个库。因为我们使用gcc编译的是C语言,而gcc就是用来编译C程序的,所以gcc编译的时候默认就找的是C库,但此时我们要链接的是哪一个库编译器是不知道的,因此我们还是需要使用-l选项,指明需要链接库文件路径下的哪一个库。

2025-05-27 11:42:30 1114

原创 Linux基础IO(二)

而当我们将运行结果重定向到log.txt文件时,数据的刷新策略就变为了全缓冲,此时我们使用printf和fputs函数打印的数据都打印到了C语言自带的缓冲区当中,之后当我们使用fork函数创建子进程时,由于进程间具有独立性,而之后当父进程或是子进程对要刷新缓冲区内容时,本质就是对父子进程共享的数据进行了修改,此时就需要对数据进行写时拷贝,至此缓冲区当中的数据就变成了两份,一份父进程的,一份子进程的,所以重定向到log.txt文件当中printf和puts函数打印的数据就有两份。

2025-05-20 10:29:03 895

原创 C++11入门

在2003年C++标准委员会提交了一份技术勘误表(简称TC1),使得C++03这个名字取代了C++98成为C++11之前的最新C++标准名称但由于C++03主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把这两个标准合并称为C++98/03标准从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准姗姗来迟。

2025-05-20 10:19:32 889

原创 Linux基础IO(一)

当进程打开log.txt文件时,我们需要先将该文件从磁盘当中加载到内存,形成对应的struct file,将该struct file连入文件双链表,并将该结构体的首地址填入到fd_array数组当中下标为3的位置,使得fd_array数组中下标为3的指针指向该struct file,最后返回该文件的文件描述符给调用进程即可。当文件存储在磁盘当中时,我们将其称之为磁盘文件,而当磁盘文件被加载到内存当中后,我们将加载到内存当中的文件称之为内存文件。open函数的返回值是新打开文件的文件描述符。

2025-05-15 14:44:02 757

原创 特殊类设计

单例模式是一种设计模式(Design Pattern),设计模式就是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式的目的就是为了可重用代码、让代码更容易被他人理解、保证代码可靠性程序的重用性单例模式指的就是一个类只能创建一个对象,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

2025-05-15 14:37:25 792

原创 哈希应用--海量数据处理

海量数据处理是指基于海量数据的存储和处理,正因为数据量太大,所以导致要么无法在短时间内迅速处理,要么无法一次性装入内存对于时间问题,就可以采用位图、布隆过滤器等数据结构来解决对于空间问题,就可以采用哈希切割等方法,将大规模的数据转换成小规模的数据逐个击破。

2025-05-09 14:23:47 709

原创 布隆过滤器

所以布隆过滤器没有提供删除的接口,因为使用布隆过滤器本来就是要节省空间和提高效率的。首先,布隆过滤器可以实现为一个模板类,因为插入布隆过滤器的元素不仅仅是字符串,也可以是其他类型的数据,只有调用者能够提供对应的哈希函数将该类型的数据转换成整型即可,但一般情况下布隆过滤器都是用来处理字符串的,所以这里可以将模板参数K的缺省类型设置为string。此外,哈希函数的个数也需要权衡,哈希函数的个数越多布隆过滤器中比特位被设置为1的速度越快,并且布隆过滤器的效率越低,但如果哈希函数的个数太少,也会导致误判率变高。

2025-05-09 14:21:19 828

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除