NOR Flash編程實現識別擦、寫、讀地址
/*
*硬件平臺:韋東山嵌入式Linxu開發板(S3C2440.v3)
*軟件平臺:運行於VMware Workstation 12 Player下UbuntuLTS16.04_x64 系統
*參考資料:《嵌入式Linux應用開發完全手冊》韋東山,開發版原理圖,MX29LV160DBTI-70G(NOR FLASH) datasheet,MX29LV800BBTC datasheet
*/
目錄
一、基礎知識點
1、可視字符與不可視字符
可視字符 | 不可視字符 | |
---|---|---|
範圍 | ASCII字符集中的95個可打印字符(0x20-0x7E) | ASCII字符集中的33個控制字符(0x00-0x1F,0x7F) |
用途 | 顯示在輸出設備上 | 向計算機發出一些特殊指令 |
具體介紹可點擊這裏查看。
二、實現功能
功能:讀取和編寫某個地址信息與擦除某個扇區
三、編程原理
繼續在上一篇博客的nor_flash.c文件上編寫
1、功能:讀某個指定地址
編程思路:通過串口中輸入操作地址,打印地址信息時固定其長度位64字節,輸出格式爲:一行16個字節,共4行,每行先打印ASCII碼數值,最後打印ASCII碼字符。
1.1 獲取操作的地址
unsigned int addr;
volatile unsigned char *p;
/* 獲取地址 */
printf("Enter the address to read: ");
addr = get_uint();
p = (volatile unsigned char *)addr;
1.2 打印地址信息
在打印字符時,需判斷字符是否可視,具體介紹可看本篇基礎知識點
/* 各變量對應含義
* 用到的標誌位
* 存儲輸入的地址
* 存儲1字節地址的信息,存儲16字節的信息
* 存儲地址,以指針形式
*/
int i,j;
unsigned int addr;
unsigned char c,str[16];
volatile unsigned char *p;
/* 打印地址信息
* 1、固定長度64字節
* 2、按照市面上的標準以16個字節爲一行,前爲數值,後爲字符
* 3、分辨字符是否可視
*/
printf("Data: \n\r");
for(i=0;i<4;i++)
{
for(j=0;j<16;j++)
{
c = *p++;
str[j] = c;
printf("%02x ",c);
}
for(j=0;j<16;j++)
{
if((str[j] < 0x20) || (str[j] > 0x7E))
printf(".");
else
printf("%c",str[j]);
}
printf("\n\r");
}
}
2、功能:擦除nor flash某個指定扇區
編程思路:通過串口中輸入操作扇區地址,根據手冊上的指令執行擦除命令,最後通過查手冊可知:可通過判斷Q6位是否在變化,可知擦除是否完畢,不變則完,變則在擦除。
2.1 獲取操作的扇區地址
unsigned int addr;
volatile unsigned char *p;
/* 獲取地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
p = (volatile unsigned char *)addr;
2.2 按照手冊執行擦除命令
查MX29LV800BT/BB datasheet第10頁可知:
nor_cmd(0x555,0xaa); //解鎖
nor_cmd(0x2aa,0x55);
nor_cmd(0x555,0x80);
printf("erasing ...\n\r");
nor_cmd(0x555,0xaa); //erase
nor_cmd(0x2aa,0x55);
nor_cmd(addr>>1,0x30);
補充說明:爲什麼這裏的addr>>1?
解釋:舉例說明,當我們輸入想操作的扇區地址addr爲0x100,由於在nor_cmd()函數中由於Nor Flash和S3c2440的數據線相差一位,函數編寫裏面已經addr<<1,實際到Nor Flash操作時地址變爲0x200,所以addr>>1。
2.3 判斷Q6位是否擦除完畢
查MX29LV800BT/BB datasheet第16頁可知:
功能說明:用戶可以通過查詢第I位切換指示是否有自動程序或擦除算法正在進行或完成。
使用方法:執行擦除命令後,通過先後兩次讀取操作地址的第I位,判斷兩次的電平是否相同,若相同則命令執行完畢,否則命令未執行完畢。
編寫判斷函數:
void wait_readly(unsigned int addr)
{
unsigned char val,flag;
/* 先後兩次讀取操作地址*/
val = nor_data(addr>>1);
flag = nor_data(addr>>1);
/* 提取第6位進行判斷 */
while((val&(1<<6))!= (val&(1<<6)))
{
val =flag;
flag = nor_data(addr>>1);;
}
}
wait_readly(addr);
3、功能:編寫某個指定地址
某個地址寫入值時的條件:
①、先擦除地址信息,後纔可寫入。
②、此時地址信息爲0xff,可直接寫入。
編程思路:通過串口輸入操作的地址和信息,後根據手冊上的指令執行燒寫命令,最後通過查手冊可知:可通過判斷Q6位是否在變化,可知燒寫是否完畢,不變則完,變則在燒寫。
3.1 獲取地址
unsigned int addr;
/* 獲取地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();
3.2 獲取寫入信息
unsigned char str[100];
/* 獲取寫入信息 */
printf("Enter the address to write: ");
gets(str);
3.3 按照手冊執行寫命令
int i = 0;
/* 執行寫命令
* 因爲芯片上爲16位數據線,所以傳輸數據時應該一次寫16位的數據,提高利用率
*/
printf("writing ...\n\r");
while( str[i] && str[i+1] )
{
val = str[i]+(str[i+1]<<8);
nor_cmd(0x555,0xaa); //解鎖
nor_cmd(0x2aa,0x55);
nor_cmd(0x555,0xa0);
nor_cmd(addr>>1,val); //proram
i += 2;
addr += 2;
}
val = str[i];
nor_cmd(0x555,0xaa); //解鎖
nor_cmd(0x2aa,0x55);
nor_cmd(0x555,0xa0);
nor_cmd(addr>>1,val); //proram
3.4 判斷Q6位是否寫完畢
wait_readly(addr);
四、編程文件
1、完整nor flash.c文件
#include "my_printf.h"
#include "string_utils.h"
#define NOR_FLASH_BASE 0 //nor--cs0,base addr :0
/* 構建一個函數,功能:往地址裏面寫東西 */
void nor_write_addr(unsigned int base,unsigned int offset,unsigned int val )
{
volatile unsigned short *p = (volatile unsigned short *)(base + (offset<<1));
*p = val;
}
/* 封裝nor_write_addr */
void nor_cmd(unsigned int offset,unsigned int cmd)
{
nor_write_addr(NOR_FLASH_BASE,offset,cmd);
}
/* 構建一個函數,功能:讀取地址中的信息 */
unsigned int nor_read_addr(unsigned int base,unsigned int offset)
{
volatile unsigned short *p = (volatile unsigned short *)(base + (offset<<1));
return *p;
}
/* 封裝nor_read_addr */
unsigned int nor_data(unsigned int offset)
{
return nor_read_addr(NOR_FLASH_BASE,offset);
}
void wait_readly(unsigned int addr)
{
unsigned char val,flag;
val = nor_data(addr>>1);
flag = nor_data(addr>>1);
while((val&(1<<6))!= (val&(1<<6)))
{
val =flag;
flag = nor_data(addr>>1);;
}
}
/* 識別nor flash */
void do_scan_nor_flash(void)
{
/* 各變量對應含義
* 用到的標誌位
* 設備容量大小
* 廠家ID,設備ID
* region的數量、第一個region的信息地址
* blocks的數量、block的地址、block的大小
*/
char str[4];
int i,j,flag;
int device_size;
int vendor,device;
int regions,regions_info_base;
int blocks,block_addr,block_size;
/* 打印設備ID
* 1、先解鎖
* 2、後根據信息輸入對應的nor_cmd指令
* 3、讀取數據後要reset
*/
nor_cmd(0x555,0xaa);
nor_cmd(0x2aa,0x55);
nor_cmd(0x555,0x90);
vendor = nor_data(0);
device = nor_data(1);
nor_cmd(0,0xf0);
/*
* 1、首先進入cfi模式
* 2、對獲取信息的不同,執行nor_cmd進行寫操作
* 3、執行nor_data,讀出對應地址的信息
*/
nor_cmd(0x55,0x98);
str[0] = nor_data(0x10);
str[1] = nor_data(0x11);
str[2] = nor_data(0x12);
str[3] = '\0';
printf("\n\r");
printf("str = %s\n\r",str);
/* 打印容量 */
device_size = 1<<(nor_data(0x27)); //2的nor_data(0x27)次方
printf("Vendor ID = 0x%x, Device ID = 0x%x, Nor Flash Size = 0x%x, %dM\n\r", vendor, device, device_size, (device_size/(1024*1024)));
/* 打印各個扇區的起始地址 */
/* 名詞解釋:
* erase block region : 裏面含有1個或多個block, 它們的大小一樣
* 一個nor flash含有1個或多個region
* 一個region含有1個或多個block(扇區)
* Erase block region information:
* 前2字節+1 : 表示該region有多少個block
* 後2字節*256 : 表示block的大小
*/
regions = nor_data(0x2C);
regions_info_base = 0x2d;
block_addr = 0;
flag = 0;
printf("\n\r");
printf("Block/Sector Start Address:\n\r");
for(i = 0;i<regions;i++)
{
blocks = nor_data(regions_info_base)+(nor_data(regions_info_base+1)<<8)+1; //計算該region中blocks的個數
block_size = 256*(nor_data(regions_info_base+2)+(nor_data(regions_info_base+3)<<8)); //計算每個block的大小
regions_info_base +=4; //使得進入下次循環時地址爲下一個region的信息地址
for(j=0;j<blocks;j++)
{
/* 打印每個block的起始地址 */
printHex(block_addr);
putchar(' ');
flag++;
block_addr +=block_size;
if (flag % 5 == 0)
printf("\n\r");
}
}
printf("\n\r");
/* 退出CFI模式 */
nor_cmd(0, 0xf0);
}
/* 擦除nor flash某個扇區
* 1、獲取扇區地址
* 2、按照手冊執行擦除命令
* 3、判斷Q6位是否擦除完畢
*/
void do_erase_nor_flash(void)
{
/* 各變量對應含義
* 存儲輸入的地址
* 存儲地址,以指針形式
*/
unsigned int addr;
volatile unsigned char *p;
/* 獲取地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
p = (volatile unsigned char *)addr;
nor_cmd(0x555,0xaa); //解鎖
nor_cmd(0x2aa,0x55);
nor_cmd(0x555,0x80);
printf("erasing ...\n\r");
nor_cmd(0x555,0xaa); //erase
nor_cmd(0x2aa,0x55);
nor_cmd(addr>>1,0x30);
wait_readly(addr);
printf("\n\r");
}
/* 編寫某個地址
* 1、獲取地址
* 2、獲取寫入信息
* 3、按照手冊執行寫命令
* 4、判斷Q6位是否寫完畢
*/
void do_write_nor_flash(void)
{
/* 各變量對應含義
* 用到的標誌位
* 存儲輸入的地址,存儲構建的16位信息
* 存儲寫入的信息
*/
int i;
unsigned int addr,val;
unsigned char str[100];
i = 0;
/* 獲取地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();
/* 獲取寫入信息 */
printf("Enter the address to write: ");
gets(str);
/* 執行寫命令
* 因爲芯片上爲16位數據線,所以傳輸數據時應該一次寫16位的數據,提高利用率
*/
printf("writing ...\n\r");
while( str[i] && str[i+1] )
{
val = str[i]+(str[i+1]<<8);
nor_cmd(0x555,0xaa); //解鎖
nor_cmd(0x2aa,0x55);
nor_cmd(0x555,0xa0);
nor_cmd(addr>>1,val); //proram
i += 2;
addr += 2;
}
val = str[i];
nor_cmd(0x555,0xaa); //解鎖
nor_cmd(0x2aa,0x55);
nor_cmd(0x555,0xa0);
nor_cmd(addr>>1,val); //proram
wait_readly(addr);
printf("\n\r");
}
/* 讀某個地址
* 1、獲取地址
* 2、打印地址信息
*/
void do_read_nor_flash(void)
{
/* 各變量對應含義
* 用到的標誌位
* 存儲輸入的地址
* 存儲1字節地址的信息,存儲16字節的信息
* 存儲地址,以指針形式
*/
int i,j;
unsigned int addr;
unsigned char c,str[16];
volatile unsigned char *p;
/* 獲取地址 */
printf("Enter the address to read: ");
addr = get_uint();
p = (volatile unsigned char *)addr;
/* 打印地址信息
* 1、固定長度64字節
* 2、按照市面上的標準以16個字節爲一行,前爲數值,後爲字符
* 3、分辨字符是否可視
*/
printf("Data: \n\r");
for(i=0;i<4;i++)
{
for(j=0;j<16;j++)
{
c = *p++;
str[j] = c;
printf("%02x ",c);
}
for(j=0;j<16;j++)
{
if((str[j] < 0x20) || (str[j] > 0x7E))
printf(".");
else
printf("%c",str[j]);
}
printf("\n\r");
}
}
void nor_flash_test(void)
{
char c;
while(1)
{
/* 打印菜單,供選擇測試內容 */
printf("[S] Scan nor flash\n\r");
printf("[E] Erase nor flash\n\r");
printf("[W] Write nor flash\n\r");
printf("[R] Read nor flash\n\r");
printf("[Q] Quit nor flash\n\r");
printf("Enter Selection: ");
c = getchar();
printf("%c\n\r",c);
/* 測試內容:
* 1、識別nor flash
* 2、擦除nor flash某個扇區
* 3、編寫某個地址
* 4、讀某個地址
*/
switch (c)
{
case 'Q':
case 'q':
return;
break;
case 'S':
case 's':
do_scan_nor_flash();
break;
case 'W':
case 'w':
do_write_nor_flash();
break;
case 'R':
case 'r':
do_read_nor_flash();
break;
case 'E':
case 'e':
do_erase_nor_flash();
break;
default:
break;
}
}
}
五、運行結果截圖
1、讀取某個指定地址
對比讀取0地址的運行截圖與反彙編代碼可知:功能實現成功。
2、擦除某個指定扇區
可以看出,執行擦除命令之後,扇區起始地址爲0x100000的信息爲0xff,擦除成功。
3、編寫某個指定地址
從圖中可以知道abcdef成功寫入到起始地址爲0x100000的扇區中。