SHT31测评+尝试读取传感器I2C总线数据
<div class='showpostmsg'><p> 原计划修改范例,增加串口输出温、湿度数据的功能,但我暂时没有J-LINK下载工具,同时又看到坛友尝试下载烧录未成功,转而一想还不如直接截取I2C总线上的数据,这样对原评测板不需要做任何变动,仅仅通过我的扩展板就行,于是我就用了一块现成的STM32L412板子进行实验,下图为实验装置:</p><p></p>
<p> 通过逻辑分析仪获取了评测板上的I2C数据,评测板是以每秒为周期对传感器进行一次读写操作:</p>
<p></p>
<p> 实际操作是先写入命令,然后等近1秒再读出,所以从逻辑分析仪截取的时序图是读在前、写在后:</p>
<p></p>
<p> 读操作是先发出地址码139,然后连续读出6个字节数据:</p>
<p></p>
<p> 从数据手册上得知前3个字节是温度数据,即16位的温度数据加1个字节的校验码,后3个字节是湿度数据,同样也是16的湿度数据加1个字节的校验码:</p>
<p> 写入的则是16位的命令,地址码是138:</p>
<p></p>
<p> 根据这些资料写了一段代码,这6个字节的数据读了出来,下面是读取I2C总线的代码:</p>
<p> 读一个字节:</p>
<p>/******************************************************<br />
*程 序 名:Read_I2C<br />
*作 用:读取I2C总线上的数据 <br />
*输入参数:无<br />
*返回参数:读到的数据(1字节)<br />
******************************************************/<br />
uint8_t Read_I2C(void)<br />
{<br />
uint8_t dat,i;<br />
//开始读一字节数据<br />
for(i=0;i<8;i++){<br />
while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 0);<br />
dat <<= 1;<br />
if(HAL_GPIO_ReadPin(SI2C_SDA_GPIO_Port,SI2C_SDA_Pin) == 1) dat |= 0x01;<br />
while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 1);<br />
}<br />
return dat;<br />
}</p>
<p> </p>
<p> 下面是读全部数据的代码:</p>
<p>/******************************************************<br />
*程 序 名:Intercept_I2C<br />
*作 用:截取I2C总线上的读数据 <br />
*输入参数:无<br />
*返回参数:<br />
******************************************************/<br />
void Intercept_I2C(void)<br />
{<br />
uint8_t i,d;<br />
uint32_t temp=0,hum=0;<br />
uint32_t d_t,d_h;<br />
//等待I2C开始信号<br />
while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin)==1){<br />
while(HAL_GPIO_ReadPin(SI2C_SDA_GPIO_Port,SI2C_SDA_Pin)==1);<br />
}</p>
<p>// while(Read_I2C() != 139) //I2C总线上出现读地址<br />
for(i = 0; i < 7; i++)<br />
d = Read_I2C();<br />
/*<br />
temp = Read_I2C(); //取高八位<br />
temp <<= 8;<br />
temp |= (uint16_t )Read_I2C();<br />
hum = Read_I2C(); //校验字节(弃用)<br />
hum = Read_I2C();<br />
hum <<= 8;<br />
hum |= (uint16_t )Read_I2C();<br />
*/<br />
d_t = d;<br />
d_t <<= 8;<br />
d_t |= d;<br />
// temp = (d_t * 175 / 65535) - 45;<br />
temp = d_t *1000 / 267 - 4500;<br />
d_h = d;<br />
d_h <<= 8;<br />
d_h |= d;<br />
hum = d_h * 10000 / 65535; <br />
LCD_write_value(12,3,5,0,1,temp);<br />
LCD_write_value(12,4,5,0,1,hum);<br />
LCD_write_value(50,3,5,0,1,d_t);<br />
LCD_write_value(50,4,5,0,1,d_h);<br />
<br />
LCD_write_value(0,0,3,0,1,d);<br />
// LCD_write_value(24,0,3,0,1,d);<br />
LCD_write_value(0,1,3,0,1,d);<br />
LCD_write_value(24,1,3,0,1,d);<br />
LCD_write_value(50,1,3,0,1,d);<br />
LCD_write_value(0,2,3,0,1,d);<br />
LCD_write_value(24,2,3,0,1,d);<br />
LCD_write_value(50,2,3,0,1,d);<br />
HAL_Delay(2);<br />
}</p>
<p> 经过反复测试,这些数据基本是读出了,但似乎不太正确,尤其是计算过程不正确,计算出的温、湿度数据与实际显示的大相径庭。下图中第1行是读取到的地址码,第2~3行分别是温、湿度数据及校验码,蓝色圈起的分别是读出的温、湿度数据,红色圈起的是计算出的温、湿度数值,与实际显示的不一致,尤其是湿度数据跳跃变动,不稳定。</p>
<p></p>
<p></p>
<p> 根据数据手册,温、湿度的计算公式如下:</p>
<p> 可是我按照这个公式写的代码计算出的数据却与手工计算的不同,也不知道代码那里错了,下面是代码:</p>
<p> d_t = d;<br />
d_t <<= 8;<br />
d_t |= d;<br />
// temp = (d_t * 175 / 65535) - 45;<br />
temp = d_t *1000 / 267 - 4500;<br />
d_h = d;<br />
d_h <<= 8;<br />
d_h |= d;<br />
hum = d_h * 10000 / 65535; //按保留两位小数</p>
<p> 我将变量的类型从16位无符号数改成32位无符号数,结果仍然不对。</p>
<p> 我用手工计算也无法对应到正确的温、湿度数值,不知道是那里还有问题。</p>
<p> 在读写I2C总线数据时,我忽略了每个字节后面的ACK,会不会是这个原因造成取数不正确?从时序图上分析,1 个ACK是3微秒,再到下个字节开始的时间是5.125微秒,总共是8.125微秒。不知道从读字节函数返回到下次再进入的时间需要多少,但这不应该会影响到同步,因为读下个字节是依据SCL时钟,除非返回到下次再进入的时间少于3微秒。</p>
<p> </p>
<p> </p>
<p> </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> <p>是数据溢出造成的吗?</p>
dcexpert 发表于 2020-1-20 16:42
是数据溢出造成的吗?
<p>好象不是,我用的数据类型是32位无符号,对于16位的数据进行乘175后再除65535,按理不会溢出啊。</p>
<p> </p>
<p>可能还是时序问题,捕捉的数据有错误</p>
dcexpert 发表于 2020-1-20 21:35
可能还是时序问题,捕捉的数据有错误
<p>可是读出的地址数据是正确的,并没有出错。</p>
<p> 经过仔细排查,没能获得正确数据的原因还是我忽略的ACK引起的,我在读I2C数据中加入了等待ACK的代码,获得的数据就正确了,代码如下:</p>
<p>/******************************************************<br />
*程 序 名:Read_I2C<br />
*作 用:读取I2C总线上的数据 <br />
*输入参数:无<br />
*返回参数:读到的数据(1字节)<br />
******************************************************/<br />
uint8_t Read_I2C(void)<br />
{<br />
uint8_t dat,i;<br />
//开始读一字节数据<br />
for(i=0;i<8;i++){<br />
while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 0);<br />
dat <<= 1;<br />
if(HAL_GPIO_ReadPin(SI2C_SDA_GPIO_Port,SI2C_SDA_Pin) == 1) dat |= 0x01;<br />
while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 1);<br />
}<br />
while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 0);<br />
while(HAL_GPIO_ReadPin(SI2C_SCL_GPIO_Port,SI2C_SCL_Pin) == 1); //等待ACK<br />
return dat;<br />
}</p>
<p> 下面是STM32L412开发板获得的温、湿度数据:</p>
<p></p>
<p> 有时会出现少许误差,这应该是两者刷新不完全同步所致:</p>
<p></p>
<p> 至此,这个测试是成功了。截取数据用的是STM32L412KB开发板,显示屏是LCD5110。</p>
<p></p>
<p> </p>
页:
[1]