一次成功的sql优化,2个表joinI/O 极大,大约9million

本文通过实例分享了一次SQL优化经验,针对2个表JOIN导致的I/O过高问题进行了解决。优化策略包括更新策略调整、避免大表直接JOIN、使用临时表等,有效减少了数据库操作的资源消耗。


原始sql:

更新#result表,更新warr_bal.I/O大的原因是CIS..trans_acd_bal数据量太大,

并且#result表的数据量并不大,且如果有重复的数据,sum出来的结果呈倍数增长。

	CREATE TABLE #scm(
		scm_no int NULL
		,amt money NULL
		)

	INSERT INTO #scm
	SELECT scm_no
		,sum(isnull(gl_amt, 0))
	FROM #result t
	INNER JOIN CIS..trans_acd_bal ta
		ON ta.gl_project = t.scm_no
	WHERE ta.gl_year = @pre_year
		AND ta.gl_month = @pre_month
	GROUP BY scm_no

	UPDATE #result
	SET warr_bal = scm.amt
	FROM #result t
	INNER JOIN #scm scm
		ON t.scm_no = scm.scm_no


修改后的sql如下:I/O小了,并且时间也减少。

循环的时候最重要的是红色的sql语句


	-----------update trans_bal from CIS..trans_acd_bal begin
	DECLARE @max_idn int
		,@min_idn int
		,@bal_amt money
		,@scm_no int

	SELECT @min_idn = isnull(min(idn), 0)
	FROM #result

	SELECT @max_idn = isnull(max(idn), 0) + 1
	FROM #result

	WHILE (@min_idn < @max_idn)
	BEGIN
		SELECT @scm_no = isnull(scm_no, 0)
			,@bal_amt = warr_bal
		FROM #result t
		WHERE t.idn = @min_idn

		IF (@bal_amt IS NULL)
		BEGIN
			SELECT @bal_amt = sum(gl_amt)
			FROM CIS..trans_acd_bal ta
			WHERE ta.gl_year = @pre_year
				AND ta.gl_month = @pre_month
				AND ta.gl_project = @scm_no

			UPDATE #result
			SET warr_bal = @bal_amt
			FROM #result t
			WHERE t.idn = @min_idn
				OR t.scm_no = @scm_no
		END

		SELECT @min_idn = min(idn)
		FROM #result
		WHERE idn > @min_idn
	END

	-----------update trans_bal from CIS..trans_acd_bal end


第二个例子:

优化前sql,2个表直接inner join I/O 异常大。

journal_entry是个大表,且有索引。

 SELECT b.dept_no
            ,max(b.dept_name) AS dept_name
    FROM journal_entry a
    INNER JOIN department_info b
            ON a.gl_department = b.dept_no
    WHERE a.gl_acct_no > 700000
    GROUP BY b.dept_no

优化后i/O降低了大概只有原来1/10的I/O,

 SELECT a.gl_department as dept_no
            ,(select max(b.dept_name) from department_info b where a.gl_department = b.dept_no)  AS dept_name
    FROM journal_entry a
    WHERE a.gl_acct_no > 700000
    GROUP BY a.gl_department


第三个例子,普通取模循环

declare @iloop int,@ibatch int
SET @iloop = 0

	SELECT @ibatch = (count(*) / 200000) + 1
	FROM #pay_tmp

	WHILE @iloop < @ibatch
	BEGIN
		EXEC (
				"
		UPDATE #pay_tmp
		SET payment = payment + pay_amt
		FROM #pay_tmp a1701250030(index id_1)
		INNER JOIN #temp_pay b(index id_1)
			ON a1701250030.order_no = b.order_no
				AND a1701250030.order_type = b.order_type
		WHERE abs(isnull(a1701250030.order_no, 0)) % @ibatch = @iloop
		"
				)

		EXEC (
				"
		DELETE #temp_pay
		FROM #pay_tmp a1701250031(index id_1)
		INNER JOIN #temp_pay b(index id_1)
			ON a1701250031.order_no = b.order_no
				AND a1701250031.order_type = b.order_type
		WHERE abs(isnull(a1701250031.order_no, 0)) % @ibatch = @iloop
		"
				)

		INSERT #pay_tmp(
			order_type
			,order_no
			,payment
			)
		SELECT order_type
			,order_no
			,pay_amt
		FROM #temp_pay a
		WHERE abs(isnull(a.order_no, 0)) % @ibatch = @iloop

		SET @iloop = @iloop + 1
	END

第四个例子:old sql update 太大,为降低I/O,增加临时表。

old sql 每个都join 去sum,所以I/O 太大

UPDATE #result
	SET w0 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 0
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w1 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 1
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w2 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 2
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w3 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 3
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w4 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 4
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w5 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 5
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w6 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 6
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w7 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 7
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w8 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 8
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w9 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 9
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w10 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 10
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w11 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 11
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w12 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 12
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w13 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 13
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w14 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 14
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w15 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 15
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w16 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 16
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w17 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 17
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w18 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 18
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w19 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 19
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w20 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 20
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w21 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 21
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w22 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 22
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w23 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 23
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w24 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 24
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w25 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 25
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
		,w26 = (
			SELECT s.reqQty
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND s.idx = 26
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			)
	FROM #result r

	UPDATE #result
	SET forecast_total = (
			SELECT sum(isnull(s.reqQty, 0))
			FROM #res s
			WHERE r.cust_no = s.custNo
				AND r.group_id = s.group_id
				AND r.loc_no = s.loc_no
				AND r.sku_no = s.skuNo
				AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')
			GROUP BY s.custNo
				,s.group_id
				,s.loc_no
				,s.skuNo
				,isnull(ltrim(rtrim(s.refNo)), '')
			)
	FROM #result r

新的sql,放入临时表,把结果计算出来后,再去update

insert into  #temptable
SELECT s.custNo
	,s.group_id
	,s.loc_no
	,s.skuNo
	,isnull(ltrim(rtrim(s.refNo)), '') AS refNo
	,sum(CASE 
			WHEN s.idx = 0
				THEN s.reqQty
			END) AS w0
	,sum(CASE 
			WHEN s.idx = 1
				THEN s.reqQty
			END) AS w1
	,sum(CASE 
			WHEN s.idx = 2
				THEN s.reqQty
			END) AS w2
	,sum(CASE 
			WHEN s.idx = 3
				THEN s.reqQty
			END) AS w3
	,sum(CASE 
			WHEN s.idx = 4
				THEN s.reqQty
			END) AS w4
	,sum(CASE 
			WHEN s.idx = 5
				THEN s.reqQty
			END) AS w5
	,sum(CASE 
			WHEN s.idx = 6
				THEN s.reqQty
			END) AS w6
	,sum(CASE 
			WHEN s.idx = 7
				THEN s.reqQty
			END) AS w7
	,sum(CASE 
			WHEN s.idx = 8
				THEN s.reqQty
			END) AS w8
	,sum(CASE 
			WHEN s.idx = 9
				THEN s.reqQty
			END) AS w9
	,sum(CASE 
			WHEN s.idx = 10
				THEN s.reqQty
			END) AS w10
	,sum(CASE 
			WHEN s.idx = 11
				THEN s.reqQty
			END) AS w11
	,sum(CASE 
			WHEN s.idx = 12
				THEN s.reqQty
			END) AS w12
	,sum(CASE 
			WHEN s.idx = 13
				THEN s.reqQty
			END) AS w13
	,sum(CASE 
			WHEN s.idx = 14
				THEN s.reqQty
			END) AS w14
	,sum(CASE 
			WHEN s.idx = 15
				THEN s.reqQty
			END) AS w15
	,sum(CASE 
			WHEN s.idx = 16
				THEN s.reqQty
			END) AS w16
	,sum(CASE 
			WHEN s.idx = 17
				THEN s.reqQty
			END) AS w17
	,sum(CASE 
			WHEN s.idx = 18
				THEN s.reqQty
			END) AS w18
	,sum(CASE 
			WHEN s.idx = 19
				THEN s.reqQty
			END) AS w19
	,sum(CASE 
			WHEN s.idx = 20
				THEN s.reqQty
			END) AS w20
	,sum(CASE 
			WHEN s.idx = 21
				THEN s.reqQty
			END) AS w21
	,sum(CASE 
			WHEN s.idx = 22
				THEN s.reqQty
			END) AS w22
	,sum(CASE 
			WHEN s.idx = 23
				THEN s.reqQty
			END) AS w23
	,sum(CASE 
			WHEN s.idx = 24
				THEN s.reqQty
			END) AS w24
	,sum(CASE 
			WHEN s.idx = 25
				THEN s.reqQty
			END) AS w25
	,sum(CASE 
			WHEN s.idx = 26
				THEN s.reqQty
			END) AS w26
	,sum(s.reqQty) as wall
FROM #res s
GROUP BY s.custNo
	,s.group_id
	,s.loc_no
	,s.skuNo
	,isnull(ltrim(rtrim(s.refNo)), '')

UPDATE #result
SET w0 = s.w0
	,w1 = s.w1
	,w2 = s.w2
	,w3 = s.w3
	,w4 = s.w4
	,w5 = s.w5
	,w6 = s.w6
	,w7 = s.w7
	,w8 = s.w8
	,w9 = s.w9
	,w10 = s.w10
	,w11 = s.w11
	,w12 = s.w12
	,w13 = s.w13
	,w14 = s.w14
	,w15 = s.w15
	,w16 = s.w16
	,w17 = s.w17
	,w18 = s.w18
	,w19 = s.w19
	,w20 = s.w20
	,w21 = s.w21
	,w22 = s.w22
	,w23 = s.w23
	,w24 = s.w24
	,w25 = s.w25
	,w26 = s.w26
	,forecast_total = s.wall
FROM #result r
INNER JOIN #temptable s
	ON r.cust_no = s.custNo
		AND r.group_id = s.group_id
		AND r.loc_no = s.loc_no
		AND r.sku_no = s.skuNo
		AND isnull(ltrim(rtrim(r.ref_no)), '') = isnull(ltrim(rtrim(s.refNo)), '')



<think>首先,用户的问题是:“12C协议中,数据有效性要求是什么?”选项是:A.SCL低电平时SDA可变化B.SCL高电平时SDA必须保持稳定C.SDA电平变化需与SCL下降沿同步D.SDA仅在应答位允许跳变用户提到了“12C协议”,但根据上下文,应该是“I2C协议”,因为I2C是一种常见的串行通信协议,而12C可能是个笔误。I2C协议中,数据有效性是关键部分。回忆I2C协议的基本原理:-I2C(Inter-IntegratedCircuit)是一种串行通信协议,使用两根线:SCL(SerialClock)和SDA(SerialData)。-数据有效性规则:在SCL为高电平时,SDA的数据必须保持稳定(不变),以避免错误。只有在SCL为低电平时,SDA的数据才能改变。-这是因为在SCL上升沿时,接收器采样SDA的数据。所以,在SCL高电平时,SDA必须稳定。现在,分析每个选项:A.SCL低电平时SDA可变化-正确,因为数据变化应在SCL低时发生。B.SCL高电平时SDA必须保持稳定-正确,这是数据有效性的核心。C.SDA电平变化需与SCL下降沿同步-实际上,变化通常在SCL下降沿附近,但规则是变化在SCL低时,而采样在上升沿。D.SDA仅在应答位允许跳变-不完全正确;SDA可以在任何时候变化,但只在SCL低时允许变化,应答位也有特定规则。在I2C中,数据位传输时:-当SCL为低时,发送器可以改变SDA(准备数据)。-当SCL为高时,SDA必须稳定,以便接收器在上升沿采样。-应答位(ACK/NACK)是第九位,传输规则相同:SCL高时SDA稳定。所以,最准确的选项是B。但选项A也正确。问题问“数据有效性要求”,核心是SCL高时SDA稳定。参考标准I2C规范:数据有效性定义为SDA仅在SCL低时改变,在SCL高时必须稳定。因此,正确选项应该是A和B,但问题可能只期望一个主要答案。选项C和D有误:-C:变化不需要与下降沿同步;它可以在SCL低时的任何时候变化。-D:SDA变化不只在应答位;在数据传输位也允许变化。在I2C中,SDA变化在SCL低时允许,不仅限于应答位。所以,主要要求是B:SCL高电平时SDA必须保持稳定。现在,用户要求我作为编程专家回答,并提供代码解释等。但这个问题是关于I2C协议的硬件/协议层面,而不是编程代码。不过,用户说“我会向你提问各个编程领域的相关问题”,而I2C在嵌入式编程中常见,所以我可以涉及。用户指定回答流程:回答问题-给出解释。然后提供代码(如果相关),最后提出相关问题。但这个问题不直接涉及写代码,而是协议规则。所以,我可以解释协议,然后如果可能,给出一个示例代码,比如使用Arduino或类似实现I2C通信。最后,提出相关问题。结构化回答:1.**回答问题**:直接给出答案。选项B是正确的。2.**给出解释**:详细解释I2C数据有效性规则。3.**提供代码**:虽然问题不是关于代码的,但为了符合用户要求,我可以给出一个简单的I2C通信代码示例,比如在Arduino上使用Wire库。4.**提出相关问题**:按照指定格式,列出4个相关问题。用户提供的例子是关于快排的代码和解释,所以我应该类似地处理。最终答案:数据有效性要求是SCL高电平时SDA必须保持稳定,所以选项B。但选项A也正确,但问题可能单选。看选项,似乎是多项选择,但用户没有指定。我会解释所有。在回答中,澄清。现在,为代码部分:提供一个简单的I2C主设备发送数据的代码示例。例如,使用Arduino:```cpp#include<Wire.h>voidsetup(){Wire.begin();//joinI2Cbusasmaster}voidloop(){Wire.beginTransmission(0x50);//transmittodeviceaddress0x50Wire.write(&#39;x&#39;);//sendsonebyteWire.endTransmission();//stoptransmittingdelay(100);}```然后解释代码中如何遵循数据有效性规则。最后,提出相关问题。相关问题格式:§§相关问题§§:1.···2.···3.···4.···相关问题应与I2C协议相关。</think>### 回答:I2C协议中数据有效性要求 在I2C(Inter-Integrated Circuit)协议中,数据有效性要求的核心是:**SCL(Serial Clock)高电平时,SDA(Serial Data)必须保持稳定**。这对应于选项B。 同时,选项A(SCL低电平时SDA可变化)也是正确的,因为数据变化只允许在SCL低电平时发生。但主要的数据有效性规则是SCL高电平时的稳定性,以确保在SCL上升沿时数据被可靠采样。 #### 详细解释 I2C协议是一种同步串行通信协议,使用SCL和SDA两根线进行数据传输。数据有效性规则如下: - **SCL高电平时SDA必须稳定(不变)**:这是因为接收设备在SCL的上升沿采样SDA的数据。如果SDA在SCL高电平时变化,可能会导致采样错误(如亚稳态或误读)。 - **SCL低电平时SDA可以变化**:发送设备在SCL低电平时可以改变SDA的状态,以准备下一个数据位。这包括数据位的起始、停止、数据字节传输和应答位(ACK/NACK)。 - **其他选项分析**: - 选项C(SDA电平变化需与SCL下降沿同步):错误。SDA变化不需要严格同步于SCL下降沿;它可以在SCL低电平期间的任何时候变化,只要在SCL高电平前稳定。 - 选项D(SDA仅在应答位允许跳变):错误。SDA变化允许在SCL低电平的任何数据位期间(包括起始位、数据字节位和应答位),而不仅限于应答位。 这些规则确保I2C通信的可靠性。例如: - 起始条件:SCL高时,SDA从高到低跳变。 - 停止条件:SCL高时,SDA从低到高跳变。 - 数据传输:每个数据位在SCL低时变化,在SCL高时稳定。 #### 代码示例 虽然I2C协议是硬件层面的规范,但在嵌入式编程中(如Arduino),我们可以使用库函数实现I2C通信。以下代码使用Arduino的Wire库演示I2C主设备发送数据,并遵守数据有效性规则。代码中,Wire库内部处理了SCL和SDA的时序,确保在SCL高时SDA稳定。 ```cpp // Arduino代码:I2C主设备向从设备发送数据 #include <Wire.h> // 引入I2C库 void setup() { Wire.begin(); // 初始化I2C总线,作为主设备 } void loop() { // 开始传输到地址为0x50的从设备 Wire.beginTransmission(0x50); // 发送一个字节的数据(例如字符 &#39;A&#39;) Wire.write(&#39;A&#39;); // 库函数在SCL低时改变SDA,在SCL高时保持稳定 // 结束传输(发送停止条件) Wire.endTransmission(); // 内部处理SCL和SDA时序 delay(100); // 延迟100ms } ``` **代码解释**: - `Wire.begin()`:初始化I2C总线。 - `Wire.beginTransmission(0x50)`:开始向地址0x50的从设备传输数据。库函数自动生成起始条件(SCL高时SDA从高到低)。 - `Wire.write(&#39;A&#39;)`:发送一个字节(ASCII &#39;A&#39;)。在底层,Wire库控制SCL和SDA: - SCL拉低时,SDA变化以设置数据位。 - SCL拉高时,SDA保持稳定,允许从设备采样。 - `Wire.endTransmission()`:结束传输并发送停止条件(SCL高时SDA从低到高)。 - 整个过程中,Wire库抽象了硬件细节,但严格遵循I2C协议的数据有效性规则。 在嵌入式系统中,开发者通常依赖库函数(如Arduino的Wire或Linux的i2c-dev)处理时序,避免手动操作GPIO。但在底层驱动开发时,需直接控制SCL和SDA引脚,确保遵守规则。 #### 注意事项 - I2C协议支持多主设备,数据有效性规则在仲裁期间尤为重要。 - 实际应用中,需考虑上拉电阻、时钟拉伸(clock stretching)和总线速度(标准模式100kHz,快速模式400kHz)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值