mbed interface with AS1107 (8 digit LED display decode/driver)

12 Mar 2012

Got it breadboarded and it works nicely most of the time. Everynow and again though, the display flakes out and will only display 1 digit or have 1 digit much brighter than the rest. If I reset the mbed once or twice it usually goes back to working well. I have found that it likes a sclk of 100,000. Anyone have any idea as to why I have this sort of intermittent failure. Is it interpin capacitance on the breadboard? Other ideas? I hate to move forward with the project not knowing exactly whats going on. Tnx all...

12 Mar 2012

Could be bad wiring on your breadboard resulting in intermittent connections or noise. Other possibility is the powersupply. Can it provide enough current, are there voltage dips, do you have any buffer capacitors and additional decoupling capacitor. Could also be software issue with mbed somehow reconfiguring the device with wrong data.

12 Mar 2012

Thanks for the reply Wim. I am using the 3v3 sourced by the mbed and the circuit can work flawlessly for days on end. It *intermittently* fails only when I cycle the mbed power. I have electrolytics and small ceramic caps at the mbed and the AS1107. I have tried adding a delay before and after the 1107 setup (though from the datasheet it looks like the chip settles pretty fast) but no joy. I'm powering the mbed via the usb; could this be the source? Thanks again for any thoughts...

12 Mar 2012

OK, does that mean you only get the error after cycling the power? That would also reset mbed and the problem may be in the 1107 initialisation code. Is there a chance that you send some uninitialised data during setup? Have you tried to software reset the device after a power-on by setting bit D1 of the Feature Register before writing the desired setup values to all registers.

You may also want to try and add a delay between the SPI write command and raising of the LOAD/CSN pin. I heard some reports that the mbed spi.write may not have finished before the function returns. Check that you have the right spi mode: Din is valid on rising edge of Scl. Can you check these two points using a logic analyser?

Quick inspection of datasheet says: In order to achieve optimal performance the AS1106/AS1107 should be placed very close to the LED display to minimize effects of electromagnetic interference and wiring inductance. It is recommended to connect a 10μF electrolytic and a 0.1μF ceramic capacitor between pins VDD and GND to avoid power supply ripple. Note: Both GND pins must be connected to ground.

12 Mar 2012

Thanks for taking the time Wim. Yes, only happens after cycling the power or sending a break via the usb-serial o/w works flawlessly. I have the recommended caps in the right places. I tried adding delay between 1 and 100 us between the spi write and bringing cs high but this doesnt change the behavior for the better (actually added a bit of flicker to the display above 10us - curious). Pretty sure I've good the correct mode. Am using 0; I've tried them all though and the others didnt play well. Will try the soft reset via Feature Register next... Thanks again

13 Mar 2012

The info about the problem showing up when you send a break via USB points towards a reset problem rather than a powersupply problem. I guess you also see the problem when you push the mbed reset button.

Could be a problem with wrong init data or maybe something related to your spi pins being reset to standard inputports briefly before reconfiguring them as a spi port again. Can you post a sample of your initcode. Can you inspect the data on the spi port with a logic analyser. If you extract just the init code I can check that for you using my analyser. I dont have the 1107 to test it in circuit.

14 Mar 2012

Thanks again Wim. Here is a portion of my code that contains the init bit. See anything weird here??

  1. define shutdown 0x0c
  2. define disptest 0x0f
  3. define decodemode 0x09
  4. define intensity 0x0a
  5. define scanlimit 0x0b
  6. define feature 0x0e

Serial pc(USBTX, USBRX);

AnalogIn ain1(p19); AnalogIn ain2(p20); DigitalOut relay(p21); alarm relay output DigitalIn forward_load(p22); forward=1 load=0 DigitalIn peak_avg(p23); peak=1 avg=0

SPI LED_disp(p5, p6, p7); setup SPI port DigitalOut cs(p8);

void AS1107(int reg, int data) { write to display interface cs=0; select the device LED_disp.write((reg<<8)|data); wait_us(5); ?? needed cs=1; deselect the device }

void LED_setup() {

LED_disp.format(16,0); mode correct ?? LED_disp.frequency(100000); spi freq ??

AS1107(feature,0x03); AS1107(shutdown,0x01); AS1107(disptest,0x00); AS1107(decodemode,0xff); AS1107(intensity,0x0f); AS1107(scanlimit,0x07); AS1107(feature,0x09);

for (int e=1; e<=8; e++) { blank all digits AS1107(e,0x0f); } }

14 Mar 2012

Hi Gary, I had a look at your code and have some news. First a hint: use the <<code>> <</code>> construct to keep code readable on this page (check the editing tips). This is what your LED_setup code looks like, original and with my proposed changes:

void LED_setup() {

//new
  cs = 1;       // deselect the device
  
  LED_disp.format(16,0);        // mode 0: Clk Low when inactive, Data valid on leading edge of Clk
  LED_disp.frequency(100000);   // spi freq 100kbit/s

#if(1)
//orig
  AS1107(feature,0x03);         // external clk, active reset                     
  AS1107(shutdown,0x01);        // normal mode, reset feature reg
  AS1107(disptest,0x00);        // normal mode
  AS1107(decodemode,0xff);      // decode mode for all digits
  AS1107(intensity,0x0f);       // max intensity
  AS1107(scanlimit,0x07);       // digit 0-7
  AS1107(feature,0x09);         // external clk, enable spi (no effect on 1107)
#else
//new
  AS1107(feature,0x02);         // activate reset, internal clk, code-B, no blink
  AS1107(feature,0x00);         // disable reset ?   
  AS1107(shutdown,0x01);        // normal mode, reset feature reg
  AS1107(disptest,0x00);        // normal mode
  AS1107(decodemode,0xff);      // decode mode for all digits
  AS1107(intensity,0x0f);       // max intensity
  AS1107(scanlimit,0x07);       // digit 0-7
#endif

  // blank all digits
  for (int e=1; e<=8; e++) {
    AS1107(e,0x0f);
  }
}   

Some comments:

You should set CS=1 as early as possible. mbed will set the level to 0 as soon as you define the pin as DigitalOut. I have added that as first instruction.

SPI init mode is OK. It should be 0. Clk Low when inactive, Data valid on leading edge of Clk

SPI clock is OK. 100kbit/s

Then we get to your original init code. You set bit0=1 in the feature reg. That means using external clk (in fact SPI clk). I would use the internal clock and set bit0=0.

It is not that clear from the datasheet how reset works. I would set the bit and then reset it. So write 0x02 and then 0x00 to the feature reg.

The remaining registers (shutdown, disptest,decodemode,intensity,scanlimit look good to me. Blanking digits also looks OK.

You can switch to my init sequence by changing #if(1) to #if(0)

I have run your code and checked it on a logic analyser. See below

/media/uploads/wim/_scaled_screenshot_spi.jpg

Higher res picture is here /media/uploads/wim/screenshot_spi.jpg

Timing and datacontent matches what we expect and looks good. There is an issue on the rising edge of CS. You inserted a 5us delay. The actual delay seems to be 0.5 us. That is due to the early return of the spi.write(). I think the actual transfer is not yet completed before the method returns. Anyhow, it still works. Detail in this pic:

/media/uploads/wim/_scaled_screenshot_spi_cs.jpg

Higher res picture is here /media/uploads/wim/screenshot_spi_cs.jpg

Try my code and see what happens.

There is one other possible explanation for the problems. I did a google search and there was a report that a compatible device from maxim enters a special testmode when the spi data, clk and CS pins are all '1' on power-on. That would happen with mbed also because all pins are inputs on poweron and float to '1'. The solution was to add a transistor switch that turns on the as1107 power under cpu control after you have initialised your spi pins to the correct levels. Not a nice solution, lets hope you dont need it. Would be strange too since you reported that the 1107 might be working fine first and that the problem then happens when you reset mbed. At that time the 1107 would remain powered and should not enter a testmode.

Wim

15 Mar 2012

Well Wim, you have gone above and beyond. Thanks much. I've made the changes as above but the dreaded flicker (pronounced) remains. You can probably tell that I'm a neophyte programmer. That said, I am not going to rest until I lick this problem. I am starting to think that it's not the initialization but perhaps the way I'm calling my display sub. It gets called in a loop and I'm wondering if it is getting re-called (ad nauseum) before the display subroutine completes. I cannot possibly ask you to spend any more time on this (its just a fun project). Will append my code and if anything jumps out as a gross mistake shoot me a buzz. Thanks again...

//pwr_swr5


#define shutdown    0x0c
#define disptest    0x0f
#define decodemode  0x09
#define intensity   0x0a
#define scanlimit   0x0b
#define feature     0x0e

#include "mbed.h"


Serial pc(USBTX, USBRX);

AnalogIn ain1(p19);
AnalogIn ain2(p20);
DigitalOut relay(p21);                      //alarm relay output
DigitalIn forward_load(p22);                //forward=1   load=0
DigitalIn peak_avg(p23);                    //peak=1       avg=0


SPI LED_disp(p5, p6, p7);                   //setup SPI port
DigitalOut cs(p8);


float dBmf,dBmr,Pf,Pr,PWR,SWR;
float alphaF=-44.061,alphaR=-44.061,betaF=94.286,betaR=94.286;          //define constants
float Pf_acc=0,Pr_acc=0,Pf_max=0,Pr_max=0,Pf_avg=0,Pr_avg=0;
float Pf_data [1600],Pr_data [1600];                                    //define buffers
int i=0;
int j=0;
bool delay1=0;
bool delay2=0;
char buf[10];


Ticker analog_in;
Timeout time_relay_off;



void relaydelay() {
    if (SWR>=2.5) {
        relay=1;
        delay2=0;
    } else {
        relay=0;
        delay1=0;
        delay2=0;
    }
}

void AS1107(int reg, int data) {            //write to display interface
    cs=0;                                   //select the device
    LED_disp.write((reg<<8)|data);          //do the write
    cs=1;                                   //deselect the device
}

void LED_setup() {

    cs=1;

    LED_disp.format(16,0);                  //define display format
    LED_disp.frequency(100000);

    AS1107(feature,0x02);         // activate reset, internal clk, code-B, no blink
    AS1107(feature,0x00);         // disable reset ?
    AS1107(shutdown,0x01);        // normal mode, reset feature reg
    AS1107(disptest,0x00);        // normal mode
    AS1107(decodemode,0xff);      // decode mode for all digits
    AS1107(intensity,0x0f);       // max intensity
    AS1107(scanlimit,0x07);       // digit 0-7

    for (int e=1; e<=8; e++) {              //blank all digits (or dashes?  0x0a)
        AS1107(e,0x0f);
    }
}

void display(float PWR,float SWR) {

    sprintf (buf,"%4.0f%4.0f",PWR*10,SWR*100);

    for (int e=0; e<=7; e++) {                  //converts to BCD
        buf[e]&=0x0f;
    }

    if ((buf[0]==0)&(buf[1]==0)) {              //handle leading zero blanking
        buf[0]=0x0f;
        buf[1]=0x0f;
    }

    if (buf[0]==0) {                            //handle leading zero blanking
        buf[0]=0x0f;
    }

    buf[2]|=0x80;                               //places decimal point
    buf[4]=0x0f;                                //writes leading digit space in swr display
    buf[5]|=0x80;                               //places decimal point

    if (SWR>9.99) {                             //handles off-scale SWR reading - displays dashes
        buf[4]=0x0a;
        buf[5]=0x0a;
        buf[6]=0x0a;
        buf[7]=0x0a;
    }
    for (int e=1; e<=8; e++) {                  //do the write
        AS1107(e,buf[e-1]);
    }

    printf ("%x %x %x %x %x %x %x %x \r\n",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]);               //lose this for final version
}

void power_in() {

    dBmf=betaF*ain1.read()+alphaF;                         //read raw data and calculate dBm values
    dBmr=betaR*ain2.read()+alphaR;
    Pf=pow(10,(dBmf-30)/10);                               //calculate forward and reverse power levels (W)
    Pr=pow(10,(dBmr-30)/10);
    Pf_data[i]=Pf;
    Pr_data[i]=Pr;                                         //feed circular buffers
    Pf_acc += Pf_data[i];
    Pr_acc += Pr_data[i];                                  //sum most recent data into the accumulators
    i++;                                                   //bump array pointers
    j++;

    if (i>1599) {                                          //reset circular buffer as necessary
        Pf_avg=Pf_acc;                                     //track averages (averaged over 1.6s)
        Pr_avg=Pr_acc;
        i=0;
    }

    Pf_acc -= Pf_data[i];
    Pr_acc -= Pr_data[i];                                  //subtract old data from running accumulators

    if (Pf>Pf_max) {                                       //update max values
        Pf_max=Pf;
        Pr_max=Pr;
    }

    if (j>199) {                                           //reset max values @ 200ms
        Pf_max=0;
        Pr_max=0;
        j=0;
    }
}

int main() {

    pc.baud(38400);
    relay=0;                                                                     //alarm relay off
    LED_setup();                                                                 //setup display interface

    while (1) {

        analog_in.attach_us(&power_in,1000);                                     //call to analog subroutine

        if ((forward_load==1)&&(peak_avg==1)) {                                  //read switches and make appropriate pwr, swr calculations
            PWR=Pf_max;
            SWR=(1+sqrt(Pr_max/Pf_max))/(1-sqrt(Pr_max/Pf_max));
        }

        if ((forward_load==1)&&(peak_avg==0)) {
            PWR=Pf_avg/1600;
            SWR=(1+sqrt(Pr_avg/Pf_avg))/(1-sqrt(Pr_avg/Pf_avg));
        }

        if ((forward_load==0)&&(peak_avg==1)) {
            PWR=Pf_max-Pr_max;
            SWR=(1+sqrt(Pr_max/Pf_max))/(1-sqrt(Pr_max/Pf_max));
        }

        if ((forward_load==0)&&(peak_avg==0)) {
            PWR=(Pf_avg-Pr_avg)/1600;
            SWR=(1+sqrt(Pr_avg/Pf_avg))/(1-sqrt(Pr_avg/Pf_avg));
        }

        display(PWR,SWR);                                                    //send pwr, swr values to display

        if (SWR>=2.5&&PWR>0&&delay1==0) {
            delay1=1;                                                        //trip relay/alarm
            time_relay_off.attach(&relaydelay,5);
        }

        if (SWR<2.5&&delay2==0) {
            delay2=1;
            time_relay_off.attach(&relaydelay,3);
        }
    }
}
15 Mar 2012

Hi Gary -

I have a couple of suggestions.

1. You are attaching your power function to your ticker inside your while loop. Therefore you are repeatedly attaching it. You only need to do this once, so do this before you get into your while loop.

2. You are calling your display function every pass through your while loop, which is probably executing extremely fast. I would recommend using a second ticker and only updating the display at a human readable frequency, maybe 5 - 10 times per second?

I hope that helps,

Mike

15 Mar 2012

Hey Mike,

Thanks for the input. I actually picked up on taking the power fnx attachment out of the while loop last nite. Man, I am coming along with this but slow... Moving it out made no improvement to the flickering display (and now I understand why). Will set up a display ticker next...

Gary

15 Mar 2012

Quote:

Well Wim, you have gone above and beyond. Thanks much...

Hi Gary, no problem. I enjoy analysing code and debugging hardware when I have some time left, in particular when it's not my own code that I need to fix :)

Anyhow, I agree with Mike's comments: move the attach outside the loop and reduce the update rate of the display. Note that the power function is interrupt driven and it can hit you at any time inside the while loop. That could also mean that the Pf_max, Pr_max etc in your calculations don't actually belong to the same timestamp since they may have been updated halfway through.

BTW Your while loop will be slowed down significantly by the printf inside display.

I have not run your code yet to inspect the external timing. Let me know if you still have problems.

Wim

17 Mar 2012

I have moved the analog attach out of the while loop. Now need to create a new Ticker and not quite sure how to do that. Thinking along these lines:

Ticker disp_out; . . . disp_out.attach(&display(PWR,SWR),0.1)

Not sure if I can pass arguments that way. Any guidance gents?

Gary

17 Mar 2012

Hi Gary, you cant pass parameters PWR SWR to that attached function. Change them into globals instead of parameters. Alternatively, you could create a class that deals with the as1107 and wrap the init, display etc all into that class. The class would have local values pwr,swr that you can set in the main loop. These values are displayed under ticker control.

20 Mar 2012

Wim, Mike,

worked a treat guys! with the changes it took to tweek this code to run properly, i am flabbergasted that the original code worked at all. now, that exquisitely irritating flicker is gone and the display rock stable. peak, avg power and swr calculations are spot on... next, will try my hand at creating a AS1107 class as you suggest Wim. when I get that one tuned up, will make it available via the site. tnx again..

gary

20 Mar 2012

Hi Gary,

good to hear it's working as you planned. Feel free to checkout my example code to drive a 'starburst' type LED display. The driver code uses SPI and is all wrapped in a Class that even has an internal ticker for refreshing the display. The refreshrate is at 300Hz, but you can easily reduce that for your needs. You can probably use that example as a skeleton for the AS1107 class.

See http://mbed.org/users/wim/notebook/starburst-led-display/

Wim

22 Mar 2012

This is the fun bit with coding. You get your program running correctly and then as you more fully exercise it, you find more gotcha's (they're layered pretty deep). Makes me wonder what has to be done to test truly critical code (avionics, medical gear, etc). I think you mentioned something about having my pwr, swr calculations potentially out of sync with the display Wim. Well, I discovered how to expose that potential flaw... On occasion, when I throw the forward_load and peak_avg lines, I will get desparate pwr values. So thats my next task. Can I do this by holding my while loop (or the calculations individually) while the display (and possibly the analog_in?) functions run?? You guys are teaching me a LOT so tnx for the help..

garyb

23 Mar 2012

Testing mission critical code is not easy. Obviously it all starts much earlier during the specification and design phase. You need to analyse exactly what you need, how the device will be used and how it should behave under all circumstances. Then you need to design, implement and test. Testing should be planned just as rigorously by formally stating the inputs and the expected responses under expected and even unexpected conditions. All of these steps need lots of reviews by external specialists and peers to help verifying and validating the work. There is lots of literature descibing these formal design methods and processes. The objective is that we never find ourselves in a drive-by-wire car that will refuse to turn right after taking a right corner without problems for 1024 times..

Back to your problem. I dont know exactly what your code looks like at the moment. In the earlier version there were two possible issues:

  1. The interrupt driven analog_in may lead to inconsistency when it happens during the computations of PWR, SWR in your main while(1) loop. The values Pr_avg etc may change in mid computation. You could prevent that by temporarily disabling the interrupt or by a mechanism that separates the interrupt from the mainloop. For example: The interrupt computes a value and stores it in variable A. The main loop uses variable C. You define a temporary variable B and a protection flag F. The interrupt handler checks F and when true copies A in B and resets F. The main loop checks F and when false copies B in C and sets F. Obviously you need to use that flag to protect all your variables in one go. In practice A,B,C could be implemented as different entries in an array named Pr_avg[3] etc.
  2. The second problem that you may have seen is switch bouncing on the DigitalIn pins forward_load and peak_avg. That may lead to some temporary instability. Try using Andy's DebounceIn libs http://mbed.org/users/AjK/libraries/DebounceIn/lky9pc