Learning to program the PIC16LF1823

jpanhalt

Joined Jan 18, 2008
11,087
What? He posted "org 2100" in post 393. And nobody posted anything after that telling him the correct address is F000. You just posted to say "I thought of it first!" Seriously?
What's your problem?

Those code snippets were taken directly from code I posted along with my review of Sparkfun's 4x20 LCD in 2018 to update its glitchy code. That product has now been retired, and my review w/ code has been removed. Here's a link to AAC where I posted the same thing: https://forum.allaboutcircuits.com/threads/fixing-the-sparkfun-4x20-lcd-display.148893/

EDIT: I initially misunderstood Cesar's question and hence posted the code in #396 for reading and writing, which is pretty much straight from Microchip's datasheets..
 
Last edited:

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
... hence posted the code in #396 for reading and writing, which is pretty much straight from Microchip's datasheets..
And that's where I was kind of lost. Nowhere in the PIC16LF1823 datasheet nor in the MPASM User's Guide is it stated that the absolute address for the EEPROM is F000H for said chip.

Here's what's shown in the User's Guide:

1605117205117.png
I was naturally confused, but made the mistake of assuming said table was infallible and did not try any other address than 0x2100.

BTW, using the F000H address worked beautifully and solved my problem. Thank you both.
 

upand_at_them

Joined May 15, 2010
939
What's your problem?

Those code snippets were taken directly from code I posted along with my review of Sparkfun's 4x20 LCD in 2018 to update its glitchy code. That product has now been retired, and my review w/ code has been removed. Here's a link to AAC where I posted the same thing: https://forum.allaboutcircuits.com/threads/fixing-the-sparkfun-4x20-lcd-display.148893/

EDIT: I initially misunderstood Cesar's question and hence posted the code in #396 for reading and writing, which is pretty much straight from Microchip's datasheets..
Your code in #396 doesn't say to use 0xF000 for EEPROM.
 

upand_at_them

Joined May 15, 2010
939
And that's where I was kind of lost. Nowhere in the PIC16LF1823 datasheet nor in the MPASM User's Guide is it stated that the absolute address for the EEPROM is F000H for said chip.

Here's what's shown in the User's Guide:

I was naturally confused, but made the mistake of assuming said table was infallible and did not try any other address than 0x2100.

BTW, using the F000H address worked beautifully and solved my problem. Thank you both.
Yes, it is definitely confusing. One would think that if Microchip says to use 0x2100, we should be using 0x2100. I, and many others, have ran into MANY datasheet errors. Apparently they use a lot of interns or newbies for important details that they shouldn't be trusted with.

I think I've only used EEPROM a few times in the past. Nothing rang a bell, but when I looked in the programming spec for that PIC (document DS41390D) I saw that this:

The physical address range of the 256 byte data
memory is 0000h-00FFh. However, these addresses
are logically mapped to address 1E000h-1E1FFh in the
hex file.
And I remembered that addresses in the hex file are 2x the actual address. So 1E000 / 2 = F000.
 

jpanhalt

Joined Jan 18, 2008
11,087
Your code in #396 doesn't say to use 0xF000 for EEPROM.
#396 was when I misunderstood the question. #399, which is from the very same code and was posted here in 2018, clearly shows 0xF000 for the DATAEE start address. It was also discussed in 2011 on the Microchip forums for the 16F1847 (https://www.microchip.com/forums/m604532.aspx). My code was for the PIC1827. In fact, I had written my response and then took time on Google to try to document that it applied to the PIC1823 (a likely assumption) when I got the message another post had appeared. I could not find any statement of that address specific to the PIC16F1823.

Believe whatever you want. I did not copy you. Maybe you learned it from my June, 2018 post? Doesn't matter to me. In any event, your accusation is silly and insulting.
 

upand_at_them

Joined May 15, 2010
939
#396 was when I misunderstood the question. #399, which is from the very same code and was posted here in 2018, clearly shows 0xF000 for the DATAEE start address. It was also discussed in 2011 on the Microchip forums for the 16F1847 (https://www.microchip.com/forums/m604532.aspx). My code was for the PIC1827. In fact, I had written my response and then took time on Google to try to document that it applied to the PIC1823 (a likely assumption) when I got the message another post had appeared. I could not find any statement of that address specific to the PIC16F1823.

Believe whatever you want. I did not copy you. Maybe you learned it from my June, 2018 post? Doesn't matter to me. In any event, your accusation is silly and insulting.
I don't care if you copy me or not. I wasn't asserting that. I just pointed out what the correct address is and you jumped on me, apparently for not reading back to 2018. Let it rest.
 

jpanhalt

Joined Jan 18, 2008
11,087
What? He posted "org 2100" in post 393. And nobody posted anything after that telling him the correct address is F000. You just posted to say "I thought of it first!" Seriously?
Let's end this now. It is quite common when someone cross posts after putting work into a reply to make that acknowledgment. Has nothing to do with claiming priority. In fact, I learned that tidbit from someone else on this forum.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
Question, how does one specify within code the four words available as User ID info?

According to the datasheet, said words lie in the address range of 8000h-8003h. And since each word is 14 bits long, how is one supposed to define them?

Would it be something similar to how the CONFIG words are defined within the file?
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
After some searching, I think I found the way. It goes something like this:

Code:
USER_ID:      ORG    0x8000          ;Four words available for User ID purposes
       da 0x0AA, 0x1BB, 0x2CC, 0x3DD
 

JohnInTX

Joined Jun 26, 2012
4,787
USER_ID: ORG 0x8000 ;Four words available for User ID purposes da 0x0AA, 0x1BB, 0x2CC, 0x3DD
That works.
You also can use the __idlocs directive and let the assembler figure out the addressing and format:
Code:
 __idlocs _IDLOC0,0aah        ; 8000h = 0x00aa
 __idlocs _IDLOC1,1bbh        ; 8001h = 0x01bb
 __idlocs _IDLOC2,2cch        ; 8002h = 0x02cc
 __idlocs _IDLOC3,3ddh        ; 8003h = 0x03dd
It's more portable and MPASM friendly. Either way, you can verify it's correct by viewing the User ID locs in MPLABX.

Have fun!
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
Question, anyone here care to share the code to calculate CRC? I intend to use it for UART purposes. I wrote a routine a while back for the 8051 that I guess I could translate, but it's based on a lookup table and it delivers an 8 bit result. And since the PIC16 works using 14 bit words, the look up table technique would be a bit tricky to implement, I think. What I've found online are algorithms that give results of 16 bits and up only.
 

jpanhalt

Joined Jan 18, 2008
11,087
PICLIst has some code for parity here: http://www.piclist.com/techref/search.asp#result (I would post it, but the organization discourages "ripping." I highlight, copy, paste to notebook and then use with attribution.)

I have not looked carefully at that code. Often the code there can be improved slightly by using the instruction set for enhanced mid-range chips, including manipulations on WREG. For my use (SPI communication), I just use parity ("one-bit CRC"). I can share that if it is of interest.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
I am now trying to transmit a string through the UART, and for that purpose I've written the following routine, but I'm getting garbage:

Code:
;*****************************************************************************************
;                    Transmit a 14-bit compact format string through
;                       the UART that is being pointed at by FSR0
;          Transmission will stop when one of the characters is null (BYTE = 0)
;*****************************************************************************************
Transmit_String:
      movlw low(Test_String01)    ;make FSR0 point to the string located at LABEL
      movwf FSR0L
      movlw high(Test_String01)
      movwf FSR0H
     
      ;remember that each 14-bit word is stored into two 7-bit bytes

Transmit_String_Loop:
      moviw FSR0++                ;load w with the byte pointed at by FSR0 and point to
                                  ;the next byte afterwards

      movwf GPT00                 ;copy W into GPT00
      rlf GPT00, f                ;rotate GPT00 to the left, to make space and bring
                                  ;bit 7 of the next byte into bit 0 of this register
     
      moviw FSR0++                ;load w with the byte pointed at by FSR0 and point to
                                  ;the next byte afterwards
      movwf GPT01                 ;copy W into GPT01
      btfsc GPT01, 7D             ;if bit 7 of GPT01 is set, then set bit 0 of GPT00
        goto Set_Bit_0_TV1

Clr_Bit_0_TV1:                    ;otherwise clear bit 0 of GPT00
      bcf GPT00, 0D
      goto Cont_Transmit_String

Set_Bit_0_TV1:
      bsf GPT00, 0D               ;set bit 0 of GPT00

Cont_Transmit_String:
      bcf GPT01, 7D               ;bit 7 of GPT01 is ALWAYS 0

      movf GPT00, w               ;load GPT00 into W, but do not transmit it
      btfsc STATUS, Z             ;if its value is zero
       goto Exit_Transmit_String
      call Transmit_W             ;transmit GPT00 through the UART

      movf GPT01, w               ;load GPT01 into W, but do not transmit it
      btfsc STATUS, Z             ;if its value is zero
       goto Exit_Transmit_String
      call Transmit_W             ;transmit GPT01 through the UART

     goto Transmit_String_Loop    ;keep transmitting characters until one of them is zero

Exit_Transmit_String:

    return

;*****************************************************************************************
;     Test strings repostitory, these strings are stored as compacted 14 bit words
;*****************************************************************************************
Test_String01: da "Greetings, Ladies and Gentelmen!", 00H
I'm using 14-bit packed string format in program memory, and this routine is my attempt to decode it.

I'm sure my problem has to do with the way I'm handling FSR0 somehow... any hints?
 
Last edited:

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
Stupid me... after giving the datasheet a more thorough read, I just realized that reading program memory using indirect addressing can only (and only) read the lower byte of a 14-bit word. DANG IT!

Right now I'm rewriting my code so as to use the "bsf EECON1, RD" instruction for that purpose. Which is supposed to retrieve both bytes of said word in the EEDATH and EEDATL registers.

I truly don't like this Harvard architecture... but I'm refraining from using the term "stupid" to refer to it because I'm sure there must be a darn good reason for things being the way they are. And I confess myself a neophyte.

Still... I miss my 8051 babies...
 

jpanhalt

Joined Jan 18, 2008
11,087
I was coming to the same conclusion, but had to take time to set up the project. While FSR0 is 16 bits so it can point to the entire memory, I was concerned that INDF0 was only 8 bits. INDF0 is not a physical register, but in your code you move to WREG, which is only 8 bits.

One small point: You are using rlf without checking STATUS,0 first.
Code:
rlf GPT00, f                ;rotate GPT00 to the left, to make space and bring
That will load or clear STATUS,0 but will also move the existing carry bit to GPT00,0. It is likely to be clear, but can you be certain? If it matters, I will either clear it before the first rotate or use lslf which puts bit 7 into carry, but doesn't move carry into f.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
There you go, I finally made it work. I'm posting my code here for future neophytes seeking help. Strangely, I couldn't make it work by working on the values reported and stored in EEDATL and EEDATH directly. They're supposed to behave like any other register, right? It wasn't until I copied their contents into GPT00 and GPT01 and worked on them that things broke through.

Code:
;*****************************************************************************************
;                    Transmit a 14-bit compact format string through 
;                       the UART that is being pointed at by FSR0
;          Transmission will stop when one of the characters is null (BYTE = 0)
;                       GPT00 and GPT01 are located in Common RAM
;*****************************************************************************************
Transmit_String:
      banksel EEADRL             ;Bank 3
      movlw low(Test_String01)   ;make EEADR point to the string located at LABEL
      movwf EEADRL
      movlw high(Test_String01)
      movwf EEADRH
      
      bcf EECON1, CFGS           ;do not select configuration memory
      bsf EECON1, EEPGD          ;point to program memory

Transmit_String_Loop:
      bcf INTCON, GIE            ;disable interrupts
      bsf EECON1, RD             ;initiate read
      nop                        ;executed \ instructions per
      nop                        ;ignored  / the datasheet

      bsf INTCON, GIE            ;re-enable interrupts
      
     ;the 14-bit word is now stored in EEDATL and EEDATH
      movf EEDATL, w
      movwf GPT00
      movf EEDATH, w
      movwf GPT01

      rlf GPT01, f               ;rotate GPT01 to the left, to make space and bring 
                                 ;bit 7 of the next byte into bit 0 of this register

      btfsc GPT00, 7D            ;if bit 7 of GPT00 is set, then set bit 0 of GPT01
        goto Set_Bit_0_GPT01

Clr_Bit_0_GPT01:                 ;otherwise clear bit 0 of GPT01
       bcf GPT01, 0D
      goto Cont_Transmit_String

Set_Bit_0_GPT01:
      bsf GPT01, 0D              ;set bit 0 of GPT00

Cont_Transmit_String:
      bcf GPT00, 7D              ;bit 7 of GPT00 is ALWAYS 0

      movf GPT01, w              ;load GPT01 into W, but do not transmit it 
      btfsc STATUS, Z            ;if its value is zero
       goto Exit_Transmit_String
      call Transmit_W            ;transmit GPT01 through the UART

      movf GPT00, w              ;load GPT00 into W, but do not transmit it 
      btfsc STATUS, Z            ;if its value is zero
       goto Exit_Transmit_String
      call Transmit_W            ;transmit GPT00 through the UART

      banksel EEADRL             ;Bank 3 (Transmit_W returns with active Bank 0)      
      incf EEADRL                ;point to the next 14-bit word
      btfsc STATUS, Z
      incf EEADRH

     goto Transmit_String_Loop   ;keep transmitting characters until one of them is zero

Exit_Transmit_String:
     
     movlb 0                      ;set active Bank 0 before leaving 
    return


;*****************************************************************************************
;     Test strings repostitory, these strings are stored as compacted 14 bit words
;*****************************************************************************************
Test_String01: da "Greetings, Ladies and Gentlemen!", 00H
Test_String02: da "Odd", 00H
Test_String03: da "Even", 00H
Test_String04: da "Go soak your face", 00H
1607631027921.png
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
A quick question: how can the PIC16L1823 pins be configured so that they can function bidirectionally?

I'm thinking that they should be configured as inputs with their internal weak pull ups enabled and their LATAx/LATCx bits set high. From there one could just read the RAx/RCx bits to read their state. And set or clear the LATAx/LATCx bits if one wanted to output a 1 or a 0 accordingly, making sure that they're set back to 1 so that they keep functioning as inputs afterwards. But something tells me that it's not that simple. This because I'm assuming that they work in a similar way as the 8051 architecture.
 

trebla

Joined Jun 29, 2019
599
Input/output direction is set via TRISx registers, look at the port diagrams in datasheet. If the port pin is set to input, nothing happens if you write to LATx register, the pin stays in high impedance state.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
Input/output direction is set via TRISx registers, look at the port diagrams in datasheet. If the port pin is set to input, nothing happens if you write to LATx register, the pin stays in high impedance state.
That's what I figured ... unfortunately, it seems that I'm going to have to play around with the TRISx registers to get what I want. In this case, I'm bit-banging an I²C communications protocol in non standard pins.
 
Top