PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pic24_flash.c
Go to the documentation of this file.
1 /*
2  * "Copyright (c) 2008 Robert B. Reese, Bryan A. Jones, J. W. Bruce ("AUTHORS")"
3  * All rights reserved.
4  * (R. Reese, reese_AT_ece.msstate.edu, Mississippi State University)
5  * (B. A. Jones, bjones_AT_ece.msstate.edu, Mississippi State University)
6  * (J. W. Bruce, jwbruce_AT_ece.msstate.edu, Mississippi State University)
7  *
8  * Permission to use, copy, modify, and distribute this software and its
9  * documentation for any purpose, without fee, and without written agreement is
10  * hereby granted, provided that the above copyright notice, the following
11  * two paragraphs and the authors appear in all copies of this software.
12  *
13  * IN NO EVENT SHALL THE "AUTHORS" BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
15  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE "AUTHORS"
16  * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17  *
18  * THE "AUTHORS" SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE "AUTHORS" HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
23  *
24  * Please maintain this header in its entirety when copying/modifying
25  * these files.
26  *
27  *
28  */
29 // Documentation for this file. If the \file tag isn't present,
30 // this file won't be documented.
31 /** \file
32  * FLASH memory read/write support functions. Thanks goes to David Weaver
33  * for suggestions on improving these functions.
34  */
35 
36 #include "pic24_generic.h"
37 #include "pic24_flash.h"
38 #include "pic24_chip.h"
39 #include "pic24_unittest.h"
40 #include "pic24_util.h"
41 
42 /**
43 Write table latch with address \em u16_addrhi:u16_addrlo, data: u16_wordhi:u16_wordlo
44 \param u16_addrhi upper word of flash memory address
45 \param u16_addrlo lower word of flash memory address
46 \param u16_wordhi upper data word (only lower 8-bits are valid, upper 8-bits should be zero)
47 \param u16_wordlo lower data word
48 */
49 //doWriteLatch ;W0=TBLPAG,W1=Wn,W2=WordHi,W3=WordLo - no return values
50 void doWriteLatchFlash(uint16_t u16_addrhi, uint16_t u16_addrlo, uint16_t u16_wordhi, uint16_t u16_wordlo) {
51  TBLPAG = u16_addrhi;
52  __builtin_tblwtl(u16_addrlo,u16_wordlo); //equivalent to asm(" tblwtl W3,[W1]")
53  __builtin_tblwth(u16_addrlo,u16_wordhi); //equivalent to asm(" tblwth W2,[W1]")
54 }
55 
56 #if (defined(__PIC24E__) || defined(__dsPIC33E__))
57 //this family uses double word programming.
58 //_LoadTwoWords: ;W0=TBLPAG,W1=Wn,W2=WordHi,W3=WordLo W4=Word2Hi,W5=Word2Lo
59 //W0,W1 is not used.
60 void LoadTwoWords(uint16_t addrhi,uint16_t addrlo,uint16_t wordhi, uint16_t wordlo, uint16_t word2hi, uint16_t word2lo) {
61  asm(" mov #0xFA,W0");
62  asm(" mov W0, TBLPAG");
63  asm(" mov #0,W1");
64  asm(" tblwtl W3,[W1]");
65  asm(" tblwth W2,[W1++]");
66  asm(" tblwtl W5,[W1]");
67  asm(" tblwth W4,[W1++]");
68 }
69 
70 void WriteMem2(uint16_t addrhi,uint16_t addrlo,uint16_t val) {
71  asm("mov w0,NVMADRU"); //; Init Pointer to page to be erased
72  asm("mov w1,NVMADR"); //; Init Pointer to offset to be erased
73  asm("mov W2,NVMCON");
74  //__builtin_write_NVM();
75  asm("disi #06");
76  asm("mov #0x55,W0");
77  asm("mov W0, NVMKEY");
78  asm("mov #0xAA,W0");
79  asm("mov W0,NVMKEY");
80  asm("bset NVMCON,#15");
81  asm("nop");
82  asm("nop");
83 
84  asm("1: btsc NVMCON,#15"); // ;Wait for write end
85  asm(" bra 1b");
86 
87 }
88 #endif
89 
90 /**
91 Read table latch from address \em u16_addrhi:u16_addrlo
92 \param u16_addrhi upper word of flash memory address
93 \param u16_addrlo lower word of flash memory address
94 \return 24-bit data value returned in \em uint32_t type
95 */
96 //_ReadLatch: ;W0=TBLPAG,W1=Wn - data in W1:W0
97 uint32_t doReadLatchFlash(uint16_t u16_addrhi, uint16_t u16_addrlo) {
98  union32 u32_a;
99  TBLPAG = u16_addrhi;
100  u32_a.u16.ls16 = __builtin_tblrdl(u16_addrlo); //equivalent to asm(" tblrdl [W1],W0")
101  u32_a.u16.ms16 = __builtin_tblrdh(u16_addrlo); //equivalent to asm(" tblrdl [W1],W1")
102  return(u32_a.u32);
103 }
104 
105 
106 /**
107 Erases a flash page at \em u16_addrhi:u16_addrlo flash address
108 \param u16_addrhi upper word of flash memory address
109 \param u16_addrlo lower word of flash memory address
110 */
111 void doErasePageFlash (uint16_t u16_addrhi, uint16_t u16_addrlo) {
112  uint16_t u16_save_SR;
113 #if (defined(__PIC24E__) || defined(__dsPIC33E__))
114  u16_save_SR = SR;
115  //disable interrupts
116  SR = SR | 0xE0;
117  NVMCON = 0x4003;
118  NVMADRU = u16_addrhi;
119  NVMADR = u16_addrlo;
120  //start erase
121  asm("disi #06");
122  asm("mov #0x55,W0");
123  asm("mov W0, NVMKEY");
124  asm("mov #0xAA,W0");
125  asm("mov W0,NVMKEY");
126  asm("bset NVMCON,#15");
127  asm("nop");
128  asm("nop");
129  //reenable interrupts
130  SR = u16_save_SR;
131  //wait for end of erase
132  while (NVMCON & 0x8000)
133  doHeartbeat();
134 #else
135  uint16_t u16_save_TBLPAG;
136 // preserve the SR and TBLPAG registers
137  u16_save_SR = SR;
138  u16_save_TBLPAG = TBLPAG;
139 //disable interrupts
140  SR = SR | 0xE0;
141 // NVCON = flash write + erase + page erase
142  NVMCON = 0x4042;
143  TBLPAG = u16_addrhi; // select page
144 //select row
145 //equivalant to "tblwtl W1,[W1]"
146  asm("tblwtl %0,[%0]"::"r"(u16_addrlo));
147 //start erase
148  __builtin_write_NVM();
149 //reenable interrupts
150  SR = u16_save_SR;
151 //wait for end of erase
152  while (NVMCON & 0x8000)
153  doHeartbeat();
154 // restore TBLPAG
155  TBLPAG = u16_save_TBLPAG;
156 #endif
157 }
158 
159 /**
160 Write current flash row
161 */
162 
164  uint16_t u16_save_SR;
165  // save SR
166  u16_save_SR = SR;
167  // disable interrupts
168  SR = SR | 0xE0;
169  // flash write + row op
170  NVMCON = 0x4001;
171 //start write
172  __builtin_write_NVM();
173 //reenable interrupts
174  SR = u16_save_SR;
175 //wait for end of write
176  while (NVMCON & 0x8000)
177  doHeartbeat();
178 }
179 
180 /**
181 Erases a flash page at \em u32_pmemAddress, then writes \em u16_len bytes
182 from \em pu8_data to program memory.
183 \param u32_pmemAddress flash memory address, should be on a page boundary
184 \param pu8_data pointer to byte data to write
185 \param u16_len number of bytes to write, this is rounded up nearest row boundary!
186 (should be evenly divisible by 64*3). For
187 */
188 
189 void doWritePageFlash(union32 u32_pmemAddress, uint8_t* pu8_data, uint16_t u16_len) {
190  uint16_t u16_byteCnt;
191  union32 u32_a;
192 #if (defined(__PIC24E__) || defined(__dsPIC33E__))
193  union32 u32_b;
194 #endif
195  uint16_t u16_ICnt, u16_numInstructions;
196 
197  ASSERT(u16_len <= FLASH_PAGEBYTES);
198  doErasePageFlash(u32_pmemAddress.u16.ms16, u32_pmemAddress.u16.ls16); //erase page
199  //write the bytes
200  //round up to nearest row boundary
201  u16_numInstructions = u16_len/3;
202  if (u16_len % 3 != 0) u16_numInstructions++;
203  u16_numInstructions += (u16_numInstructions%FLASH_ROWSIZE);
204 #if (defined(__PIC24E__) || defined(__dsPIC33E__))
205  //double word programming for this family
206  for (u16_ICnt = 0, u16_byteCnt=0; u16_ICnt<u16_numInstructions; u16_ICnt += 2,u16_byteCnt += 6) {
207  u32_a.u8[0] = pu8_data[u16_byteCnt];
208  u32_a.u8[1] = pu8_data[u16_byteCnt+1];
209  u32_a.u8[2] = pu8_data[u16_byteCnt+2];
210  u32_a.u8[3] = 0;
211  u32_b.u8[0] = pu8_data[u16_byteCnt+3];
212  u32_b.u8[1] = pu8_data[u16_byteCnt+4];
213  u32_b.u8[2] = pu8_data[u16_byteCnt+5];
214  u32_b.u8[3] = 0;
215  LoadTwoWords(u32_pmemAddress.u16.ms16, u32_pmemAddress.u16.ls16,u32_a.u16.ms16,u32_a.u16.ls16,u32_b.u16.ms16,u32_b.u16.ls16);
216  WriteMem2(u32_pmemAddress.u16.ms16, u32_pmemAddress.u16.ls16,0x4001);
217  u32_pmemAddress.u32 += 4; //program memory address increments by 4
218  }
219 #else
220  for (u16_ICnt = 0, u16_byteCnt=0; u16_ICnt<u16_numInstructions; u16_ICnt += 1,u16_byteCnt += 3) {
221  u32_a.u8[0] = pu8_data[u16_byteCnt];
222  u32_a.u8[1] = pu8_data[u16_byteCnt+1];
223  u32_a.u8[2] = pu8_data[u16_byteCnt+2];
224  u32_a.u8[3] = 0;
225  doWriteLatchFlash(u32_pmemAddress.u16.ms16, u32_pmemAddress.u16.ls16,u32_a.u16.ms16,u32_a.u16.ls16);
226  if ((u16_ICnt+1)%FLASH_ROWSIZE == 0) {
227  //row boundary, write it.
228  doWriteRowFlash();
229  }
230  u32_pmemAddress.u32 += 2; //program memory address increments by 2
231  }
232 #endif
233 }
234 
235 /**
236 Reads a flash page at \em u32_pmemAddress, returns \em u16_len bytes
237 in buffer \em pu8_data
238 \param u32_pmemAddress flash memory address, should be on a page boundary
239 \param pu8_data pointer to byte data to write
240 \param u16_len number of bytes to read
241 */
242 void doReadPageFlash(union32 u32_pmemAddress, uint8_t* pu8_data, uint16_t u16_len) {
243  uint16_t u16_byteCnt;
244  union32 u32_a;
245 
246  ASSERT(u16_len <= FLASH_PAGEBYTES);
247  for (u16_byteCnt=0; u16_byteCnt<u16_len; u16_byteCnt += 3) {
248  u32_a = (union32) doReadLatchFlash(u32_pmemAddress.u16.ms16, u32_pmemAddress.u16.ls16);
249  pu8_data[u16_byteCnt] = u32_a.u8[0];
250  pu8_data[u16_byteCnt+1] = u32_a.u8[1];
251  pu8_data[u16_byteCnt+2] = u32_a.u8[2];
252  u32_pmemAddress.u32 += 2;
253  }
254 }