IO60P16 PWM and Cerbuino Bee question

The next release is scheduled to come in few weeks.

Aaaaargh… this IO60P16 is driving me crazy :slight_smile:

I think I found another bug… or “feature”. At least I don’t get it. I got a great tip from IanLee74 using transistors to get my 8x8 LED matrix working. I got the transistors and also got the IO60P16 board with enough digital output ports to drive the matrix. The only thing it needs to do is set column and row pins high or low. Nothing with PWM. But same weirdness.

Within the next code I define the row and column pins the application will use. ProgramStarted is assigning the ports. The first 2 assignments:

          red1 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin0, false); // Port 1 pin 0 drops to 0 volts as expected.
            red2 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin1, false); // Port 1 pin 0 will start outputting 3.3 volts again?

Now, on startup port1 pin0 is high (I get 3.3 volts using the multimeter on the pin output connected to cerbuino ground). After the first line of this code (setting red1) the power drops as expected. But… —weirdness warning— now red2 gets defined. Beware, this is a completely different pin and has nothing to do with red1. After assigning red2, red1 starts outputting 3.3 volts again??? while red2 is also set to the initial state of false.

This is also beyond me. I hope I do something wrong, but I can’t see it. I tend to believe this is another driver issue. Or maybe I fried the IO60P16 board somehow? Hope the new driver release will be live soon, I currently can’t get the IO60P16 working on any project. Below is the full code sniplet.

using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;

namespace MegaGame2
{
    public partial class Program
    {

        IO60P16.OutputPort row1 = null;
        IO60P16.OutputPort row2 = null;
        IO60P16.OutputPort row3 = null;
        IO60P16.OutputPort row4 = null;
        IO60P16.OutputPort row5 = null;
        IO60P16.OutputPort row6 = null;
        IO60P16.OutputPort row7 = null;
        IO60P16.OutputPort row8 = null;
        IO60P16.OutputPort green1 = null;
        IO60P16.OutputPort green2 = null;
        IO60P16.OutputPort green3 = null;
        IO60P16.OutputPort green4 = null;
        IO60P16.OutputPort green5 = null;
        IO60P16.OutputPort green6 = null;
        IO60P16.OutputPort green7 = null;
        IO60P16.OutputPort green8 = null;
        IO60P16.OutputPort red1 = null;
        IO60P16.OutputPort red2 = null;
        IO60P16.OutputPort red3 = null;
        IO60P16.OutputPort red4 = null;
        IO60P16.OutputPort red5 = null;
        IO60P16.OutputPort red6 = null;
        IO60P16.OutputPort red7 = null;
        IO60P16.OutputPort red8 = null;

        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {

            /*******************************************************************************************
            Modules added in the Program.gadgeteer designer view are used by typing 
            their name followed by a period, e.g.  button.  or  camera.
            
            Many modules generate useful events. Type +=<tab><tab> to add a handler to an event, e.g.:
                button.ButtonPressed +=<tab><tab>
            
            If you want to do something periodically, use a GT.Timer and handle its Tick event, e.g.:
                GT.Timer timer = new GT.Timer(1000); // every second (1000ms)
                timer.Tick +=<tab><tab>
                timer.Start();
            *******************************************************************************************/

            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            red1 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin0, false);
            red2 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin1, false);
            red3 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin2, false);
            red4 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin3, false);
            red5 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin4, false);
            red6 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin5, false);
            red7 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin6, false);
            red8 = new IO60P16.OutputPort(IO60P16.IOPin.Port1_Pin7, false);
            row1 = new IO60P16.OutputPort(IO60P16.IOPin.Port4_Pin0, false);
            row2 = new IO60P16.OutputPort(IO60P16.IOPin.Port4_Pin1, false);
            row3 = new IO60P16.OutputPort(IO60P16.IOPin.Port4_Pin2, false);
            row4 = new IO60P16.OutputPort(IO60P16.IOPin.Port4_Pin3, false);
            row5 = new IO60P16.OutputPort(IO60P16.IOPin.Port4_Pin4, false);
            row6 = new IO60P16.OutputPort(IO60P16.IOPin.Port4_Pin5, false);
            row7 = new IO60P16.OutputPort(IO60P16.IOPin.Port4_Pin6, false);
            row8 = new IO60P16.OutputPort(IO60P16.IOPin.Port4_Pin7, false);
            green1 = new IO60P16.OutputPort(IO60P16.IOPin.Port7_pin0_PWM8, false);
            green2 = new IO60P16.OutputPort(IO60P16.IOPin.Port7_pin1_PWM9, false);
            green3 = new IO60P16.OutputPort(IO60P16.IOPin.Port7_pin2_PWM10, false);
            green4 = new IO60P16.OutputPort(IO60P16.IOPin.Port7_pin3_PWM11, false);
            green5 = new IO60P16.OutputPort(IO60P16.IOPin.Port7_pin4_PWM12, false);
            green6 = new IO60P16.OutputPort(IO60P16.IOPin.Port7_pin5_PWM13, false);
            green7 = new IO60P16.OutputPort(IO60P16.IOPin.Port7_pin6_PWM14, false);
            green8 = new IO60P16.OutputPort(IO60P16.IOPin.Port7_pin7_PWM15, false);

            GT.Timer Starter = new GT.Timer(1000);
            Starter.Tick += new GT.Timer.TickEventHandler(Starter_Tick);
            Starter.Start();

       }

    }
}

There was a bug with SoftwareI2C on Cerbuino. We fixed it and the new one will come soon.

Hi Dat,

Tnx. Is there any beta available for download I could use?

Dat, I suspect there’s still a bug on the Hydra also. Did you test for the same problem there? In some tests I was running on the IO60P16 on the Hydra a week or so ago, I was getting unreliable reads.

@ ianlee74 -

Yes, Hydra and Cerbuino have same issue.

Fantastic! Looking forward to seeing that bug squashed once and for all.

Hi,

I was just wondering about this bugfix. Is there any news or release date?

Hope to hear from you :slight_smile:

did you look in the 14th February release notes? They say no known issues.

In fact, GHI Electronics – Where Hardware Meets Software now says

  • Fixed SoftwareI2C on all OSHW devices

Hi Brett!,

Thanks. I thought I already had the 4.2 firmware, but this is indeed a new release. I do have a new PC with windows 8 on it but did successfully got the firmware upgraded just now and indeed, the problem seems to be fixed!

Thanks!!

Another question :slight_smile:

I’m using the new firmware and the IO60P16 is now cured of all the pain… except, but perhaps that is just me doing something wrong, it seems to be extremely slow?

I did a test creating 24 digital out ports and switching them on/off in a loop. Setting 24 pins off (just by writing pin.Write(false); to each one of them) takes around 116ms. That’s about 8ms each pin state change. Is that because of the communication between the cerbuino and the board? Or is this acceptable speed?

I’m thinking about an project that will need a lot of IO changes but at this speed it will never work.

@ mammaplank - The downside of software i2c…

Yea, you may want to do the same test using hardware I2C. Here’s my LED cube running on an IO60P16. I didn’t time it but it’s pretty darn fast. Most of my animation loops also have 20ms sleeps in order to get the brightness more visible. Note that this is being powered by the module directly which is why they’re so dim.

@ mammaplank -
To optimize a little bit, you can use adjacent port read/write feature of the chip. This saves many clock cycles to/from chip. I’ve done this using this chip in hadware i2c and native code (pic32), but this could work also with sofware i2c.

Tnx all,

I’ve got the LED Matrix to work now, but here it is trying to display the letter A:

As you can see it needs to refresh that a lot quicker in order for my eyes and brain to really see the letter A and not some flickering LED’s.

@ Ianlee74, did you blog about getting the IO60P16 working with hardware i2c? I’d like to try that, but would not know where to start about getting that to work.

Also, how could you power the LED’s with another powersource? I set a Cerbuino pin high to power a specific LED. Do you need to have each of these pins switch some kind of relais with an external powersource to the matrix pin? Because that would require 24 relais as well.

Thanks for all your help again!

[quote=“Ianlee74”]My driver is located at:
https://github.com/ianlee74/IO60P16[/quote]

Also, as dobova pointed out, writing to multiple consecutive registers in one transaction will help.
When you SetPin(X, state) you are changing one bit (at a time) in the PinState register; that is why it is so time-consuming.
It would be easier to visualize the IO60P16 as two shift-registers.

Edit:
GHI module you are implementing: http://www.ghielectronics.com/downloads/Gadgeteer/Module/LED%20Matrix%20sch.pdf
Notice there are two octal latches with pins labeled rows and columns.

Calling them rows or columns and/or whether you rotate the LED panel 90 degrees or not are both arbitrary and up to you.

Looks like you are using ports 1 and 7. The following code uses 0 and 1. The first port doesnt matter, but the second should be immediately following the first.

private I2CDevice IO60P16 = new I2CDevice(new I2CDevice.Configuration(0x20, 100));
private byte[] bytesToWrite = new byte[8];
private void RefreshColumn(byte rowOrColumnNumber) //0 - 7
{ IO60P16.Execute(new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(new byte[] { 0x8, (byte)(1 << rowOrColumnNumber), bytesToWrite [rowOrColumnNumber] }) }, 20); }

Edit: The code above is for hardware I2C. The Gadgeteer driver uses software I2C, but the module happens to be wired to use the same 3 pins as an ‘I’ socket, anyway. So to use hardware I2C, you should just plug the module into an ‘I’ socket. To use software I2C, just use ianlee’s driver.

Next, decide on row or column order and fill in bytesToWrite with the correct data; the attachment shows ‘A’ both ways.
You can rotate letters by transposing row values and column values.
Edit: While you are filling bytesToWrite, you can bit-shift your template letter to scroll them horizontally.
A thread could loop separately calling RefreshColumn(int columnIndex) for each column. The delay in this loop would set your refresh rate/brightness.

I haven’t but I probably should since it seems I’m answering this question frequently :wink: If you’re using my driver, there are only two things that you need to do.

  1. Map your “X” socket to an “I” socket. (see picture. Power & ground go straight through)
  2. Compile my driver using the HARDWARE_I2C conditional compile option.

If that doesn’t get you going, let me know and I’ll try to get the blog post up soon.

You would use transistors to switch a +5V source. There is a max current draw that the chip on the IO60P16 can supply (100 mA?). You have to be aware of this if you are powering directly from the module since this will limit the number of LEDs you can light at once. If you use transistors (one per column) then you become limited by your power source rather than the module.