使用XC8接收有关plib I2Cfunction的“未定义符号”错误

嘿那里StackOverflow!

我的问题涉及在下面粘贴的程序中报告的错误。 目标器件是PIC12LF1552 ,它上面有一个串行外设,我假设它可以与Microchip的XC8编译器提供的库一起使用。 互联网上的一些消息人士表示,只有PIC18系列中的高端设备才支持库函数,其他消息来源表示库函数运行良好。 所以我决定不想从头开始重写I2C函数,也不想为这个项目编写任何数量的程序集。 因此,我决定使用随附的XC8外设库。 我阅读了编译器文档,了解如何获取它们(如下面的i2c.h所示)。 我知道根据文档和我见过的一些例子对这些命令进行了一些错误检查,但暂时我假设主人和奴隶都表现得很完美,所以我可以把这件事搞得一团糟地面。

我已经包含了所有相关路径,这就是为什么我认为它在编译过程中得到了这么多。 我在C语言和编译器的内部工作方面的知识水平非常有限,我只知道如何在基本级别使用这些工具,所以我可能会缺少一些基本的东西。

无论如何,当我在MPLABX v1.95中编译这段代码时,我得到了这个:

:0: error: undefined symbols: _AckI2C(dist/pickit3/production\strobe.X.production.obj) _ReadI2C(dist/pickit3/production\strobe.X.production.obj) _IdleI2C(dist/pickit3/production\strobe.X.production.obj) _OpenI2C(dist/pickit3/production\strobe.X.production.obj) _StopI2C(dist/pickit3/production\strobe.X.production.obj) _NotAckI2C(dist/pickit3/production\strobe.X.production.obj) _WriteI2C(dist/pickit3/production\strobe.X.production.obj) _StartI2C(dist/pickit3/production\strobe.X.production.obj)

我无法在Google,StackOverflow或其他方面找到与我的特定上下文有关的任何相关信息(另一个人在从Microchip的传统C18编译器移植时有一个非常类似的问题,但我已经做了所有人为解决他的问题所做的一切) 。

所以我想,问题是,为什么我得到这个编译器错误,它在C语言或Microchip的实现背后的机制是什么导致了这个?

 /* * File: i2c.h * Author: James * * Created on July 23, 2014, 9:02 PM */ #ifndef I2C_H #define I2C_H #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif #include  #include  #define SLAVE_ADDRESS 0b11110000 void Connect(); void Disconnect(); void Read(unsigned char address, unsigned char * data, unsigned char length); void Write(unsigned char address, unsigned char * data, unsigned char length); #endif /* I2C_H */ #include "i2c.h" void Connect() { OpenI2C(MASTER, SLEW_OFF); } void Disconnect() { CloseI2C(); } void Read(unsigned char address, unsigned char * data, unsigned char length) { IdleI2C(); // Wait until the bus is idle StartI2C(); // Send START condition IdleI2C(); // Wait for the end of the START condition if (WriteI2C(SLAVE_ADDRESS | 0x01)) return; // Send slave address with R/W cleared for write IdleI2C(); // Wait for ACK if (WriteI2C(address)) return; // Send register address IdleI2C(); // Wait for ACK for(int i = 0; i < length; i++) { data[i] = ReadI2C(); // Write nth byte of data AckI2C(); // Wait for ACK } NotAckI2C(); // Send NACK StopI2C(); // Hang up, send STOP condition } void Write(unsigned char address, unsigned char * data, unsigned char length) { IdleI2C(); // Wait until the bus is idle StartI2C(); // Send START condition IdleI2C(); // Wait for the end of the START condition if (WriteI2C(SLAVE_ADDRESS | 0x01)) return; // Send slave address with R/W cleared for write IdleI2C(); // Wait for ACK if (WriteI2C(address)) return; // Send register address IdleI2C(); // Wait for ACK for(int i = 0; i < length; i++) { WriteI2C(data[i]); // Write nth byte of data IdleI2C(); // Wait for ACK } StopI2C(); // Hang up, send STOP condition } /* * File: main.c * Author: James * * Created on July 14, 2014, 11:00 PM */ /******************************************************************************/ /* Files to Include */ /******************************************************************************/ #if defined(__XC) #include  /* XC8 General Include File */ #endif #include  /* For uint8_t definition */ #include  /* For true/false definition */ #include  #include  #include  #include "i2c.h" /******************************************************************************/ /* Defines */ /******************************************************************************/ //#define SYS_FREQ 16000000L //#define FCY SYS_FREQ/4 #define _XTAL_FREQ 500000 __CONFIG ( MCLRE_ON & CP_OFF & BOREN_OFF & WDTE_OFF & PWRTE_OFF & FOSC_INTOSC ); void main(void) { ANSELA = 0; TRISA = 0b101111; OPTION_REG = 0b01111111; APFCONbits.SDSEL = 1; unsigned char state = 0; unsigned char count = 0; unsigned char data[8] = { 0 }; Connect(); Read ( 0x01, // System register data, // Data buffer 0x01 // Read length ); LATAbits.LATA4 = data[0]; while(1) { switch (state) { case 0: // IDLE/OFF if (LATAbits.LATA4) LATAbits.LATA4 = 0; break; case 1: // ON if (!LATAbits.LATA4) LATAbits.LATA4 = 1; break; case 2: // BLINK (slow) LATAbits.LATA4 = !LATAbits.LATA4; __delay_ms(100); break; case 3: // BLINK (fast) LATAbits.LATA4 = !LATAbits.LATA4; __delay_ms(50); break; case 4: // BEAT DETECT LATAbits.LATA4 = PORTAbits.RA5; break; default: state = 0; break; } if (TMR0 > 0) { while (count < 20) { if (!PORTAbits.RA2) count = 0; __delay_ms(10); count++; } TMR0 = 0; state++; } } } 

问题定义

这里的核心问题是Microchip XC8外设库与其前身C18外设库一样,不支持PIC18系列之外的微控制器。 因此,有一大堆头文件可以正确配置外设,所有寄存器宏都是PIC18专用的,尽管有很多相似之处。

解决方案/解决方法

但是,当Microchip在此目录中提供其外设库的源时: /path/to/xc8/install/directory/version/sources/pic18/plib

特别是在我的情况下,在Windows x64机器上的i2c源: C:\Program Files (x86)\Microchip\xc8\v1.21\sources\pic18\plib\i2c

对于PIC12LF1552 ,芯片有一个MSSP,因此您需要复制i2c _ * .c源并连接它们,如果您的PC上有任何Linux / Unix实用程序,则可以执行此操作: cat i2c_* > i2c.c

现在,首先,要么删除文件中定义的所有I2C版本,要么更简单地,进入当前构建配置文件下的xc8编译器设置并设置以下定义宏: I2C_V1

之后,您需要从v1.21版本的源代码中进行一些修改,以便与设备兼容:

  • 在您的i2c.c版本的头文件中放置: #include 以便其余代码具有所有寄存器定义
  • i2c.h添加SDA和SCL引脚的定义,以便OpenI2C()工作或只是将OpenI2C()更改为特定于设备:
    • #define I2C_SCL TRISAbits.TRISA1
    • #define I2C_SDA TRISAbits.TRISA2TRISAbits.TRISA3具体取决于您的APFCONbits.SDSEL设置。 虽然在PIC12LF1552上,RA3始终设置为输入。
  • 需要更改以下寄存器字段:
    • SSPSTATbits.R_W -> SSPSTATbits.R_nW
    • PIR1bits.SSPIF -> PIR1bits.SSP1IF
    • PIR2bits.BCLIF -> PIR2bits.BCL1IF
    • 后两个我发现奇怪,因为数据表仍然在IF之前没有定义它们,但谁知道,也许Microchip有一个特殊的内部原因

在完成所有这些之后,您仍然需要编写自己的包装器来执行主/从模式的基本完全形成的function,就像我在我的问题中所做的那样。

小意见

可以这么说,整个过程比拔牙更糟糕。 Microchip的社区是傲慢或不屑一顾(“使用汇编”,“自己写”,等等)。 Microchip自己的支持也没有帮助。 除此之外,实际代码需要非常小的细节导向的变化几乎没有意义, IF -> 1IF严重吗? 在完成所有这些之后,您需要为这些函数编写自己的包装器以进行逻辑I2C事务,更不用说测试整个设备以确保它不会落在它的脸上。 难怪没有自定义布局和/或成本要求的人使用Arduinos。

  • 考虑采用重写i2c lib代码的方法,使用MC plib代码作为起点。 它可能比进行转换更快。 另外,考虑另一个编译器,例如来自CCS的编译器 – 他们可能有一个更健壮且记录良好的库(这里有人试过这个吗?)

    是的,MC的文档有时令人沮丧。 不知道为什么十亿美元合作。 不能做得更好。 并非所有函数和宏都有记录,有时示例不太现实,例如i2c eeprom代码的示例。

    我也遇到了这个lib的问题,因为代码使用了阻塞IO调用,并且没有从总线问题中恢复。 示例:我正在制作一个用i2c设备测试PCB的测试设备。 但是如果在plib操作期间连接中断,则plib代码将无法再访问PIC18 i2c端口。 这对于一件测试装置来说是不可接受的。