View previous topic :: View next topic |
Author |
Message |
rovtech
Joined: 24 Sep 2006 Posts: 262
|
Setting port for I2C |
Posted: Sun Jul 12, 2020 8:09 am |
|
|
How should the SCL and SDA pins be set in set_tris_c() ?
I resurrected a program from 2014 and happened to have a PIC programmed with the code. The PIC worked in the circuit (graphics on an LCD via I2C) but when I programmed a new PIC with the old code it did not work. There were no errors or warnings when compiled.
Stripping the code down from 298 lines to this
Code: | /* Pre-processor directives */
#include <16F1938.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT, BROWNOUT, MCLR
#use delay (clock=8000000)
#use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
#byte portc = getenv("SFR:PORTC")
// The main function
void main(void)
{
// setup ports
set_tris_c (0x00); // all outputs
// clear display
I2C_START (); // start I2C
I2C_WRITE (0x4e); // addr of LCD
I2C_WRITE ('C'); // C CL to clear display
I2C_WRITE ('L'); // L
I2C_STOP (); // stop I2C
} // end of main function
// end |
I found it still did not work. I had been suspecting the LCD address as it was 7 bit 0x27 which needs to be shifted to 0x4E. This was not the problem.
When portC was changed to all inputs the program compiled and ran.
Code: | set_tris_c (0xff); // all inputs |
Even just commentating out the statement fixed the problem.
I checked several other programs from that time and all had a comment "Working" but all had portC set to output. I can only guess that the version of compiler I used back then corrected this error. Why does my current compiler (PCM ver 5.064) not even give a warning?
What is the correct way to set the pin directions for I2C? They need to be bidiectional and I would think the compiler would take care of that. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1908
|
|
Posted: Sun Jul 12, 2020 8:44 am |
|
|
The I2C lines are supposed to be set to inputs. What you're seeing rather makes me suspicious that the compiler isn't properly handling the tris register.
This is just one more justification that my habit of always specifying #use fast_io, for me at least, is the right choice. I don't want the compiler handling such a simple thing as whether a line is an input or an output; I sleep much better knowing that I set it properly and nothing will second guess me. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jul 12, 2020 10:05 am |
|
|
You're not supposed to set the i2c pins to outputs. That's wrong.
Here's a quote from the 16F1938 data sheet:
Quote: | 24.4.3 SDA AND SCL PINS
Selection of any I2C mode with the SSPEN bit set,
forces the SCL and SDA pins to be open-drain. These
pins should be set by the user to inputs by setting the
appropriate TRIS bits |
The 2nd problem you have is about understanding the CCS compiler.
The whole idea of CCS is to remove mundane trivial code-writing from
the user. The compiler does it for you in most cases. If you are a
a newbie and you try to out-think the compiler, you will almost always
sabotage yourself.
Look at the .LST file for your program (compiled with vs. 5.094):
Quote: |
......... void main(void)
0017: MOVLW 72
0018: MOVLB 01
0019: MOVWF OSCCON
// Here, the compiler sets both i2c pins to inputs, per the data sheet:
001A: BSF TRISC.TRISC3
001B: BSF TRISC.TRISC4
001C: MOVLB 04
001D: BCF SSPCON3.DHEN
001E: BCF SSPCON3.AHEN
001F: BCF SSPCON3.SDAHT
0020: MOVLW 13
0021: MOVWF SSPADD
0022: MOVLW 28
0023: MOVWF SSPCON1
0024: BSF SSPSTAT.SMP
0025: BCF SSPSTAT.CKE
0026: MOVLB 0F
0027: CLRF LCDCON
0028: CLRF LCDPS
0029: CLRF LCDSE0
002A: CLRF LCDSE1
002B: CLRF 79A
002C: MOVLB 03
002D: CLRF ANSELA
002E: CLRF ANSELB
002F: MOVLB 02
0030: CLRF CM1CON1
0031: CLRF CM1CON0
0032: CLRF CM2CON1
0033: CLRF CM2CON0
.................... {
.................... // setup ports
// Here, you change them to outputs, and it doesn't work:
.................... set_tris_c (0x00); // all outputs
0034: MOVLW 00
0035: MOVLB 01
0036: MOVWF TRISC
.................... |
There is one thing you can do that will help. Set all unused pins to
low-level outputs. The compiler won't do that for you. You can do this
as shown below (for example):
Code: |
output_low(PIN_A0);
output_low(PIN_A1);
output_low(PIN_A5);
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19524
|
|
Posted: Sun Jul 12, 2020 10:32 am |
|
|
The key thing is the compiler correctly sets up the pins for the I2C
to work.
You then override it's setting with the line:
set_tris_c (0x00); // all outputs
If you want to set all the other pins to output, then use:
set_tris_c (0x18); //.all except I2C as outputs. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun Jul 12, 2020 10:39 am |
|
|
Thanks everyone. This was a mistake from 5 years ago. I have 3 programs using the same hardware and all have the same mistake. I don't know why they worked back then. I always make a comment "working" after a program is developed and working. I guess I was lucky.
I have always wondered about setting pins, especially if they become bi-directional so I usually set them as inputs and assume the compiler will fix it.
What about newguy's suggestion of using fast_io? |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9231 Location: Greensville,Ontario
|
|
Posted: Sun Jul 12, 2020 11:55 am |
|
|
Using fast_io() is a two edged sword...
If, IF, you properly use it, it will speed up port/IO accesses and smaller code
but, BUT, if you make a bit an input instead of an output (or vice versa), say 3 months later, during a PCB layout revision... you can spend days trying to understand WHY doesn't it work now ???
Frankly, with the fast PICs of today, using fast_io() really isn't needed unless you've got custom hardware that requires very, very 'tight' timing. I've only needed it twice in 20+ years....
I just use 'standard mode'...though I could 'tweak' better performance using fast_io(), it's just not necessary 99.44% of the time. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1908
|
|
Posted: Sun Jul 12, 2020 1:42 pm |
|
|
fast_io() is an absolute must if your aim is the lowest possible power.
In the end, it comes down to an issue of trust. Do you trust yourself, or do you trust that the author(s) of the compiler did things properly? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jul 12, 2020 5:41 pm |
|
|
newguy wrote: |
fast_io() is an absolute must if your aim is the lowest possible power.
|
Can you explain this. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun Jul 12, 2020 5:55 pm |
|
|
How would I handle a situation where I must, because of limited pins, use mixed I/O.
Code: | set_tris_c(0b01011000); |
Now I need to read from some pins and write a pattern to C5, C2, C1, C0, as well as use I2C on C3 and C4.
I normally try to avoid this situation. I could set up a structure for the four outputs and send the pattern but I'm not even sure how I only send four bits.
I suspect that sending 1100 then 1111 to C5, C2, C1, C0 as below
Code: | output_c(0b00100100);
output_c(0b10100111); |
would mess up the I2c and other inputs and it sounds dangerous. I remember reading in a data sheet that if I write to an input the data is not placed on the pin but does get written to some internal register. Does this overwrite the last input on the read pin? Or am I confused.
Code: | output_bit(PIN_C5); |
Is safe but clumsy. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9231 Location: Greensville,Ontario
|
|
Posted: Sun Jul 12, 2020 6:05 pm |
|
|
re: Quote: | fast_io() is an absolute must if your aim is the lowest possible power. |
It could be that less executable code = lower power consumption, but I'd have to see the 'numbers'. Obviously it'd only apply to battery powered PIC projects. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1908
|
|
Posted: Sun Jul 12, 2020 7:06 pm |
|
|
temtronic wrote: | re: fast_io() is an absolute must if your aim is the lowest possible power.
It could be that less executable code = lower power consumption, but I'd have to see the 'numbers'. Obviously it'd only apply to battery powered PIC projects. |
That's it exactly. Wake, execute the lowest number of instructions possible, go back to sleep. Save a handful of instructions, then multiply that by the hundreds of thousands of wake-sleep cycles and you've added months to the life of your product on a set of batteries. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19524
|
|
Posted: Sun Jul 12, 2020 11:13 pm |
|
|
Well, the key to remember is you don't have to use fast_io, to have the same effect....
So:
Code: |
#byte PortBout=getenv("SFR:LATB")
#byte PortBin=getenv("SFR:PORTB")
//For PIC18, and modern PIC16's.
//Then your code uses 'standard IO' as normal, until the point in the
//'low power' section, where you want to read or write the port.
PortBout=xxxxx;
//Loads the output data latch, leaving the TRIS as standard IO has set it
//Result only bits set as 'output' change.
//To read the inputs:
val=PortBin;
|
In both cases the 'I/O' is directly to the port registers. No extra instructions.
You get the advantage of 'standard IO' controlling the TRIS, but also
the instruction efficiency of fast IO.....
As posted would need changing for 'old' PIC16's that don't have a separate
LAT register. However the PIC involved here is one that does. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Mon Jul 13, 2020 1:59 pm |
|
|
Back to my last question:
"How would I handle a situation where I must, because of limited pins, use mixed I/O."
I actually have the circuit connected and as I suspected it does not work.
When I get to the one added line:
the program and I2C freezes. This is trying to output 00 to the I2C pins (C4, C3) which are set as inputs.
What exactly is happening and how do I resolve such a situation.
If I add
I get the error " Option invalid Not a valid port:" |
|
|
gaugeguy
Joined: 05 Apr 2011 Posts: 303
|
|
Posted: Mon Jul 13, 2020 2:03 pm |
|
|
fast_io is set per port and not globally. The correct usage would be:
|
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9231 Location: Greensville,Ontario
|
|
Posted: Mon Jul 13, 2020 2:11 pm |
|
|
I'm curious......
Why not use output_bit(pin,value) instead of writing the whole port ?
It's fast and will not interfere with the I2C pins. |
|
|
|