c语言 - XC8 收到有关 plib I2C 函数"undefined symbols"错误



嘿,堆栈溢出!

我的问题涉及在下面粘贴(远(的程序内报告错误。目标设备是PIC12LF1552,它上面有一个串行外设,我认为它可以与Microchip的XC8编译器提供的库一起使用。互联网上的一些消息来源表示,只有PIC18系列中的高端设备才能支持库功能,其他消息来源表示库功能工作正常。所以我决定不想从头开始重写I2C函数,也不想为这个项目编写任何数量的汇编。因此,我决定使用 XC8 附带的外设库。我阅读了编译器文档,了解如何获取它们(如下i2c.h所示(。我知道根据文档和我看到的一些示例,对这些命令需要进行一些错误检查,但目前我假设主站和从站都会表现完美,这样我就可以把这件事放飞了。

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

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

:0: error: undefined symbols: _AckI2C(dist/pickit3/productionstrobe.X.production.obj) _ReadI2C(dist/pickit3/productionstrobe.X.production.obj) _IdleI2C(dist/pickit3/productionstrobe.X.production.obj) _OpenI2C(dist/pickit3/productionstrobe.X.production.obj) _StopI2C(dist/pickit3/productionstrobe.X.production.obj) _NotAckI2C(dist/pickit3/productionstrobe.X.production.obj) _WriteI2C(dist/pickit3/productionstrobe.X.production.obj) _StartI2C(dist/pickit3/productionstrobe.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 <plibpconfig.h>
#include <plibi2c.h>
#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 <xc.h>         /* XC8 General Include File */
#endif
#include <stdint.h>        /* For uint8_t definition */
#include <stdbool.h>       /* For true/false definition */
#include <stdio.h>
#include <stdlib.h>
#include <pic12lf1552.h>
#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)Microchipxc8v1.21sourcespic18plibi2c

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

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

之后,您需要对 v1.21 版本的源代码进行一些修改,使其与设备兼容:

  • 在您的版本的头文件中i2c.c放置: #include <pic12lf1552.h>以便代码的其余部分具有所有寄存器定义
  • 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之前没有1定义它们,但谁知道呢,也许Microchip有一个特殊的内部原因

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

小评论文章

可以

这么说,整个过程比拔牙还糟糕。Microchip的社区傲慢或不屑一顾("使用汇编","自己编写"等等(。Microchip自己的支持也无济于事。最重要的是,实际代码需要非常小的面向细节的更改,这些更改几乎没有意义,IF -> 1IF认真吗?在所有这些之后,您需要为这些功能编写自己的包装器来进行逻辑I2C事务,更不用说测试整个设备以确保它不会落在脸上。难怪没有自定义布局和/或成本要求的人使用Arduinos。

  • 考虑采用重写 i2c lib 代码的方法,使用MC plib代码作为起点。 它可能比做一个转换。 另外,考虑另一个编译器,例如来自CCS的编译器 -他们可能有一个更强大和有据可查的库(有任何人这里试过这个?

    是的,MC的文档有时令人沮丧。 不知道为什么一个十亿美元的公司不能做得更好。 并非所有函数和宏都是记录在案,有时示例不切实际,例如I2C EEPROM 代码示例。

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

最新更新