1.前言
前面不是开发了F407的i2c嘛,最近做项目有三四个i2c器件,项目要求用f103,于是看了一下f103,发现并没有多大区别,下面我会说一下异同。还有关于接收的过程也有要补充的。
2.F103 VS F407
两者之间几乎没有区别,唯一不同的是GPIO的区别,F103与F407在GPIO寄存器会有点区别,这里稍微改一下就能用了。其他部分基本不用动。
上次关于STM32作为主机接收过程讲的有点不足,这次我在发送的时候没发现什么问题,但是接收的时候出现了问题,下面我来展开讲讲,尤其是长数据接收。
3.数据错误
首先是接收错误的问题,上一篇文章里我说到接收数据的过程
程序没有问题,但是顺序错了,正确的顺序是先等待SR1的第六位到位,再读取数据,否则,读到的数据就会是地址,而非接收的数据。
4.多数据接收
上次因为只做了一个数据的接收,所以问题没有暴露出来,这次我在用传感器的时候要一次性接收6个数据,发现数据接收有问题。最后还是通过读手册来排故的。
首先我们再明确一下读数据的过程。
可以看到在读数据的时候,每读一位都需要一个ACK,直到最后一位数据,而这个ACK是由主机向从机发送的应答。
而在STM32中,控制这个应答的在CR1寄存器中,是第十位
通过给第十位置1来给从机应答
我们来测试一下,这里我通过i2c读取3个数据
i2c_ReadRegist(ADXL345ADDRESS,0x32,data,3);
我们看一下波形
不对啊,而且停止位没了,传输停止后总线的电平也不对。
这里我们要再看一下手册
注意看一下最后一位的数据传输,比较特殊。手册上说的比较模糊,我个人的建议是在完成上一个数据接收后就立刻给停止位置1,给应答位置0。程序可以这样写
while(wei<length)
{
if(length!=1){I2C1->CR1|=1<<10;} //多重数据接收时由主机应答
if(wei==length-1)
{
I2C1->CR1&=~(1<<10); //最后一位无需应答
I2C1->CR1|=1<<9; //提前写入停止位
}
//等待数据接收完毕
for(wait=0;(I2C1->SR1&(1<<6))==0;wait++){if(wait>WAITTIME){I2C1->CR1|=1<<9;return;}}
i2cdata[wei]=I2C1->DR; //EV8
wei++;
}
经过测试,只有这样数据才不会溢出
我们再来测试一下
可以看到,波形非常标准。
5.代码
void i2c_ReadRegist(unsigned char i2c_address,unsigned char regaddress,unsigned char* i2cdata,unsigned char length)
{
unsigned int wait;
unsigned int wei=0;
//发送
I2C1->CR1|=1<<8; //发出起始信号
//等待起始信号发送完毕
for(wait=0;(I2C1->SR1&(1<<0))==0;wait++){if(wait>WAITTIME){I2C1->CR1|=1<<9;return;}}
I2CEV(5); //EV5
#if ADDRESSBIT==7
I2C1->DR=i2c_address<<1; //写入地址
#else
I2C1->DR=i2c_address; //写入地址
#endif
//等待地址发送完毕
for(wait=0;(I2C1->SR1&(1<<1))==0;wait++){if(wait>WAITTIME){I2C1->CR1|=1<<9;return;}}
I2CEV(6);
I2C1->DR=regaddress; //EV8
//等待数据发送完毕
for(wait=0;(I2C1->SR1&(1<<7))==0;wait++){if(wait>WAITTIME){I2C1->CR1|=1<<9;return;}}
//接收
I2C1->CR1|=1<<8; //发出起始信号
//等待起始信号发送完毕
for(wait=0;(I2C1->SR1&(1<<0))==0;wait++){if(wait>WAITTIME){I2C1->CR1|=1<<9;return;}}
I2CEV(5);
#if ADDRESSBIT==7
I2C1->DR=(i2c_address<<1)+1; //写入地址
#else
I2C1->DR=i2c_address; //写入地址
#endif
//等待地址发送完毕
for(wait=0;(I2C1->SR1&(1<<1))==0;wait++){if(wait>WAITTIME){I2C1->CR1|=1<<9;return;}}
I2CEV(6);
while(wei<length)
{
if(length!=1){I2C1->CR1|=1<<10;} //多重数据接收时由主机应答
if(wei==length-1)
{
I2C1->CR1&=~(1<<10); //最后一位无需应答
I2C1->CR1|=1<<9; //提前写入停止位
}
//等待数据接收完毕
for(wait=0;(I2C1->SR1&(1<<6))==0;wait++){if(wait>WAITTIME){I2C1->CR1|=1<<9;return;}}
i2cdata[wei]=I2C1->DR; //EV8
wei++;
}
}
6.总结
前面写i2c程序还是大意了,这两天测试的时候才发现问题,不过写好的i2c用起来还是很舒服的,几乎没有卡顿(中间的空白是在跑FFT算法,不是i2c的问题)
我这接收大量数据并且直接裸机跑在死循环里也没有出错,真不错。