Jump to content

cheap dout's


bill
 Share

Recommended Posts

Hello,

it must have been covered in the past, but i did not found anything about it.

is there a "hack" or a simple way, using c, to have few digital outputs without using a "dout module" (74HC595), in the same way we can use analog inputs (J5) as digital ones with pull up resitors.

( i now talking about J14, because i'm looking for more than one output )

I want to find the cheapest way to build sync24 outputs (we need 2 lines : clock and start)

-> http://www.midibox.org/dokuwiki/mb_sync24

Thanks !!! =)

Link to comment
Share on other sites

So, Port B is the LCD data bus right ?

can send a byte to PORTB and "read" it thanks to RB0 to RB7 ?

You can use PortB as input when the appr. TRISB flags are set (Tristate driver - means: pin is not driving)

However, the best choice for output pins which are not used by any other resources are RC0, RC1 and RC3.

The best choice for an input pin which is not used by any other resource is RC2 (it already has an pull-up, which is perfect to prevent random values if the pin is open)

By using these pins, you don't need to modify the TRISx configuration, just use (for example)

   PORTCbits.RC0 = 1;

to set the pin to 5V, and

   PORTCbits.RC0 = 0;

to set the pin to 0V

Same for RC1:

   PORTCbits.RC1 = 1; // 5V

   PORTCbits.RC1 = 0; // 0V

And don't touch anything related to the LCD Port B

Hope that this answers your question?

Or do you need even more input/output pins? There are special solutions for J5 and LCD port, but they require additional configuration... and you need to warn the end-users to disconnect the LCD, or disconnect pots (of J5 used as output) to prevent short-circuits.

Best Regards, Thorsten.

Link to comment
Share on other sites

Thank you Thorsten, yes, it helps a lot, but i would like to get... 8 outputs :p

so i guess the only solution is j5 or lcd portB ! (i prefer j5)

please can you tell me where to get some informations about those special solutions ?

thanks =)

edit :


ok, i should use my brain more often... here it is http://ucapps.de/mios/j5_dout_v1_3c.zip

unfortunately, this is ASM, and i did not find any conversion to C

(i have zero ASM knowledge)  :-[

Can someone help me to turn this code into plain C ?

Link to comment
Share on other sites

Once the SVN server is up&running, a modular structure will be available where code modules can be easily shared between projects.

So - once it is available, I will program a relocatable j5_dout module for you, that should be easily integratable.

Best Regards, Thorsten.

Link to comment
Share on other sites

P.S.: so long the module is not available, you can control J5 outputs the following way from C:

Add

  ADCON1 = 0x07;

  TRISA &= 0xd0;

  TRISE &= 0xf8;

to the Init() hook

Now you can toggle the pins with

PORTAbits.RA0 = ...

PORTAbits.RA1 = ...

PORTAbits.RA2 = ...

PORTAbits.RA3 = ...

PORTAbits.RA5 = ...  // no typo! RA4 is allocated by IIC

PORTEbits.RE0 = ...

PORTEbits.RE1 = ...

PORTEbits.RE2 = ...

Please let me know if it works (it's untested)

Best Regards, Thorsten.

Link to comment
Share on other sites

a modular structure will be available where code modules can be easily shared between projects.

So - once it is available, I will program a relocatable j5_dout module for you, that should be easily integratable.

  :o

Wooow !!! I love you Thorsten  ;D

Thanks sooooo much, i test this this evening and revert as soon as it's done.

Link to comment
Share on other sites

  • 3 weeks later...

Here is what i did : 

void J5_DOUT_Set(unsigned char oct){
   char b0 = oct;
   char b1 = oct >> 1;
   char b2 = oct >> 2;
   char b3 = oct >> 3;
   char b4 = oct >> 4;
   char b5 = oct >> 5;
   char b6 = oct >> 6;
   char b7 = oct >> 7;
   PORTAbits.RA0 = b0 & 0x01;
   PORTAbits.RA1 = b1 & 0x01;
   PORTAbits.RA2 = b2 & 0x01;
   PORTAbits.RA3 = b3 & 0x01;
   PORTAbits.RA5 = b4 & 0x01;
   PORTEbits.RE0 = b5 & 0x01;
   PORTEbits.RE1 = b6 & 0x01;
   PORTEbits.RE2 = b7 & 0x01;
}

it works, but please tell me what you think of the code !

(i guess it can be done in a better way)

Link to comment
Share on other sites

No major changes just a little shorter:

void J5_DOUT_Set(unsigned char oct){
  PORTAbits.RA0 = oct & 0x01;
  PORTAbits.RA1 = (oct >> 1) & 0x01;
  PORTAbits.RA2 = (oct >> 2) & 0x01;
  PORTAbits.RA3 = (oct >> 3)  & 0x01;
  PORTAbits.RA5 = (oct >> 4)  & 0x01;
  PORTEbits.RE0 = (oct >> 5)  & 0x01;
  PORTEbits.RE1 = (oct >> 6)  & 0x01;
  PORTEbits.RE2 = (oct >> 7)  & 0x01;
}
[/code]

Link to comment
Share on other sites

try this (off the top of my head):

void J5_DOUT_Set(unsigned char oct)
{
   PORTAbits.RA0 = (oct & 0x01) && 1;  
   PORTAbits.RA1 = (oct & 0x02) && 1; 
   PORTAbits.RA2 = (oct & 0x04) && 1;
   PORTAbits.RA3 = (oct & 0x08) && 1;
   PORTAbits.RA5 = (oct & 0x10) && 1;
   PORTEbits.RE0 = (oct & 0x20) && 1;
   PORTEbits.RE1 = (oct & 0x40) && 1;
   PORTEbits.RE2 = (oct & 0x80) && 1;
}

!= 0 might be better than && 1, have to look at asm output...

(maybe could even do without the && 1 at all)

Link to comment
Share on other sites

Semi-untested and possibly controversial:

__asm 
MOVF _PORTA, _WREG
IORLW 0x1F
MOVF _WREG, _PORTA
MOVF _oct, _WREG
IORLW 0xE0
ANDWF _PORTA, 1

MOVF _PORTE, _WREG
IORLW 0x07
MOVF _WREG, _PORTE
MOVF _oct, _WREG
RRNCF _WREG, 5
IORLW 0xF8
ANDWF _PORTE, 1
__endasm;
[/code] Or sth. pretty close to what ptitjes suggested in the chat:
[code]
PORTA = (oct & 0x1F) | (PORTA & 0xE0);
PORTE = ((oct >> 5) & 0x07) | (PORTE & 0xF8);

Link to comment
Share on other sites

See also:

http://svnmios.midibox.org/trunk/apps/sequencers/midibox_seq_v3/src/j5_dout.inc

(which can be optimized, yes... it's quite old. And it's better to use PROD[LH] as temporary registers than MIOS_PARAMETER[12])

The official j5_dout module will get a C wrapper, similar to this module: http://svnmios.midibox.org/trunk/modules/iic_midi

So that it can be used by asm and C programs....

Best Regards, Thorsten.

Link to comment
Share on other sites

Or sth. pretty close to what ptitjes suggested in the chat:

In fact this was :

PORTA = (PORTA & 0xF0) | (oct & 0x0F);
if (oct & 0x10) { PORTAbits.RA5 = 1; } else { PORTAbits.RA5 = 0; }
PORTE = (PORTE & 0x1F) | ((oct & 0xE0) >> 5);

which is the most efficient C code I found by looking to the generated asm...

Link to comment
Share on other sites

In fact this was :


PORTA = (PORTA & 0xF0) | (oct & 0x0F);
if (oct & 0x10) { PORTAbits.RA5 = 1; } else { PORTAbits.RA5 = 0; }
PORTE = (PORTE & 0x1F) | ((oct & 0xE0) >> 5);
[/code]

which is the most efficient C code I found by looking to the generated asm...

There are two issues, and two improvements to make it a bit faster (in C, the assembly variant beats it anyhow ;))

a) it isn't guaranteed, that reading PORTA returns the same values which have been written into PORTA, because the "read view" of this register returns the current values of the input stage, while writing into PORTA is directed to the output stage.

Simple example: you are using RA4 as open drain pin, and you are setting it 1 to deactivate the output driver. But you could read 0, e.g. if an external device drives the dominant (=0) value.

Accordingly, with PORTA = (PORTA & 0xf0) ... you would accidently activate the output driver of RA4 in such a scenario!

Thats the reason, why the LATx registers have been introduced in the PIC18F family - it gives you direct read and write access to the output register. And thats also the reason, why I prefer to access LATx registers in my code

(btw.: there is also a pipeline issue with PORTx registers, e.g. sequential read-modify-write operations (bsf/bcf/btg) can fail if they are done without at least one instruction between the accesses, because the second RMW access would read the previous value of the *input* register - which wasn't already updated by the new output  value at this time)

b) wrong mask is applied on PORTE (0x1f instead of 0xf8)

c) "(oct & 0xe0) >> 5)" - the AND mask is probably redundant, if C clears the carry flag before doing the shift operation, and masks the value after the swap operation (I would expect this)

d) you could also write: "LATA = (LATA & 0xd0) | (oct & 0x0F) | ((oct & 0x10) ? 0x20 : 0x00);"

Best Regards, Thorsten.

Link to comment
Share on other sites

There are two issues, and two improvements to make it a bit faster (in C, the assembly variant beats it anyhow ;))

a) it isn't guaranteed, that reading PORTA returns the same values which have been written into PORTA, because the "read view" of this register returns the current values of the input stage, while writing into PORTA is directed to the output stage.

Simple example: you are using RA4 as open drain pin, and you are setting it 1 to deactivate the output driver. But you could read 0, e.g. if an external device drives the dominant (=0) value.

Accordingly, with PORTA = (PORTA & 0xf0) ... you would accidently activate the output driver of RA4 in such a scenario!

Thats the reason, why the LATx registers have been introduced in the PIC18F family - it gives you direct read and write access to the output register. And thats also the reason, why I prefer to access LATx registers in my code

I read that in the I/O pin section of the datasheet but not understood it at first. I'm not so used to read the simplified diagrams. That's ok now I think.

(btw.: there is also a pipeline issue with PORTx registers, e.g. sequential read-modify-write operations (bsf/bcf/btg) can fail if they are done without at least one instruction between the accesses, because the second RMW access would read the previous value of the *input* register - which wasn't already updated by the new output  value at this time)

Did not know the PIC had a pipelined architecture ! I hope sdcc takes care of this when he optimizes code...

b) wrong mask is applied on PORTE (0x1f instead of 0xf8)

Oups!

c) "(oct & 0xe0) >> 5)" - the AND mask is probably redundant, if C clears the carry flag before doing the shift operation, and masks the value after the swap operation (I would expect this)

Yeah! I did not even think of it :)

d) you could also write: "LATA = (LATA & 0xd0) | (oct & 0x0F) | ((oct & 0x10) ? 0x20 : 0x00);"

Well. I looked at the generated code for ternary (?:) and it used intermediate viariables and the generated code is not optimized then simple if then set else set...

Best regards, Didier.

Link to comment
Share on other sites

Did not know the PIC had a pipelined architecture ! I hope sdcc takes care of this when he optimizes code...

It isn't really an CPU architectural issue, it's more a side effect caused by the way how GPIO SFRs are handled. The same issue exists for 8051 derivatives with single-cycle execution, and so far I remember also for AVRs. It doesn't exist in microcontrollers which insert wait cycles during a read access, or which handle masked RMW operations internally in the GPIO stage.

It's one of the first things you will notice when toggling a pin as fast as possible with "bsf/bcf" instructions - the pin won't toggle due to the FF at the input stage. You have to insert a NOP (or a more useful instruction) between bsf/bcf --- or you have to write into LATx instead of PORTx (if available)

However, modern controllers always provide a seperate IN and OUT register for GPIOs :)

Best Regards, Thorsten.

Link to comment
Share on other sites

It's one of the first things you will notice when toggling a pin as fast as possible with "bsf/bcf" instructions - the pin won't toggle due to the FF at the input stage. You have to insert a NOP (or a more useful instruction) between bsf/bcf --- or you have to write into LATx instead of PORTx (if available)

Happy to know that because I'll do some shift and latch clock generation these next days... I will have to do a __asm nop __endasm define or write to latch registers directly so... Thanks for that useful information!

Best regards, Didier.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...