这段时间公司里有个项目,用到了多块MCU,用I2C进行通讯。遇到了很多问题,在这里整理一下。
主机I2C用的HAL库,比较简单。
有一个要注意的点是,刚开始我用的Start-Write-Stop-Start-Read-Stop时序的方式读取指令,控制一个电源芯片,但是死活不成功,我是想这样的时序,我不需要用到HAL库中的中断方式,用轮询方式操作I2C就可以了,方便一点,并且大部分I2C从设备也都支持这样的操作。但是这个电源芯片不支持,后来改为了Start-Write-Restart-Read-Stop时序的方式,然后就没问题了,跟厂家沟通后,他们说明在Restart之前他们会主动拉低信号线,做一些数据处理,然后才会释放信号线,如果收到了Stop信号,他们就会直接结束操作,导致后面读出来的数据是个固定值(0xFF)。
从机因为协议是自定义的,我为了更灵活的操作一下,用的是LL库。
有几个注意点:
1、I2C作为从发送器时,在主机询问的最后一个字节时,从机发送后,主机并不会发送ACK信号,这时从机如果打开了ITERREN寄存器,是会进入该中断的,这个特殊中断请求需要特殊处理。不然如果你的特殊中断里是一些耗时操作,会影响到I2C的正常通讯。
2、我怕I2C挂死了我不知道,我做了个2分钟计时,以Stop信号为刷新信号,如果2分钟没有收到Stop信号,我认为I2C中断了,然后用LED灯表示出来。我的读取时序是Start-Write-Restart-Read-Stop。后来就发现程序运行过程中LED灯会一直表示为I2C挂了(运行过程中I2C一直是做读取操作),但看了下主机的数据,I2C通信是正常的。后来又在Stm32F1的参考手册中找到了原因。在I2C的SR1寄存器中的STOPF位的说明说中有详细解释。
原来是因为在Read操作时主机发送的Stop最后是不能被从机识别到的,从机只能识别到AF(即NACK信号),导致我的计时时间一直没有刷新,导致我的LED灯“谎报军情”。后来我将刷新信号放在了ADDR中断请求中,解决该问题。