// Program FLASH_EE.C CCS, PIC16F1827
//
// copyright, Peter H. Anderson, Baltimore, MD, May 1, '10

#ifdef DESCRIPTION

Illustrates how to write to and read from 14-bit program memory.

Note that a read from a specific location is quite simple.  However,I
spent a considerable amount of time on the write.

See Manual, Section 11.  Example, 11-5 illustrates how to write how to
write to eight continguous locations and I have implemented this in
function write_8_words.  However, in order to program, one must first erase
32 contiguous words.

Thus, function flash_write_num_words is a matter of first reading from the
32 word block to a 32 word array, then inserting the new data in the array,
erasing the 32 word block in program memory and then writing the 32 words to
program memory.

This program writes eight words to program memory beginning at location
0x0800 and then reads them back and displays to the serial display on TX
(PORTB.2).

#endif

#case

#device PIC16F1827 ICD=TRUE

#include <defs_1827.h>
#include <string.h>

#rom 0x8007 = {0x09e4, 0x0003}  // Manual Section 4.0

#define TRUE 1
#define FALSE 0

void flash_write_num_words(long adr, long *v, byte num);

void flash_write_8_words(long base_address, long *a);
void flash_read_8_words(long base_address, long *a);
void display_8_words(long *a);

void flash_erase(long base_address);

void asynch_setup(void);
void ser_char(char ch);

void delay_10us(byte t);
void delay_ms(long t);
void delay_s(long t);

int main()
{
   byte n;
   long v[8]; // some values

   OSCCON = 0x6b; // 4.0 MHz internal

   asynch_setup();
   delay_ms(10);

   printf(ser_char, "?f");
   delay_ms(25);

   for (n=0; n<8; n++) // dummy up some values
   {
      v[n] = 1025 + 4 * n;
   }

   printf(ser_char, "?f");
   delay_ms(25);


   display_8_words(v);
   delay_s(2);

   printf(ser_char, "Writing ...?n");
   delay_s(1);

   flash_write_num_words(0x800, v, 8);

   printf(ser_char, "Reading ...?n");
   delay_s(1);

   flash_read_8_words(0x800, v);

   printf(ser_char, "?f");
   delay_ms(25);

   display_8_words(v);
   delay_s(1);

   while(1)
   {
   }
}

void flash_write_num_words(long write_adr, long *v, byte num)
{
   long base_address, save_buffer[32];
   byte offset, n;

   base_address = write_adr & 0x1fe0;  // 1 1111 1110 0000
   offset = write_adr & 0x001f;

   // now read 4 X 8 bytes currently in memory
   for (n=0; n<4; n++)
   {
       // read four pages of 8 words
       flash_read_8_words(base_address + n * 8, save_buffer + n*8);
   }

   // now insert the new data to be programmed
   for (n=0; n< num; n++)
   {
       save_buffer[offset+n] = v[n];
   }
   // erase the 32 words in program memory
   flash_erase(base_address);

   // write the 32 words to program memory
   for (n=0; n<4; n++)
   {
       flash_write_8_words(base_address + n * 8, save_buffer + n*8);
   }
}

void flash_erase(long base_address)
{
   cfgs = 0;
   eepgd = 1;
   wren = 1;
   free = 1;
   EECON2 = 0x55;
   EECON2 = 0xaa;
   wr = 1;
#asm
   nop;
   nop;
#endasm
}

void flash_write_8_words(long base_address, long *a)
{
   byte gie_status, n;

   gie_status = gie;// disable interrupts

   while(gie)
   {
      gie = 0;
   }

   EEADRH = base_address >> 8;
   EEADRL = base_address & 0x00ff;

   for (n=0; n<8; n++)
   {
      EEDATH = a[n] >> 8;
      EEDATL = a[n] & 0x00ff;

      cfgs = 0;
      eepgd = 1;

      wren = 1;
      lwlo = 1;  // only load the write latches

      EECON2 = 0x55;
      EECON2 = 0xaa;
      wr = 1;
#asm
      nop;
      nop;
#endasm
      ++EEADRL;
   }
   // now actually write
   EEADRH = base_address >> 8;
   EEADRL = base_address & 0x00ff;
   wren = 1;
   lwlo = 0;
   EECON2 = 0x55;
   EECON2 = 0xaa;
   wr = 1;

#asm
   nop;
   nop;
#endasm

   wren = 0;

   gie = gie_status;
}

void flash_read_8_words(long base_address, long *a)
{

   byte gie_status, n;
   long x;

   gie_status = gie;// disable interrupts

   while(gie)
   {
      gie = 0;
   }

   EEADRH = base_address >> 8;
   EEADRL = base_address & 0x00ff;

   for (n=0; n<8; n++)
   {
       cfgs = 0;
       eepgd = 1;

       rd = 1;
#asm
       nop;
       nop;
#endasm
       x = (((long) EEDATH) << 8) + EEDATL;
       a[n] = x & 0x3fff;
       ++EEADRL;
    }

    gie = gie_status;
}

void display_8_words(long *a)
{
    byte n;
    printf(ser_char, "?f");
    delay_ms(20);
    for (n=0; n<4; n++)
    {
      printf(ser_char,"%ld?t%ld?n", a[2*n], a[2*n+1]);
   }
   delay_ms(5000);
}

void ser_char(char ch)
{
    TXREG = ch;
    while(trmt == 0) // wait for trmt to return to logic one
    {
    }
}

void asynch_setup(void)
{
    txcksel = 0;    // assign TX to RB2
    trisb2 = 1;    // TX

    spen = 1;     // enable serial port
    sync = 0;     // asynchronous
    txen = 1;     // enable transmitter

    brgh = 1;
    brg16 = 1;
    SPBRGH = 0x00;
    SPBRGL = 103; // 9600 at 4.0 MHz clock
}

void delay_s(long t)
{
   while(t--)
   {
      delay_ms(1000);
   }
}

void delay_ms(long t)   // delays t millisecs
{
   do
   {
     delay_10us(100);
   } while(--t);
}

void delay_10us(byte t)
{
#asm
//      BCF STATUS, RP0
DELAY_10US_1:
      CLRWDT
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      DECFSZ t, F
      GOTO DELAY_10US_1
#endasm
}