转行做程序员来的第一个任务,就是用模拟iic给板子加上lmp91000,小公司嘛中间经历了好多事情再加上纯粹新手,调了两个多月才基本完成功能,回过头来总结,其实模拟iic很简单,多看数据手册多用示波器就行。下面总结下本次项目遇到的问题和解决方法。
第一个问题是io口操作问题,本次项目使用的是pic单片机,和stm32系列区别挺大的。最开始我只百度了pic的io口操作,以及pic数据手册的io章节,以为只需要操作TRISA和PORTA两个寄存器就可以完成输出高低电平的操作,后来有遇到连续两个问题,sda无法拉高,解决后sda拉高出现弧线。第一个问题分析应该是引脚复用功能没有完全关闭,每次需要使用sda引脚时我都把复用功能再关闭一次,好在基本上只需要开机初始化时通讯一次就够了,所以不太占用cpu资源。除了复用功能之外,看网上有的大佬分析是LATA寄存器需要设置,老实讲这个寄存器和PORTA寄存器的区别我还没有弄清楚,但是双管齐下之后sda拉高就再也没有问题了。第二个问题,拉高sda示波器里看这个过程是段弧线,查阅91000数据手册发现需要接上拉电阻,接上上拉电阻后问题解决。所以说,数据手册很重要!!!
第二个问题是iic时序问题,没啥好说的网上一大堆程序,copy下来根据数据手册和示波器调一调就好了,当然第一次接触的我也调了好久,现在再调一个全新的模拟iic估计一两天就行了吧哈哈。这里不得不再次强调,数据手册真的很重要!!调时序一定要根据数据手册来调,要不然就是无头苍蝇,像我们这种没师傅带的野生程序员,仔细读数据手册太重要了。
第三个问题是ad转换问题,属于历史遗留问题,和iic通讯关系不大其实。正常设置的ad转换参考电压是2.5和0,对应ad值为4095和0,但是一旦91000开始工作,零点电压时ad值为1365,并且特别稳定,给电测试时从0到0.6v都没有反应,大于0.6时ad值才开始上升,直到给电3.2v左右时上升至5460,再给电则跳到1开始继续走数。一开始从5460-1635=4095上入手,怀疑是ad转换设置问题,更改了参考电压,问题解决,但是同样带来另一个问题,就是电压为0时ad值为4.8k左右,给电到0.4v走到两百左右,按理来说其实已经可以使用了,但是我对于原本程序里的ad转换部分不够熟悉,直接更改参考电压怕有隐患,所以没有采用这种方法。后面逐项排查测试发现时sda引脚问题,原本板子sda这个引脚和另一个引脚并一起用于ad转换,经测试91000通讯后将sda拉低即可。
下面附上本次模拟iic代码,实测能用,不过由于没有调通读取从机寄存器数据,所以只上传驱动部分
void LMP_SDA_IN(void)
{
TRISAbits.TRISA1=1;
}
void LMP_SDA_OUT(void)
{
ANSELAbits.ANSA1 = 0;
TRISAbits.TRISA1=0;
}
void LMP_IIC_SCL (unsigned char num)//高电平用输入,低电平用输出
{
if(num)
{
TRISAbits.TRISA5=0;
PORTAbits.RA5=1;
}
else
{
TRISAbits.TRISA5=0;
PORTAbits.RA5=0;
}
}
void LMP_IIC_SDA (unsigned char num)
{
if(num)
{
ANSELAbits.ANSA1 = 0;
TRISAbits.TRISA1 = 0;
LATAbits.LATA1 = 1;
PORTAbits.RA1=1;
}
else
{
ANSELAbits.ANSA1 = 0;
TRISAbits.TRISA1 = 0;
LATAbits.LATA1 = 0;
PORTAbits.RA1=0;
}
}
unsigned char LMP_READ_SDA (void)
{
return PORTAbits.RA1;
}
void LMP_IIC_Start(void)
{
LMP_SDA_OUT(); //sda???
LMP_IIC_SDA(1);
LMP_IIC_SCL(1);
NOP();NOP();NOP();NOP();NOP();
LMP_IIC_SDA(0); //START:when CLK is high,DATA change form high to low
NOP();NOP();
LMP_IIC_SCL(0); //??I2C??,?????????
}
void LMP_IIC_Stop(void)
{
LMP_SDA_OUT();
LMP_IIC_SCL(0);
LMP_IIC_SDA(0); //STOP:when CLK is high DATA change form low to high
NOP();NOP();
LMP_IIC_SCL(1);
NOP();NOP();NOP();NOP();
LMP_IIC_SDA(1); //??I2C??????
NOP();NOP();NOP();NOP();
}
void dlay(unsigned char val)
{
while(val--)
{
NOP();NOP();NOP();NOP();
}
}
unsigned char LMP_IIC_Wait_Ack(void)
{
unsigned short errTime=0;
PORTAbits.RA1=1;
TRISAbits.TRISA1=1;
PORTAbits.RA5=1;
NOP();NOP();
while(LMP_READ_SDA())
{
errTime++;
if(errTime>2000)
{
LMP_IIC_Stop();
return 1;
}
}
LMP_IIC_SCL(0);//????0
// LMP_IIC_SDA(1);
return 0;
}
void LMP_IIC_Send_Byte(unsigned char byte)
{
unsigned char i;
unsigned char temp;
LMP_SDA_OUT();
LMP_IIC_SCL(0);
for(i=0;i<8;i++)
{
temp = (byte&0x80)>>7;
LMP_IIC_SDA(temp);
byte<<=1;
NOP();
LMP_IIC_SCL(1);
NOP();NOP();NOP();NOP();
LMP_IIC_SCL(0);
}
NOP();NOP();NOP();NOP();
LMP_IIC_SDA(0);
// LMP_IIC_SDA(1);
}
void I2C_WriteByte(unsigned char slaveAddr, unsigned char byte, unsigned char writeAddr)
{
LMP_IIC_Start();
LMP_IIC_Send_Byte(slaveAddr);
if(LMP_IIC_Wait_Ack())
{
return;
}
LMP_IIC_Send_Byte(writeAddr);
if(LMP_IIC_Wait_Ack())
{
return;
}
LMP_IIC_Send_Byte(byte);
if(LMP_IIC_Wait_Ack())
{
return;
}
LMP_IIC_Stop();
NOP();NOP();NOP();NOP();NOP();
}