#include "my_printf.h"
#include "string_utils.h"
#define NOR_FLASH_BASE 0 /* jz2440, nor-->cs0, base addr = 0 */
/* 比如: 55H 98
* 本意是: 往(0 + (0x55)<<1)写入0x98
*/
void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
*p = val;
}
void nor_cmd(unsigned int offset, unsigned int cmd)
{
nor_write_word(NOR_FLASH_BASE, offset, cmd);
}
unsigned int nor_read_word(unsigned int base, unsigned int offset)
{
volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
return *p;
}
unsigned int nor_dat(unsigned int offset)
{
return nor_read_word(NOR_FLASH_BASE, offset);
}
/* 进入NOR FLASH的CFI模式
* 读取各类信息
*/
void do_scan_nor_flash(void)
{
char str[4];
/* 打印厂家ID、设备ID */
nor_cmd(0x55, 0x98); /* 进入cfi模式 */
str[0] = nor_dat(0x10);
str[1] = nor_dat(0x11);
str[2] = nor_dat(0x12);
str[3] = '\0';
printf("str = %s\n\r", str);
/* 打印容量 */
/* 打印各个扇区的起始地址 */
}
void do_erase_nor_flash(void)
{
}
void do_write_nor_flash(void)
{
}
void do_read_nor_flash(void)
{
}
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\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 'e':
case 'E':
do_erase_nor_flash();
break;
case 'w':
case 'W':
do_write_nor_flash();
break;
case 'r':
case 'R':
do_read_nor_flash();
break;
default:
break;
}
}
}
注意:
虽然传入参数val是int类型,但是指针是short类型,说明指针只存放低16bit的数据。
void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
*p = val;
}
编译出错:
[email protected]:/work/uart/test$ make
arm-linux-gcc -c -o nor_flash.o nor_flash.c
arm-linux-gcc -c -o my_printf.o my_printf.c
arm-linux-gcc -c -o string_utils.o string_utils.c
arm-linux-gcc -c -o lib1funcs.o lib1funcs.S
#arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o exception.o interrupt.o timer.o nor_flash.o my_printf.o string_utils.o lib1funcs.o -o sdram.elf
my_printf.o: In function `out_num':
my_printf.c:(.text+0x120): undefined reference to `__aeabi_uidivmod'
my_printf.c:(.text+0x158): undefined reference to `__aeabi_uidiv'
Makefile:2: recipe for target 'all' failed
make: *** [all] Error 1
解决方法:https://blog.csdn.net/qq_40674996/article/details/105589523
运行结果:
[s] Scan nor flash
[e] Erase nor flash
[w] Write nor flash
[r] Read nor flash
[q] quit
Enter selection: s
str = QRY
说明确实进入了CFI模式
15的10进制为21,221 = 2M
221 相当于1<<21
void do_scan_nor_flash(void)
{
char str[4];
unsigned int size;
/* 打印厂家ID、设备ID */
nor_cmd(0x55, 0x98); /* 进入cfi模式 */
str[0] = nor_dat(0x10);
str[1] = nor_dat(0x11);
str[2] = nor_dat(0x12);
str[3] = '\0';
printf("str = %s\n\r", str);
/* 打印容量 */
size = 1<<(nor_dat(0x27));
printf("nor size = 0x%x, %dM\n\r", size, size/(1024*1024));
/* 打印各个扇区的起始地址 */
/* 退出CFI模式 */
nor_cmd(0, 0xf0);
}
记住,要退出CFI模式,不然就读取不了Nor上烧写的程序。
MX29LV160DBTI-70G(NOR FLASH).pdf 的芯片手册 扇区为0,不太合理。
MX29LV800BBTC.pdf 的手册提及 参考refer to the CFI publication 100
我们打开百度搜索“CFI publication 100”:
https://wenku.baidu.com/view/cd1c1e22482fb4daa58d4b42.html
表格的意思,从2D地址开始,每个地址4字节, 前16bit(2D2E) + 1表示多少个block,后16bit(2F30) *256表示block的大小。
代码:
void do_scan_nor_flash(void)
{
char str[4];
unsigned int size;
int regions, i;
int region_info_base;
int block_addr, blocks, block_size, j;
int cnt;
int vendor, device;
/* 打印厂家ID、设备ID */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0x90); /* read id */
vendor = nor_dat(0);
device = nor_dat(1);
nor_cmd(0, 0xf0); /* reset */
nor_cmd(0x55, 0x98); /* 进入cfi模式 */
str[0] = nor_dat(0x10);
str[1] = nor_dat(0x11);
str[2] = nor_dat(0x12);
str[3] = '\0';
printf("str = %s\n\r", str);
/* 打印容量 */
size = 1<<(nor_dat(0x27));
printf("vendor id = 0x%x, device id = 0x%x, nor size = 0x%x, %dM\n\r", vendor, device, size, 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_dat(0x2c);
region_info_base = 0x2d;
block_addr = 0;
printf("Block/Sector start Address:\n\r");
cnt = 0;
for (i = 0; i < regions; i++)
{
blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8);
block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));
region_info_base += 4;
// printf("\n\rregion %d, blocks = %d, block_size = 0x%x, block_addr = 0x%x\n\r", i, blocks, block_size, block_addr);
for (j = 0; j < blocks; j++)
{
/* 打印每个block的起始地址 */
//printf("0x%08x ", block_addr);
printHex(block_addr);
putchar(' ');
cnt++;
block_addr += block_size;
if (cnt % 5 == 0)
printf("\n\r");
}
}
printf("\n\r");
/* 退出CFI模式 */
nor_cmd(0, 0xf0);
}
运行结果:
[s] Scan nor flash
[e] Erase nor flash
[w] Write nor flash
[r] Read nor flash
[q] quit
Enter selection: s
str = QRY
size = 0x200000 2M
Block/Sector start Address:
0x00000000 0x00004000 0x00006000 0x00008000 0x00010000
0x00020000 0x00030000 0x00040000 0x00050000 0x00060000
0x00070000 0x00080000 0x00090000 0x000A0000 0x000B0000
0x000C0000 0x000D0000 0x000E0000 0x000F0000 0x00100000
0x00110000 0x00120000 0x00130000 0x00140000 0x00150000
0x00160000 0x00170000 0x00180000 0x00190000 0x001A0000
0x001B0000 0x001C0000 0x001D0000 0x001E0000 0x001F0000
/* 打印厂家ID、设备ID */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55); /* 解锁 */
nor_cmd(0x555, 0x90); /* read id */
vendor = nor_dat(0);
device = nor_dat(1);
nor_cmd(0, 0xf0); /* reset:退出读ID状态 */
nor_cmd(0x55, 0x98); /* 进入cfi模式 */
str[0] = nor_dat(0x10);
str[1] = nor_dat(0x11);
str[2] = nor_dat(0x12);
str[3] = '\0';
printf("str = %s\n\r", str);
/* 打印容量 */
size = 1<<(nor_dat(0x27));
printf("vendor id = 0x%x, device id = 0x%x, nor size = 0x%x, %dM\n\r", vendor, device, size, size/(1024*1024));
运行结果:
查看反汇编文件,可以看出,读出的是Nor上的数据,而不是芯片的ID。
解决方法:
主函数:
main函数代码如下所示。把timer中断去掉,否则: 测试NOR Flash时进入CFI等模式时, 如果发生了中断,cpu必定读NOR Flash,那么读不到正确的指令,导致程序崩溃。
int main(void)
{
led_init();
key_eint_init(); /* 初始化按键, 设为中断源 */
//timer_init();
puts("\n\rg_A = ");
printHex(g_A);
puts("\n\r");
nor_flash_test();
return 0;
}
再次编译,ID仍然不对, 多执行几次,程序也没有死机。
查看反汇编文件:
我们的nor_write_word是想一次性将val的两字节写入指针指向的地址:
void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
*p = val;
}
但是反汇编,分两次写入:
在必应上搜英文比较好: 搜索"volatile unsigned short ldrb ldrh"
(英语不好,都不知道哪个网页是要的答案.)
找到一个网页说是加上 --march
我们参考下uboot,看下有什么参数供我们选择:
在configmk文件里搜索到:
因此我们在makefile文件里加上:
all: start.o led.o uart.o init.o main.o exception.o interrupt.o timer.o nor_flash.o my_printf.o string_utils.o lib1funcs.o
#arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
arm-linux-ld -T sdram.lds $^ -o sdram.elf
arm-linux-objcopy -O binary -S sdram.elf sdram.bin
arm-linux-objdump -D sdram.elf > sdram.dis
clean:
rm *.bin *.o *.elf *.dis
%.o : %.c
arm-linux-gcc -march=armv4 -c -o $@ $<
%.o : %.S
arm-linux-gcc -march=armv4 -c -o $@ $<
在编译程序时加上:-march=armv4
否则,
volatile unsigned short *p = XXX;
*p = val; //会被差分成2个strb操作