STM32F429的外接nandflash當作U盤實現資料記錄功能
目錄
功能需求
實現資料採集記錄的功能。需要每一秒將資料記錄一次,並且實現24小時迴圈記錄的功能。如果電腦插上USB_SLAVE口,就可以直接將採集到的資料拷貝出來。在上位機上進行資料的管理。
主要功能
1.485資料採集
專案主要利用485進行資料採集,每接收到一個完整的一幀資料,就可以解包並進行CRC校驗,只要校驗通過即可認為這一條資料是有效的。可以進行資料記錄。
2.NANDFlash模擬成U盤
這裡需要利用fatfs+ftl將NANDFlash對映成Windows系統可直接識別的FAT32模式,該模式下檔案系統可直接被識別,FTL可以很好的對NANDFlash進行管理,實現壞區標記等功能。最後再通過移植USB,這樣就可以直接被電腦所識別。
3.讀寫Fatfs
由於前面兩個功能網上都有大量的例子,本人就不必贅言。這裡主要敘述開發過程中對Fatfs操作過程中遇到的種種錯誤以及自己思考的解決辦法。
具體實現
其實在操作Fatfs過程中,剛開始一直沒有弄清楚究竟操作的是什麼,其實這裡直接可以將操作的物件當成一個檔案即可。操作過程中,先開啟檔案
void ProcessBuffer485(u8* bInbuff)
{
//memset(databuffer, 0, sizeof(databuffer));
switch(bInbuff[0])
{
case DATPKG: //如果滿足07 資料
bInbuff++;
memcpy((u8 *)&statePkg, bInbuff, sizeof(STAT_PKG));
//如果兩個時間一致,就跳出執行,將這一幀資料丟棄
if(statePkg.timesec==lastsec)
{
return;
}
lastsec=statePkg.timesec; //時間
year = statePkg.YMDHM >> 20;
month = (statePkg.YMDHM >> 16 ) &0x0F;
day = (statePkg.YMDHM >>11) & 0x1F;
hour = (statePkg.YMDHM >> 6) & 0x1F;
minute = statePkg.YMDHM & 0x3F;
if(reload==0)
{
z=0;
lastfilelen=0;
firststate=0;
reload=1;
}
//如果檔案剛開始,寫完一條資料
if((z==0)&&(lastfilelen==0)&&(firststate==0))
{
firststate=1;
p = sprintf( databuffer, "id\t YMDHM\t lamps\t engSpeed\t engOilPress\t engWaterTemp\t trainSpeed\t RunKM\t bat24_V\t bat24_A\t engWorkTime\t Soft_Version\n");
p += sprintf( databuffer+p, "%6d\t %4d-%2d-%2d %2d:%2d:%2d\t %10d\t %5d\t %5d\t %5d\t %5d\t %10d\t %5d\t %5d\t %6d\t %34d\n", z,year,month,day,hour,minute, statePkg.timesec,statePkg.lamps,statePkg.engSpeed, statePkg.engOilPress, statePkg.engWaterTemp, statePkg.trainSpeed, statePkg.RunKM, statePkg.bat24_V, statePkg.bat24_A, statePkg.engWorkTime,statePkg.Soft_Version);
z++; //讓計數加一
break; //此時返回databuffer的資料一共是256位元組
}
//第1條資料
//if((z==1)&&(lastfilelen==0))
//{
// p += sprintf( databuffer + p, "%6d\t %4d-%2d-%2d %2d:%2d:%2d\t %10d\t %5d\t %5d\t %5d\t %5d\t %10d\t %5d\t %5d\t %6d\t %24d\n", z,year,month,day,hour,minute,statePkg.timesec, statePkg.lamps,statePkg.engSpeed, statePkg.engOilPress, statePkg.engWaterTemp, statePkg.trainSpeed, statePkg.RunKM, statePkg.bat24_V, statePkg.bat24_A, statePkg.engWorkTime,statePkg.Soft_Version);
// return;
// }
//在位元組裡又增加128位元組
p += sprintf( databuffer + p, "%6d\t %4d-%2d-%2d %2d:%2d:%2d\t %10d\t %5d\t %5d\t %5d\t %5d\t %10d\t %5d\t %5d\t %6d\t %24d\n", z,year,month,day,hour,minute,statePkg.timesec, statePkg.lamps,statePkg.engSpeed, statePkg.engOilPress, statePkg.engWaterTemp, statePkg.trainSpeed, statePkg.RunKM, statePkg.bat24_V, statePkg.bat24_A, statePkg.engWorkTime,statePkg.Soft_Version);
if(z==1)
{
z++; //此時還差一條到達512位元組
break;
}else if(z==2){
//在這一條資料之後滿足512位元組了
p=0;
}
if(lastfilelen>0)
{
//表示已經有資料儲存了
if(z==0)
{
z = lastfilelen+1;
}
}
if(z>2)
{
if((z-2)%4!=0)
{
z++;
break;
}else{
p=0;
}
}
if(z>=180002)
{
reload=0;
}
/*
if(z>=180002)
{
reload=0;
}
*/
NAND_FlashFatfsDemo("2:/1.xls",databuffer,512);
z++;
memset(databuffer, 0, sizeof(databuffer));//陣列每次使用完,都要進行清空操作
break;
default:
break;
}
}
讀寫操作
/*
********************************************************************************************************
函式名稱:NAND_FlashFatfsDemo
函式功能: 測試 nand flash 讀寫功能,帶 fatfs 檔案管理系統操作
引數: 無
返回值: 無
********************************************************************************************************
*/
void NAND_FlashFatfsDemo(const TCHAR* path,const void* buff,int len)
{
uint8_t res;
//開啟之前改變檔案許可權
//如果是隻讀模式,那麼開啟檔案的標識是不會生效的,也就是不能開啟檔案
//res = f_chmod(path,AM_ARC,AM_ARC|AM_RDO);
res = f_open(myfile, path, FA_OPEN_ALWAYS | FA_WRITE); //開啟檔案,已經滿足512位元組了
f_sync(myfile);
// delay_ms(10); //延時10ms
if(FR_OK!=res)
{
//如果檔案沒有開啟成功,直接退出
return;
}
//資料第一次被建立
offset=((z-1)/4)*512;
res = f_lseek(myfile,offset);
//delay_ms(20);
//開始寫資料
res=f_write(myfile,buff,512,&bw);
//f_sync(myfile);
//delay_ms(20);
if(FR_OK!=res||bw==0)
{
z--;
z--;
z--;
z--;
f_close(myfile);
return;
}
//關閉資料檔案
f_close(myfile);
delay_ms(50);
//開始寫計數檔案
res = f_open(ftemp, "2:/number.txt", FA_CREATE_ALWAYS | FA_WRITE | FA_READ); //開啟檔案
f_sync(ftemp);
// delay_ms(10);
if(EOF==f_printf(ftemp,"%6d",z))
{
// delay_ms(10);
f_close(ftemp);
//測試,如果寫入失敗的情況
//res = f_open(ftemp, "2:/err.txt", FA_CREATE_ALWAYS | FA_WRITE | FA_READ); //開啟檔案
// delay_ms(10);
// f_close(ftemp);
return;
}
//f_putc(z,myfile);
f_close(ftemp);
delay_ms(10);
}
主要的思路是先將資料收集到512位元組,然後一次性向檔案裡面寫。當寫完後,則開始寫編號,這裡的編號用來表示下一次重新開始的檔案偏移位置。如果通過讀取檔案大小來操作檔案的指標,就沒有辦法實現迴圈複寫的功能了。所以只能思考採用兩個檔案來實現,一個專門寫資料,另外一個專門計數。當上電時,則可以計數檔案的數值。只用操作檔案指標偏移到相應的位置,即可修改檔案了。
遇到的問題
在操作過程中,剛開始寫的資料不對,有點時候讀出資料都是亂碼。這裡一般都是檔案的偏移地址算的不對。在寫完一條資料後,如果想讓資料接著寫,不覆蓋之前的資料,首先就是檔案的開啟方式採用 FA_OPEN_ALWAYS,其次就是要將檔案指標f_lseek移到該條資料結尾。不可移動太多了,如果移動太多,很可能會出現已經刪除了的資料。這樣就不對了。
第二個問題就是插上USB,windows系統會提示驅動有問題,是否修復U盤,很有可能是檔案寫完資料後,還沒來得及關閉資料,當沒有呼叫f_close或者f_sync時,資料是沒有的。所以有的時候會發現明明f_write寫了資料,但是有的時候卻沒有資料的情況。還可能出現FAT表損壞的情況。其實這個問題很常見,有的時候一斷電,資料檔案正在寫或者還沒來得及關閉,都有可能發生。
第三個問題就是斷電資料儲存的問題。本人在網上找了大量的資料,沒有發現合適的HAL庫版本的掉電中斷的文章,這裡來進行一下簡單的操作
void MyPVD_Init(void)
{
PWR_PVDTypeDef pvd;
__HAL_RCC_PWR_CLK_ENABLE();//使能電源時鐘PWR
pvd.PVDLevel=PWR_CR_PLS_LEV7;
pvd.Mode=PWR_PVD_MODE_IT_RISING;
// HAL_PWR_DeInit();
HAL_PWR_ConfigPVD(&pvd);
HAL_PWR_EnablePVD();
//LED1=!LED1;
//__HAL_PWR_PVD_EXTI_ENABLE_IT();
HAL_NVIC_SetPriority(PVD_IRQn,0x00,0x02); //搶佔優先順序1,子優先順序2
HAL_NVIC_EnableIRQ(PVD_IRQn);
}
//產生中斷!
void PVD_IRQHandler()
{
//LED1=0;
HAL_PWR_PVD_IRQHandler();
}
//產生中斷後的回掉函式
void HAL_PWR_PVDCallback(void)
{
LED1=0;
}
經過以上的配置,可以進入到中斷裡。這裡用LED燈可以看到發光一下。但是僅靠這個中斷想要實現資料的儲存還是不夠的,需要斷電後電壓維持一段時間,這段時間內足夠操作nandflash。