I2C driver for a PCA9685 servo controller board for s90 servos

Can anyone provide a C program that can be run in CCS that would program an I2C controller on the BBB board to generate the desired clock frequency signal and the required data signals on the I2C bus.

Part 1. Program the device to generate signals to turn LED15 to full ON. Should be measurable voltage from the number 15 signal pin on the servo board.

Part 2. Develop commands you send to PCA9685 to intialize it for the correct frequency for your servo, set up a timer on the BBB to control delays, and intialize the BBB User LEDs.

The phrasing of this sounds very much like it is some sort of homework

  Doing someone else's homework is frowned upon in many forums. The
mention of CCS also complicates matters in that CCS supports bare-board
development (using TI's SDK, I believe) and maybe Linux development.

  If running under Linux, there are native compilers which run on the
BBB, though without an IDE (especially if one hasn't installed X-Window
system). Also one can set up a cross-development environment (easiest to be
running in a desktop Linux -- set up Debian in Oracle VirtualBox if running
on a Windows system... Instructions for cross-development, including
configuring Eclipse, are in Chapter 7 of Molloy's Exploring Beaglebone 2nd
Ed [or use his Exploring Raspberry-Pi -- chapter 7 is practically

  So first: Are you talking about a bare-board (no OS) configuration or
an application to run under Linux? I don't recall anyone on the forum that
regularly works with bare-board -- there is just so much that has to be set
up just to start a program running.

  Second: Show us YOUR code and explain what doesn't seem to be working
with it. And don't submit the equivalent of

#include <stdio>
void main(argc, *argv)
  /* need help here */

We can help correct your attempts, but won't write the code for you.
Providing a link to the documentation for your peripheral board would also
have been useful.

  Heh -- I'd probably start with the out-of-stock Adafruit board, and use
Python via the Adafruit_Blinka compatibility library to use their
CircuitPython PCA9685 module.

If (as Dennis asked) you are asking about this program for a bare-board (no OS) configuration, then I suggest you look at TI's starterware. It has routines for I2C and GPIOs that you can use from the C program to provide the functionality you are asking for. You can use CCS for the development. There are videos online for "getting started" with starterware and CCS.


P.S Dennis - FYI, I have a Beaglebone white and a PocketBeagle and I only run starterware on them. I do read this forum often.

Ahh if not homework to me it sounds like work assignment and it’s obvious he’s planning on running barebones to perhaps do proof of concept. Barebones examples come from starterware and are fastest way.
I agree with Dennis either way you need to make an effort
If I was green and clueless how to code this I’d start with part 1 and find an I2C starterware example and modify it

Part 2 Google “PCA9685 I2C driver c code”
basically you are encapsulating the device commands from data sheet so read that

If you can’t get part A to work I would

A) ask for a refund from your university and/Or
B) work as much overtime as you can before you get fired from the job when your boss sees you are asking questions that show how green you are.

So I have been working on this on my own time, I am a potential CE student. This was an assignment for someone who gave it to me along with caped BBB. I am running barebones using CCS and a TI XDS100v2 USB Debug Probe/CortxA8 sim. They wrote this program as an LCD driver using the I2C1 bus from the BBB.
I am trying to use this code to send a signal to the PWM PCA9685 LED15 pin to full ON. I am trying to configure the pins (19/20) on the BBB to use the I2C2 in Mode3 on the P9 connector The datasheet is here https://cdn-shop.adafruit.com/datasheets/PCA9685.pdf

Here is the code so far: It will run through the cases and go to the endless loop, but am not getting a signal on the pin 15 of the PCA9685. I am trying to just send two bytes at a time and I think this is what is wrong with the code, but I have been debugging.

// Define Indirect Addressing Macro for Registers

#define HWREG(x) (*((volatile unsigned int *)(x)))

// Common Defines
#define TRUE 1
#define FALSE 0
#define DELAY_COUNT 100000

// Base Module Defines
#define CTRLMOD_BASE 0x44E10000
#define CM_PER_BASE 0x44E00000
#define I2C2_BASE 0x4819C000
// Control Module Defines
#define CONF_I2C2_SCL 0x97C
#define CONF_I2C2_SDA 0x978
#define MODE3 0x3B

// Peripheral Control Module Defines
#define CM_PER_I2C2_CLKCTRL 0x44
#define CLK_ENABLE 0x2

// DRM Register Offset Defines
#define I2C_2_SUSPEND_CTRL 0x230

// Register Address Offset Defines
#define I2C_SA 0xAC
#define I2C_CNT 0x98
#define I2C_DATA 0x9C
#define I2C_IRQSTATUS_RAW 0x24
#define I2C_CON 0xA4
#define I2C_PSC 0xB0
#define I2C_SCLL 0xB4
#define I2C_SCLH 0xB8
#define I2C_BUFSTAT 0xC0
#define I2C_IRQENABLE_SET 0x2C

// I2C Register Values
#define _12MHZ_CLK 0x03
#define tLOW 0x08
#define tHIGH 0x0A
#define I2C2_ENABLE 0x8600
#define IRQ_DISABLED 0x0000

// Mask Defines
#define DCOUNT_VAL 0x0000FFFF
#define XRDY_RDY 0x00000010
#define XRDY_BIT 0x00000010
#define RRDY_BIT 0x00000008
#define RRDY_RDY 0x00000008
#define BF_BIT 0x00001000
#define BUS_IS_FREE 0
#define DATA_VAL 0xFF
#define BUFSTAT_VAL 0x0000003F

//I2C Communication Defines
#define SLAVE_ADDR 0x40
#define NUM_OF_DBYTES 10
#define START_COND 0x00000001
#define STOP_COND 0x00000002
#define MASTER_TX_MODE 0x600

// General registers
#define PCA9685 0x80
// I2C address for PCA9865 with all inputs at zero
#define Reset 0x01 // Reset the device
#define MODE1 0x00 // 0x00 location for Mode1 register address
#define MODE2 0x01 // 0x01 location for Mode2 register address
#define PRE_SCALE 0xFE // Prescaler address
#define P_S_VALUE 0x79 // PWM frequency value

// MODE1 bits PCA9685
#define PCA96_INIT 0x11
#define LED15_ADD 0x43

// Variables
unsigned int x;
unsigned int y;
volatile unsigned int USR_STACK[100];
volatile unsigned int IRQ_STACK[100];

void wait(void){
// Endless loop

void delay(unsigned long int y){

int is_bus_free(void){
x = HWREG(I2C2_BASE + I2C_IRQSTATUS_RAW); //Read mask 0x00001000 from I2C_IRQSTATUS_RAW (I2C Status Raw Register) offset 0x24 to check bus status.
x = (x & BF_BIT); //Mask.
if(x == BUS_IS_FREE) return 1;
else return 0;

int is_i2c_write_ready(void){
x = HWREG(I2C2_BASE + I2C_IRQSTATUS_RAW); //Read mask 0x00000010 from I2C_IRQSTATUS_RAW (I2C Status Raw Register) offset 0x24 to see if write ready
x = (x & XRDY_BIT); //Mask.
if(x == XRDY_RDY) return 1;
else return 0;

void startstop_condition(void){
HWREG(I2C2_BASE + I2C_CON) = 0x8603; //Read-Modify-Write 0x8603 to I2C_CON (Configuration Register) offset 0xA4 to queue Start/Stop Condition.
// x = (x | START_COND | STOP_COND); //Mask.
// HWREG(I2C2_BASE + I2C_CON) = x; //Write back. //Write back.

void config_master_transmitter(void){
x = HWREG(I2C2_BASE + I2C_CON); //Read-Modify-Write 0xE00 to I2C_CON (Configuration Register)offset 0xA4 to configure mode.
x = (x | MASTER_TX_MODE); //Mask.
HWREG(I2C2_BASE + I2C_CON) = x; //Write back.

void set_num_databytes(unsigned int y){
// y = (y & DCOUNT_VAL);
//Number of Data Bytes pre-transmission.

void write_to_bus(unsigned char x){
x = (x & DATA_VAL );
HWREG(I2C2_BASE + I2C_DATA) = x; //Write to data bus.

void set_slave_addr(unsigned int x){
//Slave address pre-transmission.
HWREG(I2C2_BASE + I2C_SA) = x; //Write 0x40 to I2C_SA (Slave Address Register) offset 0xAC Slave address value


//P9 Connector settings.
HWREG(CTRLMOD_BASE + CONF_I2C2_SCL) = 0x3B; //Write 0x3B to conf_uart1_rtsn offset 0x97C to enable (SCL) for MODE3 w/o pullup
HWREG(CTRLMOD_BASE + CONF_I2C2_SDA) = 0x3B; //Write 0x3B to conf_uart1_ctsn offset 0x978 to enable (SDA) for MODE3 w/o pullup

//Enable Clock to I2C2.
HWREG(CM_PER_BASE + CM_PER_I2C2_CLKCTRL) = CLK_ENABLE; //Write 0x2 to CM_PER_I2C2_CLKCTRL offset 0x48 to enable I2C2 Clock.

//Configure I2C2.
HWREG(I2C2_BASE + I2C_PSC) = _12MHZ_CLK; //Write 0x03 to I2C_PSC (Clock Prescalar Register) offset 0xB0 for ICLK of 12 MHz
HWREG(I2C2_BASE + I2C_SCLL) = tLOW; //Write 0x08 to I2C_SCLL (SCL Low Time Register) offset 0xB4 for tLOW to get 400kbps (1.25usec)
HWREG(I2C2_BASE + I2C_SCLH) = tHIGH; //Write 0x0A to I2C_SCLH (SCL High Time Register) offset 0xB8 for tHIGH to get 400kbps (1.25usec)
HWREG(I2C2_BASE + I2C_CON) = I2C2_ENABLE; //Write 0x8603 to I2C_CON (Configuration Register) offset 0xA4 to take out of reset, enable I2C2 module


void init_pwm(case_0){

HWREG(I2C2_BASE + I2C_IRQSTATUS_RAW) = 0x00000114;

HWREG(I2C2_BASE + I2C_CON) = I2C2_ENABLE; //Write 0x8603 to I2C_CON (Configuration Register) offset 0xA4 to take out of reset, enable I2C2 module

// config_master_transmitter();

//unsigned int current_DCOUNT;

// set_slave_addr(SLAVE_ADDR);

while(is_bus_free() != TRUE){


//while((HWREG(I2C2_BASE + I2C_BUFSTAT) & BUFSTAT_VAL) > 0){

//while(is_bus_free() != TRUE){


if(is_i2c_write_ready()){//If ready to write
case 0:
write_to_bus(0x00); //send 0x11 to Mode1 to set sleep and respond to AllCall
case 1:
write_to_bus(0xFE); //send 0x79 for Prescaler (using formula)
case 2:
write_to_bus(0x81); //Send 0x81 to enable RESTART,ALLCALL,INT_CLK,NORM_MODE
case 3:
write_to_bus(0x04); //Send 0x04 to enable Totem pole structure, non-inverted
case 4:
write_to_bus(0x45); //Send 0x10 to turn off LED15 before turning on
case 5:
write_to_bus(0x43); //Send 0x00 to LED15 to full ON



int main(void){



return 1;

Feel free to take a look and comment on something, maybe why I am not getting a signal?

The code you copied below seems to be incomplete. After the routine “set_slave_addr” and before the comment “//P9 Connector settings”, I believe there is missing code. I therefore cannot tell if your setup code is complete.


You are right the code that is missing is

void i2c_init(void){

FYI I do not have an oscilloscope so I have just been testing pin 15 with a multimeter and I also just have an led on a breadboard, to test if a signal is coming from the pwm on pin 15.

Id start with Original program that drove LED make sure it’s working. You may have code that’s not a valid starting point. Then wire up the Output to the new LED. Once that works use the register setting and existing code to move to the new I2C. You might have inherited broken code. Start simple as in getting a proper I2C clock and Data. Good news you have a JTAG to inspect mux and I2C register values it’s not good you don’t have a scope or Salae logic analyzer. Too me that seems a bit difficult for someone pre CSEE with no debugging experience.
Put a loop around the transmission code with a delay. You want to start with the minimal working example. You also need to be able to read the board schematic for correct I2C header pins. Did you find a starterware I2C example for your board I’d trust it more than this code. Again break it down to simplest working subset as in I2C clock and Data working I think using just a Led is to debug is a bit too much to ask for a beginner I’d nicely request a cheap serial protocol analyzer it’s essential for debugging the commands and cheaper than a scope. If you’re serious about becoming a low level embedded Engineer beg borrow or steal the $$$ if whomever assigned you this won’t supply a scope or analyzer. Look at as an investment in your future. Also a simpler example will be easier to ask for help. Stay positive and keep digging and learning and asking questions.

As for pwm if duty cycle is to fast you might not see a led turn on ask for a scope. Your Original post discussed I2C command’s I’m not familiar with that part but not seeing the duty cycle or using a LED seems brutal to require for someone starting out. Perhaps you are being hazed or the person assignment is unrealistic. My self I’d suspect the code was left incomplete hence start with Original set-up with a Led driver get it working.

I did a quick review of the set up part of the code you posted. The code is not complete. It is missing some clocks setup.

You should download the TI AM335x Technical Reference manual. I have version spruh73q. Look at the picture on page 1563. You will see that the L4_per is connected to the L3S unit. Now look at the picture on page 1565. You will see the I2C2 is on the L4 peripheral bus (L4_per). So you need to enable the clocks for both the L3s and the L4 unit to be able to use I2C2.

I did not look at any of the other code, but I think the value for the pin mux should be 0x33. That turns on the pull up.

There is a lot of great information in the Reference manual, so happy reading and coding.


Thank you for the comments, yeah I am just taking my time trying to get familiar with the datasheet because it is a lot to take into consideration.
That is the version datasheet I have been looking at for the Sitarra and will continue to study it a bit and look into the clock enables.


Referencing the TRM not the datasheet with a working example is your best chance for success. The TRM is a phone book sizeif printed it’s supplied in a odf.The datasheet is for hardware board designers.

Even if the TRM has steps to follow As in pseudo code to initialize a subsystem you need code to do things in logical order for multiple sub system’s and a minimal setup code environment to load and run and debug using CCS and JTAG some can be done within the gel script for a barebones system,

That’s why you leverage the starterware examples.

1)Have you looked over the TI starterware examples for an I2C example?
2) Have you anything that compiles in CCS and loads and runs using JTAG on your board?
3) which board are you using?

Once you have a working example lookup all register writes and read that section of TRM to port to appropriate onchip peripheral.

Without #2 things there’s no point in expecting anything to work properly as in delivering a working example to meet the requirements you were given

Here’s some cleanly written code which gives the high level overview of what is required to do most of what you need to do but not everything

If you answer my question in previous reply I can try and guide you.