ARM I2C通信

news2024/12/23 7:34:03

1.概念

	I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工同步总线,主要用于连接整体电路

2.IIC总线硬件连接

在这里插入图片描述

1.IIC总线支持多主机多从机,但是在实际开发过程中,大多数采用单主机多从机模式
2.挂接到IIC总线上,每个从机设备都有自己的7bit从机地址
3.在总线上,发送数据的叫做发送器,接收数据叫做接收器
4.主动发起数据的叫做主机,只能被动接收数据的叫做从机
5.时钟信号由主机产生,作用:给从机,为了IIC总线上传输数据同步

3.IIC总线时序

3.1起始信号

在这里插入图片描述

在SCL为高电平期间,SDA从高到低的变化(下降沿),属于起始信号
起始信号由主机产生,起始信号产生之后,总线占用状态

3.2停止信号

在这里插入图片描述

在SCL为高电平期间,SDA从低到高的变化(上升沿),属于终止信号
停止信号由主机产生,停止信号产生之后,总线空闲状态

3.3数据传输信号(读写)

在这里插入图片描述

1.在SCL为高电平期间,数据线上的数据保持稳定,接收器从数据线上读取数据
2.在SCL为低电平期间,数据线上的数据允许变化,发送器向数据线上写入数据

3.4应答信号

在这里插入图片描述

1.每一个字节必须保证是8位长度。数据传送时,先传送高位,在发送低位,每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)
2.发送器在发送完8位数据位之后,接收器在第9个时钟周期,返回一个应答信号(0),或者非应答信号(1)
    在第9个时钟周期,接收器向数据线上写入数据
    在第9个时钟周期,发送器从数据线上读取数据
    如果读取到0,代表应答信号
    如果读取到1,代表非应答信号

3.5寻址

在这里插入图片描述

1.IIC总线上传输数据是广义的,可以传输地址信号,也可以传输数据信号
2.主机在产生起始信号之后,必须传送7位从机地址,加上读写位
3.用0表示写,用1表示读

4.IIC框图

在这里插入图片描述

5.IIC总线协议

5.1主机给从机发送一个字节

在这里插入图片描述

5.2主机给从机发送多个连续字节

在这里插入图片描述

5.3主机从从机读一个字节

在这里插入图片描述

5.4主机从从机读多个连续字节

在这里插入图片描述

6.GPIO模拟IIC协议

在这里插入图片描述

7.分析SI7006芯片手册

7.1思路

1.分析SI7006芯片手册内部实现框图
2.分析SI7006从机地址
3.分析SI7006芯片通信协议
4.分析找到采集温湿度传感器命令码
5.找到将采集的模拟量转换为数字量的公式
6.分析SI7006初始化的值

7.2从机地址

在这里插入图片描述

通过以上分析可知,si7006芯片从机地址0x40
从机地址 + 读:0x40 << 1 | 1
从机地址 + 写:0x40 << 1 | 0

7.3分析命令码

在这里插入图片描述

7.4协议

在这里插入图片描述

7.5公式

在这里插入图片描述
在这里插入图片描述

7.6初始化值

在这里插入图片描述

8.代码

// si7006.h
#ifndef __SI7006_H__
#define __SI7006_H__

#include "iic.h"
#define SI7006_SLAVE 0x40

void si7006_init(void);

unsigned short si7006_read_hum_data(unsigned char slave_addr, unsigned char cmd_code);
short si7006_read_temp_data(unsigned char slave_addr, unsigned char cmd_code);

#endif //__SI7006_H__

// si7006.c
#include "iic.h"
#include "si7006.h"
/*
 * 函数名:si7006_init
 * 函数功能:SI7006芯片的初始化
 * 函数参数:无
 * 函数返回值:无
 */
extern void delay(int ms);
// 往SI7006芯片0XE6写入0X3A
void si7006_init(void)
{
	// I2初始化
	i2c_init();
	// 发送起始信号
	i2c_start();
	// 主机发送7位从机地址+1位写位
	i2c_write_byte(0X40 << 1 | 0);
	// 等待从机回应
	i2c_wait_ack();
	// 发送寄存器地址
	i2c_write_byte(0XE6);
	// 等待从机回应
	i2c_wait_ack();
	// 发送要写的数据
	i2c_write_byte(0X3A);
	// 等待从机回应
	i2c_wait_ack();
	// 发送终止信号
	i2c_stop();
}
/*
 * 函数名:si7006_read_hum_data
 * 函数功能:读取SI7006的湿度转换结果
 * 函数参数:
 *     slave_addr : 从机地址
 *     cmd_code : 命令码
 * 函数返回值:湿度测量的数字量
 */
unsigned short si7006_read_hum_data(unsigned char slave_addr,
									unsigned char cmd_code)
{
	unsigned short dat;			// 保存读取到的湿度数据
	unsigned char dat_h, dat_l; // 保存读取到的数据的高八位和低八位
	// 发送起始信号
	i2c_start();
	// 主机发送7位从机地址+1位写位
	i2c_write_byte(slave_addr << 1 | 0);
	// 等待从机回应
	i2c_wait_ack();
	// 发送寄存器地址
	i2c_write_byte(cmd_code);
	// 等待从机回应
	i2c_wait_ack();
	// 发送第二次起始信号
	i2c_start();
	// 主机发送7位从机地址+1位写位
	i2c_write_byte(slave_addr << 1 | 1);
	// 等待从机回应
	i2c_wait_ack();
	// 延时等待从机测量数据
	delay(100);
	// 读取数据的高8位
	dat_h = i2c_read_byte(0); // 读取完毕发送应答信号
	// 读取数据的低8位
	dat_l = i2c_read_byte(1); // 读取完毕发送非应答信号
	// 发送停止信号
	i2c_stop();
	// 将读取到的数据整合到一起
	dat = (dat_h << 8) | dat_l;
	return dat;
}
/*
 * 函数名:si7006_read_temp_data
 * 函数功能:读取SI7006的温度转换结果
 * 函数参数:
 *     slave_addr : 从机地址
 *     cmd_code : 命令码
 * 函数返回值:温度测量的数字量
 */
short si7006_read_temp_data(unsigned char slave_addr,
							unsigned char cmd_code)
{
	short dat;		   // 保存读取到的温度数据
	char dat_h, dat_l; // 保存读取到的数据的高八位和低八位
	// 发送起始信号
	i2c_start();
	// 主机发送7位从机地址+1位写位
	i2c_write_byte(slave_addr << 1 | 0);
	// 等待从机回应
	i2c_wait_ack();
	// 发送寄存器地址
	i2c_write_byte(cmd_code);
	// 等待从机回应
	i2c_wait_ack();
	// 发送第二次起始信号
	i2c_start();
	// 主机发送7位从机地址+1位写位
	i2c_write_byte(slave_addr << 1 | 1);
	// 等待从机回应
	i2c_wait_ack();
	// 延时等待从机测量数据
	delay(100);
	// 读取数据的高8位
	dat_h = i2c_read_byte(0); // 读取完毕发送应答信号
	// 读取数据的低8位
	dat_l = i2c_read_byte(1); // 读取完毕发送非应答信号
	// 发送停止信号
	i2c_stop();
	// 将读取到的数据整合到一起
	dat = (dat_h << 8) | dat_l;
	return dat;
}

// iic.h
#ifndef __IIC_H__
#define __IIC_H__
#include "stm32mp1xx_gpio.h"
#include "stm32mp1xx_rcc.h"
// #include "gpio.h"
/* 通过程序模拟实现I2C总线的时序和协议
 * GPIOF ---> AHB4
 * I2C1_SCL ---> PF14
 * I2C1_SDA ---> PF15
 *
 * */

#define SET_SDA_OUT                     \
	do                                  \
	{                                   \
		GPIOF->MODER &= (~(0x3 << 30)); \
		GPIOF->MODER |= (0x1 << 30);    \
	} while (0)
#define SET_SDA_IN                      \
	do                                  \
	{                                   \
		GPIOF->MODER &= (~(0x3 << 30)); \
	} while (0)

#define I2C_SCL_H                   \
	do                              \
	{                               \
		GPIOF->BSRR |= (0x1 << 14); \
	} while (0)
#define I2C_SCL_L                  \
	do                             \
	{                              \
		GPIOF->BRR |= (0x1 << 14); \
	} while (0)

#define I2C_SDA_H                   \
	do                              \
	{                               \
		GPIOF->BSRR |= (0x1 << 15); \
	} while (0)
#define I2C_SDA_L                  \
	do                             \
	{                              \
		GPIOF->BRR |= (0x1 << 15); \
	} while (0)

#define I2C_SDA_READ (GPIOF->IDR & (0x1 << 15))

void delay_us(void);
void i2c_init(void);
void i2c_start(void);
void i2c_stop(void);
void i2c_write_byte(unsigned char dat);
unsigned char i2c_read_byte(unsigned char ack);
unsigned char i2c_wait_ack(void);
void i2c_ack(void);
void i2c_nack(void);

#endif

// icc.h
#include "iic.h"

extern void printf(const char *fmt, ...);
/*
 * 函数名 : delay_us
 * 函数功能:延时函数
 * 函数参数:无
 * 函数返回值:无
 * */
void delay_us(void)
{
	unsigned int i = 2000;
	while (i--)
		;
}
/*
 * 函数名 : i2c_init
 * 函数功能: i2C总线引脚的初始化, 通用输出,推挽输出,输出速度,
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_init(void)
{
	// 使能GPIOF端口的时钟
	RCC->MP_AHB4ENSETR |= (0x1 << 5);
	// 设置PF14,PF15引脚为通用的输出功能
	GPIOF->MODER &= (~(0xF << 28));
	GPIOF->MODER |= (0x5 << 28);
	// 设置PF14, PF15引脚为推挽输出
	GPIOF->OTYPER &= (~(0x3 << 14));
	// 设置PF14, PF15引脚为高速输出
	GPIOF->OSPEEDR |= (0xF << 28);
	// 设置PF14, PF15引脚的禁止上拉和下拉
	GPIOF->PUPDR &= (~(0xF << 28));
	// 空闲状态SDA和SCL拉高
	I2C_SCL_H;
	I2C_SDA_H;
}

/*
 * 函数名:i2c_start
 * 函数功能:模拟i2c开始信号的时序
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_start(void)
{
	/*
	 * 开始信号:时钟在高电平期间,数据线从高到低的变化
	 *     --------
	 * SCL         \
	 *              --------
	 *     ----
	 * SDA     \
	 *          --------
	 * */
	// 确保SDA是输出状态 PF15输出
	SET_SDA_OUT;
	// 空闲状态SDA和SCL拉高
	I2C_SCL_H;
	I2C_SDA_H;
	delay_us(); // 延时等待一段时间
	I2C_SDA_L;	// 数据线拉低
	delay_us(); // 延时等待一段时间
	I2C_SCL_L;	// 时钟线拉低,让总线处于占用状态
}

/*
 * 函数名:i2c_stop
 * 函数功能:模拟i2c停止信号的时序
 * 函数参数:无
 * 函数返回值:无
 * */

void i2c_stop(void)
{
	/*
	 * 停止信号 : 时钟在高电平期间,数据线从低到高的变化
	 *             ----------
	 * SCL        /
	 *    --------
	 *    ---         -------
	 * SDA   X       /
	 *    --- -------
	 * */
	// 确保SDA是输出状态 PF15输出
	SET_SDA_OUT;
	// 时钟线拉低
	I2C_SCL_L;
	delay_us(); // 延时等待一段时间
	I2C_SDA_L;	// 数据线拉低
	delay_us(); // 延时等待一段时间
	// 时钟线拉高
	I2C_SCL_H;
	delay_us(); // 延时等待一段时间
	I2C_SDA_H;	// 数据线拉高
}

/*
 * 函数名: i2c_write_byte
 * 函数功能:主机向i2c总线上的从设备写8bits数据
 * 函数参数:dat : 等待发送的字节数据
 * 函数返回值: 无
 * */

void i2c_write_byte(unsigned char dat)
{
	/*
	 * 数据信号:时钟在低电平期间,发送器向数据线上写入数据
	 *          时钟在高电平期间,接收器从数据线上读取数据
	 *      ----          --------
	 *  SCL     \        /        \
	 *           --------          --------
	 *      -------- ------------------ ---
	 *  SDA         X                  X
	 *      -------- ------------------ ---
	 *
	 *      先发送高位在发送低位
	 * */
	// 确保SDA是输出状态 PF15输出
	SET_SDA_OUT;
	unsigned int i;
	for (i = 0; i < 8; i++)
	{
		// 时钟线拉低
		I2C_SCL_L;
		delay_us(); // 延时
		// 0X3A->0011 1010   0X80->10000000
		if (dat & 0X80) // 最高位为1
		{
			// 发送1
			I2C_SDA_H;
		}
		else // 最高位为0
		{
			I2C_SDA_L; // 发送0
		}
		delay_us(); // 延时
		// 时钟线拉高,接收器接收
		I2C_SCL_H;
		delay_us(); // 延时,用于等待接收器接收数据
		delay_us(); // 延时
		// 将数据左移一位,让原来第6位变为第7位
		dat = dat << 1;
	}
}

/*
 * 函数名:i2c_read_byte
 * 函数功能: 主机从i2c总线上的从设备读8bits数据,
 *          主机发送一个应答或者非应答信号
 * 函数参数: 0 : 应答信号   1 : 非应答信号
 * 函数返回值:读到的有效数据
 *
 * */
unsigned char i2c_read_byte(unsigned char ack)
{
	/*
	 * 数据信号:时钟在低电平期间,发送器向数据线上写入数据
	 *          时钟在高电平期间,接收器从数据线上读取数据
	 *      ----          --------
	 *  SCL     \        /        \
	 *           --------          --------
	 *      -------- ------------------ ---
	 *  SDA         X                  X
	 *      -------- ------------------ ---
	 *
	 *      先接收高位, 在接收低位
	 * */
	unsigned int i;
	unsigned char dat; // 保存接受的数据
	// 将数据线设置为输入
	SET_SDA_IN;
	for (i = 0; i < 8; i++)
	{
		// 先把时钟线拉低,等一段时间,保证发送器发送完毕数据
		I2C_SCL_L;
		delay_us();
		delay_us(); // 保证发送器发送完数据
		// 时钟线拉高,读取数据
		I2C_SCL_H;
		delay_us();
		dat = dat << 1;	  // 数值左移  0000 0000
		if (I2C_SDA_READ) // pf15管脚得到了一个高电平输入
		{
			dat |= 1; // 0000 0110
		}
		else
		{
			dat &= (~0X1);
		}
		delay_us();
	}
	if (ack)
	{
		i2c_nack(); // 发送非应答信号,不再接收下一次数据
	}
	else
	{
		i2c_ack(); // 发送应答信号
	}
	return dat;
}
/*
 * 函数名: i2c_wait_ack
 * 函数功能: 主机作为发送器时,等待接收器返回的应答信号
 * 函数参数:无
 * 函数返回值:
 *                  0:接收到的应答信号
 *                  1:接收到的非应答信号
 * */
unsigned char i2c_wait_ack(void)
{
	/*
	 * 主机发送一个字节之后,从机给主机返回一个应答信号
	 *
	 *                   -----------
	 * SCL              /   M:读    \
	 *     -------------             --------
	 *     --- ---- --------------------
	 * SDA    X    X
	 *     ---      --------------------
	 *     主  释   从机    主机
	 *     机  放   向数据  读数据线
	 *         总   线写    上的数据
	 *         线   数据
	 * */
	// 时钟线拉低,接收器可以发送信号
	I2C_SCL_L;
	I2C_SDA_H; // 先把数据线拉高,当接收器回应应答信号时,数据线会拉低
	delay_us();
	SET_SDA_IN; // 设置数据线为输入
	delay_us();
	delay_us();
	I2C_SCL_H;		  // 用于读取数据线数据
	if (I2C_SDA_READ) // PF15得到一个高电平输入,收到非应答信号
		return 1;
	I2C_SCL_L; // 时钟线拉低,让数据线处于占用状态
	return 0;
}
/*
 * 函数名: iic_ack
 * 函数功能: 主机作为接收器时,给发送器发送应答信号
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_ack(void)
{
	/*            --------
	 * SCL       /        \
	 *    -------          ------
	 *    ---
	 * SDA   X
	 *    --- -------------
	 * */
	// 保证数据线是输出
	SET_SDA_OUT;
	I2C_SCL_L; // 拉低时钟线
	delay_us();
	I2C_SDA_L; // 数据线拉低,表示应答信号
	delay_us();
	I2C_SCL_H; // 时钟线拉高,等待发送器读取应答信号
	delay_us();
	delay_us();
	I2C_SCL_L; // 数据线处于占用状态,发送器发送下一次数据
}
/*
 * 函数名: iic_nack
 * 函数功能: 主机作为接收器时,给发送器发送非应答信号
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_nack(void)
{
	/*            --------
	 * SCL       /        \
	 *    -------          ------
	 *    --- ---------------
	 * SDA   X
	 *    ---
	 * */
	// 保证数据线是输出
	SET_SDA_OUT;
	I2C_SCL_L; // 拉低时钟线
	delay_us();
	I2C_SDA_H; // 数据线拉高,表示非应答信号
	delay_us();
	I2C_SCL_H; // 时钟线拉高,等待发送器读取应答信号
	delay_us();
	delay_us();
	I2C_SCL_L; // 数据线处于占用状态,发送器发送下一次数据
}

// main.c
#include "si7006.h"

void delay(int ms)
{
  int i, j;
  for (i = 0; i < ms; i++)
  {
    for (j = 0; j < 2000; j++)
      ;
  }
}
int main()
{
  unsigned short hum;
  short tem;
  // 进行si7006的初始化
  si7006_init();
  while (1)
  {
    // 读取湿度
    hum = si7006_read_hum_data(0X40, 0XE5);
    // 读取温度
    tem = si7006_read_temp_data(0X40, 0XE3);
    // 将温度数据和湿度数据按照转换公式进行转换
    hum = 125 * hum / 65536 - 6;
    tem = 175.72 * tem / 65536 - 46.85;
    delay(1000); // 延时打印
    // 将获取到的数据打印到串口
    printf("hum:%d\n", hum);
    printf("tem:%d\n", tem);
  }

  return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1309033.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode--1004 最大连续1的个数 III[滑动窗口c++]

原题链接&#xff1a; 3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; 题目解析&#xff1a; 题目的翻转0&#xff0c;意思就是把0变成1&#xff1b; 将题的 最多可翻转k个0 操作看成 限定范围内最多可有k个0&#xff08;等价转换&#xff09; 因为实…

js 时间字符串截掉微秒后面的内容及加1秒

老规矩先上效果图: 一、js 时间字符串截掉微秒后面的内容&#xff0c;保留前面的 let str 2023-11-27 19:08:34.733; let index str.lastIndexOf(".") str str.substring(0, index); console.log(str) // 2023-11-27 19:08:34 二、转成时间戳&#xff0c;加1秒的…

人机融合与意图理解

人机融合本质上是人类智能与机器自动化之间的协同。 人机融合的目标是利用人类智能和机器自动化的优势&#xff0c;使二者相互补充、相互支持&#xff0c;共同实现更高效、更智能的工作和生活方式。 人类智能和机器自动化具有不同的特点和优势。人类智能具有创造性、灵活性、推…

VS Code串口监视插件Serial Monitor

文章目录 初步使用参数设置VS Code插件 初步使用 Serial Monitor&#xff0c;即串行监视器&#xff0c;提供串口和TCP协议的通信监控功能。在插件栏搜索安装之后&#xff0c;按下Ctrl打开终端&#xff0c;终端界面会多出一个串行监视器选项卡&#xff0c;进入之后&#xff0c;…

基于vue实现的疫情数据可视化分析及预测系统-计算机毕业设计推荐 django

本疫情数据可视化分析及预测系统 开发&#xff0c;用小巧灵活的MySQL数据库做完后台存储解释。本系统不仅主要实现了注册登录&#xff0c;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;全国实时数据管理&#xff0c;每日实时数据管理&#xff0c;国内实时动态…

KUKA机器人如何在程序中编辑等待时间?

KUKA机器人如何在程序中编辑等待时间&#xff1f; 如下图所示&#xff0c;如何实现在P1点和P2点之间等待设定的时间&#xff1f; 如下图所示&#xff0c;可以直接输入wait sec 2&#xff08;等待2秒&#xff09;&#xff0c; 如下图所示&#xff0c;再次选中该程序后&#…

ZLMediaKit中的线程

EventLoop的线程模型 服务器通用的IO模型event-loop 非阻塞IO。线程模型可以是单线程&#xff0c;可以是多线程。对于已经普及了的多核环境&#xff0c;通常都是采用多线程。 通常一个线程中有一个EventLoop&#xff0c;比如accept是一个专门线程&#xff0c;accept后的fd分…

I.MX RT1170双核学习(2):双核相互激活和启动流程

RT1170这个芯片带有双核&#xff1a;Cortex-M7和Corterx-M4&#xff0c;两个核都可以独立地运行&#xff0c;当然双核也可以同时运行。在上一篇文章中&#xff0c;介绍了一下在RT1170中消息模块MU的使用&#xff1a;双核通信之MU消息单元详解&#xff0c;因为这是双核之间用来通…

基于Dockerfile创建LNMP

实验组件 172.111.0.10&#xff1a;nginx docker-nginx 172.111.0.20&#xff1a;mysql docker-mysql 172.111.0.30&#xff1a;php docker-php 实验步骤 1.建立nginx-lnmp镜像及容器 cd /opt mkdir nginx cd nginx/ --上传nginx-1.22.0.tar.gz和wordpress-6.4.2-zh_C…

JOSEF 约瑟 时间继电器 DHC6A AC/DC100-240V 面板安装

特点 DIN&#xff08;4848mm&#xff09;标准面板尺寸 9 种工作模式可任意设定&#xff0c;简化外围线路,增强可靠性 带背光源 LCD 显示&#xff0c;在阳光或黑夜都能清晰显示 键保护可按要求锁定相关的按键,保护部分或全部的设定数据不受更 改&#xff0c;有效的防止误操…

02-详解请求路由的实现和常见的断言工厂

请求路由 路由转发 第一步: 新建一个SpringBoot工程如gateway模块, 引入网关依赖和nacos服务发现依赖 <!--网关依赖--> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId&…

【免费神器】一键转换PDF文件,轻松解决你的烦恼!

你是否曾经因为PDF文件的格式问题而感到困扰&#xff1f;是否曾经因为无法快速转换PDF文件而感到烦恼&#xff1f; 现在&#xff0c;这些问题都可以迎刃而解了&#xff01;下面这个在线PDF转换网站&#xff0c;就是你的解决方案。 目前5M以下文件免费转换&#xff0c;赶紧来看…

Python-自制简易音乐播放器

文章目录 前言一、代码二、代码实现1.库2.做ui窗口3爬虫 前言 原理简单&#xff1a;通过外链和歌曲Id拼接成下载链接来下载歌曲。 一、代码 做了个ui输入歌单链接&#xff1a; 注意这里歌单的url格式固定: https://music.163.com/playlist?id歌单id import sys from PyQt5.…

JavaWeb笔记之MySQL数据库

#Author 流云 #Version 1.0 一、引言 1.1 现有的数据存储方式有哪些&#xff1f; Java程序存储数据&#xff08;变量、对象、数组、集合&#xff09;&#xff0c;数据保存在内存中&#xff0c;属于瞬时状态存储。 文件&#xff08;File&#xff09;存储数据&#xff0c;保存…

飞致云与上海吉谛达成战略合作,获得Gitea企业版中国大陆地区独家代理权

2023年12月13日&#xff0c;中国领先的开源软件提供商FIT2CLOUD飞致云宣布与上海吉谛科技有限公司&#xff08;以下简称为上海吉谛&#xff09;正式达成战略合作&#xff0c;FIT2CLOUD飞致云获得上海吉谛旗下代码托管平台Gitea企业版中国大陆地区独家代理权。 Gitea项目&…

Github 2023-12-14开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2023-12-14统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量非开发语言项目5TypeScript项目2JavaScript项目1Jupyter Notebook项目1PHP项目1 基于项目的学习 创建周期&a…

[ 云计算 | Azure 实践 ] 在 Azure 门户中创建 VM 虚拟机并进行验证

文章目录 一、前言二、在 Azure Portal 中创建 VM三、验证已创建的虚拟机资源3.1 方法一&#xff1a;在虚拟机服务中查看验证3.1 方法二&#xff1a;在资源组服务中查看验证 四、文末总结 一、前言 本文会开始创建新系列的专栏&#xff0c;专门更新 Azure 云实践相关的文章。 …

CAN 三: STM32 CAN相关寄存器介绍

1、寄存器列表&#xff08;F1/F4/F7&#xff09; 寄存器名称作用CAN_MCRCAN主控制寄存器主要负责CAN工作模式的配置CAN_BTR位时序寄存器用来设置分频/TBS1/TBS2/TSWJ等参数&#xff0c;设置测试模式CAN_(T/R)IxR标识符寄存器存放(待发送/接收)的报文ID、扩展ID、IDE位及RTR位C…

MySQL 报错 You can‘t specify target table for update in FROM clause解决办法

You can’t specify target table for update in FROM clause 其含义是&#xff1a;不能在同一表中查询的数据作为同一表的更新数 单独执行复合查询是正常的&#xff0c;如下&#xff1a; 但是当执行子查询删除命令时&#xff0c;报如下错误 DELETE FROM abpusers WHERE Id I…

loki 如何格式化日志

部署 grafana-loki 首先介绍一下如何部署 官方文档&#xff1a;部署 grafana-loki 部署命令 设置集群的存储类&#xff0c;如果有默认可以不设置设置命名空间 helm install loki oci://registry-1.docker.io/bitnamicharts/grafana-loki --set global.storageClasslocal -n …