【问题标题】:Receiving "undefined symbols" error with XC8 concerning plib I2C functions使用 XC8 接收有关 plib I2C 功能的“未定义符号”错误
【发布时间】:2014-08-10 00:56:30
【问题描述】:

嘿,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 <plib\pconfig.h>
#include <plib\i2c.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++;
        }
    }
}

【问题讨论】:

  • 你是在#includ的标题中加上AckI2C之类的吗?
  • 库函数在#include &lt;plib/i2c.h&gt; 中定义,它包含在我的i2c.h 文件中(我知道命名约定不好,但在编译时并不违法,我不认为)。我的i2c.h 文件包含在我的main.c 文件中。我的i2c.c 文件是我的i2c.h 文件结束位置和main.c 开始之前之间的代码块...
  • #ifndef I2C_H #define I2C_H 行是为了强制不包含两次 i2c.h。问题可能是您的文件i2c.h 以`#ifndef I2C_H #define I2C_H` 开头,然后是#include &lt;plib\i2c.h&gt;。如果 plib\i2c.h 以 `#ifndef I2C_H #define I2C_H, nothing will be included ! You may try to rename your i2c.h` 开头为 myi2c.h 并将`#ifndef I2C_H #define I2C_H` 更改为`#ifndef MEI2C_H #define MYI2C_H`
  • 并且需要在i2c.h中选择I2C的版本。在#include &lt;plib\i2c.h&gt; 之前添加类似#define I2C_V6 的内容,这可能会解决您的问题。更有可能是之前的评论。
  • 好的!我们正在取得一些进展,我已经按照@francis 的建议完成了工作,并将我自己的 i2c 文件和#ifndef 块和#define I2C_V1 重命名为#include &lt;plib/i2c.h&gt;。所以我仍然收到未定义符号错误,但其中一些符号已从列表中删除。值得注意的是,不再抱怨仅定义包装寄存器设置的“功能”。然而,在 plib/i2c.h 中存在原型的实际函数是现在被抱怨的函数。我不知道从这里去哪里......

标签: c pic microchip mplab xc8


【解决方案1】:

问题定义

这里的核心问题是 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_* &gt; i2c.c

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

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

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

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

小意见片

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

【讨论】:

    【解决方案2】:
    • 考虑采用重写 i2c lib 代码的方法,使用 MC plib 代码作为起点。它可能比做一个更快 转换。另外,请考虑另一种编译器,例如 CCS 中的编译器 - 他们可能有一个更健壮和有据可查的图书馆(有没有人 这里试过这个?)

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-08
      • 1970-01-01
      • 2010-10-03
      相关资源
      最近更新 更多