以前的裸板程序都是通过u-boot下载到内存运行,今天实现更新程序update,程序运行时会输出一个菜单供选择。
系统:ubuntu 10.04.4
单板:tq2440
编译器:arm-linux-gcc-4.3.2
搭建开发环境详见ubuntu 10.04.4开发环境配置。
目标:实现自我更新程序,串口输出菜单,有以下·功能供选择
*********************************
update program with serial port
The board:micro2440
The NAND:K9F1216U0A 256MB
The NOR:AM29LV160DB 2MB
The SRAM:HY57V561620 x2 64MB
The NET:DM9000AEP
date: 2013.4.26
***********************************
the menu of the update programe:
[w] write the nand flash
[r] read the nand flash
[e] erase the nand flash
[g] get file, and write to nand flash 0 block
[x] get file to ddr(0x32000000), run it
[s] reset the programe
Please enter the chose:
一、编写源代码
根据s3c2440手册编写代码,包括文件:start.S init.c main.c boot.lds Makefile
文件start.S:
#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
#define MEM_CTL_BASE 0x48000000
.text
.global _start
_start:
/*1.关看门狗*/
ldr r0, =0x53000000
mov r1, #0
str r1, [r0]
/*2.设置时钟*/
ldr r0, =0x4c000014
mov r1, #0x03
str r1, [r0]
/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */
orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode” */
mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */
/* MPLLCON = S3C2440_MPLL_200MHZ */
ldr r0, =0x4c000004
ldr r1, =S3C2440_MPLL_200MHZ
str r1, [r0]
/*3.初始化SDRAM*/
ldr r0, =MEM_CTL_BASE
adr r1, sdram_config /*当前地址*/
add r3, r0, #(13*4)
1:
ldr r2, [r1], #4
str r2, [r0], #4
cmp r0 ,r3
bne 1b
/*4.重定位:把bootloader本身代码从flash复制到他的链接地址*/
ldr sp, =0x34000000
bl nand_init
mov r0, #0
ldr r1, =_start
ldr r2, =__bss_start
sub r2 ,r2, r1
bl copy_code_to_sdram
bl clear_bss
/*5.执行main*/
//bl main
ldr lr, =halt
ldr pc, =main
halt:
b halt
sdram_config:
.long 0x22011110 //BWSCON
.long 0x00000700 //BANKCON0
.long 0x00000700 //BANKCON1
.long 0x00000700 //BANKCON2
.long 0x00000700 //BANKCON3
.long 0x00000700 //BANKCON4
.long 0x00000700 //BANKCON5
.long 0x00018005 //BANKCON6
.long 0x00018005 //BANKCON7
.long 0x008C04F4 // REFRESH
.long 0x000000B1 //BANKSIZE
.long 0x00000030 //MRSRB6
.long 0x00000030 //MRSRB7
文件init.c:
/* NAND FLASH控制器 */
#define NFCONF (*((volatile unsigned long *)0x4E000000))
#define NFCONT (*((volatile unsigned long *)0x4E000004))
#define NFCMMD (*((volatile unsigned char *)0x4E000008))
#define NFADDR (*((volatile unsigned char *)0x4E00000C))
#define NFDATA (*((volatile unsigned char *)0x4E000010))
#define NFSTAT (*((volatile unsigned char *)0x4E000020))
/* GPIO */
#define GPHCON (*(volatile unsigned long *)0x56000070)
#define GPHUP (*(volatile unsigned long *)0x56000078)
/* UART registers*/
#define ULCON0 (*(volatile unsigned long *)0x50000000)
#define UCON0 (*(volatile unsigned long *)0x50000004)
#define UFCON0 (*(volatile unsigned long *)0x50000008)
#define UMCON0 (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
#define UFSTAT0 (*(volatile unsigned long *)0x50000018)
#define UTXH0 (*(volatile unsigned char *)0x50000020)
#define URXH0 (*(volatile unsigned char *)0x50000024)
#define UBRDIV0 (*(volatile unsigned long *)0x50000028)
#define TXD0READY (1<<2)
void nand_read(unsigned int nand_start, unsigned int ddr_start, unsigned int len);
int isBootFromNorFlash(void)
{
volatile int *p = (volatile int *)0;
int val;
val = *p;
*p = 0x12345678;
if (*p == 0x12345678)
{
/*写成功,是nand启动*/
*p = val;
return 0;
}
else
{
/*Nor不能像内存一样写*/
return 1;
}
}
void copy_code_to_sdram(unsigned int src, unsigned int dest, unsigned int len)
{
int i = 0;
unsigned char *src_start = (unsigned char *)src;
unsigned char *dest_start = (unsigned char *)dest;
/*如果是Nor启动*/
if(isBootFromNorFlash())
{
while (i < len)
{
//dest[i] = src[i];
dest_start[i] = src_start[i];
i++;
}
}
else
{
//nand_init();
//nand_resd(src, dest, len)
nand_read(src, dest, len);
}
}
void clear_bss(void)
{
extern int __bss_start, __bss_end;
int *p = &__bss_start;
for (; p < &__bss_end; p++)
*p = 0;
}
void nand_select(void)
{
NFCONT &= ~(1<<1);
}
void nand_deselect(void)
{
NFCONT |= (1<<1);
}
void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCMMD = cmd;
for (i = 0; i < 10; i++);
}
void nand_addr(unsigned int addr)
{
unsigned int col = addr % 2048;
unsigned int page = addr / 2048;
volatile int i;
NFADDR = col & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (col >> 8) & 0xff;
for (i = 0; i < 10; i++);
NFADDR = page & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (page >> 8) & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (page >> 16) & 0xff;
for (i = 0; i < 10; i++);
}
void wait_ready(void)
{
while (!(NFSTAT & 1));
}
unsigned char nand_get_data(void)
{
return NFDATA;
}
void nand_send_data(unsigned char data)
{
NFDATA = data;
}
void nand_reset(void)
{
/* 选中 */
nand_select();
/* 发出0xff命令 */
nand_cmd(0xff);
/* 等待就绪 */
wait_ready();
/* 取消选中 */
nand_deselect();
}
void nand_read(unsigned int nand_start, unsigned int ddr_start, unsigned int len)
{
unsigned int addr = nand_start;
int col = addr % 2048;
int i = 0;
unsigned char *dest = (unsigned char *)ddr_start;
/* 1. 选中 */
nand_select();
while (i < len)
{
/* 2. 发出读命令00h */
nand_cmd(0x00);
/* 3. 发出地址(分5步发出) */
nand_addr(addr);
/* 4. 发出读命令30h */
nand_cmd(0x30);
/* 5. 判断状态 */
wait_ready();
/* 6. 读数据 */
for (; (col < 2048) && (i < len); col++)
{
dest[i] = nand_get_data();
i++;
addr++;
}
col = 0;
}
/* 7. 取消选中 */
nand_deselect();
}
void nand_erase_block(unsigned long addr)
{
int page = addr / 2048;
nand_select();
nand_cmd(0x60);
nand_addr(page & 0xff);
nand_addr((page >> 8) & 0xff);
nand_addr((page >> 16) & 0xff);
nand_cmd(0xd0);
wait_ready();
nand_deselect();
}
void nand_write(unsigned int nand_start, unsigned char * buf, unsigned int len)
{
unsigned long count = 0;
unsigned long addr = nand_start;
int i = nand_start % 2048;
//int left = i;
nand_select();
while (count < len)
{
nand_cmd(0x80);
nand_addr(addr);
//for (; i < (2048-left) && count < len; i++)
for (; i < 2048 && count < len; i++)
{
/*if(addr<16384)//写前2K
{
if(i<(2048-left))//前2页每页只能写2K
{
nand_send_data(buf[count++]);
}
}
else
{
nand_send_data(buf[count++]);
}*/
nand_send_data(buf[count++]);
addr++;
}
nand_cmd(0x10);
wait_ready();
i = 0;
//left = i;
}
nand_deselect();
}
void nand_init(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/* 设置时序 */
NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
NFCONT = (1<<4)|(1<<1)|(1<<0);
nand_reset();
}
#define PCLK 50000000 // init.c中的clock_init函数设置PCLK为50MHz
#define UART_CLK PCLK // UART0的时钟源设为PCLK
#define UART_BAUD_RATE 115200 // 波特率
#define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)
#define ENABLE_FIFO
static void delay(void)
{
volatile int i = 10;
while (i--);
}
/*
* 初始化UART0
* 115200,8N1,无流控
*/
void uart0_init(void)
{
GPHCON |= 0xa0; // GPH2,GPH3用作TXD0,RXD0
GPHUP = 0x0c; // GPH2,GPH3内部上拉
ULCON0 = 0x03; // 8N1(8个数据位,无较验,1个停止位)
UCON0 = 0x05; // 查询方式,UART时钟源为PCLK
#ifdef ENABLE_FIFO
UFCON0 = 0x07; /* FIFO enable */
#else
UFCON0 = 0x00; /* FIFO disable */
#endif
UMCON0 = 0x00; // 不使用流控
UBRDIV0 = UART_BRD; // 波特率为115200
}
/*
* 发送一个字符
*/
void putc(unsigned char c)
{
/* 等待,直到发送缓冲区中的数据已经全部发送出去 */
//while (!(UTRSTAT0 & TXD0READY));
#ifdef ENABLE_FIFO
while (UFSTAT0 & (1<<14))delay();
#else
while ((UTRSTAT0 & (1<<2)) == 0);
#endif
/* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
UTXH0 = c;
}
/*
* 接收字符
*/
unsigned char getc(void)
{
/* 等待,直到接收缓冲区中的有数据 */
//while (!(UTRSTAT0 & RXD0READY));
#ifdef ENABLE_FIFO
while ((UFSTAT0 & (1<<6)) == 0 && (UFSTAT0 & 0x3f) == 0)delay();
#else
while ((UTRSTAT0 & (1<<0)) == 0);
#endif
/* 直接读取URXH0寄存器,即可获得接收到的数据 */
return URXH0;
}
int getc_nowait(unsigned char *pChar)
{
#ifdef ENABLE_FIFO
if ((UFSTAT0 & (1<<6)) == 0 && (UFSTAT0 & 0x3f) == 0)
#else
if ((UTRSTAT0 & (1<<0)) == 0)
#endif
{
return -1;
}
else
{
*pChar = URXH0;
return 0;
}
}
void puts(char *str)
{
int i = 0;
while (str[i])
{
putc(str[i]);
i++;
}
}
void puthex(unsigned int val)
{
/* 0x1234abcd */
int i;
int j;
puts("0x");
for (i = 0; i < 8; i++)
{
j = (val >> ((7-i)*4)) & 0xf;
if ((j >= 0) && (j <= 9))
putc('0' + j);
else
putc('A' + j - 0xa);
}
}
void putbyte(unsigned char val)
{
/* 0x1234abcd */
int i;
int j;
puts("0x");
for (i = 0; i < 2; i++)
{
j = (val >> ((1-i)*4)) & 0xf;
if ((j >= 0) && (j <= 9))
putc('0' + j);
else
putc('A' + j - 0xa);
}
}
文件main.c:
extern void uart0_init(void);
extern void nand_read(unsigned int nand_start, unsigned int ddr_start, unsigned int len);
extern void putc(char c);
extern void puts(char *str);
extern void puthex(unsigned int val);
extern unsigned char getc(void);
extern int getc_nowait(unsigned char *pChar);
extern void putbyte(unsigned char val);
extern void nand_erase_block(unsigned long addr);
extern void nand_write(unsigned int nand_start, unsigned char * buf, unsigned int len);
int strlen(char *str)
{
int i = 0;
while (str[i])
{
i++;
}
return i;
}
void nand_write_test(void)
{
char buf[20] = {"abcd1234ABCD"};
unsigned long addr;
unsigned long size;
puts("enter the start address:0x80000 ");
//scanf("%s", buf);
//addr = strtoul(buf, NULL, 0);
addr = 0x80000;
puts("enter the string:abcd1234ABCD ");
//scanf("%s", buf);
size = strlen(buf) + 1;
puts(" size= ");
puthex(size);
puts("\n\r");
nand_write(addr, buf, size);
}
void nand_read_test(void)
{
int i;
char buf[100];
unsigned long addr;
unsigned long size;
puts("enter the start address: 0x80000");
//scanf("%s", buf);
//addr = strtoul(buf, NULL, 0);
addr = 0x80000;
//puts("read addr = 0x%x\n\r", addr);
puts("enter the size: 0x60");
//scanf("%s", buf);
//size = strtoul(buf, NULL, 0);
size = 0x60;
if (size > 100)
{
puts("the max size is 100\n\r");
size = 100;
}
nand_read(addr, buf, size);
puts("datas: \n\r");
for (i = 0; i < size; i++)
{
// printf("%02x ", buf[i]);
putbyte(buf[i]);
puts("\t");
if ((i+1) % 8 == 0)
{
puts("\n\r");
}
}
puts("\n\r");
}
void nand_erase_test(void)
{
//char buf[100];
unsigned long addr;
puts("enter the start address: ");
//scanf("%s", buf);
//addr = strtoul(buf, NULL, 0);
addr = 0x80000;
puts("erase addr = ");
puthex(addr);
puts("\n\r");
nand_erase_block(addr);
}
void update_program(void)
{
unsigned char *buf = (unsigned char *)0x32000000;
//unsigned char *buf = (unsigned char *)0xD0036000;
unsigned long len = 0;
int have_begin = 0;
int nodata_time = 0;
unsigned long erase_addr;
char c;
int i;
/* 璇讳覆鍙h幏寰楁暟鎹?*/
puts("\n\ruse V2.2.exe/gtkterm to send file\n\r");
while (1)
{
if (getc_nowait(&buf[len]) == 0)
{
have_begin = 1;
nodata_time = 0;
len++;
}
else
{
if (have_begin)
{
nodata_time++;
}
}
if (nodata_time == 1000)
{
break;
}
}
puts("\n\rhave get data:");
puthex(len);
puts(" bytes\n\r");
puts("the first 16 bytes data: \n\r");
for (i = 0; i < 16; i++)
{
// put("%02x ", buf[i]);
putbyte(buf[i]);
puts("\t");
}
puts("\n\r");
puts("Press Y to program the flash: \n\r");
c = getc();
putc(c);
puts("\n\r");
if (c == 'y' || c == 'Y')
{
/* 鐑у啓鍒皀and flash block 0 */
for (erase_addr = 0; erase_addr < ((len + 0x1FFFF) & ~0x1FFFF); erase_addr += 0x20000)
{
nand_erase_block(erase_addr);
}
nand_write(0, buf, len);
puts("update program successful\n\r");
}
else
{
puts("Cancel program!\n\r");
}
}
void run_program(void)
{
unsigned char *buf = (unsigned char *)0x32000000;
//unsigned char *buf = (unsigned char *)0xD0036000;
unsigned long len = 0;
int have_begin = 0;
int nodata_time = 0;
void (*theProgram)(void);
int i;
/* 璇讳覆鍙h幏寰楁暟鎹?*/
puts("\n\r use gtkterm to send file\n\r");
while (1)
{
if (getc_nowait(&buf[len]) == 0)
{
have_begin = 1;
nodata_time = 0;
len++;
}
else
{
if (have_begin)
{
nodata_time++;
}
}
if (nodata_time == 10000)
{
break;
}
}
//printf("have get %d bytes data\n\r", len);
puts("\n\r have get data:");
puthex(len);
puts(" bytes\n\r");
puts("the first 16 bytes data: \n\r");
for (i = 0; i < 16; i++)
{
// put("%02x ", buf[i]);
putbyte(buf[i]);
puts("\t");
//putc('\0');
}
puts("\n\r");
puts("jump to 0x32000000 to run it\n\r");
theProgram = (void (*)(void))0x32000000;
theProgram();
}
int main(void)
{
char c;
uart0_init();
puts("\n\r*********************************\n\r");
puts("update program with serial port\n\r");
puts("The board:micro2440\n\r");
puts("The NAND:K9F1216U0A 256MB\n\r");
puts("The NOR:AM29LV160DB 2MB\n\r");
puts("The SRAM:HY57V561620 x2 64MB\n\r");
puts("The NET:DM9000AEP\n\r");
puts(" date: 2013.4.26\n\r");
puts("***********************************\n\r");
while (1)
{
puts("the menu of the update programe:\n\r");
puts("[w] write the nand flash\n\r");
puts("[r] read the nand flash\n\r");
puts("[e] erase the nand flash\n\r");
puts("[g] get file, and write to nand flash 0 block\n\r");
puts("[x] get file to ddr(0x32000000), run it\n\r");
puts("[s] reset the programe\n\r");
puts("Please enter the chose:\n\r");
do {
c = getc();
if (c == '\n' || c == '\r')
{
puts("\n\r");
}
else
{
putc(c);
}
} while (c == '\n' || c == '\r');
switch (c)
{
case 'w':
case 'W':
{
nand_write_test();
break;
}
case 'r':
case 'R':
{
nand_read_test();
break;
}
case 'e':
case 'E':
{
nand_erase_test();
break;
}
case 'g':
case 'G':
{
update_program();
break;
}
case 'x':
case 'X':
{
run_program();
break;
}
case 's':
case 'S':
{
void (*theProgram)(void);
theProgram = (void (*)(void))0x33f80000;
theProgram();
break;
}
}
}
return 0;
}
文件boot.lds:
SECTIONS {
. = 0x33f80000;
.text : { *(.text) }
. = ALIGN(4);
.rodata : {*(.rodata*)}
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(COMMON) }
__bss_end = .;
}
文件Makefile:
CC = arm-linux-gcc
LD = arm-linux-ld
AR = arm-linux-ar
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
CFLAGS := -Wall -O2
CPPFLAGS := -nostdinc -nostdlib -fno-builtin
objs := start.o init.o main.o
update.bin: $(objs)
${LD} -Tboot.lds -o boot.elf $^
${OBJCOPY} -O binary -S boot.elf $@
${OBJDUMP} -D -m arm boot.elf > update.dis
%.o:%.c
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o:%.S
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o *.bin *.elf *.dis
二、编译源代码
change@change:~$ cd Si/micro2440/update/
change@change:~/Si/micro2440/update$ ls
boot.lds init.c main.c Makefile start.S
change@change:~/Si/micro2440/update$ make
arm-linux-gcc -nostdinc -nostdlib -fno-builtin -Wall -O2 -c -o start.o start.S
arm-linux-gcc -nostdinc -nostdlib -fno-builtin -Wall -O2 -c -o init.o init.c
arm-linux-gcc -nostdinc -nostdlib -fno-builtin -Wall -O2 -c -o main.o main.c
main.c: In function 'nand_write_test':
main.c:38: warning: pointer targets in passing argument 2 of 'nand_write' differ in signedness
main.c: In function 'nand_read_test':
main.c:69: warning: passing argument 2 of 'nand_read' makes integer from pointer without a cast
arm-linux-ld -Tboot.lds -o boot.elf start.o init.o main.o
arm-linux-objcopy -O binary -S boot.elf update.bin
arm-linux-objdump -D -m arm boot.elf > update.dis
change@change:~/Si/micro2440/update$ cp update.bin /home/change/work/tftpboot/
change@change:~/Si/micro2440/update$
三、烧写、测试
micro2440单板从NOR flash启动,用u-boot下载上面编译的程序update.bin到NAND Flash,以后就可以不要依靠u-boot下载程序,直接用update
U-Boot 2012.04.01 (Oct 25 2012 - 22:47:25)
CPUID: 32440001
FCLK: 400 MHz
HCLK: 100 MHz
PCLK: 50 MHz
DRAM: 64 MiB
WARNING: Caches not enabled
Flash: 2 MiB
NAND: 256 MiB
In: serial
Out: serial
Err: serial
Net: dm9000
Warning: dm9000 MAC addresses don't match:
Address in SROM is ff:ff:ff:ff:ff:ff
Address in environment is 00:0c:29:4d:e4:f4
Hit any key to stop autoboot: 0
SMDK2410 # set ipaddr 172.16.1.111
SMDK2410 # set gatewayip 172.16.1.1
SMDK2410 # set serverip 172.16.1.133
SMDK2410 # tftp 0x32000000 update.bin
dm9000 i/o: 0x20000000, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 00:0c:29:4d:e4:f4
could not establish link
Using dm9000 device
TFTP from server 172.16.1.133; our IP address is 172.16.1.111
Filename 'update.bin'.
Load address: 0x32000000
Loading: #
done
Bytes transferred = 5140 (1414 hex)
SMDK2410 # nand erase 0 0x40000
NAND erase: device 0 offset 0x0, size 0x40000
Erasing at 0x20000 -- 100% complete.
OK
SMDK2410 # nand write 0x32000000 0 0x40000
NAND write: device 0 offset 0x0, size 0x40000
262144 bytes written: OK
SMDK2410 #
下载完成,单板断电从NAND启动,上电即运行上面下载的update,设置串口115200 8 n1,输出如下:
*********************************
update program with serial port
The board:micro2440
The NAND:K9F1216U0A 256MB
The NOR:AM29LV160DB 2MB
The SRAM:HY57V561620 x2 64MB
The NET:DM9000AEP
date: 2013.4.26
***********************************
the menu of the update programe:
[w] write the nand flash
[r] read the nand flash
[e] erase the nand flash
[g] get file, and write to nand flash 0 block
[x] get file to ddr(0x32000000), run it
[s] reset the programe
Please enter the chose:
下面用update下载程序,使用串口v2.2.exe(XP)或gtkterm(UBUNTU),这里用串口v2.2.exe工具
手动发送g
g
use V2.2.exe/gtkterm to send file
选择发送的程序文件eg1_led.bin,并发送文件
have get data:0x00000060 bytes
the first 16 bytes data:
0x50 0x00 0x9F 0xE5 0x55 0x1B 0xA0 0xE3 0x00 0x10 0x80 0xE5 0x48 0x00 0x9F 0xE5
Press Y to program the flash:
接着手动发送y,这里需谨慎,一旦发送y,将会擦除以前的程序,并写入新程序到NAND
y
update program successful
表面程序下载成功。现在断电,NAND启动,重启发现4个LED灯开始闪烁,说明程序烧写成功。