7
\$\begingroup\$

What causes turning ON a single output pin on Microchip PIC16F690 to spontaneously turn OFF another pin on the same port? I can work around this problem by writing a byte to the entire port, controlling all pins simultaneously, instead of just a bit to control the pin state. I'm using the Hi-Tech C compiler here. I am determining the state of the pin with 9 LEDs each consuming 3 mA. This is way below the max power specs.

The mplab header file has the 0 pin on port A declared as such:

volatile       bit RA0  @ ((unsigned)&PORTA*8)+0;

I am turning the pin on by writing a high value to it.

RA0 = 1;

Is the problem that the complier is treating the "1" as a byte and writing to the whole port? Do I need to cast it? If so, shouldn't have the complier given me an error?

RA0 = (bit) 1;

If I write to the whole port everything works as expected:

PORTA = 0b00000001;
\$\endgroup\$

5 Answers 5

16
\$\begingroup\$

It's the well-known read-modify-write problem, you will find details in the data sheet. You have to write to the whole register, as you have found. The 18F and 16-bit devices don't have the problem. There is a good description on page 2 in this document. Changing bits in a "shadow" register then writing the register to the output port is often used to get round the problem.

\$\endgroup\$
2
  • \$\begingroup\$ Are you sure this is the problem? I would imagine that RA0 = 1 should generate ASM that uses BSF, which shouldn't affect bits 1-7.You should check the assembly listing file. I think Hi-Tech C generates it automatically as the .lst file. \$\endgroup\$
    – ajs410
    Dec 13, 2010 at 22:02
  • 2
    \$\begingroup\$ BSF actually does affect all bits 0-7, even though we all wish it didn't. See techref.massmind.org/techref/readmodwrite.htm . \$\endgroup\$
    – davidcary
    Jan 2, 2011 at 4:21
1
\$\begingroup\$

Make sure you have the port you are using defined as digital. ANSEL = 0;

If a port is defined as analog and you do a digital read it returns 0. So, when the PIC F16xxx does the read-modify-write operation it reads 0 on all analog pins. Then is writes 0 back to all these pins.

If you have ANSEL set to 1 for PORT B the code below will turn on PORT B for 500ms, then read PORTB as 0b00000000 (because it is analog). Then turn PORTB off because it thought it was off.

    ANSEL = 0b11111111;
    TRISB = 0;
    PORTB = 0b11111111;
    __delay_ms(500);
    current = PORTB;
    PORTB = current;
    __delay_ms(500);

Make sure you set the corresponding ANSEL bit to 0 for any pin you want to use as digital!

\$\endgroup\$
0
\$\begingroup\$

I use Microchip's compiler. It has a header file where all the registers have a union with the bits defined. So, in my code I write:

LATAbits.LATA0 = 1;

Also, I would use the latch register instead of the PORT register to set the output. I think some chips don't care, but some do.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ The 16F PICs don't have LAT registers, hence the problem. \$\endgroup\$ Dec 9, 2010 at 20:22
  • 2
    \$\begingroup\$ @Leon Heller: The separate latch registers were a feature of later General Instruments PIC parts which unfortunately didn't get incorporated into Microchip's PIC parts until the 18F series; odd that GI noted the problem with read/write PORTx registers in the 1980's, but Microchip didn't deal with it for decades. \$\endgroup\$
    – supercat
    Mar 10, 2011 at 2:36
0
\$\begingroup\$

I'm 99% sure the compiler is doing this. Whatever structure is defined to access the pin has ambiguity in it that the compiler is resolving to create this behavior. The bit casting might help but I don't know how (bit) is defined so I can't be sure. The compiler won't necessarily give you an error if the value being written to the pin is of the proper type - I'm guessing the (bit) mask might trigger some logic to preserve the state of the other pins but still return the same type as your constant.

It's not surprising that writing to the port itself works - but working with individual pins is much more complicated. Either there's a bug in the code provided, it wasn't meant to be used with this compiler or you're just not using it correctly.

\$\endgroup\$
2
  • 3
    \$\begingroup\$ It's nothing to do with that! \$\endgroup\$ Dec 9, 2010 at 20:44
  • \$\begingroup\$ Well, actually C compilers can be the reason for many of the problems. This problem is caused by the compiler, since it is probably using something like XORWF, however it is not the compiler's fault. Programmer should use a shadow register. So, at the end, you are right, @LeonHeller. I am starting to hate C compilers, especially XC8 nowadays.. \$\endgroup\$ Jun 13, 2012 at 7:24
-2
\$\begingroup\$

Writing the whole port to 1 shows you the problem. In binary, that's 0b00000001, so you are in fact setting the first seven bits off and the last bit on.

You need to use binary operators to make sure you're only making the change to that specific bit. Do this:

PORTA = PORTA | 1;
// Equivalently:
current = PORTA;
new = current | 1;
PORTA = new;

If pins 2-8 are currently high, the OR operation will return true and the bit will remain set.

To make a bit low, use a binary AND operation:

PORTA &= 1;

Note that some compilers will have specific macros to do this (i.e, _BV() on avr-gcc) and some micros will have specific addresses aliased to each peripheral bit so you don't have to do this read-modify-write cycle (bit-banded memory on Cortex-M3)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.