STM32 软件I2C读写
单片机学习!
目录
前言
一、软件I2C读写代码框架
二、I2C初始化
三、六个时序基本单元
3.1 引脚操作的封装和改名
3.2 起始条件执行逻辑
3.3 终止条件执行逻辑
3.4 发送一个字节
3.5 接收一个字节
3.5 发送应答&接收应答
3.5.1 发送应答
3.5.2 接收应答
总结
前言
本文介绍了软件I2C读写代码,I2C协议层重点关注的是使用的两个引脚、I2C配置、时序的高低电平等协议相关的内容。
一、软件I2C读写代码框架
代码整体框架:首先建立I2C通信层的.c和.h模块,在通信层里写好I2C底层的GPIO初始化和6个时序基本单元,也就是起始、终止、发送一个字节、接收一个字节、发送应答和接收应答。
由于本代码使用软件I2C,所以I2C的库函数暂时不用看,软件I2C只需要用GPIO的读写函数就行了。
软件I2C初始化要做两个任务:
- 第一个任务,把SCL和SDA都初始化为开漏输出模式;
- 第二个任务,把SCL和SDA置高电平。
二、I2C初始化
当前接线SCL是PB10,SDA是PB11。所以要开启GPIOB,PB10和PB11都要配置成开漏输出的模式。虽然开漏输出名字上带了个输出,但并不代表它只能输出,开漏输出模式仍然可以输入。输入时先输出1再直接读取输入数据寄存器就行了。这个过程在之前博文讲I2C硬件规定时介绍过。
代码示例:
void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启时钟GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_OD;GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);//把GPIOB的PB10和PB11都置高电平}
调用MyI2C_Init函数,PB10和PB11两个端口就被初始化为开漏输出模式,然后释放总线。SCL和SDA处于高电平,此时I2C总线处于空闲状态。
三、六个时序基本单元
3.1 引脚操作的封装和改名
第一个基本单元,起始条件。根据波形图,首先把SCL和SDA都确保释放。然后先拉低SDA,再拉低SCL,这样就能产生起始条件了。
在这里可以不断地调用SetBits和ResetBit函数,来手动翻转高低电平。但是这样做会在后面的程序中出现非常多的地方来指定这个GPIO端口号。一方面,这样做语义不是很明显;另一方面,如果之后需要换一个端口那就需要改动非常多的地方。所以这时就需要在上面做个定义,把端口号统一替换一个名字,这样无论是语义还是端口的修改都会非常方便。
给端口号换一个名字有很多方法都能实现功能,一种简单的替换方法就是宏定义,
#define SCL_PORT GPIOB
#define SCL_PIN GPIO_Pin_10
之后如果想释放SCL,就调用SetBits函数将GPIOB替换为SCL_PORT;将GPIO_Pin_10替换为SCL_PIN。
GPIO_SetBits(GPIOB,GPIO_Pin_10);
GPIO_SetBits(SCL_PORT,SCL_PIN);
这样语义比较明确,而且修改引脚的时候直接在上面修改一下宏定义,下面所有引用宏定义的地方都会自动更改。但是这样宏定义的方法如果换到一个主频很高的单片机中,需要对软件时序进行延时操作的时候,也不太方便进一步修改。所以这里也可以直接一点,定义函数对操作端口的库函数进行封装,这样既容易理解,又方便加软件延时。
定义函数对操作端口的库函数进行封装代码示例:
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
}
MyI2C_W_SCL这个W代表写的意思,函数里调用GPIO_WriteBit函数,第三个参数给BitValue强转为BitAction类型。
这样套一个函数替换之后,后面再调用MyI2C_W_SCL函数,参数给1或0,就可以释放或拉低SCL了。
如果要把这个程序移植到别的单片机,就可以把这个函数里的操作替换为其他单片机对应的操作。
比如SCL是51单片机的P10口,就可以把
GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
上面这句替换为下面这句
P10=BitValue;
另外如果单片机主频比较快,函数里也非常方便加一些延时,比如这里要求每次操作引脚之后都要延时10us,就可以在引脚操作之后调用延时函数进行引脚延时操作了。I2C可以慢一些,多慢都行,但是快的话还是要看一下手册里对时序时间的要求。
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);Delay_us(10);
}
同理封装一下SDA:
//SDA封装
void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);Delay_us(10);
}
另外还要再来个读SDA的函数,因为STM32库函数中,读和写不是同一个寄存器
uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);Delay_us(10);return BitValue;
}
函数MyI2C_R_SDA中R代表读的意思,读出SDA之后也延时10us,返回读到SDA线的电平。
有了以上三个函数的封装就实现了函数名称、端口号的替换。同时也可以很方便地修改时序的延时。当需要替换端口时,或者把这个程序移植到别的单片机中时就只需要对这前4个函数里的操作对应更改,后面的函数都调用这里封装的新名称进行操作,这样在移植的时候后面的部分就不需要再进行修改了。
以上关于引脚操作的封装和改名就完成了。
3.2 起始条件执行逻辑
在起始条件里需要先把SCL和SDA都释放,也就是都输出1.然后先拉低SDA,再拉低SCL。
代码示例:
void MyI2C_Start(void)
{MyI2C_W_SDA(1);MyI2C_W_SCL(1);MyI2C_W_SDA(0);MyI2C_W_SCL(0);
}
代码中需注意,最好将释放SDA的放在最前面,这样更符合起始条件的波形,这样保险一些。
如果起始条件之前的SCL和SDA已经是高电平了,那不管先释放哪一个都是一样的效果。但是看下图中Sr这里,Start还要兼容这里的重复起始条件Sr,Sr最开始SCL是低电平,SDA电平不敢确定。所以保险起见,趁着SCL是低电平,先释放SDA;再释放SCL。这时SDA和SCL都是高电平。
然后再拉低SDA,拉低SCL。这样Start就可以兼容起始条件和重复起始条件了。
3.3 终止条件执行逻辑
当Stop开始,如果SDA和SCL都已经是低电平了,那就先释放SCL,再释放SDA就行了。但是在时序单元开始时,SDA并不一定是低电平。所以为了确保之后释放SDA能产生上升沿,要在时序单元开始时,先拉低SDA,然后再释放SCL、释放SDA。
所以在程序里Stop的执行逻辑是:先拉低SDA,再释放SCL,再释放SDA。
void MyI2C_Stop(void)
{MyI2C_W_SDA(0);MyI2C_W_SCL(1);MyI2C_W_SDA(1);
}
终止条件后,SCL和SDA都回归到高电平。
3.4 发送一个字节
函数的参数是要发送的一个字节。发送一个字节时序开始时,SCL是低电平。实际上除了终止条件,SCL以高电平结束,所有的单元都会保证SCL以低电平结束,这样方便各个单元的拼接。
上图所示,SCL低电平,变换数据;SCL高电平,保持数据稳定。由于是高位先行,所以变换数据的时候按照先放最高位,再放次高位,最后最低位,这样的顺序依次把一个字节的每一位放在SDA线上。每放完一位后,执行释放SCL,拉低SCL的操作,驱动时钟运转。
在程序中的操作就是,首先趁SCL低电平,先把Byte的最高位放在SDA线上,写SDA,写1还是写0取决于Byte的最高位。这里需要取出Byte的最高位,可以用 (Byte & 0x80) ,这是一个单片机中非常常见的操作。就是用按位与的方式取出数据的某一位或某几位。
按位与取出数据某一位的运行逻辑:
- Byte可以是任意的数据 xxxx xxxx
- 0x80就是 1000 0000
- Byte与0x80按位与结果 x000 0000
低7位因为和0相与,所以结果不受Byte数据的影响,始终是0;
最高位和1相与,所以结果取决于Byte的最高位。
- 如果Byte的最高位是1,结果就是1000 0000,也就是0x80;
- 如果Byte最高位是0,结果就是0000 0000,也就是0x00.
这就相当于把Byte的最高位取出来了。但是注意,Byte & 0x80 这个式子计算结果是0x80或0x00,而不是1或0.不过上文函数将参数BitValue强转为BitAction类型,就是非0即1,所以即使传入0x80也相当于传入了1,代码中可以直接写 (Byte & 0x80)。
上面方法步骤将最高位数据放好后,再释放SCL,再拉低SCL,驱动时钟走一个脉冲。
MyI2C_W_SDA(Byte & 0x80);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
当释放SCL之后,从机就会立刻把放好在SDA的数据读走,再拉低SCL,然后就可以放下一个数据了,下一位是次高位。
MyI2C_W_SDA(Byte & 0x40);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
写SDA,数据与0x40,取出次高位,再驱动SCL,来一个时钟。
之后继续,写SDA,数据与0x20,取出再下一位,再驱动SCL,来一个时钟。
MyI2C_W_SDA(Byte & 0x20);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
这样来8次这个操作,就可以写入一个字节。不过可以套个for循环,循环8次减少代码量。
void MyI2C_SendByte(uint8_t Byte);
{uint8_t i;for(i=0;i<8;i++){MyI2C_W_SDA(Byte & (0x80 >>i));MyI2C_W_SCL(1);MyI2C_W_SCL(0);}
}
定义一个迭代变量i,循环八次,然后把上述重复的操作单元放在里面。
第一次循环 i=0,需要 (Byte & 0x80)
第二次循环 i=1,需要 (Byte & 0x40)
第三次循环 i=2,需要 (Byte & 0x20)
...
所以这里的通式就是(Byte & (0x80 >>i)) 注意要加个括号,确保优先级。
3.5 接收一个字节
接收一个字节时序开始时,SCL低电平,此时从机需要把数据放到SDA上,为了防止主机干扰从机写入数据,主机需要先释放SDA,释放SDA也相当于切换为输入模式。那在SCL低电平时,从机会把数据放到SDA。
- 如果从机想发1,就释放SDA;
- 如果从机想发0,就拉低SDA;
然后主机释放SCL,在SCL高电平期间,读取SDA,再拉低SCL。SCL低电平期间从机就会把下一位数据放到SDA上。这样重复八次主机就能得到一个字节了。
在这里可以发现,SCL低电平变换数据,高电平读取数据。实际上就是一种读写分离的设计:
- 低电平时间定义为写的时间;
- 高电平时间定义为读的时间。
那在SCL高电平期间,如果非要动SDA来破坏读写规则的话,那这个信号就是起始条件和终止条件。SCL高电平时,SDA下降沿为起始条件,SDA上升沿为终止条件。这个设计也保证了起始条件和终止条件的特异性,能够在连续不断的波形中快速地定位起始和终止。因为起始终止与数据传输的波形有本质区别:
- 数据传输SCL高电平不许动SDA;
- 起始终止SCL高电平必须动SDA。
这就是这个设计的巧妙之处。
进接收一个字节的时序之后,SCL是低电平,主机释放SDA。从机把数据放到SDA时,主机释放SCL,SCL高电平时,主机就能读取数据了。
uint8_t MyI2C_ReceiveByte(void)
{uint8_t Byte = 0x00;MyI2C_W_SDA(1);MyI2C_W_SCL(1);if(MyI2C_R_SDA() == 1){Byte |= 0x80;}MyI2C_W_SCL(0);}
读取数据用MyI2C_R_SDA函数,套个if,如果读SDA为1,if成立,就知道接收这一位为1了。先定义一个数据Byte,给初始值0x00.
- 如果第一次读SDA为1,就Byte |= 0x80; 把Byte最高位置1;
- 如果第一次读SDA为0,if条件不成立,Byte默认为0x00,就相当于写如0了。
读取一位之后,再把SCL拉低。这时从机就会把下一位数据放到SDA上。再执行下方代码相同的流程8次就能接收一个字节了。
MyI2C_W_SCL(1);if(MyI2C_R_SDA() == 1){Byte |= 0x80;}MyI2C_W_SCL(0);
可以用个for循环,把上方代码放进去,循环8次,依次从高位到低位进行判断。所以在写个和发送一个字节一样的移位操作。就可以接收一个字节了。最后return Byte; 把接收的Byte返回去。
3.5 发送应答&接收应答
发送应答和接收应答其实就是发送一个字节和接收一个字节的简化版:
- 发送一个字节是发8位,发送应答是发1位;
- 接收一个字节是收8位,接收应答是收1位。
所以程序这里可以参照发送一个字节和接收一个字节来修改。
3.5.1 发送应答
将发送一个字节的代码中的for循环去掉,修改一下。
void MyI2C_SendAck(uint8_t AckBit)
{MyI2C_W_SDA(AckBit);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
}
现在的逻辑是:函数进来时,SCL低电平。
- 主机把AckBit放到SDA上。
- SCL高电平,从机读取应答。
- SCL低电平,进入下一个时序单元。
3.5.2 接收应答
将接收一个字节的代码中的for循环去掉,修改一下。读SDA时,直接把读到的值,赋值给AckBit就行了。最后返回读AckBit。
uint8_t MyI2C_ReceiveAck(void)
{uint8_t AckBit;MyI2C_W_SDA(1);MyI2C_W_SCL(1);AckBit = MyI2C_R_SDA();MyI2C_W_SCL(0);return AckBit;
}
现在的逻辑是:函数进来时,SCL低电平。
- 主机释放SDA,防止干扰从机,同时从机把应答位放在SDA上。
- SCL高电平,主机读取应答位。
- SCL低电平,进入下一个时序单元。
代码疑问:
1.程序里主机先把SDA置1了,然后再读取SDA,应答位就肯定是1吗?
可以从两点分析:
- 第一,I2C的引脚都是开漏输出+弱上拉的配置,主机输出1并不是强置SDA为高电平,而是释放SDA。
- 第二,I2C是在进行通信,主机释放了SDA,那从机如果在的话,从机是有义务把SDA再拉低的。
所以即使主机在前面把SDA置1了,之后再读取SDA,读到的值也可能是0.
- 读到0代表从机给了应答。
- 读到1代表从机没给应答。
2.接收一个字节的代码里不断读取SDA,但是for循环中又没写过SDA,那SDA读出来应该始终是一个值吗?
I2C进行通信是有从机的,当主机不断驱动SCL时钟时,从机就有义务去改变SDA的电平。所以主机每次循环读取SDA的时候,这个读取到的数据是从机控制的,这个读取到的数据也正是从机想要给我们发送的数据。这也就是这个时序叫做接收一个字节。
通信是有时序的,有些引脚的电平之前读和之后读,读的值就是不一样的。
四、总代码示例
.c代码示例:
#include "stm32f10x.h" // Device header
#include "Delay.h" //CL封装
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);Delay_us(10);
}//SDA封装
void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);Delay_us(10);
}//读SDA的函数
uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);Delay_us(10);return BitValue;
}void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启时钟GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_OD;GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);//把GPIOB的PB10和PB11都置高电平}//六个时序基本单元//起始条件
void MyI2C_Start(void)
{MyI2C_W_SDA(1);MyI2C_W_SCL(1);MyI2C_W_SDA(0);MyI2C_W_SCL(0);
}//终止条件
void MyI2C_Stop(void)
{MyI2C_W_SDA(0);MyI2C_W_SCL(1);MyI2C_W_SDA(1);
}//发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;for(i=0;i<8;i++){MyI2C_W_SDA(Byte & (0x80 >>i));MyI2C_W_SCL(1);MyI2C_W_SCL(0);}
}//接收一个字节
uint8_t MyI2C_ReceiveByte(void)
{uint8_t i, Byte = 0x00;MyI2C_W_SDA(1);for(i = 0 ; i < 8 ; i++){MyI2C_W_SCL(1);if(MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}MyI2C_W_SCL(0);}return Byte;
}//发送应答
void MyI2C_SendAck(uint8_t AckBit)
{MyI2C_W_SDA(AckBit);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
}//接收应答
uint8_t MyI2C_ReceiveAck(void)
{uint8_t AckBit;MyI2C_W_SDA(1);MyI2C_W_SCL(1);AckBit = MyI2C_R_SDA();MyI2C_W_SCL(0);return AckBit;
}
.h代码示例:
#ifndef __MYI2C_H__
#define __MYI2C_H__void MyI2C_Init(void);//起始条件
void MyI2C_Start(void);//终止条件
void MyI2C_Stop(void);//发送一个字节
void MyI2C_SendByte(uint8_t Byte);//接收一个字节
uint8_t MyI2C_ReceiveByte(void);//发送应答
void MyI2C_SendAck(uint8_t AckBit);//接收应答
uint8_t MyI2C_ReceiveAck(void);#endif
总结
以上就是今天要讲的内容,本文仅仅简单介绍了软件I2C读写代码。其中有使用的两个引脚、I2C配置、时序的高低电平等协议相关内容的代码配置细节。
相关文章:
STM32 软件I2C读写
单片机学习! 目录 前言 一、软件I2C读写代码框架 二、I2C初始化 三、六个时序基本单元 3.1 引脚操作的封装和改名 3.2 起始条件执行逻辑 3.3 终止条件执行逻辑 3.4 发送一个字节 3.5 接收一个字节 3.5 发送应答&接收应答 3.5.1 发送应答 3.5.2 接…...
MySQL数据导出导出的三种办法(1316)
数据导入导出 基本概述 目前常用的有3中数据导入与导出方法: 使用mysqldump工具: 优点: 简单易用,只需一条命令即可完成数据导出。可以导出表结构和数据,方便完整备份。支持过滤条件,可以选择导出部分数据…...
三层交换机配置
✍作者:柒烨带你飞 💪格言:生活的情况越艰难,我越感到自己更坚强;我这个人走得很慢,但我从不后退。 📜系列专栏:网路安全入门系列 目录 一,三层交换二,实验案…...
odoo中@api.model, @api.depends和@api.onchange 装饰器的区别
文章目录 1. api.model用途特点示例 2. api.depends用途特点示例 3. api.onchange用途特点示例 总结 在 Odoo 中,装饰器(decorators)用于修饰方法,以指定它们的行为和触发条件。api.model、api.depends 和 api.onchange 是三个常用…...
Mysql的事务隔离机制
文章目录 事务基础概念隔离性与隔离机制的重要性四种隔离级别读未提交读已提交可重复读串行化 隔离级别设置与查看 事务基础概念 事务是一组数据库操作,这些操作要么全部成功执行,要么全部不执行。在 MySQL 中,事务通常以START TRANSACTION开…...
鸿蒙项目云捐助第二十八讲云捐助项目首页组件云数据库加载轮播图
鸿蒙项目云捐助第二十八讲云捐助项目首页组件云数据库加载轮播图 在前面的章节中实现了云捐赠项目的底部导航和分类导航,本讲继续使用云技术丰富首页组件中的功能。这里使用云数据库进行首页组件轮播图的加载。 一、云数据库进行首页组件轮播图的加载 在云捐助项…...
vue项目搭建规范
项目搭建规范 一. 代码规范1.1. 集成editorconfig配置1.2. 使用prettier工具1.3. 使用ESLint检测1.4. git Husky和eslint1.5. git commit规范1.5.1. 代码提交风格1.5.2. 代码提交验证 二. 第三方库集成2.1. vue.config.js配置2.2. vue-router集成2.3. vuex集成2.4. element-plu…...
Spring Boot的开发工具(DevTools)模块中的热更新特性导致的问题
问题: java.lang.ClassCastException: class cn.best.scholarflow.framework.system.domain.entity.SysUser cannot be cast to class cn.best.scholarflow.framework.system.domain.entity.SysUser (cn.best.scholarflow.framework.system.domain.…...
Unity Shader TexelSize的意义
TexelSize在制作玻璃折射效果时会用到。 // Get the normal in tangent space fixed3 bump UnpackNormal(tex2D(_BumpMap, i.uv.zw)); // Compute the offset in tangent space float2 offset bump.xy * _Distortion * _RefractionTex_TexelSize.xy; i.scrPos.xy offset * i…...
一个C#开发的APP
开发方式 C#Web、AndroidWebView 系统设计 系统主要分两个部分。一个是内容(文章)发布系统,另一个是预约和支付系统。 内容发布系统 和普通的文章发布系统不一样的地方在于,我们把每篇文章和大师关联起来。在文章的下方会显示…...
C++ 设计模式:原型模式(Prototype Pattern)
链接:C 设计模式 链接:C 设计模式 - 工厂方法 链接:C 设计模式 - 抽象工厂 链接:C 设计模式 - 建造者模式 原型模式(Prototype Pattern)是一种创建型设计模式,它允许一个对象通过复制现有对象来…...
window如何将powershell以管理员身份添加到右键菜单?(按住Shift键显示)
window如何将powershell以管理员身份添加到右键菜单? 在 Windows 中,将 PowerShell 以管理员身份添加到右键菜单,可以让你在需要提升权限的情况下快速打开 PowerShell 窗口。以下是详细的步骤,包括手动编辑注册表和使用注册表脚本…...
python爬虫——爬取全年天气数据并做可视化分析
一、主题页面的结构与特征分析 1.主题页面的结构与特征分析 目标内容界面: 2. Htmls 页面解析 3.节点查找方法与遍历方法 查找方法:find(): 查找第一个匹配到的节点。find_all(): 查找所有匹配到的节点,并返回一个…...
【Unity3D】ECS入门学习(十二)IJob、IJobFor、IJobParallelFor
IJob:开启单个线程进行计算,线程内不允许对同一个数据进行操作,也就是如果你想用多个IJob分别计算,将其结果存储到同一个NativeArray<int>数组是不允许的,所以不要这样做,如下例子就是反面教材&#…...
存储进阶笔记(二):Linux 存储栈:从 Device Mapper、LVM 到文件系统(2024)
记录一些平时接触到的存储知识。由于是笔记而非教程,因此内容不求连贯,有基础的同学可作查漏补缺之用。 存储进阶笔记(一):硬件基础:HDD/SDD、JBOD、RAID 等(2024) 存储进阶笔记&am…...
MySQL——操作
一.库的操作 1.基本操作 创建数据库 create database 数据库名称; 查看数据库 show databases; 删除数据库 drop database 数据库名称; 执行删除之后的结果: 数据库内部看不到对应的数据库对应的数据库文件夹被删除,级联删除,里面的数据表全部被删 所…...
c++表达范围勿用数学符号
目的 遇上了一个C基础问题,一下子陷到里面,不知怎么回事了,知道后,又感觉太可笑。 这也许就是成长的代价。 下面就是细说说所遇上的问题。 关于C逻辑的一些知识点: 定义: 用逻辑运算符将两个表达式链接起来的式子称为…...
SAP PP bom历史导出 ALV 及XLSX 带ECN号
bom总数 104W PS超过XLSX上限 ,那就分文件 *&---------------------------------------------------------------------* *& Report ZRPT_PP_BOM_HIS_ECN *&---------------------------------------------------------------------* *& tcode:zpp0…...
【AIGC-ChatGPT职业提示词指令】智能职业规划助手:基于SVG可视化的职业发展指南系统
引言 在当今快速变化的职场环境中,职业发展规划变得越来越复杂和充满挑战。无论是想要转行的技术人员,还是希望突破瓶颈的职场人士,都需要一个清晰的指导方向和可执行的行动计划。基于这种需求,我们设计了一个智能职业规划助手系统,它能够通过数据可视化的方式,为用户提…...
node.js之---单线程异步非阻塞 I/O
单线程模型 1、Node.js 使用 单线程 来处理客户端请求和执行任务 2、如果遇到异步任务,node.js使用事件循环和异步 I/O 模型,使得它能够高效地处理大量并发请求 异步操作有哪些 1、读取文件 2、网络请求 3、数据库操作等等 异步非阻塞 I/O Node.…...
DotnetSpider实现网络爬虫
1. 使用DotnetSpider框架 DotnetSpider是一个开源的、轻量、灵活、高性能、跨平台的分布式网络爬虫框架,适用于.NET平台。它可以帮助开发者快速实现网页数据的抓取功能。 1.1 安装DotnetSpider NuGet包 首先,你需要在你的.NET项目中安装DotnetSpider NuGet包。你可以通过…...
01 Oracle 基本操作
Oracle 基本操作 初使用步骤 1.创建表空间 2.创建用户、设置密码、指定表空间 3.给用户授权 4.切换用户登录 5.创建表 注意点:oracle中管理表的基本单位是用户 文章目录 了解Oracle体系结构 1.创建表空间**2.删除表空间**3.创建用户4.给用户授权5.切换用户登录6.表操…...
纯血鸿蒙ArkUI线性布局详解
线性布局说明 线性布局(LinearLayout)是开发中最常用的布局,通过线性容器Row和Column构建。线性布局是其他布局的基础,其子元素在线性方向上(水平方向和垂直方向)依次排列。线性布局的排列方向由所选容器组…...
MySQL root用户密码忘记怎么办(Reset root account password)
在使用MySQL数据库的的过程中,不可避免的会出现忘记密码的现象。普通用户的密码如果忘记,可以用更高权限的用户(例如root)进行重置。但是如果root用户的密码忘记了,由于root用户本身就是最高权限,那这个方法…...
18.springcloud_openfeign之扩展组件二
文章目录 一、前言二、子容器默认组件FeignClientsConfigurationDecoder的注入Contract约定对注解的支持对类上注解的支持对方法上注解的支持对参数上注解的支持@MatrixVariable@PathVariable@RequestParam@RequestHeader@SpringQueryMap@RequestPart@CookieValueFormattingCon…...
18、【OS】【Nuttx】用gdb调试nuttx os
背景 接之前wiki 14、【OS】【Nuttx】Nsh中运行第一个程序 15、【OS】【Nuttx】OS裁剪,运行指定程序,周期打印当前任务 程序跑起来了,OS也裁剪了,下一步就是调试了 目标 用gdb把nuttx程序跑起来 准备环境 vscode商店里C和C相…...
网络爬虫科普:原理、类型、策略与常用工具
网络爬虫科普:原理、类型、策略与常用工具 网络爬虫在当今互联网时代扮演着极为重要的角色,它能帮助我们从海量的网络信息中提取出有价值的数据。以下将从网络爬虫的基本概念、工作流程、类型、搜索策略以及常用工具等方面进行详细科普介绍。 一、网络…...
SQL 实战:动态表创建与多表更新的高级 SQL
在实际的数据库管理和开发中,经常需要临时存储中间计算结果或对多表数据进行批量更新。SQL 提供了动态表创建和多表更新的能力,使复杂业务逻辑能够通过一条 SQL 语句高效完成。本文将介绍如何动态创建临时表和实现多表联动更新,并通过具体示例…...
RabbitMQ基础篇之快速入门
文章目录 一、目标需求二、RabbitMQ 控制台操作步骤1.创建队列2.交换机概述3.向交换机发送消息4.结果分析5.消息丢失原因 三、绑定交换机与队列四、测试消息发送五、消息查看六、结论 一、目标需求 新建队列:创建 hello.queue1 和 hello.queue2 两个队列。消息发送…...
NLP论文速读(NeurIPS 2024)|BERT作为生成式上下文学习者BERTs are Generative In-Context Learners
论文速读|BERTs are Generative In-Context Learners 论文信息: 简介: 本文探讨了在自然语言处理(NLP)领域中,上下文学习(in-context learning)的能力,这通常与因果语言模型&#x…...
LeetCode - Google 校招100题 第7天 序列(数据结构贪心) (15题)
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/144744418 相关文章: LeetCode 合计最常见的 112 题: 校招100题 第1天 链表(List) (19题)校招100题 第2天 树(Tree) (21…...
基于Docker基础与操作实战
6.1 Docker容器简介 Docker是一个开源的应用容器引擎,它基于Go语言并遵从Apache2.0 协议开源。 Docker是一个用于开发,交付和运行应用程序的开放平台。Docker能将应用程序与基础架构分开,从而可以快速交付软件。借助Docker,您可…...
高转化的Facebook广告文案的秘诀
Facebook 广告文案是制作有效 Facebook 广告的关键方面。它侧重于伴随广告视觉元素的文本内容。今天我们的博客将深入探讨成功的 Facebook 广告文案的秘密! 一、广告文案怎么写? 正文:这是帖子的正文,出现在您姓名的正下方。它可…...
支持向量机入门指南:从原理到实践
目录 1 支持向量机的基本概念 1.2 数学表达 2 间隔与支持向量 2.1 几何间隔 2.2 支持向量的概念 2.3 规范化超平面 2.4 支持向量的深入分析 2.4.1 支持向量的特征 2.4.2 支持向量的作用 2.4.3 支持向量的代数表示 2.5 KKT条件 3 最优化问题 3.1 问题的形成 3.2 规…...
汽车打气泵方案|智能充气泵工作原理
汽车打气泵方案最开始是机械式的开发,后来慢慢地演变成由一个气缸、压力传感器和ADC芯片以及主控芯片,就能够使得打气筒具备智能充气功能,摇身一变变成汽车打气泵方案。它具备精准压力检测以及过充过放等功能,利用ADC芯片和压力传…...
Jenkins入门使用
Jenkins入门使用 1先安装jdk才能运行jenkins yum install -y java-1.8.0-openjdk.x86_64 2 安装jenkins,运行,进行端口绑定,启动jenkins docker search jenkins docker pull jenkins/jenkins docker run -d -u root -p 8080:8080 -p 50000:50…...
iOS Masonry对包体积的影响
01 Masonry介绍 Masonry是iOS在控件布局中经常使用的一个轻量级框架,Masonry让NSLayoutConstraint使用起来更为简洁。Masonry简化了NSLayoutConstraint的使用方式,让我们可以以链式的方式为我们的控件指定约束。 常用接口声明与实现: 使用方式…...
Hive分区再分桶表
在Hive中,数据通常是根据分区(partition)来组织的,但是对于大数据集,单层分区可能不够用,因此可以进一步细分为桶(bucket)。桶可以用于提供额外的并行处理和优化查询性能。在这种情况…...
三大行业案例:AI大模型+Agent实践全景
本文将从AI Agent和大模型的发展背景切入,结合51Talk、哈啰出行以及B站三个各具特色的行业案例,带你一窥事件驱动架构、RAG技术、人机协作流程,以及一整套行之有效的实操方法。具体包含内容有:51Talk如何让智能客服“主动进攻”&a…...
国产低代码框架zdppy开发笔记002 标准的接口响应
前言 通过前面的学习, 我们已经知道了zdppy_api和zdppy_req的基本用法, 接下来我们会在学习中多次用到这两个框架. 我们已经知道了该如何响应一个字符串,但是我们该如何响应json数据呢? 在zdppy_api中,我们定义了一组规范的API响应, 我们慢慢来看看. 规范的响应 首先来看…...
关于Nginx
1.Nginx的配置 proxy_pass http: 当你需要将请求分发到多个后端服务器时,需要实现负载均衡功能,可以使用upstream指令定义一组服务器,并在proxy_pass中引用这个服务组名称。。如果不需要负载均衡,只需要将请求转发到单一的后端…...
数据库实时会话管理,性能问题诊断后的临门一脚
目录 前言 实时会话管理 DBdoctor 实时会话功能 1.实时会话列表 2.结束会话 3.操作历史 4.SQL分析 结语 前言 在之前的文章中我们介绍了DBdoctor性能洞察功能,它能够快速量化数据库连接会话单条SQL的资源消耗,实现性能问题快速根因定位并给出优…...
以EM算法为例介绍坐标上升(Coordinate Ascent)算法:中英双语
中文版 什么是 Coordinate Ascent 算法? Coordinate Ascent(坐标上升)是一种优化算法,它通过在每次迭代时优化一个变量(或一个坐标),并保持其他变量不变,逐步逼近最优解。与坐标下…...
visual studio连接sql server数据库
目录 1、为什么要建立连接2、在sql server中建立数据库3、visual studio连接sql server数据库4、学生信息管理系统页面布局5、添加事件逻辑 5.1 页面跳转5.2 读取学生信息5.3 查询学生信息5.4 修改学生信息5.5 删除学生信息5.6 添加学生信息 bilibili演示视频 github源码 1、…...
磁盘的相关操作
1.让U盘连接到虚拟机中 两种方法:1>在弹出的窗口中设置 2>通过选项设置 菜单栏---->虚拟机----->可移动设备---->找到U盘名---->连接到虚拟机中 2.查看U盘是否已被成功识别 方法:ls /dev/sd* 显示包含除了sda外的文件说明U盘连接成功…...
数据结构与算法Python版 图的应用与广度优先搜索
文章目录 一、图的应用-词梯问题二、图的广度优先搜索 一、图的应用-词梯问题 词梯问题 Word Ladder 从一个单词演变到另一个单词,其中的过程可以经过多个中间单词。要求是相邻两个单词之间差异只能是1个字母如FOOL变SAGE:FOOL >> POOL >>…...
Unity——InputField组件自动换行和enter键换行
文章目录 输入框实现换行功能 输入框实现换行功能 在Unity中,如果你想要在输入框(如InputField)中实现换行功能 ,你需要确保以下几点: 1、文本组件支持多行: 确保你的InputField的文本组件(Te…...
solr9.7 单机安装教程
1.环境要求:jdk11以上 2.下载wget https://dlcdn.apache.org/solr/solr/9.7.0/solr-9.7.0.tgz 3.解压 4.修改solr.in.sh配置 5.启动命令 bin/solr start 6.创建core bin/solr create -c <core名称> 注意:用solr ui界面创建,会提示找不到solrconfig.xml和m…...
虚幻引擎UE5渲染不够快的解决办法
虚幻引擎是由Epic Games公司开发的一款功能强大、全球最开放且先进的实时 3D 创作工具,广泛应用于游戏、影视、建筑可视化、虚拟现实等多个领域!虚幻引擎UE5如何实现在网上极速渲染呢?本文提供云渲染和云电脑两套方案用于渲染提速ÿ…...
基于STM32的智能家居环境监控系统设计
目录 引言系统设计 硬件设计软件设计系统功能模块 环境监控模块控制模块显示模块系统实现 硬件实现软件实现系统调试与优化结论与展望 1. 引言 随着智能家居技术的发展,环境监控系统已经成为家居管理的重要组成部分。智能家居环境监控系统通过实时监测室内温度、湿…...