|
|
View previous topic :: View next topic |
Author |
Message |
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Sun Sep 30, 2012 4:28 pm |
|
|
It's not necessarily how fast I can clock the bits out (though that does come into play), but a 50us low is considered a latch.
Basically I'm working with a string of LEDs driven by WS2811 ICs. these basically take 24 bits of data (8 bit red, green, blue), shift it in, and cascade the next 24 bits over to the next chip (and so on and so on down the line). Once you hold the data line low for 50+us, the ICs along the string all latch the data and display the incoming data.
What I'm seeing in my for() loop SEEMS to be 50us+ long low periods, causing the first LED to latch and the rest of the data never making it down the string (currently 100 LEDs long). When I do the bit tests individually all typed out, the data makes it all the way down the string and the expected result (LEDs do what I'm asking them to) happens. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Oct 01, 2012 3:00 am |
|
|
Try with the faster code.
Are you _sure_ your chip really is clocking at 64MHz?.
Historically, on some compiler versions, the settings as you have them, don't correctly enable the PLL. You have to have the setup_oscillator command to make it work.
I'd guess you are actually running at 16MHz, then the long time needed for the bit_test using a variable, _would_ take about 50+uSec, and 'voila' you have your problem.
Much faster though to do the single rotation as shown.
It takes about 750uSec to send the 24bits at 16Mhz using the existing code.
Average of 32uSec, but the earliest bits are the slowest, taking 52.5uSec for the first bit, 51.25 for the second, etc..
Using my version (two typing errors, but they are obvious), it takes just 4.5uSec/bit at the same clock rate, and the time between bits is constant.
Best Wishes |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Mon Oct 01, 2012 4:23 pm |
|
|
Ttelmah wrote: | Try with the faster code.
Are you _sure_ your chip really is clocking at 64MHz?.
Historically, on some compiler versions, the settings as you have them, don't correctly enable the PLL. You have to have the setup_oscillator command to make it work.
I'd guess you are actually running at 16MHz, then the long time needed for the bit_test using a variable, _would_ take about 50+uSec, and 'voila' you have your problem.
Much faster though to do the single rotation as shown.
It takes about 750uSec to send the 24bits at 16Mhz using the existing code.
Average of 32uSec, but the earliest bits are the slowest, taking 52.5uSec for the first bit, 51.25 for the second, etc..
Using my version (two typing errors, but they are obvious), it takes just 4.5uSec/bit at the same clock rate, and the time between bits is constant.
Best Wishes |
I'm pretty sure about 64mhz. I DID have issues using the internal osc and asking it to run at 64Mhz. While it happily acted like it was running at 64mhz when I tried using RS232 all I got was garbage. When I changed the clock down to 16mhz in that setup RS232 was fine. My current setup is using an external 16mhz crystal and PLL enabled. I've also used RS232 to debug in this setup and things are working as expected - so I don't yet have a reason to believe I'm _not_ at 64mhz this time.
I will try with your faster code tonight. Appreciate the insight, hopefully it'll get me to where I need to be. As a casual hobbyist bit operations have always been my weak point (and'ing/or'ing/shifting/etc are hard to grasp, dunno why). |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Mon Oct 01, 2012 8:47 pm |
|
|
Ttelmah wrote: | Try with the faster code.
Are you _sure_ your chip really is clocking at 64MHz?.
Historically, on some compiler versions, the settings as you have them, don't correctly enable the PLL. You have to have the setup_oscillator command to make it work.
I'd guess you are actually running at 16MHz, then the long time needed for the bit_test using a variable, _would_ take about 50+uSec, and 'voila' you have your problem.
Much faster though to do the single rotation as shown.
It takes about 750uSec to send the 24bits at 16Mhz using the existing code.
Average of 32uSec, but the earliest bits are the slowest, taking 52.5uSec for the first bit, 51.25 for the second, etc..
Using my version (two typing errors, but they are obvious), it takes just 4.5uSec/bit at the same clock rate, and the time between bits is constant.
Best Wishes |
Amazing. Your code works _beautifully_. I get the basics behind what it's doing.. but I don't 'get it'. Time to start reading more.. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Oct 02, 2012 12:48 am |
|
|
The key is understanding how bit_test works.
If used with a constant value, it works out in advance what byte is involved, and used the processor bit test instruction. Singe machine cycle.
With a variable though there is a problem. What it does is starts with a 'temporary' variable matching the size of the object you want to test. So in your case an int32. It then loads this with '1', and rotates this the number of times needed to get to the bit needed. So effectively in your case, it codes as:
Code: |
int1 test_bit(int32 val, int8 num){
int32 mask=1;
int8 ctr;
for (ctr=0;ctr<num;ctr++) {
mask*=2;
}
return ((val & mask)!=0);
}
|
So in your first case, with '23' as the bit number, it has to loop 23 times rotating the four byte variable. In all, it performs 23,22,21,.....2,1,0 rotations. A total of 276 4byte rotations, and 24 4byte AND operations!.....
Now my alternative hits it from the other end. You have your 24bit number stored in a variable, whose contents can be destroyed by the output. The first time round you want to send bit 23, the next time bit 22 etc...
So I code the first output, as a constant bit test (testing bit 23). Fast, gets rid of the need to perform the 4byte 'AND'.
Then I just rotate the variable _once_. What was bit 22, is now in bit 23, so the same constant test works. Each time round the loop, I just rotate once, and test. The counter becomes a simple 24* counter (no need for it to match the bit number involved), and each loop then has a single 4byte rotation, a bit test, and the increment/test of the counter. A total of just 23*4 rotations. Also, because there is no counting to the bit number, the operation takes constant time. About 5* faster on average, and nearly ten time faster on the 'worst case' bits.
Perhaps the big lesson is to 'beware' of bit test using a variable, especially with a large number like this. It can take a lot of processor work.....
However I still have to suggest you go back and triple check the processor is getting the 64MHz, since at this speed, the bit_test version should not cause the timing you are describing - I have stop-watched it, and at 64MHz, the longest bit time should still be only about 13uSec. It only gets to the timing you are reporting, and would give a problem, if your clock is not 64MHz....
Best Wishes |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Tue Oct 02, 2012 8:04 am |
|
|
Thanks for taking the time out to explain this to me in a little more detail, it certainly clarifies things. I know my strong and weak points and code optimization isn't one of my stronger points (though I'm learning :D).. I figured I'd just throw more power at the problem as that wasn't an issue in this particular case.. funny how that doesn't always seem to solve the problem
I will double check tonight to see if I can be certain the PIC is operating at 64mhz. Quick thought - you say the timings I'd be seeing are in line with a device operating at 16mhz - isn't the internal PIC clock OSC/4? So even if I was at 64mhz my PIC would be executing instructions at 16mhz.. is that the missing link here?
Also, if you don't mind schooling me a bit more (or pointing me in the direction of a good read) .. how does this_led*=2 end up rotating the bits such that I'm shifting my next lower bit into the 23rd spot repeatedly? (And I'm assuming I'm getting zeros in their empty spaces as they shift? Not that it matters.. just curious).
If my data were 8 bit and I was testing the 8th bit, would it look like this?
10111011 <- starting data
01110110 <- first iteration
11101100 <- second iteration
11011000 ..
10110000 ..
01100000 ..
.. etc as I worked my way through the loop?
Or does the arithmetic "wrap around" such that the 8th bit would now be the 1st bit?
thanks again for your help. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Oct 02, 2012 8:17 am |
|
|
No, I am allowing for that. 64MHz = 16MIPS.
I stop watched the function in MPLAB, to get the actual times involved.
Yes, you can use <<=1 to move the bits round or *=2. Both basically do the same. The compiler is smart enough to realise it can multiply by two, by simply shifting the number. Bits at the bottom, are filled with zeros as you shift, so are as you show.
You could also use the CCS 'rotate_left' instruction, which then _does_ wrap the bit round, but with your real value, you'd have to rotate another 8 times to get back to the starting number, which would take longer than just reloading it.
Best Wishes |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Tue Oct 02, 2012 8:50 am |
|
|
Ttelmah wrote: | No, I am allowing for that. 64MHz = 16MIPS.
I stop watched the function in MPLAB, to get the actual times involved.
Yes, you can use <<=1 to move the bits round or *=2. Both basically do the same. The compiler is smart enough to realise it can multiply by two, by simply shifting the number. Bits at the bottom, are filled with zeros as you shift, so are as you show.
You could also use the CCS 'rotate_left' instruction, which then _does_ wrap the bit round, but with your real value, you'd have to rotate another 8 times to get back to the starting number, which would take longer than just reloading it.
Best Wishes |
Thanks. I just had an 'A ha!' moment.
While the compiler might have been smart enough to realize it can multiply by two by simply shifting the bits, *I* wasn't. Though it makes perfect sense, I never thought of it that way.
I have a lot to learn..
When I get home tonight I'll see what I can figure out with regards to the OSC. As I said earlier, I did have a problem trying to get the internal osc going at 64mhz (and it was obvious, despite the proper #use delay, RS232 was garbage until I changed the delay to 16000000) I quit trying to debug that because it was just easier to sub in a 16mhz resonator I had at my desk - and with a #use delay of 64000000 RS232 is working properly. I'll try and time an LED blink but I'd be surprised if I wasn't running at the 64mhz this time. My current oscillator fuses are:
#FUSES HSH
#FUSES PLLEN
#use delay(clock=64000000)
With a 16mhz ceramic resonator hooked up to the OSC pins. |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Sun Oct 07, 2012 8:44 am |
|
|
I can definitely say with reasonable certainty that this setup is running at 64mhz:
- RS232 works as expected
- Blinking an LED shows the proper amount of delay given the #use delay setting.
So.. I'm still perplexed. Glad your code got me past my problem, but still a bit unsettled since I don't exactly know what the problem is (since by all indications my code should have worked at 64mhz even with its inefficiencies..) |
|
|
|
|
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
|