Reply
Posts: 6
Registered: ‎07-20-2016
Accepted Solution

I2C library for EFM8

Hi All - I'm just getting started with the EFM8 Series of microcontroller, having previously worked with the arduino series.

 

For my first project I was looking to interface with an I2C sensor, previously I'd have used the Wire library, is there something similar for EFM8?

 

Thanks for any help.

Posts: 7,953
Registered: ‎08-13-2003

Re: I2C library for EFM8

yes, you should be able to find it through simplicity studio

erik
Posts: 1,735
Registered: ‎10-14-2014

Re: I2C library for EFM8

@Engineer @

As Erikm noted, if you are using EFM8UB2, you could get the example using the driver in folder (simplicity studio)

C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\8051\v4.0.0\examples\EFM8UB2_SLSTK2001A\SMBus\Peripheral_Driver

you could get the driver source file in

C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\8051\v4.0.0\Device\EFM8UB2\peripheral_driver\src

My views are my own and do not necessarily represent the views of Silicon Labs

 

WeiguoLu
Posts: 14
Registered: ‎03-13-2016

Re: I2C library for EFM8

...to expand on the (somewhat unsatisfying) answers mentioned previously, Silicon Labs' 8051 SDK (the set of header files, peripheral drivers, and toolchain required to build projects for each family of MCU) includes some really easy-to-use peripheral drivers -- I find the I2C peripheral driver to be much easier to use than Arduino's Wire library.

 

There's really two steps in getting a peripheral like I2C working:

  1. Get all the configuration registers set-up to enable the peripheral, route the correct clock source into it (and configure the associated timer properly), and configure the peripheral the way you want it to function
  2. Add the peripheral library to the project which is responsible for providing a high-level "send/receive buffers" function (called "I2C0_transfer()") which, under the hood, does all the buffering and bit-wiggling for you.

To accomplish the first task, I recommend beginners use Simplicity Configurator projects to start with, as they allow you to easily configure I/O and peripherals for the MCU, while also being alerted to several "gotch-ya"s associated with 8051 MCUs ("Did you enable your crossbar?" "Did you disable the Watchdog timer?"). 

 

Since many advanced projects have different "modes" of operation that might use different sets of peripherals, Simplicity Studio Configurator projects support this. By default, a "DefaultMode" Port I/O and "DefaultMode" Peripherals tab will appear when you open the .hwconf document (which is the Simplicity Configurator document).

 

Under the Communications set of peripherals, enable the SMBus peripheral (which is the SMBus-compliant I2C master). On the properties panel, choose the options you want. The SMBus peripheral on the EFM8 doesn't have an internal clock generator (which is kind of crappy in this day and age), so you have to select a timer overflow that will be used to generate the SMBus clock source. I used Timer 0 in this example. If you read the datasheet, you can learn about SCL Timeout Detection (which requires Timer 4 to be configured -- sort of a "gotch-ya" -- again, check the datasheet).

studio_2017-01-11_20-36-43.png

Since I'm using Timer 0 overflow in this example, we also have to set up Timer 0, too.

 

I really hate how the Properties panel for Timers works -- each tab basically corresponds to a particular configuration register, instead of being logically organized so that all options are on a separate tab for each timer:

studio_2017-01-11_20-49-15.png

Here, I'm starting in the main Timers panel and putting Timer 0 in Mode 2, which allows auto-reload (that's how you get a timer to overflow at arbitrary frequencies that aren't nice divisions of your SYSCLK). Next, we have to go to Timer 0/1 to actually configure the rate:

studio_2017-01-11_20-53-43.png

Notice that I can do all this stuff without having to look at the datasheet -- as soon as a timer is in auto-reload mode, I can simply key in the "Target Overflow Frequency" and Simplicity Studio will calculate the necessary values to write to the registers. As handy info, it also prints the effective clock rate of peripherals that you can drive from this timer -- for example, if I set the Target Overflow Frequency to 300,000, then you can see SMBus0 SCK frequency is "102 kHz" (close to the 100 kHz standard used by SMBus/I2C). In the "olden days" you'd have to check the datasheet to see what the formula is to determine the correct clock settings.

 

At this point, head over to the DefaultMode Port I/O page and assign the peripheral to the pins:

studio_2017-01-11_20-56-48.png

Compared to more advanced MCUs that have 100% pin-swapping capabilities, the 8051 Crossbar is relatively primitive -- but it's still way better than the Arduino's AVR peripherals that are fixed to specific pins. With power comes complexity, though, so you'll probably have to read the datasheet to learn more about how the crossbar works. Essentially, pins are assigned in specific orders, and you can always "skip" a particular pin if you want to force a peripheral to go to somewhere else on the chip. While there's limited flexibility there, I'm often surprised at how rarely I have pin-muxing problems when laying out PCBs.

 

Anyway, now that your peripherals are configured and assigned, you can move onto step 2, which is to add (and use) the peripheral library to your project. Right-click on the project and go to Project Properties. Go to Project Modules and add the I2c0 peripheral driver to your project:

studio_2017-01-11_21-01-10.png

 

 

Once you do, you'll notice a new "lib" folder in your solution tree, with inc and src folders containing an i2c_0.h and i2c_0.c file.

 

In your main function, give the library a spin:

#include "i2c_0.h"

int16_t main(void) {
	// Note we shift the 7-bit address 
	// to append a low R/W bit, set by the peripheral driver
	uint8_t devAddress = 0x31 << 1;
	uint8_t txBuff[2];
	uint8_t rxBuff[2];
	
	enter_DefaultMode_from_RESET();
	
	// I don't think you have to call this, since the
	// peripheral is configured by the Configurator
	// I2C0_init(I2C0_TIMER0, false);
	
	// read two bytes from the 0x03 register 
	// of a device with address 0x31
	txBuff[0] = 0x03;
	I2C0_transfer(devAddress, txBuff, rxBuff, 1, 2);
	while(!commandComplete); // block until our Cb gets called
	
	// write 0xAA to register 0x01 of the device
	txBuff[0] = 0x01;
	txBuff[1] = 0xAA;
	I2C0_transfer(devAddress, txBuff, NULL, 2, 0);
	while(!commandComplete); // block until our Cb gets called
}

void I2C0_transferCompleteCb() {
	commandComplete = 1;
}

Let us know if you need any more help getting going! Welcome to the EFM8!

Posts: 6
Registered: ‎07-20-2016

Re: I2C library for EFM8

Thanks very much all - I think I should be set to get started!

 

Particular thanks to jaycarlson, that's a really well explained guide - extremely useful to someone like me who is not that familiar with the EFM8.

 

Posts: 2
Registered: ‎02-22-2017

Re: I2C library for EFM8

Hi Jay, 

 

Thank you for your great walkthrough. I wish there were more of these for entry-level people like me. 

 

I'm trying to build the project based on your walkthrough, but I'm getting the following errors: 

 

*** ERROR L127: UNRESOLVED EXTERNAL SYMBOL
    SYMBOL:  _SMB0_transfer
    MODULE:  ./src/EFM8UB1_I2C_Master_main.OBJ (EFM8UB1_I2C_MASTER_MAIN)

*** ERROR L127: UNRESOLVED EXTERNAL SYMBOL
    SYMBOL:  ?_SMB0_transfer?BYTE
    MODULE:  ./src/EFM8UB1_I2C_Master_main.OBJ (EFM8UB1_I2C_MASTER_MAIN)

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  ?_SMB0_transfer?BYTE
    MODULE:  ./src/EFM8UB1_I2C_Master_main.OBJ (EFM8UB1_I2C_MASTER_MAIN)
    ADDRESS: 1000018H

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  ?_SMB0_transfer?BYTE
    MODULE:  ./src/EFM8UB1_I2C_Master_main.OBJ (EFM8UB1_I2C_MASTER_MAIN)
    ADDRESS: 100001BH

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  _SMB0_transfer
    MODULE:  ./src/EFM8UB1_I2C_Master_main.OBJ (EFM8UB1_I2C_MASTER_MAIN)
    ADDRESS: 1000020H

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  ?_SMB0_transfer?BYTE
    MODULE:  ./src/EFM8UB1_I2C_Master_main.OBJ (EFM8UB1_I2C_MASTER_MAIN)
    ADDRESS: 1000035H

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  ?_SMB0_transfer?BYTE
    MODULE:  ./src/EFM8UB1_I2C_Master_main.OBJ (EFM8UB1_I2C_MASTER_MAIN)
    ADDRESS: 1000039H

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  _SMB0_transfer
    MODULE:  ./src/EFM8UB1_I2C_Master_main.OBJ (EFM8UB1_I2C_MASTER_MAIN)
    ADDRESS: 100003DH

Here is my main c file code: 

 

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <SI_EFM8UB1_Register_Enums.h>                  // SFR declarations
#include "InitDevice.h"
#include "i2c_0.h"

// $[Generated Includes]
// [Generated Includes]$

uint8_t commandComplete;
uint8_t devAddress;
uint8_t txBuff[2];
uint8_t rxBuff[2];

//-----------------------------------------------------------------------------
// main() Routine
// ----------------------------------------------------------------------------
int main (void)
{
	// Call hardware initialization routine
	enter_DefaultMode_from_RESET();

	// Note we shift the 7-bit address
	// to append a low R/W bit, set by the peripheral driver
	devAddress = 0x31 << 1;

	enter_DefaultMode_from_RESET();

	// I don't think you have to call this, since the
	// peripheral is configured by the Configurator
	// I2C0_init(I2C0_TIMER0, false);

	// read two bytes from the 0x03 register
	// of a device with address 0x31
	txBuff[0] = 0x03;
	I2C0_transfer(devAddress, txBuff, rxBuff, 1, 2);
	while(!commandComplete); // block until our Cb gets called

	// write 0xAA to register 0x01 of the device
	txBuff[0] = 0x01;
	txBuff[1] = 0xAA;
	I2C0_transfer(devAddress, txBuff, rxBuff, 2, 0);
	while(!commandComplete); // block until our Cb gets called

	while (1) 
   {
     // $[Generated Run-time code]
     // [Generated Run-time code]$
   }                             
}

void I2C0_transferCompleteCb() {
	commandComplete = 1;
}

Many thanks in advance for your help.

 

Best,

Mike

Posts: 14
Registered: ‎03-13-2016

Re: I2C library for EFM8

"UNRESOLVED EXTERNAL SYMBOL" is a general error that indicates you're missing object code (.C files compiled into .O files, or static libs you've forgotten to link to). Specific to this project, I'd suspect you've forgotten to actually include the i2C library in the project properties "Project Modules" pane. It should have created files in the "lib" folder similar to this:

studio_2017-02-22_13-19-43.png

 

For your project, you should have an "i2c_0.h" and "i2c_0.c" file in the "inc" and "src" folders, respectively.

Highlighted
Posts: 2
Registered: ‎02-22-2017

Re: I2C library for EFM8

I've attached a screenshot of my file tree below. I've already added the I2C library through that menu. 

Screen Shot 2017-02-22 at 23.53.53.png

 

I noticed that the I2C library doesn't have a C file, and looking in the header file, the whole I2C library is just a bunch of macros that point to smb_0. I've gone ahead and added that library through the properties too, but I'm getting the following

 

*** ERROR L127: UNRESOLVED EXTERNAL SYMBOL
    SYMBOL:  SMB0_commandReceivedCb
    MODULE:  ./lib/efm8ub1/peripheralDrivers/src/smb_0.OBJ (SMB_0)

*** ERROR L127: UNRESOLVED EXTERNAL SYMBOL
    SYMBOL:  _SMB0_errorCb
    MODULE:  ./lib/efm8ub1/peripheralDrivers/src/smb_0.OBJ (SMB_0)

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  _SMB0_errorCb
    MODULE:  ./lib/efm8ub1/peripheralDrivers/src/smb_0.OBJ (SMB_0)
    ADDRESS: 10000F2H

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  _SMB0_errorCb
    MODULE:  ./lib/efm8ub1/peripheralDrivers/src/smb_0.OBJ (SMB_0)
    ADDRESS: 1000160H

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  SMB0_commandReceivedCb
    MODULE:  ./lib/efm8ub1/peripheralDrivers/src/smb_0.OBJ (SMB_0)
    ADDRESS: 1000180H

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  SMB0_commandReceivedCb
    MODULE:  ./lib/efm8ub1/peripheralDrivers/src/smb_0.OBJ (SMB_0)
    ADDRESS: 10001BFH

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  SMB0_commandReceivedCb
    MODULE:  ./lib/efm8ub1/peripheralDrivers/src/smb_0.OBJ (SMB_0)
    ADDRESS: 10001D2H

*** ERROR L128: REFERENCE MADE TO UNRESOLVED EXTERNAL
    SYMBOL:  _SMB0_errorCb
    MODULE:  ./lib/efm8ub1/peripheralDrivers/src/smb_0.OBJ (SMB_0)
    ADDRESS: 10001E3H