Receiving “undefined symbols” error with XC8 concerning plib I2C functions

六眼飞鱼酱① 提交于 2019-12-10 18:26:06

问题


Hey there StackOverflow!

My question concerns errors being reporting within the program pasted (far) below. The target device is the PIC12LF1552, it has one serial peripheral on it that I assumed could be used in conjunction with the library supplied with Microchip's XC8 compiler. Some sources on the internet have said that only higher end devices in the PIC18 line would support the library functions, other sources have said the library functions work just fine. So I decided that I didn't want to rewrite the I2C functions from scratch, nor did I want to write any amount of assembly for this project. Thus I settled on using the provided peripheral library that comes with XC8. I read up on the compiler documentation for how to source them (as seen in i2c.h below). I know theres some error checking to do on those commands as per the documentation and some examples I've seen, but for the time being I'm assuming that both the master and the slave will behave perfectly just so I can get this thing off the ground.

I have included all relevant paths, which is why I assume it gets this far in the compilation process. My level of knowledge when it comes to the inner workings of a C language and compiler is very limited, I only know how to use these tools at a basic level, so there might be something fundamental I'm missing here.

Anyways, when I compile this code in MPLABX v1.95, I get this:

: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)

I couldn't find anything relevant on Google, StackOverflow, or otherwise concerning this problem from my specific context (another guy had a very similar issue when porting from Microchip's legacy C18 compiler, but I already did everything that guy did to solve his problem).

So I guess, the question is, why am I getting this compiler error, and what is the mechanism behind it in the C language or Microchip's implementation of it that is causing this?

/* 
 * 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++;
        }
    }
}

回答1:


Problem Definition

The core problem here is that the Microchip XC8 peripheral library, like its predecessor the C18 peripheral library does not support microcontrollers outside of the PIC18 series. So theres a whole bunch of header files that configure the peripherals correctly and all the register macros are specific to the PIC18 line, although there are vast similarities.

Solution/Workaround

However, as Microchip provides the sources to their peripheral library inside this directory: /path/to/xc8/install/directory/version/sources/pic18/plib

Specifically in my case for the i2c sources on a Windows x64 machine: C:\Program Files (x86)\Microchip\xc8\v1.21\sources\pic18\plib\i2c

For the PIC12LF1552, the chip has one MSSP so you'll want to copy the i2c_*.c sources and concatenate them, if you have any Linux/Unix utilities on your PC, you can do this: cat i2c_* > i2c.c

Now, first things first, either rip out all the I2C version defines in the files, or, more simply, go into your xc8 compiler settings under your current build profile and set the following define macro: I2C_V1

Afterwards, there are a few modifications you'll need to make from the v1.21 version of the sources for it to be compatible with the device:

  • In the header file for your version of i2c.c put: #include <pic12lf1552.h> so that the rest of the code has all the register defines
  • Add the defines for your SDA and SCL pins in i2c.h so that OpenI2C() works or simply change OpenI2C() to be specific to the device:
    • #define I2C_SCL TRISAbits.TRISA1
    • #define I2C_SDA TRISAbits.TRISA2 OR TRISAbits.TRISA3 depending on your APFCONbits.SDSEL setting. Although on the PIC12LF1552, RA3 is always set to input.
  • The following register fields need to be changed:
    • SSPSTATbits.R_W -> SSPSTATbits.R_nW
    • PIR1bits.SSPIF -> PIR1bits.SSP1IF
    • PIR2bits.BCLIF -> PIR2bits.BCL1IF
    • The latter two I found odd as the datasheet still defines them without the 1 before the IF, but who knows, maybe Microchip has a special internal reason for that

After all of this, you'll still need to write your own wrappers to perform basic fully formed functions for Master/Slave modes as I have in my question.

Small Opinion Piece

This entire process was worse than pulling teeth, so to speak. Microchip's community was arrogant or dismissive ("use assembly", "write it yourself", and so forth). Microchip's own support was also unhelpful. And on top of this, the actual code needed very small detail oriented changes that make almost no sense, IF -> 1IF seriously? And after all of that you need to write your own wrappers for those functions to make a logical I2C transaction and not to mention test the entire apparatus to make sure it doesn't fall on its face. No wonder people who don't have custom layout and/or cost requirements use Arduinos.




回答2:


  • Consider taking the approach of re-writing the i2c lib code, using the MC plib code as a starting point. It may be faster than doing a conversion. Also, consider another compiler like the one from CCS - they may have a more robust and well-documented library (has anyone here tried this ?)

    Yeah, MC's docs are dismal at times. Not sure why a billion dollar co. can't do better. Not all functions and macros are documented, and sometimes the examples are not realistic, like the example for the i2c eeprom code.

    I'm having trouble with this lib also, in that the code uses blocking IO calls and does not recover from bus problems. Example: I am making a piece of test gear that tests PCB's with i2c devices. But if the connection is interrupted during a plib operation, the plib code can no longer access the PIC18 i2c port. This is unacceptable for a piece of test gear.



来源:https://stackoverflow.com/questions/25224541/receiving-undefined-symbols-error-with-xc8-concerning-plib-i2c-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!