• AVR Freaks

Hot!Finding the address of a const variable, PIC24FJ64GB004?

Page: < 12 Showing page 2 of 2
Author
AndersG
Super Member
  • Total Posts : 241
  • Reward points : 0
  • Joined: 2008/08/05 04:51:24
  • Location: 0
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/10 06:52:07 (permalink)
0
If you learn how to use the self programming you will be able to work with it. grin
 
Absolutely! And if not for anything else, to help some other poor sod that stumbles in here :) So, this is what I came up with:
 
// PIC24FJ64GB004 Family
//
// Example code to store a config value in program memory.
// To be on the safe side, you need to erase before writing and you erase
// one page at a time so you will end up wasting a lot of memory if you only
// want to store one integer.
//
// On PIC24FJ the config page is last, so use the next to last page
//
//
// How to use:
// Call FlashErase first, then FlashWrite
//
#include <xc.h>
// This word should be in program flash. Next to last page
const int __attribute__((address(0x1234567))) config;

void FlashWrite(int w, char b);
void FlashErase(unsigned long progAddr);
//
// C example using MPLAB C30, from DS39940D-page 61
//
void FlashWrite(int w, char b)
{
    unsigned int offset;
    unsigned long progAddr = 0x0; // Address of word to program
    unsigned int progDataL = w; // Data to program lower word
    unsigned char progDataH = b; // Data to program upper byte

    //Set up NVMCON for word programming
    NVMCON = 0x4003; // Initialize NVMCON
    //Set up pointer to the first memory location to be written
    //TBLPAG = progAddr>>16; // Initialize PM Page Boundary SFR
    TBLPAG = __builtin_tblpage(&config);
    //offset = progAddr & 0xFFFF; // Initialize lower word of address
    offset = __builtin_tbloffset(&config);
    //Perform TBLWT instructions to write latches
    __builtin_tblwtl(offset, w); // Write to address low word
    __builtin_tblwth(offset, b); // Write to upper byte
    asm("DISI #5"); // Block interrupts with priority < 7
    // for next 5 instructions
    __builtin_write_NVM(); // C30 function to perform unlock
    // sequence and set WR
}

void FlashErase(unsigned long progAddr)
{
    // C example using MPLAB C30
    //unsigned long progAddr = 0xXXXXXX; // Address of row to write
    unsigned int offset;
    //Set up pointer to the first memory location to be written
    TBLPAG = progAddr>>16; // Initialize PM Page Boundary SFR
    offset = progAddr & 0xFFFF; // Initialize lower word of address
    __builtin_tblwtl(offset, 0x0000); // Set base address of erase block
    // with dummy latch write
    NVMCON = 0x4042; // Initialize NVMCON
    asm("DISI #5"); // Block all interrupts with priority <7
    // for next 5 instructions
    __builtin_write_NVM(); // C30 function to perform unlock
    // sequence and set WR
}

 
So, my question now is how do I find the address of the next to last program page? Ie the 0x1234567/progAddr? This pic has 64K of memory (bytes), but then it is actually made up of integers, so, how do I calculate the address of the next to last page?
#21
AndersG
Super Member
  • Total Posts : 241
  • Reward points : 0
  • Joined: 2008/08/05 04:51:24
  • Location: 0
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/10 06:53:39 (permalink)
0
If this is what you wanted, please accept my apologies for bringing up the erasing issue.
No need to apologise. I had completely missed the fact that you, to program a single integer, needed to erase the whole page.
#22
NorthGuy
Super Member
  • Total Posts : 6411
  • Reward points : 0
  • Joined: 2014/02/23 14:23:23
  • Location: Northern Canada
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/10 11:47:47 (permalink)
5 (1)
AndersG
So, my question now is how do I find the address of the next to last program page?



One page is 0x400 long. The config ends at 0xac00, so the last page starts at 0xa800. Hence the next to last one is at 0xa400.
#23
jtemples
عُضْوٌ جَدِيد
  • Total Posts : 12032
  • Reward points : 0
  • Joined: 2004/02/13 12:31:19
  • Location: Southern California
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/10 18:36:12 (permalink)
5 (2)
I'm joining very late, but Microchip has an EEPROM emulation library for the PIC24.
#24
AndersG
Super Member
  • Total Posts : 241
  • Reward points : 0
  • Joined: 2008/08/05 04:51:24
  • Location: 0
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/11 00:44:12 (permalink)
0
One page is 0x400 long. The config ends at 0xac00, so the last page starts at 0xa800. Hence the next to last one is at 0xa400.
OK. Presumably I learn that from the linker file, right?
 
__CONFIG4 = 0xABF8;
__CONFIG3 = 0xABFA;
__CONFIG2 = 0xABFC;
__CONFIG1 = 0xABFE;

 
The last config word is at 0xabfe.. 0xabff, next page would be at 0xac00, then (as you say) "the last page starts at 0xa800. Hence the next to last one is at 0xa400."
 
So. This should work, but I must test:
 
// PIC24FJ64GB004 Family
//
// Example code to store a config value in program memory.
// To be on the safe side, you need to erase before writing and you erase
// one page at a time so you will end up wasting a lot of memory if you only
// want to store one integer.
//
// On PIC24FJ the config page is last, so use the next to last page
//
//
// How to use:
// Call FlashErase first, then FlashWrite
//
#include <xc.h>
// This word should be in program flash. Next to last page. Here is how you
// arrive at the corretc address:
//
// 1. Open the linker file for the desired MPU
// 2. Find the define for CONFIG1:
//
// __CONFIG4 = 0xABF8;
// __CONFIG3 = 0xABFA;
// __CONFIG2 = 0xABFC;
// __CONFIG1 = 0xABFE;
//
// Config1 is the last word of the config block. Occupying 0xABFE..0xABFF
//
// 3. Next block would be at 0xAC00, so the config block starts at that
// minus 0x400, in this case 0xA800 and the next to last page is 0xA400
//
const int __attribute__((address(0xa400))) config;

void FlashWrite(int w, char b);
void FlashErase(unsigned long progAddr);
//
// C example using MPLAB C30, from DS39940D-page 61
//
void FlashWrite(int w, char b)
{
    unsigned int offset;
    unsigned long progAddr = 0x0; // Address of word to program
    unsigned int progDataL = w; // Data to program lower word
    unsigned char progDataH = b; // Data to program upper byte

    //Set up NVMCON for word programming
    NVMCON = 0x4003; // Initialize NVMCON
    //Set up pointer to the first memory location to be written
    //TBLPAG = progAddr>>16; // Initialize PM Page Boundary SFR
    TBLPAG = __builtin_tblpage(&config);
    //offset = progAddr & 0xFFFF; // Initialize lower word of address
    offset = __builtin_tbloffset(&config);
    //Perform TBLWT instructions to write latches
    __builtin_tblwtl(offset, w); // Write to address low word
    __builtin_tblwth(offset, b); // Write to upper byte
    asm("DISI #5"); // Block interrupts with priority < 7
    // for next 5 instructions
    __builtin_write_NVM(); // C30 function to perform unlock
    // sequence and set WR
}

void FlashErase(unsigned long progAddr)
{
    // C example using MPLAB C30
    //unsigned long progAddr = 0xXXXXXX; // Address of row to write
    unsigned int offset;
    //Set up pointer to the first memory location to be written
    TBLPAG = progAddr>>16; // Initialize PM Page Boundary SFR
    offset = progAddr & 0xFFFF; // Initialize lower word of address
    __builtin_tblwtl(offset, 0x0000); // Set base address of erase block
    // with dummy latch write
    NVMCON = 0x4042; // Initialize NVMCON
    asm("DISI #5"); // Block all interrupts with priority <7
    // for next 5 instructions
    __builtin_write_NVM(); // C30 function to perform unlock
    // sequence and set WR
}

#25
NKurzman
A Guy on the Net
  • Total Posts : 19040
  • Reward points : 0
  • Joined: 2008/01/16 19:33:48
  • Location: 0
  • Status: online
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/11 00:57:37 (permalink)
5 (1)
Try it.
Note if you use the align() __attribute__
The compiler well place your variable on a page boundary. But it will not be At a fixed location.
#26
JPortici
Super Member
  • Total Posts : 1208
  • Reward points : 0
  • Joined: 2012/11/17 06:27:45
  • Location: Grappaland
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/11 07:09:45 (permalink)
5 (1)
ric
and overwrite the old value with 0x000000, and make sure that value is ignored.

unless the device in question has ECC memory
#27
AndersG
Super Member
  • Total Posts : 241
  • Reward points : 0
  • Joined: 2008/08/05 04:51:24
  • Location: 0
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/11 07:28:23 (permalink)
0
Try it.
I did and it did not work. The write did nothing and the erase crashed. Presumably did I overwrite myself :)
 
#pragma config POSCMOD = HS // Primary Oscillator Select (HS Oscillator mode selected)
#pragma config I2C1SEL = PRI // I2C1 Pin Location Select (Use default SCL1/SDA1 pins)
#pragma config IOL1WAY = ON // IOLOCK Protection (Once IOLOCK is set, cannot be changed)
#pragma config OSCIOFNC = OFF // Primary Oscillator Output Function (OSC2/CLKO/RC15 functions as CLKO (FOSC/2))
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor (Clock switching and Fail-Safe Clock Monitor are disabled)
#pragma config FNOSC = FRCPLL //
#pragma config SOSCSEL = SOSC // Sec Oscillator Select (Default Secondary Oscillator (SOSC))
#pragma config WUTSEL = LEG // Wake-up timer Select (Legacy Wake-up Timer)
#pragma config IESO = ON // Internal External Switch Over Mode (IESO mode (Two-Speed Start-up) enabled)

// CONFIG1
#pragma config WDTPS = PS32768 // Watchdog Timer Postscaler (1:32,768)
#pragma config FWPSA = PR128 // WDT Prescaler (Prescaler ratio of 1:128)
#pragma config WINDIS = ON // Watchdog Timer Window (Standard Watchdog Timer enabled,(Windowed-mode is disabled))
#pragma config FWDTEN = OFF // Watchdog Timer Enable (Watchdog Timer is disabled)
#pragma config ICS = PGx1 // Comm Channel Select (Emulator EMUC1/EMUD1 pins are shared with PGC1/PGD1)
#pragma config GWRP = OFF // General Code Segment Write Protect (Writes to program memory are allowed)
#pragma config GCP = OFF // General Code Segment Code Protect (Code protection is disabled)
#pragma config JTAGEN = OFF // JTAG Port Enable (JTAG port is disabled)

#define FCY 16000000 //Instruction clock speed (Fosc/2)

#ifdef USE_HI_SPEED_BRG
    #define BRG_DIV 4
#else
    #define BRG_DIV 16
#endif

#define BAUDRATE 115200
#define BAUDRATEREG ((FCY + (BRG_DIV/2*BAUDRATE))/BRG_DIV/BAUDRATE-1)

#include <xc.h>
#include <stdio.h>
#include "memory.h"

#define VERSION "0.1"

extern const int __attribute__((space(psv), address (0xa400))) config;

int main(void)
{
    //
    // Set 8MHz clock FRC. PLLDIV=2, gives 4MHz. Fed to PLL gives 96MHz
    // divided by 3 gives 32MHz cpu clock
    //
    CLKDIVbits.RCDIV = 0; // FRC Postscaler Select bits, divide by 0
                            // ie FRCDIV is 8MHz, same as FRC
                            // PLLDIV is set in config bits to 2 (system.c)
    
    // UART
    _U1RXIE = 0;
 _U1TXIE = 0;
    //unlock registers
    __builtin_write_OSCCONL(OSCCON & 0xBF);
    // Configure Output Functions (Table 9-2)
    // Assign U1TX To Pin RP11
    // Assign U1RX To Pin RP10
    RPOR5bits.RP11R = 3;
    TRISBbits.TRISB11 = 0; // output
    RPINR18bits.U1RXR = 10;
    TRISBbits.TRISB10 = 1; // Input
 // Initialize UART1
    U1BRG = BAUDRATEREG;
 _UARTEN = 1;
 _UTXEN = 1;
    //
    printf("\r\nTest program %s Starting. %s %s", VERSION, __DATE__, __TIME__);
    printf("\r\nStored config is %X", config);
    //
    printf("\r\nErasing...");
    FlashErase(0xa400);
    printf("\r\nWriting...");
    FlashWrite(0x1234, 0x56);
    //
    printf("\r\nStored config is %X", config);


    while(1)
        ;
    return 0;
}

 
I have attached the project. Note that I have changed the PIC to another, but similar
#28
NKurzman
A Guy on the Net
  • Total Posts : 19040
  • Reward points : 0
  • Joined: 2008/01/16 19:33:48
  • Location: 0
  • Status: online
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/11 08:30:49 (permalink)
5 (1)
How did you tell the write did nothing?
You can’t just look at it in Mplab. You have to read back.
Your variable must take up the entire Flash page.
So it needs to be a struct or an array. And it must be the size of the page.
Unless you reserved it in the linker script.
#29
NorthGuy
Super Member
  • Total Posts : 6411
  • Reward points : 0
  • Joined: 2014/02/23 14:23:23
  • Location: Northern Canada
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/11 09:52:01 (permalink)
5 (1)
It is a possibility that you overwrite the program with erase. You only reserved an int. The rest of the page may be used for your program which you erase. You need to reserve something which is 1024 addresses long.
#30
T Yorky
Super (Thick) Member
  • Total Posts : 574
  • Reward points : 0
  • Joined: 2012/08/28 02:07:35
  • Location: UK
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/11 10:26:26 (permalink)
5 (1)
@ Andersg, I don't really want to say that isn't the way to do it because It is not the way I approached it from day 1.
I think you may be confusing the compiler by telling it const. I am sure you are familiar with these chips from what you have written but.. my take.
The compiler will allocate your named constant into the PSV at that address and set up the PSV access to suit. All other constants will be bundled in starting at that address. Mayhem.
When performing the flash rewrite I approached in the manner I had to perform all the memory management.
That is linker script unfortunately. And the use of table reads/writes. And just to warn when changing the table page register, just like in any system, you must replenish it with its contents after you have finished. If you are using constants in your interrupts, you will need to disable ints while you change. There are modifiers to handle this. But different subject.
Using linker script is the way in my opinion. Find the GLD for your chip in the compiler sub folders. Copy to your project folder with all your other modules. Add your initials to the file name (to make it unique and avoid accidental reference). Then add to your project under linker script.
Here is an example of a GB002 script was modified for one of my projects.
Note I have left the old 'program' active in that project but you will not. Comment out the 'program' and change the 'prog' below to 'program'
  program (xr)   : ORIGIN = 0x200,         LENGTH = 0xA9F6

  prog (xr)      : ORIGIN = 0x200,         LENGTH = 0x5A00
  flashpage (xr) : ORIGIN = 0x5C00,        LENGTH = 0x5000

  HMA (xr)       : ORIGIN = 0xA800,        LENGTH = 0x01F6

When you compile now you will see you have lost(!) some of the flash capacity in MPLabX. In my example above about 30K of flash was allocated to the self programming. You only need a single page.
Trust this assists.
T Yorky
 
#31
dan1138
Super Member
  • Total Posts : 4003
  • Reward points : 0
  • Joined: 2007/02/21 23:04:16
  • Location: 0
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/11 16:03:28 (permalink)
5 (1)
AndersG
Try it.
 
I did and it did not work. The write did nothing and the erase crashed. Presumably did I overwrite myself :)

It is real tricky to get all of this stuff to play nice together.
 
I looked in the code you posted to see that you as using a PIC25FJ64GA002 as a test target.
 
Here is a complete project that build and works (as far as I can tell) correctly:
/*
 * File:   main.c
 * Author: dan1138
 * Target: PIC24FJ64GA002
 * Compiler: XC16 v.140
 *
 * Created on October 11, 2020, 11:07 AM
 *
 *                            PIC24FJ64GA002                     
 *               +-----------------:_:-----------------+             
 *   ICD_VPP -> :  1 MCLR                     VDD  28 : <- 3v3     
 *           <> :  2 RA0/RP26/AN0             VSS  27 : <- GND     
 *           <> :  3 RA1/RP27/AN1   AN9 /RP15/RB15 26 : <> LED4    
 *   ICD_PGD <> :  4 RB0/PGD1       AN10/RP14/RB14 25 : <> LED5    
 *   ICD_PGC <> :  5 RB1/PGC1       AN11/RP13/RB13 24 : <> LED6    
 *           <> :  6 RB2/RP2/AN4    AN12/RP12/RB12 23 : <> LED7    
 *       POT <> :  7 RB3/RP3/AN5         RP11/RB11 22 : <>         
 *       GND -> :  8 VSS                 RP10/RB10 21 : <>         
 * 7.3728MHz <> :  9 RA2/OSCI                 VCAP 20 : <- 10uF    
 * 7.3728MHz <> : 10 RA3/OSCO                 VSS  19 : <- GND     
 * 32.768KHz <> : 11 RB4/SOSC            RP9 /RB9  18 : <> RXD
 * 32.768KHz <> : 12 RA4/SOSC            RP8 /RB8  17 : <> TXD
 *       3v3 -> : 13 VDD           INT0/ RP7 /RB7  16 : <>         
 *       SW1 <> : 14 RB5/RP5             RP6 /RB6  15 : <>         
 *               +-------------------------------------+             
 *                               DIP-28                           
 *
 * This application tested using the: DM300027 16-bit 28-pin Starter Board
 */
#if defined(__PIC24FJ64GA002__)
/*
 * Configuration words
 */
#define PLLMODE         1                      /* On-chip PLL setting */
#define INTERNAL_RC_OSC

#pragma config WDTPS = PS32768, FWPSA = PR128, WINDIS = OFF, FWDTEN = OFF, ICS = PGx1, GWRP = OFF, GCP = OFF, JTAGEN = OFF
#pragma config I2C1SEL = SEC, IOL1WAY = OFF, OSCIOFNC = OFF, FCKSM = CSDCMD, SOSCSEL = SOSC, WUTSEL = LEG, IESO = OFF

#ifdef INTERNAL_RC_OSC
/* Use this for internal (8.0000 MHz) oscillator */
#if (PLLMODE==4)
#pragma config FNOSC = FRCPLL, POSCMOD = NONE
#define XTFREQ          8000000L*4L
#else
#pragma config FNOSC = FRC, POSCMOD = NONE
#define XTFREQ          8000000L
#endif
#else
/* Use this for external (7.3728 MHz) crystal */
#if (PLLMODE==4)
#pragma config FNOSC = PRIPLL, POSCMOD = XT
#define XTFREQ          7372800L*4L
#else
#pragma config FNOSC = PRI, POSCMOD = XT
#define XTFREQ          7372800L
#endif
#endif
/*
 * Instruction clock frequency
 */
#define FCYC            (XTFREQ*PLLMODE/2)      /*Instruction Cycle Frequency */
#else
#define FCYC            (8000000ul)
#endif

#include <xc.h>
#include <stdio.h>
/*
 * UART1 configuration
 */
#define U1_BAUD 9600L
#define U1_BRGH_VALUE 0
/*
 * U1_BRGREG register value and baudrate error calculation
 */
#if U1_BRGH_VALUE
#define U1_BRGH_SCALE 4L
#else
#define U1_BRGH_SCALE 16L
#endif

#define U1_BRGREG ( ((FCYC + (U1_BRGH_SCALE * U1_BAUD)/2 )/(U1_BRGH_SCALE * U1_BAUD))-1L)

#if U1_BRGREG > 65535
#error Cannot set up UART1 for the FCYC and BAUDRATE.
#endif
/*
 * Check if baud error greater than 2.5 percent
 */
#define REAL_BAUDRATE ( FCYC / ( U1_BRGH_SCALE * ( U1_BRGREG + 1L) ) )
#if (REAL_BAUDRATE > (U1_BAUD + (U1_BAUD * 25L) / 1000L)) || (REAL_BAUDRATE < (U1_BAUD - (U1_BAUD * 25L) / 1000L))
#error UART baudrate error greater than 2.5 percent for the FCYC and U1_BAUD.
#endif
#undef REAL_BAUDRATE

/* define map input pin numbers */
enum
{
    RPI0  = 0,      /* pin RB00 */ /* PGC1 */
    RPI1,           /* pin RB01 */ /* PGD1 */
    RPI2,           /* pin RB02 */
    RPI3,           /* pin RB03 */
    RPI4,           /* pin RB04 */
    RPI5,           /* pin RB05 */
    RPI6,           /* pin RB06 */
    RPI7,           /* pin RB07 */
    RPI8,           /* pin RB08 */
    RPI9,           /* pin RB09 */
    RPI10,          /* pin RB10 */
    RPI11,          /* pin RB11 */
    RPI12,          /* pin RB12 */
    RPI13,          /* pin RB13 */
    RPI14,          /* pin RB14 */
    RPI15,          /* pin RB15 */
    RPI_NONE = 0x1f
};

/* define map output function numbers */
enum
{
    RPO_NONE = 0,
    RPO_C1OUT,
    RPO_C2OUT,
    RPO_U1TX,
    RPO_U1RTS,
    RPO_U2TX,
    RPO_U2RTS,
    RPO_SDO1,
    RPO_SCK1OUT,
    RPO_SS1OUT,
    RPO_SDO2,
    RPO_SCK2OUT,
    RPO_SS2OUT,
    RPO_OC1=18,
    RPO_OC2,
    RPO_OC3,
    RPO_OC4,
    RPO_OC5,
};
/*
 * Initialize this PIC
 */
void PIC_Init(void)
{
    /*
     * Disable all interrupt sources
     */
    __builtin_disi(0x3FFF); /* disable interrupts for 16383 cycles */
    IEC0 = 0;
    IEC1 = 0;
    IEC2 = 0;
    IEC3 = 0;
    IEC4 = 0;
    __builtin_disi(0x0000); /* enable interrupts */

    CLKDIV  = 0x0000; /* set for default clock operations */
    AD1PCFG = 0xffff; /* Set for digital I/O */

    _NSTDIS = 1;    /* disable interrupt nesting */
}
/* warning non-portable function */
/*
 * This function waits for the at least the
 * specified number milliseconds then returns.
 */
void delay( unsigned long wait_ms )
{
    for (;wait_ms; --wait_ms)
    {
        asm("    repeat  %0 \n"
            "    clrwdt     \n"
            : /* no outputs */
            : "r" ((unsigned short)(FCYC/1000-1))
            );
    }
}
/*
 * Delay for count of seconds
 */
void DelayForCountDown(unsigned short Count, char *String)
{
    printf("\r\nWaiting for %u seconds before %s",Count,String);
    do
    {
        printf("%u,",Count--);
        delay(1000);
    } while (Count);
    printf("%u\r\n\r\n",Count);
}
/*
 *
 * Function: UART1_Init
 *
 * Precondition: None.
 *
 * Overview: Setup UART1 module.
 *
 * Input: None.
 *
 * Output: None.
 *
 */
int __C30_UART = 1;     /* Select UART1 as the standard serial output */

void UART1_Init(void)
{
    #define _U1_BRGH   _BRGH
    #define _U1_UARTEN _UARTEN
    #define _U1_UTXEN  _UTXEN
    #define _U1_UTXBF  _UTXBF
    #define _U1_OERR   _OERR
    #define _U1_URXDA  _URXDA

    /* Set directions of UART IOs */
    _U1TXIE = 0;
    _U1RXIE = 0;
    _U1ERIE = 0;
    _U1RXIP = 0b100;
    _U1TXIP = 0b100;
    _U1ERIP = 0b100;

    U1MODE = 0;
    U1STA = 0;

    U1BRG = U1_BRGREG;
    _U1_BRGH = U1_BRGH_VALUE;
    _U1_UARTEN = 1;
    _U1_UTXEN  = 1;
    _U1RXIF = 0;        /* reset RX flag */
    /* Unlock Registers */
    __builtin_write_OSCCONL(OSCCON & 0xBF);

    _U1RXR  = RPI9;     /* UART1 RXD pin RP9/RB9 */
    _RP8R   = RPO_U1TX; /* UART1 TXD pin RP8/RB8 */

    /* Lock Registers */
    __builtin_write_OSCCONL(OSCCON | 0x40);
}
/*
 * The value 0xAC000 is a "magic number" found in the
 * data sheet and is the address of the start of the
 * unimplemented flash memory for the PIC24FJ64GA002.
 */
const __prog__ __attribute__((noload, space(prog), section("NextToLast"), address(0x0AC00-2048))) unsigned short Code24bits[512];
const __prog__ __attribute__((noload, space(prog), section("Last"),       address(0x0AC00-1024))) unsigned short Code24bits2[507];
/*
 * Write one instruction word at location Code24Bits
 */
#define NUM_INSTRUCTION_PER_ROW 64
void PutCode24bits(unsigned long Data)
{
    unsigned int page;
    unsigned int offset;
    
    /*
     * This wait is here to try to keep from burning
     * out the PIC while we debug this code.
     */
    DelayForCountDown(5,"write to flash\r\n");

    page   = __builtin_tblpage(&Code24bits);
    offset = __builtin_tbloffset(&Code24bits);
    NVMCON = 0x4001;
    TBLPAG = page;
    offset &= ~(2*NUM_INSTRUCTION_PER_ROW-1);
    do
    {
        __builtin_tblwtl(offset,0xFFFF);
        __builtin_tblwth(offset,0xFFFF);
        offset += 2;
    } while (offset & (2*NUM_INSTRUCTION_PER_ROW-1));
    
    offset = __builtin_tbloffset(&Code24bits);
     __builtin_tblwtl(offset,(unsigned short)(Data));
     __builtin_tblwth(offset,(unsigned char )(Data>>16));
     asm("DISI #5");
     __builtin_write_NVM();
}
/*
 * Read one instruction word at location Code24Bits
 */
unsigned long GetCode24bits(void)
{
    unsigned long result = 0;
    TBLPAG = __builtin_tblpage(&Code24bits);
    result = __builtin_tblrdh(__builtin_tbloffset(&Code24bits));
    result <<= 16;
    result |= __builtin_tblrdl(__builtin_tbloffset(&Code24bits));
    return result;
}
/*
 * Erase the block where Code24bits is located
 */
void EraseCode24bits(void)
{
    unsigned long result = 0;

    /*
     * This wait is here to try to keep from burning
     * out the PIC while we debug this code.
     */
    DelayForCountDown(10,"page erase\r\n");
    
    NVMCON = 0x4042;
    TBLPAG = __builtin_tblpage(&Code24bits);
    result = __builtin_tblrdh(__builtin_tbloffset(&Code24bits));
    result <<= 16;
    result |= __builtin_tblrdl(__builtin_tbloffset(&Code24bits));
     asm("DISI #5");
     __builtin_write_NVM();
}
/*
 * Test unit
 */
volatile unsigned long CheckIt;
int main(void)
{
    PIC_Init();
    UART1_Init();
    
    printf("\r\nPIC24FJ64GA002 Write to flash demo. Built " __DATE__ " at " __TIME__ "\r\n");
    
    CheckIt = GetCode24bits();
    if(CheckIt == 0xFFFFFF)
    {
        printf("CheckIt is blank\r\n");
        PutCode24bits(125);
        CheckIt = GetCode24bits();
        printf("CheckIt written as %lu\r\n",CheckIt);
    }
    else
    {
        printf("CheckIt is %lu\r\n",CheckIt);
        printf("No write to memory\r\n");
    }
    
    EraseCode24bits();
    CheckIt = GetCode24bits();
    printf("CheckIt is %06lX\r\n",CheckIt);
    /*
     * Application loop, embedded applications never return from main
     */
    for(;;)
    {
    }
    return 0;
}

Give this a shot in your hardware.
 
<EDIT>
 
Here's the same application ported and tested on the PIC24FJ64GB004:
/*
 * File:   main.c
 * Author: dan1138
 * Target: PIC24FJ64GB004
 * Compiler: XC16 v.140
 *
 * Created on October 12, 2020, 10:15 AM
 * Description:
 *
 *                                                          PIC24FJ64GB004
 *               +------------+               +------------+               +------------+               +------------+
 *     RXD <>  1 : RB9/PMPD3  :         <> 12 : RA10       :         <> 23 : RB2/PMPD2  :         <> 34 : RA4/SOSCO  :
 *         <>  2 : RC6/RP22   :         <> 13 : RA7        :         <> 24 : RB3/PMPWR  :         <> 35 : RA9        :
 *         <>  3 : RC7/PMPA0  :         <> 14 : RB14/RP14  :         <> 25 : RC0/AN6    :         <> 36 : RC3/RP19   :
 *         <>  4 : RC8/RP24   :         <> 15 : RB15/RP15  :         <> 26 : RC1/AN7    :         <> 37 : RC4/RP20   :
 *         <>  5 : RC9/RP25   :     GND -> 16 : AVSS       :         <> 27 : RC2/RP18   :         <> 38 : RC5/RP21   :
 *     GND <>  6 : DISVREG    :     3v3 -> 17 : AVDD       :     3v3 -> 28 : VDD        :     GND -> 39 : VSS        :
 *    10uf ->  7 : VCAP       :     VPP -> 18 : MCLR       :     GND -> 29 : VSS        :     3v3 -> 40 : VDD        :
 *     PGD <>  8 : RB10/PGD2  :         <> 19 : RA0/PMPD7  :         <> 30 : RA2/OSCI   :         <> 41 : RB5        :
 *     PGC <>  9 : RB11/PGC2  :         <> 20 : RA1/PMPD6  :         <> 31 : RA3/OSCO   :     3v3 -> 42 : VBUS       :
 *     3v3 -> 10 : VUSB       :         <> 21 : RB0/PMPD0  :         <> 32 : RA8        :         <> 43 : RB7/PMPD5  :
 *         <> 11 : RB13/PMPRD :         <> 22 : RB1/PMPD1  :         <> 33 : RB4/SOSCI  :     TXD <> 44 : RB8/PMPD4  :
 *               +------------+               +------------+               +------------+               +------------+
 *                                                             TQFP-44
 *
 */
/*
 * Configuration words
 */
#pragma config JTAGEN=OFF, GCP=OFF, GWRP=OFF, ICS=PGx2, FWDTEN=OFF, WINDIS=OFF, FWPSA=PR32, WDTPS=PS8192
#pragma config IESO=OFF, FNOSC=FRC, POSCMOD=NONE, PLL96MHZ=OFF, PLLDIV=DIV2, FCKSM=CSECMD, OSCIOFNC=ON
#pragma config I2C1SEL=PRI, WPEND=WPENDMEM, WPCFG=WPCFGDIS, WPDIS=WPDIS, WUTSEL=LEG, SOSCSEL=IO, WPFP=WPFP63
#pragma config IOL1WAY=OFF, DSWDTEN=OFF, DSBOREN=OFF, RTCOSC=LPRC, DSWDTOSC=LPRC, DSWDTPS=DSWDTPS6
/*
 * Target specific definitions
 */
#include <xc.h>
/*
 * Standard library definitions
 */
#include <stdio.h>
/*
 * Application specific definitions
 */
#define FOSC (8000000UL)
#define FCYC (FOSC/2UL)
/*
 * UART1 configuration
 */
#define U1_BAUD 9600L
#define U1_BRGH_VALUE 0
/*
 * U1_BRGREG register value and baudrate error calculation
 */
#if U1_BRGH_VALUE
#define U1_BRGH_SCALE 4L
#else
#define U1_BRGH_SCALE 16L
#endif

#define U1_BRGREG ( ((FCYC + (U1_BRGH_SCALE * U1_BAUD)/2 )/(U1_BRGH_SCALE * U1_BAUD))-1L)

#if U1_BRGREG > 65535
#error Cannot set up UART1 for the FCYC and BAUDRATE.
#endif
/*
 * Check if baud error greater than 2.5 percent
 */
#define REAL_BAUDRATE ( FCYC / ( U1_BRGH_SCALE * ( U1_BRGREG + 1L) ) )
#if (REAL_BAUDRATE > (U1_BAUD + (U1_BAUD * 25L) / 1000L)) || (REAL_BAUDRATE < (U1_BAUD - (U1_BAUD * 25L) / 1000L))
#error UART baudrate error greater than 2.5 percent for the FCYC and U1_BAUD.
#endif
#undef REAL_BAUDRATE

/* PIC24FJ64GB004 PPS input pin numbers */
enum
{
    RPI0  = 0,      /* pin RB00 PGD1 */
    RPI1,           /* pin RB01 PGC1 */
    RPI2,           /* pin RB02 */
    RPI3,           /* pin RB03 */
    RPI4,           /* pin RB04 */
    RPI5,           /* pin RA00 */
    RPI6,           /* pin RA01 */
    RPI7,           /* pin RB07 INT0/CN23 */
    RPI8,           /* pin RB08 */
    RPI9,           /* pin RB09 */
    RPI10,          /* pin RB10 */
    RPI11,          /* pin RB11 */
    RPI13 = 13,     /* pin RB13 (REFO output)*/
    RPI14,          /* pin RB14 */
    RPI15,          /* pin RB15 */
    RPI16,          /* pin RC00 */
    RPI17,          /* pin RC01 */
    RPI18,          /* pin RC02 */
    RPI19,          /* pin RC03 */
    RPI20,          /* pin RC04 */
    RPI21,          /* pin RC05 */
    RPI22,          /* pin RC06 */
    RPI23,          /* pin RC07 */
    RPI24,          /* pin RC08 */
    RPI25,          /* pin RC09 */
    RPI_NONE = 0x1f
};

/* PIC24FJ64GB004 PPS output function numbers */
enum
{
    RPO_NONE = 0,
    RPO_C1OUT,
    RPO_C2OUT,
    RPO_U1TX,
    RPO_U1RTS,
    RPO_U2TX,
    RPO_U2RTS,
    RPO_SDO1,
    RPO_SCK1OUT,
    RPO_SS1OUT,
    RPO_SDO2,
    RPO_SCK2OUT,
    RPO_SS2OUT,
    RPO_OC1,
    RPO_OC2,
    RPO_OC3,
    RPO_OC4,
    RPO_OC5,
    RPO_CTPLS=29,
    RPO_C3OUT
};
/*
 * Initialize this PIC
 */
void PIC_Init(void)
{
    unsigned int ClockSwitchTimeout;
    /*
     * Disable all interrupt sources
     */
    __builtin_disi(0x3FFF); /* disable interrupts for 16383 cycles */
    IEC0 = 0;
    IEC1 = 0;
    IEC2 = 0;
    IEC3 = 0;
    IEC4 = 0;
    IEC5 = 0;
    __builtin_disi(0x0000); /* enable interrupts */

    _NSTDIS = 1;    /* disable interrupt nesting */
    /*
     * Set system oscillator to 8MHz using the internal FRC.
     */
    CLKDIV = 0x0000;
    if(OSCCONbits.CLKLOCK == 0) /* if primary oscillator switching is unlocked */
    {
        __builtin_write_OSCCONH(0x01); /* select FRCPLL as primary clock source */
        __builtin_write_OSCCONL(OSCCON | (1<<_OSCCON_OSWEN_POSITION));
        /* wait for clock to switch */
        for(ClockSwitchTimeout = 60000; ClockSwitchTimeout; ClockSwitchTimeout--)
        {
            if(!OSCCONbits.OSWEN) break;
        }
        /* primary oscillator is now 8MHz for a 4MIPS instruction cycle */
    }
    /*
     * Make all pins digital GPIO
     */
    AD1PCFG = 0xffff; /* Set for digital I/O */
}
/* warning non-portable function */
/*
 * This function waits for the at least the
 * specified number milliseconds then returns.
 */
void delay( unsigned long wait_ms )
{
    for (;wait_ms; --wait_ms)
    {
        asm("    repeat  %0 \n"
            "    clrwdt     \n"
            : /* no outputs */
            : "r" ((unsigned short)(FCYC/1000-1))
            );
    }
}
/*
 * Delay for count of seconds
 */
void DelayForCountDown(unsigned short Count, char *String)
{
    printf("\r\nWaiting for %u seconds before %s",Count,String);
    do
    {
        printf("%u,",Count--);
        delay(1000);
    } while (Count);
    printf("%u\r\n\r\n",Count);
}
/*
 *
 * Function: UART1_Init
 *
 * Precondition: None.
 *
 * Overview: Setup UART1 module.
 *
 * Input: None.
 *
 * Output: None.
 *
 */
int __C30_UART = 1;     /* Select UART1 as the standard serial output */

void UART1_Init(void)
{
    #define _U1_BRGH   _BRGH
    #define _U1_UARTEN _UARTEN
    #define _U1_UTXEN  _UTXEN
    #define _U1_UTXBF  _UTXBF
    #define _U1_OERR   _OERR
    #define _U1_URXDA  _URXDA

    /* Set directions of UART IOs */
    _U1TXIE = 0;
    _U1RXIE = 0;
    _U1ERIE = 0;
    _U1RXIP = 0b100;
    _U1TXIP = 0b100;
    _U1ERIP = 0b100;

    U1MODE = 0;
    U1STA = 0;

    U1BRG = U1_BRGREG;
    _U1_BRGH = U1_BRGH_VALUE;
    _U1_UARTEN = 1;
    _U1_UTXEN  = 1;
    _U1RXIF = 0;        /* reset RX flag */
    /* Unlock Registers */
    __builtin_write_OSCCONL(OSCCON & 0xBF);

    _U1RXR  = RPI9;     /* UART1 RXD pin RP9/RB9 */
    _RP8R   = RPO_U1TX; /* UART1 TXD pin RP8/RB8 */

    /* Lock Registers */
    __builtin_write_OSCCONL(OSCCON | 0x40);
}
/*
 * The value 0xAC000 is a "magic number" found in the
 * data sheet and is the address of the start of the
 * unimplemented flash memory for the PIC24FJ64GB004.
 */
const __prog__ __attribute__((noload, space(prog), section("NextToLast"), address(0x0AC00-2048))) unsigned short Code24bits[512];
const __prog__ __attribute__((noload, space(prog), section("Last"),       address(0x0AC00-1024))) unsigned short Code24bits2[507];
/*
 * Write one instruction word at location Code24Bits
 */
#define NUM_INSTRUCTION_PER_ROW 64
void PutCode24bits(unsigned long Data)
{
    unsigned int page;
    unsigned int offset;
    
    /*
     * This wait is here to try to keep from burning
     * out the PIC while we debug this code.
     */
    DelayForCountDown(5,"write to flash\r\n");

    page   = __builtin_tblpage(&Code24bits);
    offset = __builtin_tbloffset(&Code24bits);
    NVMCON = 0x4001;
    TBLPAG = page;
    offset &= ~(2*NUM_INSTRUCTION_PER_ROW-1);
    do
    {
        __builtin_tblwtl(offset,0xFFFF);
        __builtin_tblwth(offset,0xFFFF);
        offset += 2;
    } while (offset & (2*NUM_INSTRUCTION_PER_ROW-1));
    
    offset = __builtin_tbloffset(&Code24bits);
     __builtin_tblwtl(offset,(unsigned short)(Data));
     __builtin_tblwth(offset,(unsigned char )(Data>>16));
     asm("DISI #5");
     __builtin_write_NVM();
}
/*
 * Read one instruction word at location Code24Bits
 */
unsigned long GetCode24bits(void)
{
    unsigned long result = 0;
    TBLPAG = __builtin_tblpage(&Code24bits);
    result = __builtin_tblrdh(__builtin_tbloffset(&Code24bits));
    result <<= 16;
    result |= __builtin_tblrdl(__builtin_tbloffset(&Code24bits));
    return result;
}
/*
 * Erase the block where Code24bits is located
 */
void EraseCode24bits(void)
{
    unsigned long result = 0;

    /*
     * This wait is here to try to keep from burning
     * out the PIC while we debug this code.
     */
    DelayForCountDown(10,"page erase\r\n");
    
    NVMCON = 0x4042;
    TBLPAG = __builtin_tblpage(&Code24bits);
    result = __builtin_tblrdh(__builtin_tbloffset(&Code24bits));
    result <<= 16;
    result |= __builtin_tblrdl(__builtin_tbloffset(&Code24bits));
     asm("DISI #5");
     __builtin_write_NVM();
}
/*
 * Test unit
 */
volatile unsigned long CheckIt;
int main(void)
{
    PIC_Init();
    UART1_Init();
    
    printf("\r\nPIC24FJ64GB004 Write to flash demo. Built " __DATE__ " at " __TIME__ "\r\n");
    
    CheckIt = GetCode24bits();
    if(CheckIt == 0xFFFFFF)
    {
        printf("CheckIt is blank\r\n");
        PutCode24bits(125);
        CheckIt = GetCode24bits();
        printf("CheckIt written as %lu\r\n",CheckIt);
    }
    else
    {
        printf("CheckIt is %lu\r\n",CheckIt);
        printf("No write to memory\r\n");
    }
    
    EraseCode24bits();
    CheckIt = GetCode24bits();
    printf("CheckIt is %06lX\r\n",CheckIt);
    /*
     * Application loop, embedded applications never return from main
     */
    for(;;)
    {
    }
    return 0;
}

post edited by dan1138 - 2020/10/13 09:51:08
#32
dan1138
Super Member
  • Total Posts : 4003
  • Reward points : 0
  • Joined: 2007/02/21 23:04:16
  • Location: 0
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/12 11:53:08 (permalink)
5 (1)
After reading again the comment that T Yorky made in post#31 on using the linker script file I think I should offer some additional notes on how the declarations of these arrays work:
/*
 * The value 0xAC000 is a "magic number" found in the
 * data sheet and is the address of the start of the
 * unimplemented flash memory for the PIC24FJ64GA002
 * and PIC24FJ64GB004.
 */
const __prog__ __attribute__((noload, space(prog), section("NextToLast"), address(0x0AC00-2048))) unsigned short Code24bits[512];
const __prog__ __attribute__((noload, space(prog), section("Last"),       address(0x0AC00-1024))) unsigned short Code24bits2[507];


The way these declarations work is to reserve space in the flash memory at absolute addresses and prevent the linker from locating any objects in those areas. This method works with the generic linker script files that Microchip provides.

Another thing to note is that while instruction words are 24-bits there are just two addresses assigned to each instruction word. This means is that all of the 8-bit bytes in an instruction words do not have unique address. This is inconvenient for the GCC based C compilers as their memory models do not directly support this architecture. This is a trap for new players that are only familiar with controllers with regular memory models where code space and data space all use unique and uniform byte addresses throughout these memory spaces.

This now gets me to why these arrays use the "unsigned short" type. This type tells the Microchip XC16 C compiler that each element requires two byte address units and is aligned on an even address boundary.

Now comes some odd stuff. The "Last" array reserves 507 elements, not the 512 elements that the data sheet specifies are in each erase page for the PIC24FJ64GB004 controller. There is no documentation I can find about this but the linker asserts an error when any object is located within one instruction word of where the configuration words are located in flash. There are four configuration words in the PIC24FJ64GB004 so I expected the maximum to be 508, not 507.

One last thing is about the "noload" attribute. This causes the linker to allocate the space but not include any records it in the HEX file to initialize this area. I did this so the HEX file size will be smaller. Also I want these areas of flash to be erased after the ISCP device programmer has programmed the HEX file the first time.
post edited by dan1138 - 2020/10/13 09:45:50
#33
T Yorky
Super (Thick) Member
  • Total Posts : 574
  • Reward points : 0
  • Joined: 2012/08/28 02:07:35
  • Location: UK
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/12 13:23:25 (permalink)
3 (1)
Agree with Dan1138. If you look at the effect of his declarations you will see that it cuts off/consumes the top area of memory. Basically pre-allocates to a non existent array of 16 bit words.
The GLD extract I posted effectively does the same thing.
When you compile, Dans will show the full 64K but with a lot of flash used up. The linker file method will show a smaller flash chip so the compiler never generates code for that area.
Note that in the extract that I posted that project actually allocated about 30K. This was for a USB MSD loader that allowed new applications to be loaded in by emulating a memory stick. Plug into PC. Save file to E: drive and hey presto.
You will actually only need to change:
program (xr)   : ORIGIN = 0x200,         LENGTH = 0xA9F6
to:
program (xr)   : ORIGIN = 0x200,         LENGTH = 0xA200
if this is the chosen way.
But the objective of both methods is to stop the compiler producing code (or constants) for this area. You then have to manage this area with your programming and erasing.
T Yorky
#34
davea
Super Member
  • Total Posts : 514
  • Reward points : 0
  • Joined: 2016/01/28 13:12:13
  • Location: Tampa Bay FL USA
  • Status: online
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/12 16:28:19 (permalink)
0
what if you programmed the last 20 address the first being your initial setup value
and rest to xFFFFFF and doing a test to find the first block of all F's
then back up 1 address and use that value..
now when you need a new setup value write it to the next address...
EXAMPLE 5-4: PROGRAMMING A SINGLE WORD OF FLASH PROGRAM MEMORY
now you will have 17 more changes you can make before you run out space
no erase needed.......
EDIT:
post 10,11 covered this
post edited by davea - 2020/10/12 21:00:19
#35
AndersG
Super Member
  • Total Posts : 241
  • Reward points : 0
  • Joined: 2008/08/05 04:51:24
  • Location: 0
  • Status: offline
Re: Finding the address of a const variable, PIC24FJ64GB004? 2020/10/14 10:03:00 (permalink)
3 (1)
Here is a complete project that build and works (as far as I can tell) correctly
 
Yes, it does and I am sure that I will find this useful in the future as will no doubt others that spelunk these forums. I also deepened my understanding somewhat, which will help me when I tackle the next project, which is understanding the An1157 bootloader:)
 
I will create a separate thread on that!
#36
Page: < 12 Showing page 2 of 2
Jump to:
© 2020 APG vNext Commercial Version 4.5