hujj 发表于 2020-1-20 15:50

SHT31测评+尝试读取传感器I2C总线数据

<div class='showpostmsg'><p>&nbsp; &nbsp; 原计划修改范例,增加串口输出温、湿度数据的功能,但我暂时没有J-LINK下载工具,同时又看到坛友尝试下载烧录未成功,转而一想还不如直接截取I2C总线上的数据,这样对原评测板不需要做任何变动,仅仅通过我的扩展板就行,于是我就用了一块现成的STM32L412板子进行实验,下图为实验装置:</p>

<p></p>

<p>&nbsp; &nbsp; 通过逻辑分析仪获取了评测板上的I2C数据,评测板是以每秒为周期对传感器进行一次读写操作:</p>

<p></p>

<p>&nbsp; &nbsp; 实际操作是先写入命令,然后等近1秒再读出,所以从逻辑分析仪截取的时序图是读在前、写在后:</p>

<p></p>

<p>&nbsp; &nbsp; 读操作是先发出地址码139,然后连续读出6个字节数据:</p>

<p></p>

<p>&nbsp; &nbsp; 从数据手册上得知前3个字节是温度数据,即16位的温度数据加1个字节的校验码,后3个字节是湿度数据,同样也是16的湿度数据加1个字节的校验码:</p>

<p> &nbsp;&nbsp; 写入的则是16位的命令,地址码是138:</p>

<p></p>

<p>&nbsp; &nbsp; 根据这些资料写了一段代码,这6个字节的数据读了出来,下面是读取I2C总线的代码:</p>

<p>&nbsp; &nbsp; 读一个字节:</p>

<p>/******************************************************<br />
*程 序 名:Read_I2C<br />
*作 &nbsp; &nbsp;用:读取I2C总线上的数据&nbsp;<br />
*输入参数:无<br />
*返回参数:读到的数据(1字节)<br />
******************************************************/<br />
uint8_t Read_I2C(void)<br />
{<br />
&nbsp; &nbsp; uint8_t dat,i;<br />
&nbsp;&nbsp; &nbsp;//开始读一字节数据<br />
&nbsp;&nbsp; &nbsp;for(i=0;i&lt;8;i++){<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 0);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;dat &lt;&lt;= 1;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if(HAL_GPIO_ReadPin(SI2C_SDA_GPIO_Port,SI2C_SDA_Pin) == 1) dat |= 0x01;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 1);<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;return dat;<br />
}</p>

<p>&nbsp;</p>

<p>&nbsp; &nbsp; 下面是读全部数据的代码:</p>

<p>/******************************************************<br />
*程 序 名:Intercept_I2C<br />
*作 &nbsp; &nbsp;用:截取I2C总线上的读数据&nbsp;<br />
*输入参数:无<br />
*返回参数:<br />
******************************************************/<br />
void Intercept_I2C(void)<br />
{<br />
&nbsp; &nbsp; uint8_t i,d;<br />
&nbsp;&nbsp; &nbsp;uint32_t temp=0,hum=0;<br />
&nbsp;&nbsp; &nbsp;uint32_t d_t,d_h;<br />
&nbsp;&nbsp; &nbsp;//等待I2C开始信号<br />
&nbsp;&nbsp; &nbsp;while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin)==1){<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;while(HAL_GPIO_ReadPin(SI2C_SDA_GPIO_Port,SI2C_SDA_Pin)==1);<br />
&nbsp;&nbsp; &nbsp;}</p>

<p>// &nbsp; &nbsp;while(Read_I2C() != 139) &nbsp; &nbsp;//I2C总线上出现读地址<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;for(i = 0; i &lt; 7; i++)<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;d = Read_I2C();<br />
/*<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;temp = Read_I2C(); &nbsp;//取高八位<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;temp &lt;&lt;= 8;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;temp |= (uint16_t )Read_I2C();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;hum = Read_I2C(); &nbsp; //校验字节(弃用)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;hum = Read_I2C();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;hum &lt;&lt;= 8;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;hum |= (uint16_t )Read_I2C();<br />
*/<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_t = d;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_t &lt;&lt;= 8;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_t |= d;<br />
//&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;temp = (d_t * 175 / 65535) - 45;<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;temp = d_t *1000 / 267 - 4500;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_h = d;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_h &lt;&lt;= 8;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_h |= d;<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;hum = d_h * 10000 / 65535;&nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;LCD_write_value(12,3,5,0,1,temp);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;LCD_write_value(12,4,5,0,1,hum);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;LCD_write_value(50,3,5,0,1,d_t);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;LCD_write_value(50,4,5,0,1,d_h);<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;LCD_write_value(0,0,3,0,1,d);<br />
//&nbsp;&nbsp; &nbsp;LCD_write_value(24,0,3,0,1,d);<br />
&nbsp;&nbsp; &nbsp;LCD_write_value(0,1,3,0,1,d);<br />
&nbsp;&nbsp; &nbsp;LCD_write_value(24,1,3,0,1,d);<br />
&nbsp;&nbsp; &nbsp;LCD_write_value(50,1,3,0,1,d);<br />
&nbsp;&nbsp; &nbsp;LCD_write_value(0,2,3,0,1,d);<br />
&nbsp;&nbsp; &nbsp;LCD_write_value(24,2,3,0,1,d);<br />
&nbsp;&nbsp; &nbsp;LCD_write_value(50,2,3,0,1,d);<br />
&nbsp;&nbsp; &nbsp;HAL_Delay(2);<br />
}</p>

<p>&nbsp; &nbsp; 经过反复测试,这些数据基本是读出了,但似乎不太正确,尤其是计算过程不正确,计算出的温、湿度数据与实际显示的大相径庭。下图中第1行是读取到的地址码,第2~3行分别是温、湿度数据及校验码,蓝色圈起的分别是读出的温、湿度数据,红色圈起的是计算出的温、湿度数值,与实际显示的不一致,尤其是湿度数据跳跃变动,不稳定。</p>

<p></p>

<p></p>

<p>&nbsp; &nbsp; 根据数据手册,温、湿度的计算公式如下:</p>

<p> &nbsp;&nbsp; 可是我按照这个公式写的代码计算出的数据却与手工计算的不同,也不知道代码那里错了,下面是代码:</p>

<p>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_t = d;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_t &lt;&lt;= 8;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_t |= d;<br />
//&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;temp = (d_t * 175 / 65535) - 45;<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;temp = d_t *1000 / 267 - 4500;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_h = d;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_h &lt;&lt;= 8;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;d_h |= d;<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;hum = d_h * 10000 / 65535; //按保留两位小数</p>

<p>&nbsp; &nbsp; 我将变量的类型从16位无符号数改成32位无符号数,结果仍然不对。</p>

<p>&nbsp; &nbsp; 我用手工计算也无法对应到正确的温、湿度数值,不知道是那里还有问题。</p>

<p>&nbsp; &nbsp; 在读写I2C总线数据时,我忽略了每个字节后面的ACK,会不会是这个原因造成取数不正确?从时序图上分析,1 个ACK是3微秒,再到下个字节开始的时间是5.125微秒,总共是8.125微秒。不知道从读字节函数返回到下次再进入的时间需要多少,但这不应该会影响到同步,因为读下个字节是依据SCL时钟,除非返回到下次再进入的时间少于3微秒。</p>

<p>&nbsp; &nbsp;&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p><br />
<b><font color="#5e7384">此内容由EEWORLD论坛网友<font size="3">hujj</font>原创,如需转载或用于商业用途需征得作者同意并注明出处</font></b></p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

dcexpert 发表于 2020-1-20 16:42

<p>是数据溢出造成的吗?</p>

hujj 发表于 2020-1-20 16:59

dcexpert 发表于 2020-1-20 16:42
是数据溢出造成的吗?

<p>好象不是,我用的数据类型是32位无符号,对于16位的数据进行乘175后再除65535,按理不会溢出啊。</p>

<p>&nbsp;</p>

dcexpert 发表于 2020-1-20 21:35

<p>可能还是时序问题,捕捉的数据有错误</p>

hujj 发表于 2020-1-20 22:30

dcexpert 发表于 2020-1-20 21:35
可能还是时序问题,捕捉的数据有错误

<p>可是读出的地址数据是正确的,并没有出错。</p>

hujj 发表于 2020-1-21 11:20

<p>&nbsp; &nbsp; 经过仔细排查,没能获得正确数据的原因还是我忽略的ACK引起的,我在读I2C数据中加入了等待ACK的代码,获得的数据就正确了,代码如下:</p>

<p>/******************************************************<br />
*程 序 名:Read_I2C<br />
*作 &nbsp; &nbsp;用:读取I2C总线上的数据&nbsp;<br />
*输入参数:无<br />
*返回参数:读到的数据(1字节)<br />
******************************************************/<br />
uint8_t Read_I2C(void)<br />
{<br />
&nbsp; &nbsp; uint8_t dat,i;<br />
&nbsp;&nbsp; &nbsp;//开始读一字节数据<br />
&nbsp;&nbsp; &nbsp;for(i=0;i&lt;8;i++){<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 0);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;dat &lt;&lt;= 1;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if(HAL_GPIO_ReadPin(SI2C_SDA_GPIO_Port,SI2C_SDA_Pin) == 1) dat |= 0x01;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 1);<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 0);<br />
&nbsp;&nbsp; &nbsp;while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 1); &nbsp;//等待ACK<br />
&nbsp;&nbsp; &nbsp;return dat;<br />
}</p>

<p>&nbsp; &nbsp; 下面是STM32L412开发板获得的温、湿度数据:</p>

<p></p>

<p>&nbsp; &nbsp; 有时会出现少许误差,这应该是两者刷新不完全同步所致:</p>

<p></p>

<p>&nbsp; &nbsp; 至此,这个测试是成功了。截取数据用的是STM32L412KB开发板,显示屏是LCD5110。</p>

<p></p>

<p>&nbsp;</p>
页: [1]
查看完整版本: SHT31测评+尝试读取传感器I2C总线数据