PIC18F26K22 Unexpected file register behaviour - Page 1
Forum section: electronics / general

author: rollingstone
author_rank: Contributor
timestamp: November 21, 2016, 12:37:00 pm
content: Hello everybody.I have a problem and I can't solve it myself, but tried really hard.I am programming PIC18F26K22 in assembler using Mplab and PicKIT3.In my program i use temporary values, which need to be stored on location 070h.So I use this command to link RXTEMP to location 070h:RXTEMP EQU 070h ; temporary USART received char register.Everything works fine, I can put any value to RXTEMP. For example I can do "INCF RXTEMP" and it's value increases (as expected)But IF I run some code (which is from PIC18F26K22 datasheet) before working with 070h register, then it starts to behave unexpectedly.The code is this:MOVLB 0xF ; Set BSR for banked SFRsCLRF PORTA ; Initialize PORTA by; clearing output; data latchesCLRF LATA ; Alternate method; to clear output; data latchesMOVLW 0E0h ; Configure I/OMOVWF ANSELA ; for digital inputsMOVLW B'00000000' ; Value used to; initialize dataMOVWF TRISA ; directionFor example, in the beginning RXTEMP value is 00h.The command "INCF RXTEMP" does not work (it does not increases RXTEMP value as expected).Also I can't write any value to RXTEMP using MOVFF command. So it's value stays 00h.But if I change RXTEMP location to any other for example 071h it starts working as expected.And i also noticed if i remove folowing line from the initialization code above:MOVLB 0xF ; Set BSR for banked SFRsThen register RXTEMP at location 070h also starts working as expected. But Then another problem happens (USARTdoes not receive characters)So what kind of black hole is 070h location in RAM?I can change RXTEMP location to 071h or any other it is not a problem to me, but I want to understand what is wrong here, should I avoid 070h ?Thank You for any information.Best regards,

author: AndyC_772
author_rank: Super Contributor
timestamp: November 21, 2016, 12:54:09 pm
content: http://ww1.microchip.com/downloads/en/DeviceDoc/40001412G.pdfSection 5.4.1, Bank Select Register. I'm willing to bet you're not setting it correctly before each RAM or SFR access.

author: zzattack
author_rank: Regular Contributor
timestamp: November 21, 2016, 01:13:44 pm
content: I believe 0x70 is still in access RAM so it's not as much a matter of incorrect BSR as selecting the correct mode for the instruction.Code:INCF RXTEMP,F,ACCESS^should work, but it is very important you are aware of banked registers and common RAM!

author: rollingstone
author_rank: Contributor
timestamp: November 21, 2016, 06:52:50 pm
content: QuoteThank you, I'm really not setting it correctly. Tried to put "MOVLB 0x0" command and it started working as expected.QuoteWell it should be in access RAM, but debugger shows something different.QuoteIt still doesn't work. But it does work when I use "MOVLB 0x0" command before, and "INCF RXTEMP" starts to work, as well as "INCF RXTEMP,1,1" ... strange...So I guess I should rewrite all my code and add "MOVLB" instructions everywhere?

author: rollingstone
author_rank: Contributor
timestamp: November 21, 2016, 07:01:51 pm
content: QuoteCorrection: if I understood datasheet corectly then 070h is not in access ram.

author: Andy Watson
author_rank: Super Contributor
timestamp: November 21, 2016, 08:04:01 pm
content: Quote from: rollingstone on November 21, 2016, 12:37:00 pmAre you using interrupts ?

author: rollingstone
author_rank: Contributor
timestamp: November 21, 2016, 09:30:34 pm
content: Yes, i am

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: November 21, 2016, 11:01:17 pm
content: Quote from: rollingstone on November 21, 2016, 12:37:00 pmThere is a conflict between this code and your UART routines. It may be due to the UART code not setting BSR, or trying to use the same memory location for some other purpose (can't tell without seeing all your code).In any case, assigning RAM locations with EQU is not good practice. You should use CBLOCK and ENDC to allocate RAM space, that way each variable automatically gets its own address and there is less chance of a conflict between different parts of your program. For example:-Code:; initializationCBLOCK 0x70 ; my variables start at 0x70RXTEMP ; RXTEMP at 0x70 (1 byte)TMP16:2 ; TMP16 at 0x71 (2 bytes)ENDC ; next CBLOCK starts at 0x73...; UART variablesCBLOCK ; continues from last CBLOCKUART_VAR ; UART_VAR at 0x73 (1 byte)UART_BUFFER:8 ; buffer at 0x74 (8 bytes)ENDCInstead of MOVLB use the directive BANKSEL {SFR name or bank#}, which avoids having to remember which bank an SFR is in and is more portable between different PICs.RAM below 0x60 can be accessed directly by instructions that have the 'ACCESS' option, so you should allocate your variables in this area if possible. SFRs between 0x60 and 0xFF are also in the 'access' bank (unfortunately this doesnotinclude certain UART registers). The less you fiddle with the BSR the more efficient your code will be. However in assembler it is easy to lose track of which bank you are in, so for safety it is best to set it before any SFR accessexceptwhen you need the tightest possible code.Avoid using 'magic' numbers, and write your code so that it can be easily modified without the risk of making silly mistakes. For example, instead of:-Code:MOVLW 0E0h ; Configure I/OMOVWF ANSELA ; for digital inputsMOVLW B'00100100' ; Value used to; initialize dataMOVWF TRISA ; directiondo this:-Code:#define LED_1 RA0 ; pin 2 Red LED output#define LED_2 RA1 ; pin 3 Green LED output#define SW_1 RA2 ; pin 4 switch input; RA3 ; pin 5 (unused output); RA4 ; pin 6 (unused output)#define ADC_IN RA5 ; pin 7 battery voltage inputMOVLW (1<<ADC_IN) ; bit = 1 for analog input, else digitalBANKSEL ANSELAMOVWF ANSELA ; set PORTA analog inputsMOVLW (1<<SW1)|(1<<ADC_IN) ; bit = 1 for input, else outputBANKSEL TRISAMOVWF TRISA ; set PORTA digital I/OIt may seem like a lot more work initially, but will make your program a lot easier to expand and maintain. You can easily reallocate pin functions without having to hunt down and change a bunch of 'magic' numbers, and the code is much easier to read and understand.

author: rollingstone
author_rank: Contributor
timestamp: November 28, 2016, 12:06:31 pm
content: QuoteYes, it looks like it was. I used BANKSEL before each variable and SFR and then everything worked fine.Thank you for such valuable information, I changed my code according to Your suggestions. However I would like to ask one more question. Sorry if question is too long but I am still confused and can't sort it myself.If I am declaring variable like this:Code:UART_BUFFER:8 ; buffer at 0x74 (8 bytes)Is it possible to access this variable (each byte) somehow in simple way? for example not using file select registers and doing table reads and writes?But if not, If I must useCode:LFSR FSR, Fcommand to load address andCode:TBLRD*+command to read or commandCode:PLUSW0to write, then how can my program automatically can get this variable star and end address before doing LFSR? The point is that I want to declare for ex.Code:UART_buffer:32variable using CBLOCK directive and then be able to access it in program automatically without having to LFSR manually. For now I do it like this:Code:RAM_WRT_CHR; put received char to ramBANKSELRXINDEX; select bankMOVFFRXINDEX,WREG; load RXINDEX value to WLFSRFSR0,090h; load UART buffer adressBANKSELRXTEMP_BYTE; select bankMOVFFRXTEMP_BYTE,PLUSW0; write received char from temp reg. to location 90h + W (W is offset)MOVFFRXTEMP_BYTE,TXREG1; echo char backBANKSELRXINDEX; select bankINCFRXINDEX; increase UART buffer indexAnd here I must load UART buffer beginning address manually (090h). If I decide to change it's location address in RAM, then I must change it everywhere in program where it is being accessed.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: November 28, 2016, 05:18:32 pm
content: Quote from: rollingstone on November 28, 2016, 12:06:31 pmTable read/write is only for program memory (ROM).QuoteIt's assembly code, so of course you have to load the FSR 'manually'. But only once.QuoteThat's what symbolic names are for! Don't hard-code the address, instead do this:-Code:CLBOCKUART_BUFFER:32ENDCLFSR FSR0,UART_BUFFER..LFSR FSR1,UART_BUFFERNow the assembler 'automatically' knows the address of the buffer, and you don't have to change it everywhere in your program.QuoteThat code should work OK, but it's not the most efficient way to do it. If you don't need to do arithmetic on the index then you can just post-increment the FSR itself. Of course you still need to make sure it stays within the buffer RAM area, so you can either check for the buffer end by comparing it with UART_BUFFER+32, or for the ultimate brevity and speed do this:-Code:CLBOCK 0x90 ; must be aligned on 32 byte boundary with bit 5 clear!UART_BUFFER:32 ; 32 byte UART receive bufferENDC; init FSR0LFSR FSR0,UART_BUFFER; receive characterMOVFF RCREG1,POSTINC0 ; receive character, increment pointerBCF FSR0,5 ; force pointer to stay inside 0x90~0xAFTo save a few cycles I copy the received byte directly from the UART into the buffer. The buffer is aligned on a 32 byte boundary, so to stop the FSR from going beyond it I simply stop bit 5 from changing. Resetting bit 5 forces the FSR back to 0x90 when it increments to 0xB0. A circular buffer in just two instructions!