3

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++;
        }
    }
}
4

2 回答 2

3

问题定义

这里的核心问题是 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.cput:版本的头文件中,#include <pic12lf1552.h>以便其余代码具有所有寄存器定义
  • 为您的 SDA 和 SCL 引脚添加定义,i2c.h以便 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。

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

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

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

于 2014-10-05T16:52:17.953 回答