I2C driver for a PCA9685 servo controller board for s90 servos

134 views
Skip to first unread message

marc...@gmail.com

unread,
Aug 8, 2020, 3:57:15 PM8/8/20
to BeagleBoard
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.

Dennis Lee Bieber

unread,
Aug 9, 2020, 2:07:59 PM8/9/20
to Beagleboard
On Wed, 5 Aug 2020 18:36:12 -0700 (PDT), in
gmane.comp.hardware.beagleboard.user
The phrasing of this sounds very much like it is some sort of homework
assignment.

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
identical).

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.


--
Dennis L Bieber

Graham Stott

unread,
Aug 9, 2020, 9:15:12 PM8/9/20
to beagl...@googlegroups.com
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.

Graham

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.
--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beagleboard/4dd0jfd9hajh7649m43sun2j6t02ti9a1p%404ax.com.

Mark Lazarewicz

unread,
Aug 9, 2020, 9:45:30 PM8/9/20
to beagl...@googlegroups.com
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.


On Sun, Aug 9, 2020 at 8:15 PM, Graham Stott
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.

Graham

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.



-----Original Message-----
From: beagl...@googlegroups.com [mailto:beagl...@googlegroups.com] On Behalf Of Dennis Lee Bieber
Sent: Sunday, August 09, 2020 11:08 AM
To: Beagleboard <beagl...@googlegroups.com>
Subject: [beagleboard] Re: I2C driver for a PCA9685 servo controller board for s90 servos

On Wed, 5 Aug 2020 18:36:12 -0700 (PDT), in gmane.comp.hardware.beagleboard.user
marcbob34-Re5JQE...@public.gmane.org wrote:

>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  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 assignment.

    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 identical).

    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.


--
Dennis L Bieber

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard+unsub...@googlegroups.com.
--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard+unsub...@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/beagleboard/000501d66eb3%24a5aa96b0%24f0ffc410%24%40comcast.net.

marc...@gmail.com

unread,
Aug 15, 2020, 5:35:21 PM8/15/20
to BeagleBoard
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
#define NAME_BYTE_LENGTH    13

// 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){
    while(1){
    // Endless loop
                   };
                   }

    void delay(unsigned long int y){
while(y>0){
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.
        HWREG(I2C2_BASE + I2C_CNT) = y;
}
void write_to_bus(unsigned char x){
        x = (x & DATA_VAL );
        HWREG(I2C2_BASE + I2C_DATA) = x;                            //Write to data bus.
        delay(2000);
                                }
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
    //config_master_transmitter();
    // HWREG(I2C2_BASE + I2C_IRQENABLE_SET) = IRQ_DISABLED;
    set_slave_addr(SLAVE_ADDR);

                                }
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){

                                    }
    //set_num_databytes(0x2);
    //startstop_condition();

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

    //current_DCOUNT = HWREG(I2C2_BASE + I2C_BUFSTAT) & BUFSTAT_VAL;
    //while(is_bus_free() != TRUE){

                                  //}
    //set_num_databytes(0x2);
    //startstop_condition();

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

                break;

                                }
                        }
                    }
                //}





                int main(void){

                i2c_init();

                init_pwm(0);
                init_pwm(1);
                init_pwm(2);
                init_pwm(3);
                init_pwm(4);
                init_pwm(5);


                wait();
return 1;
}

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

Graham Stott

unread,
Aug 15, 2020, 7:20:29 PM8/15/20
to beagl...@googlegroups.com

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.

 

Graham

 

From: beagl...@googlegroups.com [mailto:beagl...@googlegroups.com] On Behalf Of marc...@gmail.com
Sent: Saturday, August 15, 2020 2:35 PM
To: BeagleBoard <beagl...@googlegroups.com>
Subject: [beagleboard] Re: I2C driver for a PCA9685 servo controller board for s90 servos

 

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.

--

For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.

To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beagleboard/5a0fb22d-8c0b-4c1e-8e90-6576aba1717eo%40googlegroups.com.

marc...@gmail.com

unread,
Aug 15, 2020, 8:27:02 PM8/15/20
to BeagleBoard
You are right the code that is missing is


void i2c_init(void){

On Saturday, August 8, 2020 at 12:57:15 PM UTC-7, M wrote:

marc...@gmail.com

unread,
Aug 15, 2020, 11:18:52 PM8/15/20
to BeagleBoard
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.


On Saturday, August 8, 2020 at 12:57:15 PM UTC-7, M wrote:

Mark Lazarewicz

unread,
Aug 15, 2020, 11:58:30 PM8/15/20
to beagl...@googlegroups.com
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.

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard...@googlegroups.com.
To view this discussion on the web visit

Mark Lazarewicz

unread,
Aug 16, 2020, 12:09:14 AM8/16/20
to beagl...@googlegroups.com
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.
On Sat, Aug 15, 2020 at 10:18 PM, marc...@gmail.com

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard...@googlegroups.com.
To view this discussion on the web visit

Graham Stott

unread,
Aug 16, 2020, 6:37:10 PM8/16/20
to beagl...@googlegroups.com

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.

 

Graham

 

 

 

From: beagl...@googlegroups.com [mailto:beagl...@googlegroups.com] On Behalf Of marc...@gmail.com
Sent: Saturday, August 15, 2020 5:27 PM
To: BeagleBoard <beagl...@googlegroups.com>
Subject: [beagleboard] Re: I2C driver for a PCA9685 servo controller board for s90 servos

 

You are right the code that is missing is

--

For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard...@googlegroups.com.

marc...@gmail.com

unread,
Aug 16, 2020, 7:28:02 PM8/16/20
to BeagleBoard
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.


On Saturday, August 8, 2020 at 12:57:15 PM UTC-7, M wrote:

Mark Lazarewicz

unread,
Aug 16, 2020, 8:45:07 PM8/16/20
to beagl...@googlegroups.com
Hello

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





--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard...@googlegroups.com.
To view this discussion on the web visit

Mark Lazarewicz

unread,
Aug 16, 2020, 9:48:30 PM8/16/20
to beagl...@googlegroups.com
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. 




Reply all
Reply to author
Forward
0 new messages