CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

losing my mind? or can I not figure out a simple for() loop?
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
thefloyd



Joined: 02 Sep 2009
Posts: 46

View user's profile Send private message

losing my mind? or can I not figure out a simple for() loop?
PostPosted: Mon Sep 17, 2012 10:30 am     Reply with quote

ok, so I'm not the best programmer/hardware guy out there but I can certainly handle a for() loop.. why oh why doesn't this work?

Code:

    for(bit=23;bit>=0;bit--) {
      if(bit_test(this_led, bit) == 1) {
        spi_write(ws2811_one);
      }
      else {
        spi_write(ws2811_zero);
      }
    }


Whereas this works just fine:

Code:

    if(bit_test(this_led, 23) == 1) {
      spi_write(ws2811_one);
    }
    else {
      spi_write(ws2811_zero);
    }
    if(bit_test(this_led, 22) == 1) {
      spi_write(ws2811_one);
    }
    else {
      spi_write(ws2811_zero);
    }
    if(bit_test(this_led, 21) == 1) {
      spi_write(ws2811_one);
    }
    else {
      spi_write(ws2811_zero);
    }
    if(bit_test(this_led, 20) == 1) {
      spi_write(ws2811_one);
    }
..... all the way down to zero


Obviously the hardware is fine as the latter works.

I've tried all sorts of permutations of my for() loop. I'm sure I'm missing something obvious.. can someone provide me with the electronic slap upside the head?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Sep 17, 2012 11:50 am     Reply with quote

Make the first code into a small but compilable test program, with the
#include for the PIC, #fuses, #use delay(), main(), all variable and
constant declarations, etc.

Also post your compiler version.
Ttelmah



Joined: 11 Mar 2010
Posts: 19514

View user's profile Send private message

PostPosted: Mon Sep 17, 2012 11:50 am     Reply with quote

Number types...
If you have an unsigned integer, it is _always_ going to be >=0.
When you decrement past 0 it wraps to be 255,

Hence loop can't work.

Best Wishes
thefloyd



Joined: 02 Sep 2009
Posts: 46

View user's profile Send private message

PostPosted: Mon Sep 17, 2012 12:16 pm     Reply with quote

Thanks for the quick replies.

ttelmah, nice catch. This explains a different problem I had somewhere else, but not this one as the following don't work:

Code:



for(bit=23;bit!=255;bit--)

or

int bit;
for(bit=23;bit>=0;bit--)



PCM: my compiler version is 4.134

A quick sample program is below. I've included the broken send_frame() routine as the working one is fairly obvious.

Code:

#include <18f46k22.h>
#Fuses INTRC_IO,PLLEN,PRIMARY_ON,NOFCMEN,NOIESO,PUT,
#Fuses BROWNOUT,BORV29,NOWDT,
#Fuses CCP2C1,NOPBADEN,CCP3B5,
#Fuses NOHFOFST,TIMER3B5,NOMCLR,
#Fuses NOSTVREN,NOLVP,NOXINST,NODEBUG
#Fuses NOPROTECT,NOCPB,NOCPD,NOWRT,NOWRTC
#Fuses NOWRTB,NOWRTD,NOEBTR,NOEBTRB

#use delay( clock=64000000,INTERNAL )

#define LED_STRIP_LEN 50
unsigned int32 led_strip_colors[LED_STRIP_LEN];

#define ws2811_zero 0b10000000
#define ws2811_one 0b11110000

void send_frame() {
  unsigned int16 i;
  unsigned int bit;
  unsigned int32 this_led;

  for(i=0;i<LED_STRIP_LEN;i++) {
    this_led = led_strip_colors[i];
    for(bit=23;bit!=255;bit--) {
      if(bit_test(this_led, bit) == 1) {
        spi_write(ws2811_one);
      }
      else {
        spi_write(ws2811_zero);
      }
    }
  } 
  delay_us(50); // latch
}

void single_color(unsigned int32 color1) {
  unsigned long i;
  for(i=0; i<LED_STRIP_LEN; i++) {
    led_strip_colors[i] = color1;
  }
}

void main(void) {

  unsigned int16 i;
  unsigned int j;
  setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16 );

  delay_ms(250);
 
  single_color(0xFFFFFF);
  send_frame();
  delay_ms(5000);
  single_color(0xFFC58F);
  send_frame();
  delay_ms(5000);
  single_color(0xFF9329);
  send_frame();
  delay_ms(5000);
  single_color(0xFF0000);
  send_frame();
  delay_ms(1000);
  single_color(0x00FF00);
  send_frame();
  delay_ms(1000);
  single_color(0x0000FF);
  send_frame();
  sleep;
}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Sep 17, 2012 12:54 pm     Reply with quote

You may be able to debug your problem by running your program in
MPLAB simulator. Modify it as shown below in the lines marked "TESTING"
and setup the UART1 feature of MPLAB to display hardware UART text in
the Output Window of MPLAB. This post explains how to do that:
http://www.ccsinfo.com/forum/viewtopic.php?t=23408&start=1

For example, if your send_frame() routine gets an input value that is all
1's, then it should transmit 24 bits of all 1's, like this:
Quote:
111111111111111111111111

With this method you can test the "pure code" portion of your program
to see if it's working OK. It doesn't test SPI. It just tests your internal
algorithm. But at least you can easily test it now.

Note all lines marked with *** TESTING ***:
Code:

#include <18f46k22.h>
#Fuses INTRC_IO,PLLEN,PRIMARY_ON,NOFCMEN,NOIESO,PUT,
#Fuses BROWNOUT,BORV29,NOWDT,
#Fuses CCP2C1,NOPBADEN,CCP3B5,
#Fuses NOHFOFST,TIMER3B5,NOMCLR,
#Fuses NOSTVREN,NOLVP,NOXINST,NODEBUG
#Fuses NOPROTECT,NOCPB,NOCPD,NOWRT,NOWRTC
#Fuses NOWRTB,NOWRTD,NOEBTR,NOEBTRB

#use delay( clock=64000000,INTERNAL )
#use rs232(baud=9600, UART1, ERRORS)  // *** TESTING ***

#define LED_STRIP_LEN 1   // *** FOR TESTING ***  set to 1 (was 50)
unsigned int32 led_strip_colors[LED_STRIP_LEN];

#define ws2811_zero 0b10000000
#define ws2811_one 0b11110000

void send_frame() {
  unsigned int16 i;
  unsigned int bit;
  unsigned int32 this_led;

  for(i=0;i<LED_STRIP_LEN;i++) {
    this_led = led_strip_colors[i];
    for(bit=23; bit!=255; bit--) {
      if(bit_test(this_led, bit) == 1) {
       // spi_write(ws2811_one);
        putc('1');  // *** TESTING ***
      }
      else {
        //spi_write(ws2811_zero);
       putc('0');   // *** TESTING ***
      }
    }
  } 
  delay_us(50); // latch

 putc('\r');  // *** FOR TESTING ***
}

void single_color(unsigned int32 color1) {
  unsigned long i;
  for(i=0; i<LED_STRIP_LEN; i++) {
    led_strip_colors[i] = color1;
  }
}

void main(void) {

  unsigned int16 i;
  unsigned int j;

  // Commented out the next two lines *** FOR TESTING ***
  //setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16 );
  //delay_ms(250);
 
  single_color(0xFFFFFF);
  send_frame();
  while(1);  // *** TESTING ***

  delay_ms(5000);
  single_color(0xFFC58F);
  send_frame();
  delay_ms(5000);
  single_color(0xFF9329);
  send_frame();
  delay_ms(5000);
  single_color(0xFF0000);
  send_frame();
  delay_ms(1000);
  single_color(0x00FF00);
  send_frame();
  delay_ms(1000);
  single_color(0x0000FF);
  send_frame();
//  sleep;
}
thefloyd



Joined: 02 Sep 2009
Posts: 46

View user's profile Send private message

PostPosted: Mon Sep 17, 2012 3:33 pm     Reply with quote

Sometimes it takes someone else to remind you what you already know :D I've used putc/printf/etc to debug programs that deal with text. Silly me not to think of it in this case. I put a USB to rs232 converter on my breadboard and found some interesting things.

1) my loop seems to work fine. Tested it with several combinations of data and saw exactly what I expected. the bit!=255 part seems to stop the loop right where it should. I got 1s and 0s exactly where I was expecting them and exactly as many of them as I was expecting.
2) There's something wrong with my hardware or setup (or compiler?), and the loop timing may be throwing timing off enough to break things, see below.
3) My PIC can't be oscillating at 64mhz. Despite the use of:
Code:

#Fuses INTRC_IO,PLLEN,PRIMARY_ON,NOFCMEN,NOIESO,PUT,

and

#use delay( clock=64000000,INTERNAL )

The RS232 data was useless. Off to debugging land I went, I tried changing the use delay line to "clock=8000000, INTERNAL" and viola! My RS232 data made sense.. Note I had forgotten to change the fuse back to NOPLLEN yet this worked. huh?

So now the next question is why the hell isn't my PIC running at 64mhz? I have #Fuses PLLEN and a proper #use delay setting. Is this the bug I'm really chasing?

Am I missing something in the setup?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Sep 17, 2012 3:55 pm     Reply with quote

This program blinks an LED at a 1 Hz rate with vs. 4.134. I tested it in
hardware with an 18F45K22, but it should also work with an 18F46K22.
Code:

#include <18F45K22.h>
#fuses INTRC_IO,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=64M)

//======================================
void main(void)
{

// Blink the LED once per second.  The 10/90 duty cycle
// makes it easier to time the flashes with a stopwatch.
while(1)
  {
   output_high(PIN_B0);
   delay_ms(100);
   output_low(PIN_B0);
   delay_ms(900);
  }

}
thefloyd



Joined: 02 Sep 2009
Posts: 46

View user's profile Send private message

PostPosted: Sat Sep 29, 2012 2:50 pm     Reply with quote

So after taking a break, head scratching, debugging, and taking another break I got to take a better look on a scope.

For some reason, it seems trying to execute the for() loop is severely retarding the timing. Enough that there's at least a 50us delay, and that's causing the light string to latch and move on with life. I really didn't think that'd be possible (at 64 mhz!), but I can definitely confirm the data is coming much more slowly with long(ish) pauses when the loop is executed versus doing the bit test sequentially and banging the bits out.

Am I being unrealistic with my timing demands here? Or is there something else I'm missing? Or is there something else possibly going on? I know there are people doing this on a 16mhz arduino (with some careful optimization, I get that.. but 64mhz should allow me to be a bit more "lazy" no?)
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Sep 29, 2012 3:13 pm     Reply with quote

Quote:
there's at least a 50us delay, and that's causing the light string to latch

Yes there is, and you put it in there. See the line in bold in your code below:
Quote:

void send_frame() {
unsigned int16 i;
unsigned int bit;
unsigned int32 this_led;

for(i=0;i<LED_STRIP_LEN;i++) {
this_led = led_strip_colors[i];
for(bit=23;bit!=255;bit--) {
if(bit_test(this_led, bit) == 1) {
spi_write(ws2811_one);
}
else {
spi_write(ws2811_zero);
}
}
}
delay_us(50); // latch
}
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Sat Sep 29, 2012 3:46 pm     Reply with quote

style: bit_test is True or false Boolean purity ;-))
Code:


if(bit_test(this_led, bit) == 1)

// VS

if(bit_test(this_led, bit))



can't help but notice that
thefloyd



Joined: 02 Sep 2009
Posts: 46

View user's profile Send private message

PostPosted: Sun Sep 30, 2012 9:47 am     Reply with quote

PCM programmer wrote:
Quote:
there's at least a 50us delay, and that's causing the light string to latch

Yes there is, and you put it in there. See the line in bold in your code below:
Quote:

void send_frame() {
unsigned int16 i;
unsigned int bit;
unsigned int32 this_led;

for(i=0;i<LED_STRIP_LEN;i++) {
this_led = led_strip_colors[i];
Code:
    for(bit=23;bit!=255;bit--) {
      if(bit_test(this_led, bit) == 1) {
        spi_write(ws2811_one);
      }
      else {
        spi_write(ws2811_zero);
      }

}
}
delay_us(50); // latch
}


Yes. The latch is there but ONLY after iterating through the loop. What I'm saying is that WITHIN the loop, I'm finding at least a 50us delay.

this works:

Code:

void send_frame() {
  unsigned int16 i;
  unsigned int32 this_led;
 
  for(i=0;i<LED_STRIP_LEN;i++) {
    this_led = led_strip_colors[i];

    if(bit_test(this_led, 23) == 1) {
      spi_write(ws2811_one);
    }
    else {
      spi_write(ws2811_zero);
    }
    if(bit_test(this_led, 22) == 1) {
      spi_write(ws2811_one);
    }
    else {
      spi_write(ws2811_zero);
    }
...
    if(bit_test(this_led, 1) == 1) {
      spi_write(ws2811_one);
    }
    else {
      spi_write(ws2811_zero);
    }
    if(bit_test(this_led, 0) == 1) {
      spi_write(ws2811_one);
    }
    else {
      spi_write(ws2811_zero);
    }
  }
  delay_us(50);
}


THIS doesn't:
Code:

void send_frame() {
  unsigned int16 i;
  unsigned int bit;
  unsigned int32 this_led;
 
  for(i=0;i<LED_STRIP_LEN;i++) {
    this_led = led_strip_colors[i];
    for(bit=23;bit!=255;bit--) {
      if(bit_test(this_led, bit) == 1) {
        spi_write(ws2811_one);
      }
      else {
        spi_write(ws2811_zero);
      }
    }
  }
  delay_us(50);
}

I'm seeing delays of > 50us INSIDE the for(bit=23;bit!=255;bit--) loop. Debugging prints the right number of 1s and 0s to the screen. But when executed on the actual hardware, only the first LED does things (flickers, does weird stuff). Probing with a scope and the timing inside the loop is all broken, but doing each of the 24 bit tests by hand manages to avoid the 50us+ pauses. Why?
Ttelmah



Joined: 11 Mar 2010
Posts: 19514

View user's profile Send private message

PostPosted: Sun Sep 30, 2012 10:27 am     Reply with quote

You don't show your SPI configuration code.
I'd say it is using soft SPI at perhaps about 200KHz.
This will then take 48uSec to send the bytes.

Best Wishes
thefloyd



Joined: 02 Sep 2009
Posts: 46

View user's profile Send private message

PostPosted: Sun Sep 30, 2012 10:47 am     Reply with quote

Ttelmah wrote:
You don't show your SPI configuration code.
I'd say it is using soft SPI at perhaps about 200KHz.
This will then take 48uSec to send the bytes.

Best Wishes


Sorry, the setup_spi command is earlier up in the thread. This is a PIC @ 64mhz with the following:
Code:

setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16 );
Ttelmah



Joined: 11 Mar 2010
Posts: 19514

View user's profile Send private message

PostPosted: Sun Sep 30, 2012 11:20 am     Reply with quote

However it'd be a _lot_ faster, to generate your own bit mask, load this with 2^23, and just rotate this once each time round the loop, and 'and' this with the value.

Problem is that 'bit test' with a constant generates a single machine instruction. With a variable, it generates a '1', then rotates this by the specified number, and 'and's this to find if the bit is true/false.

So your test generates an average of 13 4byte rotations each time round the loop. Add the loop count and the and operation, and you have something like perhaps 200 instructions, so I can see it taking perhaps 15+uSec.

Using your own mask instead, which only has to rotate once each time round the loop will save a lot of time.

Best Wishes
Ttelmah



Joined: 11 Mar 2010
Posts: 19514

View user's profile Send private message

PostPosted: Sun Sep 30, 2012 12:09 pm     Reply with quote

As a comment, the fastest way to do this is probably:

Code:

     for (bit=0;bit<24 bit++) {
         if(bit_test(this_led, 23) == 1) {
            spi_write(ws2811_one);
         }
          else {
            spi_write(ws2811_zero);
         }
     this_ked*=2;
    }


This way you test the single _fixed_ bit (fast) and just rotate your stored number once each loop. I'd guess at least a dozen times faster.

Best Wishes
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group