|
本帖最后由 cruelfox 于 2015-10-31 21:37 编辑
我对底层的东西特别感兴趣。十多年前有了自己的电脑,一直保留DOS用了好些年。喜欢汇编,讨厌Java。接触的第一个单片机是AT89S52,下程序是用串口CTS/RTS以及DSR/DTR控制线来模拟SPI,在DOS下把时钟中断调到几十kHz来定时确定时序。后来就用89S52做成了2051的编程器,再后来用2051做了串口转SPI下载器玩起AVR了,玩过最多的还是AVR.
不过51、AVR这类Harvard结构MCU不能干的事情是自己Load程序来运行,只能从ROM执行啊(51可以有办法映射,但是51学习机计划没有实施)。我小时候听说过“中华学习机”,不同于小霸王学习机那样ROM固化死了的,人家是从软盘装载程序的。家里头的旧杂志上介绍过TD-I型8031的学习机,是用开关来二进制编程RAM然后执行的,倘若放到今天……
我生活的时代已经不是穿孔纸带了,DIY还是玩现代的东西吧。言归正传,我的第一个“学习机”作品是ARM7TDMI的,尽管这个核已经早过时了。我选择了NXP的LPC2220作为处理器,它自带64kB的RAM,在单片机中算是大的了。核心比较简单,作为学习机适宜,而且有外部总线,比较方便扩展RAM,类似PC那样来玩。这个PCB做得比较早了,搁置没玩起来(手头没时间玩的板子多了,这是另外的问题),上面除了MCU还有一块16bit*256k的SRAM,一片8bit*128k的NOR Flash ROM,预留16-bit总线插针扩展,具备一个计算机的配置了(除了缺少DMA支持)。下面是线路图:
PCB的顶层图
PCB的底层:
作为MCU自带的外设是可以引出来的,所以UART, SPI, I2C基本I/O通信用分组接到插针,再另外放了16个GPIO在一侧。核心部分先焊上就可以玩了。
上面介绍的是硬件部分。LPC2220是为数不多的ROMless单片机,里面没有Flash哦,OTP也没有。这也是我选择它的原因,作为学习机不是做任何应用的原型板,烧写Flash尽量避免吧。虽然Flash的烧写次数已经够多了,能省一事算一事。NXP的ARM7 (LPC21xx系列)有个特点是可以从PC直接ISP下载到RAM,然后运行。我在第一次玩ARM (LPC2103)的时候发现Flash工具有这个功能。
读NXP的手册,发现Bootloader是使用串口命令进行交互的,命令也不复杂。
所以,只要连接MCU的UART0到PC的串口,上电或者复位就进入Bootloader里面的ISP程序(因为没有可执行的ROM嘛),就可以从PC把代码直接下载到ROM然后运行了。64kB的SRAM哦,可以实现不少东西了吧。
当然,用NXP官方的FlashMagic来下载的效率太低了,不适应“学习机"要快速载入程序的要求。所以我自己做一个程序:
- #include<windows.h>
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
- HANDLE dev;
- OVERLAPPED oWR = {0}, oRD={0};
- void errexit(char *s)
- {
- fprintf(stderr,"%s",s);
- exit(1);
- }
- struct ROM
- {
- unsigned char b;
- char oc;
- };
- int readcomm(unsigned char *buf, int maxlen, int timeout);
- int writecomm(unsigned char *buf, int len);
- int loadhexfile(char *fn,int szlimit,int *poff,struct ROM *rom);
- int showbinary(int szlimit, struct ROM *rom);
- int uuencode(int szlimit, struct ROM *rom, char *buf, int slen, int linelen);
- int checksum(int szlimit, struct ROM *rom);
- main(int argc, char *argv[])
- {
- DCB dcb;
- int i,r;
-
- if(argc<2)
- {
- printf("Supply a HEX file name.\n");
- return;
- }
-
- dev=CreateFile("COM3", GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING, FILE_FLAG_OVERLAPPED,0);
- if(dev==INVALID_HANDLE_VALUE)
- {
- printf("Cannot open COM3 port.\n");
- exit(1);
- }
- FillMemory(&dcb, sizeof(dcb), 0);
- dcb.DCBlength = sizeof(dcb);
- BuildCommDCB("57600,n,8,1",&dcb);
- if (!SetCommState(dev, &dcb))
- {
- printf("Cannot set port parameters.\n");
- exit(1);
- }
- oWR.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- oRD.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- for(i=0;i<1000;i++)
- {
- int len0;
- char c, buf[32];
- char done=0;
- writecomm("?",1);
- r=readcomm(buf,4,20);
- if(r>0)
- {
- int j;
- for(j=0;j<r;j++)
- {
- printf("%c",buf[j]);
- if(buf[j]=='S')
- {
- r=readcomm(buf+r,14-(4-j),20);
- if(r==14-(4-j) && strncmp(buf+j,"Synchronized\r\n",14)==0)
- {
- printf("SYNC\n");
- writecomm("Synchronized\r\n",14);
- r=readcomm(buf,32,50);
- if(r==18 && strncmp(buf,"Synchronized\r\nOK\r\n",18)==0)
- {
- printf("Synchronization done\n");
- done=1;
- break;
- }
- else
- printf("(%d) %s",r,buf);
- }
- }
- }
- if(done) break;
- }
- else
- {
- printf(".");
- continue;
- }
- }
- {
- char c;
- writecomm("11095\r\n",7);
- while(readcomm(&c,1,10)>0)
- {
- printf("%c",c);
- }
- }
- {
- char c;
- writecomm("J\r\n",3);
- while(readcomm(&c,1,10)>0)
- {
- printf("%c",c);
- }
- }
- {
- unsigned int progaddr=0x40000200;
- char c;
- char str[128];
- char buf[512];
- int slen;
- int pgsize;
- struct ROM rom[1024];
- int offset=-1;
- pgsize=loadhexfile(argv[1],1024,&offset,rom);
- printf("Program Size: %d bytes from %X\n", pgsize, offset);
- showbinary(1024,rom);
- sprintf(str,"W %d %d\r\n",progaddr, pgsize);
- slen=strlen(str);
- writecomm(str,slen);
- while(readcomm(&c,1,10)>0)
- {
- printf("%c",c);
- }
- slen=uuencode(1024, rom, buf, 512, 61);
- writecomm(buf,slen);
- sprintf(str,"%d\r\n",checksum(1024,rom));
- slen=strlen(str);
- writecomm(str,slen);
- while(readcomm(&c,1,10)>0)
- {
- printf("%c",c);
- }
- writecomm("U 23130\r\n",9);
- while(readcomm(&c,1,10)>0)
- {
- printf("%c",c);
- }
- sprintf(str,"G %d T\r\n",progaddr);
- slen=strlen(str);
- writecomm(str,slen);
- while(readcomm(&c,1,10)>0)
- {
- printf("%c",c);
- }
- }
- CloseHandle(oWR.hEvent);
- CloseHandle(oRD.hEvent);
- CloseHandle(dev);
- }
- int writecomm(unsigned char *buf, int len)
- {
- int r;
- int len0;
- r=WriteFile(dev,buf,len,&len0, &oWR);
- if(r==0)
- {
- if(GetLastError()!=ERROR_IO_PENDING)
- {
- printf("COM port access error!\n");
- exit(1);
- }
- GetOverlappedResult(dev, &oWR, &len0, TRUE);
- }
- return len0;
- }
- int readcomm(unsigned char *buf, int maxlen, int tmout)
- {
- static unsigned wait=0;
- static unsigned char ch;
- int i, len0, r;
- if(tmout<=0)
- return 0;
- for(i=0;i<maxlen;i++)
- {
- if(!wait)
- {
- if(ReadFile(dev,&ch,1,&len0, &oRD))
- {
- wait=0;
- buf[i]=ch;
- continue;
- }
- if(GetLastError()!=ERROR_IO_PENDING)
- {
- printf("Read COM port error!\n");
- exit(1);
- }
- }
- r=WaitForSingleObject(oRD.hEvent,1);
- if(r==WAIT_TIMEOUT)
- {
- wait=1;
- tmout--;
- if(tmout==0)
- return i;
- else
- {
- i--;
- continue;
- }
- }
- if(r==WAIT_OBJECT_0)
- {
- wait=0;
- buf[i]=ch;
- continue;
- }
- }
- return i;
- }
- int loadhexfile(char *fn,int szlimit,int *poff,struct ROM *rom)
- {
- int i,j,n,count,addr,waddr,bsz;
- char line[512];
- unsigned char bb,sum,hex[256];
- FILE *fp;
- memset(rom,0,sizeof(struct ROM)*szlimit);
- fp=fopen(fn,"r");
- if(fp==NULL)
- {
- printf("Cannot open HEX file %s\n",fn);
- return 0;
- }
- printf("Opening file %s for reading... ",fn);
- count=0;
- sum=0;
- for(n=1;;n++)
- {
- if(!fgets(line,512,fp))
- errexit("read error\n");
- if(line[0]!=':')
- errexit("FORMAT ERROR\n");
- j=0;
- for(i=1;;i+=2) // convert ASCII to binary
- {
- switch(line[i])
- {
- case 'A': bb=0xa0; break;
- case 'B': bb=0xb0; break;
- case 'C': bb=0xc0; break;
- case 'D': bb=0xd0; break;
- case 'E': bb=0xe0; break;
- case 'F': bb=0xf0; break;
- case '\0': case '\r': case '\n': bb=0x77; break;
- default: if(line[i]>='0' && line[i]<='9')
- bb=(line[i]-'0')<<4;
- else
- bb=0xff;
- }
- if(bb==0x77)
- break;
- if(bb==0xff)
- errexit("INVALID CHARACTER\n");
- switch(line[i+1])
- {
- case 'A': bb+=0xa; break;
- case 'B': bb+=0xb; break;
- case 'C': bb+=0xc; break;
- case 'D': bb+=0xd; break;
- case 'E': bb+=0xe; break;
- case 'F': bb+=0xf; break;
- default: if(line[i+1]>='0' && line[i+1]<='9')
- bb+=(line[i+1]-'0');
- else
- errexit("BAD BYTE\n");
- }
- sum+=bb;
- hex[j]=bb;
- j++;
- } // converted ASCII to binary
- if(sum!=0)
- errexit("CHECKSUM ERROR\n");
- if(j<5)
- errexit("LINE TOO SHORT\n");
- if(hex[0]!=j-5)
- errexit("FORMAT ERROR\n");
- bsz=j-5;
- switch(hex[3])
- {
- case 0: addr=hex[1]; addr<<=8; addr+=hex[2];
- if(*poff==-1)
- {
- *poff=addr;
- }
- for(i=0;i<bsz;i++)
- {
- int waddr=addr-(*poff);
- if(waddr>szlimit)
- {
- printf("address %X beyond range (base %X) \n",addr, *poff);
- }
- else
- {
- rom[waddr].b=hex[4+i];
- rom[waddr].oc=1;
- }
- addr++;
- count++;
- }
- break;
- case 1: if(j!=5)
- errexit("UNEXPECTED END\n");
- fclose(fp);
- return count;
- case 2: if(hex[1]!=0 || hex[2]!=0 || hex[4]!=0 || hex[5]!=0)
- printf("UNSUPPORTED PARAGRAPH\n");
- break;
- case 3:
- default: printf("UNSUPPORTED RECODE TYPE (%X)\n", hex[3]); break;
- }
- }
- }
- int showbinary(int szlimit, struct ROM *rom)
- {
- int i;
- for(i=0;i<szlimit;)
- {
- int j;
- char blank=1;
- for(j=0;j<16;j++)
- {
- if(rom[i+j].oc)
- {
- blank=0;
- break;
- }
- }
- if(blank)
- {
- i+=16;
- continue;
- }
- printf("%04X: ",i);
- for(j=0;j<16;j++)
- {
- if(rom[i+j].oc)
- printf("%02X",rom[i+j].b);
- else
- printf("..");
- if(j==7)
- printf(" - ");
- else
- printf(" ");
- }
- printf(" | ");
- for(j=0;j<16;j++)
- {
- if(rom[i+j].oc)
- {
- if(rom[i+j].b>=0x20 && rom[i+j].b<=0x7f)
- printf("%c",rom[i+j].b);
- else
- printf("?");
- }
- else
- printf(".");
- }
- i+=16;
- printf("\n");
- }
- }
- int uuencode(int szlimit, struct ROM *rom, char *buf, int slen, int linelen)
- {
- int i, sz=0,mem;
- int lmax, lc, brem;
- int index=0;
- for(i=0;i<szlimit;i++)
- {
- if(rom[i].oc)
- sz=i+1;
- }
- lmax=((linelen-1)/4)*3;
- lc=sz/lmax;
- brem=sz%lmax;
- mem=lc*(((linelen-1)/4)*4+3)+3+(brem/3*4)+(brem%3+1); // include CR LF
- if(mem>=slen)
- {
- printf("UUEncode: buffer too small! need %d bytes\n",mem);
- return 0;
- }
- for(i=0;i<sz;)
- {
- int j, len;
- char r=0;
- unsigned char b, a=0;
- if(i+lmax<sz)
- len=lmax;
- else
- len=brem;
- buf[index++]=0x20+len;
- for(j=0;j<len;j++)
- {
- if(rom[i+j].oc)
- b=rom[i+j].b;
- else
- b=0xff;
- a|=(b>>(2+r));
- if(a) buf[index++]=0x20+a; else buf[index++]='`';
- r+=2;
- a=((b&((1<<r)-1))<<(6-r));
- if(r==6)
- {
- if(a) buf[index++]=0x20+a; else buf[index++]='`';
- r=0;
- a=0;
- }
- }
- if(r!=0)
- {
- if(a) buf[index++]=0x20+a; else buf[index++]='`';
- }
- i+=lmax;
- buf[index++]='\r';
- buf[index++]='\n';
- }
- printf("MEM %d, actual %d\n",mem,index);
- return index;
- }
- int checksum(int szlimit, struct ROM *rom)
- {
- int i, sz, sum;
- sz=0;
- for(i=0;i<szlimit;i++)
- {
- if(rom[i].oc)
- sz=i+1;
- }
- sum=0;
- for(i=0;i<sz;i++)
- sum+=rom[i].b;
- return sum;
- }
复制代码
这个程序做的工作是打开串口, 和单片机Bootloader ISP命令同步,然后读取指定的HEX文件,下载到LPC2220的RAM地址 0x40000200, 再解除锁定,最后发送执行程序的命令,单片机从 0x40000200 开始运行。如果要重新下载程序,把单片机Reset了,再运行以上的程序即可。命令行一步搞定,容易吧!
注: LPC2220的RAM地址从 0x40000000 开始,但是BootLoader会用一部分RAM,所以为了避免冲突,程序下载从RAM的512字节之后起。NXP的Bootloader是用UUEncode编码传输二进制数据的,上面的程序包含编码的子程序。这个程序也写得不完善,还有比如代码长度检查都没有做进去,目前做到的只是能下载一小段并执行。
|
|