LPCOpen Platform  v1.03
LPCOpen Platform for NXP LPC Microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
DataflashManager.c
Go to the documentation of this file.
1 /*
2  * @note
3  * Copyright(C) NXP Semiconductors, 2012
4  * All rights reserved.
5  *
6  * @par
7  * Software that is described herein is for illustrative purposes only
8  * which provides customers with programming information regarding the
9  * LPC products. This software is supplied "AS IS" without any warranties of
10  * any kind, and NXP Semiconductors and its licensor disclaim any and
11  * all warranties, express or implied, including all implied warranties of
12  * merchantability, fitness for a particular purpose and non-infringement of
13  * intellectual property rights. NXP Semiconductors assumes no responsibility
14  * or liability for the use of the software, conveys no license or rights under any
15  * patent, copyright, mask work right, or any other intellectual property rights in
16  * or to any products. NXP Semiconductors reserves the right to make changes
17  * in the software without notification. NXP Semiconductors also makes no
18  * representation or warranty that such application will be suitable for the
19  * specified use without further testing or modification.
20  *
21  * @par
22  * Permission to use, copy, modify, and distribute this software and its
23  * documentation is hereby granted, under NXP Semiconductors' and its
24  * licensor's relevant copyrights in the software, without fee, provided that it
25  * is used in conjunction with NXP Semiconductors microcontrollers. This
26  * copyright, permission, and disclaimer notice must appear in all copies of
27  * this code.
28  */
29 
38 #define INCLUDE_FROM_DATAFLASHMANAGER_C
39 #include "DataflashManager.h"
40 
41 /*****************************************************************************
42  * Private types/enumerations/variables
43  ****************************************************************************/
44 
45 static int InitializedFlag = 1;
46 
47 /*****************************************************************************
48  * Public types/enumerations/variables
49  ****************************************************************************/
50 
51 /*****************************************************************************
52  * Private functions
53  ****************************************************************************/
54 
55 /*****************************************************************************
56  * Public functions
57  ****************************************************************************/
58 
68  const uint32_t BlockAddress,
69  uint16_t TotalBlocks)
70 {
71  uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE);
72  uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE);
73  uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4);
74  bool UsingSecondBuffer = false;
75 
76  /* Select the correct starting Dataflash IC for the block requested */
77  Dataflash_SelectChipFromPage(CurrDFPage);
78 
79 #if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE)
80  /* Copy selected dataflash's current page contents to the Dataflash buffer */
81  Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1);
82  Dataflash_SendAddressBytes(CurrDFPage, 0);
83  Dataflash_WaitWhileBusy();
84 #endif
85 
86  /* Send the Dataflash buffer write command */
87  Dataflash_SendByte(DF_CMD_BUFF1WRITE);
88  Dataflash_SendAddressBytes(0, CurrDFPageByte);
89 
90  /* Wait until endpoint is ready before continuing */
92  return;
93  }
94 
95  while (TotalBlocks) {
96  uint8_t BytesInBlockDiv16 = 0;
97 
98  /* Write an endpoint packet sized data block to the Dataflash */
99  while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) {
100  /* Check if the endpoint is currently empty */
101  if (!(Endpoint_IsReadWriteAllowed())) {
102  /* Clear the current endpoint bank */
104 
105  /* Wait until the host has sent another packet */
106  if (Endpoint_WaitUntilReady()) {
107  return;
108  }
109  }
110 
111  /* Check if end of Dataflash page reached */
112  if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) {
113  /* Write the Dataflash buffer contents back to the Dataflash page */
114  Dataflash_WaitWhileBusy();
115  Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE);
116  Dataflash_SendAddressBytes(CurrDFPage, 0);
117 
118  /* Reset the Dataflash buffer counter, increment the page counter */
119  CurrDFPageByteDiv16 = 0;
120  CurrDFPage++;
121 
122  /* Once all the Dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */
123  if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) {
124  UsingSecondBuffer = !(UsingSecondBuffer);
125  }
126 
127  /* Select the next Dataflash chip based on the new Dataflash page index */
128  Dataflash_SelectChipFromPage(CurrDFPage);
129 
130 #if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE)
131  /* If less than one Dataflash page remaining, copy over the existing page to preserve trailing data */
132  if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) {
133  /* Copy selected dataflash's current page contents to the Dataflash buffer */
134  Dataflash_WaitWhileBusy();
135  Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1);
136  Dataflash_SendAddressBytes(CurrDFPage, 0);
137  Dataflash_WaitWhileBusy();
138  }
139 #endif
140 
141  /* Send the Dataflash buffer write command */
142  Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2WRITE : DF_CMD_BUFF1WRITE);
143  Dataflash_SendAddressBytes(0, 0);
144  }
145 
146  /* Write one 16-byte chunk of data to the Dataflash */
147  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
148  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
149  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
150  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
151  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
152  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
153  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
154  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
155  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
156  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
157  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
158  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
159  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
160  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
161  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
162  Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
163 
164  /* Increment the Dataflash page 16 byte block counter */
165  CurrDFPageByteDiv16++;
166 
167  /* Increment the block 16 byte block counter */
168  BytesInBlockDiv16++;
169 
170  /* Check if the current command is being aborted by the host */
171  if (MSInterfaceInfo->State.IsMassStoreReset) {
172  return;
173  }
174  }
175 
176  /* Decrement the blocks remaining counter */
177  TotalBlocks--;
178  }
179 
180  /* Write the Dataflash buffer contents back to the Dataflash page */
181  Dataflash_WaitWhileBusy();
182  Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE);
183  Dataflash_SendAddressBytes(CurrDFPage, 0x00);
184  Dataflash_WaitWhileBusy();
185 
186  /* If the endpoint is empty, clear it ready for the next packet from the host */
187  if (!(Endpoint_IsReadWriteAllowed())) {
189  }
190 
191  /* Deselect all Dataflash chips */
192  Dataflash_DeselectChip();
193 }
194 
204  const uint32_t BlockAddress,
205  uint16_t TotalBlocks)
206 {
207  uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE);
208  uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE);
209  uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4);
210 
211  /* Select the correct starting Dataflash IC for the block requested */
212  Dataflash_SelectChipFromPage(CurrDFPage);
213 
214  /* Send the Dataflash main memory page read command */
215  Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD);
216  Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte);
217  Dataflash_SendByte(0x00);
218  Dataflash_SendByte(0x00);
219  Dataflash_SendByte(0x00);
220  Dataflash_SendByte(0x00);
221 
222  /* Wait until endpoint is ready before continuing */
223  if (Endpoint_WaitUntilReady()) {
224  return;
225  }
226 
227  while (TotalBlocks) {
228  uint8_t BytesInBlockDiv16 = 0;
229 
230  /* Write an endpoint packet sized data block to the Dataflash */
231  while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) {
232  /* Check if the endpoint is currently full */
233  if (!(Endpoint_IsReadWriteAllowed())) {
234  /* Clear the endpoint bank to send its contents to the host */
236 
237  /* Wait until the endpoint is ready for more data */
238  if (Endpoint_WaitUntilReady()) {
239  return;
240  }
241  }
242 
243  /* Check if end of Dataflash page reached */
244  if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) {
245  /* Reset the Dataflash buffer counter, increment the page counter */
246  CurrDFPageByteDiv16 = 0;
247  CurrDFPage++;
248 
249  /* Select the next Dataflash chip based on the new Dataflash page index */
250  Dataflash_SelectChipFromPage(CurrDFPage);
251 
252  /* Send the Dataflash main memory page read command */
253  Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD);
254  Dataflash_SendAddressBytes(CurrDFPage, 0);
255  Dataflash_SendByte(0x00);
256  Dataflash_SendByte(0x00);
257  Dataflash_SendByte(0x00);
258  Dataflash_SendByte(0x00);
259  }
260 
261  /* Read one 16-byte chunk of data from the Dataflash */
262  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
263  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
264  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
265  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
266  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
267  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
268  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
269  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
270  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
271  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
272  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
273  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
274  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
275  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
276  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
277  Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
278 
279  /* Increment the Dataflash page 16 byte block counter */
280  CurrDFPageByteDiv16++;
281 
282  /* Increment the block 16 byte block counter */
283  BytesInBlockDiv16++;
284 
285  /* Check if the current command is being aborted by the host */
286  if (MSInterfaceInfo->State.IsMassStoreReset) {
287  return;
288  }
289  }
290 
291  /* Decrement the blocks remaining counter */
292  TotalBlocks--;
293  }
294 
295  /* If the endpoint is full, send its contents to the host */
296  if (!(Endpoint_IsReadWriteAllowed())) {
297  Endpoint_ClearIN(MSInterfaceInfo->Config.PortNumber);
298  }
299 
300  /* Deselect all Dataflash chips */
301  Dataflash_DeselectChip();
302 }
303 
314  uint16_t TotalBlocks,
315  uint8_t *BufferPtr)
316 {
317  uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE);
318  uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE);
319  uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4);
320  bool UsingSecondBuffer = false;
321 
322  /* Select the correct starting Dataflash IC for the block requested */
323  Dataflash_SelectChipFromPage(CurrDFPage);
324 
325 #if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE)
326  /* Copy selected dataflash's current page contents to the Dataflash buffer */
327  Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1);
328  Dataflash_SendAddressBytes(CurrDFPage, 0);
329  Dataflash_WaitWhileBusy();
330 #endif
331 
332  /* Send the Dataflash buffer write command */
333  Dataflash_SendByte(DF_CMD_BUFF1WRITE);
334  Dataflash_SendAddressBytes(0, CurrDFPageByte);
335 
336  while (TotalBlocks) {
337  uint8_t BytesInBlockDiv16 = 0;
338 
339  /* Write an endpoint packet sized data block to the Dataflash */
340  while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) {
341  /* Check if end of Dataflash page reached */
342  if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) {
343  /* Write the Dataflash buffer contents back to the Dataflash page */
344  Dataflash_WaitWhileBusy();
345  Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE);
346  Dataflash_SendAddressBytes(CurrDFPage, 0);
347 
348  /* Reset the Dataflash buffer counter, increment the page counter */
349  CurrDFPageByteDiv16 = 0;
350  CurrDFPage++;
351 
352  /* Once all the Dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */
353  if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) {
354  UsingSecondBuffer = !(UsingSecondBuffer);
355  }
356 
357  /* Select the next Dataflash chip based on the new Dataflash page index */
358  Dataflash_SelectChipFromPage(CurrDFPage);
359 
360 #if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE)
361  /* If less than one Dataflash page remaining, copy over the existing page to preserve trailing data */
362  if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) {
363  /* Copy selected dataflash's current page contents to the Dataflash buffer */
364  Dataflash_WaitWhileBusy();
365  Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1);
366  Dataflash_SendAddressBytes(CurrDFPage, 0);
367  Dataflash_WaitWhileBusy();
368  }
369 #endif
370 
371  /* Send the Dataflash buffer write command */
372  Dataflash_ToggleSelectedChipCS();
373  Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2WRITE : DF_CMD_BUFF1WRITE);
374  Dataflash_SendAddressBytes(0, 0);
375  }
376 
377  /* Write one 16-byte chunk of data to the Dataflash */
378  for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) {
379  Dataflash_SendByte(*(BufferPtr++));
380  }
381 
382  /* Increment the Dataflash page 16 byte block counter */
383  CurrDFPageByteDiv16++;
384 
385  /* Increment the block 16 byte block counter */
386  BytesInBlockDiv16++;
387  }
388 
389  /* Decrement the blocks remaining counter */
390  TotalBlocks--;
391  }
392 
393  /* Write the Dataflash buffer contents back to the Dataflash page */
394  Dataflash_WaitWhileBusy();
395  Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE);
396  Dataflash_SendAddressBytes(CurrDFPage, 0x00);
397  Dataflash_WaitWhileBusy();
398 
399  /* Deselect all Dataflash chips */
400  Dataflash_DeselectChip();
401 }
402 
413  uint16_t TotalBlocks,
414  uint8_t *BufferPtr)
415 {
416  uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE);
417  uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE);
418  uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4);
419 
420  /* Select the correct starting Dataflash IC for the block requested */
421  Dataflash_SelectChipFromPage(CurrDFPage);
422 
423  /* Send the Dataflash main memory page read command */
424  Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD);
425  Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte);
426  Dataflash_SendByte(0x00);
427  Dataflash_SendByte(0x00);
428  Dataflash_SendByte(0x00);
429  Dataflash_SendByte(0x00);
430 
431  while (TotalBlocks) {
432  uint8_t BytesInBlockDiv16 = 0;
433 
434  /* Write an endpoint packet sized data block to the Dataflash */
435  while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) {
436  /* Check if end of Dataflash page reached */
437  if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) {
438  /* Reset the Dataflash buffer counter, increment the page counter */
439  CurrDFPageByteDiv16 = 0;
440  CurrDFPage++;
441 
442  /* Select the next Dataflash chip based on the new Dataflash page index */
443  Dataflash_SelectChipFromPage(CurrDFPage);
444 
445  /* Send the Dataflash main memory page read command */
446  Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD);
447  Dataflash_SendAddressBytes(CurrDFPage, 0);
448  Dataflash_SendByte(0x00);
449  Dataflash_SendByte(0x00);
450  Dataflash_SendByte(0x00);
451  Dataflash_SendByte(0x00);
452  }
453 
454  /* Read one 16-byte chunk of data from the Dataflash */
455  for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) {
456  *(BufferPtr++) = Dataflash_ReceiveByte();
457  }
458 
459  /* Increment the Dataflash page 16 byte block counter */
460  CurrDFPageByteDiv16++;
461 
462  /* Increment the block 16 byte block counter */
463  BytesInBlockDiv16++;
464  }
465 
466  /* Decrement the blocks remaining counter */
467  TotalBlocks--;
468  }
469 
470  /* Deselect all Dataflash chips */
471  Dataflash_DeselectChip();
472 }
473 
476 {
477  /* Select first Dataflash chip, send the read status register command */
478  Dataflash_SelectChip(DATAFLASH_CHIP1);
479  Dataflash_SendByte(DF_CMD_GETSTATUS);
480 
481  /* Check if sector protection is enabled */
482  if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) {
483  Dataflash_ToggleSelectedChipCS();
484 
485  /* Send the commands to disable sector protection */
486  Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]);
487  Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]);
488  Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]);
489  Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]);
490  }
491 
492  /* Select second Dataflash chip (if present on selected board), send read status register command */
493 #if (DATAFLASH_TOTALCHIPS == 2)
494  Dataflash_SelectChip(DATAFLASH_CHIP2);
495  Dataflash_SendByte(DF_CMD_GETSTATUS);
496 
497  /* Check if sector protection is enabled */
498  if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) {
499  Dataflash_ToggleSelectedChipCS();
500 
501  /* Send the commands to disable sector protection */
502  Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]);
503  Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]);
504  Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]);
505  Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]);
506  }
507 #endif
508 
509  /* Deselect current Dataflash chip */
510  Dataflash_DeselectChip();
511 }
512 
518 {
519  uint8_t ReturnByte;
520 
521  /* Test first Dataflash IC is present and responding to commands */
522  Dataflash_SelectChip(DATAFLASH_CHIP1);
523  Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO);
524  ReturnByte = Dataflash_ReceiveByte();
525  Dataflash_DeselectChip();
526 
527  /* If returned data is invalid, fail the command */
528  if (ReturnByte != DF_MANUFACTURER_ATMEL) {
529  return false;
530  }
531 
532 #if (DATAFLASH_TOTALCHIPS == 2)
533  /* Test second Dataflash IC is present and responding to commands */
534  Dataflash_SelectChip(DATAFLASH_CHIP2);
535  Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO);
536  ReturnByte = Dataflash_ReceiveByte();
537  Dataflash_DeselectChip();
538 
539  /* If returned data is invalid, fail the command */
540  if (ReturnByte != DF_MANUFACTURER_ATMEL) {
541  return false;
542  }
543 #endif
544 
545  return true;
546 }