Author Topic: PIC: can't set individual outputs without affecting others. why?? (PIC16F1789)  (Read 2525 times)

0 Members and 1 Guest are viewing this topic.

Offline bitter_mikeTopic starter

  • Contributor
  • Posts: 36
  • Country: us
  • Expert in electroplating, pretender at electronics
Hi,

I'm using a PIC16F1789 and just trying to get a feel for setting I/O pins. I've encountered this issue which must just be me doing something nuts but I can't figure it out. I'm just trying to set 1 pin on a port to switch low/high to blink an LED, but when I do this, it also affects the state of the other pins on the same port. Here's the code:

Code: [Select]
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = ON    // Clock Out Enable (CLKOUT function is enabled on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable bit (Vcap functionality is disabled on RA6.)
#pragma config PLLEN = OFF       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = OFF      // Low Power Brown-Out Reset Enable Bit (Low power brown-out is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

#define _XTAL_FREQ 500000
void main()
{
TRISB = 0x00;   
PORTB=0b11111111;
OSCCON=0b00111000;

while(1)   
    {
    RB1=0;
    __delay_ms(500);   
    RB1=1;
     __delay_ms(500); 
    }
}

What this should be doing is setting all the pins on Port B as outputs, initializing them all as high, then blinking pin 1. However, what happens is that all the pins are initially set high, then once the "RB1=0;" instruction gets processed, pin 1 goes low, as it should, but pins 0, 2, 3, 4, 5, and 6 also go low and stay there (pin 7 does not). I have run exactly the same code except on port D and the same thing happens except that only pins 0, 1, and 2 go low. If instead of using "RB1=0;", I explicitly set the whole register, as in:

Code: [Select]
while(1)   
    {
    PORTB=0b11111101;
    __delay_ms(500);   
    PORTB=0b11111111;
     __delay_ms(500); 
    }
}

This works flawlessly. I must be doing something foolish and I would greatly appreciate it if someone could point out what it is.

Thank you.
 

Online oPossum

  • Super Contributor
  • ***
  • Posts: 1522
  • Country: us
  • Very dangerous - may attack at any time
Change the output latch (LATB), not the input port (PORTB).

Code: [Select]
LATBbits.LATB1 = 0;

LATBbits.LATB1 = 1;
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 965
A write to PORT or LAT writes to the same register (LAT), a read of PORT is a read of PORT (the physical pin) and not the LAT register. When ANSEL is set to 1 (default) for a pin, then a read produces '0'. Also note RB1 is effectively the same as PORTBbits.RB1 and will normally result in a BCF/BSF instruction.

Since the BCF/BSF instruction results in a read-modify-write, when used on PORT you may get unwanted results, and when ANSEL is not cleared you have more problems.

TRISB = 0x00;   //all output, but ansel is still set (any read will be '0')
PORTB = 0b11111111; //all bits in LATB set, resulting in high level for all pins

RB1 = 0;
    effectively rolled up in the resulting single instruction-
    U8 tmp = PORTB; // tmp = 0 as ansel is set for all pins
    tmp &= 0b11111101; // tmp = 0
    PORTB = tmp; // all pins 0

RB1 = 1;
    effectively rolled up in the resulting single instruction-
    U8 tmp = PORTB; // tmp = 0 as ansel is set for all pins
    tmp |= 0b00000010; // tmp = 2
    PORTB = tmp; // all pins 0, except for b1

PORTB=0b11111101; //there is no read-modify-write, so no PORT read, only LAT write
PORTB=0b11111111; //there is no read-modify-write, so no PORT read, only LAT write


So, use LAT when writing to an output to avoid the rmr problem when using PORT. The use of LATBbits.LATB1 is what they they want you to use, but they also provide the simple LATB1 version if wanted.
 

Online MarkF

  • Super Contributor
  • ***
  • Posts: 3026
  • Country: us
A picture is worth ...

   
 

Offline bitter_mikeTopic starter

  • Contributor
  • Posts: 36
  • Country: us
  • Expert in electroplating, pretender at electronics
Ok, so I see that controlling this via a LAT register is better than the PORT register, but I would like to understand more about the origins of the "read-modify-write" problem and how the LAT register is handled differently. I also gather from googling around that some PICs don't have LAT registers and this problem has to be overcome via other means. Is there some more detailed reading someone can recommend on the subject?

Thank you all for your prompt responses.
 

Offline rhodges

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: us
  • Available for embedded projects.
    • My public libraries, code samples, and projects for STM8.
Quote
I also gather from googling around that some PICs don't have LAT registers and this problem has to be overcome via other means.
In that case, you use memory (a "shadow" byte) instead of LAT register. Modify the port memory byte, then write it to the port.
Currently developing embedded RISC-V. Recently STM32 and STM8. All are excellent choices. Past includes 6809, Z80, 8086, PIC, MIPS, PNX1302, and some 8748 and 6805. Check out my public code on github. https://github.com/unfrozen
 

Offline KL27x

  • Super Contributor
  • ***
  • Posts: 4108
  • Country: us
^Yes.
Ok, so I see that controlling this via a LAT register is better than the PORT register, but I would like to understand more about the origins of the "read-modify-write" problem and how the LAT register is handled differently. I also gather from googling around that some PICs don't have LAT registers and this problem has to be overcome via other means. Is there some more detailed reading someone can recommend on the subject?

Thank you all for your prompt responses.
When you write bsf PORTB,4, the hardware (the actual transistor machinery that sets the pin tristates) only accepts a full byte. A bit operation to a port is like a lottery ticket with only one number penciled in, and you try to put that in the lottery ticket machine to buy your ticket. The older micros don't remember what the rest of the port is set to. So what the micro actually does with a bit instruction can be viewed as multiple distinct operations in a specific sequence.

1. READ: perform a read of PORTB. This depends on the actual physical voltage of the pin at this time. And also will automatically read as 0/lo if ANSEL is on for that pin.

So let's say that PORTB,0 is set as output high through TRISB and LATB registers. But the pin is soldered to the ground rail; so the actual read will record as lo/0. Let's say PORTB,1 is using an analog peripheral, so ANSEL is on for that pin, i.e. ANSELB contains b'00000010'. No matter if the voltage on RB1 is actually equal to Vdd (and even if LATB,1 is set to 1), the port read will automatically return a zero for RB1, because of the digital filter being turned off for that pin.
                                           
2. copy this reading into working memory, which is now XXXX XX00

3. MODIFY:  set bit 4 of working memory, so it's now XXX1 XX00               ;bit 4 is the 5th order bit, 5th from the right. This is because the bits start at 0 by convention, not 1.

4. WRITE:  Now we have the full lottery ticket request, with all of the digits plus the powerball. This gets fed into LATB (or PORTB, in the older micros). So now LATB will be XXX1 XX00. LATB,4 is now high. This is what you wanted. But LATB,0 and LATB,1 are also going to be set to low, if they weren't already.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;edit: example of shadow register, for a device that doesn't have a LAT register

bsf       myLATBshadowregister,4      ;this is a register you defined in user data memory, yourself.
                                                     ;And of course, you also initialized it, essentially the same way as a normal LAT register.
                                                     ;and during this initialization, you already copied this register into PORTB, so they
                                                     ;are initially synced

movf    myLATBshadowregister,w      ;copy it                these parts you have to do inline, or in a subroutine
movwf  PORTB                                ;into PORTB          vs in the modern PIC that does this automagically.


;When you write the entire byte, it's a straight write-over operation. There's no read-modify-write.
« Last Edit: June 29, 2020, 06:53:26 am by KL27x »
 

Offline Simon

  • Global Moderator
  • *****
  • Posts: 18614
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
read-modify-write has been a staple of micro-controller programming because there was never an ability to address pins. I am not familiar with your chip as I use atmel where on the latest version of 8 bitters they feature port set and clear registers, you write a 1 to the register and it does not affect the other pins.
 

Offline bitter_mikeTopic starter

  • Contributor
  • Posts: 36
  • Country: us
  • Expert in electroplating, pretender at electronics
Ok I think I see it now. With what I was doing, the pins may not have had time to reach their assigned level and since the RB1 instruction was  actually reading the state on the pin, it may have mistakenly copied over the old value on the other pins and over written them. Just to convince myself, I'm going to try it with a delay built in and compare it with the results using the latch registers

Thank you all. Very helpful.
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 965
>With what I was doing, the pins may not have had time to reach their assigned level and since the RB1 instruction was  actually reading the state on the pin

You may want to re-read the thread. With ANSEL unchanged from reset values, you are always reading 0 from PORT (read the datasheet where it talks about ANSEL). The point of using LAT when writing output values is to avoid any mystery to what you may get when reading, as LAT does not depend on ANSEL or what a pin value may happen to be.
 

Offline bitter_mikeTopic starter

  • Contributor
  • Posts: 36
  • Country: us
  • Expert in electroplating, pretender at electronics
You're right. I was speaking of a different problem I've read about with capacitive loads. I've tried this now on a couple of different ports and the pins which end up in the wrong state correspond exactly to those pins which are available as analog inputs and thus have ANSEL registers to set (0-2 on D, 0-6 on B).

Mystery solved. Much has been learned.
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3614
  • Country: gb
read-modify-write has been a staple of micro-controller programming because there was never an ability to address pins.

RMW operations are a staple of all CPU programming, not just microcontrollers.  FWIW the ancient 8051 had areas of bit addressable memory and IO.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf