PIC Reading and Writing to Registers - Page 1
Forum section: electronics / general

author: jackbob
author_rank: Regular Contributor
timestamp: April 04, 2016, 10:03:42 am
content: Hello everyone, I have come for a little help from the PIC pro's on here. I have a fair amount of experience programming AVR micros through both arduino libraries as well as with standalone AVR C. There seems to be a good amount of documentation and help for AVR's (I think mostly due to their popularity from arduino) but I am having trouble finding any help in documents, datasheets or microchips website. I have done quite a bit of AVR programming and and more than comfortable programming in C and working with micro's but I just cant seem to find the PIC way of doing the same I did with AVR's. For instance a simple blinking led program, in AVR we would access an IO register and set the output with something like PORTB |= (1<<PORTB1) then clear it with the same bit shifting method using the & bitwise operator. I try the same in PIC and nothing. I am working on a PIC12F683, I have it hooked up correctly and it works if I do something like GP2=1 then GP2=0 it will set and clear that pin and blink the led, but what if I want to access the whole port? Hex works but its a bit of a head puzzle converting a hex to a 8 bit binary port every time (I know this particular chip only has 6 IO). I tried GP2=0b00000100 to set the bit and it works but clears the rest of the bits in the process. So after all this mess my question is how can I set and clear bits on a port without writhing a line of code for each one and how do I read the values of these pins if they are set up as inputs? In AVR you use bit shifting to read the state of a pin on a port but that doesn't seem to work the same with PIC so I'm hosed on that idea. I wish Microchip would provide just a few lines of example code in their datasheets like Atmel does for both assembly and C. Thanks for the help everyone!

author: jpanhalt
author_rank: Super Contributor
timestamp: April 04, 2016, 11:06:07 am
content: I only program in Assembly, but hope to have some insight on your problems with the 12F683, which I used for years.1) All ports come up in analog mode. Turn off those functions, like comparators and ADC's. Setting TRIS is necessary for an output, but not sufficient. ANSEL,ADCON0, and CMCON (comparator) are key registers to set up.2) The 1<<PORTx,1 syntax works in Assembly, but with the 683, remember port is named GPIO, If you make a small move to the 12F1840, then you can use the more familiar name of "port." An alternative structure is bsf/bcf GPIO,3. Be aware of the R-M-W issue, but for LED's that will probably not be a problem. In brief, bsf and similar instructions are RMW instructions. Setting a hex or binary in wreg and then moving it is not a RMW instruction.3) As for example code, there is tons of it for the 683, but most is probably Assembly. Microchip in its Instruction Summaries has examples. Again, most are in Assembly for that chip. There is an abbreviated version with each chip, but there are compilations with more examples for the mid-range devices.Sorry, I have little insight into C, but hope the above hints are helpful.John

author: JPortici
author_rank: Super Contributor
timestamp: April 04, 2016, 11:24:47 am
content: seems to me you have -never- read a mcu datasheetthere is TONS of documentation on the microchip websiteSo: PIC12F683. Google first result is microchip's product page:http://www.microchip.com/wwwproducts/en/PIC12F683Upright corner you see "datasheet" button, or at the bottom of the page you can find latest datasheet, device errata, programming specification and (for more complex devices) family reference manuals (focused on the hardware, as the datasheet for complex devices becomes only a reference on position and bit values.) and programming manuals (focused on the architecture, instruction set etc)The a number of app notes using this specific pic or a simillar one.Now, how to access the whole port?Section 4, Page 33 -> GPIO PORT.A description of the module, an example of initialization code and then the registers involved with all the explaining.GPIO is the name of the register and each one of its bits is labelled GP0 for bit 0, GP1 for bit 1 etc. Every implemented bit controls one pin.It's pretty obvious that reading/writing directly to GPIO will access the whole port. Even if it isn't obvious it's clearly stated.Being a pic12 with only one io port it is called GPIO, with TRISIO the tristate register (select if the pin is an input or an output) from pic16 onwards you will have multiple io ports, so they will be TRISA/PORTA/LATA*, TRISB/PORTB/LATB*, ...*LATx register when implemented controls the latch out pins. it is different than PORTx because PORTx controls the pins directly.. for example if the pin is controlled by a peripheral you can still force it to another state for a clock cycle.. which is unadvisable. so for you not to care you write the outputs to LATx

author: dannyf
author_rank: Super Contributor
timestamp: April 04, 2016, 04:20:05 pm
content: "LATx register when implemented controls the latch out pins. "Portx as well: a write to portx writes to the output data register.

author: jpanhalt
author_rank: Super Contributor
timestamp: April 04, 2016, 04:25:58 pm
content: The 12F683 doesn't have latches.John

author: JPortici
author_rank: Super Contributor
timestamp: April 04, 2016, 04:54:10 pm
content: please, read the whole sentence before commenting.mine was an introduction to pics, a more general approach, which is why i wroteWHEN they are implementedand i clearly stated the difference between the two registers and why you should use the LATx if you have it available.which is also written in the datasheets by the way..Glitches in analog readings, especially at higher samplerates when you toggle the port pin of an analog input, glitches in pwm signals and things like thatSo yes, you can use portx to toggle pins but it's safer to use LATx if and when it's available, as the old saying:"read from portx, write to latx"

author: dannyf
author_rank: Super Contributor
timestamp: April 04, 2016, 05:47:39 pm
content: "12F683 doesn't have latches"Depends on what you meant by "latches". The particular chip doesn't have an output latch register, but each pin has numerous latches (- flip flops).

author: dannyf
author_rank: Super Contributor
timestamp: April 04, 2016, 05:51:23 pm
content: "it's safer to use LATx if and when it's available, as "Depends on your meaning of "safer". The fundamental issue is that pins logic state may not accurately be reflected by the output latch / data register, but will always be accurately reflected by the port (input) data register.Because of that you can construct cases where the use of portx registers are preferred over the use of latx registers.Hard to make a sweeping statement.

author: JPortici
author_rank: Super Contributor
timestamp: April 04, 2016, 06:34:15 pm
content: i though i wrote it somewhere... read from PORT, write to LATbecause PORT will have the actual state of the pins (so it will reflect the state of the input pins, while reading from LAT will give incorrect results.. IIRC LATx bits associated to output pins will be 0 regardless of the state of the pin).maybe we are on different pages.. I provided examples where writing to PORT will cause serious problems that writing to LAT wouldn't, so you shoult be really careful to write to PORTxcan you provide some examples where writing to port should be preferred? Always good to learn new tricks

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: April 04, 2016, 10:43:50 pm
content: The 12F683 doesn't have LATx registers so you can ignore that.The I/O port on 12F and 16F devices only has one address which is the same for writing and reading. So whenanyread occurs it reads the pin values, not the output register. 'PORTB |= (1<<PORTB1)' first reads the port pins to get their current state, then modifies one bit, and finally writes the result to the output register.If any pins are being forced up or down by external hardware (eg. a large capacitor, LED without resistor) or cannot be read because they are set to analog mode, then the OR operation may fail to maintain the values ofotherbits (not the bit you are trying to set) in the output register. To avoid this you can either set the pin to digital mode and ensure that it is not overloaded, or keep the output value in a 'shadow' variable, modify it and write the result to the port, eg.Code:unsigned char port_b = 0;port_b |= (1<<PORTB1); // set bit in 'shadow' variablePORTB = port_b; // copy 'shadow' variable to portQuote from: jackbobNo. You cannot shift the bits on an input port - they always reflect the external values on the pins. If you mean operations like '(1<<PORTB1)', this merely creates a constant value with a particular bit set. The compiler will resolve this to 0b00000010. Some compilers also provide a way to reference the bits directly, eg. in XC8 you can use 'PORTBbits.PB1'.

author: dannyf
author_rank: Super Contributor
timestamp: April 04, 2016, 10:57:28 pm
content: Shadow variables may have other implications, by writing to other pins or input pins.As to bit fields, even if the compiler doesn't provide pin support, you can create a struct with bit fields and point to any address, port or direction registers, and implement your own.Personally I prefer masks for its portability.

author: jackbob
author_rank: Regular Contributor
timestamp: April 04, 2016, 11:12:34 pm
content: Bruce, thanks for your help I think my problem is the I did not realize there are no output latches as well as an analog or digital mode for the pins, these are carried out a bit different in AVR. Generally all pins on an AVR chip are GPIO inputs by default and become analog or special function when you enable that specific hardware within the chip and override the digital GPIO function.Quote from: Bruce Abbott on April 04, 2016, 10:43:50 pmAs for reading from AVR pins, you can use bit shifting when reading pins, although there are other ways, it makes sense to me reading it. It would look something likeCode:input = PINB & (1<<PB2);of course you can still use binary or hex bit masks but this doesnt require you to stop for a second and determine which pin a binary or hex number represents.

author: jackbob
author_rank: Regular Contributor
timestamp: April 04, 2016, 11:20:44 pm
content: Quote from: JPortici on April 04, 2016, 11:24:47 amYup you caught me I have been programming AVR's for years without datasheets?!? I can read and interpret a datasheet. Although I will say I may have skipped over a bit too much in the datasheet for startng with PIC's. Generally these days with AVR after programming them for years I know all the functions, defaults and configuration for the chip and can skip straight to the section in the datasheet I am trying to work on. I came in assuming I could do the same with PIC's but may have to work and understand this architecture a little better before I jump straight to the GPIO section and try to start programming. Thier website has lots of documentation but most of it are just sections cut out of the datasheet. Their programming documentation is straight out of the datasheet so its not much additional help.

author: dannyf
author_rank: Super Contributor
timestamp: April 05, 2016, 12:40:55 am
content: Quoteit can be done with PICs too.

author: jackbob
author_rank: Regular Contributor
timestamp: April 05, 2016, 12:57:40 am
content: After reading through the datasheet a little more thoroughly here is what I found. First off I will admit I should have read the datasheet in more detail before just assuming its like AVR's. The architecture is quite a bit different and I found that writing to a pin configured as an output works just like AVR regardless if it is o=in analog or digital mode (the ANSEL register) however when reading the pin, it must be in digital mode to function like a normal GPIO. My problem I was having by setting the bits using GPIO |= (1<<GP2) is that it has to read the register first then modify and write the bit changes. The key here is it has to read the registers current data to modify it, for that it has to be in digital mode with the ANSEL bit cleared for that port. After that it works great. Although I was having a weird problem with the led blinking once then hanging for a second and resuming its normal blink pattern, come to find the watchdog was enabled and resetting the chip. Thanks for your help everyone! I learned a lot just reading the replies on this thread. EEVblog is the place!

author: dannyf
author_rank: Super Contributor
timestamp: April 05, 2016, 01:02:03 am
content: QuoteThere are other functions you may wish to disable for the port to work as gpio. typically the first thing you do.AVRs boot up to gpio, and PICs don't. Nothing to do with architectures of either chip.

author: jackbob
author_rank: Regular Contributor
timestamp: April 05, 2016, 01:37:17 am
content: Quote from: dannyf on April 05, 2016, 12:40:55 amDanny, I got it working very close to my example above but it seems to have trouble recognizing the bit name within the register.This is my code I run in my while loop. After disabling the analog select bit in the ANSEL register it works.Code:GPIO |= (1<<2);__delay_ms(500);GPIO &= ~(1<<2);__delay_ms(500);But this very similar code doesn't work. It compiles without errors but when I upload it, nothing..Code:GPIO |= (1<<GP2);__delay_ms(500);GPIO &= ~(1<<GP2);__delay_ms(500);I don't mind the first piece of code in this scenario but the second one is better when the bits are configuration bits or something not as organized. When setting config bits it is much easier to do something likeCode:SPSR |= (1<<SPIF);rather than its equivalentCode:SPSR |= 0x80;

author: Ian.M
author_rank: Super Contributor
timestamp: April 05, 2016, 02:38:44 am
content: Microchip don't define SFR bit names that way. The general form for accessing individual SFR bits or groups of related bits for Microchip's XC compilers isSFRbits.BITNAMEbut for legacy compatibilityBITNAMEon its own can be used for single bits in many registers on older devices. Substitute an actual register name and bit name for the italics. The dotted notation is preferred as the direct bitnames are being depreciated.These definitions are C variables (albeit volatile and located at the SFR address), so(1<<GP2) reads the pin GP2 and returns either 1, if its low or 2 if its high - which is nothing like what you want to do.Take a look at the XC8 header include/pic12f683.h, and see what is actually defined for the SFR you want to access. In general, if using << for port bits, simply use the bit number as the bit position name is horrible, e.g. _GPIO_GP2_POSN, so you'd need to use:Code:GPIO |= (1<<_GPIO_GP2_POSN);if you weren't settling for the numeric form. However most of us would simply use:Code:GPIObits.GP2=1;

author: JPortici
author_rank: Super Contributor
timestamp: April 05, 2016, 06:55:11 am
content: Quote from: jackbob on April 04, 2016, 11:20:44 pmDon't get me wrong, this is the internet. i don't know you, you don' know me and when i see these kind of question 99% of the time he who asked didn't even bother to look up for the datasheet.. you know.. how many people these days are learning programming by copying tutorials without reading the documentation.. or even the turoial... not making any names here..One last note, once you get used to the instruction set you should look at the disassembly to see whyQuotedidn't work, but on top of my head i have honestly no idea.Anyway the """bitfield""" elements are treated in a special way by the compiler and you should indeed use GPIObits.GP2 = 1 because it will translate into the single instruction "BSF GPIO,2" instead of load 1... clear carry, rotate it left (twice)... OR with GPIO

author: westfw
author_rank: Super Contributor
timestamp: April 05, 2016, 10:42:34 am
content: What Ian said. If you look at the PIC12f683.h file, you'll find:Code:extern volatile __bit GP2 @ (((unsigned) &GPIO)*8) + 2;Which of course is much different than Atmel'sCode:#define PB2 2Neither is "incorrect", but I've always found Atmel's habit of wanting to name "2" different things for every possible register (PB2, PD2, ...) to be pretty annoying. They're constants don't really have anything to do with PB any more; they might as well be numeric constants, or something generic like "BIT2"). (then again, Microchip's definition is pretty far from "standard C")A nasty trap for people going back and forth, though...

author: dannyf
author_rank: Super Contributor
timestamp: April 05, 2016, 10:59:16 am
content: "It compiles without errors but when I upload it, nothing.."You may want to read the device header file, as you would on the avr, to know how everything is defined. Compiling without error is just the first step in getting your code to work.

author: Ian.M
author_rank: Super Contributor
timestamp: April 05, 2016, 11:23:49 am
content: Yes. The direct access bit variable form (that is being depreciated)Code:extern volatile __bit GP2 @ (((unsigned) &GPIO)*8) + 2;uses two non-standard extensions:__bitfor a single unsigned bit type, (also namedbitwhen CCI isn't enabled), and@which sets the address of the object being defined/declared.@ is fairly simple, for all except bit variables it simply takes the memory address as its parameter. For bit variables, the required parameter is eight times the address and the three LSBs select the bit number in the byte.(unsigned) &GPIO)is the address of the GPIO SFR, and the rest should be fairly obvious.Variables ofbittype are a little more complicated. Normally the smallest unit of storage for C is a char, usually a single byte. Because the processor can only access memory one byte at a time, and 8 bit PICs dont have a barrel shifter, you are not allowed to define a pointer to a bit variable or an array of bit variables. Also the C startup code cant initialise bit variables. There are a few other limitations , but since eight bits can fit in the same RAM as a single bool, bit variables are commonly used for flags when minimum RAM usage is important. The PIC instruction set can natively set, clear and test bits, and on PIC18, toggle them, but there is no bit assignment instruction, so if you put a non-const expression on the RHS of an assignment operator to a bit, the generated code to handle the assignment can get pretty fugly.The dotted form is pure ANSI C apart from the @ forcing the structure/union to the SFR address. Most cross-compilers for embedded systems have their own equivalent of @, as its a royal PITA to have to do all SFR access via addresses cast to pointers. Also, it handles predefined groups of bits cleanly. e.g.ADCON0bits.CHSfor the ADC channel selection, direct by channel number rather than having to set the individual bits in binary.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: April 11, 2016, 01:36:46 am
content: Quote from: jackbob on April 04, 2016, 11:12:34 pmTo reiterate, this isnota bit shift operation, but merely a convenient way to define the bitmask. You could write "input = PINB & 0x04;" and it would be functionally equivalent.If you look in the port definitions include file you should see something likeCode:#define PB22#define replaces one text with another, so 'PB2' is replaced by '2', and the compiler sees '(1<<2)' which is a constant value. No bits get shifted except in thecompiler'smath parsing routines.