Author Topic: pic 24 code to output an int to a pin  (Read 4065 times)

0 Members and 1 Guest are viewing this topic.

Offline snarkysparkyTopic starter

  • Frequent Contributor
  • **
  • Posts: 419
  • Country: us
pic 24 code to output an int to a pin
« on: March 02, 2017, 02:41:20 am »
The goal is to create a function that will output a 16 bit integer to a digital pin for debugging with an oscilloscope.

For a pic24.

Code: [Select]

void int2scope( unsigned int testval)
{
    unsigned int bitpnt = 1<<15;
    unsigned int seq[14];
    unsigned int LATBShadow;
    unsigned int index = 0;

   
    LATBShadow = LATB;   //  store current state
   
    seq[index] = LATBShadow | ScopeBitMask;  // start bit
    do
    {
        index++;
        if(bitpnt&testval)seq[index] = LATBShadow | ScopeBitMask;
        else
           seq[index] = LATBShadow & ~ScopeBitMask;
   
        bitpnt >>=1;
       
    }while(bitpnt);
    index++;
    seq[index] = LATBShadow | ScopeBitMask;  // stop bit

    LATB = seq[0];
    LATB = seq[1];
    LATB = seq[2];
    LATB = seq[3];
    LATB = seq[4];
    LATB = seq[5];
    LATB = seq[6];
    LATB = seq[7];
    LATB = seq[8];
    LATB = seq[9];
    LATB = seq[10];
    LATB = seq[11];
    LATB = seq[12];
    LATB = seq[13];

}




Any suggestions for improvement ??

Thanks
 

Offline helius

  • Super Contributor
  • ***
  • Posts: 3683
  • Country: us
Re: pic 24 code to output an int to a pin
« Reply #1 on: March 02, 2017, 03:01:45 am »
You only output 12 bits, not 16.
It looks like you are trying to set the output bits as fast as possible, so you hard-coded them instead of doing it in a loop; but just reading from that array is likely to take even more time, unless the compiler can overlap it with writes to LATB.
Generally this kind of problem requires that you understand what the compiler can and can't do. For instance, if the MCU has instructions that can write a single bit into an IO reg then all the precomputation of shadow values etc is pointless.
 

Offline snarkysparkyTopic starter

  • Frequent Contributor
  • **
  • Posts: 419
  • Country: us
Re: pic 24 code to output an int to a pin
« Reply #2 on: March 02, 2017, 03:49:57 am »
Well my main goal was to insure that both conditions ( true or not ) took the exact same cycles so that it was easy to decode the data using the cursors on my scope.   Yes I did only mean for this to be used for 12 bits.  It's to debug A/D converter inputs.  But easily enough expandable to the full 16 bits.

I coded it this way so that the on and off times would be exactly one cycle.  I don't have to worry about testing and branching effects on the cycle time.  It's important to be able to read the values off a scope capture.

Shouldn't the compiler be able to hard code the addresses assigned to the array and then call them for setting the LATB words.

From my perspective the compiler should put the array on the stack and then pop these values off with single cycle instructions and write to LATB.   But then I don't know much about this stuff.
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 8178
  • Country: ca
    • LinkedIn
Re: pic 24 code to output an int to a pin
« Reply #3 on: March 02, 2017, 06:11:50 am »
Now, this can be written a bit better, but, moving bits out in 1 clock cycle in 'C' I would think is impossible since I can't think of a way to do so in assembly.  Maybe 2 cycles, prestoring 12 varibles, using a movf, then a movwf line by line.

Now, I would sacrifice one additional cycle just so that I only affect 1 pin on port B and don't have to generate the 12 or 16 bytes ahead of time.  To do this, I would only use #asm, btfsc and bcf/bsf to your IO pin in your C code, you'll get 3 instructions per bit coming out of the pin and a reference low output gap between consecutive high bits so you can see the spacing on you scope.  Here is how I would go about this:  Now, when I say comes out fast, I mean with a 40Mhz pic, this will come out at 3.3333 megabits/sec + my fat setup pulse.  You need not worry about the C compiler altering the timing ever & since I only bit-check a 2 byte integer low & high memory points directly.  It's been awhile since I've added assembly in line into C routines with MPLab, so I hope I got everything OK.

(Note, you need to have set testval_h & testval_l at the beginning of you C code, they can be ram or directly pointing to your ADC registers)
Code: [Select]
#asm
     bsf     PORTB,0  ; I'm using B0 in this test
     bsf     PORTB,1  ; Optional trigger for scope on a second channel
     bcf     PORTB,1
     bcf     PORTB,0  ; Initial 4 cpu cycle high for scope to trigger, using the scope to only trigger on x ns high.

     btfsc     testval_h,3
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_h,2
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_h,1
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_h,0
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_l,7
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_l,6
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_l,5
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_l,4
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_l,3
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_l,2
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_l,1
     bsf        PORTB,0
     bcf        PORTB,0

     btfsc     testval_l,0
     bsf        PORTB,0
     bcf        PORTB,0

#endasm

Using the equivalent movf seq(x),0  , then movwf LATB, will give you a 2 CPU cycle per bit, but, all of port B will be affected and you need to prep and store all of the seq(x) which would take a mound of many more CPU cycles ahead of time.  You can also add a NOP inbetween my previous bsf & bcf to make everything an even 4 cpu cycles & fatter high bits.  You can also change those NOPs to an alternating bsf PORTB,1 and bcf PORTB,1 to generate a high and low latch clock for a second channel on you scope.

The nature of my code which makes the fat header & a blank 0 between bits makes the output easy to decode visually on a single trace of your oscilloscope display.
« Last Edit: March 02, 2017, 06:13:26 am by BrianHG »
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 8178
  • Country: ca
    • LinkedIn
Re: pic 24 code to output an int to a pin
« Reply #4 on: March 02, 2017, 06:22:21 am »
Another example with 4 cpu cycles per byte + a true data latch clock on PORTB,1.

Code: [Select]
#asm

     movlw   b'11111100'

     btfsc     testval_h,3
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_h,2
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_h,1
     bsf        PORTB,0
      bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_h,0
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_l,7
     bsf        PORTB,0
      bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_l,6
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_l,5
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_l,4
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_l,3
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_l,2
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_l,1
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

     btfsc     testval_l,0
     bsf        PORTB,0
     bsf        PORTB,1  ; Trigger and clock pulse on PORTB,1
     andwf    PORTB,1  ; Clear PORTB bits 0&1

#endasm
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3557
  • Country: it
Re: pic 24 code to output an int to a pin
« Reply #5 on: March 02, 2017, 06:36:38 am »
this would be the perfect use case for UART, which you are not using because..?

otherwise i like the code on reply #3. (is the asm correct? wouldn't it be bset, bclr and btsc?)
 
The following users thanked this post: julianhigginson

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 8178
  • Country: ca
    • LinkedIn
Re: pic 24 code to output an int to a pin
« Reply #6 on: March 02, 2017, 09:15:02 am »
Woops, 'PIC24' has a 16 bit core, I was using the 8 bit core opcode.  Yes, you need to use the enhanced 16bit opcodes, now, you would only need to point to a 16 bit int memory space instead of the high and low byte bit tests.  In fact, if you are using a 16 bit port, all you need to do is move your 16 bit word to that port, then in assembly, do 12 rotates to the right right on the latch or port, then you will get at pin bit 0, 1 bit per cpu clock.  This would be the only way to get 10 megabits/sec from a 40Mhz cpu.  You can only do 8 bits at a time like this with the 8 bit PICs.  There would be a 2 cycle hump when switching from the high to the low byte in that case.  Note that all the IO pins would sequence like mad doing it like this.

Since I am not aware of the behavior of the 'latchB' register, ''maybe'', if you port b on the chip has only a few bits, but the latch register is complete 16 bits, loading your 16 bit data into the latch and rotating the latch 12 time ''might'' still actually work.  this is something you would have to test.  You might also consider changing the unused bits of port B to inputs since the latch register shouldn't be affected by the input data.

In fact, before loading the latch for the port, I would rotate the data to the left by 1, fill the end with 0 and bit 14 with 0, then feed that int to the port and do 14 right rotations on the latch.  This way your output will start with a low and end with a low.
« Last Edit: March 02, 2017, 09:30:38 am by BrianHG »
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3557
  • Country: it
Re: pic 24 code to output an int to a pin
« Reply #7 on: March 02, 2017, 12:12:04 pm »
Since I am not aware of the behavior of the 'latchB' register, ''maybe'', if you port b on the chip has only a few bits, but the latch register is complete 16 bits, loading your 16 bit data into the latch and rotating the latch 12 time ''might'' still actually work.  this is something you would have to test.  You might also consider changing the unused bits of port B to inputs since the latch register shouldn't be affected by the input data.

from what i remember the LAT register is not bit complete. each unimplemented bit is actually zero (i did a simillar thing a long time ago)
the fastest way to shift out a word is your code with the appropriate opcodes :)
if one wanted a fixed bit rate it should look like
Code: [Select]
BTSC  WREG,#0
BSET  LATB,#0
BTSS  WREG,#0
BCLR  LATB,#0
for each bit
suggestion: first load the word into one accumulator. some instructions are not available in GP ram and certain families of PIC24 need two clock cycles each time they have to access GP RAM.. those also need more than 2 cycles to excecute a branch... check the programmer's manual!
« Last Edit: March 02, 2017, 12:13:43 pm by JPortici »
 

Offline snarkysparkyTopic starter

  • Frequent Contributor
  • **
  • Posts: 419
  • Country: us
Re: pic 24 code to output an int to a pin
« Reply #8 on: March 02, 2017, 12:56:31 pm »
     btfsc     testval_h,0
     bsf        PORTB,0
     bcf        PORTB,0


doesn't this always clear B0,  if the branch is taken or not. 

I would use the UART but I didn't want to have to juggle combining two bytes in my head while debugging.
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 855
Re: pic 24 code to output an int to a pin
« Reply #9 on: March 02, 2017, 04:53:58 pm »
This is untested and could be wrong, but how about something like so-

void int2scope( uint16_t val)
{
    for( int8_t i = 11; i>=0; i--){ //only 12 bits (11-0)
      uint8_t v = val>>i; //get desired bit into bit0 of v
      _LATB1 = 1; //1 1   - 'start' bit
      _LATB1 = v; //1 0   - data
      _LATB1 = v; //1 0
      _LATB1 = 0; //0 0  - always leave pin as 0
      if(!(i & 3)){ NOP; NOP; NOP; NOP; } //create gap between nibbles
    }
}


A 1000... is a 0 (short length high), a 1110... is a 1 (long length high) , with a 'space' between each nibble to make it more readable. Should be able to mentally convert the nibbles to hex somewhat easily. I would imagine if using the original idea it would get difficult when there are lots of consecutive zeros or ones. With something like this, the timing really doesn't matter as you are just looking for short/long 1's.

Just a thought.
« Last Edit: March 02, 2017, 07:57:02 pm by cv007 »
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 8178
  • Country: ca
    • LinkedIn
Re: pic 24 code to output an int to a pin
« Reply #10 on: March 03, 2017, 01:11:55 am »
     btfsc     testval_h,0
     bsf        PORTB,0
     bcf        PORTB,0


doesn't this always clear B0,  if the branch is taken or not. 

I would use the UART but I didn't want to have to juggle combining two bytes in my head while debugging.

Yes it does.  Remember, according to the initial post, you want to visually see and capture the data on 1 channel of the scope as fast as possible, not create a valid com signal.  The forced clearing of port b,0 makes a nice set of evenly spaced high pulses for the 1s, which you can visually evaluate the 12 bit chain of data.

The uart doesn't support 12 or 16 consecutive bits without start and stop bits.

The newer post above with the bit-test for the setting and clearing, where the data is set then might be cleared with a skew makes it visually more difficult to decode the bits.

I've used this with a simple scopes & then thin high pulses make visualizing the individual bit easy to read by eye as I can place the pulses right on the horizontal grid spacing even if your CPU clock doesn't fit on a particular grid spacing.  If you want fatter high pulses, just add a NOP between the bsf & bcf.  You can also change this to a bit width modulation like this:

........
bsf   PORTB,0
btfss  integer,bit#
bcf  PORTB,0
nop
bcf  PORTB,0
nop
........

Ok, this one is 6 cpu clocks per bit, but, always get an initial high per bit.  But now, the width of that high is fat for a 1, thin for a 0.  This is also useful for the 1 bit bus communications of the 1 wire full RGB color LED com port with slower CPU speeds.

« Last Edit: March 03, 2017, 01:44:31 am by BrianHG »
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3557
  • Country: it
Re: pic 24 code to output an int to a pin
« Reply #11 on: March 03, 2017, 07:05:12 am »
I would use the UART but I didn't want to have to juggle combining two bytes in my head while debugging.

with realterm and probably other terminal emulators you can decide how to interpret the acquired data. there is also an uint16_t options which will combine the two bytes for you
 

Offline snarkysparkyTopic starter

  • Frequent Contributor
  • **
  • Posts: 419
  • Country: us
Re: pic 24 code to output an int to a pin
« Reply #12 on: March 03, 2017, 01:32:39 pm »
bsf   PORTB,0
btfss  integer,bit#
bcf  PORTB,0
nop
bcf  PORTB,0
nop

I like it..  I was thinking of changing to fat and thin pulses already to make it more readable.  I think I will try to trick my C compiler ( CCS ) into emitting this code.  It has intrinsic bit_test()  and bit_set()

thanks
 

Offline @rt

  • Super Contributor
  • ***
  • Posts: 1076
Re: pic 24 code to output an int to a pin
« Reply #13 on: March 03, 2017, 01:48:19 pm »
If consistency was the issue rather than speed, was there a reason not to bang it out?

Code: [Select]
int outword = something;
count = 0;
while (count < 12) {
LATB.0 = outword;
outword = outword >> 1;
count++;
} // count

or if you wanted the bitmask

Code: [Select]
int outword = something;
count = 0;
while (count < 12) {
LATB.0 = outword & 0x0001;
outword = outword >> 1;
count++;
} // count
 

Offline snarkysparkyTopic starter

  • Frequent Contributor
  • **
  • Posts: 419
  • Country: us
Re: pic 24 code to output an int to a pin
« Reply #14 on: March 03, 2017, 02:03:48 pm »
LATB.0 = outword;

Just asking.. Isn't this assigning an integer to a bit value,  how does that work.

Code: [Select]

void int2scope2(unsigned int testval)
{
   bit_set(LATB,ScopeBit);
   if(!bit_test(testval,15))bit_clear(LATB,ScopeBit);
   bit_clear(LATB,ScopeBit);
   delay_cycles(1);

.
.
.
.


the compiler emitted this

.................... void int2scope2(unsigned int testval)
.................... {
....................    bit_set(LATB,ScopeBit);
0218:  BSET.B  2CD.0
....................    if(!bit_test(testval,15))bit_clear(LATB,ScopeBit);
021A:  BTSC.B  819.7
021C:  BRA     220
021E:  BCLR.B  2CD.0
....................    bit_clear(LATB,ScopeBit);
0220:  BCLR.B  2CD.0
....................    delay_cycles(1);
0222:  NOP     

Which looks like 5 6 cycles either way. 
« Last Edit: March 03, 2017, 02:41:30 pm by snarkysparky »
 

Offline Buriedcode

  • Super Contributor
  • ***
  • Posts: 1701
  • Country: gb
Re: pic 24 code to output an int to a pin
« Reply #15 on: March 03, 2017, 03:01:27 pm »
I've seen UART mentioned, but... what about SPI?  The PIC24's SPI module has a 16-bit mode.  This will spit out 16 bits/an int happily.  The clock will be helpful too because viewing serial data on an oscilloscope is tedious when you have long runs of 1's or 0's.

I'm probably missing something here, but seems like an awful lot of replies for someone who just wants to output a16-bit value out a pin...
 

Offline @rt

  • Super Contributor
  • ***
  • Posts: 1076
Re: pic 24 code to output an int to a pin
« Reply #16 on: March 03, 2017, 04:36:21 pm »
LS bit of the int doesn’t write to the port pin?
It should be ok then to compare two integers:
Code: [Select]
int mask;
int sendint;

OUT_LAT = 0;
for (mask = 0x01; mask; mask <<= 1) {
if (sendint & mask) {OUT_LAT = 1;} else {OUT_LAT = 0;}
}

or are you saying it takes longer to check one bit state than the other?
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 8178
  • Country: ca
    • LinkedIn
Re: pic 24 code to output an int to a pin
« Reply #17 on: March 04, 2017, 03:45:24 am »
I've seen UART mentioned, but... what about SPI?  The PIC24's SPI module has a 16-bit mode.  This will spit out 16 bits/an int happily.  The clock will be helpful too because viewing serial data on an oscilloscope is tedious when you have long runs of 1's or 0's.

I'm probably missing something here, but seems like an awful lot of replies for someone who just wants to output a16-bit value out a pin...

The nice thing about snarkysparky using the intrinsic C code bit-tests and bit-set/clear is that he can use ANY free IO pin from ANY pic to probe from.  I believe the #asm & #endasm in the middle of your C code should also still work if you want to force things to always be dead on.

Remember, this appears to be for testing, not as a main function.
« Last Edit: March 04, 2017, 10:08:01 am by BrianHG »
 

Offline Lunasix

  • Regular Contributor
  • *
  • Posts: 142
  • Country: fr
Re: pic 24 code to output an int to a pin
« Reply #18 on: March 04, 2017, 02:11:24 pm »
You can use SPI. If you want to send more bits than possible with the peripheral maximum size (some has fifo), simply use DMA, which will then send all words for you. So, you can send 1kbits if you want, with few line code and in one time.
 

Offline Buriedcode

  • Super Contributor
  • ***
  • Posts: 1701
  • Country: gb
Re: pic 24 code to output an int to a pin
« Reply #19 on: March 04, 2017, 02:40:57 pm »
The nice thing about snarkysparky using the intrinsic C code bit-tests and bit-set/clear is that he can use ANY free IO pin from ANY pic to probe from.  I believe the #asm & #endasm in the middle of your C code should also still work if you want to force things to always be dead on.

Remember, this appears to be for testing, not as a main function.

I appreciate that using a generic routine, rather than relying on specific hardware, is preferable, and it really is quite simple to spit out bits serially... I suggested the SPI because as far as I am aware, all PIC24's have an SPI peripheral, and most have peripheral pin select, allowing the SPI MOSI to be on (almost) any pin. It can also run very quickly, or can be slowed down easily.  Perhaps the biggest advantage is that it can run in the background. 

I noticed the OP mention outputting PPM, or PWM to indicate a bit 0, or bit 1 for debugging - similar to cheap RF and IR remote's.  That is quite a good idea for 'eyeballing' serial data, it is remarkably easier to read since every pulse is a bit, and its width determines its value.

I'm still unsure of the end-application of this.  If its just debugging, then surely terminal would be better than looking at values on a scope?  A fast UART with DMA with set 'sentences' and debug memory locations uses minimal CPU, and is human-readable on any terminal.  It uses one pin - and with PPS, that can be any IO - and generally makes debugging a breeze.  If this was for a PIC10F202 or an Attiny I might understand the whole bit-banging thing but for a peripheral-rich device like the PIC24 (be it F, H or E) debugging by 'looking at waveforms on a scope to read out an int' seems like an awkward way of doing it. Again, I must be missing something
 

Offline Lunasix

  • Regular Contributor
  • *
  • Posts: 142
  • Country: fr
Re: pic 24 code to output an int to a pin
« Reply #20 on: March 04, 2017, 02:56:23 pm »
If the goal is to output a readable value, and if there is no need to show this on a scope among other signals, you can send an ascii string, with as many chars as you want, so with readable text, with a small amount of code and and in one time if you use uart with DMA.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf