Digital output - struggling

27 Nov 2010

I  am trying to bit bang an output port linr  this

Digitalout WR(p15)  /* declare port  here and cal l it 'WR'  */

code . . . .

code. . . . .

do {

        shift_out_bit = (serialdata&m);       /* m is a bit position mask */
               if (shift_out_bit==1) {
            WR=HIGH;                        /* I am hoping to set the p15 HIGH with this statement */
        } else if (shift_out_bit==0) {
            WR=LOW;                       /* or set it LOW here  */
        }
       
        printf("wr = %d\n\r",WR);   /* instead,  when I print it out here,  I get 28456 instead of  either 1 or 0 */
        wait_ms(1);
        RS=LOW;                /* clock goes high on main board - data latches */
        wait_ms(1);
        RS=HIGH;                /* clock goes low */
        SCOUNT=SCOUNT+1;        /* increment the bit counter */
        m=(m<<1);               /* shift bit mask up one position towards the MSB */
    } while (SCOUNT<16);

 

and,  nothing happens  on p15 - it just  initially spews out a tean of bits and after that never changes.  My bit bang serial bus is stuck.

 

 

27 Nov 2010

Do you need to reset SCOUNT somewhere?

27 Nov 2010

I put a scope on the port line.  Its toggling  up and down,  so clearly its writing  data out,  its  just not the data  I want.

27 Nov 2010

let me take a look

 

27 Nov 2010

Yes,  SCOUNT is reset  as I enter the function  thus

void serialout(void) {

int SCOUNT=0;

.

.

.

}

27 Nov 2010

If you still can't work it out, it might be worthwhile publishing your whole program then anybody wanting to help can load it into the compiler. It's a bit difficult debugging code snippets!

Regards Daniel

27 Nov 2010

Unfiortunately  its about 5 pages long,  but here is the full listing of void serialout(void)  where I am having the problem.

Digitalout WR(p15);

.

.

.

/*************** Serial bus routine for control relays and digital I/O ***************/
/* this routine takes the 8 bit relay data and the 8 bit  control data and joins them */
/* and writes 16 bits out on the serial bus */

void serialout(void) {

    int unsigned serialdata=0;/* the 16 bits of serial data to go out to the  analog board - first flush  it so its clean */
    int unsigned m=1;   /* this is the bit position counter */
    int shift_out_bit=0;  /* this is the bit that will be sent out */
    int SCOUNT=0;  /* loop counter  */
    int control=0;  /*control is temporary storage of the lower 8 bits of the  serial bitstream */


    /* put the hpmute, mute and recloop1 bits into the relay part of the output bitstream */
    inputrelay=(inputrelay|hpmutebit|mutebit|recloop1bit);  /* these are bits  set in other routines  */

    //printf("1   %d   %d\n\r",relay,inputrelay);  /* just for debugging */

    /* relay  now has the input selection and the status of the mute and recloop1 relays */
    /* next shift the relay data into the top 8 MSB's */

    serialdata=(inputrelay<<8);

    //printf("2   serialdata=%u\n\r",serialdata);

    /* top 8 MSB's now  have the relay data including mute, hpmute and recloop1 and the bottom 8 LSB's  are filled with 0's */
    /* now add the  bottom 8 control bits below */


    control=(control|bright|trigger|power|loopgain); /* last  bit position is unused in the control word */
   // printf("3   control=%d\n\r",control);
    serialdata=(serialdata|control); /* join them  - we now have all the data to send  to the analog board */


    RD=LOW;                     /* ASTROBE - it must be LOW on the analog board intitially */
    WR=HIGH;                     /* DATA */
    RS=HIGH;                     /* ACLK  - so the clock line on the analog board is now LOW*/
    SBUSON=HIGH;                /* turn the SBUS on */
    wait_ms(0.5);
    do {

        shift_out_bit = (serialdata&m);       /* ADATA = the LSB (k=1)  */
        //printf("4   serialdata = %u\n\r",serialdata);
        //printf("5   shiftoutbit = %u\n\r",shift_out_bit);
        if (shift_out_bit==1) {
            WR=HIGH;
        } else if (shift_out_bit==0) {
            WR=LOW;
        }
        //WR=shift_out_bit;
        printf("wr = %d\n\r",WR);
        wait_ms(1);
        RS=LOW;                /* clock goes high on main board - data latches */
        wait_ms(1);
        RS=HIGH;                /* clock goes low */
        SCOUNT=SCOUNT+1;        /* increment the bit counter */
        m=(m<<1);               /* shift bit mask up one position towards the MSB */
    } while (SCOUNT<16);

    wait_ms(1);
    RD=HIGH;                    /* Strobe the data  into the A6821*/
    wait_ms(1);
    RD=LOW;                     /* Strobe now goes LOW again  on the main board */
    wait_ms(1);
    SBUSON=LOW;                 /* remember to enable the outputs after intial set-up */
    wait_ms(1);
}

27 Nov 2010 . Edited: 27 Nov 2010

Here's how to fix your code.  Replace:

if (shift_out_bit==1) {
WR=HIGH;
} else if (shift_out_bit==0) {
WR=LOW;
}

By:

if (shift_out_bit) {
WR=HIGH;
} else  {
WR=LOW;
}

Your code would only ever give the correct result the first time around the loop with m=1.

27 Nov 2010

Thank you.  I  will try  it  now.

27 Nov 2010

I tried it.  No  difference.  I am still  printing out

wr = 28444 
wr = 28444
wr = 28444
wr = 28444
wr = 28444
wr = 28444
wr = 28444
wr = 28444

(this is just a snippet  BTW - it  prints wr=28444  15 times  evertime I turn the  knob.)

 

 

 

27 Nov 2010

Dave,  how is your  code  different to mine?  I  have  written  mine out in full,  while  you  used  'short hand'.

For some reason, I am not getting the port line (i.e. p15) to toggle depending on whether shift_out_bit is TRUE (i.e. non zero) or FALSE (i.e. zero).

 

 

 

27 Nov 2010

Think about the second time through your loop.

At that point m=2.  So shift_out_bit will be either 2 or 0 (depending on the value of serialdata).

Let's suppose that shift_out_bit is 2; both your if statements will execute the "else", and WR will be neither set HIGH nor LOW.

By contrast, my suggested code checks for M nonzero, for which it sets WR HIGH; otherwise it sets it LOW.

27 Nov 2010

I changed the code but  still did not get  the  correct result. 

 

I've  printed out m  using the printf statement,  expecting it to increment but  it  sits at 0.

27 Nov 2010

Andrew

In the compiler select the program in the left pane, right click the mouse and select "publish program". That way it will appear in your profile and we can load it up onto an Mbed and take a closer look. I think that's what Daniel meant earlier

27 Nov 2010

Thanks for explaining - I have published it.

 

I  think the problem is once I send the data out in serialout(),  the program does not seem to respond to subsequent interrupts from the input select routine.

27 Nov 2010

Just going to have a look at your program now. But while I do, take a look at this:-

function "sr" just means shift register

I just knocked this up and on my logic analysiser works fine to shift a 16bit word, LSB first out on a couple of pins, one pin data, one pin clk. I also used "frame" to make triggering my LA easier but could act as a "chip select"

#include "mbed.h"

DigitalOut myled(LED1);
DigitalOut myled2(LED2);
Serial pc(USBTX, USBRX);

DigitalOut frame(p21);
DigitalOut clk(p22);
DigitalOut data(p23);

void sr(uint8_t serialData, uint8_t control) {
    uint16_t word = (serialData << 8) | control;
    
    frame = 0; // Go low to indicate start of frame/word
    clk   = 0; // Ensure clock out is low at start.
    
    for (int loop = 16; loop; loop--, word = word >> 1) {
        data = (word & 1) == 1 ? 1 : 0;
        wait_ms(1);
        clk = 1;
        wait_ms(1);
        clk = 0;
    }
    data = 0;
    frame = 1;

}

int main() {
    pc.baud(115200);
    
    frame = 1;
    clk = 0;
    data = 0;
    
    sr (0xF0, 0x0F);
    
    while (1) {
        myled = !myled;
        wait(0.25);
    }
}
27 Nov 2010

I'm confused about this:-

00015 DigitalIn IPSELA(p24);
00016 DigitalIn VOLPB(p23);
00017 DigitalIn VOLB(p22);
00018 DigitalIn VOLA(p21);

and then later:-

00091 InterruptIn volumein(p21);        /* interuppt from the volume control encoder */
00092 InterruptIn inputsel(p24);      /* for the input select encoder */
00093 InterruptIn mutesw(p26);          /* this mutes the output - pb att to the sel encoder */
00094 InterruptIn powersw(p23);         /* this turns the main power on-off.  Att. to the vol control encoder */

what is p21 and p24, DigitalIn or InterruptIn ?

Note, iirc if you define a pin as InterruptIn then you can read it as though it were a DigitalIn anyway. But decalring the same pin like this to two or more library objects is probably going to give you issues.

 

 

28 Nov 2010

Hi Andy,

thanks for  taking a look.

port lines 21,22 and 23  go to a rotary  encoder.  21  is the encoder channel A  input and interrupt. 24, 25 and 26  are the input select  encoder inputs with P24  the  channel A input on the input select encoder.

These encoders  have an integrated p/button switch - so  this  is where P23 and P26  come in  for the on/off  and  input select inputs. 

I will  use these  functions for programming input as well  at a later date.

 

all  4  of the allowed input pins are assigned to seaprate  input lines,  so  this  should be ok  I think (?)

cheers

 

 

 

 

 

28 Nov 2010

I think I may have worked out the problem,  but not the solution.

Ideally, serialout() is called by the inputselect routine.  It  does  this, but only  once,  and after that simpley  sites in the  serialout()  routine.  This is my  problem.

once the input select value has  been  written out (16  bits  of  info),  it  should  come out of serialout()  and  wait  for the next  interrupt.

I  am clearly  not manipulating  the  interrupts  correctly.

 

28 Nov 2010

starting to look at this. I'll get onto maybe why things are broken shortly. But the first thing I notice is the "write to PGA2320" routine. You are sending LSB first. The data sheet clearly shows it's MSB first. Looking at the datasheet, the SPI/SSP interfaces on the Mbed should be able to handle communincating with the PGA2320 no problem. So that's the first thing I will look at sorting out for you.

Erm, well, I would have done, however, it appears you haven't really wired it up well to make use of the Mbed peripherals. Both SPI ports are wired to other functions (IO) and the CS/SCLK/DATA for the PGA2320 are wired to Mbed pins that don't support either of the available SPI interfaces :(

Can you sketch out a circuit diagram of what you have so far

28 Nov 2010

Andy,  yes  in retrospect,  I could  have used the SPI's,  but  my  board layout  favoured the approach I  used  and the pin assignments  you  see.

I  dont  have a hardware  problem. 

The problem  is I get the first  interrupt  from the input select routine,  once  processed,  this calls the serialout() routine,  I send the  16  bits  and I then never  come out  of  serialout().

 For the PGA2320 - I  will  check that out when I put some audio  through the front end (which I have not done yet) - probably quite an easy  problem to solve

28 Nov 2010

When an interrupt is called vai the callback you should try and ensure you spend the least amount of time within the "interrupt context" as possible. Better if your irq service routine just sets a flag or changes a state that is picked up in the main loop and acted on there. All thos wait_ms() and printf()s inside the isr can be deadly!

But I think you also need to look at that shifting out. It's sending LSB first and it needs to send MSB first for the PGA2320 to understand it properly.

28 Nov 2010

In fact, think about it more, because you are within an ISR of your own, you are going to block all other interrupts until you return. So I can't quite see how the wait_ms() (which uses Timer3 and possibly it's interrupt) might not be timing out. Try using simple for(int tout=1000;tout;tout--); as a small delay rather than wait_ms(). And remove those printf()s

28 Nov 2010

Success - its  working!

Causes  of  problem:- 

1. was stuck in the serialout() procedure,  which was called from the input select ISR.  I removed all the printf statements,  disbled the IRQ  on entry and re-enabled it  (re-attached it)  after completing the ISR.

2. had MSB and LSB bytes swapped.  I  could see the bottom 8 bits of data  changing on my  scope,  but the relays  did not budge.  I swapped the MSB and LSB bytes and voila.

BTW,  this  issue  was  around the input select and mute relays,  and not the  PGA2320.  I'll  keep  a sharp lookout  for the same situation when I test that  part of the system later this week.

Thanks to everyone for their help.