【飞思卡尔 MC9S12】内部Flash读写

 

上一篇讲到PRM文件与内存映射,其中有个重要寄存器叫做GPAGE,可以全局访问所有地址范围,Flash操作也是基于这个地址。

在讲述Flash读写之前,有一个重要概念要普及,就是Flash操作代码不能存储在被操作的Flash物理块中,例如我要擦除一个Flash中某一个扇区内容,这个擦除动作的代码不能存在这个Flash物理块中(Flash中代码运行时,相当于读操作,此时不能擦写)。

那我们如何擦写Flash呢?

很简单,只有一个解决方案,就是将Flash操作代码存储到RAM中。不过,就我们以往的理解,函数都是存储在Flash中的,如何存储到RAM中呢?

由这个解决方案引申出好几个子解决方案。

一、通过在PRM文件中设置 RAM RELOCATE_TO 到对应的Flash块,运行时将代码复制到这块RAM中,这样执行函数时,实际上是在RAM中执行;

二、直接将操作函数的二进制存到数组中,将数组当做函数体执行。

 

此处我们使用第二种方案,这种方案PRM比较简洁,无需多余设置,函数执行前也不需要进行Copy动作。

不过这个方案最大的难点在于:如何获取到函数的二进制代码,并在RAM中执行?

这实际上是两个问题。我们下面先解决这两个问题,然后再讲述Flash操作详细流程。

Flash擦除或写入都是先将地址及数据写到寄存器中,最后运行启动执行指令,只有这个执行指令需要放到RAM中。这个指令如下:

FSTAT = 0x80; //执行擦写命令
while(FSTAT_CCIF == 0U);  //等待操作完毕

我们先将这个操作的二进制代码提取出来。

新建一个CodeWarrior工程,默认即可,删除掉Start12.c和datapage.c,修改main.c中的内容,改为如下:

#include <hidef.h>      /* common defines and macros */
#include "derivative.h"      /* derivative-specific definitions */


void _Startup(void) 
{
    FSTAT = 0x80;
    while(FSTAT_CCIF == 0U);
}

编译,查看S19文件。

S0610000473A5C576F726B5C50726F6A656374735C467265657363616C655C4D43395331325845503130305C436F6D6D6F6E5C4D43395331325F426F6F746C6F616465725C434349465F416E645F576169745C62696E5C50726F6A6563742E6162732C
S10EC000C6807B01061F010680FB3D8B
S105FFFEC0003D
S9030000FC

第一行为文件名,第二行为代码二进制,第三行为Vector Table,我们需要的是第二行。

S1 0E C000 C6 80 7B 01 06 1F 01 06 80 FB 3D 8B

我们将红色部分的代码二进制存到数组中,然后就可以直接在RAM中使用代码了(实际上就是通过汇编跳到函数的起始地址)。

//RAM中定义的函数
unsigned char flash_fire_and_wait[11]={0xC6,0x80,0x7B,0x01,0x06,0x1F,0x01,0x06,0x80,0xFB,0x3D};

//运行RAM中的函数
_asm("JSR flash_fire_and_wait");

 

好,最基本的问题解决了,现在我们来看看Flash擦写具体流程(PFlash与DFlash原理相似,只是Sector大小和操作指令不同,此处专讲PFlash,下一篇将DFlash模拟EEPROM)。

需要注意的是,Flash操作时钟只能分频到1MHz左右,具体的参考手册上有表格,对应各个晶振频率的分频数。

先贴上所有代码。

#include <hidef.h>      /* common defines and macros */
#include "derivative.h"      /* derivative-specific definitions */

#include "Typedefs.h"
#include "flash.h"
#include "string.h"




/**** P-Flash and D-Flash Commands ****/

#define ERASE_VERIFY_ALL_BLOCKS  0x01 
/* Verify that all program and data Flash blocks are erased. */
/* CCOBIX end = 0 */
/* CCOB Params - NONE */
/* MGSTAT set if fault */

#define ERASE_VERIFY_BLOCK       0x02      
/* Verify that a Flash block is erased. */
/* CCOBIX end = 0 */
/* CCOB Params - gpage */
/* MGSTAT set if fault */

#define ERASE_ALL_BLOCKS         0x08 
/* Erase all program and data Flash blocks.
   An erase of all Flash blocks is only possible when the FPLDIS, FPHDIS, and FPOPEN
   bits in the FPROT register and the EPDIS and EPOPEN bits in the EPROM register are
   set prior to launching the command. */
/* CCOBIX end = 0 */
/* CCOB Params - NONE */
/* MGSTAT set if fault, FPVIOL / ACCERR set where appropriate */

#define UNSECURE_FLASH           0x0B 
/*Supports a method of releasing MCU security by erasing all program and data Flash
  blocks and verifying that all program and data Flash blocks are erased. */
/* CCOBIX end = 0 */
/* CCOB Params - NONE */
/* MGSTAT set if fault */

#define SET_USER_MARGIN_LEVEL    0x0D 
/*Specifies a user margin read level for all program Flash blocks. */
/* CCOBIX end = 1 */
/* CCOB Params - gpage, level setting (0-2) in CCOB[1] */
/* ACCERR set if invalid level */

#define SET_FIELD_MARGIN_LEVEL   0x0E 
/*Specifies a field margin read level for all program Flash blocks (special modes only). */
/* CCOBIX end = 1 */
/* CCOB Params - gpage, level setting (0-4) in CCOB[1] */
/* ACCERR set if invalid level */



/*-------------------------------*/
/* **** P-Flash Only Commands ****/

#define ERASE_VERIFY_P_FLASH_SECTION 0x03  
/*Verify that a given number of words starting at the address provided are erased. */
/* CCOBIX end = 2 */
/* CCOB Params - global address, number of phrases in CCOB[2]*/
/* MGSTAT set if fault */

#define READ_ONCE	               0x04  
/* Read a phrase from a dedicated 64 word area in a hidden region of a programFlash block
   that was previously programmed using the Program Once command. */
/* CCOBIX end = 1 */
/* CCOB Params - read once index (0-3) in CCOB[1], phrase in CCOB [5:2] */
/* returns phrase in CCOB [4:1] */

#define PROGRAM_P_FLASH          0x06 
/* Program a phrase in a program Flash block and any previously loaded phrases for any
   other program Flash block (see Load Data Field command). */
/* CCOBIX end = 5 */
/* CCOB Params - global address, phrase in CCOB [5:2] */
/* MGSTAT set if fault, FPVIOL / ACCERR set where appropriate */

#define PROGRAM_ONCE             0x07 
/* Program a dedicated 64 word area in a hidden region of a program Flash block that is
   allowed to be programmed only once. */
/* CCOBIX end = 5 */
/* CCOB Params - read once index (0-3) in CCOB[1], phrase in CCOB [5:2] */
/* MGSTAT set if fault */

#define ERASE_P_FLASH_BLOCK      0x09 
/* Erase a program Flash block.
   An erase of the full program Flash block is only possible when FPLDIS, FPHDIS and
   FPOPEN bits in the FPROT register are set prior to launching the command. */
/* CCOBIX end = 1 */
/* CCOB Params - global address */
/* MGSTAT set if fault, FPVIOL / ACCERR set where appropriate */

#define ERASE_P_FLASH_SECTOR 0x0A 
/* Erase all bytes in a program Flash sector. */
/* CCOBIX end = 1 */
/* CCOB Params - global address */
/* MGSTAT set if fault, FPVIOL / ACCERR set where appropriate */

#define VERIFY_BACKDOOR_ACCESS_KEY 0x0C 
/*Supports a method of releasing MCU security by verifying a set of security keys. */
/* CCOBIX end = 4 */
/* CCOB Params - backdoor key in CCOB [1:4] */
/* ACCERR set if not verified */


/*-------------------------------*/
/**** D-Flash Only Commands ****/
#define ERASE_D_FLASH_BLOCK      0x09 
/* Erase a program Flash block.
   An erase of the full program Flash block is only possible when DPOPEN bit in the DFPROT
     register is set prior to launching the command. */
/* CCOBIX end = 1 */
/* CCOB Params - global address */
/* MGSTAT set if fault, FPVIOL / ACCERR set where appropriate */

#define ERASE_VERIFY_D_FLASH_SECTION 0x10 
/* Verify that a given number of words starting at the address provided are erased. */
/* CCOBIX end = 2 */
/* CCOB Params - global address of first word, number of words to verify CCOB[2]*/
/* MGSTAT set if fault */

#define PROGRAM_D_FLASH         0x11 
/* Program up to four words in the data Flash block (see Load Data Field command). */
/* CCOBIX end = 2 */
/* CCOB Params - global address, up to 4 data words in CCOB [2:5] */
/* MGSTAT set if fault, EPVIOL / ACCERR set where appropriate */

#define ERASE_D_FLASH_SECTOR    0x12 
/* Erase all bytes in a data Flash sector. */
/* CCOBIX end = 2 */
/* CCOB Params - global address */
/* MGSTAT set if fault, EPVIOL  set where appropriate */

/*--------------------------------*/
#define ENABLE_EEPROM_EMULATION    0x13 
// Requests the FTMSM to enable EEPROM emulation.
// CCOBIX end = 0
// CCOB Params - NONE

#define DISABLE_EEPROM_EMULATION   0x14 
// Requests the FTMSM to suspend all current erase and program activity related to
// EEPROM emulation but leave current EEE tags set.
// CCOBIX end = 0
// CCOB Params - NONE

#define CANCEL_EEPROM_EMULATION    0x15   /* M22E mask only */
// Requests the FTMSM to suspend all current erase and program activity related to
// EEPROM emulation and clear all outstanding EEE tags.
// CCOBIX end = 0
// CCOB Params - NONE

#define EEPROM_QUERY    0x15   /* M48H mask only */
// Requests EEE status information.
// CCOBIX end = 0
// CCOB Return Params -
// CCOB[1] DFPART - size of D-Flash user partition (x256 bytes)
// CCOB[2] ERPART - size of EEE ram (x256 bytes)
// CCOB[3] ECOUNT - typical number of erase cycles for the EEE sectors
// CCOB[4] Dead sector count / Ready sector count

#define PARTITION_D_FLASH 0x20  /* M48H mask only */
// Partition a section of D-Flash for user access and EEE.
// CCOBIX end = 2
// CCOB Params - number of sectors for D-Flash in CCOB[1],  number of sectors for EEE in CCOB[2]
// ACCERR set if fault

#define FULL_PARTITION_D_FLASH 0x0F 
// Partition a section of D-Flash for user access and EEE.
// CCOBIX end = 2
// CCOB Params - number of sectors for D-Flash in CCOB[1],  number of sectors for EEE in CCOB[2]
// ACCERR set if fault


#define DFLASH_SECTOR_ADDR_MASK			0xFFFFFF00		//256 bytes
#define DFLASH_SECTOR_SIZE				256U

#define PFLASH_SECTOR_ADDR_MASK			0xFFFFFC00		//1024 bytes
#define PFLASH_SECTOR_SIZE				1024U

#define PROGRAM_Flash_Phrase_SIZE				8U
#define PROGRAM_Flash_Phrase_MASK			0xFFFFFFF8


#define ClearFlags()  (FSTAT = 0x30U)


typedef struct
{
	UINT8 flashCmd;
	UINT32 globalAddr;
	UINT8 Datas[PROGRAM_Flash_Phrase_SIZE];
	UINT8 dataLength;

}HDL_FLASH_SCM_FCCO_PARA;





/*the function run in ram, 
clear CCIF, then wait CCIF to set
*/
/*use CALL for this function*/
//unsigned char flash_fire_and_wait[10]={0x1C,0x01,0x06,0x80,0x1F,0x01,0x06,0x80,0xFB,0x0A};
/*use JSR for this function*/
unsigned char flash_fire_and_wait[11]={0xC6,0x80,0x7B,0x01,0x06,0x1F,0x01,0x06,0x80,0xFB,0x3D};


//check the flash operating frequency and ccif and accerr and fpviol
void HDL_Flash_CheckRegisterBeforeOperate(void)
{	
	//check if the clock for flash is valid, for 16MHz OSC, the divider should be 0x0F, here is 8MHz
	if(FCLKDIV_FDIVLD == 0)
	{
    	#ifdef ON_DEVELOP_BOARD
    		FCLKDIV = 0x0F;    //osc=16MHz
    	#else
    	    FCLKDIV = 0x07;    //osc=8MHz
    	#endif
	}

	//check and wait for an ongoing flash command  
	while(FSTAT_CCIF == 0)
	{
		_asm(nop);
	}

	//must clear ACCERR or FPVIOL bits before starting any command write sequence
	if((FSTAT_ACCERR != 0)||(FSTAT_FPVIOL !=0))
	{
		FSTAT_ACCERR = 1;
		FSTAT_FPVIOL = 1;
	}
}

//return 0-busy, return 1=ok
int HDL_Flash_GetFlashState(void)
{
    return FSTAT_CCIF;
}

//check the address is whether the phrase start, 8bytes is a phrase
int HDL_Flash_CheckAddressIsPhraseStart(UINT32 globalAddr)
{
	int nRetval = FLASH_SUCCESS;
	
	if(globalAddr%PROGRAM_Flash_Phrase_SIZE != 0)
	{
		nRetval = FLASH_ADDRESS_ERROR;
	}
	
	return nRetval;
}




/******************************************* PFlash **********************************/

//check the address is whether valid
int HDL_PFlash_CheckAddressIsValid(UINT32 globalAddr)
{
	int nRetval = FLASH_SUCCESS;
	
	if(! (globalAddr >= 0x700000) && (globalAddr <= 0x7FFFFF) )
	{
		nRetval = FLASH_ADDRESS_ERROR;
	}
	
	return nRetval;
}





#pragma MESSAGE DISABLE C1420 //result of function call is ignore
//execute one flash command
int HDL_Flash_ExecOneFlashCmd(HDL_FLASH_SCM_FCCO_PARA* pFCCOpara)
{
	UINT8 uIndex = FLASH_SUCCESS;
	
	HDL_Flash_CheckRegisterBeforeOperate();
	
	if (FSTAT_CCIF == 0U)     /* Is command buffer full ? */
    {              
        //ExitCritical();                    /* Exit critical section */
        return FLASH_BUSY_ERROR;                   /* If yes then error */
    }
	
	FCCOBIX = 0x00;
	FCCOBHI = pFCCOpara->flashCmd;
	FCCOBLO = (UINT)((pFCCOpara->globalAddr & 0x007F0000) >> 16);
	
	FCCOBIX = 0x01;
	FCCOB = (UINT16)(pFCCOpara->globalAddr & 0x0000FFFF) ;
	
	if(pFCCOpara->flashCmd==PROGRAM_P_FLASH)
	{

    	for(uIndex = 0; uIndex < pFCCOpara->dataLength; uIndex++)
    	{
    		FCCOBIX = 0x02 + uIndex / 2;
    		FCCOBHI = * (pFCCOpara->Datas + uIndex);
    		FCCOBLO = * (pFCCOpara->Datas + uIndex + 1);
    		uIndex++;
    	}

	    
	}
	

	
	DisableInterrupts;	
	//_asm("CALL flash_fire_and_wait");
	_asm("JSR flash_fire_and_wait");
	EnableInterrupts;
	
    if (FSTAT_FPVIOL == 1U) 
    {            /* Is protection violation detected ? */
        
        return FLASH_PROTECTED_ERROR;               /* If yes then error */
    }
    if (FSTAT_ACCERR == 1U) 
    {            /* Is acces error detected ? */

        return FLASH_NOTAVAIL_ERROR;               /* If yes then error */
    }
    if (FSTAT_MGSTAT) 
    {                  /* Was attempt to write data to the given address errorneous? */

        return FLASH_MGSTAT_ERROR;                  /* If yes then error */
    }
    
    return FLASH_SUCCESS;                      /* Exit critical section */
  
}



#pragma MESSAGE DISABLE C1420 //result of function call is ignore
//erase one sector,1024bytes
int HDL_Flash_PFlash_EraseOneSector(UINT32 sectorAddr)
{
	int nRetval = FLASH_SUCCESS;
	HDL_FLASH_SCM_FCCO_PARA fccoPara;
	
	if(HDL_PFlash_CheckAddressIsValid(sectorAddr))
	{
	
        fccoPara.flashCmd = ERASE_P_FLASH_SECTOR;		
	    fccoPara.globalAddr = sectorAddr&PROGRAM_Flash_Phrase_MASK; //erase start address must be phrase start address 	
		fccoPara.dataLength = 0;
		
		nRetval = HDL_Flash_ExecOneFlashCmd(&fccoPara);

	}
	else
	{
		nRetval = FLASH_ADDRESS_ERROR;
	}
	
	return nRetval;
}

#pragma MESSAGE DISABLE C1420 //result of function call is ignore
//erase multiple sector
int HDL_Flash_PFlash_EraseMultiSectors(UINT32 startGlobalAddr, UINT32 endGlobalAddr)
{
	int nRetval = FLASH_SUCCESS;
	
	if(HDL_PFlash_CheckAddressIsValid(startGlobalAddr) && HDL_PFlash_CheckAddressIsValid(endGlobalAddr) && startGlobalAddr<=endGlobalAddr )
	{	
		UINT32 addr = 0;
		
		
		for(addr = startGlobalAddr; addr <= endGlobalAddr; addr+=PFLASH_SECTOR_SIZE)
		{
			nRetval = HDL_Flash_PFlash_EraseOneSector(addr);
			if(nRetval!=FLASH_SUCCESS)
			    return nRetval;
			
		}
		
	}
	else
	{
		nRetval = FLASH_ADDRESS_ERROR;
	}
	
	return nRetval;
}

#pragma MESSAGE DISABLE C1420 //result of function call is ignore
//program one phrase,8bytes
int HDL_Flash_PFlash_ProgramOneSector(UINT32 globalAddr, UINT8 * pData,UINT8 dataLength)
{
	int nRetval = FLASH_SUCCESS;
	HDL_FLASH_SCM_FCCO_PARA fccoPara;
	
	if(dataLength!=PROGRAM_Flash_Phrase_SIZE)
	    return FLASH_DATALENGTH_ERROR;
	
	if(HDL_PFlash_CheckAddressIsValid(globalAddr) && HDL_Flash_CheckAddressIsPhraseStart(globalAddr))
	{
	    
	
	    fccoPara.flashCmd = PROGRAM_P_FLASH;		
    	fccoPara.globalAddr = globalAddr; 
    	
    	fccoPara.dataLength = dataLength;
    	memcpy(fccoPara.Datas,pData,dataLength);
    	
    	HDL_Flash_ExecOneFlashCmd(&fccoPara);

    	
	}
	else
	    nRetval =  FLASH_ADDRESS_ERROR;
	
	return nRetval;
}


#pragma MESSAGE DISABLE C1420 //result of function call is ignore
//program multiple phrases
int HDL_Flash_PFlash_ProgramMultiSectors(UINT32 globalAddr, UINT8 * pData, UINT8 dataLength)
{
	int nRetval = FLASH_SUCCESS;

	UINT32 addr = 0, offsetFromPhraseStartAddr = 0, endAddr = 0;
	//UINT8 *dataInFlash=&addr;
	UINT8 datas[8];
	UINT32 phraseStarAddr = globalAddr&PROGRAM_Flash_Phrase_MASK;   //first phrase start address


	if (!HDL_PFlash_CheckAddressIsValid(globalAddr))
		return FLASH_ADDRESS_ERROR;

	/*************************************************
	start
	0 1 2 3 4 5 6 7 8 .....  0 1 2 3 4 5 6 7 8
	|offset|<-   dataLength   ->| no 8 left data

	**************************************************/


	//the offset of phrase start
	offsetFromPhraseStartAddr = globalAddr - phraseStarAddr;
	endAddr = globalAddr + dataLength-1;

	//首组不对齐
	if (offsetFromPhraseStartAddr != 0)
	{
		//first contain offset area,read offset area data from flash
		for (addr = phraseStarAddr; addr<globalAddr; addr++)
		{
		    //dataInFlash = (UINT8 *)addr; //get the maintain value
		    datas[addr - phraseStarAddr] = 0xff;//*dataInFlash; //0xFF;
		}
			

		//first contain offset fill the left with data    
		for (addr = globalAddr; addr<globalAddr+ PROGRAM_Flash_Phrase_SIZE - offsetFromPhraseStartAddr; addr++)
			datas[addr - phraseStarAddr] = pData[addr - globalAddr];

		//program first one phrase
		HDL_Flash_PFlash_ProgramOneSector(phraseStarAddr,datas, PROGRAM_Flash_Phrase_SIZE);

		//下一组要偏移一个Phrase
		phraseStarAddr += PROGRAM_Flash_Phrase_SIZE;
	}


	//other data 
	for (addr = phraseStarAddr ; addr <= endAddr + 1 - PROGRAM_Flash_Phrase_SIZE; addr += PROGRAM_Flash_Phrase_SIZE)
	{
		//program data
		HDL_Flash_PFlash_ProgramOneSector(addr, &pData[addr - globalAddr], PROGRAM_Flash_Phrase_SIZE);
	}



	//endaddr phrase start addr
	phraseStarAddr = endAddr&PROGRAM_Flash_Phrase_MASK;   //last phrase start address

	
	//endaddr is not the phrase last one
	if (endAddr != (phraseStarAddr+ PROGRAM_Flash_Phrase_SIZE-1))
	{
		//the last no 8 left data
		for (addr = phraseStarAddr; addr <= endAddr; addr++)
			datas[addr - phraseStarAddr] = pData[addr - globalAddr];

		for (addr = endAddr + 1; addr<8+phraseStarAddr; addr++)
		{
		    //dataInFlash = (UINT8 *)addr;  //get the maintain value
		    datas[addr - phraseStarAddr] = 0xff;//*dataInFlash;//0xFF;
		}
			

		//program first one phrase
		HDL_Flash_PFlash_ProgramOneSector(phraseStarAddr, datas, PROGRAM_Flash_Phrase_SIZE);
	}


	return nRetval;
}

上述代码主要是擦除和编程,读的话很简单,不需要操作寄存器,直接*要读的地址即可。

要注意的是,擦除必须是一整个Sector,PFlash一个Sector大小为1024Bytes,DFlash为256Bytes,注意擦除地址并非该Sector内任意地址,而是一个Phrase的首地址,否则擦除不成功,Phrase大小为8Bytes。写入最小单位为一个Phrase,也就是8Bytes。Flash擦写最终暴露出来的函数只有两个,会根据提供的地址自动擦除或写入,无需考虑Sector及Phrase大小。

int HDL_Flash_PFlash_EraseMultiSectors(UINT32 startGlobalAddr, UINT32 endGlobalAddr)

int HDL_Flash_PFlash_ProgramMultiSectors(UINT32 globalAddr, UINT8 * pData, UINT8 dataLength)

 

使用范例:

#include <hidef.h>      /* common defines and macros */
#include "derivative.h"      /* derivative-specific definitions */
#include "Typedefs.h"
#include "gpio.h"
#include "System.h"
#include "flash.h"




UINT32 m_maincount=0;
void main(void) 
{
  /* put your own code here */
  UINT32 index = 0;
  UINT32 globalDFlashAddr1 = 0x100000,globalDFlashAddr2 = 0x100002,globalDFlashAddr3=0x13F800;
  UINT32 globalAddr1 = 0x7F4000,globalAddr2 = 0x7F4002,globalAddr3=0x7db460;
  UINT8 datas1[] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
  UINT8 datas2[] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
  
    McuDrivers_System_Init();
    McuDrivers_GPIO_Init();

	EnableInterrupts;
	
	//for(index = 0;index<129;index++)
	//    HDL_Flash_PFlash_ProgramMultiSectors(globalAddr2+index*8,datas,sizeof(datas));
	
	//HDL_Flash_PFlash_EraseOneSector(0x7F4000);    
//	HDL_Flash_PFlash_EraseMultiSectors(globalAddr2,globalAddr2+1001); 
	//IFsh1_EraseSector(globalAddr2);	
	
	//HDL_Flash_PFlash_ProgramMultiSectors(globalAddr1,datas,sizeof(datas));
	
	//HDL_Flash_PFlash_ProgramMultiSectors(globalAddr3,datas2,sizeof(datas2));

    HDL_Flash_DFlash_EraseMultiSectors(globalDFlashAddr1,globalDFlashAddr1+1000);
    for(index = 0;index<33;index++)
	    HDL_Flash_DFlash_ProgramMultiSectors(globalDFlashAddr1+index*16,datas1,sizeof(datas1));
    HDL_Flash_DFlash_EraseMultiSectors(globalDFlashAddr1,globalDFlashAddr1+1000);
    
    HDL_Flash_DFlash_ProgramMultiSectors(globalDFlashAddr2,datas1,sizeof(datas1));
    
  for(;;) 
  {
     m_maincount++;
     
     if(m_maincount>100000)
     {
        m_maincount = 0;
         PORTB_PB0 ^=1;
     }
     
  
    _FEED_COP(); /* feeds the dog */
  } /* loop forever */
  /* please make sure that you never leave main */
}

 

posted on 2019-07-04 08:19  Beatfan  阅读(621)  评论(0)    收藏  举报

导航