PIC: can't set individual outputs without affecting others. why?? (PIC16F1789) - Page 1
Forum section: electronics / general

author: bitter_mike
author_rank: Contributor
timestamp: June 28, 2020, 03:10:12 am
content: 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:#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 500000void 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: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.

author: oPossum
author_rank: Super Contributor
timestamp: June 28, 2020, 03:19:38 am
content: Change the output latch (LATB), not the input port (PORTB).Code:LATBbits.LATB1 = 0;LATBbits.LATB1 = 1;

author: cv007
author_rank: Frequent Contributor
timestamp: June 28, 2020, 04:19:51 am
content: 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 pinsRB1 = 0;effectively rolled up in the resulting single instruction-U8 tmp = PORTB; // tmp = 0 as ansel is set for all pinstmp &= 0b11111101; // tmp = 0PORTB = tmp; // all pins 0RB1 = 1;effectively rolled up in the resulting single instruction-U8 tmp = PORTB; // tmp = 0 as ansel is set for all pinstmp |= 0b00000010; // tmp = 2PORTB = tmp; // all pins 0, except for b1PORTB=0b11111101; //there is no read-modify-write, so no PORT read, only LAT writePORTB=0b11111111; //there is no read-modify-write, so no PORT read, only LAT writeSo, 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.

author: MarkF
author_rank: Super Contributor
timestamp: June 28, 2020, 06:28:16 am
content: A picture is worth ...

author: bitter_mike
author_rank: Contributor
timestamp: June 28, 2020, 12:44:40 pm
content: 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.

author: rhodges
author_rank: Frequent Contributor
timestamp: June 28, 2020, 05:25:55 pm
content: QuoteIn that case, you use memory (a "shadow" byte) instead of LAT register. Modify the port memory byte, then write it to the port.

author: KL27x
author_rank: Super Contributor
timestamp: June 28, 2020, 08:11:47 pm
content: ^Yes.Quote from: bitter_mike on June 28, 2020, 12:44:40 pmWhen 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 XX003. 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 registerbsf 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 syncedmovf myLATBshadowregister,w ;copy it these parts you have to do inline, or in a subroutinemovwf 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.

author: Simon
author_rank: Unknown
timestamp: June 28, 2020, 08:37:37 pm
content: 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.

author: bitter_mike
author_rank: Contributor
timestamp: June 28, 2020, 10:22:03 pm
content: 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 registersThank you all. Very helpful.

author: cv007
author_rank: Frequent Contributor
timestamp: June 28, 2020, 11:29:35 pm
content: >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 pinYou 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.

author: bitter_mike
author_rank: Contributor
timestamp: June 29, 2020, 12:33:47 am
content: 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.

author: mikerj
author_rank: Super Contributor
timestamp: July 03, 2020, 04:38:43 pm
content: Quote from: Simon on June 28, 2020, 08:37:37 pmRMW operations are a staple of all CPU programming, not just microcontrollers. FWIW the ancient 8051 had areas of bit addressable memory and IO.